Merge branch 'development'
diff --git a/.gitignore b/.gitignore
index bba9d5d..d4e0454 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,6 @@
-/tests-clar/clar.suite
-/tests-clar/clar.suite.rule
-/tests-clar/.clarcache
+/tests/clar.suite
+/tests/clar.suite.rule
+/tests/.clarcache
 /apidocs
 /trash-*.exe
 /libgit2.pc
diff --git a/.mailmap b/.mailmap
index 3f5a087..c656f64 100644
--- a/.mailmap
+++ b/.mailmap
@@ -16,3 +16,6 @@
 Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
 Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
 Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
+Edward Thomson <ethomson@microsoft.com> <ethomson@edwardthomson.com>
+J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com>
+Russell Belfer <rb@github.com> <arrbee@arrbee.com>
diff --git a/.travis.yml b/.travis.yml
index 0d5746f..151060f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,7 +1,6 @@
 # Travis-CI Build for libgit2
 # see travis-ci.org for details
 
-# As CMake is not officially supported we use erlang VMs
 language: c
 
 compiler:
@@ -18,26 +17,18 @@
    - compiler: i586-mingw32msvc-gcc
      env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
 
-# Make sure CMake is installed
 install:
- - sudo apt-get update >/dev/null
- - sudo apt-get -q install cmake valgrind
+ - sudo apt-get -qq update
+ - sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
 
-# Run the Build script
+# Run the Build script and tests
 script:
- - mkdir _temp
- - git init --bare _temp/test.git
- - git daemon --listen=localhost --export-all --enable=receive-pack --base-path=_temp _temp 2>/dev/null &
- - export GITTEST_REMOTE_URL="git://localhost/test.git"
- - mkdir _build
- - cd _build
- - cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
- - cmake --build . --target install
- - ctest -V .
+ - script/cibuild.sh
 
 # Run Tests
 after_success:
- - valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar -ionline
+ - sudo apt-get -qq install valgrind
+ - valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
 
 # Only watch the development branch
 branches:
diff --git a/AUTHORS b/AUTHORS
index 587da24..f3a03ee 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -68,5 +68,6 @@
 Tim Branyen
 Tim Clem
 Tim Harder
+Torsten Bögershausen
 Trent Mick
 Vicent Marti
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 34d56b3..ac1032a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -27,25 +27,44 @@
 OPTION( TAGS				"Generate tags"							OFF )
 OPTION( PROFILE				"Generate profiling information"		OFF )
 OPTION( ENABLE_TRACE		"Enables tracing support"				OFF )
-OPTION( LIBGIT2_FILENAME	"Name of the produced binary" OFF )
+OPTION( LIBGIT2_FILENAME	"Name of the produced binary"			OFF )
+
+OPTION( ANDROID				"Build for android NDK"	 				OFF )
+
+OPTION( USE_ICONV			"Link with and use iconv library" 		OFF )
+OPTION( USE_SSH				"Link with libssh to enable SSH support" ON )
+
+IF(APPLE)
+	SET( USE_ICONV ON )
+ENDIF()
 
 IF(MSVC)
-	# This option is only availalbe when building with MSVC. By default,
-	# libgit2 is build using the stdcall calling convention, as that's what
-	# the CLR expects by default and how the Windows API is built.
+	# This option is only available when building with MSVC. By default, libgit2
+	# is build using the cdecl calling convention, which is useful if you're
+	# writing C. However, the CLR and Win32 API both expect stdcall.
 	#
-	# If you are writing a C or C++ program and want to link to libgit2, you
-	# have to either:
-	# - Add /Gz to the compiler options of _your_ program / library.
-	# - Turn this off by invoking CMake with the "-DSTDCALL=Off" argument.
-	#
-	OPTION( STDCALL			"Build libgit2 with the __stdcall convention"	ON  )
+	# If you are writing a CLR program and want to link to libgit2, you'll want
+	# to turn this on by invoking CMake with the "-DSTDCALL=ON" argument.
+	OPTION( STDCALL			"Build libgit2 with the __stdcall convention"	OFF  )
 
 	# This option must match the settings used in your program, in particular if you
 	# are linking statically
 	OPTION( STATIC_CRT		"Link the static CRT libraries"	ON  )
+
+	# By default, libgit2 is built with WinHTTP.  To use the built-in
+	# HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
+	OPTION( WINHTTP			"Use Win32 WinHTTP routines"	ON  )
 ENDIF()
 
+# This variable will contain the libraries we need to put into
+# libgit2.pc's Requires.private. That is, what we're linking to or
+# what someone who's statically linking us needs to link to.
+SET(LIBGIT2_PC_REQUIRES "")
+# This will be set later if we use the system's http-parser library or
+# use iconv (OSX) and will be written to the Libs.private field in the
+# pc file.
+SET(LIBGIT2_PC_LIBS "")
+
 # Installation paths
 #
 SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
@@ -57,7 +76,18 @@
 		TARGET_LINK_LIBRARIES(${target} ws2_32)
 	ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
 		TARGET_LINK_LIBRARIES(${target} socket nsl)
-	ENDIF ()
+		SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lsocket -lnsl" PARENT_SCOPE)
+	ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+		TARGET_LINK_LIBRARIES(${target} rt)
+		SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE)
+	ENDIF()
+
+	IF(USE_ICONV)
+		TARGET_LINK_LIBRARIES(${target} iconv)
+		ADD_DEFINITIONS(-DGIT_USE_ICONV)
+		SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -liconv" PARENT_SCOPE)
+	ENDIF()
+
 	IF(THREADSAFE)
 		TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
 	ENDIF()
@@ -67,7 +97,7 @@
 # explorer does. This is esp. useful with the libgit2_clar project, were
 # usually 2 or more files share the same name.  Sadly, this file grouping
 # is a per-directory option in cmake and not per-target, resulting in
-# empty virtual folders "tests-clar" for the git2.dll
+# empty virtual folders "tests" for the git2.dll
 FUNCTION(MSVC_SPLIT_SOURCES target)
 	IF(MSVC_IDE)
 		GET_TARGET_PROPERTY(sources ${target} SOURCES)
@@ -96,8 +126,10 @@
 # Find required dependencies
 INCLUDE_DIRECTORIES(src include)
 
-IF (WIN32 AND NOT MINGW)
+IF (WIN32 AND WINHTTP AND NOT MINGW)
 	ADD_DEFINITIONS(-DGIT_WINHTTP)
+	INCLUDE_DIRECTORIES(deps/http-parser)
+	FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
 ELSE ()
 	IF (NOT AMIGA)
 		FIND_PACKAGE(OpenSSL)
@@ -107,10 +139,11 @@
 	IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
 		INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
 		LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
+		SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lhttp_parser")
 	ELSE()
 		MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.")
 		INCLUDE_DIRECTORIES(deps/http-parser)
-		FILE(GLOB SRC_HTTP deps/http-parser/*.c)
+		FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
 	ENDIF()
 ENDIF()
 
@@ -120,6 +153,7 @@
 	FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
 ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
 	ADD_DEFINITIONS(-DOPENSSL_SHA1)
+	SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
 ELSE()
 	FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
 ENDIF()
@@ -130,7 +164,7 @@
 ENDIF()
 
 # Include POSIX regex when it is required
-IF(WIN32 OR AMIGA)
+IF(WIN32 OR AMIGA OR ANDROID)
 	INCLUDE_DIRECTORIES(deps/regex)
 	SET(SRC_REGEX deps/regex/regex.c)
 ENDIF()
@@ -142,24 +176,31 @@
 IF (ZLIB_FOUND)
 	INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
 	LINK_LIBRARIES(${ZLIB_LIBRARIES})
+	IF(APPLE)
+		SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz")
+	ELSE()
+		SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
+	ENDIF()
 	# Fake the message CMake would have shown
 	MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
 ELSE()
 	MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
 	INCLUDE_DIRECTORIES(deps/zlib)
 	ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
-	FILE(GLOB SRC_ZLIB deps/zlib/*.c)
+	FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
 ENDIF()
 
-IF(NOT LIBSSH2_LIBRARY)
+IF (USE_SSH AND NOT MINGW)
 	FIND_PACKAGE(LIBSSH2 QUIET)
 ENDIF()
 IF (LIBSSH2_FOUND)
 	ADD_DEFINITIONS(-DGIT_SSH)
 	INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR})
+	SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2")
 	SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
 ENDIF()
 
+
 # Platform specific compilation flags
 IF (MSVC)
 
@@ -285,19 +326,19 @@
 ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
 
 # Collect sourcefiles
-FILE(GLOB SRC_H include/git2/*.h)
+FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
 
 # On Windows use specific platform sources
 IF (WIN32 AND NOT CYGWIN)
 	ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
-	FILE(GLOB SRC_OS src/win32/*.c)
+	FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
 ELSEIF (AMIGA)
 	ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R)
-	FILE(GLOB SRC_OS src/amiga/*.c)
+	FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h)
 ELSE()
-	FILE(GLOB SRC_OS src/unix/*.c)
+	FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
 ENDIF()
-FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
+FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
 
 # Determine architecture of the machine
 IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
@@ -309,7 +350,7 @@
 ENDIF()
 
 # Compile and link libgit2
-ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
+ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
 TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
 TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
 TARGET_OS_LIBRARIES(git2)
@@ -352,19 +393,19 @@
 IF (BUILD_CLAR)
 	FIND_PACKAGE(PythonInterp REQUIRED)
 
-	SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
-	SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar")
-	SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources" CACHE PATH "Path to test resources.")
+	SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
+	SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests")
+	SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
 	ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
 	ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
 
 	INCLUDE_DIRECTORIES(${CLAR_PATH})
-	FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c)
+	FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
 	SET(SRC_CLAR "${CLAR_PATH}/main.c" "${CLAR_PATH}/clar_libgit2.c" "${CLAR_PATH}/clar.c")
 
 	ADD_CUSTOM_COMMAND(
 		OUTPUT ${CLAR_PATH}/clar.suite
-		COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline .
+		COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
 		DEPENDS ${SRC_TEST}
 		WORKING_DIRECTORY ${CLAR_PATH}
 	)
@@ -373,7 +414,7 @@
 		${CLAR_PATH}/clar.c
 		PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
 
-	ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
+	ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
 
 	TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
 	TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
@@ -409,23 +450,5 @@
 ENDIF ()
 
 IF (BUILD_EXAMPLES)
-	FILE(GLOB_RECURSE EXAMPLE_SRC examples/network/*.c)
-	ADD_EXECUTABLE(cgit2 ${EXAMPLE_SRC})
-	IF(WIN32)
-		TARGET_LINK_LIBRARIES(cgit2 git2)
-	ELSE()
-		TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
-	ENDIF()
-
-	ADD_EXECUTABLE(git-diff examples/diff.c)
-	TARGET_LINK_LIBRARIES(git-diff git2)
-
-	ADD_EXECUTABLE(git-general examples/general.c)
-	TARGET_LINK_LIBRARIES(git-general git2)
-
-	ADD_EXECUTABLE(git-showindex examples/showindex.c)
-	TARGET_LINK_LIBRARIES(git-showindex git2)
-
-	ADD_EXECUTABLE(git-rev-list examples/rev-list.c)
-	TARGET_LINK_LIBRARIES(git-rev-list git2)
+	ADD_SUBDIRECTORY(examples)
 ENDIF ()
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 28ef27f..807cd53 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3,6 +3,12 @@
 We're making it easy to do interesting things with git, and we'd love to have
 your help.
 
+## Licensing
+
+By contributing to libgit2, you agree to release your contribution under the terms of the license.
+For code under `examples`, this is governed by the [CC0 Public Domain Dedication](examples/COPYING).
+All other code is released under the [GPL v2 with linking exception](COPYING).
+
 ## Discussion & Chat
 
 We hang out in the #libgit2 channel on irc.freenode.net.
@@ -48,6 +54,12 @@
 to read the whole diff to figure out why you're contributing in the first
 place, you're less likely to get feedback and have your change merged in.
 
+If you are working on a particular area then feel free to submit a PR that
+highlights your work in progress (and flag in the PR title that it's not
+ready to merge). This will help in getting visibility for your fix, allow
+others to comment early on the changes and also let others know that you
+are currently working on something.
+
 ## Porting Code From Other Open-Source Projects
 
 `libgit2` is licensed under the terms of the GPL v2 with a linking
@@ -57,14 +69,17 @@
 project, which means that in order to port code to this project, we need the
 explicit permission of the author.  Check the
 [`git.git-authors`](https://github.com/libgit2/libgit2/blob/development/git.git-authors)
-file for authors who have already consented; feel free to add someone if
-you've obtained their consent.
+file for authors who have already consented.
 
 Other licenses have other requirements; check the license of the library
 you're porting code *from* to see what you need to do.  As a general rule,
 MIT and BSD (3-clause) licenses are typically no problem.  Apache 2.0
 license typically doesn't work due to GPL incompatibility.
 
+If you are pulling in code from core Git, another project or code you've pulled from
+a forum / Stack Overflow then please flag this in your PR and also make sure you've
+given proper credit to the original author in the code snippet.
+
 ## Style Guide
 
 `libgit2` is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
diff --git a/COPYING b/COPYING
index d1ca4d4..f7e9f3a 100644
--- a/COPYING
+++ b/COPYING
@@ -928,3 +928,66 @@
   Ty Coon, President of Vice
 
 That's all there is to it!
+
+----------------------------------------------------------------------
+
+Portions of src/win32/posix_w32.c are derrived from link_win32.c in PHP:
+
+-------------------------------------------------------------------- 
+                  The PHP License, version 3.01
+Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
+-------------------------------------------------------------------- 
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+ 
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+ 
+  3. The name "PHP" must not be used to endorse or promote products
+     derived from this software without prior written permission. For
+     written permission, please contact group@php.net.
+  
+  4. Products derived from this software may not be called "PHP", nor
+     may "PHP" appear in their name, without prior written permission
+     from group@php.net.  You may indicate that your software works in
+     conjunction with PHP by saying "Foo for PHP" instead of calling
+     it "PHP Foo" or "phpfoo"
+ 
+  5. The PHP Group may publish revised and/or new versions of the
+     license from time to time. Each version will be given a
+     distinguishing version number.
+     Once covered code has been published under a particular version
+     of the license, you may always continue to use it under the terms
+     of that version. You may also choose to use such covered code
+     under the terms of any subsequent version of the license
+     published by the PHP Group. No one other than the PHP Group has
+     the right to modify the terms applicable to covered code created
+     under this License.
+
+  6. Redistributions of any form whatsoever must retain the following
+     acknowledgment:
+     "This product includes PHP software, freely available from
+     <http://www.php.net/software/>".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
+ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
+PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE PHP
+DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------- 
+
diff --git a/Makefile.embed b/Makefile.embed
index 76b4d3c..eb8a78e 100644
--- a/Makefile.embed
+++ b/Makefile.embed
@@ -1,26 +1,44 @@
-PLATFORM=$(shell uname -o)
+PLATFORM=$(shell uname -s)
+
+ifneq (,$(CROSS_COMPILE))
+	PREFIX=$(CROSS_COMPILE)-
+else
+	PREFIX=
+endif
+
+MINGW=0
+ifneq (,$(findstring MINGW32,$(PLATFORM)))
+	MINGW=1
+endif
+ifneq (,$(findstring mingw,$(CROSS_COMPILE)))
+	MINGW=1
+endif
 
 rm=rm -f
-AR=ar cq
-RANLIB=ranlib
+AR=$(PREFIX)ar cq
+RANLIB=$(PREFIX)ranlib
+
 LIBNAME=libgit2.a
-ifeq ($(PLATFORM),Msys)
+
+ifeq ($(MINGW),1)
 	CC=gcc
 else
 	CC=cc
 endif
 
+CC:=$(PREFIX)$(CC)
+
 INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
 
 DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
-CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS)
+CFLAGS= -g $(DEFINES) -Wall -Wextra -Wno-missing-field-initializers -O2 $(EXTRA_CFLAGS)
 
 SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
 
-ifeq ($(PLATFORM),Msys)
+ifeq ($(MINGW),1)
 	SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c
 	INCLUDES += -Ideps/regex
-	DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501
+	DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501 -D__USE_MINGW_ANSI_STDIO=1
 else
 	SRCS += $(wildcard src/unix/*.c) 
 	CFLAGS += -fPIC
diff --git a/README.md b/README.md
index a2a1876..8ce60af 100644
--- a/README.md
+++ b/README.md
@@ -1,30 +1,36 @@
 libgit2 - the Git linkable library
-======================
+==================================
 
 [![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2)
 
-libgit2 is a portable, pure C implementation of the Git core methods provided as a
+`libgit2` is a portable, pure C implementation of the Git core methods provided as a
 re-entrant linkable library with a solid API, allowing you to write native
 speed custom Git applications in any language with bindings.
 
-libgit2 is licensed under a **very permissive license** (GPLv2 with a special Linking Exception).
-This basically means that you can link it (unmodified) with any kind of software without having to
-release its source code.
+`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
+Linking Exception).  This basically means that you can link it (unmodified)
+with any kind of software without having to release its source code.
+Additionally, the example code has been released to the public domain (see the
+[separate license](examples/COPYING) for more information).
 
-* Mailing list: ~~<libgit2@librelist.org>~~
-    The libgit2 mailing list has
-    traditionally been hosted in Librelist, but Librelist is and has always
-    been a shitshow. We encourage you to [open an issue](https://github.com/libgit2/libgit2/issues)
-    on GitHub instead for any questions regarding the library.
-    * Archives: <http://librelist.com/browser/libgit2/>
-* Website: <http://libgit2.github.com>
+* Website: [libgit2.github.com](http://libgit2.github.com)
+* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
+* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!)
 * API documentation: <http://libgit2.github.com/libgit2>
-* IRC: #libgit2 on irc.freenode.net.
+* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
+* Mailing list: The libgit2 mailing list was
+    traditionally hosted in Librelist but has been deprecated. We encourage you to 
+    [use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
+    the library, or [open an issue](https://github.com/libgit2/libgit2/issues) 
+    on GitHub for bug reports.  The mailing list archives are still available at 
+    <http://librelist.com/browser/libgit2/>.
+
 
 What It Can Do
-==================================
+==============
 
-libgit2 is already very usable.
+`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM 
+and also powering Microsoft's Visual Studio tools for Git.  The library provides:
 
 * SHA conversions, formatting and shortening
 * abstracted ODB backend system
@@ -39,15 +45,26 @@
 * descriptive and detailed error messages
 * ...and more (over 175 different API calls)
 
+Optional dependencies
+=====================
+
+While the library provides git functionality without the need for
+dependencies, it can make use of a few libraries to add to it:
+
+- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
+- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
+- LibSSH2 to enable the ssh transport
+- iconv (OSX) to handle the HFS+ path encoding peculiarities
+
 Building libgit2 - Using CMake
 ==============================
 
-libgit2 builds cleanly on most platforms without any external dependencies.
+`libgit2` builds cleanly on most platforms without any external dependencies.
 Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
 they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
 for threading.
 
-The libgit2 library is built using CMake 2.6+ (<http://www.cmake.org>) on all platforms.
+The `libgit2` library is built using `CMake 2.6+` (<http://www.cmake.org>) on all platforms.
 
 On most systems you can build the library using the following commands
 
@@ -104,6 +121,28 @@
 (https://github.com/libgit2/libgit2/wiki/Building-libgit2-on-Windows)
 for more detailed instructions.
 
+Android
+-------
+
+Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
+Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
+toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
+with full path to the toolchain):
+
+	SET(CMAKE_SYSTEM_NAME Linux)
+	SET(CMAKE_SYSTEM_VERSION Android)
+	
+	SET(CMAKE_C_COMPILER   {PATH}/bin/arm-linux-androideabi-gcc)
+	SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
+	SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
+	
+	SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+	SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+	SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile} -DANDROID=1` to cmake command
+when configuring.
+
 Language Bindings
 ==================================
 
@@ -118,9 +157,9 @@
 * Delphi
     * GitForDelphi <https://github.com/libgit2/GitForDelphi>
 * Erlang
-    * Geef <https://github.com/schacon/geef>
+    * Geef <https://github.com/carlosmn/geef>
 * Go
-    * go-git <https://github.com/str1ngs/go-git>
+    * git2go <https://github.com/libgit2/git2go>
 * GObject
     * libgit2-glib <https://live.gnome.org/Libgit2-glib>
 * Haskell
@@ -128,8 +167,8 @@
 * Lua
     * luagit2 <https://github.com/libgit2/luagit2>
 * .NET
-    * libgit2net, low level bindings <https://github.com/txdv/libgit2net>
     * libgit2sharp <https://github.com/libgit2/libgit2sharp>
+    * libgit2net, low level bindings superseded by libgit2sharp <https://github.com/txdv/libgit2net>
 * Node.js
     * node-gitteh <https://github.com/libgit2/node-gitteh>
     * nodegit <https://github.com/tbranyen/nodegit>
@@ -161,9 +200,9 @@
 
 License
 ==================================
-libgit2 is under GPL2 **with linking exemption**. This means you
-can link to the library with any program, commercial, open source or
-other.  However, you cannot modify libgit2 and distribute it without
+`libgit2` is under GPL2 **with linking exemption**. This means you
+can link to and use the library from any program, proprietary or open source; paid
+or gratis.  However, you cannot modify libgit2 and distribute it without
 supplying the source.
 
 See the COPYING file for the full license text.
diff --git a/docs/checkout-internals.md b/docs/checkout-internals.md
index cb646da..6147ffd 100644
--- a/docs/checkout-internals.md
+++ b/docs/checkout-internals.md
@@ -71,19 +71,19 @@
 Diff with 2 non-workdir iterators
 ---------------------------------
 
-    Old New
-    --- ---
-  0   x   x - nothing
-  1   x  B1 - added blob
-  2   x  T1 - added tree
-  3  B1   x - removed blob
-  4  B1  B1 - unmodified blob
-  5  B1  B2 - modified blob
-  6  B1  T1 - typechange blob -> tree
-  7  T1   x - removed tree
-  8  T1  B1 - typechange tree -> blob
-  9  T1  T1 - unmodified tree
- 10  T1  T2 - modified tree (implies modified/added/removed blob inside)
+|    | Old | New |                                                            |
+|----|-----|-----|------------------------------------------------------------|
+|  0 |   x |   x | nothing                                                    |
+|  1 |   x |  B1 | added blob                                                 |
+|  2 |   x |  T1 | added tree                                                 |
+|  3 |  B1 |   x | removed blob                                               |
+|  4 |  B1 |  B1 | unmodified blob                                            |
+|  5 |  B1 |  B2 | modified blob                                              |
+|  6 |  B1 |  T1 | typechange blob -> tree                                    |
+|  7 |  T1 |   x | removed tree                                               |
+|  8 |  T1 |  B1 | typechange tree -> blob                                    |
+|  9 |  T1 |  T1 | unmodified tree                                            |
+| 10 |  T1 |  T2 | modified tree (implies modified/added/removed blob inside) |
 
 
 Now, let's make the "New" iterator into a working directory iterator, so
@@ -92,23 +92,23 @@
 Diff with non-work & workdir iterators
 --------------------------------------
 
-    Old New-WD
-    --- ------
-  0   x   x - nothing
-  1   x  B1 - untracked blob
-  2   x  Bi - ignored file
-  3   x  T1 - untracked tree
-  4   x  Ti - ignored tree
-  5  B1   x - removed blob
-  6  B1  B1 - unmodified blob
-  7  B1  B2 - modified blob
-  8  B1  T1 - typechange blob -> tree
-  9  B1  Ti - removed blob AND ignored tree as separate items
- 10  T1   x - removed tree
- 11  T1  B1 - typechange tree -> blob
- 12  T1  Bi - removed tree AND ignored blob as separate items
- 13  T1  T1 - unmodified tree
- 14  T1  T2 - modified tree (implies modified/added/removed blob inside)
+|    | Old | New |                                                            |
+|----|-----|-----|------------------------------------------------------------|
+|  0 |  x  | x   | nothing                                                    |
+|  1 |  x  | B1  | untracked blob                                             |
+|  2 |  x  | Bi  | ignored file                                               |
+|  3 |  x  | T1  | untracked tree                                             |
+|  4 |  x  | Ti  | ignored tree                                               |
+|  5 | B1  | x   | removed blob                                               |
+|  6 | B1  | B1  | unmodified blob                                            |
+|  7 | B1  | B2  | modified blob                                              |
+|  8 | B1  | T1  | typechange blob -> tree                                    |
+|  9 | B1  | Ti  | removed blob AND ignored tree as separate items            |
+| 10 | T1  | x   | removed tree                                               |
+| 11 | T1  | B1  | typechange tree -> blob                                    |
+| 12 | T1  | Bi  | removed tree AND ignored blob as separate items            |
+| 13 | T1  | T1  | unmodified tree                                            |
+| 14 | T1  | T2  | modified tree (implies modified/added/removed blob inside) |
 
 Note: if there is a corresponding entry in the old tree, then a working
 directory item won't be ignored (i.e. no Bi or Ti for tracked items).
@@ -122,46 +122,47 @@
 
 (base == old HEAD; target == what to checkout; actual == working dir)
 
-      base target actual/workdir
-      ---- ------ ------
-  0      x      x      x - nothing
-  1      x      x B1/Bi/T1/Ti - untracked/ignored blob/tree (SAFE)
-  2+     x     B1      x - add blob (SAFE)
-  3      x     B1     B1 - independently added blob (FORCEABLE-2)
-  4*     x     B1 B2/Bi/T1/Ti - add blob with content conflict (FORCEABLE-2)
-  5+     x     T1      x - add tree (SAFE)
-  6*     x     T1  B1/Bi - add tree with blob conflict (FORCEABLE-2)
-  7      x     T1   T1/i - independently added tree (SAFE+MISSING)
-  8     B1      x      x - independently deleted blob (SAFE+MISSING)
-  9-    B1      x     B1 - delete blob (SAFE)
- 10-    B1      x     B2 - delete of modified blob (FORCEABLE-1)
- 11     B1      x  T1/Ti - independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!)
- 12     B1     B1      x - locally deleted blob (DIRTY || SAFE+CREATE)
- 13+    B1     B2      x - update to deleted blob (SAFE+MISSING)
- 14     B1     B1     B1 - unmodified file (SAFE)
- 15     B1     B1     B2 - locally modified file (DIRTY)
- 16+    B1     B2     B1 - update unmodified blob (SAFE)
- 17     B1     B2     B2 - independently updated blob (FORCEABLE-1)
- 18+    B1     B2     B3 - update to modified blob (FORCEABLE-1)
- 19     B1     B1  T1/Ti - locally deleted blob AND untrack/ign tree (DIRTY)
- 20*    B1     B2  T1/Ti - update to deleted blob AND untrack/ign tree (F-1)
- 21+    B1     T1      x - add tree with locally deleted blob (SAFE+MISSING)
- 22*    B1     T1     B1 - add tree AND deleted blob (SAFE)
- 23*    B1     T1     B2 - add tree with delete of modified blob (F-1)
- 24     B1     T1     T1 - add tree with deleted blob (F-1)
- 25     T1      x      x - independently deleted tree (SAFE+MISSING)
- 26     T1      x  B1/Bi - independently deleted tree AND untrack/ign blob (F-1)
- 27-    T1      x     T1 - deleted tree (MAYBE SAFE)
- 28+    T1     B1      x - deleted tree AND added blob (SAFE+MISSING)
- 29     T1     B1     B1 - independently typechanged tree -> blob (F-1)
- 30+    T1     B1     B2 - typechange tree->blob with conflicting blob (F-1)
- 31*    T1     B1  T1/T2 - typechange tree->blob (MAYBE SAFE)
- 32+    T1     T1      x - restore locally deleted tree (SAFE+MISSING)
- 33     T1     T1  B1/Bi - locally typechange tree->untrack/ign blob (DIRTY)
- 34     T1     T1  T1/T2 - unmodified tree (MAYBE SAFE)
- 35+    T1     T2      x - update locally deleted tree (SAFE+MISSING)
- 36*    T1     T2  B1/Bi - update to tree with typechanged tree->blob conflict (F-1)
- 37     T1     T2 T1/T2/T3 - update to existing tree (MAYBE SAFE)
+|     |base | target | actual/workdir |                                                                    |
+|-----|-----|------- |----------------|--------------------------------------------------------------------|
+|  0  |   x |      x |      x         | nothing                                                            |
+|  1  |   x |      x | B1/Bi/T1/Ti    | untracked/ignored blob/tree (SAFE)                                 |
+|  2+ |   x |     B1 |      x         | add blob (SAFE)                                                    |
+|  3  |   x |     B1 |     B1         | independently added blob (FORCEABLE-2)                             |
+|  4* |   x |     B1 | B2/Bi/T1/Ti    | add blob with content conflict (FORCEABLE-2)                       |
+|  5+ |   x |     T1 |      x         | add tree (SAFE)                                                    |
+|  6* |   x |     T1 |  B1/Bi         | add tree with blob conflict (FORCEABLE-2)                          |
+|  7  |   x |     T1 |   T1/i         | independently added tree (SAFE+MISSING)                            |
+|  8  |  B1 |      x |      x         | independently deleted blob (SAFE+MISSING)                          |
+|  9- |  B1 |      x |     B1         | delete blob (SAFE)                                                 |
+| 10- |  B1 |      x |     B2         | delete of modified blob (FORCEABLE-1)                              |
+| 11  |  B1 |      x |  T1/Ti         | independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!) |
+| 12  |  B1 |     B1 |      x         | locally deleted blob (DIRTY || SAFE+CREATE)                        |
+| 13+ |  B1 |     B2 |      x         | update to deleted blob (SAFE+MISSING)                              |
+| 14  |  B1 |     B1 |     B1         | unmodified file (SAFE)                                             |
+| 15  |  B1 |     B1 |     B2         | locally modified file (DIRTY)                                      |
+| 16+ |  B1 |     B2 |     B1         | update unmodified blob (SAFE)                                      |
+| 17  |  B1 |     B2 |     B2         | independently updated blob (FORCEABLE-1)                           |
+| 18+ |  B1 |     B2 |     B3         | update to modified blob (FORCEABLE-1)                              |
+| 19  |  B1 |     B1 |  T1/Ti         | locally deleted blob AND untrack/ign tree (DIRTY)                  |
+| 20* |  B1 |     B2 |  T1/Ti         | update to deleted blob AND untrack/ign tree (F-1)                  |
+| 21+ |  B1 |     T1 |      x         | add tree with locally deleted blob (SAFE+MISSING)                  |
+| 22* |  B1 |     T1 |     B1         | add tree AND deleted blob (SAFE)                                   |
+| 23* |  B1 |     T1 |     B2         | add tree with delete of modified blob (F-1)                        |
+| 24  |  B1 |     T1 |     T1         | add tree with deleted blob (F-1)                                   |
+| 25  |  T1 |      x |      x         | independently deleted tree (SAFE+MISSING)                          |
+| 26  |  T1 |      x |  B1/Bi         | independently deleted tree AND untrack/ign blob (F-1)              |
+| 27- |  T1 |      x |     T1         | deleted tree (MAYBE SAFE)                                          |
+| 28+ |  T1 |     B1 |      x         | deleted tree AND added blob (SAFE+MISSING)                         |
+| 29  |  T1 |     B1 |     B1         | independently typechanged tree -> blob (F-1)                       |
+| 30+ |  T1 |     B1 |     B2         | typechange tree->blob with conflicting blob (F-1)                  |
+| 31* |  T1 |     B1 |  T1/T2         | typechange tree->blob (MAYBE SAFE)                                 |
+| 32+ |  T1 |     T1 |      x         | restore locally deleted tree (SAFE+MISSING)                        |
+| 33  |  T1 |     T1 |  B1/Bi         | locally typechange tree->untrack/ign blob (DIRTY)                  |
+| 34  |  T1 |     T1 |  T1/T2         | unmodified tree (MAYBE SAFE)                                       |
+| 35+ |  T1 |     T2 |      x         | update locally deleted tree (SAFE+MISSING)                         |
+| 36* |  T1 |     T2 |  B1/Bi         | update to tree with typechanged tree->blob conflict (F-1)          |
+| 37  |  T1 |     T2 | T1/T2/T3       | update to existing tree (MAYBE SAFE)                               |
+
 
 The number is followed by ' ' if no change is needed or '+' if the case
 needs to write to disk or '-' if something must be deleted and '*' if
@@ -169,34 +170,34 @@
 
 There are four tiers of safe cases:
 
-- SAFE         == completely safe to update
-- SAFE+MISSING == safe except the workdir is missing the expect content
-- MAYBE SAFE   == safe if workdir tree matches (or is missing) baseline
+* SAFE         == completely safe to update
+* SAFE+MISSING == safe except the workdir is missing the expect content
+* MAYBE SAFE   == safe if workdir tree matches (or is missing) baseline
                   content, which is unknown at this point
-- FORCEABLE == conflict unless FORCE is given
-- DIRTY     == no conflict but change is not applied unless FORCE
+* FORCEABLE == conflict unless FORCE is given
+* DIRTY     == no conflict but change is not applied unless FORCE
 
 Some slightly unusual circumstances:
 
-  8 - parent dir is only deleted when file is, so parent will be left if
-      empty even though it would be deleted if the file were present
- 11 - core git does not consider this a conflict but attempts to delete T1
-      and gives "unable to unlink file" error yet does not skip the rest
-      of the operation
- 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
-      dirty (and warning message "D file" is printed), with FORCE, file is
-      restored.
- 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
-      combined, but core git considers this a conflict unless forced.
- 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
-      which are ok on their own, but core git treat this as a conflict.
-      If not forced, this is a conflict.  If forced, this actually doesn't
-      have to write anything and leaves the new blob as an untracked file.
- 32 - This is the only case where the baseline and target values match
-      and yet we will still write to the working directory.  In all other
-      cases, if baseline == target, we don't touch the workdir (it is
-      either already right or is "dirty").  However, since this case also
-      implies that a ?/B1/x case will exist as well, it can be skipped.
+* 8 - parent dir is only deleted when file is, so parent will be left if
+    empty even though it would be deleted if the file were present
+* 11 - core git does not consider this a conflict but attempts to delete T1
+    and gives "unable to unlink file" error yet does not skip the rest
+    of the operation
+* 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
+    dirty (and warning message "D file" is printed), with FORCE, file is
+    restored.
+* 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
+    combined, but core git considers this a conflict unless forced.
+* 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
+    which are ok on their own, but core git treat this as a conflict.
+    If not forced, this is a conflict.  If forced, this actually doesn't
+    have to write anything and leaves the new blob as an untracked file.
+* 32 - This is the only case where the baseline and target values match
+    and yet we will still write to the working directory.  In all other
+    cases, if baseline == target, we don't touch the workdir (it is
+    either already right or is "dirty").  However, since this case also
+    implies that a ?/B1/x case will exist as well, it can be skipped.
 
 Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
 none of them will require making any updates to the working directory.
diff --git a/docs/diff-internals.md b/docs/diff-internals.md
index 53e71f5..cf8ad53 100644
--- a/docs/diff-internals.md
+++ b/docs/diff-internals.md
@@ -45,44 +45,48 @@
 * `git_diff_file_content` is an internal structure that represents the
   data on one side of an item to be diffed; it is an augmented
   `git_diff_file` with more flags and the actual file data.
-** it is created from a repository plus a) a git_diff_file, b) a git_blob,
+
+    * it is created from a repository plus a) a git_diff_file, b) a git_blob,
    or c) raw data and size
-** there are three main operations on git_diff_file_content:
-*** _initialization_ sets up the data structure and does what it can up to,
-    but not including loading and looking at the actual data
-*** _loading_ loads the data, preprocesses it (i.e. applies filters) and
-    potentially analyzes it (to decide if binary)
-*** _free_ releases loaded data and frees any allocated memory
+    * there are three main operations on git_diff_file_content:
+    
+        * _initialization_ sets up the data structure and does what it can up to,
+          but not including loading and looking at the actual data
+        * _loading_ loads the data, preprocesses it (i.e. applies filters) and
+          potentially analyzes it (to decide if binary)
+        * _free_ releases loaded data and frees any allocated memory
 
 * The internal structure of a `git_diff_patch` stores the actual diff
   between a pair of `git_diff_file_content` items
-** it may be "unset" if the items are not diffable
-** "empty" if the items are the same
-** otherwise it will consist of a set of hunks each of which covers some
-   number of lines of context, additions and deletions
-** a patch is created from two git_diff_file_content items
-** a patch is fully instantiated in three phases:
-*** initial creation and initialization
-*** loading of data and preliminary data examination
-*** diffing of data and optional storage of diffs
-** (TBD) if a patch is asked to store the diffs and the size of the diff
-   is significantly smaller than the raw data of the two sides, then the
-   patch may be flattened using a pool of string data
+
+    * it may be "unset" if the items are not diffable
+    * "empty" if the items are the same
+    * otherwise it will consist of a set of hunks each of which covers some
+      number of lines of context, additions and deletions
+    * a patch is created from two git_diff_file_content items
+    * a patch is fully instantiated in three phases:
+    
+        * initial creation and initialization
+        * loading of data and preliminary data examination
+        * diffing of data and optional storage of diffs
+    * (TBD) if a patch is asked to store the diffs and the size of the diff
+      is significantly smaller than the raw data of the two sides, then the
+      patch may be flattened using a pool of string data
 
 * `git_diff_output` is an internal structure that represents an output
   target for a `git_diff_patch`
-** It consists of file, hunk, and line callbacks, plus a payload
-** There is a standard flattened output that can be used for plain text output
-** Typically we use a `git_xdiff_output` which drives the callbacks via the
-   xdiff code taken from core Git.
+    * It consists of file, hunk, and line callbacks, plus a payload
+    * There is a standard flattened output that can be used for plain text output
+    * Typically we use a `git_xdiff_output` which drives the callbacks via the
+      xdiff code taken from core Git.
 
 * `git_diff_driver` is an internal structure that encapsulates the logic
   for a given type of file
-** a driver is looked up based on the name and mode of a file.
-** the driver can then be used to:
-*** determine if a file is binary (by attributes, by git_diff_options
-    settings, or by examining the content)
-*** give you a function pointer that is used to evaluate function context
-    for hunk headers
-** At some point, the logic for getting a filtered version of file content
-   or calculating the OID of a file may be moved into the driver.
+    * a driver is looked up based on the name and mode of a file.
+    * the driver can then be used to:
+        * determine if a file is binary (by attributes, by git_diff_options
+          settings, or by examining the content)
+        * give you a function pointer that is used to evaluate function context
+          for hunk headers
+    * At some point, the logic for getting a filtered version of file content
+      or calculating the OID of a file may be moved into the driver.
diff --git a/docs/error-handling.md b/docs/error-handling.md
index 655afeb..2dbe64a 100644
--- a/docs/error-handling.md
+++ b/docs/error-handling.md
@@ -1,111 +1,270 @@
 Error reporting in libgit2
 ==========================
 
-Error reporting is performed on an explicit `git_error **` argument, which appears at the end of all API calls that can return an error. Yes, this does clutter the API.
+Libgit2 tries to follow the POSIX style: functions return an `int` value
+with 0 (zero) indicating success and negative values indicating an error.
+There are specific negative error codes for each "expected failure"
+(e.g. `GIT_ENOTFOUND` for files that take a path which might be missing)
+and a generic error code (-1) for all critical or non-specific failures
+(e.g. running out of memory or system corruption).
 
-When a function fails, an error is set on the error variable **and** returns one of the generic error codes.
+When a negative value is returned, an error message is also set.  The
+message can be accessed via the `giterr_last` function which will return a
+pointer to a `git_error` structure containing the error message text and
+the class of error (i.e. what part of the library generated the error).
+
+For instance: An object lookup by SHA prefix (`git_object_lookup_prefix`)
+has two expected failure cases: the SHA is not found at all which returns
+`GIT_ENOTFOUND` or the SHA prefix is ambiguous (i.e. two or more objects
+share the prefix) which returns `GIT_EAMBIGUOUS`.  There are any number of
+critical failures (such as a packfile being corrupted, a loose object
+having the wrong access permissions, etc.) all of which will return -1.
+When the object lookup is successful, it will return 0.
+
+If libgit2 was compiled with threads enabled (`-DTHREADSAFE=ON` when using
+CMake), then the error message will be kept in thread-local storage, so it
+will not be modified by other threads.  If threads are not enabled, then
+the error message is in global data.
+
+All of the error return codes, the `git_error` type, the error access
+functions, and the error classes are defined in `include/git2/errors.h`.
+See the documentation there for details on the APIs for accessing,
+clearing, and even setting error codes.
+
+When writing libgit2 code, please be smart and conservative when returning
+error codes.  Functions usually have a maximum of two or three "expected
+errors" and in most cases only one.  If you feel there are more possible
+expected error scenarios, then the API you are writing may be at too high
+a level for core libgit2.
+
+Example usage
+-------------
+
+When using libgit2, you will typically capture the return value from
+functions using an `int` variable and check to see if it is negative.
+When that happens, you can, if you wish, look at the specific value or
+look at the error message that was generated.
 
 ~~~c
-int git_repository_open(git_repository **repository, const char *path, git_error **error)
 {
-	// perform some opening
-	if (p_exists(path) < 0) {
-		giterr_set(error, GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
-		return GIT_ENOTFOUND;
+	git_repository *repo;
+	int error = git_repository_open(&repo, "path/to/repo");
+
+	if (error < 0) {
+		fprintf(stderr, "Could not open repository: %s\n", giterr_last()->message);
+		exit(1);
 	}
 
-	...
+	... use `repo` here ...
 
-	if (try_to_parse(path, error) < 0)
-		return GIT_ERROR;
+	git_repository_free(repo); /* void function - no error return code */
+}
+~~~
+
+Some of the error return values do have meaning.  Optionally, you can look
+at the specific error values to decide what to do.
+
+~~~c
+{
+	git_repository *repo;
+	const char *path = "path/to/repo";
+	int error = git_repository_open(&repo, path);
+
+	if (error < 0) {
+		if (error == GIT_ENOTFOUND)
+			fprintf(stderr, "Could not find repository at path '%s'\n", path);
+		else
+			fprintf(stderr, "Unable to open repository: %s\n",
+				giterr_last()->message);
+		exit(1);
+	}
+
+	... happy ...
+}
+~~~
+
+Some of the higher-level language bindings may use a range of information
+from libgit2 to convert error return codes into exceptions, including the
+specific error return codes and even the class of error and the error
+message returned by `giterr_last`, but the full range of that logic is
+beyond the scope of this document.
+
+Example internal implementation
+-------------------------------
+
+Internally, libgit2 detects error scenarios, records error messages, and
+returns error values.  Errors from low-level functions are generally
+passed upwards (unless the higher level can either handle the error or
+wants to translate the error into something more meaningful).
+
+~~~c
+int git_repository_open(git_repository **repository, const char *path)
+{
+	/* perform some logic to open the repository */
+	if (p_exists(path) < 0) {
+		giterr_set(GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
+		return GIT_ENOTFOUND;
+	}
 
 	...
 }
 ~~~
 
-The simple error API
+The public error API
 --------------------
 
-- `void giterr_set(git_error **, int, const char *, ...)`: the main function used to set an error. It allocates a new error object and stores it in the passed error pointer. It has no return value. The arguments for `giterr_set` are as follows:
+- `const git_error *giterr_last(void)`: The main function used to look up
+  the last error.  This may return NULL if no error has occurred.
+  Otherwise this should return a `git_error` object indicating the class
+  of error and the error message that was generated by the library.
 
-	- `git_error **error_ptr`: the pointer where the error will be created.
-	- `int error_class`: the class for the error. This is **not** an error code: this is an specific enum that specifies the error family. The point is to map these families 1-1 with Exception types on higher level languages (e.g. GitRepositoryException)
-	- `const char *error_str, ...`: the error string, with optional formatting arguments
+  The last error is stored in thread-local storage when libgit2 is
+  compiled with thread support, so you do not have to worry about another
+  thread overwriting the value.  When thread support is off, the last
+  error is a global value.
 
-- `void giterr_free(git_error *)`: takes an error and frees it. This function is available in the external API.
+  _Note_ There are some known bugs in the library where this may return
+  NULL even when an error code was generated.  Please report these as
+  bugs, but in the meantime, please code defensively and check for NULL
+  when calling this function.
 
-- `void giterr_clear(git_error **)`: clears an error previously set in an error pointer, setting it to NULL and calling `giterr_free` on it.
+- `void geterr_clear(void)`: This function clears the last error.  The
+  library will call this when an error is generated by low level function
+  and the higher level function handles the error.
 
-- `void giterr_propagate(git_error **, git_error *)`: moves an error to a given error pointer, handling the case when the error pointer is NULL (in that case the error gets freed, because it cannot be propagated).
+  _Note_ There are some known bugs in the library where a low level
+  function's error message is not cleared by higher level code that
+  handles the error and returns zero.  Please report these as bugs, but in
+  the meantime, a zero return value from a libgit2 API does not guarantee
+  that `giterr_last()` will return NULL.
 
-The new error code return values
---------------------------------
+- `void giterr_set_str(int error_class, const char *message)`: This
+  function can be used when writing a custom backend module to set the
+  libgit2 error message.  See the documentation on this function for its
+  use.  Normal usage of libgit2 will probably never need to call this API.
 
-We are doing this the POSIX way: one error code for each "expected failure", and a generic error code for all the critical failures.
+- `void giterr_set_oom(void)`: This is a standard function for reporting
+  an out-of-memory error.  It is written in a manner that it doesn't have
+  to allocate any extra memory in order to record the error, so this is
+  the best way to report that scenario.
 
-For instance: A reference lookup can have an expected failure (which is when the reference cannot be found), and a critical failure (which could be any of a long list of things that could go wrong, such as the refs packfile being corrupted, a loose ref being written with the wrong permissions, etc). We cannot have distinct error codes for every single error in the library, hence `git_reference_lookup` would return GIT_SUCCESS if the operation was successful, GIT_ENOTFOUND when the reference doesn't exist, and GIT_ERROR when an error happens -- **the error is then detailed in the `git_error` parameter**.
+Deviations from the standard
+----------------------------
 
-Please be smart when returning error codes. Functions have max two "expected errors", and in most cases only one.
+There are some public functions that do not return `int` values.  There
+are two primary cases:
+
+* `void` return values: If a function has a `void` return, then it will
+  never fail.  This primary will be used for object destructors.
+
+* `git_xyz *` return values: These are simple accessor functions where the
+  only meaningful error would typically be looking something up by index
+  and having the index be out of bounds.  In those cases, the function
+  will typically return NULL.
+
+* Boolean return values: There are some cases where a function cannot fail
+  and wants to return a boolean value.  In those cases, we try to return 1
+  for true and 0 for false.  These cases are rare and the return value for
+  the function should probably be an `unsigned int` to denote these cases.
+  If you find an exception, please open an issue and let's fix it.
+
+There are a few other exceptions to these rules here and there in the
+library, but those are extremely rare and should probably be converted
+over to other to more standard patterns for usage.  Feel free to open
+issues pointing these out.
+
+There are some known bugs in the library where some functions may return a
+negative value but not set an error message and some other functions may
+return zero (no error) and yet leave an error message set.  Please report
+these cases as issues and they will be fixed.  In the meanwhile, please
+code defensively, checking that the return value of `giterr_last` is not
+NULL before using it, and not relying on `giterr_last` to return NULL when
+a function returns 0 for success.
+
+The internal error API
+----------------------
+
+- `void giterr_set(int error_class, const char *fmt, ...)`: This is the
+  main internal function for setting an error.  It works like `printf` to
+  format the error message.  See the notes of `giterr_set_str` for a
+  general description of how error messages are stored (and also about
+  special handling for `error_class` of `GITERR_OS`).
 
 Writing error messages
 ----------------------
 
 Here are some guidelines when writing error messages:
 
-- Use proper English, and an impersonal or past tenses: *The given path does not exist*, *Failed to lookup object in ODB*
+- Use proper English, and an impersonal or past tenses: *The given path
+  does not exist*, *Failed to lookup object in ODB*
 
-- Use short, direct and objective messages. **One line, max**. libgit2 is a low level library: think that all the messages reported will be thrown as Ruby or Python exceptions. Think how long are common exception messages in those languages.
+- Use short, direct and objective messages. **One line, max**. libgit2 is
+  a low level library: think that all the messages reported will be thrown
+  as Ruby or Python exceptions. Think how long are common exception
+  messages in those languages.
 
-- **Do not add redundant information to the error message**, specially information that can be inferred from the context.
+- **Do not add redundant information to the error message**, specially
+  information that can be inferred from the context.
 
-	E.g. in `git_repository_open`, do not report a message like "Failed to open repository: path not found". Somebody is
-	calling that function. If it fails, he already knows that the repository failed to open!
+	E.g. in `git_repository_open`, do not report a message like "Failed to
+	open repository: path not found". Somebody is calling that
+	function. If it fails, they already know that the repository failed to
+	open!
 
 General guidelines for error reporting
 --------------------------------------
 
-- We never handle programming errors with these functions. Programming errors are `assert`ed, and when their source is internal, fixed as soon as possible. This is C, people.
+- Libgit2 does not handle programming errors with these
+  functions. Programming errors are `assert`ed, and when their source is
+  internal, fixed as soon as possible. This is C, people.
 
-	Example of programming errors that would **not** be handled: passing NULL to a function that expects a valid pointer; passing a `git_tree` to a function that expects a `git_commit`. All these cases need to be identified with `assert` and fixed asap.
+	Example of programming errors that would **not** be handled: passing
+    NULL to a function that expects a valid pointer; passing a `git_tree`
+    to a function that expects a `git_commit`. All these cases need to be
+    identified with `assert` and fixed asap.
 
-	Example of a runtime error: failing to parse a `git_tree` because it contains invalid data. Failing to open a file because it doesn't exist on disk. These errors would be handled, and a `git_error` would be set.
+	Example of a runtime error: failing to parse a `git_tree` because it
+    contains invalid data. Failing to open a file because it doesn't exist
+    on disk. These errors are handled, a meaningful error message is set,
+    and an error code is returned.
 
-- The `git_error **` argument is always the last in the signature of all API calls. No exceptions.
+- In general, *do not* try to overwrite errors internally and *do*
+  propagate error codes from lower level functions to the higher level.
+  There are some cases where propagating an error code will be more
+  confusing rather than less, so there are some exceptions to this rule,
+  but the default behavior should be to simply clean up and pass the error
+  on up to the caller.
 
-- When the programmer (or us, internally) doesn't need error handling, he can pass `NULL` to the `git_error **` param. This means that the errors won't be *reported*, but obviously they still will be handled (i.e. the failing function will interrupt and return cleanly). This is transparently handled by `giterr_set`
-
-- `git_error *` **must be initialized to `NULL` before passing its value to a function!!**
+    **WRONG**
 
 	~~~c
-	git_error *err;
-	git_error *good_error = NULL;
-
-	git_foo_func(arg1, arg2, &error); // invalid: `error` is not initialized
-	git_foo_func2(arg1, arg2, &good_error); // OK!
-	git_foo_func3(arg1, arg2, NULL); // OK! But no error reporting!
-	~~~
-
-- Piling up errors is an error! Don't do this! Errors must always be free'd when a function returns.
-
-	~~~c
-	git_error *error = NULL;
-
-	git_foo_func1(arg1, &error);
-	git_foo_func2(arg2, &error); // WRONG! What if func1 failed? `error` would leak!
-	~~~
-
-- Likewise: do not rethrow errors internally!
-
-	~~~c
-	int git_commit_create(..., git_error **error)
+	int git_commit_parent(...)
 	{
-		if (git_reference_exists("HEAD", error) < 0) {
-			/* HEAD does not exist; create it so we can commit... */
-			if (git_reference_create("HEAD", error) < 0) {
-				/* error could be rethrown */
-			}
+		...
+
+		if (git_commit_lookup(parent, repo, parent_id) < 0) {
+			giterr_set(GITERR_COMMIT, "Overwrite lookup error message");
+			return -1; /* mask error code */
 		}
 
-- Remember that errors are now allocated, and hence they need to be free'd after they've been used. Failure to do so internally (e.g. in the already seen examples of error piling) will be reported by Valgrind, so we can easily find where are we rethrowing errors.
+		...
+	}
+	~~~
 
-- Remember that any function that fails **will set an error object**, and that object will be freed.
+	**RIGHT**
+
+	~~~c
+	int git_commit_parent(...)
+	{
+		...
+
+		error = git_commit_lookup(parent, repo, parent_id);
+		if (error < 0) {
+			/* cleanup intermediate objects if necessary */
+			/* leave error message and propagate error code */
+			return error;
+		}
+
+		...
+	}
+	~~~
diff --git a/examples/.gitignore b/examples/.gitignore
index e8e0820..7114939 100644
--- a/examples/.gitignore
+++ b/examples/.gitignore
@@ -2,4 +2,10 @@
 showindex
 diff
 rev-list
+blame
+cat-file
+init
+log
+rev-parse
+status
 *.dSYM
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..596be45
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,16 @@
+FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
+ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
+IF(WIN32 OR ANDROID)
+	TARGET_LINK_LIBRARIES(cgit2 git2)
+ELSE()
+	TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
+ENDIF()
+
+FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
+FOREACH(src_app ${SRC_EXAMPLE_APPS})
+	STRING(REPLACE ".c" "" app_name ${src_app})
+	IF(NOT ${app_name} STREQUAL "common")
+		ADD_EXECUTABLE(${app_name} ${src_app} "common.c")
+		TARGET_LINK_LIBRARIES(${app_name} git2)
+	ENDIF()
+ENDFOREACH()
diff --git a/examples/COPYING b/examples/COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/examples/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
diff --git a/examples/Makefile b/examples/Makefile
index 140cc4d..2e7f68f 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -3,12 +3,12 @@
 CC = gcc
 CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
 LFLAGS = -L../build -lgit2 -lz
-APPS = general showindex diff rev-list cat-file status
+APPS = general showindex diff rev-list cat-file status log rev-parse init blame
 
 all: $(APPS)
 
 % : %.c
-	$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
+	$(CC) -o $@ common.c $(CFLAGS) $< $(LFLAGS)
 
 clean:
 	$(RM) $(APPS)
diff --git a/examples/README.md b/examples/README.md
index f2b6d7d..769c4b2 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -1,11 +1,22 @@
 libgit2 examples
 ================
 
-These examples are meant as thin, easy-to-read snippets for Docurium
-(https://github.com/github/docurium) rather than full-blown
-implementations of Git commands.  They are not vetted as carefully
-for bugs, error handling, or cross-platform compatibility as the
-rest of the code in libgit2, so copy with some caution.
+These examples are a mixture of basic emulation of core Git command line
+functions and simple snippets demonstrating libgit2 API usage (for use
+with Docurium).  As a whole, they are not vetted carefully for bugs, error
+handling, and cross-platform compatibility in the same manner as the rest
+of the code in libgit2, so copy with caution.
 
-For HTML versions, check "Examples" at http://libgit2.github.com/libgit2
+That being said, you are welcome to copy code from these examples as
+desired when using libgit2. They have been [released to the public domain][cc0],
+so there are no restrictions on their use.
 
+[cc0]: COPYING
+
+For annotated HTML versions, see the "Examples" section of:
+
+    http://libgit2.github.com/libgit2
+
+such as:
+
+    http://libgit2.github.com/libgit2/ex/HEAD/general.html
diff --git a/examples/add.c b/examples/add.c
new file mode 100644
index 0000000..336596b
--- /dev/null
+++ b/examples/add.c
@@ -0,0 +1,159 @@
+/*
+ * libgit2 "add" example - shows how to modify the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+#include <assert.h>
+
+enum print_options {
+	SKIP = 1,
+	VERBOSE = 2,
+	UPDATE = 4,
+};
+
+struct print_payload {
+	enum print_options options;
+	git_repository *repo;
+};
+
+/* Forward declarations for helpers */
+static void parse_opts(int *options, int *count, int argc, char *argv[]);
+void init_array(git_strarray *array, int argc, char **argv);
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload);
+
+int main (int argc, char** argv)
+{
+	git_index_matched_path_cb matched_cb = NULL;
+	git_repository *repo = NULL;
+	git_index *index;
+	git_strarray array = {0};
+	int options = 0, count = 0;
+	struct print_payload payload = {0};
+
+	git_threads_init();
+
+	parse_opts(&options, &count, argc, argv);
+
+	init_array(&array, argc-count, argv+count);
+
+	check_lg2(git_repository_open(&repo, "."), "No git repository", NULL);
+	check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
+
+	if (options&VERBOSE || options&SKIP) {
+		matched_cb = &print_matched_cb;
+	}
+
+	payload.options = options;
+	payload.repo = repo;
+
+	if (options&UPDATE) {
+		git_index_update_all(index, &array, matched_cb, &payload);
+	} else {
+		git_index_add_all(index, &array, 0, matched_cb, &payload);
+	}
+
+	git_index_write(index);
+	git_index_free(index);
+	git_repository_free(repo);
+
+	git_threads_shutdown();
+
+	return 0;
+}
+
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
+{
+	struct print_payload p = *(struct print_payload*)(payload);
+	int ret;
+	git_status_t status;
+	(void)matched_pathspec;
+
+	if (git_status_file(&status, p.repo, path)) {
+		return -1; //abort
+	}
+
+	if (status & GIT_STATUS_WT_MODIFIED ||
+	         status & GIT_STATUS_WT_NEW) {
+		printf("add '%s'\n", path);
+		ret = 0;
+	} else {
+		ret = 1;
+	}
+
+	if(p.options & SKIP) {
+		ret = 1;
+	}
+
+	return ret;
+}
+
+void init_array(git_strarray *array, int argc, char **argv)
+{
+	unsigned int i;
+
+	array->count = argc;
+	array->strings = malloc(sizeof(char*) * array->count);
+	assert(array->strings!=NULL);
+
+	for(i=0; i<array->count; i++) {
+		array->strings[i]=argv[i];
+	}
+
+	return;
+}
+
+void print_usage(void)
+{
+	fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
+	fprintf(stderr, "\t-n, --dry-run    dry run\n");
+	fprintf(stderr, "\t-v, --verbose    be verbose\n");
+	fprintf(stderr, "\t-u, --update     update tracked files\n");
+	exit(1);
+}
+
+static void parse_opts(int *options, int *count, int argc, char *argv[])
+{
+	int i;
+
+	for (i = 1; i < argc; ++i) {
+		if (argv[i][0] != '-') {
+			break;
+		}
+		else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
+			*options |= VERBOSE;
+		}
+		else if(!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) {
+			*options |= SKIP;
+		}
+		else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
+			*options |= UPDATE;
+		}
+		else if(!strcmp(argv[i], "-h")) {
+			print_usage();
+			break;
+		}
+		else if(!strcmp(argv[i], "--")) {
+			i++;
+			break;
+		}
+		else {
+			fprintf(stderr, "Unsupported option %s.\n", argv[i]);
+			print_usage();
+		}
+	}
+
+	if (argc<=i)
+		print_usage();
+
+	*count = i;
+}
diff --git a/examples/blame.c b/examples/blame.c
new file mode 100644
index 0000000..06310d5
--- /dev/null
+++ b/examples/blame.c
@@ -0,0 +1,199 @@
+/*
+ * libgit2 "blame" example - shows how to use the blame API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates how to invoke the libgit2 blame API to roughly
+ * simulate the output of `git blame` and a few of its command line arguments.
+ */
+
+struct opts {
+	char *path;
+	char *commitspec;
+	int C;
+	int M;
+	int start_line;
+	int end_line;
+};
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+int main(int argc, char *argv[])
+{
+	int i, line, break_on_null_hunk;
+	char spec[1024] = {0};
+	struct opts o = {0};
+	const char *rawdata;
+	git_repository *repo = NULL;
+	git_revspec revspec = {0};
+	git_blame_options blameopts = GIT_BLAME_OPTIONS_INIT;
+	git_blame *blame = NULL;
+	git_blob *blob;
+	git_object *obj;
+
+	git_threads_init();
+
+	parse_opts(&o, argc, argv);
+	if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+	if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+
+	/** Open the repository. */
+	check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL);
+
+	/**
+	 * The commit range comes in "commitish" form. Use the rev-parse API to
+	 * nail down the end points.
+	 */
+	if (o.commitspec) {
+		check_lg2(git_revparse(&revspec, repo, o.commitspec), "Couldn't parse commit spec", NULL);
+		if (revspec.flags & GIT_REVPARSE_SINGLE) {
+			git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.from));
+			git_object_free(revspec.from);
+		} else {
+			git_oid_cpy(&blameopts.oldest_commit, git_object_id(revspec.from));
+			git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.to));
+			git_object_free(revspec.from);
+			git_object_free(revspec.to);
+		}
+	}
+
+	/** Run the blame. */
+	check_lg2(git_blame_file(&blame, repo, o.path, &blameopts), "Blame error", NULL);
+
+	/**
+	 * Get the raw data inside the blob for output. We use the
+	 * `commitish:path/to/file.txt` format to find it.
+	 */
+	if (git_oid_iszero(&blameopts.newest_commit))
+		strcpy(spec, "HEAD");
+	else
+		git_oid_tostr(spec, sizeof(spec), &blameopts.newest_commit);
+	strcat(spec, ":");
+	strcat(spec, o.path);
+
+	check_lg2(git_revparse_single(&obj, repo, spec), "Object lookup error", NULL);
+	check_lg2(git_blob_lookup(&blob, repo, git_object_id(obj)), "Blob lookup error", NULL);
+	git_object_free(obj);
+
+	rawdata = git_blob_rawcontent(blob);
+
+	/** Produce the output. */
+	line = 1;
+	i = 0;
+	break_on_null_hunk = 0;
+	while (i < git_blob_rawsize(blob)) {
+		const char *eol = strchr(rawdata+i, '\n');
+		char oid[10] = {0};
+		const git_blame_hunk *hunk = git_blame_get_hunk_byline(blame, line);
+
+		if (break_on_null_hunk && !hunk) break;
+
+		if (hunk) {
+			break_on_null_hunk = 1;
+			char sig[128] = {0};
+
+			git_oid_tostr(oid, 10, &hunk->final_commit_id);
+			snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email);
+
+			printf("%s ( %-30s %3d) %.*s\n",
+					oid,
+					sig,
+					line,
+					(int)(eol-rawdata-i),
+					rawdata+i);
+		}
+
+		i = (int)(eol - rawdata + 1);
+		line++;
+	}
+
+	/** Cleanup. */
+	git_blob_free(blob);
+	git_blame_free(blame);
+	git_repository_free(repo);
+
+	git_threads_shutdown();
+
+	return 0;
+}
+
+/** Tell the user how to make this thing work. */
+static void usage(const char *msg, const char *arg)
+{
+	if (msg && arg)
+		fprintf(stderr, "%s: %s\n", msg, arg);
+	else if (msg)
+		fprintf(stderr, "%s\n", msg);
+	fprintf(stderr, "usage: blame [options] [<commit range>] <path>\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "   <commit range>      example: `HEAD~10..HEAD`, or `1234abcd`\n");
+	fprintf(stderr, "   -L <n,m>            process only line range n-m, counting from 1\n");
+	fprintf(stderr, "   -M                  find line moves within and across files\n");
+	fprintf(stderr, "   -C                  find line copies within and across files\n");
+	fprintf(stderr, "\n");
+	exit(1);
+}
+
+/** Parse the arguments. */
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+	int i;
+	char *bare_args[3] = {0};
+
+	if (argc < 2) usage(NULL, NULL);
+
+	for (i=1; i<argc; i++) {
+		char *a = argv[i];
+
+		if (a[0] != '-') {
+			int i=0;
+			while (bare_args[i] && i < 3) ++i;
+			if (i >= 3)
+				usage("Invalid argument set", NULL);
+			bare_args[i] = a;
+		}
+		else if (!strcmp(a, "--"))
+			continue;
+		else if (!strcasecmp(a, "-M"))
+			o->M = 1;
+		else if (!strcasecmp(a, "-C"))
+			o->C = 1;
+		else if (!strcasecmp(a, "-L")) {
+			i++; a = argv[i];
+			if (i >= argc) fatal("Not enough arguments to -L", NULL);
+			check_lg2(sscanf(a, "%d,%d", &o->start_line, &o->end_line)-2, "-L format error", NULL);
+		}
+		else {
+			/* commit range */
+			if (o->commitspec) fatal("Only one commit spec allowed", NULL);
+			o->commitspec = a;
+		}
+	}
+
+	/* Handle the bare arguments */
+	if (!bare_args[0]) usage("Please specify a path", NULL);
+	o->path = bare_args[0];
+	if (bare_args[1]) {
+		/* <commitspec> <path> */
+		o->path = bare_args[1];
+		o->commitspec = bare_args[0];
+	}
+	if (bare_args[2]) {
+		/* <oldcommit> <newcommit> <path> */
+		char spec[128] = {0};
+		o->path = bare_args[2];
+		sprintf(spec, "%s..%s", bare_args[0], bare_args[1]);
+		o->commitspec = spec;
+	}
+}
diff --git a/examples/cat-file.c b/examples/cat-file.c
index ebb6cb0..fa6add0 100644
--- a/examples/cat-file.c
+++ b/examples/cat-file.c
@@ -1,37 +1,18 @@
-#include <stdio.h>
-#include <git2.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * libgit2 "cat-file" example - shows how to print data from the ODB
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
 
-static git_repository *g_repo;
-
-static void check(int error, const char *message)
-{
-	if (error) {
-		fprintf(stderr, "%s (%d)\n", message, error);
-		exit(1);
-	}
-}
-
-static void usage(const char *message, const char *arg)
-{
-	if (message && arg)
-		fprintf(stderr, "%s: %s\n", message, arg);
-	else if (message)
-		fprintf(stderr, "%s\n", message);
-	fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [<options>] <object>\n");
-	exit(1);
-}
-
-static int check_str_param(
-	const char *arg, const char *pattern, const char **val)
-{
-	size_t len = strlen(pattern);
-	if (strncmp(arg, pattern, len))
-		return 0;
-	*val = (const char *)(arg + len);
-	return 1;
-}
+#include "common.h"
 
 static void print_signature(const char *header, const git_signature *sig)
 {
@@ -57,12 +38,14 @@
 		   sign, hours, minutes);
 }
 
+/** Printing out a blob is simple, get the contents and print */
 static void show_blob(const git_blob *blob)
 {
 	/* ? Does this need crlf filtering? */
 	fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout);
 }
 
+/** Show each entry with its type, id and attributes */
 static void show_tree(const git_tree *tree)
 {
 	size_t i, max_i = (int)git_tree_entrycount(tree);
@@ -81,6 +64,9 @@
 	}
 }
 
+/**
+ * Commits and tags have a few interesting fields in their header.
+ */
 static void show_commit(const git_commit *commit)
 {
 	unsigned int i, max_i;
@@ -123,53 +109,34 @@
 	SHOW_PRETTY = 4
 };
 
+/* Forward declarations for option-parsing helper */
+struct opts {
+	const char *dir;
+	const char *rev;
+	int action;
+	int verbose;
+};
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+
+/** Entry point for this command */
 int main(int argc, char *argv[])
 {
-	const char *dir = ".", *rev = NULL;
-	int i, action = 0, verbose = 0;
+	git_repository *repo;
+	struct opts o = { ".", NULL, 0, 0 };
 	git_object *obj = NULL;
 	char oidstr[GIT_OID_HEXSZ + 1];
 
 	git_threads_init();
 
-	for (i = 1; i < argc; ++i) {
-		char *a = argv[i];
+	parse_opts(&o, argc, argv);
 
-		if (a[0] != '-') {
-			if (rev != NULL)
-				usage("Only one rev should be provided", NULL);
-			else
-				rev = a;
-		}
-		else if (!strcmp(a, "-t"))
-			action = SHOW_TYPE;
-		else if (!strcmp(a, "-s"))
-			action = SHOW_SIZE;
-		else if (!strcmp(a, "-e"))
-			action = SHOW_NONE;
-		else if (!strcmp(a, "-p"))
-			action = SHOW_PRETTY;
-		else if (!strcmp(a, "-q"))
-			verbose = 0;
-		else if (!strcmp(a, "-v"))
-			verbose = 1;
-		else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
-			usage(NULL, NULL);
-		else if (!check_str_param(a, "--git-dir=", &dir))
-			usage("Unknown option", a);
-	}
+	check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
+			"Could not open repository", NULL);
+	check_lg2(git_revparse_single(&obj, repo, o.rev),
+			"Could not resolve", o.rev);
 
-	if (!action || !rev)
-		usage(NULL, NULL);
-
-	check(git_repository_open_ext(&g_repo, dir, 0, NULL),
-		"Could not open repository");
-
-	if (git_revparse_single(&obj, g_repo, rev) < 0) {
-		fprintf(stderr, "Could not resolve '%s'\n", rev);
-		exit(1);
-	}
-	if (verbose) {
+	if (o.verbose) {
 		char oidstr[GIT_OID_HEXSZ + 1];
 		git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
 
@@ -177,7 +144,7 @@
 			git_object_type2string(git_object_type(obj)), oidstr);
 	}
 
-	switch (action) {
+	switch (o.action) {
 	case SHOW_TYPE:
 		printf("%s\n", git_object_type2string(git_object_type(obj)));
 		break;
@@ -185,9 +152,9 @@
 		git_odb *odb;
 		git_odb_object *odbobj;
 
-		check(git_repository_odb(&odb, g_repo), "Could not open ODB");
-		check(git_odb_read(&odbobj, odb, git_object_id(obj)),
-			"Could not find obj");
+		check_lg2(git_repository_odb(&odb, repo), "Could not open ODB", NULL);
+		check_lg2(git_odb_read(&odbobj, odb, git_object_id(obj)),
+			"Could not find obj", NULL);
 
 		printf("%ld\n", (long)git_odb_object_size(odbobj));
 
@@ -221,9 +188,59 @@
 	}
 
 	git_object_free(obj);
-	git_repository_free(g_repo);
+	git_repository_free(repo);
 
 	git_threads_shutdown();
 
 	return 0;
 }
+
+/** Print out usage information */
+static void usage(const char *message, const char *arg)
+{
+	if (message && arg)
+		fprintf(stderr, "%s: %s\n", message, arg);
+	else if (message)
+		fprintf(stderr, "%s\n", message);
+	fprintf(stderr,
+			"usage: cat-file (-t | -s | -e | -p) [-v] [-q] "
+			"[-h|--help] [--git-dir=<dir>] <object>\n");
+	exit(1);
+}
+
+/** Parse the command-line options taken from git */
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+	struct args_info args = ARGS_INFO_INIT;
+
+	for (args.pos = 1; args.pos < argc; ++args.pos) {
+		char *a = argv[args.pos];
+
+		if (a[0] != '-') {
+			if (o->rev != NULL)
+				usage("Only one rev should be provided", NULL);
+			else
+				o->rev = a;
+		}
+		else if (!strcmp(a, "-t"))
+			o->action = SHOW_TYPE;
+		else if (!strcmp(a, "-s"))
+			o->action = SHOW_SIZE;
+		else if (!strcmp(a, "-e"))
+			o->action = SHOW_NONE;
+		else if (!strcmp(a, "-p"))
+			o->action = SHOW_PRETTY;
+		else if (!strcmp(a, "-q"))
+			o->verbose = 0;
+		else if (!strcmp(a, "-v"))
+			o->verbose = 1;
+		else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
+			usage(NULL, NULL);
+		else if (!match_str_arg(&o->dir, &args, "--git-dir"))
+			usage("Unknown option", a);
+	}
+
+	if (!o->action || !o->rev)
+		usage(NULL, NULL);
+
+}
diff --git a/examples/common.c b/examples/common.c
new file mode 100644
index 0000000..12dbccf
--- /dev/null
+++ b/examples/common.c
@@ -0,0 +1,191 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+void check_lg2(int error, const char *message, const char *extra)
+{
+	const git_error *lg2err;
+	const char *lg2msg = "", *lg2spacer = "";
+
+	if (!error)
+		return;
+
+	if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
+		lg2msg = lg2err->message;
+		lg2spacer = " - ";
+	}
+
+	if (extra)
+		fprintf(stderr, "%s '%s' [%d]%s%s\n",
+			message, extra, error, lg2spacer, lg2msg);
+	else
+		fprintf(stderr, "%s [%d]%s%s\n",
+			message, error, lg2spacer, lg2msg);
+
+	exit(1);
+}
+
+void fatal(const char *message, const char *extra)
+{
+	if (extra)
+		fprintf(stderr, "%s %s\n", message, extra);
+	else
+		fprintf(stderr, "%s\n", message);
+
+	exit(1);
+}
+
+size_t is_prefixed(const char *str, const char *pfx)
+{
+	size_t len = strlen(pfx);
+	return strncmp(str, pfx, len) ? 0 : len;
+}
+
+int match_str_arg(
+	const char **out, struct args_info *args, const char *opt)
+{
+	const char *found = args->argv[args->pos];
+	size_t len = is_prefixed(found, opt);
+
+	if (!len)
+		return 0;
+
+	if (!found[len]) {
+		if (args->pos + 1 == args->argc)
+			fatal("expected value following argument", opt);
+		args->pos += 1;
+		*out = args->argv[args->pos];
+		return 1;
+	}
+
+	if (found[len] == '=') {
+		*out = found + len + 1;
+		return 1;
+	}
+
+	return 0;
+}
+
+static const char *match_numeric_arg(struct args_info *args, const char *opt)
+{
+	const char *found = args->argv[args->pos];
+	size_t len = is_prefixed(found, opt);
+
+	if (!len)
+		return NULL;
+
+	if (!found[len]) {
+		if (args->pos + 1 == args->argc)
+			fatal("expected numeric value following argument", opt);
+		args->pos += 1;
+		found = args->argv[args->pos];
+	} else {
+		found = found + len;
+		if (*found == '=')
+			found++;
+	}
+
+	return found;
+}
+
+int match_uint16_arg(
+	uint16_t *out, struct args_info *args, const char *opt)
+{
+	const char *found = match_numeric_arg(args, opt);
+	uint16_t val;
+	char *endptr = NULL;
+
+	if (!found)
+		return 0;
+
+	val = (uint16_t)strtoul(found, &endptr, 0);
+	if (!endptr || *endptr != '\0')
+		fatal("expected number after argument", opt);
+
+	if (out)
+		*out = val;
+	return 1;
+}
+
+static int match_int_internal(
+	int *out, const char *str, int allow_negative, const char *opt)
+{
+	char *endptr = NULL;
+	int	  val = (int)strtol(str, &endptr, 10);
+
+	if (!endptr || *endptr != '\0')
+		fatal("expected number", opt);
+	else if (val < 0 && !allow_negative)
+		fatal("negative values are not allowed", opt);
+
+	if (out)
+		*out = val;
+
+	return 1;
+}
+
+int is_integer(int *out, const char *str, int allow_negative)
+{
+	return match_int_internal(out, str, allow_negative, NULL);
+}
+
+int match_int_arg(
+	int *out, struct args_info *args, const char *opt, int allow_negative)
+{
+	const char *found = match_numeric_arg(args, opt);
+	if (!found)
+		return 0;
+	return match_int_internal(out, found, allow_negative, opt);
+}
+
+int diff_output(
+	const git_diff_delta *d,
+	const git_diff_hunk *h,
+	const git_diff_line *l,
+	void *p)
+{
+	FILE *fp = p;
+
+	(void)d; (void)h;
+
+	if (!fp)
+		fp = stdout;
+
+	if (l->origin == GIT_DIFF_LINE_CONTEXT ||
+		l->origin == GIT_DIFF_LINE_ADDITION ||
+		l->origin == GIT_DIFF_LINE_DELETION)
+		fputc(l->origin, fp);
+
+	fwrite(l->content, 1, l->content_len, fp);
+
+	return 0;
+}
+
+void treeish_to_tree(
+	git_tree **out, git_repository *repo, const char *treeish)
+{
+	git_object *obj = NULL;
+
+	check_lg2(
+		git_revparse_single(&obj, repo, treeish),
+		"looking up object", treeish);
+
+	check_lg2(
+		git_object_peel((git_object **)out, obj, GIT_OBJ_TREE),
+		"resolving object to tree", treeish);
+
+	git_object_free(obj);
+}
+
diff --git a/examples/common.h b/examples/common.h
new file mode 100644
index 0000000..2fd7d57
--- /dev/null
+++ b/examples/common.h
@@ -0,0 +1,87 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <git2.h>
+
+/**
+ * Check libgit2 error code, printing error to stderr on failure and
+ * exiting the program.
+ */
+extern void check_lg2(int error, const char *message, const char *extra);
+
+/**
+ * Exit the program, printing error to stderr
+ */
+extern void fatal(const char *message, const char *extra);
+
+/**
+ * Check if a string has the given prefix.  Returns 0 if not prefixed
+ * or the length of the prefix if it is.
+ */
+extern size_t is_prefixed(const char *str, const char *pfx);
+
+/**
+ * Match an integer string, returning 1 if matched, 0 if not.
+ */
+extern int is_integer(int *out, const char *str, int allow_negative);
+
+struct args_info {
+	int    argc;
+	char **argv;
+	int    pos;
+};
+#define ARGS_INFO_INIT { argc, argv, 0 }
+
+/**
+ * Check current `args` entry against `opt` string.  If it matches
+ * exactly, take the next arg as a string; if it matches as a prefix with
+ * an equal sign, take the remainder as a string; otherwise return 0.
+ */
+extern int match_str_arg(
+	const char **out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as uint16.  If
+ * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
+ * is a prefix (equal sign optional), take the remainder of the arg as a
+ * uint16_t value; otherwise return 0.
+ */
+extern int match_uint16_arg(
+	uint16_t *out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as int.  If
+ * `opt` matches exactly, take the next arg as an int value; if it matches
+ * as a prefix (equal sign optional), take the remainder of the arg as a
+ * int value; otherwise return 0.
+ */
+extern int match_int_arg(
+	int *out, struct args_info *args, const char *opt, int allow_negative);
+
+/**
+ * Basic output function for plain text diff output
+ * Pass `FILE*` such as `stdout` or `stderr` as payload (or NULL == `stdout`)
+ */
+extern int diff_output(
+	const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+
+/**
+ * Convert a treeish argument to an actual tree; this will call check_lg2
+ * and exit the program if `treeish` cannot be resolved to a tree
+ */
+extern void treeish_to_tree(
+	git_tree **out, git_repository *repo, const char *treeish);
diff --git a/examples/diff.c b/examples/diff.c
index 11efa21..daf5d70 100644
--- a/examples/diff.c
+++ b/examples/diff.c
@@ -1,41 +1,31 @@
-#include <stdio.h>
-#include <git2.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * libgit2 "diff" example - shows how to use the diff API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
 
-static void check(int error, const char *message)
-{
-	if (error) {
-		fprintf(stderr, "%s (%d)\n", message, error);
-		exit(1);
-	}
-}
+#include "common.h"
 
-static int resolve_to_tree(
-	git_repository *repo, const char *identifier, git_tree **tree)
-{
-	int err = 0;
-	git_object *obj = NULL;
+/**
+ * This example demonstrates the use of the libgit2 diff APIs to
+ * create `git_diff` objects and display them, emulating a number of
+ * core Git `diff` command line options.
+ *
+ * This covers on a portion of the core Git diff options and doesn't
+ * have particularly good error handling, but it should show most of
+ * the core libgit2 diff APIs, including various types of diffs and
+ * how to do renaming detection and patch formatting.
+ */
 
-	if ((err = git_revparse_single(&obj, repo, identifier)) < 0)
-		return err;
-
-	switch (git_object_type(obj)) {
-	case GIT_OBJ_TREE:
-		*tree = (git_tree *)obj;
-		break;
-	case GIT_OBJ_COMMIT:
-		err = git_commit_tree(tree, (git_commit *)obj);
-		git_object_free(obj);
-		break;
-	default:
-		err = GIT_ENOTFOUND;
-	}
-
-	return err;
-}
-
-char *colors[] = {
+static const char *colors[] = {
 	"\033[m", /* reset */
 	"\033[1m", /* bold */
 	"\033[31m", /* red */
@@ -43,67 +33,112 @@
 	"\033[36m" /* cyan */
 };
 
-static int printer(
-	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char usage,
-	const char *line,
-	size_t line_len,
-	void *data)
+/** The 'opts' struct captures all the various parsed command line options. */
+struct opts {
+	git_diff_options diffopts;
+	git_diff_find_options findopts;
+	int color;
+	int cached;
+	git_diff_format_t format;
+	const char *treeish1;
+	const char *treeish2;
+	const char *dir;
+};
+
+/** These functions are implemented at the end */
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+static int color_printer(
+	const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+
+int main(int argc, char *argv[])
 {
-	int *last_color = data, color = 0;
+	git_repository *repo = NULL;
+	git_tree *t1 = NULL, *t2 = NULL;
+	git_diff *diff;
+	struct opts o = {
+		GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
+		-1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
+	};
 
-	(void)delta; (void)range; (void)line_len;
+	git_threads_init();
 
-	if (*last_color >= 0) {
-		switch (usage) {
-		case GIT_DIFF_LINE_ADDITION: color = 3; break;
-		case GIT_DIFF_LINE_DELETION: color = 2; break;
-		case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
-		case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
-		case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
-		case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
-		default: color = 0;
-		}
-		if (color != *last_color) {
-			if (*last_color == 1 || color == 1)
-				fputs(colors[0], stdout);
-			fputs(colors[color], stdout);
-			*last_color = color;
-		}
+	parse_opts(&o, argc, argv);
+
+	check_lg2(git_repository_open_ext(&repo, o.dir, 0, NULL),
+		"Could not open repository", o.dir);
+
+	/**
+	 * Possible argument patterns:
+	 *
+	 *  * &lt;sha1&gt; &lt;sha2&gt;
+	 *  * &lt;sha1&gt; --cached
+	 *  * &lt;sha1&gt;
+	 *  * --cached
+	 *  * nothing
+	 *
+	 * Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
+	 * are not supported in this example
+	 */
+
+	if (o.treeish1)
+		treeish_to_tree(&t1, repo, o.treeish1);
+	if (o.treeish2)
+		treeish_to_tree(&t2, repo, o.treeish2);
+
+	if (t1 && t2)
+		check_lg2(
+			git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
+			"diff trees", NULL);
+	else if (t1 && o.cached)
+		check_lg2(
+			git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+			"diff tree to index", NULL);
+	else if (t1)
+		check_lg2(
+			git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
+			"diff tree to working directory", NULL);
+	else if (o.cached) {
+		treeish_to_tree(&t1, repo, "HEAD");
+		check_lg2(
+			git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+			"diff tree to index", NULL);
 	}
+	else
+		check_lg2(
+			git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
+			"diff index to working directory", NULL);
 
-	fputs(line, stdout);
+	/** Apply rename and copy detection if requested. */
+
+	if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
+		check_lg2(
+			git_diff_find_similar(diff, &o.findopts),
+			"finding renames and copies", NULL);
+
+	/** Generate simple output using libgit2 display helper. */
+
+	if (o.color >= 0)
+		fputs(colors[0], stdout);
+
+	check_lg2(
+		git_diff_print(diff, o.format, color_printer, &o.color),
+		"displaying diff", NULL);
+
+	if (o.color >= 0)
+		fputs(colors[0], stdout);
+
+	/** Cleanup before exiting. */
+
+	git_diff_free(diff);
+	git_tree_free(t1);
+	git_tree_free(t2);
+	git_repository_free(repo);
+
+	git_threads_shutdown();
+
 	return 0;
 }
 
-static int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
-{
-	size_t len = strlen(pattern);
-	uint16_t strval;
-	char *endptr = NULL;
-	if (strncmp(arg, pattern, len))
-		return 0;
-	if (arg[len] == '\0' && pattern[len - 1] != '=')
-		return 1;
-	if (arg[len] == '=')
-		len++;
-	strval = strtoul(arg + len, &endptr, 0);
-	if (endptr == arg)
-		return 0;
-	*val = strval;
-	return 1;
-}
-
-static int check_str_param(const char *arg, const char *pattern, const char **val)
-{
-	size_t len = strlen(pattern);
-	if (strncmp(arg, pattern, len))
-		return 0;
-	*val = (const char *)(arg + len);
-	return 1;
-}
-
 static void usage(const char *message, const char *arg)
 {
 	if (message && arg)
@@ -114,152 +149,109 @@
 	exit(1);
 }
 
-enum {
-	FORMAT_PATCH = 0,
-	FORMAT_COMPACT = 1,
-	FORMAT_RAW = 2
-};
-
-int main(int argc, char *argv[])
+/** This implements very rudimentary colorized output. */
+static int color_printer(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
+	void *data)
 {
-	git_repository *repo = NULL;
-	git_tree *t1 = NULL, *t2 = NULL;
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
-	git_diff_list *diff;
-	int i, color = -1, format = FORMAT_PATCH, cached = 0;
-	char *a, *treeish1 = NULL, *treeish2 = NULL;
-	const char *dir = ".";
+	int *last_color = data, color = 0;
 
-	git_threads_init();
+	(void)delta; (void)hunk;
 
-	/* parse arguments as copied from git-diff */
+	if (*last_color >= 0) {
+		switch (line->origin) {
+		case GIT_DIFF_LINE_ADDITION:  color = 3; break;
+		case GIT_DIFF_LINE_DELETION:  color = 2; break;
+		case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
+		case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
+		case GIT_DIFF_LINE_FILE_HDR:  color = 1; break;
+		case GIT_DIFF_LINE_HUNK_HDR:  color = 4; break;
+		default: break;
+		}
 
-	for (i = 1; i < argc; ++i) {
-		a = argv[i];
+		if (color != *last_color) {
+			if (*last_color == 1 || color == 1)
+				fputs(colors[0], stdout);
+			fputs(colors[color], stdout);
+			*last_color = color;
+		}
+	}
+
+	return diff_output(delta, hunk, line, stdout);
+}
+
+/** Parse arguments as copied from git-diff. */
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+	struct args_info args = ARGS_INFO_INIT;
+
+
+	for (args.pos = 1; args.pos < argc; ++args.pos) {
+		const char *a = argv[args.pos];
 
 		if (a[0] != '-') {
-			if (treeish1 == NULL)
-				treeish1 = a;
-			else if (treeish2 == NULL)
-				treeish2 = a;
+			if (o->treeish1 == NULL)
+				o->treeish1 = a;
+			else if (o->treeish2 == NULL)
+				o->treeish2 = a;
 			else
 				usage("Only one or two tree identifiers can be provided", NULL);
 		}
 		else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
 			!strcmp(a, "--patch"))
-			format = FORMAT_PATCH;
+			o->format = GIT_DIFF_FORMAT_PATCH;
 		else if (!strcmp(a, "--cached"))
-			cached = 1;
+			o->cached = 1;
+		else if (!strcmp(a, "--name-only"))
+			o->format = GIT_DIFF_FORMAT_NAME_ONLY;
 		else if (!strcmp(a, "--name-status"))
-			format = FORMAT_COMPACT;
+			o->format = GIT_DIFF_FORMAT_NAME_STATUS;
 		else if (!strcmp(a, "--raw"))
-			format = FORMAT_RAW;
+			o->format = GIT_DIFF_FORMAT_RAW;
 		else if (!strcmp(a, "--color"))
-			color = 0;
+			o->color = 0;
 		else if (!strcmp(a, "--no-color"))
-			color = -1;
+			o->color = -1;
 		else if (!strcmp(a, "-R"))
-			opts.flags |= GIT_DIFF_REVERSE;
+			o->diffopts.flags |= GIT_DIFF_REVERSE;
 		else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
-			opts.flags |= GIT_DIFF_FORCE_TEXT;
+			o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
 		else if (!strcmp(a, "--ignore-space-at-eol"))
-			opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
+			o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
 		else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
-			opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
+			o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
 		else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
-			opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
+			o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
 		else if (!strcmp(a, "--ignored"))
-			opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+			o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
 		else if (!strcmp(a, "--untracked"))
-			opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
-		else if (check_uint16_param(a, "-M", &findopts.rename_threshold) ||
-				 check_uint16_param(a, "--find-renames",
-					&findopts.rename_threshold))
-			findopts.flags |= GIT_DIFF_FIND_RENAMES;
-		else if (check_uint16_param(a, "-C", &findopts.copy_threshold) ||
-				 check_uint16_param(a, "--find-copies",
-					&findopts.copy_threshold))
-			findopts.flags |= GIT_DIFF_FIND_COPIES;
+			o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+		else if (match_uint16_arg(
+				&o->findopts.rename_threshold, &args, "-M") ||
+			match_uint16_arg(
+				&o->findopts.rename_threshold, &args, "--find-renames"))
+			o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
+		else if (match_uint16_arg(
+				&o->findopts.copy_threshold, &args, "-C") ||
+			match_uint16_arg(
+				&o->findopts.copy_threshold, &args, "--find-copies"))
+			o->findopts.flags |= GIT_DIFF_FIND_COPIES;
 		else if (!strcmp(a, "--find-copies-harder"))
-			findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
-		else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) {
+			o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
+		else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
 			/* TODO: parse thresholds */
-			findopts.flags |= GIT_DIFF_FIND_REWRITES;
-		}
-		else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
-			!check_uint16_param(a, "--unified=", &opts.context_lines) &&
-			!check_uint16_param(a, "--inter-hunk-context=",
-				&opts.interhunk_lines) &&
-			!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
-			!check_str_param(a, "--dst-prefix=", &opts.new_prefix) &&
-			!check_str_param(a, "--git-dir=", &dir))
-			usage("Unknown arg", a);
+			o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
+		else if (!match_uint16_arg(
+				&o->diffopts.context_lines, &args, "-U") &&
+			!match_uint16_arg(
+				&o->diffopts.context_lines, &args, "--unified") &&
+			!match_uint16_arg(
+				&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
+			!match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
+			!match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
+			!match_str_arg(&o->dir, &args, "--git-dir"))
+			usage("Unknown command line argument", a);
 	}
-
-	/* open repo */
-
-	check(git_repository_open_ext(&repo, dir, 0, NULL),
-		"Could not open repository");
-
-	if (treeish1)
-		check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
-	if (treeish2)
-		check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
-
-	/* <sha1> <sha2> */
-	/* <sha1> --cached */
-	/* <sha1> */
-	/* --cached */
-	/* nothing */
-
-	if (t1 && t2)
-		check(git_diff_tree_to_tree(&diff, repo, t1, t2, &opts), "Diff");
-	else if (t1 && cached)
-		check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
-	else if (t1) {
-		git_diff_list *diff2;
-		check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
-		check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff");
-		check(git_diff_merge(diff, diff2), "Merge diffs");
-		git_diff_list_free(diff2);
-	}
-	else if (cached) {
-		check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
-		check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
-	}
-	else
-		check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
-
-	if ((findopts.flags & GIT_DIFF_FIND_ALL) != 0)
-		check(git_diff_find_similar(diff, &findopts),
-			"finding renames and copies ");
-
-	if (color >= 0)
-		fputs(colors[0], stdout);
-
-	switch (format) {
-	case FORMAT_PATCH:
-		check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
-		break;
-	case FORMAT_COMPACT:
-		check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
-		break;
-	case FORMAT_RAW:
-		check(git_diff_print_raw(diff, printer, &color), "Displaying diff");
-		break;
-	}
-
-	if (color >= 0)
-		fputs(colors[0], stdout);
-
-	git_diff_list_free(diff);
-	git_tree_free(t1);
-	git_tree_free(t2);
-	git_repository_free(repo);
-
-	git_threads_shutdown();
-
-	return 0;
 }
-
diff --git a/examples/general.c b/examples/general.c
index d7a5847..58e141b 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -1,3 +1,17 @@
+/*
+ * libgit2 "general" example - shows basic libgit2 concepts
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
 // [**libgit2**][lg] is a portable, pure C implementation of the Git core
 // methods provided as a re-entrant linkable library with a solid API,
 // allowing you to write native speed custom Git applications in any
@@ -52,7 +66,7 @@
   // simplest.  There are also [methods][me] for specifying the index file
   // and work tree locations, here we assume they are in the normal places.
 	//
-	// (Try running this program against tests-clar/resources/testrepo.git.)
+	// (Try running this program against tests/resources/testrepo.git.)
   //
   // [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
   int error;
diff --git a/examples/init.c b/examples/init.c
new file mode 100644
index 0000000..0e823ab
--- /dev/null
+++ b/examples/init.c
@@ -0,0 +1,253 @@
+/*
+ * libgit2 "init" example - shows how to initialize a new repo
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This is a sample program that is similar to "git init".  See the
+ * documentation for that (try "git help init") to understand what this
+ * program is emulating.
+ *
+ * This demonstrates using the libgit2 APIs to initialize a new repository.
+ *
+ * This also contains a special additional option that regular "git init"
+ * does not support which is "--initial-commit" to make a first empty commit.
+ * That is demonstrated in the "create_initial_commit" helper function.
+ */
+
+/** Forward declarations of helpers */
+struct opts {
+	int no_options;
+	int quiet;
+	int bare;
+	int initial_commit;
+	uint32_t shared;
+	const char *template;
+	const char *gitdir;
+	const char *dir;
+};
+static void create_initial_commit(git_repository *repo);
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+
+
+int main(int argc, char *argv[])
+{
+	git_repository *repo = NULL;
+	struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
+
+	git_threads_init();
+
+	parse_opts(&o, argc, argv);
+
+	/* Initialize repository. */
+
+	if (o.no_options) {
+		/**
+		 * No options were specified, so let's demonstrate the default
+		 * simple case of git_repository_init() API usage...
+		 */
+		check_lg2(git_repository_init(&repo, o.dir, 0),
+			"Could not initialize repository", NULL);
+	}
+	else {
+		/**
+		 * Some command line options were specified, so we'll use the
+		 * extended init API to handle them
+		 */
+		git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+		initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
+
+		if (o.bare)
+			initopts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+		if (o.template) {
+			initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+			initopts.template_path = o.template;
+		}
+
+		if (o.gitdir) {
+			/**
+			 * If you specified a separate git directory, then initialize
+			 * the repository at that path and use the second path as the
+			 * working directory of the repository (with a git-link file)
+			 */
+			initopts.workdir_path = o.dir;
+			o.dir = o.gitdir;
+		}
+
+		if (o.shared != 0)
+			initopts.mode = o.shared;
+
+		check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
+				"Could not initialize repository", NULL);
+	}
+
+	/** Print a message to stdout like "git init" does. */
+
+	if (!o.quiet) {
+		if (o.bare || o.gitdir)
+			o.dir = git_repository_path(repo);
+		else
+			o.dir = git_repository_workdir(repo);
+
+		printf("Initialized empty Git repository in %s\n", o.dir);
+	}
+
+	/**
+	 * As an extension to the basic "git init" command, this example
+	 * gives the option to create an empty initial commit.  This is
+	 * mostly to demonstrate what it takes to do that, but also some
+	 * people like to have that empty base commit in their repo.
+	 */
+	if (o.initial_commit) {
+		create_initial_commit(repo);
+		printf("Created empty initial commit\n");
+	}
+
+	git_repository_free(repo);
+	git_threads_shutdown();
+
+	return 0;
+}
+
+/**
+ * Unlike regular "git init", this example shows how to create an initial
+ * empty commit in the repository.  This is the helper function that does
+ * that.
+ */
+static void create_initial_commit(git_repository *repo)
+{
+	git_signature *sig;
+	git_index *index;
+	git_oid tree_id, commit_id;
+	git_tree *tree;
+
+	/** First use the config to initialize a commit signature for the user. */
+
+	if (git_signature_default(&sig, repo) < 0)
+		fatal("Unable to create a commit signature.",
+		      "Perhaps 'user.name' and 'user.email' are not set");
+
+	/* Now let's create an empty tree for this commit */
+
+	if (git_repository_index(&index, repo) < 0)
+		fatal("Could not open repository index", NULL);
+
+	/**
+	 * Outside of this example, you could call git_index_add_bypath()
+	 * here to put actual files into the index.  For our purposes, we'll
+	 * leave it empty for now.
+	 */
+
+	if (git_index_write_tree(&tree_id, index) < 0)
+		fatal("Unable to write initial tree from index", NULL);
+
+	git_index_free(index);
+
+	if (git_tree_lookup(&tree, repo, &tree_id) < 0)
+		fatal("Could not look up initial tree", NULL);
+
+	/**
+	 * Ready to create the initial commit.
+	 *
+	 * Normally creating a commit would involve looking up the current
+	 * HEAD commit and making that be the parent of the initial commit,
+	 * but here this is the first commit so there will be no parent.
+	 */
+
+	if (git_commit_create_v(
+			&commit_id, repo, "HEAD", sig, sig,
+			NULL, "Initial commit", tree, 0) < 0)
+		fatal("Could not create the initial commit", NULL);
+
+	/** Clean up so we don't leak memory. */
+
+	git_tree_free(tree);
+	git_signature_free(sig);
+}
+
+static void usage(const char *error, const char *arg)
+{
+	fprintf(stderr, "error: %s '%s'\n", error, arg);
+	fprintf(stderr,
+			"usage: init [-q | --quiet] [--bare] [--template=<dir>]\n"
+			"            [--shared[=perms]] [--initial-commit]\n"
+			"            [--separate-git-dir] <directory>\n");
+	exit(1);
+}
+
+/** Parse the tail of the --shared= argument. */
+static uint32_t parse_shared(const char *shared)
+{
+	if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
+		return GIT_REPOSITORY_INIT_SHARED_UMASK;
+
+	else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
+		return GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+	else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
+			 !strcmp(shared, "everybody"))
+		return GIT_REPOSITORY_INIT_SHARED_ALL;
+
+	else if (shared[0] == '0') {
+		long val;
+		char *end = NULL;
+		val = strtol(shared + 1, &end, 8);
+		if (end == shared + 1 || *end != 0)
+			usage("invalid octal value for --shared", shared);
+		return (uint32_t)val;
+	}
+
+	else
+		usage("unknown value for --shared", shared);
+
+	return 0;
+}
+
+static void parse_opts(struct opts *o, int argc, char *argv[])
+{
+	struct args_info args = ARGS_INFO_INIT;
+	const char *sharedarg;
+
+	/** Process arguments. */
+
+	for (args.pos = 1; args.pos < argc; ++args.pos) {
+		char *a = argv[args.pos];
+
+		if (a[0] == '-')
+			o->no_options = 0;
+
+		if (a[0] != '-') {
+			if (o->dir != NULL)
+				usage("extra argument", a);
+			o->dir = a;
+		}
+		else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
+			o->quiet = 1;
+		else if (!strcmp(a, "--bare"))
+			o->bare = 1;
+		else if (!strcmp(a, "--shared"))
+			o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
+		else if (!strcmp(a, "--initial-commit"))
+			o->initial_commit = 1;
+		else if (match_str_arg(&sharedarg, &args, "--shared"))
+			o->shared = parse_shared(sharedarg);
+		else if (!match_str_arg(&o->template, &args, "--template") ||
+		         !match_str_arg(&o->gitdir, &args, "--separate-git-dir"))
+			usage("unknown option", a);
+	}
+
+	if (!o->dir)
+		usage("must specify directory to init", NULL);
+}
diff --git a/examples/log.c b/examples/log.c
new file mode 100644
index 0000000..471c5ff
--- /dev/null
+++ b/examples/log.c
@@ -0,0 +1,434 @@
+/*
+ * libgit2 "log" example - shows how to walk history and get commit info
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 rev walker APIs to roughly
+ * simulate the output of `git log` and a few of command line arguments.
+ * `git log` has many many options and this only shows a few of them.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Colorized or paginated output formatting
+ * - Most of the `git log` options
+ *
+ * This does have:
+ *
+ * - Examples of translating command line arguments to equivalent libgit2
+ *   revwalker configuration calls
+ * - Simplified options to apply pathspec limits and to show basic diffs
+ */
+
+/** log_state represents walker being configured while handling options */
+struct log_state {
+	git_repository *repo;
+	const char *repodir;
+	git_revwalk *walker;
+	int hide;
+	int sorting;
+	int revisions;
+};
+
+/** utility functions that are called to configure the walker */
+static void set_sorting(struct log_state *s, unsigned int sort_mode);
+static void push_rev(struct log_state *s, git_object *obj, int hide);
+static int add_revision(struct log_state *s, const char *revstr);
+
+/** log_options holds other command line options that affect log output */
+struct log_options {
+	int show_diff;
+	int skip, limit;
+	int min_parents, max_parents;
+	git_time_t before;
+	git_time_t after;
+	char *author;
+	char *committer;
+};
+
+/** utility functions that parse options and help with log output */
+static int parse_options(
+	struct log_state *s, struct log_options *opt, int argc, char **argv);
+static void print_time(const git_time *intime, const char *prefix);
+static void print_commit(git_commit *commit);
+static int match_with_parent(git_commit *commit, int i, git_diff_options *);
+
+
+int main(int argc, char *argv[])
+{
+	int i, count = 0, printed = 0, parents, last_arg;
+	struct log_state s;
+	struct log_options opt;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_oid oid;
+	git_commit *commit = NULL;
+	git_pathspec *ps = NULL;
+
+	git_threads_init();
+
+	/** Parse arguments and set up revwalker. */
+
+	last_arg = parse_options(&s, &opt, argc, argv);
+
+	diffopts.pathspec.strings = &argv[last_arg];
+	diffopts.pathspec.count	  = argc - last_arg;
+	if (diffopts.pathspec.count > 0)
+		check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
+			"Building pathspec", NULL);
+
+	if (!s.revisions)
+		add_revision(&s, NULL);
+
+	/** Use the revwalker to traverse the history. */
+
+	printed = count = 0;
+
+	for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
+		check_lg2(git_commit_lookup(&commit, s.repo, &oid),
+			"Failed to look up commit", NULL);
+
+		parents = (int)git_commit_parentcount(commit);
+		if (parents < opt.min_parents)
+			continue;
+		if (opt.max_parents > 0 && parents > opt.max_parents)
+			continue;
+
+		if (diffopts.pathspec.count > 0) {
+			int unmatched = parents;
+
+			if (parents == 0) {
+				git_tree *tree;
+				check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
+				if (git_pathspec_match_tree(
+						NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
+					unmatched = 1;
+				git_tree_free(tree);
+			} else if (parents == 1) {
+				unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
+			} else {
+				for (i = 0; i < parents; ++i) {
+					if (match_with_parent(commit, i, &diffopts))
+						unmatched--;
+				}
+			}
+
+			if (unmatched > 0)
+				continue;
+		}
+
+		if (count++ < opt.skip)
+			continue;
+		if (opt.limit != -1 && printed++ >= opt.limit) {
+			git_commit_free(commit);
+			break;
+		}
+
+		print_commit(commit);
+
+		if (opt.show_diff) {
+			git_tree *a = NULL, *b = NULL;
+			git_diff *diff = NULL;
+
+			if (parents > 1)
+				continue;
+			check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
+			if (parents == 1) {
+				git_commit *parent;
+				check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
+				check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+				git_commit_free(parent);
+			}
+
+			check_lg2(git_diff_tree_to_tree(
+				&diff, git_commit_owner(commit), a, b, &diffopts),
+				"Diff commit with parent", NULL);
+			check_lg2(
+                git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
+				"Displaying diff", NULL);
+
+			git_diff_free(diff);
+			git_tree_free(a);
+			git_tree_free(b);
+		}
+	}
+
+	git_pathspec_free(ps);
+	git_revwalk_free(s.walker);
+	git_repository_free(s.repo);
+	git_threads_shutdown();
+
+	return 0;
+}
+
+/** Push object (for hide or show) onto revwalker. */
+static void push_rev(struct log_state *s, git_object *obj, int hide)
+{
+	hide = s->hide ^ hide;
+
+	/** Create revwalker on demand if it doesn't already exist. */
+	if (!s->walker) {
+		check_lg2(git_revwalk_new(&s->walker, s->repo),
+			"Could not create revision walker", NULL);
+		git_revwalk_sorting(s->walker, s->sorting);
+	}
+
+	if (!obj)
+		check_lg2(git_revwalk_push_head(s->walker),
+			"Could not find repository HEAD", NULL);
+	else if (hide)
+		check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
+			"Reference does not refer to a commit", NULL);
+	else
+		check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
+			"Reference does not refer to a commit", NULL);
+
+	git_object_free(obj);
+}
+
+/** Parse revision string and add revs to walker. */
+static int add_revision(struct log_state *s, const char *revstr)
+{
+	git_revspec revs;
+	int hide = 0;
+
+	/** Open repo on demand if it isn't already open. */
+	if (!s->repo) {
+		if (!s->repodir) s->repodir = ".";
+		check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
+			"Could not open repository", s->repodir);
+	}
+
+	if (!revstr) {
+		push_rev(s, NULL, hide);
+		return 0;
+	}
+
+	if (*revstr == '^') {
+		revs.flags = GIT_REVPARSE_SINGLE;
+		hide = !hide;
+
+		if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
+			return -1;
+	} else if (git_revparse(&revs, s->repo, revstr) < 0)
+		return -1;
+
+	if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
+		push_rev(s, revs.from, hide);
+	else {
+		push_rev(s, revs.to, hide);
+
+		if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+			git_oid base;
+			check_lg2(git_merge_base(&base, s->repo,
+				git_object_id(revs.from), git_object_id(revs.to)),
+				"Could not find merge base", revstr);
+			check_lg2(
+				git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
+				"Could not find merge base commit", NULL);
+
+			push_rev(s, revs.to, hide);
+		}
+
+		push_rev(s, revs.from, !hide);
+	}
+
+	return 0;
+}
+
+/** Update revwalker with sorting mode. */
+static void set_sorting(struct log_state *s, unsigned int sort_mode)
+{
+	/** Open repo on demand if it isn't already open. */
+	if (!s->repo) {
+		if (!s->repodir) s->repodir = ".";
+		check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
+			"Could not open repository", s->repodir);
+	}
+
+	/** Create revwalker on demand if it doesn't already exist. */
+	if (!s->walker)
+		check_lg2(git_revwalk_new(&s->walker, s->repo),
+			"Could not create revision walker", NULL);
+
+	if (sort_mode == GIT_SORT_REVERSE)
+		s->sorting = s->sorting ^ GIT_SORT_REVERSE;
+	else
+		s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
+
+	git_revwalk_sorting(s->walker, s->sorting);
+}
+
+/** Helper to format a git_time value like Git. */
+static void print_time(const git_time *intime, const char *prefix)
+{
+	char sign, out[32];
+	struct tm *intm;
+	int offset, hours, minutes;
+	time_t t;
+
+	offset = intime->offset;
+	if (offset < 0) {
+		sign = '-';
+		offset = -offset;
+	} else {
+		sign = '+';
+	}
+
+	hours   = offset / 60;
+	minutes = offset % 60;
+
+	t = (time_t)intime->time + (intime->offset * 60);
+
+	intm = gmtime(&t);
+	strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
+
+	printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
+}
+
+/** Helper to print a commit object. */
+static void print_commit(git_commit *commit)
+{
+	char buf[GIT_OID_HEXSZ + 1];
+	int i, count;
+	const git_signature *sig;
+	const char *scan, *eol;
+
+	git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
+	printf("commit %s\n", buf);
+
+	if ((count = (int)git_commit_parentcount(commit)) > 1) {
+		printf("Merge:");
+		for (i = 0; i < count; ++i) {
+			git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+			printf(" %s", buf);
+		}
+		printf("\n");
+	}
+
+	if ((sig = git_commit_author(commit)) != NULL) {
+		printf("Author: %s <%s>\n", sig->name, sig->email);
+		print_time(&sig->when, "Date:   ");
+	}
+	printf("\n");
+
+	for (scan = git_commit_message(commit); scan && *scan; ) {
+		for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
+
+		printf("    %.*s\n", (int)(eol - scan), scan);
+		scan = *eol ? eol + 1 : NULL;
+	}
+	printf("\n");
+}
+
+/** Helper to find how many files in a commit changed from its nth parent. */
+static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
+{
+	git_commit *parent;
+	git_tree *a, *b;
+	git_diff *diff;
+	int ndeltas;
+
+	check_lg2(
+		git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
+	check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+	check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
+	check_lg2(
+		git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
+		"Checking diff between parent and commit", NULL);
+
+	ndeltas = (int)git_diff_num_deltas(diff);
+
+	git_diff_free(diff);
+	git_tree_free(a);
+	git_tree_free(b);
+	git_commit_free(parent);
+
+	return ndeltas > 0;
+}
+
+/** Print a usage message for the program. */
+static void usage(const char *message, const char *arg)
+{
+	if (message && arg)
+		fprintf(stderr, "%s: %s\n", message, arg);
+	else if (message)
+		fprintf(stderr, "%s\n", message);
+	fprintf(stderr, "usage: log [<options>]\n");
+	exit(1);
+}
+
+/** Parse some log command line options. */
+static int parse_options(
+	struct log_state *s, struct log_options *opt, int argc, char **argv)
+{
+	struct args_info args = ARGS_INFO_INIT;
+
+	memset(s, 0, sizeof(*s));
+	s->sorting = GIT_SORT_TIME;
+
+	memset(opt, 0, sizeof(*opt));
+	opt->max_parents = -1;
+	opt->limit = -1;
+
+	for (args.pos = 1; args.pos < argc; ++args.pos) {
+		const char *a = argv[args.pos];
+
+		if (a[0] != '-') {
+			if (!add_revision(s, a))
+				s->revisions++;
+			else
+				/** Try failed revision parse as filename. */
+				break;
+		} else if (!strcmp(a, "--")) {
+			++args.pos;
+			break;
+		}
+		else if (!strcmp(a, "--date-order"))
+			set_sorting(s, GIT_SORT_TIME);
+		else if (!strcmp(a, "--topo-order"))
+			set_sorting(s, GIT_SORT_TOPOLOGICAL);
+		else if (!strcmp(a, "--reverse"))
+			set_sorting(s, GIT_SORT_REVERSE);
+		else if (match_str_arg(&s->repodir, &args, "--git-dir"))
+			/** Found git-dir. */;
+		else if (match_int_arg(&opt->skip, &args, "--skip", 0))
+			/** Found valid --skip. */;
+		else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
+			/** Found valid --max-count. */;
+		else if (a[1] >= '0' && a[1] <= '9')
+			is_integer(&opt->limit, a + 1, 0);
+		else if (match_int_arg(&opt->limit, &args, "-n", 0))
+			/** Found valid -n. */;
+		else if (!strcmp(a, "--merges"))
+			opt->min_parents = 2;
+		else if (!strcmp(a, "--no-merges"))
+			opt->max_parents = 1;
+		else if (!strcmp(a, "--no-min-parents"))
+			opt->min_parents = 0;
+		else if (!strcmp(a, "--no-max-parents"))
+			opt->max_parents = -1;
+		else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
+			/** Found valid --max-parents. */;
+		else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
+			/** Found valid --min_parents. */;
+		else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
+			opt->show_diff = 1;
+		else
+			usage("Unsupported argument", a);
+	}
+
+	return args.pos;
+}
+
diff --git a/examples/network/Makefile b/examples/network/Makefile
index 810eb70..f65c6cb 100644
--- a/examples/network/Makefile
+++ b/examples/network/Makefile
@@ -11,7 +11,8 @@
   ls-remote.o \
   fetch.o \
   clone.o \
-  index-pack.o
+  index-pack.o \
+  common.o
 
 all: $(OBJECTS)
 	$(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS) $(LIBRARIES)
diff --git a/examples/network/clone.c b/examples/network/clone.c
index 00c25c1..4df47eb 100644
--- a/examples/network/clone.c
+++ b/examples/network/clone.c
@@ -9,19 +9,6 @@
 # include <unistd.h>
 #endif
 
-/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
- * with permission of the original author, Martin Pool.
- * http://sourcefrog.net/weblog/software/languages/C/unused.html
- */
-#ifdef UNUSED
-#elif defined(__GNUC__)
-# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
-#elif defined(__LCLINT__)
-# define UNUSED(x) /*@unused@*/ x
-#else
-# define UNUSED(x) x
-#endif
-
 typedef struct progress_data {
 	git_transfer_progress fetch_progress;
 	size_t completed_steps;
@@ -38,13 +25,19 @@
 		: 0.f;
 	int kbytes = pd->fetch_progress.received_bytes / 1024;
 
-	printf("net %3d%% (%4d kb, %5d/%5d)  /  idx %3d%% (%5d/%5d)  /  chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
+	if (pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
+		printf("Resolving deltas %d/%d\r",
+		       pd->fetch_progress.indexed_deltas,
+		       pd->fetch_progress.total_deltas);
+	} else {
+		printf("net %3d%% (%4d kb, %5d/%5d)  /  idx %3d%% (%5d/%5d)  /  chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
 		   network_percent, kbytes,
 		   pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
 		   index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
 		   checkout_percent,
 		   pd->completed_steps, pd->total_steps,
 		   pd->path);
+	}
 }
 
 static int fetch_progress(const git_transfer_progress *stats, void *payload)
@@ -63,24 +56,6 @@
 	print_progress(pd);
 }
 
-static int cred_acquire(git_cred **out,
-		const char * UNUSED(url),
-		const char * UNUSED(username_from_url),
-		unsigned int UNUSED(allowed_types),
-		void * UNUSED(payload))
-{
-	char username[128] = {0};
-	char password[128] = {0};
-
-	printf("Username: ");
-	scanf("%s", username);
-
-	/* Yup. Right there on your terminal. Careful where you copy/paste output. */
-	printf("Password: ");
-	scanf("%s", password);
-
-	return git_cred_userpass_plaintext_new(out, username, password);
-}
 
 int do_clone(git_repository *repo, int argc, char **argv)
 {
@@ -105,9 +80,9 @@
 	checkout_opts.progress_cb = checkout_progress;
 	checkout_opts.progress_payload = &pd;
 	clone_opts.checkout_opts = checkout_opts;
-	clone_opts.fetch_progress_cb = &fetch_progress;
-	clone_opts.fetch_progress_payload = &pd;
-	clone_opts.cred_acquire_cb = cred_acquire;
+	clone_opts.remote_callbacks.transfer_progress = &fetch_progress;
+	clone_opts.remote_callbacks.credentials = cred_acquire_cb;
+	clone_opts.remote_callbacks.payload = &pd;
 
 	// Do the clone
 	error = git_clone(&cloned_repo, url, path, &clone_opts);
diff --git a/examples/network/common.c b/examples/network/common.c
new file mode 100644
index 0000000..d123eed
--- /dev/null
+++ b/examples/network/common.c
@@ -0,0 +1,34 @@
+#include "common.h"
+#include <stdio.h>
+
+/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
+ * with permission of the original author, Martin Pool.
+ * http://sourcefrog.net/weblog/software/languages/C/unused.html
+ */
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+int cred_acquire_cb(git_cred **out,
+		const char * UNUSED(url),
+		const char * UNUSED(username_from_url),
+		unsigned int UNUSED(allowed_types),
+		void * UNUSED(payload))
+{
+	char username[128] = {0};
+	char password[128] = {0};
+
+	printf("Username: ");
+	scanf("%s", username);
+
+	/* Yup. Right there on your terminal. Careful where you copy/paste output. */
+	printf("Password: ");
+	scanf("%s", password);
+
+	return git_cred_userpass_plaintext_new(out, username, password);
+}
diff --git a/examples/network/common.h b/examples/network/common.h
index a4cfa1a..1b09caa 100644
--- a/examples/network/common.h
+++ b/examples/network/common.h
@@ -12,6 +12,12 @@
 int index_pack(git_repository *repo, int argc, char **argv);
 int do_clone(git_repository *repo, int argc, char **argv);
 
+int cred_acquire_cb(git_cred **out,
+		const char * url,
+		const char * username_from_url,
+		unsigned int allowed_types,
+		void *payload);
+
 #ifndef PRIuZ
 /* Define the printf format specifer to use for size_t output */
 #if defined(_MSC_VER) || defined(__MINGW32__)
diff --git a/examples/network/fetch.c b/examples/network/fetch.c
index 6020ec6..474b45b 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -14,11 +14,12 @@
 	int finished;
 };
 
-static void progress_cb(const char *str, int len, void *data)
+static int progress_cb(const char *str, int len, void *data)
 {
 	(void)data;
 	printf("remote: %.*s", len, str);
 	fflush(stdout); /* We don't have the \n to force the flush */
+	return 0;
 }
 
 static void *download(void *ptr)
@@ -35,7 +36,7 @@
 	// Download the packfile and index it. This function updates the
 	// amount of received data and the indexer stats which lets you
 	// inform the user about progress.
-	if (git_remote_download(data->remote, NULL, NULL) < 0) {
+	if (git_remote_download(data->remote) < 0) {
 		data->ret = -1;
 		goto exit;
 	}
@@ -47,6 +48,11 @@
 	return &data->ret;
 }
 
+/**
+ * This function gets called for each remote-tracking branch that gets
+ * updated. The message we output depends on whether it's a new one or
+ * an update.
+ */
 static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
 {
 	char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
@@ -66,6 +72,7 @@
 	return 0;
 }
 
+/** Entry point for this command */
 int fetch(git_repository *repo, int argc, char **argv)
 {
 	git_remote *remote = NULL;
@@ -91,6 +98,7 @@
 	// Set up the callbacks (only update_tips for now)
 	callbacks.update_tips = &update_cb;
 	callbacks.progress = &progress_cb;
+	callbacks.credentials = cred_acquire_cb;
 	git_remote_set_callbacks(remote, &callbacks);
 
 	// Set up the information for the background worker thread
@@ -112,10 +120,14 @@
 	do {
 		usleep(10000);
 
-		if (stats->total_objects > 0)
+		if (stats->received_objects == stats->total_objects) {
+			printf("Resolving deltas %d/%d\r",
+			       stats->indexed_deltas, stats->total_deltas);
+		} else if (stats->total_objects > 0) {
 			printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
 			       stats->received_objects, stats->total_objects,
 				   stats->indexed_objects, stats->received_bytes);
+		}
 	} while (!data.finished);
 
 	if (data.ret < 0)
@@ -124,8 +136,18 @@
 	pthread_join(worker, NULL);
 #endif
 
-	printf("\rReceived %d/%d objects in %zu bytes\n",
+	/**
+	 * If there are local objects (we got a thin pack), then tell
+	 * the user how many objects we saved from having to cross the
+	 * network.
+	 */
+	if (stats->local_objects > 0) {
+		printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
+		       stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
+	} else{
+		printf("\rReceived %d/%d objects in %zu bytes\n",
 			stats->indexed_objects, stats->total_objects, stats->received_bytes);
+	}
 
 	// Disconnect the underlying connection to prevent from idling.
 	git_remote_disconnect(remote);
diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c
index 889305d..314f211 100644
--- a/examples/network/index-pack.c
+++ b/examples/network/index-pack.c
@@ -31,7 +31,7 @@
 
 int index_pack(git_repository *repo, int argc, char **argv)
 {
-	git_indexer_stream *idx;
+	git_indexer *idx;
 	git_transfer_progress stats = {0, 0};
 	int error;
 	char hash[GIT_OID_HEXSZ + 1] = {0};
@@ -46,7 +46,7 @@
 		return EXIT_FAILURE;
 	}
 
-	if (git_indexer_stream_new(&idx, ".", NULL, NULL) < 0) {
+	if (git_indexer_new(&idx, ".", 0, NULL, NULL, NULL) < 0) {
 		puts("bad idx");
 		return -1;
 	}
@@ -61,7 +61,7 @@
 		if (read_bytes < 0)
 			break;
 
-		if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0)
+		if ((error = git_indexer_append(idx, buf, read_bytes, &stats)) < 0)
 			goto cleanup;
 
 		index_cb(&stats, NULL);
@@ -73,16 +73,16 @@
 		goto cleanup;
 	}
 
-	if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
+	if ((error = git_indexer_commit(idx, &stats)) < 0)
 		goto cleanup;
 
 	printf("\rIndexing %d of %d\n", stats.indexed_objects, stats.total_objects);
 
-	git_oid_fmt(hash, git_indexer_stream_hash(idx));
+	git_oid_fmt(hash, git_indexer_hash(idx));
 	puts(hash);
 
  cleanup:
 	close(fd);
-	git_indexer_stream_free(idx);
+	git_indexer_free(idx);
 	return error;
 }
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c
index 2520118..1e08b29 100644
--- a/examples/network/ls-remote.c
+++ b/examples/network/ls-remote.c
@@ -4,65 +4,52 @@
 #include <string.h>
 #include "common.h"
 
-static int show_ref__cb(git_remote_head *head, void *payload)
-{
-	char oid[GIT_OID_HEXSZ + 1] = {0};
-
-	(void)payload;
-	git_oid_fmt(oid, &head->oid);
-	printf("%s\t%s\n", oid, head->name);
-	return 0;
-}
-
-static int use_unnamed(git_repository *repo, const char *url)
-{
-	git_remote *remote = NULL;
-	int error;
-
-	// Create an instance of a remote from the URL. The transport to use
-	// is detected from the URL
-	error = git_remote_create_inmemory(&remote, repo, NULL, url);
-	if (error < 0)
-		goto cleanup;
-
-	// When connecting, the underlying code needs to know wether we
-	// want to push or fetch
-	error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
-	if (error < 0)
-		goto cleanup;
-
-	// With git_remote_ls we can retrieve the advertised heads
-	error = git_remote_ls(remote, &show_ref__cb, NULL);
-
-cleanup:
-	git_remote_free(remote);
-	return error;
-}
-
 static int use_remote(git_repository *repo, char *name)
 {
 	git_remote *remote = NULL;
 	int error;
+	const git_remote_head **refs;
+	size_t refs_len, i;
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
 
 	// Find the remote by name
 	error = git_remote_load(&remote, repo, name);
-	if (error < 0)
-		goto cleanup;
+	if (error < 0) {
+		error = git_remote_create_inmemory(&remote, repo, NULL, name);
+		if (error < 0)
+			goto cleanup;
+	}
+
+	/**
+	 * Connect to the remote and call the printing function for
+	 * each of the remote references.
+	 */
+	callbacks.credentials = cred_acquire_cb;
+	git_remote_set_callbacks(remote, &callbacks);
 
 	error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
 	if (error < 0)
 		goto cleanup;
 
-	error = git_remote_ls(remote, &show_ref__cb, NULL);
+	/**
+	 * Get the list of references on the remote and print out
+	 * their name next to what they point to.
+	 */
+	if (git_remote_ls(&refs, &refs_len, remote) < 0)
+		goto cleanup;
+
+	for (i = 0; i < refs_len; i++) {
+		char oid[GIT_OID_HEXSZ + 1] = {0};
+		git_oid_fmt(oid, &refs[i]->oid);
+		printf("%s\t%s\n", oid, refs[i]->name);
+	}
 
 cleanup:
 	git_remote_free(remote);
 	return error;
 }
 
-// This gets called to do the work. The remote can be given either as
-// the name of a configured remote or an URL.
-
+/** Entry point for this command */
 int ls_remote(git_repository *repo, int argc, char **argv)
 {
 	int error;
@@ -72,12 +59,7 @@
 		return EXIT_FAILURE;
 	}
 
-	/* If there's a ':' in the name, assume it's an URL */
-	if (strchr(argv[1], ':') != NULL) {
-		error = use_unnamed(repo, argv[1]);
-	} else {
-		error = use_remote(repo, argv[1]);
-	}
+	error = use_remote(repo, argv[1]);
 
 	return error;
 }
diff --git a/examples/rev-list.c b/examples/rev-list.c
index 1fb7ebf..5c0d751 100644
--- a/examples/rev-list.c
+++ b/examples/rev-list.c
@@ -1,17 +1,43 @@
-#include <stdio.h>
-#include <string.h>
+/*
+ * libgit2 "rev-list" example - shows how to transform a rev-spec into a list
+ * of commit ids
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
 
-#include <git2.h>
+#include "common.h"
 
-static void check_error(int error_code, const char *action)
+static int revwalk_parseopts(git_repository *repo, git_revwalk *walk, int nopts, char **opts);
+
+int main (int argc, char **argv)
 {
-	if (!error_code)
-		return;
+	git_repository *repo;
+	git_revwalk *walk;
+	git_oid oid;
+	char buf[41];
 
-	const git_error *error = giterr_last();
-	fprintf(stderr, "Error %d %s: %s\n", -error_code, action,
-	        (error && error->message) ? error->message : "???");
-	exit(1);
+	git_threads_init();
+
+	check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "opening repository", NULL);
+	check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
+	check_lg2(revwalk_parseopts(repo, walk, argc-1, argv+1), "parsing options", NULL);
+
+	while (!git_revwalk_next(&oid, walk)) {
+		git_oid_fmt(buf, &oid);
+		buf[40] = '\0';
+		printf("%s\n", buf);
+	}
+
+	git_threads_shutdown();
+	return 0;
 }
 
 static int push_commit(git_revwalk *walk, const git_oid *oid, int hide)
@@ -93,27 +119,3 @@
 	return 0;
 }
 
-int main (int argc, char **argv)
-{
-	int error;
-	git_repository *repo;
-	git_revwalk *walk;
-	git_oid oid;
-	char buf[41];
-
-	error = git_repository_open_ext(&repo, ".", 0, NULL);
-	check_error(error, "opening repository");
-
-	error = git_revwalk_new(&walk, repo);
-	check_error(error, "allocating revwalk");
-	error = revwalk_parseopts(repo, walk, argc-1, argv+1);
-	check_error(error, "parsing options");
-
-	while (!git_revwalk_next(&oid, walk)) {
-		git_oid_fmt(buf, &oid);
-		buf[40] = '\0';
-		printf("%s\n", buf);
-	}
-
-	return 0;
-}
diff --git a/examples/rev-parse.c b/examples/rev-parse.c
new file mode 100644
index 0000000..a248337
--- /dev/null
+++ b/examples/rev-parse.c
@@ -0,0 +1,115 @@
+/*
+ * libgit2 "rev-parse" example - shows how to parse revspecs
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/** Forward declarations for helpers. */
+struct parse_state {
+	git_repository *repo;
+	const char *repodir;
+	const char *spec;
+	int not;
+};
+static void parse_opts(struct parse_state *ps, int argc, char *argv[]);
+static int parse_revision(struct parse_state *ps);
+
+
+int main(int argc, char *argv[])
+{
+	struct parse_state ps = {0};
+
+	git_threads_init();
+	parse_opts(&ps, argc, argv);
+
+	check_lg2(parse_revision(&ps), "Parsing", NULL);
+
+	git_repository_free(ps.repo);
+	git_threads_shutdown();
+
+	return 0;
+}
+
+static void usage(const char *message, const char *arg)
+{
+	if (message && arg)
+		fprintf(stderr, "%s: %s\n", message, arg);
+	else if (message)
+		fprintf(stderr, "%s\n", message);
+	fprintf(stderr, "usage: rev-parse [ --option ] <args>...\n");
+	exit(1);
+}
+
+static void parse_opts(struct parse_state *ps, int argc, char *argv[])
+{
+	struct args_info args = ARGS_INFO_INIT;
+
+	for (args.pos=1; args.pos < argc; ++args.pos) {
+		const char *a = argv[args.pos];
+
+		if (a[0] != '-') {
+			if (ps->spec)
+				usage("Too many specs", a);
+			ps->spec = a;
+		} else if (!strcmp(a, "--not"))
+			ps->not = !ps->not;
+		else if (!match_str_arg(&ps->repodir, &args, "--git-dir"))
+			usage("Cannot handle argument", a);
+	}
+}
+
+static int parse_revision(struct parse_state *ps)
+{
+	git_revspec rs;
+	char str[GIT_OID_HEXSZ + 1];
+
+	if (!ps->repo) {
+		if (!ps->repodir)
+			ps->repodir = ".";
+		check_lg2(git_repository_open_ext(&ps->repo, ps->repodir, 0, NULL),
+			"Could not open repository from", ps->repodir);
+	}
+
+	check_lg2(git_revparse(&rs, ps->repo, ps->spec), "Could not parse", ps->spec);
+
+	if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
+		git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+		printf("%s\n", str);
+		git_object_free(rs.from);
+	}
+	else if ((rs.flags & GIT_REVPARSE_RANGE) != 0) {
+		git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
+		printf("%s\n", str);
+		git_object_free(rs.to);
+
+		if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
+			git_oid base;
+			check_lg2(git_merge_base(&base, ps->repo,
+						git_object_id(rs.from), git_object_id(rs.to)),
+					"Could not find merge base", ps->spec);
+
+			git_oid_tostr(str, sizeof(str), &base);
+			printf("%s\n", str);
+		}
+
+		git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+		printf("^%s\n", str);
+		git_object_free(rs.from);
+	}
+	else {
+		fatal("Invalid results from git_revparse", ps->spec);
+	}
+
+	return 0;
+}
+
diff --git a/examples/showindex.c b/examples/showindex.c
index e92a9c8..7f7c383 100644
--- a/examples/showindex.c
+++ b/examples/showindex.c
@@ -1,10 +1,21 @@
-#include <git2.h>
-#include <stdio.h>
-#include <string.h>
+/*
+ * libgit2 "showindex" example - shows how to extract data from the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
 
 int main (int argc, char** argv)
 {
-	git_repository *repo = NULL;
 	git_index *index;
 	unsigned int i, ecount;
 	char *dir = ".";
@@ -12,31 +23,24 @@
 	char out[41];
 	out[40] = '\0';
 
+	git_threads_init();
+
+	if (argc > 2)
+		fatal("usage: showindex [<repo-dir>]", NULL);
 	if (argc > 1)
 		dir = argv[1];
-	if (!dir || argc > 2) {
-		fprintf(stderr, "usage: showindex [<repo-dir>]\n");
-		return 1;
-	}
 
 	dirlen = strlen(dir);
 	if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
-		if (git_index_open(&index, dir) < 0) {
-			fprintf(stderr, "could not open index: %s\n", dir);
-			return 1;
-		}
+		check_lg2(git_index_open(&index, dir), "could not open index", dir);
 	} else {
-		if (git_repository_open_ext(&repo, dir, 0, NULL) < 0) {
-			fprintf(stderr, "could not open repository: %s\n", dir);
-			return 1;
-		}
-		if (git_repository_index(&index, repo) < 0) {
-			fprintf(stderr, "could not open repository index\n");
-			return 1;
-		}
+		git_repository *repo;
+		check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
+		check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
+		git_repository_free(repo);
 	}
 
-	git_index_read(index);
+	git_index_read(index, 0);
 
 	ecount = git_index_entrycount(index);
 	if (!ecount)
@@ -60,8 +64,7 @@
 	}
 
 	git_index_free(index);
-	git_repository_free(repo);
+	git_threads_shutdown();
 
 	return 0;
 }
-
diff --git a/examples/status.c b/examples/status.c
index 6890984..3adfe0d 100644
--- a/examples/status.c
+++ b/examples/status.c
@@ -1,33 +1,32 @@
 /*
- * Copyright (C) the libgit2 contributors. All rights reserved.
+ * libgit2 "status" example - shows how to use the status APIs
  *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
  */
-#include <git2.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
 
-enum {
-	FORMAT_DEFAULT   = 0,
-	FORMAT_LONG      = 1,
-	FORMAT_SHORT     = 2,
-	FORMAT_PORCELAIN = 3,
-};
-#define MAX_PATHSPEC 8
+#include "common.h"
 
-/*
+/**
  * This example demonstrates the use of the libgit2 status APIs,
  * particularly the `git_status_list` object, to roughly simulate the
  * output of running `git status`.  It serves as a simple example of
  * using those APIs to get basic status information.
  *
  * This does not have:
+ *
  * - Robust error handling
  * - Colorized or paginated output formatting
  *
  * This does have:
+ *
  * - Examples of translating command line arguments to the status
  *   options settings to mimic `git status` results.
  * - A sample status formatter that matches the default "long" format
@@ -35,34 +34,91 @@
  * - A sample status formatter that matches the "short" format
  */
 
-static void check(int error, const char *message, const char *extra)
+enum {
+	FORMAT_DEFAULT   = 0,
+	FORMAT_LONG      = 1,
+	FORMAT_SHORT     = 2,
+	FORMAT_PORCELAIN = 3,
+};
+
+#define MAX_PATHSPEC 8
+
+struct opts {
+    git_status_options statusopt;
+    char *repodir;
+    char *pathspec[MAX_PATHSPEC];
+    int npaths;
+    int format;
+    int zterm;
+    int showbranch;
+};
+
+static void parse_opts(struct opts *o, int argc, char *argv[]);
+static void show_branch(git_repository *repo, int format);
+static void print_long(git_status_list *status);
+static void print_short(git_repository *repo, git_status_list *status);
+
+int main(int argc, char *argv[])
 {
-	const git_error *lg2err;
-	const char *lg2msg = "", *lg2spacer = "";
+	git_repository *repo = NULL;
+	git_status_list *status;
+	struct opts o = { GIT_STATUS_OPTIONS_INIT, "." };
 
-	if (!error)
-		return;
+	git_threads_init();
 
-	if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
-		lg2msg = lg2err->message;
-		lg2spacer = " - ";
-	}
+	o.statusopt.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+	o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+		GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+		GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
 
-	if (extra)
-		fprintf(stderr, "%s '%s' [%d]%s%s\n",
-			message, extra, error, lg2spacer, lg2msg);
+	parse_opts(&o, argc, argv);
+
+	/**
+	 * Try to open the repository at the given path (or at the current
+	 * directory if none was given).
+	 */
+	check_lg2(git_repository_open_ext(&repo, o.repodir, 0, NULL),
+		  "Could not open repository", o.repodir);
+
+	if (git_repository_is_bare(repo))
+		fatal("Cannot report status on bare repository",
+			git_repository_path(repo));
+
+	/**
+	 * Run status on the repository
+	 *
+	 * We use `git_status_list_new()` to generate a list of status
+	 * information which lets us iterate over it at our
+	 * convenience and extract the data we want to show out of
+	 * each entry.
+	 *
+	 * You can use `git_status_foreach()` or
+	 * `git_status_foreach_ext()` if you'd prefer to execute a
+	 * callback for each entry. The latter gives you more control
+	 * about what results are presented.
+	 */
+	check_lg2(git_status_list_new(&status, repo, &o.statusopt),
+		  "Could not get status", NULL);
+
+	if (o.showbranch)
+		show_branch(repo, o.format);
+
+	if (o.format == FORMAT_LONG)
+		print_long(status);
 	else
-		fprintf(stderr, "%s [%d]%s%s\n",
-			message, error, lg2spacer, lg2msg);
+		print_short(repo, status);
 
-	exit(1);
+	git_status_list_free(status);
+	git_repository_free(repo);
+	git_threads_shutdown();
+
+	return 0;
 }
 
-static void fail(const char *message)
-{
-	check(-1, message, NULL);
-}
-
+/**
+ * If the user asked for the branch, let's show the short name of the
+ * branch.
+ */
 static void show_branch(git_repository *repo, int format)
 {
 	int error = 0;
@@ -71,14 +127,12 @@
 
 	error = git_repository_head(&head, repo);
 
-	if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+	if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
 		branch = NULL;
 	else if (!error) {
-		branch = git_reference_name(head);
-		if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
-			branch += strlen("refs/heads/");
+		branch = git_reference_shorthand(head);
 	} else
-		check(error, "failed to get current branch", NULL);
+		check_lg2(error, "failed to get current branch", NULL);
 
 	if (format == FORMAT_LONG)
 		printf("# On branch %s\n",
@@ -89,7 +143,11 @@
 	git_reference_free(head);
 }
 
-static void print_long(git_repository *repo, git_status_list *status)
+/**
+ * This function print out an output similar to git's status command
+ * in long form, including the command-line hints.
+ */
+static void print_long(git_status_list *status)
 {
 	size_t i, maxi = git_status_list_entrycount(status);
 	const git_status_entry *s;
@@ -97,9 +155,7 @@
 	int changed_in_workdir = 0, rm_in_workdir = 0;
 	const char *old_path, *new_path;
 
-	(void)repo;
-
-	/* print index changes */
+	/** Print index changes. */
 
 	for (i = 0; i < maxi; ++i) {
 		char *istatus = NULL;
@@ -148,16 +204,22 @@
 	}
 	header = 0;
 
-	/* print workdir changes to tracked files */
+	/** Print workdir changes to tracked files. */
 
 	for (i = 0; i < maxi; ++i) {
 		char *wstatus = NULL;
 
 		s = git_status_byindex(status, i);
 
+		/**
+		 * With `GIT_STATUS_OPT_INCLUDE_UNMODIFIED` (not used in this example)
+		 * `index_to_workdir` may not be `NULL` even if there are
+		 * no differences, in which case it will be a `GIT_DELTA_UNMODIFIED`.
+		 */
 		if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
 			continue;
 
+		/** Print out the output since we know the file has some changes */
 		if (s->status & GIT_STATUS_WT_MODIFIED)
 			wstatus = "modified: ";
 		if (s->status & GIT_STATUS_WT_DELETED)
@@ -191,9 +253,8 @@
 		changed_in_workdir = 1;
 		printf("#\n");
 	}
-	header = 0;
 
-	/* print untracked files */
+	/** Print untracked files. */
 
 	header = 0;
 
@@ -215,7 +276,7 @@
 
 	header = 0;
 
-	/* print ignored files */
+	/** Print ignored files. */
 
 	for (i = 0; i < maxi; ++i) {
 		s = git_status_byindex(status, i);
@@ -237,6 +298,10 @@
 		printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
 }
 
+/**
+ * This version of the output prefixes each path with two status
+ * columns and shows submodule status information.
+ */
 static void print_short(git_repository *repo, git_status_list *status)
 {
 	size_t i, maxi = git_status_list_entrycount(status);
@@ -287,6 +352,10 @@
 		if (istatus == '?' && wstatus == '?')
 			continue;
 
+		/**
+		 * A commit in a tree is how submodules are stored, so
+		 * let's go take a look at its status.
+		 */
 		if (s->index_to_workdir &&
 			s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
 		{
@@ -308,6 +377,10 @@
 			}
 		}
 
+		/**
+		 * Now that we have all the information, it's time to format the output.
+		 */
+
 		if (s->head_to_index) {
 			a = s->head_to_index->old_file.path;
 			b = s->head_to_index->new_file.path;
@@ -341,103 +414,61 @@
 	}
 }
 
-int main(int argc, char *argv[])
+/**
+ * Parse options that git's status command supports.
+ */
+static void parse_opts(struct opts *o, int argc, char *argv[])
 {
-	git_repository *repo = NULL;
-	int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
-	git_status_options opt = GIT_STATUS_OPTIONS_INIT;
-	git_status_list *status;
-	char *repodir = ".", *pathspec[MAX_PATHSPEC];
+	struct args_info args = ARGS_INFO_INIT;
 
-	opt.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
-	opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
-		GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
-		GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+	for (args.pos = 1; args.pos < argc; ++args.pos) {
+		char *a = argv[args.pos];
 
-	for (i = 1; i < argc; ++i) {
-		if (argv[i][0] != '-') {
-			if (npaths < MAX_PATHSPEC)
-				pathspec[npaths++] = argv[i];
+		if (a[0] != '-') {
+			if (o->npaths < MAX_PATHSPEC)
+				o->pathspec[o->npaths++] = a;
 			else
-				fail("Example only supports a limited pathspec");
+				fatal("Example only supports a limited pathspec", NULL);
 		}
-		else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
-			format = FORMAT_SHORT;
-		else if (!strcmp(argv[i], "--long"))
-			format = FORMAT_LONG;
-		else if (!strcmp(argv[i], "--porcelain"))
-			format = FORMAT_PORCELAIN;
-		else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
-			showbranch = 1;
-		else if (!strcmp(argv[i], "-z")) {
-			zterm = 1;
-			if (format == FORMAT_DEFAULT)
-				format = FORMAT_PORCELAIN;
+		else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
+			o->format = FORMAT_SHORT;
+		else if (!strcmp(a, "--long"))
+			o->format = FORMAT_LONG;
+		else if (!strcmp(a, "--porcelain"))
+			o->format = FORMAT_PORCELAIN;
+		else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
+			o->showbranch = 1;
+		else if (!strcmp(a, "-z")) {
+			o->zterm = 1;
+			if (o->format == FORMAT_DEFAULT)
+				o->format = FORMAT_PORCELAIN;
 		}
-		else if (!strcmp(argv[i], "--ignored"))
-			opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
-		else if (!strcmp(argv[i], "-uno") ||
-				 !strcmp(argv[i], "--untracked-files=no"))
-			opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
-		else if (!strcmp(argv[i], "-unormal") ||
-				 !strcmp(argv[i], "--untracked-files=normal"))
-			opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
-		else if (!strcmp(argv[i], "-uall") ||
-				 !strcmp(argv[i], "--untracked-files=all"))
-			opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+		else if (!strcmp(a, "--ignored"))
+			o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
+		else if (!strcmp(a, "-uno") ||
+				 !strcmp(a, "--untracked-files=no"))
+			o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+		else if (!strcmp(a, "-unormal") ||
+				 !strcmp(a, "--untracked-files=normal"))
+			o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+		else if (!strcmp(a, "-uall") ||
+				 !strcmp(a, "--untracked-files=all"))
+			o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
 				GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
-		else if (!strcmp(argv[i], "--ignore-submodules=all"))
-			opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
-		else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
-			repodir = argv[i] + strlen("--git-dir=");
+		else if (!strcmp(a, "--ignore-submodules=all"))
+			o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+		else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
+			o->repodir = a + strlen("--git-dir=");
 		else
-			check(-1, "Unsupported option", argv[i]);
+			check_lg2(-1, "Unsupported option", a);
 	}
 
-	if (format == FORMAT_DEFAULT)
-		format = FORMAT_LONG;
-	if (format == FORMAT_LONG)
-		showbranch = 1;
-	if (npaths > 0) {
-		opt.pathspec.strings = pathspec;
-		opt.pathspec.count   = npaths;
+	if (o->format == FORMAT_DEFAULT)
+		o->format = FORMAT_LONG;
+	if (o->format == FORMAT_LONG)
+		o->showbranch = 1;
+	if (o->npaths > 0) {
+		o->statusopt.pathspec.strings = o->pathspec;
+		o->statusopt.pathspec.count   = o->npaths;
 	}
-
-	/*
-	 * Try to open the repository at the given path (or at the current
-	 * directory if none was given).
-	 */
-	check(git_repository_open_ext(&repo, repodir, 0, NULL),
-		  "Could not open repository", repodir);
-
-	if (git_repository_is_bare(repo))
-		fail("Cannot report status on bare repository");
-
-	/*
-	 * Run status on the repository
-	 *
-	 * Because we want to simluate a full "git status" run and want to
-	 * support some command line options, we use `git_status_foreach_ext()`
-	 * instead of just the plain status call.  This allows (a) iterating
-	 * over the index and then the workdir and (b) extra flags that control
-	 * which files are included.  If you just want simple status (e.g. to
-	 * enumerate files that are modified) then you probably don't need the
-	 * extended API.
-	 */
-	check(git_status_list_new(&status, repo, &opt),
-		  "Could not get status", NULL);
-
-	if (showbranch)
-		show_branch(repo, format);
-
-	if (format == FORMAT_LONG)
-		print_long(repo, status);
-	else
-		print_short(repo, status);
-
-	git_status_list_free(status);
-	git_repository_free(repo);
-
-	return 0;
 }
-
diff --git a/examples/test/test-rev-list.sh b/examples/test/test-rev-list.sh
index bc0eea7..aa645be 100755
--- a/examples/test/test-rev-list.sh
+++ b/examples/test/test-rev-list.sh
@@ -4,7 +4,7 @@
 ROOT="$(dirname "$(dirname "$(dirname "$THIS_FILE")")")"
 PROGRAM="$ROOT"/examples/rev-list
 LIBDIR="$ROOT"/build
-REPO="$ROOT"/tests-clar/resources/testrepo.git
+REPO="$ROOT"/tests/resources/testrepo.git
 
 cd "$REPO"
 
diff --git a/git.git-authors b/git.git-authors
index 3fcf27f..7d4c95b 100644
--- a/git.git-authors
+++ b/git.git-authors
@@ -68,3 +68,4 @@
 ok	Shawn O. Pearce <spearce@spearce.org>
 ok	Steffen Prohaska <prohaska@zib.de>
 ok	Sven Verdoolaege <skimo@kotnet.org>
+ok	Torsten Bögershausen <tboegi@web.de>
diff --git a/include/git2.h b/include/git2.h
index 5f9fc48..d00a20a 100644
--- a/include/git2.h
+++ b/include/git2.h
@@ -8,53 +8,52 @@
 #ifndef INCLUDE_git_git_h__
 #define INCLUDE_git_git_h__
 
-#include "git2/version.h"
-
-#include "git2/common.h"
-#include "git2/threads.h"
-#include "git2/errors.h"
-
-#include "git2/types.h"
-
-#include "git2/oid.h"
-#include "git2/signature.h"
-#include "git2/odb.h"
-
-#include "git2/repository.h"
-#include "git2/revwalk.h"
-#include "git2/merge.h"
-#include "git2/graph.h"
-#include "git2/refs.h"
-#include "git2/reflog.h"
-#include "git2/revparse.h"
-
-#include "git2/object.h"
-#include "git2/blob.h"
-#include "git2/commit.h"
-#include "git2/tag.h"
-#include "git2/tree.h"
-#include "git2/diff.h"
-
-#include "git2/index.h"
-#include "git2/config.h"
-#include "git2/transport.h"
-#include "git2/remote.h"
-#include "git2/clone.h"
-#include "git2/checkout.h"
-#include "git2/push.h"
-
 #include "git2/attr.h"
-#include "git2/ignore.h"
+#include "git2/blob.h"
+#include "git2/blame.h"
 #include "git2/branch.h"
-#include "git2/refspec.h"
-#include "git2/net.h"
-#include "git2/status.h"
+#include "git2/buffer.h"
+#include "git2/checkout.h"
+#include "git2/clone.h"
+#include "git2/commit.h"
+#include "git2/common.h"
+#include "git2/config.h"
+#include "git2/diff.h"
+#include "git2/errors.h"
+#include "git2/filter.h"
+#include "git2/graph.h"
+#include "git2/ignore.h"
+#include "git2/index.h"
 #include "git2/indexer.h"
-#include "git2/submodule.h"
-#include "git2/notes.h"
-#include "git2/reset.h"
+#include "git2/merge.h"
 #include "git2/message.h"
+#include "git2/net.h"
+#include "git2/notes.h"
+#include "git2/object.h"
+#include "git2/odb.h"
+#include "git2/oid.h"
 #include "git2/pack.h"
+#include "git2/patch.h"
+#include "git2/pathspec.h"
+#include "git2/push.h"
+#include "git2/refdb.h"
+#include "git2/reflog.h"
+#include "git2/refs.h"
+#include "git2/refspec.h"
+#include "git2/remote.h"
+#include "git2/repository.h"
+#include "git2/reset.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/signature.h"
 #include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/submodule.h"
+#include "git2/tag.h"
+#include "git2/threads.h"
+#include "git2/transport.h"
+#include "git2/tree.h"
+#include "git2/types.h"
+#include "git2/version.h"
 
 #endif
diff --git a/include/git2/blame.h b/include/git2/blame.h
new file mode 100644
index 0000000..73bcc5b
--- /dev/null
+++ b/include/git2/blame.h
@@ -0,0 +1,198 @@
+/*
+ * 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_blame_h__
+#define INCLUDE_git_blame_h__
+
+#include "common.h"
+#include "oid.h"
+
+/**
+ * @file git2/blame.h
+ * @brief Git blame routines
+ * @defgroup git_blame Git blame routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Flags for indicating option behavior for git_blame APIs.
+ */
+typedef enum {
+	/** Normal blame, the default */
+	GIT_BLAME_NORMAL = 0,
+	/** Track lines that have moved within a file (like `git blame -M`).
+	 * NOT IMPLEMENTED. */
+	GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
+	/** Track lines that have moved across files in the same commit (like `git blame -C`).
+	 * NOT IMPLEMENTED. */
+	GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
+	/** Track lines that have been copied from another file that exists in the
+	 * same commit (like `git blame -CC`). Implies SAME_FILE.
+	 * NOT IMPLEMENTED. */
+	GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
+	/** Track lines that have been copied from another file that exists in *any*
+	 * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
+	 * NOT IMPLEMENTED. */
+	GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
+} git_blame_flag_t;
+
+/**
+ * Blame options structure
+ *
+ * Use zeros to indicate default settings.  It's easiest to use the
+ * `GIT_BLAME_OPTIONS_INIT` macro:
+ *     git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+ *
+ * - `flags` is a combination of the `git_blame_flag_t` values above.
+ * - `min_match_characters` is the lower bound on the number of alphanumeric
+ *   characters that must be detected as moving/copying within a file for it to
+ *   associate those lines with the parent commit. The default value is 20.
+ *   This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
+ *   flags are specified.
+ * - `newest_commit` is the id of the newest commit to consider.  The default
+ *                   is HEAD.
+ * - `oldest_commit` is the id of the oldest commit to consider.  The default
+ *                   is the first commit encountered with a NULL parent.
+ *	- `min_line` is the first line in the file to blame.  The default is 1 (line
+ *	             numbers start with 1).
+ *	- `max_line` is the last line in the file to blame.  The default is the last
+ *	             line of the file.
+ */
+
+typedef struct git_blame_options {
+	unsigned int version;
+
+	uint32_t flags;
+	uint16_t min_match_characters;
+	git_oid newest_commit;
+	git_oid oldest_commit;
+	uint32_t min_line;
+	uint32_t max_line;
+} git_blame_options;
+
+#define GIT_BLAME_OPTIONS_VERSION 1
+#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
+
+/**
+ * Structure that represents a blame hunk.
+ *
+ * - `lines_in_hunk` is the number of lines in this hunk
+ * - `final_commit_id` is the OID of the commit where this line was last
+ *   changed.
+ * - `final_start_line_number` is the 1-based line number where this hunk
+ *   begins, in the final version of the file
+ * - `orig_commit_id` is the OID of the commit where this hunk was found.  This
+ *   will usually be the same as `final_commit_id`, except when
+ *   `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
+ * - `orig_path` is the path to the file where this hunk originated, as of the
+ *   commit specified by `orig_commit_id`.
+ * - `orig_start_line_number` is the 1-based line number where this hunk begins
+ *   in the file named by `orig_path` in the commit specified by
+ *   `orig_commit_id`.
+ * - `boundary` is 1 iff the hunk has been tracked to a boundary commit (the
+ *   root, or the commit specified in git_blame_options.oldest_commit)
+ */
+typedef struct git_blame_hunk {
+	uint16_t lines_in_hunk;
+
+	git_oid final_commit_id;
+	uint16_t final_start_line_number;
+	git_signature *final_signature;
+
+	git_oid orig_commit_id;
+	const char *orig_path;
+	uint16_t orig_start_line_number;
+	git_signature *orig_signature;
+
+	char boundary;
+} git_blame_hunk;
+
+
+/* Opaque structure to hold blame results */
+typedef struct git_blame git_blame;
+
+/**
+ * Gets the number of hunks that exist in the blame structure.
+ */
+GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame);
+
+/**
+ * Gets the blame hunk at the given index.
+ *
+ * @param blame the blame structure to query
+ * @param index index of the hunk to retrieve
+ * @return the hunk at the given index, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
+		git_blame *blame,
+		uint32_t index);
+
+/**
+ * Gets the hunk that relates to the given line number in the newest commit.
+ *
+ * @param blame the blame structure to query
+ * @param lineno the (1-based) line number to find a hunk for
+ * @return the hunk that contains the given line, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
+		git_blame *blame,
+		uint32_t lineno);
+
+/**
+ * Get the blame for a single file.
+ *
+ * @param out pointer that will receive the blame object
+ * @param repo repository whose history is to be walked
+ * @param path path to file to consider
+ * @param options options for the blame operation.  If NULL, this is treated as
+ *                though GIT_BLAME_OPTIONS_INIT were passed.
+ * @return 0 on success, or an error code. (use giterr_last for information
+ *         about the error.)
+ */
+GIT_EXTERN(int) git_blame_file(
+		git_blame **out,
+		git_repository *repo,
+		const char *path,
+		git_blame_options *options);
+
+
+/**
+ * Get blame data for a file that has been modified in memory. The `reference`
+ * parameter is a pre-calculated blame for the in-odb history of the file. This
+ * means that once a file blame is completed (which can be expensive), updating
+ * the buffer blame is very fast.
+ *
+ * Lines that differ between the buffer and the committed version are marked as
+ * having a zero OID for their final_commit_id.
+ *
+ * @param out pointer that will receive the resulting blame data
+ * @param reference cached blame from the history of the file (usually the output
+ *                  from git_blame_file)
+ * @param buffer the (possibly) modified contents of the file
+ * @param buffer_len number of valid bytes in the buffer
+ * @return 0 on success, or an error code. (use giterr_last for information
+ *         about the error)
+ */
+GIT_EXTERN(int) git_blame_buffer(
+		git_blame **out,
+		git_blame *reference,
+		const char *buffer,
+		uint32_t buffer_len);
+
+/**
+ * Free memory allocated by git_blame_file or git_blame_buffer.
+ *
+ * @param blame the blame structure to free
+ */
+GIT_EXTERN(void) git_blame_free(git_blame *blame);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/blob.h b/include/git2/blob.h
index 8fca489..dcab4fb 100644
--- a/include/git2/blob.h
+++ b/include/git2/blob.h
@@ -11,6 +11,7 @@
 #include "types.h"
 #include "oid.h"
 #include "object.h"
+#include "buffer.h"
 
 /**
  * @file git2/blob.h
@@ -96,6 +97,37 @@
 GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
 
 /**
+ * Get a buffer with the filtered content of a blob.
+ *
+ * This applies filters as if the blob was being checked out to the
+ * working directory under the specified filename.  This may apply
+ * CRLF filtering or other types of changes depending on the file
+ * attributes set for the blob and the content detected in it.
+ *
+ * The output is written into a `git_buf` which the caller must free
+ * when done (via `git_buf_free`).
+ *
+ * If no filters need to be applied, then the `out` buffer will just be
+ * populated with a pointer to the raw content of the blob.  In that case,
+ * be careful to *not* free the blob until done with the buffer.  To keep
+ * the data detached from the blob, call `git_buf_grow` on the buffer
+ * with a `want_size` of 0 and the buffer will be reallocated to be
+ * detached from the blob.
+ *
+ * @param out The git_buf to be filled in
+ * @param blob Pointer to the blob
+ * @param as_path Path used for file attribute lookups, etc.
+ * @param check_for_binary_data Should this test if blob content contains
+ *        NUL bytes / looks like binary data before applying filters?
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_blob_filtered_content(
+	git_buf *out,
+	git_blob *blob,
+	const char *as_path,
+	int check_for_binary_data);
+
+/**
  * Read a file from the working folder of a repository
  * and write it to the Object Database as a loose blob
  *
diff --git a/include/git2/branch.h b/include/git2/branch.h
index de414e9..44d6fd9 100644
--- a/include/git2/branch.h
+++ b/include/git2/branch.h
@@ -66,33 +66,41 @@
  */
 GIT_EXTERN(int) git_branch_delete(git_reference *branch);
 
-typedef int (*git_branch_foreach_cb)(
-	const char *branch_name,
-	git_branch_t branch_type,
-	void *payload);
+/** Iterator type for branches */
+typedef struct git_branch_iterator git_branch_iterator;
 
 /**
- * Loop over all the branches and issue a callback for each one.
+ * Create an iterator which loops over the requested branches.
  *
- * If the callback returns a non-zero value, this will stop looping.
- *
+ * @param out the iterator
  * @param repo Repository where to find the branches.
- *
  * @param list_flags Filtering flags for the branch
  * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
  * or a combination of the two.
  *
- * @param branch_cb Callback to invoke per found branch.
- *
- * @param payload Extra parameter to callback function.
- *
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @return 0 on success  or an error code
  */
-GIT_EXTERN(int) git_branch_foreach(
+GIT_EXTERN(int) git_branch_iterator_new(
+	git_branch_iterator **out,
 	git_repository *repo,
-	unsigned int list_flags,
-	git_branch_foreach_cb branch_cb,
-	void *payload);
+	git_branch_t list_flags);
+
+/**
+ * Retrieve the next branch from the iterator
+ *
+ * @param out the reference
+ * @param out_type the type of branch (local or remote-tracking)
+ * @param iter the branch iterator
+ * @return 0 on success, GIT_ITEROVER if there are no more branches or an error code.
+ */
+GIT_EXTERN(int) git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *iter);
+
+/**
+ * Free a branch iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
 
 /**
  * Move/rename an existing local branch reference.
diff --git a/include/git2/buffer.h b/include/git2/buffer.h
new file mode 100644
index 0000000..36a61e6
--- /dev/null
+++ b/include/git2/buffer.h
@@ -0,0 +1,112 @@
+/*
+ * 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_buf_h__
+#define INCLUDE_git_buf_h__
+
+#include "common.h"
+
+/**
+ * @file git2/buffer.h
+ * @brief Buffer export structure
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * A data buffer for exporting data from libgit2
+ *
+ * Sometimes libgit2 wants to return an allocated data buffer to the
+ * caller and have the caller take responsibility for freeing that memory.
+ * This can be awkward if the caller does not have easy access to the same
+ * allocation functions that libgit2 is using.  In those cases, libgit2
+ * will fill in a `git_buf` and the caller can use `git_buf_free()` to
+ * release it when they are done.
+ *
+ * A `git_buf` may also be used for the caller to pass in a reference to
+ * a block of memory they hold.  In this case, libgit2 will not resize or
+ * free the memory, but will read from it as needed.
+ *
+ * A `git_buf` is a public structure with three fields:
+ *
+ * - `ptr` points to the start of the allocated memory.  If it is NULL,
+ *   then the `git_buf` is considered empty and libgit2 will feel free
+ *   to overwrite it with new data.
+ *
+ * - `size` holds the size (in bytes) of the data that is actually used.
+ *
+ * - `asize` holds the known total amount of allocated memory if the `ptr`
+ *    was allocated by libgit2.  It may be larger than `size`.  If `ptr`
+ *    was not allocated by libgit2 and should not be resized and/or freed,
+ *    then `asize` will be set to zero.
+ *
+ * Some APIs may occasionally do something slightly unusual with a buffer,
+ * such as setting `ptr` to a value that was passed in by the user.  In
+ * those cases, the behavior will be clearly documented by the API.
+ */
+typedef struct {
+	char   *ptr;
+	size_t asize, size;
+} git_buf;
+
+/**
+ * Static initializer for git_buf from static buffer
+ */
+#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
+
+/**
+ * Free the memory referred to by the git_buf.
+ *
+ * Note that this does not free the `git_buf` itself, just the memory
+ * pointed to by `buffer->ptr`.  This will not free the memory if it looks
+ * like it was not allocated internally, but it will clear the buffer back
+ * to the empty state.
+ *
+ * @param buffer The buffer to deallocate
+ */
+GIT_EXTERN(void) git_buf_free(git_buf *buffer);
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accomodate the target size.
+ *
+ * If the buffer refers to memory that was not allocated by libgit2 (i.e.
+ * the `asize` field is zero), then `ptr` will be replaced with a newly
+ * allocated block of data.  Be careful so that memory allocated by the
+ * caller is not lost.  As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param buffer The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
+
+/**
+ * Set buffer to a copy of some raw data.
+ *
+ * @param buffer The buffer to set
+ * @param data The data to copy into the buffer
+ * @param datalen The length of the data to copy into the buffer
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_set(
+	git_buf *buffer, const void *data, size_t datalen);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
index a086408..efafdc3 100644
--- a/include/git2/checkout.h
+++ b/include/git2/checkout.h
@@ -131,6 +131,13 @@
 	/** Don't refresh index/config/etc before doing checkout */
 	GIT_CHECKOUT_NO_REFRESH = (1u << 9),
 
+	/** Allow checkout to skip unmerged files */
+	GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
+	/** For unmerged files, checkout stage 2 from index */
+	GIT_CHECKOUT_USE_OURS = (1u << 11),
+	/** For unmerged files, checkout stage 3 from index */
+	GIT_CHECKOUT_USE_THEIRS = (1u << 12),
+
 	/** Treat pathspec as simple list of exact match file paths */
 	GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
 
@@ -141,13 +148,6 @@
 	 * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
 	 */
 
-	/** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
-	GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
-	/** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
-	GIT_CHECKOUT_USE_OURS = (1u << 11),
-	/** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
-	GIT_CHECKOUT_USE_THEIRS = (1u << 12),
-
 	/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
 	GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
 	/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
@@ -238,6 +238,9 @@
 	git_tree *baseline; /** expected content of workdir, defaults to HEAD */
 
 	const char *target_directory; /** alternative checkout path to workdir */
+
+	const char *our_label; /** the name of the "our" side of conflicts */
+	const char *their_label; /** the name of the "their" side of conflicts */
 } git_checkout_opts;
 
 #define GIT_CHECKOUT_OPTS_VERSION 1
@@ -249,13 +252,13 @@
  *
  * @param repo repository to check out (must be non-bare)
  * @param opts specifies checkout options (may be NULL)
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
  * branch, GIT_ERROR otherwise (use giterr_last for information
  * about the error)
  */
 GIT_EXTERN(int) git_checkout_head(
 	git_repository *repo,
-	git_checkout_opts *opts);
+	const git_checkout_opts *opts);
 
 /**
  * Updates files in the working tree to match the content of the index.
@@ -269,7 +272,7 @@
 GIT_EXTERN(int) git_checkout_index(
 	git_repository *repo,
 	git_index *index,
-	git_checkout_opts *opts);
+	const git_checkout_opts *opts);
 
 /**
  * Updates files in the index and working tree to match the content of the
@@ -277,7 +280,7 @@
  *
  * @param repo repository to check out (must be non-bare)
  * @param treeish a commit, tag or tree which content will be used to update
- * the working directory
+ * the working directory (or NULL to use HEAD)
  * @param opts specifies checkout options (may be NULL)
  * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
  * about the error)
@@ -285,7 +288,7 @@
 GIT_EXTERN(int) git_checkout_tree(
 	git_repository *repo,
 	const git_object *treeish,
-	git_checkout_opts *opts);
+	const git_checkout_opts *opts);
 
 /** @} */
 GIT_END_DECL
diff --git a/include/git2/clone.h b/include/git2/clone.h
index 5858b4e..331cf92 100644
--- a/include/git2/clone.h
+++ b/include/git2/clone.h
@@ -35,30 +35,12 @@
  *   set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
  * - `bare` should be set to zero to create a standard repo, non-zero for
  *   a bare repo
- * - `fetch_progress_cb` is optional callback for fetch progress. Be aware that
- *   this is called inline with network and indexing operations, so performance
- *   may be affected.
- * - `fetch_progress_payload` is payload for fetch_progress_cb
+ * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's
+ *   certificate should be ignored.
  *
  *   ** "origin" remote options: **
  * - `remote_name` is the name given to the "origin" remote.  The default is
  *   "origin".
- * - `pushurl` is a URL to be used for pushing.  NULL means use the fetch url.
- * - `fetch_spec` is the fetch specification to be used for fetching.  NULL
- *   results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.
- * - `push_spec` is the fetch specification to be used for pushing.  NULL means
- *   use the same spec as for fetching.
- * - `cred_acquire_cb` is a callback to be used if credentials are required
- *   during the initial fetch.
- * - `cred_acquire_payload` is the payload for the above callback.
- * - `transport_flags` is flags used to create transport if no transport is
- *   provided.
- * - `transport` is a custom transport to be used for the initial fetch.  NULL
- *   means use the transport autodetected from the URL.
- * - `remote_callbacks` may be used to specify custom progress callbacks for
- *   the origin remote before the fetch is initiated.
- * - `remote_autotag` may be used to specify the autotag setting before the
- *   initial fetch.  The default is GIT_REMOTE_DOWNLOAD_TAGS_ALL.
  * - `checkout_branch` gives the name of the branch to checkout. NULL means
  *   use the remote's HEAD.
  */
@@ -67,29 +49,23 @@
 	unsigned int version;
 
 	git_checkout_opts checkout_opts;
-	int bare;
-	git_transfer_progress_callback fetch_progress_cb;
-	void *fetch_progress_payload;
+	git_remote_callbacks remote_callbacks;
 
+	int bare;
+	int ignore_cert_errors;
 	const char *remote_name;
-	const char *pushurl;
-	const char *fetch_spec;
-	const char *push_spec;
-	git_cred_acquire_cb cred_acquire_cb;
-	void *cred_acquire_payload;
-    git_transport_flags_t transport_flags;
-	git_transport *transport;
-	git_remote_callbacks *remote_callbacks;
-	git_remote_autotag_option_t remote_autotag;
 	const char* checkout_branch;
 } git_clone_options;
 
 #define GIT_CLONE_OPTIONS_VERSION 1
-#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}}
+#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
 
 /**
- * Clone a remote repository, and checkout the branch pointed to by the remote
- * HEAD.
+ * Clone a remote repository.
+ *
+ * This version handles the simple case. If you'd like to create the
+ * repository or remote with non-default settings, you can create and
+ * configure them and then use `git_clone_into()`.
  *
  * @param out pointer that will receive the resulting repository object
  * @param url the remote repository to clone
@@ -105,6 +81,22 @@
 		const char *local_path,
 		const git_clone_options *options);
 
+/**
+ * Clone into a repository
+ *
+ * After creating the repository and remote and configuring them for
+ * paths and callbacks respectively, you can call this function to
+ * perform the clone operation and optionally checkout files.
+ *
+ * @param repo the repository to use
+ * @param remote the remote repository to clone from
+ * @param co_opts options to use during checkout
+ * @param branch the branch to checkout after the clone, pass NULL for the remote's
+ * default branch
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/commit.h b/include/git2/commit.h
index 544d21d..a08cf1c 100644
--- a/include/git2/commit.h
+++ b/include/git2/commit.h
@@ -24,17 +24,24 @@
 /**
  * Lookup a commit object from a repository.
  *
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
+ *
  * @param commit pointer to the looked up commit
  * @param repo the repo to use when locating the commit.
  * @param id identity of the commit to locate. If the object is
  *		an annotated tag it will be peeled back to the commit.
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
+GIT_EXTERN(int) git_commit_lookup(
+	git_commit **commit, git_repository *repo, const git_oid *id);
 
 /**
- * Lookup a commit object from a repository,
- * given a prefix of its identifier (short id).
+ * Lookup a commit object from a repository, given a prefix of its
+ * identifier (short id).
+ *
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
  *
  * @see git_object_lookup_prefix
  *
@@ -45,7 +52,8 @@
  * @param len the length of the short identifier
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
+GIT_EXTERN(int) git_commit_lookup_prefix(
+	git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
 
 /**
  * Close an open commit
@@ -92,12 +100,23 @@
 /**
  * Get the full message of a commit.
  *
+ * The returned message will be slightly prettified by removing any
+ * potential leading newlines.
+ *
  * @param commit a previously loaded commit.
  * @return the message of a commit
  */
 GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
 
 /**
+ * Get the full raw message of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the raw message of a commit
+ */
+GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
+
+/**
  * Get the commit time (i.e. committer time) of a commit.
  *
  * @param commit a previously loaded commit.
@@ -130,6 +149,14 @@
 GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
 
 /**
+ * Get the full raw text of the commit header.
+ *
+ * @param commit a previously loaded commit
+ * @return the header text of the commit
+ */
+GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
+
+/**
  * Get the tree pointed to by a commit.
  *
  * @param tree_out pointer where to store the tree object
diff --git a/include/git2/common.h b/include/git2/common.h
index b52e139..dca0c9c 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -105,7 +105,8 @@
  */
 typedef enum {
 	GIT_CAP_THREADS			= ( 1 << 0 ),
-	GIT_CAP_HTTPS			= ( 1 << 1 )
+	GIT_CAP_HTTPS			= ( 1 << 1 ),
+	GIT_CAP_SSH				= ( 1 << 2 ),
 } git_cap_t;
 
 /**
@@ -135,7 +136,9 @@
 	GIT_OPT_SET_CACHE_OBJECT_LIMIT,
 	GIT_OPT_SET_CACHE_MAX_SIZE,
 	GIT_OPT_ENABLE_CACHING,
-	GIT_OPT_GET_CACHED_MEMORY
+	GIT_OPT_GET_CACHED_MEMORY,
+	GIT_OPT_GET_TEMPLATE_PATH,
+	GIT_OPT_SET_TEMPLATE_PATH
 } git_libgit2_opt_t;
 
 /**
@@ -209,6 +212,18 @@
  *		> Get the current bytes in cache and the maximum that would be
  *		> allowed in the cache.
  *
+ *	* opts(GIT_OPT_GET_TEMPLATE_PATH, char *out, size_t len)
+ *
+ *		> Get the default template path.
+ *		> The path is written to the `out`
+ *		> buffer up to size `len`.  Returns GIT_EBUFS if buffer is too small.
+ *
+ *	* opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
+ *
+ *		> Set the default template path.
+ *		>
+ *		> - `path` directory of template.
+ *
  * @param option Option key
  * @param ... value to set the option
  * @return 0 on success, <0 on failure
diff --git a/include/git2/config.h b/include/git2/config.h
index 827d435..95da4bc 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -61,6 +61,7 @@
 } git_config_entry;
 
 typedef int  (*git_config_foreach_cb)(const git_config_entry *, void *);
+typedef struct git_config_iterator git_config_iterator;
 
 typedef enum {
 	GIT_CVAR_FALSE = 0,
@@ -327,7 +328,7 @@
 GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
 
 /**
- * Get each value of a multivar.
+ * Get each value of a multivar in a foreach callback
  *
  * The callback will be called on each variable found
  *
@@ -338,7 +339,34 @@
  * @param callback the function to be called on each value of the variable
  * @param payload opaque pointer to pass to the callback
  */
-GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+
+/**
+ * Get each value of a multivar
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ */
+GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
+
+/**
+ * Return the current entry and advance the iterator
+ *
+ * @param entry pointer to store the entry
+ * @param iter the iterator
+ * @return 0 or an error code. GIT_ITEROVER if the iteration has completed
+ */
+GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
+
+/**
+ * Free a config iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
 
 /**
  * Set the value of an integer config variable in the config file
@@ -407,6 +435,17 @@
 GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
 
 /**
+ * Deletes one or several entries from a multivar in the local config file.
+ *
+ * @param cfg where to look for the variables
+ * @param name the variable's name
+ * @param regexp a regular expression to indicate which values to delete
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp);
+
+/**
  * Perform an operation on each config variable.
  *
  * The callback receives the normalized name and value of each variable
@@ -425,6 +464,29 @@
 	void *payload);
 
 /**
+ * Iterate over all the config variables
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ */
+GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
+
+/**
+ * Iterate over all the config variables whose name matches a pattern
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ * @param regexp regular expression to match the names
+ */
+GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
+
+/**
  * Perform an operation on each config variable matching a regular expression.
  *
  * This behaviors like `git_config_foreach` with an additional filter of a
@@ -535,6 +597,25 @@
 GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
 
 
+/**
+ * Perform an operation on each config variable in given config backend
+ * matching a regular expression.
+ *
+ * This behaviors like `git_config_foreach_match` except instead of all config
+ * entries it just enumerates through the given backend entry.
+ *
+ * @param backend where to get the variables from
+ * @param regexp regular expression to match against config names (can be NULL)
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ */
+GIT_EXTERN(int) git_config_backend_foreach_match(
+	git_config_backend *backend,
+	const char *regexp,
+	int (*fn)(const git_config_entry *, void *),
+	void *data);
+
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h
index 5d93cf4..1d88092 100644
--- a/include/git2/cred_helpers.h
+++ b/include/git2/cred_helpers.h
@@ -7,7 +7,7 @@
 #ifndef INCLUDE_git_cred_helpers_h__
 #define INCLUDE_git_cred_helpers_h__
 
-#include "git2/transport.h"
+#include "transport.h"
 
 /**
  * @file git2/cred_helpers.h
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 43029c4..61d2883 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -20,34 +20,38 @@
  * Overview
  * --------
  *
- * Calculating diffs is generally done in two phases: building a diff list
- * then traversing the diff list.  This makes is easier to share logic
- * across the various types of diffs (tree vs tree, workdir vs index, etc.),
- * and also allows you to insert optional diff list post-processing phases,
- * such as rename detected, in between the steps.  When you are done with a
- * diff list object, it must be freed.
+ * Calculating diffs is generally done in two phases: building a list of
+ * diffs then traversing it.  This makes is easier to share logic across
+ * the various types of diffs (tree vs tree, workdir vs index, etc.), and
+ * also allows you to insert optional diff post-processing phases,
+ * such as rename detection, in between the steps.  When you are done with
+ * a diff object, it must be freed.
  *
  * Terminology
  * -----------
  *
  * To understand the diff APIs, you should know the following terms:
  *
- * - A `diff` or `diff list` represents the cumulative list of differences
- *   between two snapshots of a repository (possibly filtered by a set of
- *   file name patterns).  This is the `git_diff_list` object.
+ * - A `diff` represents the cumulative list of differences between two
+ *   snapshots of a repository (possibly filtered by a set of file name
+ *   patterns).  This is the `git_diff` object.
+ *
  * - A `delta` is a file pair with an old and new revision.  The old version
  *   may be absent if the file was just created and the new version may be
  *   absent if the file was deleted.  A diff is mostly just a list of deltas.
+ *
  * - A `binary` file / delta is a file (or pair) for which no text diffs
- *   should be generated.  A diff list can contain delta entries that are
+ *   should be generated.  A diff can contain delta entries that are
  *   binary, but no diff content will be output for those files.  There is
  *   a base heuristic for binary detection and you can further tune the
  *   behavior with git attributes or diff flags and option settings.
+ *
  * - A `hunk` is a span of modified lines in a delta along with some stable
  *   surrounding context.  You can configure the amount of context and other
  *   properties of how hunks are generated.  Each hunk also comes with a
  *   header that described where it starts and ends in both the old and new
  *   versions in the delta.
+ *
  * - A `line` is a range of characters inside a hunk.  It could be a context
  *   line (i.e. in both old and new versions), an added line (i.e. only in
  *   the new version), or a removed line (i.e. only in the old version).
@@ -68,100 +72,125 @@
 typedef enum {
 	/** Normal diff, the default */
 	GIT_DIFF_NORMAL = 0,
+
+	/*
+	 * Options controlling which files will be in the diff
+	 */
+
 	/** Reverse the sides of the diff */
-	GIT_DIFF_REVERSE = (1 << 0),
-	/** Treat all files as text, disabling binary attributes & detection */
-	GIT_DIFF_FORCE_TEXT = (1 << 1),
-	/** Ignore all whitespace */
-	GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
-	/** Ignore changes in amount of whitespace */
-	GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
-	/** Ignore whitespace at end of line */
-	GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
-	/** Exclude submodules from the diff completely */
-	GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
-	/** Use the "patience diff" algorithm (currently unimplemented) */
-	GIT_DIFF_PATIENCE = (1 << 6),
-	/** Include ignored files in the diff list */
-	GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
-	/** Include untracked files in the diff list */
-	GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
-	/** Include unmodified files in the diff list */
-	GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
+	GIT_DIFF_REVERSE = (1u << 0),
+
+	/** Include ignored files in the diff */
+	GIT_DIFF_INCLUDE_IGNORED = (1u << 1),
+
+	/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
+	 *  will be marked with only a single entry in the diff; this flag
+	 *  adds all files under the directory as IGNORED entries, too.
+	 */
+	GIT_DIFF_RECURSE_IGNORED_DIRS = (1u << 2),
+
+	/** Include untracked files in the diff */
+	GIT_DIFF_INCLUDE_UNTRACKED = (1u << 3),
 
 	/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
-	 *  directory will be marked with only a single entry in the diff list
+	 *  directory will be marked with only a single entry in the diff
 	 *  (a la what core Git does in `git status`); this flag adds *all*
 	 *  files under untracked directories as UNTRACKED entries, too.
 	 */
-	GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
+	GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1u << 4),
 
-	/** If the pathspec is set in the diff options, this flags means to
-	 *  apply it as an exact match instead of as an fnmatch pattern.
-	 */
-	GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
-
-	/** Use case insensitive filename comparisons */
-	GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
-
-	/** When generating patch text, include the content of untracked
-	 *  files.  This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
-	 *  it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS.  Add that
-	 *  flag if you want the content of every single UNTRACKED file.
-	 */
-	GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
-
-	/** Disable updating of the `binary` flag in delta records.  This is
-	 *  useful when iterating over a diff if you don't need hunk and data
-	 *  callbacks and want to avoid having to load file completely.
-	 */
-	GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
+	/** Include unmodified files in the diff */
+	GIT_DIFF_INCLUDE_UNMODIFIED = (1u << 5),
 
 	/** Normally, a type change between files will be converted into a
 	 *  DELETED record for the old and an ADDED record for the new; this
 	 *  options enabled the generation of TYPECHANGE delta records.
 	 */
-	GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
+	GIT_DIFF_INCLUDE_TYPECHANGE = (1u << 6),
 
 	/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
 	 *  generally show as a DELETED blob.  This flag tries to correctly
 	 *  label blob->tree transitions as TYPECHANGE records with new_file's
 	 *  mode set to tree.  Note: the tree SHA will not be available.
 	 */
-	GIT_DIFF_INCLUDE_TYPECHANGE_TREES  = (1 << 16),
+	GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1u << 7),
 
 	/** Ignore file mode changes */
-	GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
+	GIT_DIFF_IGNORE_FILEMODE = (1u << 8),
 
-	/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
-	 *  will be marked with only a single entry in the diff list; this flag
-	 *  adds all files under the directory as IGNORED entries, too.
+	/** Treat all submodules as unmodified */
+	GIT_DIFF_IGNORE_SUBMODULES = (1u << 9),
+
+	/** Use case insensitive filename comparisons */
+	GIT_DIFF_IGNORE_CASE = (1u << 10),
+
+	/** If the pathspec is set in the diff options, this flags means to
+	 *  apply it as an exact match instead of as an fnmatch pattern.
 	 */
-	GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
+	GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12),
 
-	/** Core Git scans inside untracked directories, labeling them IGNORED
-	 *  if they are empty or only contain ignored files; a directory is
-	 *  consider UNTRACKED only if it has an actual untracked file in it.
-	 *  This scan is extra work for a case you often don't care about.  This
-	 *  flag makes libgit2 immediately label an untracked directory as
-	 *  UNTRACKED without looking inside it (which differs from core Git).
-	 *  Of course, ignore rules are still checked for the directory itself.
+	/** Disable updating of the `binary` flag in delta records.  This is
+	 *  useful when iterating over a diff if you don't need hunk and data
+	 *  callbacks and want to avoid having to load file completely.
 	 */
-	GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19),
+	GIT_DIFF_SKIP_BINARY_CHECK = (1u << 13),
 
+	/** When diff finds an untracked directory, to match the behavior of
+	 *  core Git, it scans the contents for IGNORED and UNTRACKED files.
+	 *  If *all* contents are IGNORED, then the directory is IGNORED; if
+	 *  any contents are not IGNORED, then the directory is UNTRACKED.
+	 *  This is extra work that may not matter in many cases.  This flag
+	 *  turns off that scan and immediately labels an untracked directory
+	 *  as UNTRACKED (changing the behavior to not match core Git).
+	 */
+	GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
+
+	/*
+	 * Options controlling how output will be generated
+	 */
+
+	/** Treat all files as text, disabling binary attributes & detection */
+	GIT_DIFF_FORCE_TEXT = (1u << 20),
 	/** Treat all files as binary, disabling text diffs */
-	GIT_DIFF_FORCE_BINARY = (1 << 20),
+	GIT_DIFF_FORCE_BINARY = (1u << 21),
+
+	/** Ignore all whitespace */
+	GIT_DIFF_IGNORE_WHITESPACE = (1u << 22),
+	/** Ignore changes in amount of whitespace */
+	GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1u << 23),
+	/** Ignore whitespace at end of line */
+	GIT_DIFF_IGNORE_WHITESPACE_EOL = (1u << 24),
+
+	/** When generating patch text, include the content of untracked
+	 *  files.  This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
+	 *  it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS.  Add that
+	 *  flag if you want the content of every single UNTRACKED file.
+	 */
+	GIT_DIFF_SHOW_UNTRACKED_CONTENT = (1u << 25),
+
+	/** When generating output, include the names of unmodified files if
+	 *  they are included in the git_diff.  Normally these are skipped in
+	 *  the formats that list files (e.g. name-only, name-status, raw).
+	 *  Even with this, these will not be included in patch format.
+	 */
+	GIT_DIFF_SHOW_UNMODIFIED = (1u << 26),
+
+	/** Use the "patience diff" algorithm */
+	GIT_DIFF_PATIENCE = (1u << 28),
+	/** Take extra time to find minimal diff */
+	GIT_DIFF_MINIMAL = (1 << 29),
+
 } git_diff_option_t;
 
 /**
- * The diff list object that contains all individual file deltas.
+ * The diff object that contains all individual file deltas.
  *
  * This is an opaque structure which will be allocated by one of the diff
  * generator functions below (such as `git_diff_tree_to_tree`).  You are
  * responsible for releasing the object memory when done, using the
- * `git_diff_list_free()` function.
+ * `git_diff_free()` function.
  */
-typedef struct git_diff_list git_diff_list;
+typedef struct git_diff git_diff;
 
 /**
  * Flags for the delta object and the file objects on each side.
@@ -172,16 +201,16 @@
  * considered reserved for internal or future use.
  */
 typedef enum {
-	GIT_DIFF_FLAG_BINARY     = (1 << 0), /** file(s) treated as binary data */
-	GIT_DIFF_FLAG_NOT_BINARY = (1 << 1), /** file(s) treated as text data */
-	GIT_DIFF_FLAG_VALID_OID  = (1 << 2), /** `oid` value is known correct */
+	GIT_DIFF_FLAG_BINARY     = (1u << 0), /** file(s) treated as binary data */
+	GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */
+	GIT_DIFF_FLAG_VALID_OID  = (1u << 2), /** `oid` value is known correct */
 } git_diff_flag_t;
 
 /**
  * What type of change is described by a git_diff_delta?
  *
  * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run
- * `git_diff_find_similar()` on the diff list object.
+ * `git_diff_find_similar()` on the diff object.
  *
  * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE`
  * in the option flags (otherwise type changes will be split into ADDED /
@@ -200,11 +229,11 @@
 } git_delta_t;
 
 /**
- * Description of one side of a diff entry.
+ * Description of one side of a delta.
  *
- * Although this is called a "file", it may actually represent a file, a
- * symbolic link, a submodule commit id, or even a tree (although that only
- * if you are tracking type changes or ignored/untracked directories).
+ * Although this is called a "file", it could represent a file, a symbolic
+ * link, a submodule commit id, or even a tree (although that only if you
+ * are tracking type changes or ignored/untracked directories).
  *
  * The `oid` is the `git_oid` of the item.  If the entry represents an
  * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
@@ -231,9 +260,8 @@
 /**
  * Description of changes to one entry.
  *
- * When iterating over a diff list object, this will be passed to most
- * callback functions and you can use the contents to understand exactly
- * what has changed.
+ * When iterating over a diff, this will be passed to most callbacks and
+ * you can use the contents to understand exactly what has changed.
  *
  * The `old_file` represents the "from" side of the diff and the `new_file`
  * represents to "to" side of the diff.  What those means depend on the
@@ -266,28 +294,29 @@
  * the score (a la `printf("M%03d", 100 - delta->similarity)`).
  */
 typedef struct {
+	git_delta_t   status;
+	uint32_t      flags;	   /**< git_diff_flag_t values */
+	uint16_t      similarity;  /**< for RENAMED and COPIED, value 0-100 */
+	uint16_t      nfiles;	   /**< number of files in this delta */
 	git_diff_file old_file;
 	git_diff_file new_file;
-	git_delta_t   status;
-	uint32_t      similarity; /**< for RENAMED and COPIED, value 0-100 */
-	uint32_t      flags;
 } git_diff_delta;
 
 /**
  * Diff notification callback function.
  *
  * The callback will be called for each file, just before the `git_delta_t`
- * gets inserted into the diff list.
+ * gets inserted into the diff.
  *
  * When the callback:
  * - returns < 0, the diff process will be aborted.
- * - returns > 0, the delta will not be inserted into the diff list, but the
+ * - returns > 0, the delta will not be inserted into the diff, but the
  *		diff process continues.
- * - returns 0, the delta is inserted into the diff list, and the diff process
+ * - returns 0, the delta is inserted into the diff, and the diff process
  *		continues.
  */
 typedef int (*git_diff_notify_cb)(
-	const git_diff_list *diff_so_far,
+	const git_diff *diff_so_far,
 	const git_diff_delta *delta_to_add,
 	const char *matched_pathspec,
 	void *payload);
@@ -314,28 +343,44 @@
  * - `notify_cb` is an optional callback function, notifying the consumer of
  *   which files are being examined as the diff is generated
  * - `notify_payload` is the payload data to pass to the `notify_cb` function
+ * - `ignore_submodules` overrides the submodule ignore setting for all
+ *   submodules in the diff.
  */
 typedef struct {
 	unsigned int version;      /**< version for the struct */
 	uint32_t flags;            /**< defaults to GIT_DIFF_NORMAL */
-	uint16_t context_lines;    /**< defaults to 3 */
-	uint16_t interhunk_lines;  /**< defaults to 0 */
-	const char *old_prefix;    /**< defaults to "a" */
-	const char *new_prefix;    /**< defaults to "b" */
-	git_strarray pathspec;     /**< defaults to include all paths */
-	git_off_t max_size;        /**< defaults to 512MB */
+
+	/* options controlling which files are in the diff */
+
+	git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */
+	git_strarray       pathspec;     /**< defaults to include all paths */
 	git_diff_notify_cb notify_cb;
-	void *notify_payload;
+	void              *notify_payload;
+
+	/* options controlling how to diff text is generated */
+
+	uint16_t    context_lines;    /**< defaults to 3 */
+	uint16_t    interhunk_lines;  /**< defaults to 0 */
+	uint16_t    oid_abbrev;       /**< default 'core.abbrev' or 7 if unset */
+	git_off_t   max_size;         /**< defaults to 512MB */
+	const char *old_prefix;       /**< defaults to "a" */
+	const char *new_prefix;       /**< defaults to "b" */
 } git_diff_options;
 
+/* The current version of the diff options structure */
 #define GIT_DIFF_OPTIONS_VERSION 1
-#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_NORMAL, 3}
+
+/* Stack initializer for diff options.  Alternatively use
+ * `git_diff_options_init` programmatic initialization.
+ */
+#define GIT_DIFF_OPTIONS_INIT \
+	{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
 
 /**
  * When iterating over a diff, callback that will be made per file.
  *
  * @param delta A pointer to the delta data for the file
- * @param progress Goes from 0 to 1 over the diff list
+ * @param progress Goes from 0 to 1 over the diff
  * @param payload User-specified pointer from foreach function
  */
 typedef int (*git_diff_file_cb)(
@@ -346,34 +391,35 @@
 /**
  * Structure describing a hunk of a diff.
  */
-typedef struct {
-	int old_start; /** Starting line number in old_file */
-	int old_lines; /** Number of lines in old_file */
-	int new_start; /** Starting line number in new_file */
-	int new_lines; /** Number of lines in new_file */
-} git_diff_range;
+typedef struct git_diff_hunk git_diff_hunk;
+struct git_diff_hunk {
+	int    old_start;     /** Starting line number in old_file */
+	int    old_lines;     /** Number of lines in old_file */
+	int    new_start;     /** Starting line number in new_file */
+	int    new_lines;     /** Number of lines in new_file */
+	size_t header_len;    /** Number of bytes in header text */
+	char   header[128];   /** Header text, NUL-byte terminated */
+};
 
 /**
  * When iterating over a diff, callback that will be made per hunk.
  */
 typedef int (*git_diff_hunk_cb)(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	const char *header,
-	size_t header_len,
+	const git_diff_hunk *hunk,
 	void *payload);
 
 /**
  * Line origin constants.
  *
  * These values describe where a line came from and will be passed to
- * the git_diff_data_cb when iterating over a diff.  There are some
+ * the git_diff_line_cb when iterating over a diff.  There are some
  * special origin constants at the end that are used for the text
  * output callbacks to demarcate lines that are actually part of
  * the file or hunk headers.
  */
 typedef enum {
-	/* These values will be sent to `git_diff_data_cb` along with the line */
+	/* These values will be sent to `git_diff_line_cb` along with the line */
 	GIT_DIFF_LINE_CONTEXT   = ' ',
 	GIT_DIFF_LINE_ADDITION  = '+',
 	GIT_DIFF_LINE_DELETION  = '-',
@@ -382,16 +428,29 @@
 	GIT_DIFF_LINE_ADD_EOFNL = '>',     /**< Old has no LF at end, new does */
 	GIT_DIFF_LINE_DEL_EOFNL = '<',     /**< Old has LF at end, new does not */
 
-	/* The following values will only be sent to a `git_diff_data_cb` when
-	 * the content of a diff is being formatted (eg. through
-	 * git_diff_print_patch() or git_diff_print_compact(), for instance).
+	/* The following values will only be sent to a `git_diff_line_cb` when
+	 * the content of a diff is being formatted through `git_diff_print`.
 	 */
 	GIT_DIFF_LINE_FILE_HDR  = 'F',
 	GIT_DIFF_LINE_HUNK_HDR  = 'H',
-	GIT_DIFF_LINE_BINARY    = 'B'
+	GIT_DIFF_LINE_BINARY    = 'B' /**< For "Binary files x and y differ" */
 } git_diff_line_t;
 
 /**
+ * Structure describing a line (or data span) of a diff.
+ */
+typedef struct git_diff_line git_diff_line;
+struct git_diff_line {
+	char   origin;       /** A git_diff_line_t value */
+	int    old_lineno;   /** Line number in old file or -1 for added line */
+	int    new_lineno;   /** Line number in new file or -1 for deleted line */
+	int    num_lines;    /** Number of newline characters in content */
+	size_t content_len;  /** Number of bytes of data */
+	git_off_t content_offset; /** Offset in the original file to the content */
+	const char *content; /** Pointer to diff text, not NUL-byte terminated */
+};
+
+/**
  * When iterating over a diff, callback that will be made per text diff
  * line. In this context, the provided range will be NULL.
  *
@@ -399,46 +458,36 @@
  * of text.  This uses some extra GIT_DIFF_LINE_... constants for output
  * of lines of file and hunk headers.
  */
-typedef int (*git_diff_data_cb)(
+typedef int (*git_diff_line_cb)(
 	const git_diff_delta *delta, /** delta that contains this data */
-	const git_diff_range *range, /** range of lines containing this data */
-	char line_origin,            /** git_diff_list_t value from above */
-	const char *content,         /** diff data - not NUL terminated */
-	size_t content_len,          /** number of bytes of diff data */
+	const git_diff_hunk *hunk,   /** hunk containing this data */
+	const git_diff_line *line,   /** line data */
 	void *payload);              /** user reference data */
 
 /**
- * The diff patch is used to store all the text diffs for a delta.
- *
- * You can easily loop over the content of patches and get information about
- * them.
- */
-typedef struct git_diff_patch git_diff_patch;
-
-/**
  * Flags to control the behavior of diff rename/copy detection.
  */
 typedef enum {
 	/** look for renames? (`--find-renames`) */
-	GIT_DIFF_FIND_RENAMES = (1 << 0),
+	GIT_DIFF_FIND_RENAMES = (1u << 0),
 	/** consider old side of modified for renames? (`--break-rewrites=N`) */
-	GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1 << 1),
+	GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1),
 
 	/** look for copies? (a la `--find-copies`) */
-	GIT_DIFF_FIND_COPIES = (1 << 2),
+	GIT_DIFF_FIND_COPIES = (1u << 2),
 	/** consider unmodified as copy sources? (`--find-copies-harder`) */
-	GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
+	GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3),
 
 	/** mark large rewrites for split (`--break-rewrites=/M`) */
-	GIT_DIFF_FIND_REWRITES = (1 << 4),
+	GIT_DIFF_FIND_REWRITES = (1u << 4),
 	/** actually split large rewrites into delete/add pairs */
-	GIT_DIFF_BREAK_REWRITES = (1 << 5),
+	GIT_DIFF_BREAK_REWRITES = (1u << 5),
 	/** mark rewrites for split and break into delete/add pairs */
 	GIT_DIFF_FIND_AND_BREAK_REWRITES =
 		(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES),
 
 	/** find renames/copies for untracked items in working directory */
-	GIT_DIFF_FIND_FOR_UNTRACKED = (1 << 6),
+	GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6),
 
 	/** turn on all finding features */
 	GIT_DIFF_FIND_ALL = (0x0ff),
@@ -446,11 +495,14 @@
 	/** measure similarity ignoring leading whitespace (default) */
 	GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
 	/** measure similarity ignoring all whitespace */
-	GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12),
+	GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12),
 	/** measure similarity including all data */
-	GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13),
+	GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13),
 	/** measure similarity only by comparing SHAs (fast and cheap) */
-	GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1 << 14),
+	GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14),
+
+	/** do not break rewrites unless they contribute to a rename */
+	GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY  = (1u << 15),
 } git_diff_find_t;
 
 /**
@@ -515,22 +567,22 @@
 #define GIT_DIFF_FIND_OPTIONS_VERSION 1
 #define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
 
-/** @name Diff List Generator Functions
+/** @name Diff Generator Functions
  *
  * These are the functions you would use to create (or destroy) a
- * git_diff_list from various objects in a repository.
+ * git_diff from various objects in a repository.
  */
 /**@{*/
 
 /**
- * Deallocate a diff list.
+ * Deallocate a diff.
  *
- * @param diff The previously created diff list; cannot be used after free.
+ * @param diff The previously created diff; cannot be used after free.
  */
-GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
+GIT_EXTERN(void) git_diff_free(git_diff *diff);
 
 /**
- * Create a diff list with the difference between two tree objects.
+ * Create a diff with the difference between two tree objects.
  *
  * This is equivalent to `git diff <old-tree> <new-tree>`
  *
@@ -539,21 +591,21 @@
  * pass NULL to indicate an empty tree, although it is an error to pass
  * NULL for both the `old_tree` and `new_tree`.
  *
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * @param diff Output pointer to a git_diff pointer to be allocated.
  * @param repo The repository containing the trees.
  * @param old_tree A git_tree object to diff from, or NULL for empty tree.
  * @param new_tree A git_tree object to diff to, or NULL for empty tree.
  * @param opts Structure with options to influence diff or NULL for defaults.
  */
 GIT_EXTERN(int) git_diff_tree_to_tree(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	git_tree *new_tree,
 	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Create a diff list between a tree and repository index.
+ * Create a diff between a tree and repository index.
  *
  * This is equivalent to `git diff --cached <treeish>` or if you pass
  * the HEAD tree, then like `git diff --cached`.
@@ -561,21 +613,25 @@
  * The tree you pass will be used for the "old_file" side of the delta, and
  * the index will be used for the "new_file" side of the delta.
  *
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used.  In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
  * @param repo The repository containing the tree and index.
  * @param old_tree A git_tree object to diff from, or NULL for empty tree.
  * @param index The index to diff with; repo index used if NULL.
  * @param opts Structure with options to influence diff or NULL for defaults.
  */
 GIT_EXTERN(int) git_diff_tree_to_index(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	git_index *index,
 	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Create a diff list between the repository index and the workdir directory.
+ * Create a diff between the repository index and the workdir directory.
  *
  * This matches the `git diff` command.  See the note below on
  * `git_diff_tree_to_workdir` for a discussion of the difference between
@@ -585,19 +641,23 @@
  * The index will be used for the "old_file" side of the delta, and the
  * working directory will be used for the "new_file" side of the delta.
  *
- * @param diff Output pointer to a git_diff_list pointer to be allocated.
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used.  In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
  * @param repo The repository.
  * @param index The index to diff from; repo index used if NULL.
  * @param opts Structure with options to influence diff or NULL for defaults.
  */
 GIT_EXTERN(int) git_diff_index_to_workdir(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_index *index,
 	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Create a diff list between a tree and the working directory.
+ * Create a diff between a tree and the working directory.
  *
  * The tree you provide will be used for the "old_file" side of the delta,
  * and the working directory will be used for the "new_file" side.
@@ -611,31 +671,51 @@
  * files in the index.  It may come as a surprise, but there is no direct
  * equivalent in core git.
  *
- * To emulate `git diff <treeish>`, call both `git_diff_tree_to_index` and
- * `git_diff_index_to_workdir`, then call `git_diff_merge` on the results.
- * That will yield a `git_diff_list` that matches the git output.
+ * To emulate `git diff <tree>`, use `git_diff_tree_to_workdir_with_index`
+ * (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call
+ * `git_diff_merge` on the results).  That will yield a `git_diff` that
+ * matches the git output.
  *
  * If this seems confusing, take the case of a file with a staged deletion
  * where the file has then been put back into the working dir and modified.
  * The tree-to-workdir diff for that file is 'modified', but core git would
  * show status 'deleted' since there is a pending deletion in the index.
  *
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ * @param diff A pointer to a git_diff pointer that will be allocated.
  * @param repo The repository containing the tree.
  * @param old_tree A git_tree object to diff from, or NULL for empty tree.
  * @param opts Structure with options to influence diff or NULL for defaults.
  */
 GIT_EXTERN(int) git_diff_tree_to_workdir(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Merge one diff list into another.
+ * Create a diff between a tree and the working directory using index data
+ * to account for staged deletes, tracked files, etc.
+ *
+ * This emulates `git diff <tree>` by diffing the tree to the index and
+ * the index to the working directory and blending the results into a
+ * single diff that includes staged deleted, etc.
+ *
+ * @param diff A pointer to a git_diff pointer that will be allocated.
+ * @param repo The repository containing the tree.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ */
+GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
+	git_diff **diff,
+	git_repository *repo,
+	git_tree *old_tree,
+	const git_diff_options *opts); /**< can be NULL for defaults */
+
+/**
+ * Merge one diff into another.
  *
  * This merges items from the "from" list into the "onto" list.  The
- * resulting diff list will have all items that appear in either list.
+ * resulting diff will have all items that appear in either list.
  * If an item appears in both lists, then it will be "merged" to appear
  * as if the old version was from the "onto" list and the new version
  * is from the "from" list (with the exception that if the item has a
@@ -645,50 +725,117 @@
  * @param from Diff to merge.
  */
 GIT_EXTERN(int) git_diff_merge(
-	git_diff_list *onto,
-	const git_diff_list *from);
+	git_diff *onto,
+	const git_diff *from);
 
 /**
- * Transform a diff list marking file renames, copies, etc.
+ * Transform a diff marking file renames, copies, etc.
  *
- * This modifies a diff list in place, replacing old entries that look
+ * This modifies a diff in place, replacing old entries that look
  * like renames or copies with new entries reflecting those changes.
  * This also will, if requested, break modified files into add/remove
  * pairs if the amount of change is above a threshold.
  *
- * @param diff Diff list to run detection algorithms on
+ * @param diff diff to run detection algorithms on
  * @param options Control how detection should be run, NULL for defaults
  * @return 0 on success, -1 on failure
  */
 GIT_EXTERN(int) git_diff_find_similar(
-	git_diff_list *diff,
-	git_diff_find_options *options);
+	git_diff *diff,
+	const git_diff_find_options *options);
+
+/**
+ * Initialize diff options structure
+ *
+ * In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to
+ * initialize the diff options structure, but in some cases that is not
+ * going to work.  You can call this function instead.  Note that you
+ * must pass both a pointer to the structure to be initialized and the
+ * `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with.
+ *
+ * @param options Pointer to git_diff_options memory to be initialized
+ * @param version Should be `GIT_DIFF_OPTIONS_VERSION`
+ * @return 0 on success, negative on failure (such as unsupported version)
+ */
+GIT_EXTERN(int) git_diff_options_init(
+	git_diff_options *options,
+	unsigned int version);
 
 /**@}*/
 
 
-/** @name Diff List Processor Functions
+/** @name Diff Processor Functions
  *
- * These are the functions you apply to a diff list to process it
+ * These are the functions you apply to a diff to process it
  * or read it in some way.
  */
 /**@{*/
 
 /**
- * Loop over all deltas in a diff list issuing callbacks.
+ * Query how many diff records are there in a diff.
+ *
+ * @param diff A git_diff generated by one of the above functions
+ * @return Count of number of deltas in the list
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas(const git_diff *diff);
+
+/**
+ * Query how many diff deltas are there in a diff filtered by type.
+ *
+ * This works just like `git_diff_entrycount()` with an extra parameter
+ * that is a `git_delta_t` and returns just the count of how many deltas
+ * match that particular type.
+ *
+ * @param diff A git_diff generated by one of the above functions
+ * @param type A git_delta_t value to filter the count
+ * @return Count of number of deltas matching delta_t type
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
+	const git_diff *diff, git_delta_t type);
+
+/**
+ * Return the diff delta for an entry in the diff list.
+ *
+ * The `git_delta` pointer points to internal data and you do not have
+ * to release it when you are done with it.  It will go away when the
+ * `git_diff` (or any associated `git_patch`) goes away.
+ *
+ * Note that the flags on the delta related to whether it has binary
+ * content or not may not be set if there are no attributes set for the
+ * file and there has been no reason to load the file data at this point.
+ * For now, if you need those flags to be up to date, your only option is
+ * to either use `git_diff_foreach` or create a `git_patch`.
+ *
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return Pointer to git_diff_delta (or NULL if `idx` out of range)
+ */
+GIT_EXTERN(const git_diff_delta *) git_diff_get_delta(
+	const git_diff *diff, size_t idx);
+
+/**
+ * Check if deltas are sorted case sensitively or insensitively.
+ *
+ * @param diff diff to check
+ * @return 0 if case sensitive, 1 if case is ignored
+ */
+GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
+
+/**
+ * Loop over all deltas in a diff issuing callbacks.
  *
  * This will iterate through all of the files described in a diff.  You
  * should provide a file callback to learn about each file.
  *
  * The "hunk" and "line" callbacks are optional, and the text diff of the
  * files will only be calculated if they are not NULL.  Of course, these
- * callbacks will not be invoked for binary files on the diff list or for
+ * callbacks will not be invoked for binary files on the diff or for
  * files whose only changed is a file mode change.
  *
  * Returning a non-zero value from any of the callbacks will terminate
  * the iteration and cause this return `GIT_EUSER`.
  *
- * @param diff A git_diff_list generated by one of the above functions.
+ * @param diff A git_diff generated by one of the above functions.
  * @param file_cb Callback function to make per file in the diff.
  * @param hunk_cb Optional callback to make per hunk of text diff.  This
  *                callback is called to describe a range of lines in the
@@ -700,52 +847,19 @@
  * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_diff_foreach(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
-	void *payload);
-
-/**
- * Iterate over a diff generating text output like "git diff --name-status".
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param print_cb Callback to make per line of diff text.
- * @param payload Reference pointer that will be passed to your callback.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_diff_print_compact(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
-	void *payload);
-
-/**
- * Iterate over a diff generating text output like "git diff --raw".
- *
- * Returning a non-zero value from the callbacks will terminate the
- * iteration and cause this return `GIT_EUSER`.
- *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param print_cb Callback to make per line of diff text.
- * @param payload Reference pointer that will be passed to your callback.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_diff_print_raw(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
+	git_diff_line_cb line_cb,
 	void *payload);
 
 /**
  * Look up the single character abbreviation for a delta status code.
  *
- * When you call `git_diff_print_compact` it prints single letter codes into
- * the output such as 'A' for added, 'D' for deleted, 'M' for modified, etc.
- * It is sometimes convenient to convert a git_delta_t value into these
- * letters for your own purposes.  This function does just that.  By the
- * way, unmodified will return a space (i.e. ' ').
+ * When you run `git diff --name-status` it uses single letter codes in
+ * the output such as 'A' for added, 'D' for deleted, 'M' for modified,
+ * etc.  This function converts a git_delta_t value into these letters for
+ * your own purposes.  GIT_DELTA_UNTRACKED will return a space (i.e. ' ').
  *
  * @param status The git_delta_t value to look up
  * @return The single character label for that code
@@ -753,212 +867,34 @@
 GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
 
 /**
- * Iterate over a diff generating text output like "git diff".
- *
- * This is a super easy way to generate a patch from a diff.
+ * Possible output formats for diff data
+ */
+typedef enum {
+	GIT_DIFF_FORMAT_PATCH        = 1u, /**< full git diff */
+	GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */
+	GIT_DIFF_FORMAT_RAW          = 3u, /**< like git diff --raw */
+	GIT_DIFF_FORMAT_NAME_ONLY    = 4u, /**< like git diff --name-only */
+	GIT_DIFF_FORMAT_NAME_STATUS  = 5u, /**< like git diff --name-status */
+} git_diff_format_t;
+
+/**
+ * Iterate over a diff generating formatted text output.
  *
  * Returning a non-zero value from the callbacks will terminate the
  * iteration and cause this return `GIT_EUSER`.
  *
- * @param diff A git_diff_list generated by one of the above functions.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @param print_cb Callback function to output lines of the diff.  This
- *                 same function will be called for file headers, hunk
- *                 headers, and diff lines.  Fortunately, you can probably
- *                 use various GIT_DIFF_LINE constants to determine what
- *                 text you are given.
+ * @param diff A git_diff generated by one of the above functions.
+ * @param format A git_diff_format_t value to pick the text format.
+ * @param print_cb Callback to make per line of diff text.
+ * @param payload Reference pointer that will be passed to your callback.
  * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
-GIT_EXTERN(int) git_diff_print_patch(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
+GIT_EXTERN(int) git_diff_print(
+	git_diff *diff,
+	git_diff_format_t format,
+	git_diff_line_cb print_cb,
 	void *payload);
 
-/**
- * Query how many diff records are there in a diff list.
- *
- * @param diff A git_diff_list generated by one of the above functions
- * @return Count of number of deltas in the list
- */
-GIT_EXTERN(size_t) git_diff_num_deltas(git_diff_list *diff);
-
-/**
- * Query how many diff deltas are there in a diff list filtered by type.
- *
- * This works just like `git_diff_entrycount()` with an extra parameter
- * that is a `git_delta_t` and returns just the count of how many deltas
- * match that particular type.
- *
- * @param diff A git_diff_list generated by one of the above functions
- * @param type A git_delta_t value to filter the count
- * @return Count of number of deltas matching delta_t type
- */
-GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
-	git_diff_list *diff,
-	git_delta_t type);
-
-/**
- * Return the diff delta and patch for an entry in the diff list.
- *
- * The `git_diff_patch` is a newly created object contains the text diffs
- * for the delta.  You have to call `git_diff_patch_free()` when you are
- * done with it.  You can use the patch object to loop over all the hunks
- * and lines in the diff of the one delta.
- *
- * For an unchanged file or a binary file, no `git_diff_patch` will be
- * created, the output will be set to NULL, and the `binary` flag will be
- * set true in the `git_diff_delta` structure.
- *
- * The `git_diff_delta` pointer points to internal data and you do not have
- * to release it when you are done with it.  It will go away when the
- * `git_diff_list` and `git_diff_patch` go away.
- *
- * It is okay to pass NULL for either of the output parameters; if you pass
- * NULL for the `git_diff_patch`, then the text diff will not be calculated.
- *
- * @param patch_out Output parameter for the delta patch object
- * @param delta_out Output parameter for the delta object
- * @param diff Diff list object
- * @param idx Index into diff list
- * @return 0 on success, other value < 0 on error
- */
-GIT_EXTERN(int) git_diff_get_patch(
-	git_diff_patch **patch_out,
-	const git_diff_delta **delta_out,
-	git_diff_list *diff,
-	size_t idx);
-
-/**
- * Free a git_diff_patch object.
- */
-GIT_EXTERN(void) git_diff_patch_free(
-	git_diff_patch *patch);
-
-/**
- * Get the delta associated with a patch
- */
-GIT_EXTERN(const git_diff_delta *) git_diff_patch_delta(
-	git_diff_patch *patch);
-
-/**
- * Get the number of hunks in a patch
- */
-GIT_EXTERN(size_t) git_diff_patch_num_hunks(
-	git_diff_patch *patch);
-
-/**
- * Get line counts of each type in a patch.
- *
- * This helps imitate a diff --numstat type of output.  For that purpose,
- * you only need the `total_additions` and `total_deletions` values, but we
- * include the `total_context` line count in case you want the total number
- * of lines of diff output that will be generated.
- *
- * All outputs are optional. Pass NULL if you don't need a particular count.
- *
- * @param total_context Count of context lines in output, can be NULL.
- * @param total_additions Count of addition lines in output, can be NULL.
- * @param total_deletions Count of deletion lines in output, can be NULL.
- * @param patch The git_diff_patch object
- * @return 0 on success, <0 on error
- */
-GIT_EXTERN(int) git_diff_patch_line_stats(
-	size_t *total_context,
-	size_t *total_additions,
-	size_t *total_deletions,
-	const git_diff_patch *patch);
-
-/**
- * Get the information about a hunk in a patch
- *
- * Given a patch and a hunk index into the patch, this returns detailed
- * information about that hunk.  Any of the output pointers can be passed
- * as NULL if you don't care about that particular piece of information.
- *
- * @param range Output pointer to git_diff_range of hunk
- * @param header Output pointer to header string for hunk.  Unlike the
- *               content pointer for each line, this will be NUL-terminated
- * @param header_len Output value of characters in header string
- * @param lines_in_hunk Output count of total lines in this hunk
- * @param patch Input pointer to patch object
- * @param hunk_idx Input index of hunk to get information about
- * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
- */
-GIT_EXTERN(int) git_diff_patch_get_hunk(
-	const git_diff_range **range,
-	const char **header,
-	size_t *header_len,
-	size_t *lines_in_hunk,
-	git_diff_patch *patch,
-	size_t hunk_idx);
-
-/**
- * Get the number of lines in a hunk.
- *
- * @param patch The git_diff_patch object
- * @param hunk_idx Index of the hunk
- * @return Number of lines in hunk or -1 if invalid hunk index
- */
-GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk(
-	git_diff_patch *patch,
-	size_t hunk_idx);
-
-/**
- * Get data about a line in a hunk of a patch.
- *
- * Given a patch, a hunk index, and a line index in the hunk, this
- * will return a lot of details about that line.  If you pass a hunk
- * index larger than the number of hunks or a line index larger than
- * the number of lines in the hunk, this will return -1.
- *
- * @param line_origin A GIT_DIFF_LINE constant from above
- * @param content Pointer to content of diff line, not NUL-terminated
- * @param content_len Number of characters in content
- * @param old_lineno Line number in old file or -1 if line is added
- * @param new_lineno Line number in new file or -1 if line is deleted
- * @param patch The patch to look in
- * @param hunk_idx The index of the hunk
- * @param line_of_hunk The index of the line in the hunk
- * @return 0 on success, <0 on failure
- */
-GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
-	char *line_origin,
-	const char **content,
-	size_t *content_len,
-	int *old_lineno,
-	int *new_lineno,
-	git_diff_patch *patch,
-	size_t hunk_idx,
-	size_t line_of_hunk);
-
-/**
- * Serialize the patch to text via callback.
- *
- * Returning a non-zero value from the callback will terminate the iteration
- * and cause this return `GIT_EUSER`.
- *
- * @param patch A git_diff_patch representing changes to one file
- * @param print_cb Callback function to output lines of the patch.  Will be
- *                 called for file headers, hunk headers, and diff lines.
- * @param payload Reference pointer that will be passed to your callbacks.
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
- */
-GIT_EXTERN(int) git_diff_patch_print(
-	git_diff_patch *patch,
-	git_diff_data_cb print_cb,
-	void *payload);
-
-/**
- * Get the content of a patch as a single diff text.
- *
- * @param string Allocated string; caller must free.
- * @param patch A git_diff_patch representing changes to one file
- * @return 0 on success, <0 on failure.
- */
-GIT_EXTERN(int) git_diff_patch_to_str(
-	char **string,
-	git_diff_patch *patch);
-
 /**@}*/
 
 
@@ -1001,34 +937,10 @@
 	const git_diff_options *options,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
+	git_diff_line_cb line_cb,
 	void *payload);
 
 /**
- * Directly generate a patch from the difference between two blobs.
- *
- * This is just like `git_diff_blobs()` except it generates a patch object
- * for the difference instead of directly making callbacks.  You can use the
- * standard `git_diff_patch` accessor functions to read the patch data, and
- * you must call `git_diff_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param new_blob Blob for new side of diff, or NULL for empty blob
- * @param new_as_path Treat new blob as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_diff_patch_from_blobs(
-	git_diff_patch **out,
-	const git_blob *old_blob,
-	const char *old_as_path,
-	const git_blob *new_blob,
-	const char *new_as_path,
-	const git_diff_options *opts);
-
-/**
  * Directly run a diff between a blob and a buffer.
  *
  * As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
@@ -1048,7 +960,7 @@
  * @param options Options for diff, or NULL for default options
  * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
  * @param hunk_cb Callback for each hunk in diff; can be NULL
- * @param data_cb Callback for each line in diff; can be NULL
+ * @param line_cb Callback for each line in diff; can be NULL
  * @param payload Payload passed to each callback function
  * @return 0 on success, GIT_EUSER on non-zero callback return, or error code
  */
@@ -1061,35 +973,9 @@
 	const git_diff_options *options,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb data_cb,
+	git_diff_line_cb line_cb,
 	void *payload);
 
-/**
- * Directly generate a patch from the difference between a blob and a buffer.
- *
- * This is just like `git_diff_blob_to_buffer()` except it generates a patch
- * object for the difference instead of directly making callbacks.  You can
- * use the standard `git_diff_patch` accessor functions to read the patch
- * data, and you must call `git_diff_patch_free()` on the patch when done.
- *
- * @param out The generated patch; NULL on error
- * @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param old_as_path Treat old blob as if it had this filename; can be NULL
- * @param buffer Raw data for new side of diff, or NULL for empty
- * @param buffer_len Length of raw data for new side of diff
- * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
- * @param opts Options for diff, or NULL for default options
- * @return 0 on success or error code < 0
- */
-GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer(
-	git_diff_patch **out,
-	const git_blob *old_blob,
-	const char *old_as_path,
-	const char *buffer,
-	size_t buffer_len,
-	const char *buffer_as_path,
-	const git_diff_options *opts);
-
 
 GIT_END_DECL
 
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 2032a43..be7a31d 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -8,6 +8,7 @@
 #define INCLUDE_git_errors_h__
 
 #include "common.h"
+#include "buffer.h"
 
 /**
  * @file git2/errors.h
@@ -27,11 +28,12 @@
 	GIT_EBUFS = -6,
 	GIT_EUSER = -7,
 	GIT_EBAREREPO = -8,
-	GIT_EORPHANEDHEAD = -9,
+	GIT_EUNBORNBRANCH = -9,
 	GIT_EUNMERGED = -10,
 	GIT_ENONFASTFORWARD = -11,
 	GIT_EINVALIDSPEC = -12,
 	GIT_EMERGECONFLICT = -13,
+	GIT_ELOCKED = -14,
 
 	GIT_PASSTHROUGH = -30,
 	GIT_ITEROVER = -31,
@@ -44,6 +46,7 @@
 
 /** Error classes */
 typedef enum {
+	GITERR_NONE = 0,
 	GITERR_NOMEMORY,
 	GITERR_OS,
 	GITERR_INVALID,
@@ -66,6 +69,8 @@
 	GITERR_CHECKOUT,
 	GITERR_FETCHHEAD,
 	GITERR_MERGE,
+	GITERR_SSH,
+	GITERR_FILTER,
 } git_error_t;
 
 /**
@@ -82,6 +87,18 @@
 GIT_EXTERN(void) giterr_clear(void);
 
 /**
+ * Get the last error data and clear it.
+ *
+ * This copies the last error into the given `git_error` struct
+ * and returns 0 if the copy was successful, leaving the error 
+ * cleared as if `giterr_clear` had been called.
+ *
+ * If there was no existing error in the library, -1 will be returned
+ * and the contents of `cpy` will be left unmodified.
+ */
+GIT_EXTERN(int) giterr_detach(git_error *cpy);
+
+/**
  * Set the error message string for this thread.
  *
  * This function is public so that custom ODB backends and the like can
diff --git a/include/git2/filter.h b/include/git2/filter.h
new file mode 100644
index 0000000..f96b676
--- /dev/null
+++ b/include/git2/filter.h
@@ -0,0 +1,142 @@
+/*
+ * 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_filter_h__
+#define INCLUDE_git_filter_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "buffer.h"
+
+/**
+ * @file git2/filter.h
+ * @brief Git filter APIs
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Filters are applied in one of two directions: smudging - which is
+ * exporting a file from the Git object database to the working directory,
+ * and cleaning - which is importing a file from the working directory to
+ * the Git object database.  These values control which direction of
+ * change is being applied.
+ */
+typedef enum {
+	GIT_FILTER_TO_WORKTREE = 0,
+	GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE,
+	GIT_FILTER_TO_ODB = 1,
+	GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
+} git_filter_mode_t;
+
+/**
+ * A filter that can transform file data
+ *
+ * This represents a filter that can be used to transform or even replace
+ * file data.  Libgit2 includes one built in filter and it is possible to
+ * write your own (see git2/sys/filter.h for information on that).
+ *
+ * The two builtin filters are:
+ *
+ * * "crlf" which uses the complex rules with the "text", "eol", and
+ *   "crlf" file attributes to decide how to convert between LF and CRLF
+ *   line endings
+ * * "ident" which replaces "$Id$" in a blob with "$Id: <blob OID>$" upon
+ *   checkout and replaced "$Id: <anything>$" with "$Id$" on checkin.
+ */
+typedef struct git_filter git_filter;
+
+/**
+ * List of filters to be applied
+ *
+ * This represents a list of filters to be applied to a file / blob.  You
+ * can build the list with one call, apply it with another, and dispose it
+ * with a third.  In typical usage, there are not many occasions where a
+ * git_filter_list is needed directly since the library will generally
+ * handle conversions for you, but it can be convenient to be able to
+ * build and apply the list sometimes.
+ */
+typedef struct git_filter_list git_filter_list;
+
+/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return 0 on success (which could still return NULL if no filters are
+ *         needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load(
+	git_filter_list **filters,
+	git_repository *repo,
+	git_blob *blob, /* can be NULL */
+	const char *path,
+	git_filter_mode_t mode);
+
+/**
+ * Apply filter list to a data buffer.
+ *
+ * See `git2/buffer.h` for background on `git_buf` objects.
+ *
+ * If the `in` buffer holds data allocated by libgit2 (i.e. `in->asize` is
+ * not zero), then it will be overwritten when applying the filters.  If
+ * not, then it will be left untouched.
+ *
+ * If there are no filters to apply (or `filters` is NULL), then the `out`
+ * buffer will reference the `in` buffer data (with `asize` set to zero)
+ * instead of allocating data.  This keeps allocations to a minimum, but
+ * it means you have to be careful about freeing the `in` data since `out`
+ * may be pointing to it!
+ *
+ * @param out Buffer to store the result of the filtering
+ * @param filters A loaded git_filter_list (or NULL)
+ * @param in Buffer containing the data to filter
+ * @return 0 on success, an error code otherwise
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_data(
+	git_buf *out,
+	git_filter_list *filters,
+	git_buf *in);
+
+/**
+ * Apply filter list to the contents of a file on disk
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_file(
+	git_buf *out,
+	git_filter_list *filters,
+	git_repository *repo,
+	const char *path);
+
+/**
+ * Apply filter list to the contents of a blob
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_blob(
+	git_buf *out,
+	git_filter_list *filters,
+	git_blob *blob);
+
+/**
+ * Free a git_filter_list
+ *
+ * @param filters A git_filter_list created by `git_filter_list_load`
+ */
+GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters);
+
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/index.h b/include/git2/index.h
index 51694ad..a60db37 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -120,9 +120,9 @@
 
 /** Capabilities of system that affect index actions. */
 typedef enum {
-	GIT_INDEXCAP_IGNORE_CASE = 1,
-	GIT_INDEXCAP_NO_FILEMODE = 2,
-	GIT_INDEXCAP_NO_SYMLINKS = 4,
+	GIT_INDEXCAP_IGNORE_CASE = 1u,
+	GIT_INDEXCAP_NO_FILEMODE = 2u,
+	GIT_INDEXCAP_NO_SYMLINKS = 4u,
 	GIT_INDEXCAP_FROM_OWNER  = ~0u
 } git_indexcap_t;
 
@@ -138,6 +138,14 @@
 	GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
 } git_index_add_option_t;
 
+/**
+ * Match any index stage.
+ *
+ * Some index APIs take a stage to match; pass this value to match
+ * any entry matching the path regardless of stage.
+ */
+#define GIT_INDEX_STAGE_ANY -1
+
 /** @name Index File Functions
  *
  * These functions work on the index file itself.
@@ -214,13 +222,23 @@
 GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps);
 
 /**
- * Update the contents of an existing index object in memory
- * by reading from the hard disk.
+ * Update the contents of an existing index object in memory by reading
+ * from the hard disk.
+ *
+ * If `force` is true, this performs a "hard" read that discards in-memory
+ * changes and always reloads the on-disk index data.  If there is no
+ * on-disk version, the index will be cleared.
+ *
+ * If `force` is false, this does a "soft" read that reloads the index
+ * data from disk only if it has changed since the last time it was
+ * loaded.  Purely in-memory index data will be untouched.  Be aware: if
+ * there are changes on disk, unwritten in-memory changes are discarded.
  *
  * @param index an existing index object
+ * @param force if true, always reload, vs. only read if file has changed
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_read(git_index *index);
+GIT_EXTERN(int) git_index_read(git_index *index, int force);
 
 /**
  * Write an existing index object from memory back to disk
@@ -232,6 +250,14 @@
 GIT_EXTERN(int) git_index_write(git_index *index);
 
 /**
+ * Get the full path to the index file on disk.
+ *
+ * @param index an existing index object
+ * @return path to index file or NULL for in-memory index
+ */
+GIT_EXTERN(const char *) git_index_path(git_index *index);
+
+/**
  * Read a tree into the index file with stats
  *
  * The current index contents will be replaced by the specified tree.
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
index 4db072c..e4c03ad 100644
--- a/include/git2/indexer.h
+++ b/include/git2/indexer.h
@@ -13,19 +13,25 @@
 
 GIT_BEGIN_DECL
 
-typedef struct git_indexer_stream git_indexer_stream;
+typedef struct git_indexer git_indexer;
 
 /**
- * Create a new streaming indexer instance
+ * Create a new indexer instance
  *
  * @param out where to store the indexer instance
  * @param path to the directory where the packfile should be stored
+ * @param mode permissions to use creating packfile or 0 for defaults
+ * @param odb object database from which to read base objects when
+ * fixing thin packs. Pass NULL if no thin pack is expected (an error
+ * will be returned if there are bases missing)
  * @param progress_cb function to call with progress information
  * @param progress_cb_payload payload for the progress callback
  */
-GIT_EXTERN(int) git_indexer_stream_new(
-		git_indexer_stream **out,
+GIT_EXTERN(int) git_indexer_new(
+		git_indexer **out,
 		const char *path,
+		unsigned int mode,
+		git_odb *odb,
 		git_transfer_progress_callback progress_cb,
 		void *progress_cb_payload);
 
@@ -37,7 +43,7 @@
  * @param size the size of the data in bytes
  * @param stats stat storage
  */
-GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats);
+GIT_EXTERN(int) git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats);
 
 /**
  * Finalize the pack and index
@@ -46,7 +52,7 @@
  *
  * @param idx the indexer
  */
-GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats);
+GIT_EXTERN(int) git_indexer_commit(git_indexer *idx, git_transfer_progress *stats);
 
 /**
  * Get the packfile's hash
@@ -56,14 +62,14 @@
  *
  * @param idx the indexer instance
  */
-GIT_EXTERN(const git_oid *) git_indexer_stream_hash(const git_indexer_stream *idx);
+GIT_EXTERN(const git_oid *) git_indexer_hash(const git_indexer *idx);
 
 /**
  * Free the indexer and its resources
  *
  * @param idx the indexer to free
  */
-GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
+GIT_EXTERN(void) git_indexer_free(git_indexer *idx);
 
 GIT_END_DECL
 
diff --git a/include/git2/merge.h b/include/git2/merge.h
index cef6f77..3354fbe 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -7,11 +7,11 @@
 #ifndef INCLUDE_git_merge_h__
 #define INCLUDE_git_merge_h__
 
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/oid.h"
-#include "git2/checkout.h"
-#include "git2/index.h"
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "checkout.h"
+#include "index.h"
 
 /**
  * @file git2/merge.h
@@ -66,6 +66,29 @@
 
 
 /**
+ * Option flags for `git_merge`.
+ *
+ * GIT_MERGE_NO_FASTFORWARD - Do not fast-forward.
+ */
+typedef enum {
+	GIT_MERGE_NO_FASTFORWARD      = 1,
+	GIT_MERGE_FASTFORWARD_ONLY    = 2,
+} git_merge_flags_t;
+
+typedef struct {
+	unsigned int version;
+
+	git_merge_flags_t merge_flags;
+	git_merge_tree_opts merge_tree_opts;
+
+	git_checkout_opts checkout_opts;
+} git_merge_opts;
+
+#define GIT_MERGE_OPTS_VERSION 1
+#define GIT_MERGE_OPTS_INIT {GIT_MERGE_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT}
+
+
+/**
  * Find a merge base between two commits
  *
  * @param out the OID of a merge base between 'one' and 'two'
@@ -85,15 +108,15 @@
  *
  * @param out the OID of a merge base considering all the commits
  * @param repo the repository where the commits exist
- * @param input_array oids of the commits
  * @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
  * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
  */
 GIT_EXTERN(int) git_merge_base_many(
 	git_oid *out,
 	git_repository *repo,
-	const git_oid input_array[],
-	size_t length);
+	size_t length,
+	const git_oid input_array[]);
 
 /**
  * Creates a `git_merge_head` from the given reference
@@ -168,6 +191,43 @@
 	const git_tree *their_tree,
 	const git_merge_tree_opts *opts);
 
+/**
+ * Merges the given commits into HEAD, producing a new commit.
+ *
+ * @param out the results of the merge
+ * @param repo the repository to merge
+ * @param merge_heads the heads to merge into
+ * @param merge_heads_len the number of heads to merge
+ * @param flags merge flags
+ */
+GIT_EXTERN(int) git_merge(
+	git_merge_result **out,
+	git_repository *repo,
+	const git_merge_head **their_heads,
+	size_t their_heads_len,
+	const git_merge_opts *opts);
+
+/**
+ * Returns true if a merge is up-to-date (we were asked to merge the target
+ * into itself.)
+ */
+GIT_EXTERN(int) git_merge_result_is_uptodate(git_merge_result *merge_result);
+
+/**
+ * Returns true if a merge is eligible for fastforward
+ */
+GIT_EXTERN(int) git_merge_result_is_fastforward(git_merge_result *merge_result);
+
+/**
+ * Gets the fast-forward OID if the merge was a fastforward.
+ *
+ * @param out the OID of the fast-forward
+ * @param merge_result the results of the merge
+ */
+GIT_EXTERN(int) git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result);
+
+GIT_EXTERN(void) git_merge_result_free(git_merge_result *merge_result);
+
 /** @} */
 GIT_END_DECL
 #endif
diff --git a/include/git2/notes.h b/include/git2/notes.h
index 7382904..7636163 100644
--- a/include/git2/notes.h
+++ b/include/git2/notes.h
@@ -99,7 +99,7 @@
 /**
  * Get the note message
  *
- * @param note
+ * @param note the note
  * @return the note message
  */
 GIT_EXTERN(const char *) git_note_message(const git_note *note);
@@ -108,7 +108,7 @@
 /**
  * Get the note object OID
  *
- * @param note
+ * @param note the note
  * @return the note object OID
  */
 GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note);
diff --git a/include/git2/object.h b/include/git2/object.h
index b91b04d..c40631f 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -36,7 +36,7 @@
  * @param repo the repository to look up the object
  * @param id the unique identifier for the object
  * @param type the type of the object
- * @return a reference to the object
+ * @return 0 or an error code
  */
 GIT_EXTERN(int) git_object_lookup(
 		git_object **object,
@@ -78,6 +78,23 @@
 		size_t len,
 		git_otype type);
 
+
+/**
+ * Lookup an object that represents a tree entry.
+ *
+ * @param out buffer that receives a pointer to the object (which must be freed
+ *            by the caller)
+ * @param treeish root object that can be peeled to a tree
+ * @param path relative path from the root object to the desired object
+ * @param type type of object desired
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_object_lookup_bypath(
+		git_object **out,
+		const git_object *treeish,
+		const char *path,
+		git_otype type);
+
 /**
  * Get the id (SHA1) of a repository object
  *
diff --git a/include/git2/odb.h b/include/git2/odb.h
index b64436c..ad56384 100644
--- a/include/git2/odb.h
+++ b/include/git2/odb.h
@@ -120,7 +120,7 @@
  * @param db database to search for the object in.
  * @param short_id a prefix of the id of the object to read.
  * @param len the length of the prefix
- * @return 
+ * @return
  * - 0 if the object was read;
  * - GIT_ENOTFOUND if the object is not in the database.
  * - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
@@ -219,18 +219,12 @@
  * The type and final length of the object must be specified
  * when opening the stream.
  *
- * The returned stream will be of type `GIT_STREAM_WRONLY` and
- * will have the following methods:
+ * The returned stream will be of type `GIT_STREAM_WRONLY`, and it
+ * won't be effective until `git_odb_stream_finalize_write` is called
+ * and returns without an error
  *
- *		- stream->write: write `n` bytes into the stream
- *		- stream->finalize_write: close the stream and store the object in
- *			the odb
- *		- stream->free: free the stream
- *
- * The streaming write won't be effective until `stream->finalize_write`
- * is called and returns without an error
- *
- * The stream must always be free'd or will leak memory.
+ * The stream must always be freed when done with `git_odb_stream_free` or
+ * will leak memory.
  *
  * @see git_odb_stream
  *
@@ -243,6 +237,48 @@
 GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, size_t size, git_otype type);
 
 /**
+ * Write to an odb stream
+ *
+ * This method will fail if the total number of received bytes exceeds the
+ * size declared with `git_odb_open_wstream()`
+ *
+ * @param stream the stream
+ * @param buffer the data to write
+ * @param len the buffer's length
+ * @return 0 if the write succeeded; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
+
+/**
+ * Finish writing to an odb stream
+ *
+ * The object will take its final name and will be available to the
+ * odb.
+ *
+ * This method will fail if the total number of received bytes
+ * differs from the size declared with `git_odb_open_wstream()`
+ *
+ * @param out pointer to store the resulting object's id
+ * @param stream the stream
+ * @return 0 on success; an error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
+
+/**
+ * Read from an odb stream
+ *
+ * Most backends don't implement streaming reads
+ */
+GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len);
+
+/**
+ * Free an odb stream
+ *
+ * @param stream the stream to free
+ */
+GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
+
+/**
  * Open a stream to read an object from the ODB
  *
  * Note that most backends do *not* support streaming reads
@@ -322,6 +358,20 @@
 GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
 
 /**
+ * Create a copy of an odb_object
+ *
+ * The returned copy must be manually freed with `git_odb_object_free`.
+ * Note that because of an implementation detail, the returned copy will be
+ * the same pointer as `source`: the object is internally refcounted, so the
+ * copy still needs to be freed twice.
+ *
+ * @param dest pointer where to store the copy
+ * @param source object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source);
+
+/**
  * Close an ODB object
  *
  * This method must always be called once a `git_odb_object` is no
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
index af1e3e5..4d772ca 100644
--- a/include/git2/odb_backend.h
+++ b/include/git2/odb_backend.h
@@ -7,8 +7,8 @@
 #ifndef INCLUDE_git_odb_backend_h__
 #define INCLUDE_git_odb_backend_h__
 
-#include "git2/common.h"
-#include "git2/types.h"
+#include "common.h"
+#include "types.h"
 
 /**
  * @file git2/backend.h
@@ -40,10 +40,18 @@
  * @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 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
  *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
+GIT_EXTERN(int) git_odb_backend_loose(
+	git_odb_backend **out,
+	const char *objects_dir,
+	int compression_level,
+	int do_fsync,
+	unsigned int dir_mode,
+	unsigned int file_mode);
 
 /**
  * Create a backend out of a single packfile
@@ -65,14 +73,50 @@
 	GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
 } git_odb_stream_t;
 
-/** A stream to read/write from a backend */
+/**
+ * A stream to read/write from a backend.
+ *
+ * This represents a stream of data being written to or read from a
+ * backend. When writing, the frontend functions take care of
+ * calculating the object's id and all `finalize_write` needs to do is
+ * store the object with the id it is passed.
+ */
 struct git_odb_stream {
 	git_odb_backend *backend;
 	unsigned int mode;
+	void *hash_ctx;
 
+	size_t declared_size;
+	size_t received_bytes;
+
+	/**
+	 * Write at most `len` bytes into `buffer` and advance the stream.
+	 */
 	int (*read)(git_odb_stream *stream, char *buffer, size_t len);
+
+	/**
+	 * Write `len` bytes from `buffer` into the stream.
+	 */
 	int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
-	int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
+
+	/**
+	 * Store the contents of the stream as an object with the id
+	 * specified in `oid`.
+	 *
+	 * This method might not be invoked if:
+	 * - an error occurs earlier with the `write` callback,
+	 * - the object referred to by `oid` already exists in any backend, or
+	 * - the final number of received bytes differs from the size declared
+	 *   with `git_odb_open_wstream()`
+	 */
+	int (*finalize_write)(git_odb_stream *stream, const git_oid *oid);
+
+	/**
+	 * Free the stream's memory.
+	 *
+	 * This method might be called without a call to `finalize_write` if
+	 * an error occurs or if the object is already present in the ODB.
+	 */
 	void (*free)(git_odb_stream *stream);
 };
 
@@ -80,7 +124,7 @@
 struct git_odb_writepack {
 	git_odb_backend *backend;
 
-	int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
+	int (*append)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
 	int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
 	void (*free)(git_odb_writepack *writepack);
 };
diff --git a/include/git2/oid.h b/include/git2/oid.h
index 662338d..384b656 100644
--- a/include/git2/oid.h
+++ b/include/git2/oid.h
@@ -188,8 +188,7 @@
  *
  * @param id oid structure.
  * @param str input hex string of an object id.
- * @return GIT_ENOTOID if str is not a valid hex string,
- * 0 in case of a match, GIT_ERROR otherwise.
+ * @return 0 in case of a match, -1 otherwise.
  */
 GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
 
@@ -241,13 +240,13 @@
  * or freed.
  *
  * For performance reasons, there is a hard-limit of how many
- * OIDs can be added to a single set (around ~22000, assuming
+ * OIDs can be added to a single set (around ~32000, assuming
  * a mostly randomized distribution), which should be enough
  * for any kind of program, and keeps the algorithm fast and
  * memory-efficient.
  *
  * Attempting to add more than those OIDs will result in a
- * GIT_ENOMEM error
+ * GITERR_INVALID error
  *
  * @param os a `git_oid_shorten` instance
  * @param text_id an OID in text form
diff --git a/include/git2/pack.h b/include/git2/pack.h
index cc1f48a..88a2716 100644
--- a/include/git2/pack.h
+++ b/include/git2/pack.h
@@ -38,7 +38,7 @@
  *   `git_packbuilder_set_threads` can be used to adjust the number of
  *   threads used for the process.
  *
- * See tests-clar/pack/packbuilder.c for an example.
+ * See tests/pack/packbuilder.c for an example.
  *
  * @ingroup Git
  * @{
@@ -46,6 +46,14 @@
 GIT_BEGIN_DECL
 
 /**
+ * Stages that are reported by the packbuilder progress callback.
+ */
+typedef enum {
+	GIT_PACKBUILDER_ADDING_OBJECTS = 0,
+	GIT_PACKBUILDER_DELTAFICATION = 1,
+} git_packbuilder_stage_t;
+	
+/**
  * Initialize a new packbuilder
  *
  * @param out The new packbuilder object
@@ -111,6 +119,7 @@
  *
  * @param pb The packbuilder
  * @param path to the directory where the packfile and index should be stored
+ * @param mode permissions to use creating a packfile or 0 for defaults
  * @param progress_cb function to call with progress information from the indexer (optional)
  * @param progress_cb_payload payload for the progress callback (optional)
  *
@@ -119,9 +128,20 @@
 GIT_EXTERN(int) git_packbuilder_write(
 	git_packbuilder *pb,
 	const char *path,
+	unsigned int mode,
 	git_transfer_progress_callback progress_cb,
 	void *progress_cb_payload);
 
+/**
+* Get the packfile's hash
+*
+* A packfile's name is derived from the sorted hashing of all object
+* names. This is only correct after the packfile has been written.
+*
+* @param pb The packbuilder object
+*/
+GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
+
 typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
 /**
  * Create the new pack and pass each object to the callback
@@ -137,7 +157,7 @@
  * Get the total number of objects the packbuilder will write out
  *
  * @param pb the packbuilder
- * @return
+ * @return the number of objects in the packfile
  */
 GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
 
@@ -145,10 +165,32 @@
  * Get the number of objects the packbuilder has already written out
  *
  * @param pb the packbuilder
- * @return
+ * @return the number of objects which have already been written
  */
 GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
 
+/** Packbuilder progress notification function */
+typedef int (*git_packbuilder_progress)(
+	int stage,
+	unsigned int current,
+	unsigned int total,
+	void *payload);
+
+/**
+ * Set the callbacks for a packbuilder
+ *
+ * @param pb The packbuilder object
+ * @param progress_cb Function to call with progress information during
+ * pack building. Be aware that this is called inline with pack building
+ * operations, so performance may be affected.
+ * @param progress_cb_payload Payload for progress callback.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_set_callbacks(
+	git_packbuilder *pb,
+	git_packbuilder_progress progress_cb,
+	void *progress_cb_payload);
+
 /**
  * Free the packbuilder and all associated data
  *
diff --git a/include/git2/patch.h b/include/git2/patch.h
new file mode 100644
index 0000000..6a6ad92
--- /dev/null
+++ b/include/git2/patch.h
@@ -0,0 +1,250 @@
+/*
+ * 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_patch_h__
+#define INCLUDE_git_patch_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "diff.h"
+
+/**
+ * @file git2/patch.h
+ * @brief Patch handling routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * The diff patch is used to store all the text diffs for a delta.
+ *
+ * You can easily loop over the content of patches and get information about
+ * them.
+ */
+typedef struct git_patch git_patch;
+
+/**
+ * Return the diff delta and patch for an entry in the diff list.
+ *
+ * The `git_patch` is a newly created object contains the text diffs
+ * for the delta.  You have to call `git_patch_free()` when you are
+ * done with it.  You can use the patch object to loop over all the hunks
+ * and lines in the diff of the one delta.
+ *
+ * For an unchanged file or a binary file, no `git_patch` will be
+ * created, the output will be set to NULL, and the `binary` flag will be
+ * set true in the `git_diff_delta` structure.
+ *
+ * The `git_diff_delta` pointer points to internal data and you do not have
+ * to release it when you are done with it.  It will go away when the
+ * `git_diff` and `git_patch` go away.
+ *
+ * It is okay to pass NULL for either of the output parameters; if you pass
+ * NULL for the `git_patch`, then the text diff will not be calculated.
+ *
+ * @param out Output parameter for the delta patch object
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return 0 on success, other value < 0 on error
+ */
+GIT_EXTERN(int) git_patch_from_diff(
+	git_patch **out, git_diff *diff, size_t idx);
+
+/**
+ * Directly generate a patch from the difference between two blobs.
+ *
+ * This is just like `git_diff_blobs()` except it generates a patch object
+ * for the difference instead of directly making callbacks.  You can use the
+ * standard `git_patch` accessor functions to read the patch data, and
+ * you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param new_blob Blob for new side of diff, or NULL for empty blob
+ * @param new_as_path Treat new blob as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blobs(
+	git_patch **out,
+	const git_blob *old_blob,
+	const char *old_as_path,
+	const git_blob *new_blob,
+	const char *new_as_path,
+	const git_diff_options *opts);
+
+/**
+ * Directly generate a patch from the difference between a blob and a buffer.
+ *
+ * This is just like `git_diff_blob_to_buffer()` except it generates a patch
+ * object for the difference instead of directly making callbacks.  You can
+ * use the standard `git_patch` accessor functions to read the patch
+ * data, and you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param buffer Raw data for new side of diff, or NULL for empty
+ * @param buffer_len Length of raw data for new side of diff
+ * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blob_and_buffer(
+	git_patch **out,
+	const git_blob *old_blob,
+	const char *old_as_path,
+	const char *buffer,
+	size_t buffer_len,
+	const char *buffer_as_path,
+	const git_diff_options *opts);
+
+/**
+ * Free a git_patch object.
+ */
+GIT_EXTERN(void) git_patch_free(git_patch *patch);
+
+/**
+ * Get the delta associated with a patch
+ */
+GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
+
+/**
+ * Get the number of hunks in a patch
+ */
+GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
+
+/**
+ * Get line counts of each type in a patch.
+ *
+ * This helps imitate a diff --numstat type of output.  For that purpose,
+ * you only need the `total_additions` and `total_deletions` values, but we
+ * include the `total_context` line count in case you want the total number
+ * of lines of diff output that will be generated.
+ *
+ * All outputs are optional. Pass NULL if you don't need a particular count.
+ *
+ * @param total_context Count of context lines in output, can be NULL.
+ * @param total_additions Count of addition lines in output, can be NULL.
+ * @param total_deletions Count of deletion lines in output, can be NULL.
+ * @param patch The git_patch object
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_patch_line_stats(
+	size_t *total_context,
+	size_t *total_additions,
+	size_t *total_deletions,
+	const git_patch *patch);
+
+/**
+ * Get the information about a hunk in a patch
+ *
+ * Given a patch and a hunk index into the patch, this returns detailed
+ * information about that hunk.  Any of the output pointers can be passed
+ * as NULL if you don't care about that particular piece of information.
+ *
+ * @param out Output pointer to git_diff_hunk of hunk
+ * @param lines_in_hunk Output count of total lines in this hunk
+ * @param patch Input pointer to patch object
+ * @param hunk_idx Input index of hunk to get information about
+ * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
+ */
+GIT_EXTERN(int) git_patch_get_hunk(
+	const git_diff_hunk **out,
+	size_t *lines_in_hunk,
+	git_patch *patch,
+	size_t hunk_idx);
+
+/**
+ * Get the number of lines in a hunk.
+ *
+ * @param patch The git_patch object
+ * @param hunk_idx Index of the hunk
+ * @return Number of lines in hunk or -1 if invalid hunk index
+ */
+GIT_EXTERN(int) git_patch_num_lines_in_hunk(
+	git_patch *patch,
+	size_t hunk_idx);
+
+/**
+ * Get data about a line in a hunk of a patch.
+ *
+ * Given a patch, a hunk index, and a line index in the hunk, this
+ * will return a lot of details about that line.  If you pass a hunk
+ * index larger than the number of hunks or a line index larger than
+ * the number of lines in the hunk, this will return -1.
+ *
+ * @param out The git_diff_line data for this line
+ * @param patch The patch to look in
+ * @param hunk_idx The index of the hunk
+ * @param line_of_hunk The index of the line in the hunk
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_patch_get_line_in_hunk(
+	const git_diff_line **out,
+	git_patch *patch,
+	size_t hunk_idx,
+	size_t line_of_hunk);
+
+/**
+ * Look up size of patch diff data in bytes
+ *
+ * This returns the raw size of the patch data.  This only includes the
+ * actual data from the lines of the diff, not the file or hunk headers.
+ *
+ * If you pass `include_context` as true (non-zero), this will be the size
+ * of all of the diff output; if you pass it as false (zero), this will
+ * only include the actual changed lines (as if `context_lines` was 0).
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param include_context Include context lines in size if non-zero
+ * @param include_hunk_headers Include hunk header lines if non-zero
+ * @param include_file_headers Include file header lines if non-zero
+ * @return The number of bytes of data
+ */
+GIT_EXTERN(size_t) git_patch_size(
+	git_patch *patch,
+	int include_context,
+	int include_hunk_headers,
+	int include_file_headers);
+
+/**
+ * Serialize the patch to text via callback.
+ *
+ * Returning a non-zero value from the callback will terminate the iteration
+ * and cause this return `GIT_EUSER`.
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param print_cb Callback function to output lines of the patch.  Will be
+ *                 called for file headers, hunk headers, and diff lines.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_patch_print(
+	git_patch *patch,
+	git_diff_line_cb print_cb,
+	void *payload);
+
+/**
+ * Get the content of a patch as a single diff text.
+ *
+ * @param string Allocated string; caller must free.
+ * @param patch A git_patch representing changes to one file
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_patch_to_str(
+	char **string,
+	git_patch *patch);
+
+
+GIT_END_DECL
+
+/**@}*/
+
+#endif
diff --git a/include/git2/pathspec.h b/include/git2/pathspec.h
new file mode 100644
index 0000000..2fb0bb7
--- /dev/null
+++ b/include/git2/pathspec.h
@@ -0,0 +1,260 @@
+/*
+ * 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_pathspec_h__
+#define INCLUDE_git_pathspec_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+#include "diff.h"
+
+/**
+ * Compiled pathspec
+ */
+typedef struct git_pathspec git_pathspec;
+
+/**
+ * List of filenames matching a pathspec
+ */
+typedef struct git_pathspec_match_list git_pathspec_match_list;
+
+/**
+ * Options controlling how pathspec match should be executed
+ *
+ * - GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
+ *   match will use native case sensitivity of platform filesystem
+ * - GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
+ *   match will use native case sensitivity of platform filesystem
+ * - GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
+ *   string comparison for matching
+ * - GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
+ *   code GIT_ENOTFOUND if no matches are found; otherwise no matches is
+ *   still success (return 0) but `git_pathspec_match_list_entrycount`
+ *   will indicate 0 matches.
+ * - GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
+ *   should track which patterns matched which files so that at the end of
+ *   the match we can identify patterns that did not match any files.
+ * - GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
+ *   does not need to keep the actual matching filenames.  Use this to
+ *   just test if there were any matches at all or in combination with
+ *   GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
+ */
+typedef enum {
+	GIT_PATHSPEC_DEFAULT        = 0,
+	GIT_PATHSPEC_IGNORE_CASE    = (1u << 0),
+	GIT_PATHSPEC_USE_CASE       = (1u << 1),
+	GIT_PATHSPEC_NO_GLOB        = (1u << 2),
+	GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
+	GIT_PATHSPEC_FIND_FAILURES  = (1u << 4),
+	GIT_PATHSPEC_FAILURES_ONLY  = (1u << 5),
+} git_pathspec_flag_t;
+
+/**
+ * Compile a pathspec
+ *
+ * @param out Output of the compiled pathspec
+ * @param pathspec A git_strarray of the paths to match
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_pathspec_new(
+	git_pathspec **out, const git_strarray *pathspec);
+
+/**
+ * Free a pathspec
+ *
+ * @param ps The compiled pathspec
+ */
+GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps);
+
+/**
+ * Try to match a path against a pathspec
+ *
+ * Unlike most of the other pathspec matching functions, this will not
+ * fall back on the native case-sensitivity for your platform.  You must
+ * explicitly pass flags to control case sensitivity or else this will
+ * fall back on being case sensitive.
+ *
+ * @param ps The compiled pathspec
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param path The pathname to attempt to match
+ * @return 1 is path matches spec, 0 if it does not
+ */
+GIT_EXTERN(int) git_pathspec_matches_path(
+	const git_pathspec *ps, uint32_t flags, const char *path);
+
+/**
+ * Match a pathspec against the working directory of a repository.
+ *
+ * This matches the pathspec against the current files in the working
+ * directory of the repository.  It is an error to invoke this on a bare
+ * repo.  This handles git ignores (i.e. ignored files will not be
+ * considered to match the `pathspec` unless the file is tracked in the
+ * index).
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`.  That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag).  You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param repo The repository in which to match; bare repo is an error
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ *         the GIT_PATHSPEC_NO_MATCH_ERROR flag was given
+ */
+GIT_EXTERN(int) git_pathspec_match_workdir(
+	git_pathspec_match_list **out,
+	git_repository *repo,
+	uint32_t flags,
+	git_pathspec *ps);
+
+/**
+ * Match a pathspec against entries in an index.
+ *
+ * This matches the pathspec against the files in the repository index.
+ *
+ * NOTE: At the moment, the case sensitivity of this match is controlled
+ * by the current case-sensitivity of the index object itself and the
+ * USE_CASE and IGNORE_CASE flags will have no effect.  This behavior will
+ * be corrected in a future release.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`.  That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag).  You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param index The index to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ *         the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_index(
+	git_pathspec_match_list **out,
+	git_index *index,
+	uint32_t flags,
+	git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a tree.
+ *
+ * This matches the pathspec against the files in the given tree.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`.  That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag).  You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param tree The root-level tree to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ *         the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_tree(
+	git_pathspec_match_list **out,
+	git_tree *tree,
+	uint32_t flags,
+	git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a diff list.
+ *
+ * This matches the pathspec against the files in the given diff list.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`.  That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag).  You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param diff A generated diff list
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ *         the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_diff(
+	git_pathspec_match_list **out,
+	git_diff *diff,
+	uint32_t flags,
+	git_pathspec *ps);
+
+/**
+ * Free memory associates with a git_pathspec_match_list
+ *
+ * @param m The git_pathspec_match_list to be freed
+ */
+GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m);
+
+/**
+ * Get the number of items in a match list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in match list
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_entrycount(
+	const git_pathspec_match_list *m);
+
+/**
+ * Get a matching filename by position.
+ *
+ * This routine cannot be used if the match list was generated by
+ * `git_pathspec_match_diff`.  If so, it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_entry(
+	const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get a matching diff delta by position.
+ *
+ * This routine can only be used if the match list was generated by
+ * `git_pathspec_match_diff`.  Otherwise it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry(
+	const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get the number of pathspec items that did not match.
+ *
+ * This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when
+ * generating the git_pathspec_match_list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in original pathspec that had no matches
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
+	const git_pathspec_match_list *m);
+
+/**
+ * Get an original pathspec string that had no matches.
+ *
+ * This will be return NULL for positions out of range.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the failed items
+ * @return The pathspec pattern that didn't match anything
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
+	const git_pathspec_match_list *m, size_t pos);
+
+#endif
diff --git a/include/git2/push.h b/include/git2/push.h
index f923081..77ef740 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -8,6 +8,7 @@
 #define INCLUDE_git_push_h__
 
 #include "common.h"
+#include "pack.h"
 
 /**
  * @file git2/push.h
@@ -38,6 +39,13 @@
 #define GIT_PUSH_OPTIONS_VERSION 1
 #define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
 
+/** Push network progress notification function */
+typedef int (*git_push_transfer_progress)(
+	unsigned int current,
+	unsigned int total,
+	size_t bytes,
+	void* payload);
+
 /**
  * Create a new push object
  *
@@ -61,6 +69,27 @@
 	const git_push_options *opts);
 
 /**
+ * Set the callbacks for a push
+ *
+ * @param push The push object
+ * @param pack_progress_cb Function to call with progress information during
+ * pack building. Be aware that this is called inline with pack building
+ * operations, so performance may be affected.
+ * @param pack_progress_cb_payload Payload for the pack progress callback.
+ * @param transfer_progress_cb Function to call with progress information during
+ * the upload portion of a push. Be aware that this is called inline with
+ * pack building operations, so performance may be affected.
+ * @param transfer_progress_cb_payload Payload for the network progress callback.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_set_callbacks(
+	git_push *push,
+	git_packbuilder_progress pack_progress_cb,
+	void *pack_progress_cb_payload,
+	git_push_transfer_progress transfer_progress_cb,
+	void *transfer_progress_cb_payload);
+
+/**
  * Add a refspec to be pushed
  *
  * @param push The push object
@@ -98,7 +127,7 @@
  *
  * @param push The push object
  *
- * @return true if equal, false otherwise
+ * @return true if remote side successfully unpacked, false otherwise
  */
 GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
 
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
index 4944530..2d1b6ee 100644
--- a/include/git2/reflog.h
+++ b/include/git2/reflog.h
@@ -31,10 +31,11 @@
  * git_reflog_free().
  *
  * @param out pointer to reflog
- * @param ref reference to read the reflog for
+ * @param repo the repostiory
+ * @param name reference to look up
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reflog_read(git_reflog **out, const git_reference *ref);
+GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo,  const char *name);
 
 /**
  * Write an existing in-memory reflog object back to disk
@@ -59,26 +60,45 @@
 GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
 
 /**
- * Rename the reflog for the given reference
+ * Add a new entry to the named reflog.
+ *
+ * This utility function loads the named reflog, appends to it and
+ * writes it back out to the backend.
+ *
+ * `msg` is optional and can be NULL.
+ *
+ * @param repo the repository to act on
+ * @param name the reflog's name
+ * @param id the OID the reference is now pointing to
+ * @param committer the signature of the committer
+ * @param msg the reflog message
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg);
+
+/**
+ * Rename a reflog
  *
  * The reflog to be renamed is expected to already exist
  *
  * The new name will be checked for validity.
  * See `git_reference_create_symbolic()` for rules about valid names.
  *
- * @param ref the reference
- * @param name the new name of the reference
+ * @param repo the repository
+ * @param old_name the old name of the reference
+ * @param new_name the new name of the reference
  * @return 0 on success, GIT_EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *name);
+GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
 
 /**
  * Delete the reflog for the given reference
  *
- * @param ref the reference
+ * @param repo the repository
+ * @param name the reflog to delete
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reflog_delete(git_reference *ref);
+GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name);
 
 /**
  * Get the number of log entries in a reflog
diff --git a/include/git2/refs.h b/include/git2/refs.h
index 795f7ab..4041947 100644
--- a/include/git2/refs.h
+++ b/include/git2/refs.h
@@ -32,7 +32,7 @@
  * @param out pointer to the looked-up reference
  * @param repo the repository to look up the reference
  * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
  */
 GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
 
@@ -49,7 +49,7 @@
  * @param out Pointer to oid to be filled in
  * @param repo The repository in which to look up the reference
  * @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
  */
 GIT_EXTERN(int) git_reference_name_to_id(
 	git_oid *out, git_repository *repo, const char *name);
@@ -94,7 +94,7 @@
  * @param name The name of the reference
  * @param target The target of the reference
  * @param force Overwrite existing references
- * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
 
@@ -126,7 +126,7 @@
  * @param name The name of the reference
  * @param id The object id pointed to by the reference.
  * @param force Overwrite existing references
- * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
 
@@ -225,7 +225,7 @@
  * @param out Pointer to the newly created reference
  * @param ref The reference
  * @param target The new target for the reference
- * @return 0 on success, EINVALIDSPEC or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_reference_symbolic_set_target(
 	git_reference **out,
@@ -268,7 +268,7 @@
  * @param ref The reference to rename
  * @param new_name The new name for the reference
  * @param force Overwrite an existing reference
- * @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
  *
  */
 GIT_EXTERN(int) git_reference_rename(
@@ -442,9 +442,18 @@
  */
 GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
 
+/**
+ * Check if a reference is a tag
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/tags
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
 
 typedef enum {
-	GIT_REF_FORMAT_NORMAL = 0,
+	GIT_REF_FORMAT_NORMAL = 0u,
 
 	/**
 	 * Control whether one-level refnames are accepted
@@ -452,7 +461,7 @@
 	 * components). Those are expected to be written only using
 	 * uppercase letters and underscore (FETCH_HEAD, ...)
 	 */
-	GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0),
+	GIT_REF_FORMAT_ALLOW_ONELEVEL = (1u << 0),
 
 	/**
 	 * Interpret the provided name as a reference pattern for a
@@ -461,14 +470,14 @@
 	 * in place of a one full pathname component
 	 * (e.g., foo/<star>/bar but not foo/bar<star>).
 	 */
-	GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
+	GIT_REF_FORMAT_REFSPEC_PATTERN = (1u << 1),
 
 	/**
 	 * Interpret the name as part of a refspec in shorthand form
 	 * so the `ONELEVEL` naming rules aren't enforced and 'master'
 	 * becomes a valid name.
 	 */
-	GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
+	GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
 } git_reference_normalize_t;
 
 /**
@@ -488,7 +497,7 @@
  * @param name Reference name to be checked.
  * @param flags Flags to constrain name validation rules - see the
  *              GIT_REF_FORMAT constants above.
- * @return 0 on success, GIT_EBUFS if buffer is too small, EINVALIDSPEC
+ * @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC
  * or an error code.
  */
 GIT_EXTERN(int) git_reference_normalize_name(
diff --git a/include/git2/remote.h b/include/git2/remote.h
index 45d15d0..7410909 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -25,13 +25,6 @@
 GIT_BEGIN_DECL
 
 typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
-/*
- * TODO: This functions still need to be implemented:
- * - _listcb/_foreach
- * - _add
- * - _rename
- * - _del (needs support from config)
- */
 
 /**
  * Add a remote with the default fetch refspec to the repository's configuration.  This
@@ -50,6 +43,25 @@
 		const char *url);
 
 /**
+ * Add a remote with the provided fetch refspec (or default if NULL) to the repository's
+ * configuration.  This
+ * calls git_remote_save before returning.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @param fetch the remote fetch value
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create_with_fetchspec(
+		git_remote **out,
+		git_repository *repo,
+		const char *name,
+		const char *url,
+		const char *fetch);
+
+/**
  * Create a remote in memory
  *
  * Create a remote with the given refspec in memory. You can use
@@ -61,7 +73,7 @@
  *
  * @param out pointer to the new remote object
  * @param repo the associated repository
- * @param fetch the fetch refspec to use for this remote. May be NULL for defaults.
+ * @param fetch the fetch refspec to use for this remote.
  * @param url the remote repository's URL
  * @return 0 or an error code
  */
@@ -96,6 +108,14 @@
 GIT_EXTERN(int) git_remote_save(const git_remote *remote);
 
 /**
+ * Get the remote's repository
+ *
+ * @param remote the remote
+ * @return a pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote);
+
+/**
  * Get the remote's name
  *
  * @param remote the remote
@@ -144,8 +164,11 @@
 /**
  * Add a fetch refspec to the remote
  *
+ * Convenience function for adding a single fetch refspec to the
+ * current list in the remote.
+ *
  * @param remote the remote
- * @apram refspec the new fetch refspec
+ * @param refspec the new fetch refspec
  * @return 0 or an error value
  */
 GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
@@ -162,8 +185,21 @@
 GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
 
 /**
+ * Set the remote's list of fetch refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of fetch resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
+
+/**
  * Add a push refspec to the remote
  *
+ * Convenience function for adding a single push refspec to the
+ * current list in the remote.
+ *
  * @param remote the remote
  * @param refspec the new push refspec
  * @return 0 or an error value
@@ -182,6 +218,16 @@
 GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
 
 /**
+ * Set the remote's list of push refspecs
+ *
+ * The contents of the string array are copied.
+ *
+ * @param remote the remote to modify
+ * @param array the new list of push resfpecs
+ */
+GIT_EXTERN(int) git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
+
+/**
  * Clear the refspecs
  *
  * Remove all configured fetch and push refspecs from the remote.
@@ -208,15 +254,6 @@
 GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n);
 
 /**
- * Remove a refspec from the remote
- *
- * @param remote the remote to query
- * @param n the refspec to remove
- * @return 0 or GIT_ENOTFOUND
- */
-GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n);
-
-/**
  * Open a connection to a remote
  *
  * The transport is selected based on the URL. The direction argument
@@ -236,36 +273,30 @@
  * The remote (or more exactly its transport) must be connected. The
  * memory belongs to the remote.
  *
- * If you a return a non-zero value from the callback, this will stop
- * looping over the refs.
+ * The array will stay valid as long as the remote object exists and
+ * its transport isn't changed, but a copy is recommended for usage of
+ * the data.
  *
+ * @param out pointer to the array
+ * @param size the number of remote heads
  * @param remote the remote
- * @param list_cb function to call with each ref discovered at the remote
- * @param payload additional data to pass to the callback
- * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ * @return 0 on success, or an error code
  */
-GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
+GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out,  size_t *size, git_remote *remote);
 
 /**
- * Download the packfile
+ * Download and index the packfile
  *
- * Negotiate what objects should be downloaded and download the
- * packfile with those objects. The packfile is downloaded with a
- * temporary filename, as it's final name is not known yet. If there
- * was no packfile needed (all the objects were available locally),
- * filename will be NULL and the function will return success.
+ * Connect to the remote if it hasn't been done yet, negotiate with
+ * the remote git which objects are missing, download and index the
+ * packfile.
  *
- * @param remote the remote to download from
- * @param progress_cb function to call with progress information.  Be aware that
- * this is called inline with network and indexing operations, so performance
- * may be affected.
- * @param payload payload for the progress callback
+ * The .idx file will be created and both it and the packfile with be
+ * renamed to their final name.
+ *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_download(
-		git_remote *remote,
-		git_transfer_progress_callback progress_cb,
-		void *payload);
+GIT_EXTERN(int) git_remote_download(git_remote *remote);
 
 /**
  * Check whether the remote is connected
@@ -317,6 +348,17 @@
 GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
 
 /**
+ * Download new data and update tips
+ *
+ * Convenience function to connect to a remote, download the data,
+ * disconnect and update the remote-tracking branches.
+ *
+ * @param remote the remote to fetch from
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_fetch(git_remote *remote);
+
+/**
  * Return whether a string is a valid remote URL
  *
  * @param url the url to check
@@ -352,21 +394,6 @@
 GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
 
 /**
- * Set a credentials acquisition callback for this remote. If the remote is
- * not available for anonymous access, then you must set this callback in order
- * to provide credentials to the transport at the time of authentication
- * failure so that retry can be performed.
- *
- * @param remote the remote to configure
- * @param cred_acquire_cb The credentials acquisition callback to use (defaults
- * to NULL)
- */
-GIT_EXTERN(void) git_remote_set_cred_acquire_cb(
-	git_remote *remote,
-	git_cred_acquire_cb cred_acquire_cb,
-	void *payload);
-
-/**
  * Sets a custom transport for the remote. The caller can use this function
  * to bypass the automatic discovery of a transport by URL scheme (i.e.
  * http://, https://, git://) and supply their own transport to be used
@@ -395,13 +422,47 @@
 /**
  * The callback settings structure
  *
- * Set the calbacks to be called by the remote.
+ * Set the callbacks to be called by the remote when informing the user
+ * about the progress of the network operations.
  */
 struct git_remote_callbacks {
 	unsigned int version;
-	void (*progress)(const char *str, int len, void *data);
+	/**
+	 * Textual progress from the remote. Text send over the
+	 * progress side-band will be passed to this function (this is
+	 * the 'counting objects' output.
+	 */
+	int (*progress)(const char *str, int len, void *data);
+
+	/**
+	 * Completion is called when different parts of the download
+	 * process are done (currently unused).
+	 */
 	int (*completion)(git_remote_completion_type type, void *data);
+
+	/**
+	 * This will be called if the remote host requires
+	 * authentication in order to connect to it.
+	 */
+	int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types,	void *data);
+
+	/**
+	 * During the download of new data, this will be regularly
+	 * called with the current count of progress done by the
+	 * indexer.
+	 */
+	int (*transfer_progress)(const git_transfer_progress *stats, void *data);
+
+	/**
+	 * Each time a reference is updated locally, this function
+	 * will be called with information about it.
+	 */
 	int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+	/**
+	 * This will be passed to each of the callbacks in this struct
+	 * as the last parameter.
+	 */
 	void *payload;
 };
 
@@ -418,7 +479,7 @@
  * @param callbacks a pointer to the user's callback settings
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
+GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
 
 /**
  * Get the statistics structure that is filled in by the fetch operation.
diff --git a/include/git2/repository.h b/include/git2/repository.h
index 2164cfa..b4d5619 100644
--- a/include/git2/repository.h
+++ b/include/git2/repository.h
@@ -94,10 +94,14 @@
  *   changes from the `stat` system call).  (E.g. Searching in a user's home
  *   directory "/home/user/source/" will not return "/.git/" as the found
  *   repo if "/" is a different filesystem than "/home".)
+ * * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
+ *   of core.bare config, and defer loading config file for faster setup.
+ *   Unlike `git_repository_open_bare`, this can follow gitlinks.
  */
 typedef enum {
 	GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
 	GIT_REPOSITORY_OPEN_CROSS_FS  = (1 << 1),
+	GIT_REPOSITORY_OPEN_BARE      = (1 << 2),
 } git_repository_open_flag_t;
 
 /**
@@ -178,7 +182,7 @@
  * when initializing a new repo.  Details of individual values are:
  *
  * * BARE   - Create a bare repository with no working directory.
- * * NO_REINIT - Return an EEXISTS error if the repo_path appears to
+ * * NO_REINIT - Return an GIT_EEXISTS error if the repo_path appears to
  *        already be an git repository.
  * * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
  *        path for non-bare repos (if it is not already there), but
@@ -293,7 +297,7 @@
  * @param out pointer to the reference which will be retrieved
  * @param repo a repository object
  *
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
  * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
  */
 GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
@@ -311,16 +315,16 @@
 GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
 
 /**
- * Check if the current branch is an orphan
+ * Check if the current branch is unborn
  *
- * An orphan branch is one named from HEAD but which doesn't exist in
+ * An unborn branch is one named from HEAD but which doesn't exist in
  * the refs namespace, because it doesn't have any commit to point to.
  *
  * @param repo Repo to test
- * @return 1 if the current branch is an orphan, 0 if it's not; error
+ * @return 1 if the current branch is unborn, 0 if it's not; error
  * code if there was an error
  */
-GIT_EXTERN(int) git_repository_head_orphan(git_repository *repo);
+GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
 
 /**
  * Check if a repository is empty
@@ -471,7 +475,7 @@
  * @param out Buffer to write data into or NULL to just read required size
  * @param len Length of `out` buffer in bytes
  * @param repo Repository to read prepared message from
- * @return GIT_ENOUTFOUND if no message exists, other value < 0 for other
+ * @return GIT_ENOTFOUND if no message exists, other value < 0 for other
  *         errors, or total bytes in message (may be > `len`) on success
  */
 GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
@@ -601,13 +605,13 @@
  * If the HEAD is already detached and points to a Tag, the HEAD is
  * updated into making it point to the peeled Commit, and 0 is returned.
  *
- * If the HEAD is already detached and points to a non commitish, the HEAD is 
+ * If the HEAD is already detached and points to a non commitish, the HEAD is
  * unaltered, and -1 is returned.
  *
  * Otherwise, the HEAD will be detached and point to the peeled Commit.
  *
  * @param repo Repository pointer
- * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
  * branch or an error code
  */
 GIT_EXTERN(int) git_repository_detach_head(
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
index 786a9da..d170e16 100644
--- a/include/git2/revparse.h
+++ b/include/git2/revparse.h
@@ -10,7 +10,6 @@
 #include "common.h"
 #include "types.h"
 
-
 /**
  * @file git2/revparse.h
  * @brief Git revision parsing routines
@@ -21,27 +20,37 @@
 GIT_BEGIN_DECL
 
 /**
- * Find a single object, as specified by a revision string. See `man gitrevisions`,
- * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * Find a single object, as specified by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
  * information on the syntax accepted.
  *
+ * The returned object should be released with `git_object_free` when no
+ * longer needed.
+ *
  * @param out pointer to output object
  * @param repo the repository to search in
  * @param spec the textual specification for an object
  * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
+GIT_EXTERN(int) git_revparse_single(
+	git_object **out, git_repository *repo, const char *spec);
 
 /**
- * Find a single object, as specified by a revision string.
- * See `man gitrevisions`,
- * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * Find a single object and intermediate reference by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
  * information on the syntax accepted.
  *
  * In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
  * point to an intermediate reference. When such expressions are being passed
  * in, `reference_out` will be valued as well.
  *
+ * The returned object should be released with `git_object_free` and the
+ * returned reference with `git_reference_free` when no longer needed.
+ *
  * @param object_out pointer to output object
  * @param reference_out pointer to output reference or NULL
  * @param repo the repository to search in
@@ -76,25 +85,27 @@
 	git_object *from;
 	/** The right element of the revspec; must be freed by the user */
 	git_object *to;
-	/** The intent of the revspec */
+	/** The intent of the revspec (i.e. `git_revparse_mode_t` flags) */
 	unsigned int flags;
 } git_revspec;
 
 /**
- * Parse a revision string for `from`, `to`, and intent. See `man gitrevisions` or
- * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for information
- * on the syntax accepted.
+ * Parse a revision string for `from`, `to`, and intent.
  *
- * @param revspec Pointer to an user-allocated git_revspec struct where the result
- *	of the rev-parse will be stored
+ * See `man gitrevisions` or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * @param revspec Pointer to an user-allocated git_revspec struct where
+ *	              the result of the rev-parse will be stored
  * @param repo the repository to search in
  * @param spec the rev-parse spec to parse
  * @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
  */
 GIT_EXTERN(int) git_revparse(
-		git_revspec *revspec,
-		git_repository *repo,
-		const char *spec);
+	git_revspec *revspec,
+	git_repository *repo,
+	const char *spec);
 
 
 /** @} */
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index 8bfe0b5..c59b799 100644
--- a/include/git2/revwalk.h
+++ b/include/git2/revwalk.h
@@ -232,6 +232,14 @@
 GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
 
 /**
+ * Simplify the history by first-parent
+ *
+ * No parents other than the first for each commit will be enqueued.
+ */
+GIT_EXTERN(void) git_revwalk_simplify_first_parent(git_revwalk *walk);
+
+
+/**
  * Free a revision walker previously allocated.
  *
  * @param walk traversal handle to close. If NULL nothing occurs.
diff --git a/include/git2/signature.h b/include/git2/signature.h
index 00d19de..2fa46d0 100644
--- a/include/git2/signature.h
+++ b/include/git2/signature.h
@@ -48,6 +48,19 @@
  */
 GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
 
+/**
+ * Create a new action signature with default user and now timestamp.
+ *
+ * This looks up the user.name and user.email from the configuration and
+ * uses the current time as the timestamp, and creates a new signature
+ * based on that information.  It will return GIT_ENOTFOUND if either the
+ * user.name or user.email are not set.
+ *
+ * @param out new signature
+ * @param repo repository pointer
+ * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
+ */
+GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
 
 /**
  * Create a copy of an existing signature.  All internal strings are also
diff --git a/include/git2/stash.h b/include/git2/stash.h
index 68d1b54..b48d33f 100644
--- a/include/git2/stash.h
+++ b/include/git2/stash.h
@@ -57,7 +57,7 @@
 GIT_EXTERN(int) git_stash_save(
 	git_oid *out,
 	git_repository *repo,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message,
 	unsigned int flags);
 
diff --git a/include/git2/status.h b/include/git2/status.h
index 63aea2f..4ec3432 100644
--- a/include/git2/status.h
+++ b/include/git2/status.h
@@ -60,25 +60,24 @@
 	const char *path, unsigned int status_flags, void *payload);
 
 /**
- * For extended status, select the files on which to report status.
+ * Select the files on which to report status.
+ *
+ * With `git_status_foreach_ext`, this will control which changes get
+ * callbacks.  With `git_status_list_new`, these will control which
+ * changes are included in the list.
  *
  * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default.  This roughly
- *   matches `git status --porcelain` where each file gets a callback
- *   indicating its status in the index and in the working directory.
+ *   matches `git status --porcelain` regarding which files are
+ *   included and in what order.
  * - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
  *   comparison, not looking at working directory changes.
  * - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
  *   working directory comparison, not comparing the index to the HEAD.
- * - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
- *   issuing (up to) two callbacks per file (first index, then workdir).
- *   This is slightly more efficient than separate calls and can make it
- *   easier to emulate plain `git status` text output.
  */
 typedef enum {
 	GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
 	GIT_STATUS_SHOW_INDEX_ONLY = 1,
 	GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
-	GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
 } git_status_show_t;
 
 /**
@@ -108,7 +107,7 @@
  * - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
  *   should be processed between the head and the index and enables
  *   the GIT_STATUS_INDEX_RENAMED as a possible status flag.
- * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
+ * - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates that rename
  *   detection should be run between the index and the working directory
  *   and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
  * - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
@@ -117,6 +116,11 @@
  * - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
  *   sensitivity for the file system and forces the output to be in
  *   case-insensitive order
+ * - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
+ *   should include rewritten files
+ * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
+ *   doing a "soft" index reload (i.e. reloading the index data if the
+ *   file on disk has been modified outside libgit2).
  *
  * Calling `git_status_foreach()` is like calling the extended version
  * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
@@ -135,6 +139,8 @@
 	GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
 	GIT_STATUS_OPT_SORT_CASE_SENSITIVELY    = (1u << 9),
 	GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY  = (1u << 10),
+	GIT_STATUS_OPT_RENAMES_FROM_REWRITES    = (1u << 11),
+	GIT_STATUS_OPT_NO_REFRESH               = (1u << 12),
 } git_status_opt_t;
 
 #define GIT_STATUS_OPT_DEFAULTS \
@@ -235,12 +241,12 @@
  * This is not quite the same as calling `git_status_foreach_ext()` with
  * the pathspec set to the specified path.
  *
- * @param status_flags The status value for the file
+ * @param status_flags Output combination of git_status_t values for file
  * @param repo A repository object
- * @param path The file to retrieve status for, rooted at the repo's workdir
+ * @param path The file to retrieve status for relative to the repo workdir
  * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
- *      index, and work tree, GIT_EINVALIDPATH if `path` points at a folder,
- *      GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error.
+ *      index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files
+ *      or if it refers to a folder, and -1 on other errors.
  */
 GIT_EXTERN(int) git_status_file(
 	unsigned int *status_flags,
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 91b5300..186f263 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -14,51 +14,18 @@
 /**
  * @file git2/submodule.h
  * @brief Git submodule management utilities
- * @defgroup git_submodule Git submodule management routines
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Opaque structure representing a submodule.
  *
  * Submodule support in libgit2 builds a list of known submodules and keeps
  * it in the repository.  The list is built from the .gitmodules file, the
  * .git/config file, the index, and the HEAD tree.  Items in the working
  * directory that look like submodules (i.e. a git repo) but are not
  * mentioned in those places won't be tracked.
- */
-typedef struct git_submodule git_submodule;
-
-/**
- * Values that could be specified for the update rule of a submodule.
  *
- * Use the DEFAULT value if you have altered the update value via
- * `git_submodule_set_update()` and wish to reset to the original default.
+ * @defgroup git_submodule Git submodule management routines
+ * @ingroup Git
+ * @{
  */
-typedef enum {
-	GIT_SUBMODULE_UPDATE_DEFAULT = -1,
-	GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
-	GIT_SUBMODULE_UPDATE_REBASE = 1,
-	GIT_SUBMODULE_UPDATE_MERGE = 2,
-	GIT_SUBMODULE_UPDATE_NONE = 3
-} git_submodule_update_t;
-
-/**
- * Values that could be specified for how closely to examine the
- * working directory when getting submodule status.
- *
- * Use the DEFUALT value if you have altered the ignore value via
- * `git_submodule_set_ignore()` and wish to reset to the original value.
- */
-typedef enum {
-	GIT_SUBMODULE_IGNORE_DEFAULT = -1,  /* reset to default */
-	GIT_SUBMODULE_IGNORE_NONE = 0,      /* any change or untracked == dirty */
-	GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
-	GIT_SUBMODULE_IGNORE_DIRTY = 2,     /* only dirty if HEAD moved */
-	GIT_SUBMODULE_IGNORE_ALL = 3        /* never dirty */
-} git_submodule_ignore_t;
+GIT_BEGIN_DECL
 
 /**
  * Return codes for submodule status.
@@ -119,19 +86,9 @@
 	GIT_SUBMODULE_STATUS_WD_UNTRACKED      = (1u << 13),
 } git_submodule_status_t;
 
-#define GIT_SUBMODULE_STATUS__IN_FLAGS \
-	(GIT_SUBMODULE_STATUS_IN_HEAD | \
-	GIT_SUBMODULE_STATUS_IN_INDEX | \
-	GIT_SUBMODULE_STATUS_IN_CONFIG | \
-	GIT_SUBMODULE_STATUS_IN_WD)
-
-#define GIT_SUBMODULE_STATUS__INDEX_FLAGS \
-	(GIT_SUBMODULE_STATUS_INDEX_ADDED | \
-	GIT_SUBMODULE_STATUS_INDEX_DELETED | \
-	GIT_SUBMODULE_STATUS_INDEX_MODIFIED)
-
-#define GIT_SUBMODULE_STATUS__WD_FLAGS \
-	~(GIT_SUBMODULE_STATUS__IN_FLAGS | GIT_SUBMODULE_STATUS__INDEX_FLAGS)
+#define GIT_SUBMODULE_STATUS__IN_FLAGS		0x000Fu
+#define GIT_SUBMODULE_STATUS__INDEX_FLAGS	0x0070u
+#define GIT_SUBMODULE_STATUS__WD_FLAGS		0x3F80u
 
 #define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
 	(((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
@@ -359,9 +316,10 @@
 GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
 
 /**
- * Get the ignore rule for the submodule.
+ * Get the ignore rule that will be used for the submodule.
  *
- * There are four ignore values:
+ * These values control the behavior of `git_submodule_status()` for this
+ * submodule.  There are four ignore values:
  *
  *  - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
  *    of the submodule from a clean checkout to be dirty, including the
@@ -375,6 +333,13 @@
  *  - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
  *    The working directory will be consider clean so long as there is a
  *    checked out version present.
+ *
+ * plus the special **GIT_SUBMODULE_IGNORE_RESET** which can be used with
+ * `git_submodule_set_ignore()` to revert to the on-disk setting.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_ignore_t valyue what will be used for
+ *         this submodule.
  */
 GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
 	git_submodule *submodule);
@@ -382,15 +347,17 @@
 /**
  * Set the ignore rule for the submodule.
  *
- * This sets the ignore rule in memory for the submodule.  This will be used
- * for any following actions (such as `git_submodule_status()`) while the
- * submodule is in memory.  You should call `git_submodule_save()` if you
- * want to persist the new ignore role.
+ * This sets the in-memory ignore rule for the submodule which will
+ * control the behavior of `git_submodule_status()`.
  *
- * Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling
- * `git_submodule_reload()` will revert the rule to the value that was in the
- * original config.
+ * To make changes persistent, call `git_submodule_save()` to write the
+ * value to disk (in the ".gitmodules" and ".git/config" files).
  *
+ * Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()`
+ * to revert the in-memory rule to the value that is on disk.
+ *
+ * @param submodule The submodule to update
+ * @param ignore The new value for the ignore rule
  * @return old value for ignore
  */
 GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
@@ -398,7 +365,16 @@
 	git_submodule_ignore_t ignore);
 
 /**
- * Get the update rule for the submodule.
+ * Get the update rule that will be used for the submodule.
+ *
+ * This value controls the behavior of the `git submodule update` command.
+ * There are four useful values documented with `git_submodule_update_t`
+ * plus the `GIT_SUBMODULE_UPDATE_RESET` which can be used to revert to
+ * the on-disk setting.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_update_t value that will be used
+ *         for this submodule.
  */
 GIT_EXTERN(git_submodule_update_t) git_submodule_update(
 	git_submodule *submodule);
@@ -406,13 +382,17 @@
 /**
  * Set the update rule for the submodule.
  *
- * This sets the update rule in memory for the submodule.  You should call
- * `git_submodule_save()` if you want to persist the new update rule.
+ * The initial value comes from the ".git/config" setting of
+ * `submodule.$name.update` for this submodule (which is initialized from
+ * the ".gitmodules" file).  Using this function sets the update rule in
+ * memory for the submodule.  Call `git_submodule_save()` to write out the
+ * new update rule.
  *
- * Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling
- * `git_submodule_reload()` will revert the rule to the value that was in the
- * original config.
+ * Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling
+ * `git_submodule_reload()` will revert the rule to the on disk value.
  *
+ * @param submodule The submodule to update
+ * @param update The new value to use
  * @return old value for update
  */
 GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
index 11e59cf..419ad7e 100644
--- a/include/git2/sys/config.h
+++ b/include/git2/sys/config.h
@@ -21,6 +21,33 @@
 GIT_BEGIN_DECL
 
 /**
+ * Every iterator must have this struct as its first element, so the
+ * API can talk to it. You'd define your iterator as
+ *
+ *     struct my_iterator {
+ *             git_config_iterator parent;
+ *             ...
+ *     }
+ *
+ * and assign `iter->parent.backend` to your `git_config_backend`.
+ */
+struct git_config_iterator {
+	git_config_backend *backend;
+	unsigned int flags;
+
+	/**
+	 * Return the current entry and advance the iterator. The
+	 * memory belongs to the library.
+	 */
+	int (*next)(git_config_entry **entry, git_config_iterator *iter);
+
+	/**
+	 * Free the iterator
+	 */
+	void (*free)(git_config_iterator *iter);
+};
+
+/**
  * Generic backend that implements the interface to
  * access a configuration file
  */
@@ -31,11 +58,11 @@
 	/* Open means open the file/database and parse if necessary */
 	int (*open)(struct git_config_backend *, git_config_level_t level);
 	int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
-	int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
 	int (*set)(struct git_config_backend *, const char *key, const char *value);
 	int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
 	int (*del)(struct git_config_backend *, const char *key);
-	int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
+	int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
+	int (*iterator)(git_config_iterator **, struct git_config_backend *);
 	int (*refresh)(struct git_config_backend *);
 	void (*free)(struct git_config_backend *);
 };
diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h
new file mode 100644
index 0000000..94ad3ae
--- /dev/null
+++ b/include/git2/sys/filter.h
@@ -0,0 +1,292 @@
+/*
+ * 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_sys_git_filter_h__
+#define INCLUDE_sys_git_filter_h__
+
+#include "git2/filter.h"
+
+/**
+ * @file git2/sys/filter.h
+ * @brief Git filter backend and plugin routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Look up a filter by name
+ *
+ * @param name The name of the filter
+ * @return Pointer to the filter object or NULL if not found
+ */
+GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
+
+#define GIT_FILTER_CRLF  "crlf"
+#define GIT_FILTER_IDENT "ident"
+
+/**
+ * This is priority that the internal CRLF filter will be registered with
+ */
+#define GIT_FILTER_CRLF_PRIORITY 0
+
+/**
+ * This is priority that the internal ident filter will be registered with
+ */
+#define GIT_FILTER_IDENT_PRIORITY 100
+
+/**
+ * This is priority to use with a custom filter to imitate a core Git
+ * filter driver, so that it will be run last on checkout and first on
+ * checkin.  You do not have to use this, but it helps compatibility.
+ */
+#define GIT_FILTER_DRIVER_PRIORITY 200
+
+/**
+ * Create a new empty filter list
+ *
+ * Normally you won't use this because `git_filter_list_load` will create
+ * the filter list for you, but you can use this in combination with the
+ * `git_filter_lookup` and `git_filter_list_push` functions to assemble
+ * your own chains of filters.
+ */
+GIT_EXTERN(int) git_filter_list_new(
+	git_filter_list **out, git_repository *repo, git_filter_mode_t mode);
+
+/**
+ * Add a filter to a filter list with the given payload.
+ *
+ * Normally you won't have to do this because the filter list is created
+ * by calling the "check" function on registered filters when the filter
+ * attributes are set, but this does allow more direct manipulation of
+ * filter lists when desired.
+ *
+ * Note that normally the "check" function can set up a payload for the
+ * filter.  Using this function, you can either pass in a payload if you
+ * know the expected payload format, or you can pass NULL.  Some filters
+ * may fail with a NULL payload.  Good luck!
+ */
+GIT_EXTERN(int) git_filter_list_push(
+	git_filter_list *fl, git_filter *filter, void *payload);
+
+/**
+ * Look up how many filters are in the list
+ *
+ * We will attempt to apply all of these filters to any data passed in,
+ * but note that the filter apply action still has the option of skipping
+ * data that is passed in (for example, the CRLF filter will skip data
+ * that appears to be binary).
+ *
+ * @param fl A filter list
+ * @return The number of filters in the list
+ */
+GIT_EXTERN(size_t) git_filter_list_length(const git_filter_list *fl);
+
+/**
+ * A filter source represents a file/blob to be processed
+ */
+typedef struct git_filter_source git_filter_source;
+
+/**
+ * Get the repository that the source data is coming from.
+ */
+GIT_EXTERN(git_repository *) git_filter_source_repo(const git_filter_source *src);
+
+/**
+ * Get the path that the source data is coming from.
+ */
+GIT_EXTERN(const char *) git_filter_source_path(const git_filter_source *src);
+
+/**
+ * Get the file mode of the source file
+ * If the mode is unknown, this will return 0
+ */
+GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
+
+/**
+ * Get the OID of the source
+ * If the OID is unknown (often the case with GIT_FILTER_CLEAN) then
+ * this will return NULL.
+ */
+GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
+
+/**
+ * Get the git_filter_mode_t to be applied
+ */
+GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
+
+/*
+ * struct git_filter
+ *
+ * The filter lifecycle:
+ * - initialize - first use of filter
+ * - shutdown   - filter removed/unregistered from system
+ * - check      - considering filter for file
+ * - apply      - apply filter to file contents
+ * - cleanup    - done with file
+ */
+
+/**
+ * Initialize callback on filter
+ *
+ * Specified as `filter.initialize`, this is an optional callback invoked
+ * before a filter is first used.  It will be called once at most.
+ *
+ * If non-NULL, the filter's `initialize` callback will be invoked right
+ * before the first use of the filter, so you can defer expensive
+ * initialization operations (in case libgit2 is being used in a way that
+ * doesn't need the filter).
+ */
+typedef int (*git_filter_init_fn)(git_filter *self);
+
+/**
+ * Shutdown callback on filter
+ *
+ * Specified as `filter.shutdown`, this is an optional callback invoked
+ * when the filter is unregistered or when libgit2 is shutting down.  It
+ * will be called once at most and should release resources as needed.
+ *
+ * Typically this function will free the `git_filter` object itself.
+ */
+typedef void (*git_filter_shutdown_fn)(git_filter *self);
+
+/**
+ * Callback to decide if a given source needs this filter
+ *
+ * Specified as `filter.check`, this is an optional callback that checks
+ * if filtering is needed for a given source.
+ *
+ * It should return 0 if the filter should be applied (i.e. success),
+ * GIT_PASSTHROUGH if the filter should not be applied, or an error code
+ * to fail out of the filter processing pipeline and return to the caller.
+ *
+ * The `attr_values` will be set to the values of any attributes given in
+ * the filter definition.  See `git_filter` below for more detail.
+ *
+ * The `payload` will be a pointer to a reference payload for the filter.
+ * This will start as NULL, but `check` can assign to this pointer for
+ * later use by the `apply` callback.  Note that the value should be heap
+ * allocated (not stack), so that it doesn't go away before the `apply`
+ * callback can use it.  If a filter allocates and assigns a value to the
+ * `payload`, it will need a `cleanup` callback to free the payload.
+ */
+typedef int (*git_filter_check_fn)(
+	git_filter  *self,
+	void       **payload, /* points to NULL ptr on entry, may be set */
+	const git_filter_source *src,
+	const char **attr_values);
+
+/**
+ * Callback to actually perform the data filtering
+ *
+ * Specified as `filter.apply`, this is the callback that actually filters
+ * data.  If it successfully writes the output, it should return 0.  Like
+ * `check`, it can return GIT_PASSTHROUGH to indicate that the filter
+ * doesn't want to run.  Other error codes will stop filter processing and
+ * return to the caller.
+ *
+ * The `payload` value will refer to any payload that was set by the
+ * `check` callback.  It may be read from or written to as needed.
+ */
+typedef int (*git_filter_apply_fn)(
+	git_filter    *self,
+	void         **payload, /* may be read and/or set */
+	git_buf       *to,
+	const git_buf *from,
+	const git_filter_source *src);
+
+/**
+ * Callback to clean up after filtering has been applied
+ *
+ * Specified as `filter.cleanup`, this is an optional callback invoked
+ * after the filter has been applied.  If the `check` or `apply` callbacks
+ * allocated a `payload` to keep per-source filter state, use this
+ * callback to free that payload and release resources as required.
+ */
+typedef void (*git_filter_cleanup_fn)(
+	git_filter *self,
+	void       *payload);
+
+/**
+ * Filter structure used to register custom filters.
+ *
+ * To associate extra data with a filter, allocate extra data and put the
+ * `git_filter` struct at the start of your data buffer, then cast the
+ * `self` pointer to your larger structure when your callback is invoked.
+ *
+ * `version` should be set to GIT_FILTER_VERSION
+ *
+ * `attributes` is a whitespace-separated list of attribute names to check
+ * for this filter (e.g. "eol crlf text").  If the attribute name is bare,
+ * it will be simply loaded and passed to the `check` callback.  If it has
+ * a value (i.e. "name=value"), the attribute must match that value for
+ * the filter to be applied.
+ *
+ * The `initialize`, `shutdown`, `check`, `apply`, and `cleanup` callbacks
+ * are all documented above with the respective function pointer typedefs.
+ */
+struct git_filter {
+	unsigned int           version;
+
+	const char            *attributes;
+
+	git_filter_init_fn     initialize;
+	git_filter_shutdown_fn shutdown;
+	git_filter_check_fn    check;
+	git_filter_apply_fn    apply;
+	git_filter_cleanup_fn  cleanup;
+};
+
+#define GIT_FILTER_VERSION 1
+
+/**
+ * Register a filter under a given name with a given priority.
+ *
+ * As mentioned elsewhere, the initialize callback will not be invoked
+ * immediately.  It is deferred until the filter is used in some way.
+ *
+ * A filter's attribute checks and `check` and `apply` callbacks will be
+ * issued in order of `priority` on smudge (to workdir), and in reverse
+ * order of `priority` on clean (to odb).
+ *
+ * Two filters are preregistered with libgit2:
+ * - GIT_FILTER_CRLF with priority 0
+ * - GIT_FILTER_IDENT with priority 100
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name A name by which the filter can be referenced.  Attempting
+ * 			to register with an in-use name will return GIT_EEXISTS.
+ * @param filter The filter definition.  This pointer will be stored as is
+ * 			by libgit2 so it must be a durable allocation (either static
+ * 			or on the heap).
+ * @param priority The priority for filter application
+ * @return 0 on successful registry, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_register(
+	const char *name, git_filter *filter, int priority);
+
+/**
+ * Remove the filter with the given name
+ *
+ * Attempting to remove the builtin libgit2 filters is not permitted and
+ * will return an error.
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name The name under which the filter was registered
+ * @return 0 on success, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_unregister(const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h
index a32e070..1a06a4d 100644
--- a/include/git2/sys/index.h
+++ b/include/git2/sys/index.h
@@ -72,7 +72,6 @@
  * Remove all filename conflict entries.
  *
  * @param index an existing index object
- * @return 0 or an error code
  */
 GIT_EXTERN(void) git_index_name_clear(git_index *index);
 
@@ -168,7 +167,6 @@
  * Remove all resolve undo entries from the index
  *
  * @param index an existing index object
- * @return 0 or an error code
  */
 GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
 
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
index 3cd2734..8039a5b 100644
--- a/include/git2/sys/odb_backend.h
+++ b/include/git2/sys/odb_backend.h
@@ -48,12 +48,12 @@
 	int (* read_header)(
 		size_t *, git_otype *, git_odb_backend *, const git_oid *);
 
-	/* The writer may assume that the object
-	 * has already been hashed and is passed
-	 * in the first parameter.
+	/**
+	 * Write an object into the backend. The id of the object has
+	 * already been calculated and is passed in.
 	 */
 	int (* write)(
-		git_oid *, git_odb_backend *, const void *, size_t, git_otype);
+		git_odb_backend *, const git_oid *, const void *, size_t, git_otype);
 
 	int (* writestream)(
 		git_odb_stream **, git_odb_backend *, size_t, git_otype);
@@ -64,13 +64,23 @@
 	int (* exists)(
 		git_odb_backend *, const git_oid *);
 
+	/**
+	 * If the backend implements a refreshing mechanism, it should be exposed
+	 * through this endpoint. Each call to `git_odb_refresh()` will invoke it.
+	 *
+	 * However, the backend implementation should try to stay up-to-date as much
+	 * as possible by itself as libgit2 will not automatically invoke
+	 * `git_odb_refresh()`. For instance, a potential strategy for the backend
+	 * implementation to achieve this could be to internally invoke this
+	 * endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`).
+	 */
 	int (* refresh)(git_odb_backend *);
 
 	int (* foreach)(
 		git_odb_backend *, git_odb_foreach_cb cb, void *payload);
 
 	int (* writepack)(
-		git_odb_writepack **, git_odb_backend *,
+		git_odb_writepack **, git_odb_backend *, git_odb *odb,
 		git_transfer_progress_callback progress_cb, void *progress_payload);
 
 	void (* free)(git_odb_backend *);
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
index 9b457b0..9cf5073 100644
--- a/include/git2/sys/refdb_backend.h
+++ b/include/git2/sys/refdb_backend.h
@@ -103,7 +103,7 @@
 	 * Deletes the given reference from the refdb.  A refdb implementation
 	 * must provide this function.
 	 */
-	int (*delete)(git_refdb_backend *backend, const char *ref_name);
+	int (*del)(git_refdb_backend *backend, const char *ref_name);
 
 	/**
 	 * Suggests that the given refdb compress or optimize its references.
@@ -119,10 +119,30 @@
 	 * provide this function; if it is not provided, nothing will be done.
 	 */
 	void (*free)(git_refdb_backend *backend);
+
+	/**
+	 * Read the reflog for the given reference name.
+	 */
+	int (*reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name);
+
+	/**
+	 * Write a reflog to disk.
+	 */
+	int (*reflog_write)(git_refdb_backend *backend, git_reflog *reflog);
+
+	/**
+	 * Rename a reflog
+	 */
+	int (*reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name);
+
+	/**
+	 * Remove a reflog.
+	 */
+	int (*reflog_delete)(git_refdb_backend *backend, const char *name);
 };
 
-#define GIT_ODB_BACKEND_VERSION 1
-#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
+#define GIT_REFDB_BACKEND_VERSION 1
+#define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
 
 /**
  * Constructors for default filesystem-based refdb backend
diff --git a/include/git2/sys/reflog.h b/include/git2/sys/reflog.h
new file mode 100644
index 0000000..c9d0041
--- /dev/null
+++ b/include/git2/sys/reflog.h
@@ -0,0 +1,21 @@
+/*
+ * 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_sys_git_reflog_h__
+#define INCLUDE_sys_git_reflog_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+GIT_BEGIN_DECL
+
+GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void);
+GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/refs.h b/include/git2/sys/refs.h
index 8596325..dd95ca1 100644
--- a/include/git2/sys/refs.h
+++ b/include/git2/sys/refs.h
@@ -16,7 +16,7 @@
  *
  * @param name the reference name
  * @param oid the object id for a direct reference
- * @param symbolic the target for a symbolic reference
+ * @param peel the first non-tag object's OID, or NULL
  * @return the created git_reference or NULL on error
  */
 GIT_EXTERN(git_reference *) git_reference__alloc(
@@ -28,7 +28,7 @@
  * Create a new symbolic reference.
  *
  * @param name the reference name
- * @param symbolic the target for a symbolic reference
+ * @param target the target for a symbolic reference
  * @return the created git_reference or NULL on error
  */
 GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index ba3d65a..36f8b58 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -27,7 +27,6 @@
  */
 GIT_EXTERN(int) git_repository_new(git_repository **out);
 
-
 /**
  * Reset all the internal state in a repository.
  *
@@ -42,6 +41,25 @@
 GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
 
 /**
+ * Update the filesystem config settings for an open repository
+ *
+ * When a repository is initialized, config values are set based on the
+ * properties of the filesystem that the repository is on, such as
+ * "core.ignorecase", "core.filemode", "core.symlinks", etc.  If the
+ * repository is moved to a new filesystem, these properties may no
+ * longer be correct and API calls may not behave as expected.  This
+ * call reruns the phase of repository initialization that sets those
+ * properties to compensate for the current filesystem of the repo.
+ *
+ * @param repo A repository object
+ * @param recurse_submodules Should submodules be updated recursively
+ * @returrn 0 on success, < 0 on error
+ */
+GIT_EXTERN(int) git_repository_reinit_filesystem(
+	git_repository *repo,
+	int recurse_submodules);
+
+/**
  * Set the configuration file for this repository
  *
  * This configuration file will be used for all configuration
diff --git a/include/git2/transport.h b/include/git2/transport.h
index 81bb3ab..caabd04 100644
--- a/include/git2/transport.h
+++ b/include/git2/transport.h
@@ -28,22 +28,31 @@
  *** Begin interface for credentials acquisition ***
  */
 
+/** Authentication type requested */
 typedef enum {
 	/* git_cred_userpass_plaintext */
-	GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
-	GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
-	GIT_CREDTYPE_SSH_PUBLICKEY = 3,
+	GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
+
+	/* git_cred_ssh_key */
+	GIT_CREDTYPE_SSH_KEY = (1u << 1),
+
+	/* git_cred_ssh_custom */
+	GIT_CREDTYPE_SSH_CUSTOM = (1u << 2),
+
+	/* git_cred_default */
+	GIT_CREDTYPE_DEFAULT = (1u << 3),
 } git_credtype_t;
 
 /* The base structure for all credential types */
-typedef struct git_cred {
-	git_credtype_t credtype;
-	void (*free)(
-		struct git_cred *cred);
-} git_cred;
+typedef struct git_cred git_cred;
 
-/* A plaintext username and password */
-typedef struct git_cred_userpass_plaintext {
+struct git_cred {
+	git_credtype_t credtype;
+	void (*free)(git_cred *cred);
+};
+
+/** A plaintext username and password */
+typedef struct {
 	git_cred parent;
 	char *username;
 	char *password;
@@ -51,27 +60,46 @@
 
 #ifdef GIT_SSH
 typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
-
-/* A ssh key file and passphrase */
-typedef struct git_cred_ssh_keyfile_passphrase {
-	git_cred parent;
-	char *publickey;
-	char *privatekey;
-	char *passphrase;
-} git_cred_ssh_keyfile_passphrase;
-
-/* A ssh public key and authentication callback */
-typedef struct git_cred_ssh_publickey {
-	git_cred parent;
-	char *publickey;
-    size_t publickey_len;
-	void *sign_callback;
-	void *sign_data;
-} git_cred_ssh_publickey;
+#else
+typedef int (*git_cred_sign_callback)(void *, ...);
 #endif
 
 /**
- * Creates a new plain-text username and password credential object.
+ * A ssh key from disk
+ */
+typedef struct git_cred_ssh_key {
+	git_cred parent;
+	char *username;
+	char *publickey;
+	char *privatekey;
+	char *passphrase;
+} git_cred_ssh_key;
+
+/**
+ * A key with a custom signature function
+ */
+typedef struct git_cred_ssh_custom {
+	git_cred parent;
+	char *username;
+	char *publickey;
+	size_t publickey_len;
+	void *sign_callback;
+	void *sign_data;
+} git_cred_ssh_custom;
+
+/** A key for NTLM/Kerberos "default" credentials */
+typedef struct git_cred git_cred_default;
+
+/**
+ * Check whether a credential object contains username information.
+ *
+ * @param cred object to check
+ * @return 1 if the credential object has non-NULL username, 0 otherwise
+ */
+GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
+
+/**
+ * Create a new plain-text username and password credential object.
  * The supplied credential parameter will be internally duplicated.
  *
  * @param out The newly created credential object.
@@ -84,52 +112,68 @@
 	const char *username,
 	const char *password);
 
-#ifdef GIT_SSH
 /**
- * Creates a new ssh key file and passphrase credential object.
+ * Create a new passphrase-protected ssh key credential object.
  * The supplied credential parameter will be internally duplicated.
  *
  * @param out The newly created credential object.
+ * @param username username to use to authenticate
  * @param publickey The path to the public key of the credential.
  * @param privatekey The path to the private key of the credential.
  * @param passphrase The passphrase of the credential.
  * @return 0 for success or an error code for failure
  */
-GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
+GIT_EXTERN(int) git_cred_ssh_key_new(
 	git_cred **out,
+	const char *username,
 	const char *publickey,
 	const char *privatekey,
-    const char *passphrase);
+	const char *passphrase);
 
 /**
- * Creates a new ssh public key credential object.
+ * Create an ssh key credential with a custom signing function.
+ *
+ * This lets you use your own function to sign the challenge.
+ *
+ * This function and its credential type is provided for completeness
+ * and wraps `libssh2_userauth_publickey()`, which is undocumented.
+ *
  * The supplied credential parameter will be internally duplicated.
  *
  * @param out The newly created credential object.
+ * @param username username to use to authenticate
  * @param publickey The bytes of the public key.
  * @param publickey_len The length of the public key in bytes.
- * @param sign_callback The callback method for authenticating.
- * @param sign_data The abstract data sent to the sign_callback method.
+ * @param sign_fn The callback method to sign the data during the challenge.
+ * @param sign_data The data to pass to the sign function.
  * @return 0 for success or an error code for failure
  */
-GIT_EXTERN(int) git_cred_ssh_publickey_new(
+GIT_EXTERN(int) git_cred_ssh_custom_new(
 	git_cred **out,
+	const char *username,
 	const char *publickey,
-    size_t publickey_len,
-    git_cred_sign_callback,
-    void *sign_data);
-#endif
+	size_t publickey_len,
+	git_cred_sign_callback sign_fn,
+	void *sign_data);
+
+/**
+ * Create a "default" credential usable for Negotiate mechanisms like NTLM
+ * or Kerberos authentication.
+ *
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_cred_default_new(git_cred **out);
 
 /**
  * Signature of a function which acquires a credential object.
  *
- * @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"
+ * - 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"
  *                          remote url, or NULL if not included.
- * @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 or an error code for failure
+ * - allowed_types: A bitmask stating which cred types are OK to return.
+ * - payload: The payload provided when specifying this callback.
+ * - returns 0 for success or non-zero to indicate an error
  */
 typedef int (*git_cred_acquire_cb)(
 	git_cred **cred,
@@ -150,39 +194,45 @@
 	GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
 } git_transport_flags_t;
 
-typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
+typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
 
-typedef struct git_transport {
+typedef struct git_transport git_transport;
+
+struct git_transport {
 	unsigned int version;
 	/* Set progress and error callbacks */
-	int (*set_callbacks)(struct git_transport *transport,
+	int (*set_callbacks)(
+		git_transport *transport,
 		git_transport_message_cb progress_cb,
 		git_transport_message_cb error_cb,
 		void *payload);
 
 	/* Connect the transport to the remote repository, using the given
 	 * direction. */
-	int (*connect)(struct git_transport *transport,
+	int (*connect)(
+		git_transport *transport,
 		const char *url,
 		git_cred_acquire_cb cred_acquire_cb,
 		void *cred_acquire_payload,
 		int direction,
 		int flags);
 
-	/* This function may be called after a successful call to connect(). The
-	 * provided callback is invoked for each ref discovered on the remote
-	 * end. */
-	int (*ls)(struct git_transport *transport,
-		git_headlist_cb list_cb,
-		void *payload);
+	/* This function may be called after a successful call to
+	 * connect(). The array returned is owned by the transport and
+	 * is guranteed until the next call of a transport function. */
+	int (*ls)(
+		const git_remote_head ***out,
+		size_t *size,
+		git_transport *transport);
 
 	/* Executes the push whose context is in the git_push object. */
-	int (*push)(struct git_transport *transport, git_push *push);
+	int (*push)(git_transport *transport, git_push *push);
 
 	/* This function may be called after a successful call to connect(), when
 	 * the direction is FETCH. The function performs a negotiation to calculate
 	 * the wants list for the fetch. */
-	int (*negotiate_fetch)(struct git_transport *transport,
+	int (*negotiate_fetch)(
+		git_transport *transport,
 		git_repository *repo,
 		const git_remote_head * const *refs,
 		size_t count);
@@ -190,28 +240,29 @@
 	/* This function may be called after a successful call to negotiate_fetch(),
 	 * when the direction is FETCH. This function retrieves the pack file for
 	 * the fetch from the remote end. */
-	int (*download_pack)(struct git_transport *transport,
+	int (*download_pack)(
+		git_transport *transport,
 		git_repository *repo,
 		git_transfer_progress *stats,
 		git_transfer_progress_callback progress_cb,
 		void *progress_payload);
 
 	/* Checks to see if the transport is connected */
-	int (*is_connected)(struct git_transport *transport);
+	int (*is_connected)(git_transport *transport);
 
 	/* Reads the flags value previously passed into connect() */
-	int (*read_flags)(struct git_transport *transport, int *flags);
+	int (*read_flags)(git_transport *transport, int *flags);
 
 	/* Cancels any outstanding transport operation */
-	void (*cancel)(struct git_transport *transport);
+	void (*cancel)(git_transport *transport);
 
 	/* This function is the reverse of connect() -- it terminates the
 	 * connection to the remote end. */
-	int (*close)(struct git_transport *transport);
+	int (*close)(git_transport *transport);
 
 	/* Frees/destructs the git_transport object. */
-	void (*free)(struct git_transport *transport);
-} git_transport;
+	void (*free)(git_transport *transport);
+};
 
 #define GIT_TRANSPORT_VERSION 1
 #define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
@@ -231,6 +282,40 @@
 /* Signature of a function which creates a transport */
 typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
 
+/**
+ * Add a custom transport definition, to be used in addition to the built-in
+ * set of transports that come with libgit2.
+ *
+ * The caller is responsible for synchronizing calls to git_transport_register
+ * and git_transport_unregister with other calls to the library that
+ * instantiate transports.
+ *
+ * @param prefix The scheme (ending in "://") to match, i.e. "git://"
+ * @param priority The priority of this transport relative to others with
+ *		the same prefix. Built-in transports have a priority of 1.
+ * @param cb The callback used to create an instance of the transport
+ * @param param A fixed parameter to pass to cb at creation time
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_register(
+	const char *prefix,
+	unsigned priority,
+	git_transport_cb cb,
+	void *param);
+
+/**
+ *
+ * Unregister a custom transport definition which was previously registered
+ * with git_transport_register.
+ *
+ * @param prefix From the previous call to git_transport_register
+ * @param priority From the previous call to git_transport_register
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_unregister(
+	const char *prefix,
+	unsigned priority);
+
 /* Transports which come with libgit2 (match git_transport_cb). The expected
  * value for "param" is listed in-line below. */
 
@@ -299,35 +384,36 @@
 	GIT_SERVICE_RECEIVEPACK = 4,
 } git_smart_service_t;
 
-struct git_smart_subtransport;
+typedef struct git_smart_subtransport git_smart_subtransport;
+typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
 
 /* A stream used by the smart transport to read and write data
  * from a subtransport */
-typedef struct git_smart_subtransport_stream {
+struct git_smart_subtransport_stream {
 	/* The owning subtransport */
-	struct git_smart_subtransport *subtransport;
+	git_smart_subtransport *subtransport;
 
 	int (*read)(
-			struct git_smart_subtransport_stream *stream,
-			char *buffer,
-			size_t buf_size,
-			size_t *bytes_read);
+		git_smart_subtransport_stream *stream,
+		char *buffer,
+		size_t buf_size,
+		size_t *bytes_read);
 
 	int (*write)(
-			struct git_smart_subtransport_stream *stream,
-			const char *buffer,
-			size_t len);
+		git_smart_subtransport_stream *stream,
+		const char *buffer,
+		size_t len);
 
 	void (*free)(
-			struct git_smart_subtransport_stream *stream);
-} git_smart_subtransport_stream;
+		git_smart_subtransport_stream *stream);
+};
 
 /* An implementation of a subtransport which carries data for the
  * smart transport */
-typedef struct git_smart_subtransport {
+struct git_smart_subtransport {
 	int (* action)(
 			git_smart_subtransport_stream **out,
-			struct git_smart_subtransport *transport,
+			git_smart_subtransport *transport,
 			const char *url,
 			git_smart_service_t action);
 
@@ -337,10 +423,10 @@
 	 *
 	 * 1. UPLOADPACK_LS -> UPLOADPACK
 	 * 2. RECEIVEPACK_LS -> RECEIVEPACK */
-	int (* close)(struct git_smart_subtransport *transport);
+	int (*close)(git_smart_subtransport *transport);
 
-	void (* free)(struct git_smart_subtransport *transport);
-} git_smart_subtransport;
+	void (*free)(git_smart_subtransport *transport);
+};
 
 /* A function which creates a new subtransport for the smart transport */
 typedef int (*git_smart_subtransport_cb)(
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 65d8cc1..d94b446 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -199,6 +199,17 @@
 GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
 
 /**
+ * Get the raw UNIX file attributes of a tree entry
+ *
+ * This function does not perform any normalization and is only useful
+ * if you need to be able to recreate the original tree object.
+ *
+ * @param entry a tree entry
+ * @return filemode as an integer
+ */
+
+GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry);
+/**
  * Compare two tree entries
  *
  * @param e1 first tree entry
@@ -208,7 +219,7 @@
 GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
 
 /**
- * Convert a tree entry to the git_object it points too.
+ * Convert a tree entry to the git_object it points to.
  *
  * You must call `git_object_free()` on the object when you are done with it.
  *
diff --git a/include/git2/types.h b/include/git2/types.h
index dc34407..55505b1 100644
--- a/include/git2/types.h
+++ b/include/git2/types.h
@@ -174,6 +174,9 @@
 /** Merge heads, the input to merge */
 typedef struct git_merge_head git_merge_head;
 
+/** Merge result */
+typedef struct git_merge_result git_merge_result;
+
 /** Representation of a status collection */
 typedef struct git_status_list git_status_list;
 
@@ -212,11 +215,21 @@
 /**
  * This is passed as the first argument to the callback to allow the
  * user to see the progress.
+ *
+ * - total_objects: number of objects in the packfile being downloaded
+ * - indexed_objects: received objects that have been hashed
+ * - received_objects: objects which have been downloaded
+ * - local_objects: locally-available objects that have been injected
+ *    in order to fix a thin pack.
+ * - received-bytes: size of the packfile received up to now
  */
 typedef struct git_transfer_progress {
 	unsigned int total_objects;
 	unsigned int indexed_objects;
 	unsigned int received_objects;
+	unsigned int local_objects;
+	unsigned int total_deltas;
+	unsigned int indexed_deltas;
 	size_t received_bytes;
 } git_transfer_progress;
 
@@ -229,6 +242,87 @@
  */
 typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
 
+/**
+ * Opaque structure representing a submodule.
+ */
+typedef struct git_submodule git_submodule;
+
+/**
+ * Submodule update values
+ *
+ * These values represent settings for the `submodule.$name.update`
+ * configuration value which says how to handle `git submodule update` for
+ * this submodule.  The value is usually set in the ".gitmodules" file and
+ * copied to ".git/config" when the submodule is initialized.
+ *
+ * You can override this setting on a per-submodule basis with
+ * `git_submodule_set_update()` and write the changed value to disk using
+ * `git_submodule_save()`.  If you have overwritten the value, you can
+ * revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value.
+ * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
+ *   updated, checkout the new detached HEAD to the submodule directory.
+ * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
+ *   out branch onto the commit from the superproject.
+ * - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the
+ *   superproject into the current checkout out branch of the submodule.
+ * - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when
+ *   the commit in the superproject is updated.
+ * - GIT_SUBMODULE_UPDATE_DEFAULT: not used except as static initializer
+ *   when we don't want any particular update rule to be specified.
+ */
+typedef enum {
+	GIT_SUBMODULE_UPDATE_RESET    = -1,
+
+	GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
+	GIT_SUBMODULE_UPDATE_REBASE   = 2,
+	GIT_SUBMODULE_UPDATE_MERGE    = 3,
+	GIT_SUBMODULE_UPDATE_NONE     = 4,
+
+	GIT_SUBMODULE_UPDATE_DEFAULT  = 0
+} git_submodule_update_t;
+
+/**
+ * Submodule ignore values
+ *
+ * These values represent settings for the `submodule.$name.ignore`
+ * configuration value which says how deeply to look at the working
+ * directory when getting submodule status.
+ *
+ * You can override this value in memory on a per-submodule basis with
+ * `git_submodule_set_ignore()` and can write the changed value to disk
+ * with `git_submodule_save()`.  If you have overwritten the value, you
+ * can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_IGNORE_RESET: reset to the on-disk value.
+ * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
+ *   untracked file, will mark the submodule as dirty.  Ignored files are
+ *   still ignored, of course.
+ * - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes
+ *   to tracked files, or the index or the HEAD commit will matter.
+ * - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory,
+ *   only considering changes if the HEAD of submodule has moved from the
+ *   value in the superproject.
+ * - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty
+ * - GIT_SUBMODULE_IGNORE_DEFAULT: not used except as static initializer
+ *   when we don't want any particular ignore rule to be specified.
+ */
+typedef enum {
+	GIT_SUBMODULE_IGNORE_RESET     = -1, /* reset to on-disk value */
+
+	GIT_SUBMODULE_IGNORE_NONE      = 1,  /* any change or untracked == dirty */
+	GIT_SUBMODULE_IGNORE_UNTRACKED = 2,  /* dirty if tracked files change */
+	GIT_SUBMODULE_IGNORE_DIRTY     = 3,  /* only dirty if HEAD moved */
+	GIT_SUBMODULE_IGNORE_ALL       = 4,  /* never dirty */
+
+	GIT_SUBMODULE_IGNORE_DEFAULT   = 0
+} git_submodule_ignore_t;
+
 /** @} */
 GIT_END_DECL
 
diff --git a/include/git2/version.h b/include/git2/version.h
index d8a915f..c4c5e8e 100644
--- a/include/git2/version.h
+++ b/include/git2/version.h
@@ -7,9 +7,9 @@
 #ifndef INCLUDE_git_version_h__
 #define INCLUDE_git_version_h__
 
-#define LIBGIT2_VERSION "0.19.0"
+#define LIBGIT2_VERSION "0.20.0"
 #define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 19
+#define LIBGIT2_VER_MINOR 20
 #define LIBGIT2_VER_REVISION 0
 
 #endif
diff --git a/libgit2.pc.in b/libgit2.pc.in
index 52ad901..8f52792 100644
--- a/libgit2.pc.in
+++ b/libgit2.pc.in
@@ -4,6 +4,7 @@
 Name: libgit2
 Description: The git library, take 2
 Version: @LIBGIT2_VERSION_STRING@
-Requires: libcrypto
-Libs: -L${libdir} -lgit2 -lz -lcrypto
+Requires.private: @LIBGIT2_PC_REQUIRES@
+Libs.private: @LIBGIT2_PC_LIBS@
+Libs: -L${libdir} -lgit2
 Cflags: -I${includedir}
diff --git a/packaging/rpm/README b/packaging/rpm/README
deleted file mode 100644
index 1a6410b..0000000
--- a/packaging/rpm/README
+++ /dev/null
@@ -1,6 +0,0 @@
-To build RPM pakcages for Fedora, follow these steps:
- cp packaging/rpm/libgit2.spec ~/rpmbuild/SPECS
- cd ~/rpmbuild/SOURCES
- wget https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
- cd ~/rpmbuild/SPECS
- rpmbuild -ba libgit2.spec
diff --git a/packaging/rpm/libgit2.spec b/packaging/rpm/libgit2.spec
deleted file mode 100644
index 80e70c1..0000000
--- a/packaging/rpm/libgit2.spec
+++ /dev/null
@@ -1,106 +0,0 @@
-#
-# spec file for package libgit2
-#
-# Copyright (c) 2012 Saleem Ansari <tuxdna@gmail.com>
-# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
-# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>
-#
-# All modifications and additions to the file contributed by third parties
-# remain the property of their copyright owners, unless otherwise agreed
-# upon. The license for this file, and modifications and additions to the
-# file, is the same license as for the pristine package itself (unless the
-# license for the pristine package is not an Open Source License, in which
-# case the license is the MIT License). An "Open Source License" is a
-# license that conforms to the Open Source Definition (Version 1.9)
-# published by the Open Source Initiative.
-
-# Please submit bugfixes or comments via http://bugs.opensuse.org/
-#
-Name:           libgit2
-Version:        0.16.0
-Release:        1
-Summary:        C git library
-License:        GPL-2.0 with linking
-Group:          Development/Libraries/C and C++
-Url:            http://libgit2.github.com/
-Source0:        https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
-BuildRequires:  cmake
-BuildRequires:  pkgconfig
-BuildRoot:      %{_tmppath}/%{name}-%{version}-build
-%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
-BuildRequires:  openssl-devel
-%else
-BuildRequires:  libopenssl-devel
-%endif
-
-%description
-libgit2 is a portable, pure C implementation of the Git core methods
-provided as a re-entrant linkable library with a solid API, allowing
-you to write native speed custom Git applications in any language
-with bindings.
-
-%package -n %{name}-0
-Summary:        C git library
-Group:          System/Libraries
-
-%description -n %{name}-0
-libgit2 is a portable, pure C implementation of the Git core methods
-provided as a re-entrant linkable library with a solid API, allowing
-you to write native speed custom Git applications in any language
-with bindings.
-
-%package devel
-Summary:        C git library
-Group:          Development/Libraries/C and C++
-Requires:       %{name}-0 >= %{version}
-
-%description devel
-This package contains all necessary include files and libraries needed
-to compile and develop applications that use libgit2.
-
-%prep
-%setup -q
-
-%build
-cmake . \
-    -DCMAKE_C_FLAGS:STRING="%{optflags}" \
-    -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
-    -DLIB_INSTALL_DIR:PATH=%{_libdir}S
-make %{?_smp_mflags}
-
-%install
-%make_install
-
-%post -n %{name}-0 -p /sbin/ldconfig
-%postun -n %{name}-0 -p /sbin/ldconfig
-
-%files -n %{name}-0
-%defattr (-,root,root)
-%doc AUTHORS COPYING README.md
-%{_libdir}/%{name}.so.*
-
-%files devel
-%defattr (-,root,root)
-%doc CONVENTIONS examples
-%{_libdir}/%{name}.so
-%{_includedir}/git2*
-%{_libdir}/pkgconfig/libgit2.pc
-
-%changelog
-* Tue Mar 04 2012 tuxdna@gmail.com
-- Update to version 0.16.0 
-* Tue Jan 31 2012 jengelh@medozas.de
-- Provide pkgconfig symbols
-* Thu Oct 27 2011 saschpe@suse.de
-- Change license to 'GPL-2.0 with linking', fixes bnc#726789
-* Wed Oct 26 2011 saschpe@suse.de
-- Update to version 0.15.0:
-  * Upstream doesn't provide changes
-- Removed outdated %%clean section
-* Tue Jan 18 2011 saschpe@gmx.de
-- Proper Requires for devel package
-* Tue Jan 18 2011 saschpe@gmx.de
-- Set BuildRequires to "openssl-devel" also for RHEL and CentOS
-* Tue Jan 18 2011 saschpe@gmx.de
-- Initial commit (0.0.1)
-- Added patch to fix shared library soname
diff --git a/script/cibuild.sh b/script/cibuild.sh
new file mode 100755
index 0000000..aa4fa47
--- /dev/null
+++ b/script/cibuild.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Create a test repo which we can use for the online::push tests
+mkdir $HOME/_temp
+git init --bare $HOME/_temp/test.git
+git daemon --listen=localhost --export-all --enable=receive-pack --base-path=$HOME/_temp $HOME/_temp 2>/dev/null &
+export GITTEST_REMOTE_URL="git://localhost/test.git"
+
+mkdir _build
+cd _build
+cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
+cmake --build . --target install || exit $?
+ctest -V . || exit $?
+
+# Now that we've tested the raw git protocol, let's set up ssh to we
+# can do the push tests over it
+
+killall git-daemon
+sudo start ssh
+ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
+cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
+ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
+
+export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
+export GITTEST_REMOTE_USER=$USER
+export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
+export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
+export GITTEST_REMOTE_SSH_PASSPHRASE=""
+
+if [ -e ./libgit2_clar ]; then
+    ./libgit2_clar -sonline::push
+fi
diff --git a/src/array.h b/src/array.h
index 2d77c71..1d4e1c2 100644
--- a/src/array.h
+++ b/src/array.h
@@ -30,37 +30,45 @@
 #define git_array_init(a) \
 	do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
 
+#define git_array_init_to_size(a, desired) \
+	do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0)
+
 #define git_array_clear(a) \
 	do { git__free((a).ptr); git_array_init(a); } while (0)
 
 #define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr)
 
 
-typedef git_array_t(void) git_array_generic_t;
+typedef git_array_t(char) git_array_generic_t;
 
 /* use a generic array for growth so this can return the new item */
-GIT_INLINE(void *) git_array_grow(git_array_generic_t *a, size_t item_size)
+GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
 {
+	git_array_generic_t *a = _a;
 	uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
-	void *new_array = git__realloc(a->ptr, new_size * item_size);
+	char *new_array = git__realloc(a->ptr, new_size * item_size);
 	if (!new_array) {
 		git_array_clear(*a);
 		return NULL;
 	} else {
 		a->ptr = new_array; a->asize = new_size; a->size++;
-		return (((char *)a->ptr) + (a->size - 1) * item_size);
+		return a->ptr + (a->size - 1) * item_size;
 	}
 }
 
 #define git_array_alloc(a) \
-	((a).size >= (a).asize) ? \
-	git_array_grow((git_array_generic_t *)&(a), sizeof(*(a).ptr)) :	\
-	(a).ptr ? &(a).ptr[(a).size++] : NULL
+	(((a).size >= (a).asize) ? \
+	git_array_grow(&(a), sizeof(*(a).ptr)) : \
+	((a).ptr ? &(a).ptr[(a).size++] : NULL))
 
 #define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
 
+#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
+
 #define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
 
 #define git_array_size(a) (a).size
 
+#define git_array_valid_index(a, i) ((i) < (a).size)
+
 #endif
diff --git a/src/attr.c b/src/attr.c
index 6cdff29..98a328a 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -1,3 +1,4 @@
+#include "common.h"
 #include "repository.h"
 #include "fileops.h"
 #include "config.h"
@@ -26,7 +27,6 @@
 	return GIT_ATTR_VALUE_T;
 }
 
-
 static int collect_attr_files(
 	git_repository *repo,
 	uint32_t flags,
@@ -103,8 +103,6 @@
 	attr_get_many_info *info = NULL;
 	size_t num_found = 0;
 
-	memset((void *)values, 0, sizeof(const char *) * num_attr);
-
 	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
 		return -1;
 
@@ -141,6 +139,11 @@
 		}
 	}
 
+	for (k = 0; k < num_attr; k++) {
+		if (!info[k].found)
+			values[k] = NULL;
+	}
+
 cleanup:
 	git_vector_free(&files);
 	git_attr_path__free(&path);
diff --git a/src/attr_file.c b/src/attr_file.c
index d880398..4eb7324 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -39,7 +39,7 @@
 		attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
 		GITERR_CHECK_ALLOC(attrs->key);
 
-		attrs->key[0] = '0' + from;
+		attrs->key[0] = '0' + (char)from;
 		attrs->key[1] = '#';
 		memcpy(&attrs->key[2], path, len);
 		attrs->key[len + 2] = '\0';
@@ -79,9 +79,13 @@
 
 	while (!error && *scan) {
 		/* allocate rule if needed */
-		if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
-			error = -1;
-			break;
+		if (!rule) {
+			if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+				error = -1;
+				break;
+			}
+			rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
+				GIT_ATTR_FNMATCH_ALLOWMACRO;
 		}
 
 		/* parse the next "pattern attr attr attr" line */
@@ -351,8 +355,8 @@
 	if (parse_optimized_patterns(spec, pool, *base))
 		return 0;
 
-	spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
-	allow_space = (spec->flags != 0);
+	spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
+	allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
 
 	pattern = *base;
 
@@ -362,7 +366,7 @@
 		return GIT_ENOTFOUND;
 	}
 
-	if (*pattern == '[') {
+	if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
 		if (strncmp(pattern, "[attr]", 6) == 0) {
 			spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
 			pattern += 6;
@@ -370,7 +374,7 @@
 		/* else a character range like [a-e]* which is accepted */
 	}
 
-	if (*pattern == '!') {
+	if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
 		spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
 		pattern++;
 	}
diff --git a/src/attr_file.h b/src/attr_file.h
index 15bba1c..3bc7c6c 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -28,6 +28,12 @@
 #define GIT_ATTR_FNMATCH_ALLOWSPACE	(1U << 6)
 #define GIT_ATTR_FNMATCH_ICASE		(1U << 7)
 #define GIT_ATTR_FNMATCH_MATCH_ALL	(1U << 8)
+#define GIT_ATTR_FNMATCH_ALLOWNEG   (1U << 9)
+#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
+
+#define GIT_ATTR_FNMATCH__INCOMING \
+	(GIT_ATTR_FNMATCH_ALLOWSPACE | \
+	 GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
 
 extern const char *git_attr__true;
 extern const char *git_attr__false;
diff --git a/src/bitvec.h b/src/bitvec.h
new file mode 100644
index 0000000..fd6f0cc
--- /dev/null
+++ b/src/bitvec.h
@@ -0,0 +1,75 @@
+/*
+ * 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_bitvec_h__
+#define INCLUDE_bitvec_h__
+
+#include "util.h"
+
+/*
+ * This is a silly little fixed length bit vector type that will store
+ * vectors of 64 bits or less directly in the structure and allocate
+ * memory for vectors longer than 64 bits.  You can use the two versions
+ * transparently through the API and avoid heap allocation completely when
+ * using a short bit vector as a result.
+ */
+typedef struct {
+	size_t length;
+	union {
+		uint64_t *words;
+		uint64_t bits;
+	} u;
+} git_bitvec;
+
+GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
+{
+	memset(bv, 0x0, sizeof(*bv));
+
+	if (capacity >= 64) {
+		bv->length = (capacity / 64) + 1;
+		bv->u.words = git__calloc(bv->length, sizeof(uint64_t));
+		if (!bv->u.words)
+			return -1;
+	}
+
+	return 0;
+}
+
+#define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64))
+#define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits)
+
+GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
+{
+	uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+	uint64_t mask = GIT_BITVEC_MASK(bit);
+
+	if (on)
+		*word |= mask;
+	else
+		*word &= ~mask;
+}
+
+GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
+{
+	uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+	return (*word & GIT_BITVEC_MASK(bit)) != 0;
+}
+
+GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
+{
+	if (!bv->length)
+		bv->u.bits = 0;
+	else
+		memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t));
+}
+
+GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
+{
+	if (bv->length)
+		git__free(bv->u.words);
+}
+
+#endif
diff --git a/src/blame.c b/src/blame.c
new file mode 100644
index 0000000..219a6bf
--- /dev/null
+++ b/src/blame.c
@@ -0,0 +1,476 @@
+/*
+ * 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 "blame.h"
+#include "git2/commit.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/tree.h"
+#include "git2/diff.h"
+#include "git2/blob.h"
+#include "git2/signature.h"
+#include "util.h"
+#include "repository.h"
+#include "blame_git.h"
+
+
+static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
+{
+	uint16_t lineno = (uint16_t)*(size_t*)key;
+	git_blame_hunk *hunk = (git_blame_hunk*)entry;
+
+	if (lineno < hunk->final_start_line_number)
+		return -1;
+	if (lineno >= hunk->final_start_line_number + hunk->lines_in_hunk)
+		return 1;
+	return 0;
+}
+
+static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); }
+static int hunk_cmp(const void *_a, const void *_b)
+{
+	git_blame_hunk *a = (git_blame_hunk*)_a,
+						*b = (git_blame_hunk*)_b;
+
+	return a->final_start_line_number - b->final_start_line_number;
+}
+
+static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
+{
+	return line >= (size_t)(hunk->final_start_line_number + hunk->lines_in_hunk - 1);
+}
+
+static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
+{
+	return line <= hunk->final_start_line_number;
+}
+
+static git_blame_hunk* new_hunk(
+		uint16_t start,
+		uint16_t lines,
+		uint16_t orig_start,
+		const char *path)
+{
+	git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
+	if (!hunk) return NULL;
+
+	hunk->lines_in_hunk = lines;
+	hunk->final_start_line_number = start;
+	hunk->orig_start_line_number = orig_start;
+	hunk->orig_path = path ? git__strdup(path) : NULL;
+
+	return hunk;
+}
+
+static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
+{
+	git_blame_hunk *newhunk = new_hunk(
+			hunk->final_start_line_number,
+			hunk->lines_in_hunk,
+			hunk->orig_start_line_number,
+			hunk->orig_path);
+	git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
+	git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
+	newhunk->boundary = hunk->boundary;
+	newhunk->final_signature = git_signature_dup(hunk->final_signature);
+	newhunk->orig_signature = git_signature_dup(hunk->orig_signature);
+	return newhunk;
+}
+
+static void free_hunk(git_blame_hunk *hunk)
+{
+	git__free((void*)hunk->orig_path);
+	git_signature_free(hunk->final_signature);
+	git_signature_free(hunk->orig_signature);
+	git__free(hunk);
+}
+
+/* Starting with the hunk that includes start_line, shift all following hunks'
+ * final_start_line by shift_by lines */
+static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
+{
+	size_t i;
+
+	if (!git_vector_bsearch2( &i, v, hunk_byfinalline_search_cmp, &start_line)) {
+		for (; i < v->length; i++) {
+			git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
+			hunk->final_start_line_number += shift_by;
+		}
+	}
+}
+
+git_blame* git_blame__alloc(
+	git_repository *repo,
+	git_blame_options opts,
+	const char *path)
+{
+	git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame));
+	if (!gbr) {
+		giterr_set_oom();
+		return NULL;
+	}
+	git_vector_init(&gbr->hunks, 8, hunk_cmp);
+	git_vector_init(&gbr->paths, 8, paths_cmp);
+	gbr->repository = repo;
+	gbr->options = opts;
+	gbr->path = git__strdup(path);
+	git_vector_insert(&gbr->paths, git__strdup(path));
+	return gbr;
+}
+
+void git_blame_free(git_blame *blame)
+{
+	size_t i;
+	git_blame_hunk *hunk;
+	char *path;
+
+	if (!blame) return;
+
+	git_vector_foreach(&blame->hunks, i, hunk)
+		free_hunk(hunk);
+	git_vector_free(&blame->hunks);
+
+	git_vector_foreach(&blame->paths, i, path)
+		git__free(path);
+	git_vector_free(&blame->paths);
+
+	git_array_clear(blame->line_index);
+
+	git__free((void*)blame->path);
+	git_blob_free(blame->final_blob);
+	git__free(blame);
+}
+
+uint32_t git_blame_get_hunk_count(git_blame *blame)
+{
+	assert(blame);
+	return (uint32_t)blame->hunks.length;
+}
+
+const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index)
+{
+	assert(blame);
+	return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
+}
+
+const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
+{
+	size_t i;
+	assert(blame);
+
+	if (!git_vector_bsearch2( &i, &blame->hunks, hunk_byfinalline_search_cmp, &lineno)) {
+		return git_blame_get_hunk_byindex(blame, (uint32_t)i);
+	}
+
+	return NULL;
+}
+
+static void normalize_options(
+		git_blame_options *out,
+		const git_blame_options *in,
+		git_repository *repo)
+{
+	git_blame_options dummy = GIT_BLAME_OPTIONS_INIT;
+	if (!in) in = &dummy;
+
+	memcpy(out, in, sizeof(git_blame_options));
+
+	/* No newest_commit => HEAD */
+	if (git_oid_iszero(&out->newest_commit)) {
+		git_reference_name_to_id(&out->newest_commit, repo, "HEAD");
+	}
+
+	/* min_line 0 really means 1 */
+	if (!out->min_line) out->min_line = 1;
+	/* max_line 0 really means N, but we don't know N yet */
+
+	/* Fix up option implications */
+	if (out->flags & GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
+		out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+	if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
+		out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+	if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
+		out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
+}
+
+static git_blame_hunk *split_hunk_in_vector(
+		git_vector *vec,
+		git_blame_hunk *hunk,
+		size_t rel_line,
+		bool return_new)
+{
+	size_t new_line_count;
+	git_blame_hunk *nh;
+
+	/* Don't split if already at a boundary */
+	if (rel_line <= 0 ||
+	    rel_line >= hunk->lines_in_hunk)
+	{
+		return hunk;
+	}
+
+	new_line_count = hunk->lines_in_hunk - rel_line;
+	nh = new_hunk((uint16_t)(hunk->final_start_line_number+rel_line), (uint16_t)new_line_count,
+			(uint16_t)(hunk->orig_start_line_number+rel_line), hunk->orig_path);
+	git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id);
+	git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
+
+	/* Adjust hunk that was split */
+	hunk->lines_in_hunk -= (uint16_t)new_line_count;
+	git_vector_insert_sorted(vec, nh, NULL);
+	{
+		git_blame_hunk *ret = return_new ? nh : hunk;
+		return ret;
+	}
+}
+
+/*
+ * Construct a list of char indices for where lines begin
+ * Adapted from core git:
+ * https://github.com/gitster/git/blob/be5c9fb9049ed470e7005f159bb923a5f4de1309/builtin/blame.c#L1760-L1789
+ */
+static int index_blob_lines(git_blame *blame)
+{
+    const char *buf = blame->final_buf;
+    git_off_t len = blame->final_buf_size;
+    int num = 0, incomplete = 0, bol = 1;
+    size_t *i;
+ 
+    if (len && buf[len-1] != '\n')
+        incomplete++; /* incomplete line at the end */
+    while (len--) {
+        if (bol) {
+            i = git_array_alloc(blame->line_index);
+            GITERR_CHECK_ALLOC(i);
+            *i = buf - blame->final_buf;
+            bol = 0;
+        }
+        if (*buf++ == '\n') {
+            num++;
+            bol = 1;
+        }
+    }
+    i = git_array_alloc(blame->line_index);
+    GITERR_CHECK_ALLOC(i);
+    *i = buf - blame->final_buf;
+    blame->num_lines = num + incomplete;
+    return blame->num_lines;
+}
+ 
+static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
+{
+	git_blame_hunk *h = new_hunk(
+			e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
+	git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
+	h->final_signature = git_signature_dup(git_commit_author(e->suspect->commit));
+	h->boundary = e->is_boundary ? 1 : 0;
+	return h;
+}
+
+static int load_blob(git_blame *blame)
+{
+	int error;
+
+	if (blame->final_blob) return 0;
+
+	error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit);
+	if (error < 0)
+		goto cleanup;
+	error = git_object_lookup_bypath((git_object**)&blame->final_blob,
+			(git_object*)blame->final, blame->path, GIT_OBJ_BLOB);
+	if (error < 0)
+		goto cleanup;
+
+cleanup:
+	return error;
+}
+
+static int blame_internal(git_blame *blame)
+{
+	int error;
+	git_blame__entry *ent = NULL;
+	git_blame__origin *o;
+
+	if ((error = load_blob(blame)) < 0 ||
+	    (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0)
+		goto cleanup;
+	blame->final_buf = git_blob_rawcontent(blame->final_blob);
+	blame->final_buf_size = git_blob_rawsize(blame->final_blob);
+
+	ent = git__calloc(1, sizeof(git_blame__entry));
+	ent->num_lines = index_blob_lines(blame);
+	ent->lno = blame->options.min_line - 1;
+	ent->num_lines = ent->num_lines - blame->options.min_line + 1;
+	if (blame->options.max_line > 0)
+		ent->num_lines = blame->options.max_line - blame->options.min_line + 1;
+	ent->s_lno = ent->lno;
+	ent->suspect = o;
+
+	blame->ent = ent;
+	blame->path = blame->path;
+
+	git_blame__like_git(blame, blame->options.flags);
+
+cleanup:
+	for (ent = blame->ent; ent; ) {
+		git_blame__entry *e = ent->next;
+
+		git_vector_insert(&blame->hunks, hunk_from_entry(ent));
+
+		git_blame__free_entry(ent);
+		ent = e;
+	}
+
+	return error;
+}
+
+/*******************************************************************************
+ * File blaming
+ ******************************************************************************/
+
+int git_blame_file(
+		git_blame **out,
+		git_repository *repo,
+		const char *path,
+		git_blame_options *options)
+{
+	int error = -1;
+	git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT;
+	git_blame *blame = NULL;
+
+	assert(out && repo && path);
+	normalize_options(&normOptions, options, repo);
+
+	blame = git_blame__alloc(repo, normOptions, path);
+	GITERR_CHECK_ALLOC(blame);
+
+	if ((error = load_blob(blame)) < 0)
+		goto on_error;
+
+	if ((error = blame_internal(blame)) < 0)
+		goto on_error;
+
+	*out = blame;
+	return 0;
+
+on_error:
+	git_blame_free(blame);
+	return error;
+}
+
+/*******************************************************************************
+ * Buffer blaming
+ *******************************************************************************/
+
+static bool hunk_is_bufferblame(git_blame_hunk *hunk)
+{
+	return git_oid_iszero(&hunk->final_commit_id);
+}
+
+static int buffer_hunk_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	void *payload)
+{
+	git_blame *blame = (git_blame*)payload;
+	uint32_t wedge_line;
+
+	GIT_UNUSED(delta);
+
+	wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
+	blame->current_diff_line = wedge_line;
+
+	blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
+	if (!blame->current_hunk) {
+		/* Line added at the end of the file */
+		blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path);
+		git_vector_insert(&blame->hunks, blame->current_hunk);
+	} else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
+		/* If this hunk doesn't start between existing hunks, split a hunk up so it does */
+		blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
+				wedge_line - blame->current_hunk->orig_start_line_number, true);
+	}
+
+	return 0;
+}
+
+static int ptrs_equal_cmp(const void *a, const void *b) { return a<b ? -1 : a>b ? 1 : 0; }
+static int buffer_line_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
+	void *payload)
+{
+	git_blame *blame = (git_blame*)payload;
+
+	GIT_UNUSED(delta);
+	GIT_UNUSED(hunk);
+	GIT_UNUSED(line);
+
+	if (line->origin == GIT_DIFF_LINE_ADDITION) {
+		if (hunk_is_bufferblame(blame->current_hunk) &&
+		    hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
+			/* Append to the current buffer-blame hunk */
+			blame->current_hunk->lines_in_hunk++;
+			shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1);
+		} else {
+			/* Create a new buffer-blame hunk with this line */
+			shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
+			blame->current_hunk = new_hunk((uint16_t)blame->current_diff_line, 1, 0, blame->path);
+			git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
+		}
+		blame->current_diff_line++;
+	}
+
+	if (line->origin == GIT_DIFF_LINE_DELETION) {
+		/* Trim the line from the current hunk; remove it if it's now empty */
+		size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1;
+
+		if (--(blame->current_hunk->lines_in_hunk) == 0) {
+			size_t i;
+			shift_base--;
+			if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
+				git_vector_remove(&blame->hunks, i);
+				free_hunk(blame->current_hunk);
+				blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i);
+			}
+		}
+		shift_hunks_by(&blame->hunks, shift_base, -1);
+	}
+	return 0;
+}
+
+int git_blame_buffer(
+		git_blame **out,
+		git_blame *reference,
+		const char *buffer,
+		uint32_t buffer_len)
+{
+	git_blame *blame;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	size_t i;
+	git_blame_hunk *hunk;
+
+	diffopts.context_lines = 0;
+
+	assert(out && reference && buffer && buffer_len);
+
+	blame = git_blame__alloc(reference->repository, reference->options, reference->path);
+
+	/* Duplicate all of the hunk structures in the reference blame */
+	git_vector_foreach(&reference->hunks, i, hunk) {
+		git_vector_insert(&blame->hunks, dup_hunk(hunk));
+	}
+
+	/* Diff to the reference blob */
+	git_diff_blob_to_buffer(reference->final_blob, blame->path,
+			buffer, buffer_len, blame->path,
+			&diffopts, NULL, buffer_hunk_cb, buffer_line_cb, blame);
+
+	*out = blame;
+	return 0;
+}
diff --git a/src/blame.h b/src/blame.h
new file mode 100644
index 0000000..637e439
--- /dev/null
+++ b/src/blame.h
@@ -0,0 +1,93 @@
+#ifndef INCLUDE_blame_h__
+#define INCLUDE_blame_h__
+
+#include "git2/blame.h"
+#include "common.h"
+#include "vector.h"
+#include "diff.h"
+#include "array.h"
+#include "git2/oid.h"
+
+/*
+ * One blob in a commit that is being suspected
+ */
+typedef struct git_blame__origin {
+	int refcnt;
+	struct git_blame__origin *previous;
+	git_commit *commit;
+	git_blob *blob;
+	char path[GIT_FLEX_ARRAY];
+} git_blame__origin;
+
+/*
+ * Each group of lines is described by a git_blame__entry; it can be split
+ * as we pass blame to the parents.  They form a linked list in the
+ * scoreboard structure, sorted by the target line number.
+ */
+typedef struct git_blame__entry {
+	struct git_blame__entry *prev;
+	struct git_blame__entry *next;
+
+	/* the first line of this group in the final image;
+	 * internally all line numbers are 0 based.
+	 */
+	int lno;
+
+	/* how many lines this group has */
+	int num_lines;
+
+	/* the commit that introduced this group into the final image */
+	git_blame__origin *suspect;
+
+	/* true if the suspect is truly guilty; false while we have not
+	 * checked if the group came from one of its parents.
+	 */
+	bool guilty;
+
+	/* true if the entry has been scanned for copies in the current parent
+	 */
+	bool scanned;
+
+	/* the line number of the first line of this group in the
+	 * suspect's file; internally all line numbers are 0 based.
+	 */
+	int s_lno;
+
+	/* how significant this entry is -- cached to avoid
+	 * scanning the lines over and over.
+	 */
+	unsigned score;
+
+	/* Whether this entry has been tracked to a boundary commit.
+	 */
+	bool is_boundary;
+} git_blame__entry;
+
+struct git_blame {
+	const char *path;
+	git_repository *repository;
+	git_blame_options options;
+
+	git_vector hunks;
+	git_vector paths;
+
+	git_blob *final_blob;
+	git_array_t(size_t) line_index;
+
+	size_t current_diff_line;
+	git_blame_hunk *current_hunk;
+
+	/* Scoreboard fields */
+	git_commit *final;
+	git_blame__entry *ent;
+	int num_lines;
+	const char *final_buf;
+	git_off_t final_buf_size;
+};
+
+git_blame *git_blame__alloc(
+	git_repository *repo,
+	git_blame_options opts,
+	const char *path);
+
+#endif
diff --git a/src/blame_git.c b/src/blame_git.c
new file mode 100644
index 0000000..800f1f0
--- /dev/null
+++ b/src/blame_git.c
@@ -0,0 +1,622 @@
+/*
+ * 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 "blame_git.h"
+#include "commit.h"
+#include "blob.h"
+#include "xdiff/xinclude.h"
+
+/*
+ * Origin is refcounted and usually we keep the blob contents to be
+ * reused.
+ */
+static git_blame__origin *origin_incref(git_blame__origin *o)
+{
+	if (o)
+		o->refcnt++;
+	return o;
+}
+
+static void origin_decref(git_blame__origin *o)
+{
+	if (o && --o->refcnt <= 0) {
+		if (o->previous)
+			origin_decref(o->previous);
+		git_blob_free(o->blob);
+		git_commit_free(o->commit);
+		git__free(o);
+	}
+}
+
+/* Given a commit and a path in it, create a new origin structure. */
+static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
+{
+	int error = 0;
+	git_blame__origin *o;
+
+	o = git__calloc(1, sizeof(*o) + strlen(path) + 1);
+	GITERR_CHECK_ALLOC(o);
+	o->commit = commit;
+	o->refcnt = 1;
+	strcpy(o->path, path);
+
+	if (!(error = git_object_lookup_bypath((git_object**)&o->blob, (git_object*)commit,
+			path, GIT_OBJ_BLOB))) {
+		*out = o;
+	} else {
+		origin_decref(o);
+	}
+	return error;
+}
+
+/* Locate an existing origin or create a new one. */
+int git_blame__get_origin(
+		git_blame__origin **out,
+		git_blame *blame,
+		git_commit *commit,
+		const char *path)
+{
+	git_blame__entry *e;
+
+	for (e = blame->ent; e; e = e->next) {
+		if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) {
+			*out = origin_incref(e->suspect);
+		}
+	}
+	return make_origin(out, commit, path);
+}
+
+typedef struct blame_chunk_cb_data {
+	git_blame *blame;
+	git_blame__origin *target;
+	git_blame__origin *parent;
+	long tlno;
+	long plno;
+}blame_chunk_cb_data;
+
+static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
+{
+	if (a == b)
+		return true;
+	if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit)))
+		return false;
+	return 0 == strcmp(a->path, b->path);
+}
+
+/* find the line number of the last line the target is suspected for */
+static int find_last_in_target(git_blame *blame, git_blame__origin *target)
+{
+	git_blame__entry *e;
+	int last_in_target = -1;
+
+	for (e=blame->ent; e; e=e->next) {
+		if (e->guilty || !same_suspect(e->suspect, target))
+			continue;
+		if (last_in_target < e->s_lno + e->num_lines)
+			last_in_target = e->s_lno + e->num_lines;
+	}
+	return last_in_target;
+}
+
+/*
+ * It is known that lines between tlno to same came from parent, and e
+ * has an overlap with that range.  it also is known that parent's
+ * line plno corresponds to e's line tlno.
+ *
+ *                <---- e ----->
+ *                   <------>         (entirely within)
+ *                   <------------>   (extends past)
+ *             <------------>         (starts before)
+ *             <------------------>   (entirely encloses)
+ *
+ * Split e into potentially three parts; before this chunk, the chunk
+ * to be blamed for the parent, and after that portion.
+ */
+static void split_overlap(git_blame__entry *split, git_blame__entry *e,
+		int tlno, int plno, int same, git_blame__origin *parent)
+{
+	int chunk_end_lno;
+
+	if (e->s_lno < tlno) {
+		/* there is a pre-chunk part not blamed on the parent */
+		split[0].suspect = origin_incref(e->suspect);
+		split[0].lno = e->lno;
+		split[0].s_lno = e->s_lno;
+		split[0].num_lines = tlno - e->s_lno;
+		split[1].lno = e->lno + tlno - e->s_lno;
+		split[1].s_lno = plno;
+	} else {
+		split[1].lno = e->lno;
+		split[1].s_lno = plno + (e->s_lno - tlno);
+	}
+
+	if (same < e->s_lno + e->num_lines) {
+		/* there is a post-chunk part not blamed on parent */
+		split[2].suspect = origin_incref(e->suspect);
+		split[2].lno = e->lno + (same - e->s_lno);
+		split[2].s_lno = e->s_lno + (same - e->s_lno);
+		split[2].num_lines = e->s_lno + e->num_lines - same;
+		chunk_end_lno = split[2].lno;
+	} else {
+		chunk_end_lno = e->lno + e->num_lines;
+	}
+	split[1].num_lines = chunk_end_lno - split[1].lno;
+
+	/*
+	 * if it turns out there is nothing to blame the parent for, forget about
+	 * the splitting. !split[1].suspect signals this.
+	 */
+	if (split[1].num_lines < 1)
+		return;
+	split[1].suspect = origin_incref(parent);
+}
+
+/*
+ * Link in a new blame entry to the scoreboard. Entries that cover the same
+ * line range have been removed from the scoreboard previously.
+ */
+static void add_blame_entry(git_blame *blame, git_blame__entry *e)
+{
+	git_blame__entry *ent, *prev = NULL;
+
+	origin_incref(e->suspect);
+
+	for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next)
+		prev = ent;
+
+	/* prev, if not NULL, is the last one that is below e */
+	e->prev = prev;
+	if (prev) {
+		e->next = prev->next;
+		prev->next = e;
+	} else {
+		e->next = blame->ent;
+		blame->ent = e;
+	}
+	if (e->next)
+		e->next->prev = e;
+}
+
+/*
+ * src typically is on-stack; we want to copy the information in it to
+ * a malloced blame_entry that is already on the linked list of the scoreboard.
+ * The origin of dst loses a refcnt while the origin of src gains one.
+ */
+static void dup_entry(git_blame__entry *dst, git_blame__entry *src)
+{
+	git_blame__entry *p, *n;
+
+	p = dst->prev;
+	n = dst->next;
+	origin_incref(src->suspect);
+	origin_decref(dst->suspect);
+	memcpy(dst, src, sizeof(*src));
+	dst->prev = p;
+	dst->next = n;
+	dst->score = 0;
+}
+
+/*
+ * split_overlap() divided an existing blame e into up to three parts in split.
+ * Adjust the linked list of blames in the scoreboard to reflect the split.
+ */
+static void split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
+{
+	git_blame__entry *new_entry;
+
+	if (split[0].suspect && split[2].suspect) {
+		/* The first part (reuse storage for the existing entry e */
+		dup_entry(e, &split[0]);
+
+		/* The last part -- me */
+		new_entry = git__malloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+		add_blame_entry(blame, new_entry);
+
+		/* ... and the middle part -- parent */
+		new_entry = git__malloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+		add_blame_entry(blame, new_entry);
+	} else if (!split[0].suspect && !split[2].suspect) {
+		/*
+		 * The parent covers the entire area; reuse storage for e and replace it
+		 * with the parent
+		 */
+		dup_entry(e, &split[1]);
+	} else if (split[0].suspect) {
+		/* me and then parent */
+		dup_entry(e, &split[0]);
+		new_entry = git__malloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+		add_blame_entry(blame, new_entry);
+	} else {
+		/* parent and then me */
+		dup_entry(e, &split[1]);
+		new_entry = git__malloc(sizeof(*new_entry));
+		memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+		add_blame_entry(blame, new_entry);
+	}
+}
+
+/* 
+ * After splitting the blame, the origins used by the on-stack blame_entry
+ * should lose one refcnt each.
+ */
+static void decref_split(git_blame__entry *split)
+{
+	int i;
+	for (i=0; i<3; i++)
+		origin_decref(split[i].suspect);
+}
+
+/*
+ * Helper for blame_chunk(). blame_entry e is known to overlap with the patch
+ * hunk; split it and pass blame to the parent.
+ */
+static void blame_overlap(
+		git_blame *blame,
+		git_blame__entry *e,
+		int tlno,
+		int plno,
+		int same,
+		git_blame__origin *parent)
+{
+	git_blame__entry split[3] = {{0}};
+
+	split_overlap(split, e, tlno, plno, same, parent);
+	if (split[1].suspect)
+		split_blame(blame, split, e);
+	decref_split(split);
+}
+
+/*
+ * Process one hunk from the patch between the current suspect for blame_entry
+ * e and its parent. Find and split the overlap, and pass blame to the
+ * overlapping part to the parent.
+ */
+static void blame_chunk(
+		git_blame *blame,
+		int tlno,
+		int plno,
+		int same,
+		git_blame__origin *target,
+		git_blame__origin *parent)
+{
+	git_blame__entry *e;
+
+	for (e = blame->ent; e; e = e->next) {
+		if (e->guilty || !same_suspect(e->suspect, target))
+			continue;
+		if (same <= e->s_lno)
+			continue;
+		if (tlno < e->s_lno + e->num_lines) {
+			blame_overlap(blame, e, tlno, plno, same, parent);
+		}
+	}
+}
+
+static int my_emit(
+		xdfenv_t *xe,
+		xdchange_t *xscr,
+		xdemitcb_t *ecb,
+		xdemitconf_t const *xecfg)
+{
+	xdchange_t *xch = xscr;
+	GIT_UNUSED(xe);
+	GIT_UNUSED(xecfg);
+	while (xch) {
+		blame_chunk_cb_data *d = ecb->priv;
+		blame_chunk(d->blame, d->tlno, d->plno, xch->i2, d->target, d->parent);
+		d->plno = xch->i1 + xch->chg1;
+		d->tlno = xch->i2 + xch->chg2;
+		xch = xch->next;
+	}
+	return 0;
+}
+
+static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx)
+{
+	const int blk = 1024;
+	long trimmed = 0, recovered = 0;
+	char *ap = a->ptr + a->size;
+	char *bp = b->ptr + b->size;
+	long smaller = (long)((a->size < b->size) ? a->size : b->size);
+
+	if (ctx)
+		return;
+
+	while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {
+		trimmed += blk;
+		ap -= blk;
+		bp -= blk;
+	}
+
+	while (recovered < trimmed)
+		if (ap[recovered++] == '\n')
+			break;
+	a->size -= trimmed - recovered;
+	b->size -= trimmed - recovered;
+}
+
+static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
+{
+	xpparam_t xpp = {0};
+	xdemitconf_t xecfg = {0};
+	xdemitcb_t ecb = {0};
+
+	xecfg.emit_func = (void(*)(void))my_emit;
+	ecb.priv = cb_data;
+
+	trim_common_tail(&file_a, &file_b, 0);
+	return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
+}
+
+static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
+{
+	memset(file, 0, sizeof(*file));
+	if (o->blob) {
+		file->ptr = (char*)git_blob_rawcontent(o->blob);
+		file->size = (size_t)git_blob_rawsize(o->blob);
+	}
+}
+
+static int pass_blame_to_parent(
+		git_blame *blame,
+		git_blame__origin *target,
+		git_blame__origin *parent)
+{
+	int last_in_target;
+	mmfile_t file_p, file_o;
+	blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
+
+	last_in_target = find_last_in_target(blame, target);
+	if (last_in_target < 0)
+		return 1; /* nothing remains for this target */
+
+	fill_origin_blob(parent, &file_p);
+	fill_origin_blob(target, &file_o);
+
+	diff_hunks(file_p, file_o, &d);
+	/* The reset (i.e. anything after tlno) are the same as the parent */
+	blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
+
+	return 0;
+}
+
+static int paths_on_dup(void **old, void *new)
+{
+	GIT_UNUSED(old);
+	git__free(new);
+	return -1;
+}
+
+static git_blame__origin* find_origin(
+		git_blame *blame,
+		git_commit *parent,
+		git_blame__origin *origin)
+{
+	git_blame__origin *porigin = NULL;
+	git_diff *difflist = NULL;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_tree *otree=NULL, *ptree=NULL;
+
+	/* Get the trees from this commit and its parent */
+	if (0 != git_commit_tree(&otree, origin->commit) ||
+	    0 != git_commit_tree(&ptree, parent))
+		goto cleanup;
+
+	/* Configure the diff */
+	diffopts.context_lines = 0;
+	diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK;
+
+	/* Check to see if files we're interested have changed */
+	diffopts.pathspec.count = blame->paths.length;
+	diffopts.pathspec.strings = (char**)blame->paths.contents;
+	if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+			goto cleanup;
+
+	if (!git_diff_num_deltas(difflist)) {
+		/* No changes; copy data */
+		git_blame__get_origin(&porigin, blame, parent, origin->path);
+	} else {
+		git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+		int i;
+
+		/* Generate a full diff between the two trees */
+		git_diff_free(difflist);
+		diffopts.pathspec.count = 0;
+		if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+			goto cleanup;
+
+		/* Let diff find renames */
+		findopts.flags = GIT_DIFF_FIND_RENAMES;
+		if (0 != git_diff_find_similar(difflist, &findopts))
+			goto cleanup;
+
+		/* Find one that matches */
+		for (i=0; i<(int)git_diff_num_deltas(difflist); i++) {
+			const git_diff_delta *delta = git_diff_get_delta(difflist, i);
+
+			if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path))
+			{
+				git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path),
+						paths_on_dup);
+				make_origin(&porigin, parent, delta->old_file.path);
+			}
+		}
+	}
+
+cleanup:
+	git_diff_free(difflist);
+	git_tree_free(otree);
+	git_tree_free(ptree);
+	return porigin;
+}
+
+/*
+ * 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,
+		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);
+	for (e=blame->ent; e; e=e->next) {
+		if (!same_suspect(e->suspect, origin))
+			continue;
+		origin_incref(porigin);
+		origin_decref(e->suspect);
+		e->suspect = porigin;
+	}
+}
+
+static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
+{
+	git_commit *commit = origin->commit;
+	int i, num_parents;
+	git_blame__origin *sg_buf[16];
+	git_blame__origin *porigin, **sg_origin = sg_buf;
+
+	GIT_UNUSED(opt);
+
+	num_parents = git_commit_parentcount(commit);
+	if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
+		/* Stop at oldest specified commit */
+		num_parents = 0;
+	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))
+		memset(sg_buf, 0, sizeof(sg_buf));
+	else
+		sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
+
+	for (i=0; i<num_parents; i++) {
+		git_commit *p;
+		int j, same;
+
+		if (sg_origin[i])
+			continue;
+
+		git_commit_parent(&p, origin->commit, i);
+		porigin = find_origin(blame, p, origin);
+
+		if (!porigin)
+			continue;
+		if (porigin->blob && origin->blob &&
+		    !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
+			pass_whole_blame(blame, origin, porigin);
+			origin_decref(porigin);
+			goto finish;
+		}
+		for (j = same = 0; j<i; j++)
+			if (sg_origin[j] &&
+				 !git_oid_cmp(git_blob_id(sg_origin[j]->blob), git_blob_id(porigin->blob))) {
+				same = 1;
+				break;
+			}
+		if (!same)
+			sg_origin[i] = porigin;
+		else
+			origin_decref(porigin);
+	}
+
+	/* Standard blame */
+	for (i=0; i<num_parents; i++) {
+		git_blame__origin *porigin = sg_origin[i];
+		if (!porigin)
+			continue;
+		if (!origin->previous) {
+			origin_incref(porigin);
+			origin->previous = porigin;
+		}
+		if (pass_blame_to_parent(blame, origin, porigin))
+			goto finish;
+	}
+
+	/* TODO: optionally find moves in parents' files */
+
+	/* TODO: optionally find copies in parents' files */
+
+finish:
+	for (i=0; i<num_parents; i++)
+		if (sg_origin[i])
+			origin_decref(sg_origin[i]);
+	if (sg_origin != sg_buf)
+		git__free(sg_origin);
+	return;
+}
+
+/*
+ * If two blame entries that are next to each other came from
+ * contiguous lines in the same origin (i.e. <commit, path> pair),
+ * merge them together.
+ */
+static void coalesce(git_blame *blame)
+{
+	git_blame__entry *ent, *next;
+
+	for (ent=blame->ent; ent && (next = ent->next); ent = next) {
+		if (same_suspect(ent->suspect, next->suspect) &&
+		    ent->guilty == next->guilty &&
+		    ent->s_lno + ent->num_lines == next->s_lno)
+		{
+			ent->num_lines += next->num_lines;
+			ent->next = next->next;
+			if (ent->next)
+				ent->next->prev = ent;
+			origin_decref(next->suspect);
+			git__free(next);
+			ent->score = 0;
+			next = ent; /* again */
+		}
+	}
+}
+
+void git_blame__like_git(git_blame *blame, uint32_t opt)
+{
+	while (true) {
+		git_blame__entry *ent;
+		git_blame__origin *suspect = NULL;
+
+		/* Find a suspect to break down */
+		for (ent = blame->ent; !suspect && ent; ent = ent->next)
+			if (!ent->guilty)
+				suspect = ent->suspect;
+		if (!suspect)
+			return; /* all done */
+
+		/* We'll use this suspect later in the loop, so hold on to it for now. */
+		origin_incref(suspect);
+		pass_blame(blame, suspect, opt);
+
+		/* Take responsibility for the remaining entries */
+		for (ent = blame->ent; ent; ent = ent->next) {
+			if (same_suspect(ent->suspect, suspect)) {
+				ent->guilty = true;
+				ent->is_boundary = !git_oid_cmp(
+						git_commit_id(suspect->commit),
+						&blame->options.oldest_commit);
+			}
+		}
+		origin_decref(suspect);
+	}
+
+	coalesce(blame);
+}
+
+void git_blame__free_entry(git_blame__entry *ent)
+{
+	if (!ent) return;
+	origin_decref(ent->suspect);
+	git__free(ent);
+}
diff --git a/src/blame_git.h b/src/blame_git.h
new file mode 100644
index 0000000..3ec2710
--- /dev/null
+++ b/src/blame_git.h
@@ -0,0 +1,20 @@
+/*
+ * 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_blame_git__
+#define INCLUDE_blame_git__
+
+#include "blame.h"
+
+int git_blame__get_origin(
+		git_blame__origin **out,
+		git_blame *sb,
+		git_commit *commit,
+		const char *path);
+void git_blame__free_entry(git_blame__entry *ent);
+void git_blame__like_git(git_blame *sb, uint32_t flags);
+
+#endif
diff --git a/src/blob.c b/src/blob.c
index 2e4d5f4..2c6d528 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -60,10 +60,10 @@
 		(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
 		return error;
 
-	if ((error = stream->write(stream, buffer, len)) == 0)
-		error = stream->finalize_write(oid, stream);
+	if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
+		error = git_odb_stream_finalize_write(oid, stream);
 
-	stream->free(stream);
+	git_odb_stream_free(stream);
 	return error;
 }
 
@@ -80,12 +80,12 @@
 		return error;
 
 	if ((fd = git_futils_open_ro(path)) < 0) {
-		stream->free(stream);
+		git_odb_stream_free(stream);
 		return -1;
 	}
 
 	while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
-		error = stream->write(stream, buffer, read_len);
+		error = git_odb_stream_write(stream, buffer, read_len);
 		written += read_len;
 	}
 
@@ -97,36 +97,32 @@
 	}
 
 	if (!error)
-		error = stream->finalize_write(oid, stream);
+		error = git_odb_stream_finalize_write(oid, stream);
 
-	stream->free(stream);
+	git_odb_stream_free(stream);
 	return error;
 }
 
 static int write_file_filtered(
 	git_oid *oid,
+	git_off_t *size,
 	git_odb *odb,
 	const char *full_path,
-	git_vector *filters)
+	git_filter_list *fl)
 {
 	int error;
-	git_buf source = GIT_BUF_INIT;
-	git_buf dest = GIT_BUF_INIT;
+	git_buf tgt = GIT_BUF_INIT;
 
-	if ((error = git_futils_readbuffer(&source, full_path)) < 0)
-		return error;
-
-	error = git_filters_apply(&dest, &source, filters);
-
-	/* Free the source as soon as possible. This can be big in memory,
-	 * and we don't want to ODB write to choke */
-	git_buf_free(&source);
+	error = git_filter_list_apply_to_file(&tgt, fl, NULL, full_path);
 
 	/* Write the file to disk if it was properly filtered */
-	if (!error)
-		error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+	if (!error) {
+		*size = tgt.size;
 
-	git_buf_free(&dest);
+		error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
+	}
+
+	git_buf_free(&tgt);
 	return error;
 }
 
@@ -152,45 +148,67 @@
 	return error;
 }
 
-static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
+int git_blob__create_from_paths(
+	git_oid *oid,
+	struct stat *out_st,
+	git_repository *repo,
+	const char *content_path,
+	const char *hint_path,
+	mode_t hint_mode,
+	bool try_load_filters)
 {
 	int error;
 	struct stat st;
 	git_odb *odb = NULL;
 	git_off_t size;
+	mode_t mode;
+	git_buf path = GIT_BUF_INIT;
 
 	assert(hint_path || !try_load_filters);
 
-	if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
-		return error;
+	if (!content_path) {
+		if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
+			return GIT_EBAREREPO;
+
+		if (git_buf_joinpath(
+				&path, git_repository_workdir(repo), hint_path) < 0)
+			return -1;
+
+		content_path = path.ptr;
+	}
+
+	if ((error = git_path_lstat(content_path, &st)) < 0 ||
+		(error = git_repository_odb(&odb, repo)) < 0)
+		goto done;
+
+	if (out_st)
+		memcpy(out_st, &st, sizeof(st));
 
 	size = st.st_size;
+	mode = hint_mode ? hint_mode : st.st_mode;
 
-	if (S_ISLNK(st.st_mode)) {
+	if (S_ISLNK(mode)) {
 		error = write_symlink(oid, odb, content_path, (size_t)size);
 	} else {
-		git_vector write_filters = GIT_VECTOR_INIT;
-		int filter_count = 0;
+		git_filter_list *fl = NULL;
 
-		if (try_load_filters) {
+		if (try_load_filters)
 			/* Load the filters for writing this file to the ODB */
-			filter_count = git_filters_load(
-				&write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
-		}
+			error = git_filter_list_load(
+				&fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
 
-		if (filter_count < 0) {
-			/* Negative value means there was a critical error */
-			error = filter_count;
-		} else if (filter_count == 0) {
+		if (error < 0)
+			/* well, that didn't work */;
+		else if (fl == NULL)
 			/* No filters need to be applied to the document: we can stream
 			 * directly from disk */
 			error = write_file_stream(oid, odb, content_path, size);
-		} else {
+		else {
 			/* We need to apply one or more filters */
-			error = write_file_filtered(oid, odb, content_path, &write_filters);
-		}
+			error = write_file_filtered(oid, &size, odb, content_path, fl);
 
-		git_filters_free(&write_filters);
+			git_filter_list_free(fl);
+		}
 
 		/*
 		 * TODO: eventually support streaming filtered files, for files
@@ -207,34 +225,21 @@
 		 */
 	}
 
+done:
+	git_odb_free(odb);
+	git_buf_free(&path);
+
 	return error;
 }
 
-int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromworkdir(
+	git_oid *oid, git_repository *repo, const char *path)
 {
-	git_buf full_path = GIT_BUF_INIT;
-	const char *workdir;
-	int error;
-
-	if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
-		return error;
-
-	workdir = git_repository_workdir(repo);
-
-	if (git_buf_joinpath(&full_path, workdir, path) < 0) {
-		git_buf_free(&full_path);
-		return -1;
-	}
-
-	error = blob_create_internal(
-		oid, repo, git_buf_cstr(&full_path),
-		git_buf_cstr(&full_path) + strlen(workdir), true);
-
-	git_buf_free(&full_path);
-	return error;
+	return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true);
 }
 
-int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromdisk(
+	git_oid *oid, git_repository *repo, const char *path)
 {
 	int error;
 	git_buf full_path = GIT_BUF_INIT;
@@ -251,8 +256,8 @@
 	if (workdir && !git__prefixcmp(hintpath, workdir))
 		hintpath += strlen(workdir);
 
-	error = blob_create_internal(
-		oid, repo, git_buf_cstr(&full_path), hintpath, true);
+	error = git_blob__create_from_paths(
+		oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
 
 	git_buf_free(&full_path);
 	return error;
@@ -272,17 +277,14 @@
 	git_filebuf file = GIT_FILEBUF_INIT;
 	git_buf path = GIT_BUF_INIT;
 
-	if (git_buf_join_n(
-		&path, '/', 3, 
-		git_repository_path(repo),
-		GIT_OBJECTS_DIR, 
-		"streamed") < 0)
-			goto cleanup;
+	if (git_buf_joinpath(
+			&path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0)
+		goto cleanup;
 
 	content = git__malloc(BUFFER_SIZE);
 	GITERR_CHECK_ALLOC(content);
 
-	if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY) < 0)
+	if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0)
 		goto cleanup;
 
 	while (1) {
@@ -303,7 +305,8 @@
 	if (git_filebuf_flush(&file) < 0)
 		goto cleanup;
 
-	error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
+	error = git_blob__create_from_paths(
+		oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
 
 cleanup:
 	git_buf_free(&path);
@@ -318,8 +321,34 @@
 
 	assert(blob);
 
-	content.ptr = blob->odb_object->buffer;
-	content.size = min(blob->odb_object->cached.size, 4000);
+	content.ptr   = blob->odb_object->buffer;
+	content.size  = min(blob->odb_object->cached.size, 4000);
+	content.asize = 0;
 
 	return git_buf_text_is_binary(&content);
 }
+
+int git_blob_filtered_content(
+	git_buf *out,
+	git_blob *blob,
+	const char *path,
+	int check_for_binary_data)
+{
+	int error = 0;
+	git_filter_list *fl = NULL;
+
+	assert(blob && path && out);
+
+	if (check_for_binary_data && git_blob_is_binary(blob))
+		return 0;
+
+	if (!(error = git_filter_list_load(
+			&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
+
+		error = git_filter_list_apply_to_blob(out, fl, blob);
+
+		git_filter_list_free(fl);
+	}
+
+	return error;
+}
diff --git a/src/blob.h b/src/blob.h
index 22e37cc..4cd9f1e 100644
--- a/src/blob.h
+++ b/src/blob.h
@@ -21,4 +21,13 @@
 int git_blob__parse(void *blob, git_odb_object *obj);
 int git_blob__getbuf(git_buf *buffer, git_blob *blob);
 
+extern int git_blob__create_from_paths(
+	git_oid *out_oid,
+	struct stat *out_st,
+	git_repository *repo,
+	const char *full_path,
+	const char *hint_path,
+	mode_t hint_mode,
+	bool apply_filters);
+
 #endif
diff --git a/src/branch.c b/src/branch.c
index 7064fa7..95b3fd9 100644
--- a/src/branch.c
+++ b/src/branch.c
@@ -124,50 +124,68 @@
 	return error;
 }
 
-int git_branch_foreach(
-	git_repository *repo,
-	unsigned int list_flags,
-	git_branch_foreach_cb callback,
-	void *payload)
-{
+typedef struct {
 	git_reference_iterator *iter;
+	unsigned int flags;
+} branch_iter;
+
+int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
+{
+	branch_iter *iter = (branch_iter *) _iter;
 	git_reference *ref;
-	int error = 0;
+	int error;
 
-	if (git_reference_iterator_new(&iter, repo) < 0)
-		return -1;
+	while ((error = git_reference_next(&ref, iter->iter)) == 0) {
+		if ((iter->flags & GIT_BRANCH_LOCAL) &&
+		    !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
+			*out = ref;
+			*out_type = GIT_BRANCH_LOCAL;
 
-	while ((error = git_reference_next(&ref, iter)) == 0) {
-		if (list_flags & GIT_BRANCH_LOCAL &&
-		    git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) {
-			if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR),
-					GIT_BRANCH_LOCAL, payload)) {
-				error = GIT_EUSER;
-			}
+			return 0;
+		} else  if ((iter->flags & GIT_BRANCH_REMOTE) &&
+			    !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
+			*out = ref;
+			*out_type = GIT_BRANCH_REMOTE;
+
+			return 0;
+		} else {
+			git_reference_free(ref);
 		}
-
-		if (list_flags & GIT_BRANCH_REMOTE &&
-		    git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0) {
-			if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR),
-					GIT_BRANCH_REMOTE, payload)) {
-				error = GIT_EUSER;
-			}
-		}
-
-		git_reference_free(ref);
-
-		/* check if the callback has cancelled iteration */
-		if (error == GIT_EUSER)
-			break;
 	}
 
-	if (error == GIT_ITEROVER)
-		error = 0;
-
-	git_reference_iterator_free(iter);
 	return error;
 }
 
+int git_branch_iterator_new(
+	git_branch_iterator **out,
+	git_repository *repo,
+	git_branch_t list_flags)
+{
+	branch_iter *iter;
+
+	iter = git__calloc(1, sizeof(branch_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	iter->flags = list_flags;
+
+	if (git_reference_iterator_new(&iter->iter, repo) < 0) {
+		git__free(iter);
+		return -1;
+	}
+
+	*out = (git_branch_iterator *) iter;
+
+	return 0;
+}
+
+void git_branch_iterator_free(git_branch_iterator *_iter)
+{
+	branch_iter *iter = (branch_iter *) _iter;
+
+	git_reference_iterator_free(iter->iter);
+	git__free(iter);
+}
+
 int git_branch_move(
 	git_reference **out,
 	git_reference *branch,
@@ -585,7 +603,7 @@
 
 	error = git_repository_head(&head, git_reference_owner(branch));
 
-	if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+	if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
 		return false;
 
 	if (error < 0)
diff --git a/src/buf_text.c b/src/buf_text.c
index 443454b..631feb3 100644
--- a/src/buf_text.c
+++ b/src/buf_text.c
@@ -70,10 +70,10 @@
 	assert(tgt != src);
 
 	if (!next)
-		return GIT_ENOTFOUND;
+		return git_buf_set(tgt, src->ptr, src->size);
 
 	/* reduce reallocs while in the loop */
-	if (git_buf_grow(tgt, src->size) < 0)
+	if (git_buf_grow(tgt, src->size + 1) < 0)
 		return -1;
 	out = tgt->ptr;
 	tgt->size = 0;
@@ -81,20 +81,25 @@
 	/* Find the next \r and copy whole chunk up to there to tgt */
 	for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
 		if (next > scan) {
-			size_t copylen = next - scan;
+			size_t copylen = (size_t)(next - scan);
 			memcpy(out, scan, copylen);
 			out += copylen;
 		}
 
 		/* Do not drop \r unless it is followed by \n */
-		if (next[1] != '\n')
+		if (next + 1 == scan_end || next[1] != '\n')
 			*out++ = '\r';
 	}
 
 	/* Copy remaining input into dest */
-	memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
-	out += (scan_end - scan);
-	tgt->size = out - tgt->ptr;
+	if (scan < scan_end) {
+		size_t remaining = (size_t)(scan_end - scan);
+		memcpy(out, scan, remaining);
+		out += remaining;
+	}
+
+	tgt->size = (size_t)(out - tgt->ptr);
+	tgt->ptr[tgt->size] = '\0';
 
 	return 0;
 }
@@ -109,7 +114,7 @@
 	assert(tgt != src);
 
 	if (!next)
-		return GIT_ENOTFOUND;
+		return git_buf_set(tgt, src->ptr, src->size);
 
 	/* attempt to reduce reallocs while in the loop */
 	if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
@@ -170,8 +175,14 @@
 bool git_buf_text_is_binary(const git_buf *buf)
 {
 	const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+	git_bom_t bom;
 	int printable = 0, nonprintable = 0;
 
+	scan += git_buf_text_detect_bom(&bom, buf, 0);
+
+	if (bom > GIT_BOM_UTF8)
+		return 1;
+
 	while (scan < end) {
 		unsigned char c = *scan++;
 
@@ -262,7 +273,7 @@
 	while (scan < end) {
 		unsigned char c = *scan++;
 
-		if ((c > 0x1F && c < 0x7F) || c > 0x9f)
+		if (c > 0x1F && c != 0x7F)
 			stats->printable++;
 		else switch (c) {
 			case '\0':
diff --git a/src/buf_text.h b/src/buf_text.h
index 58e4e26..3ac9d14 100644
--- a/src/buf_text.h
+++ b/src/buf_text.h
@@ -56,16 +56,16 @@
 extern void git_buf_text_unescape(git_buf *buf);
 
 /**
- * Replace all \r\n with \n (or do nothing if no \r\n are found)
+ * Replace all \r\n with \n. Does not modify \r without trailing \n.
  *
- * @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
+ * @return 0 on success, -1 on memory error
  */
 extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
 
 /**
- * Replace all \n with \r\n (or do nothing if no \n are found)
+ * Replace all \n with \r\n. Does not modify existing \r\n.
  *
- * @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
+ * @return 0 on success, -1 on memory error
  */
 extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
 
diff --git a/src/buffer.c b/src/buffer.c
index 6e3ffe5..2068232 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -6,6 +6,7 @@
  */
 #include "buffer.h"
 #include "posix.h"
+#include "git2/buffer.h"
 #include <stdarg.h>
 #include <ctype.h>
 
@@ -31,7 +32,8 @@
 		git_buf_grow(buf, initial_size);
 }
 
-int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
+int git_buf_try_grow(
+	git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external)
 {
 	char *new_ptr;
 	size_t new_size;
@@ -39,6 +41,9 @@
 	if (buf->ptr == git_buf__oom)
 		return -1;
 
+	if (!target_size)
+		target_size = buf->size;
+
 	if (target_size <= buf->asize)
 		return 0;
 
@@ -66,6 +71,9 @@
 		return -1;
 	}
 
+	if (preserve_external && !buf->asize && buf->ptr != NULL && buf->size > 0)
+		memcpy(new_ptr, buf->ptr, min(buf->size, new_size));
+
 	buf->asize = new_size;
 	buf->ptr   = new_ptr;
 
@@ -77,11 +85,16 @@
 	return 0;
 }
 
+int git_buf_grow(git_buf *buffer, size_t target_size)
+{
+	return git_buf_try_grow(buffer, target_size, true, true);
+}
+
 void git_buf_free(git_buf *buf)
 {
 	if (!buf) return;
 
-	if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
+	if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
 		git__free(buf->ptr);
 
 	git_buf_init(buf, 0);
@@ -90,11 +103,15 @@
 void git_buf_clear(git_buf *buf)
 {
 	buf->size = 0;
+
+	if (!buf->ptr)
+		buf->ptr = git_buf__initbuf;
+
 	if (buf->asize > 0)
 		buf->ptr[0] = '\0';
 }
 
-int git_buf_set(git_buf *buf, const char *data, size_t len)
+int git_buf_set(git_buf *buf, const void *data, size_t len)
 {
 	if (len == 0 || data == NULL) {
 		git_buf_clear(buf);
@@ -137,7 +154,7 @@
 	return git_buf_put(buf, string, strlen(string));
 }
 
-static const char b64str[64] =
+static const char b64str[] =
 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
@@ -194,6 +211,8 @@
 			format, args
 		);
 
+		va_end(args);
+
 		if (len < 0) {
 			git__free(buf->ptr);
 			buf->ptr = git_buf__oom;
@@ -259,6 +278,15 @@
 	}
 }
 
+void git_buf_shorten(git_buf *buf, size_t amount)
+{
+	if (amount > buf->size)
+		amount = buf->size;
+
+	buf->size = buf->size - amount;
+	buf->ptr[buf->size] = '\0';
+}
+
 void git_buf_rtruncate_at_char(git_buf *buf, char separator)
 {
 	ssize_t idx = git_buf_rfind_next(buf, separator);
diff --git a/src/buffer.h b/src/buffer.h
index 5402f38..4ca9d4d 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -9,18 +9,26 @@
 
 #include "common.h"
 #include "git2/strarray.h"
+#include "git2/buffer.h"
 #include <stdarg.h>
 
-typedef struct {
-	char *ptr;
-	size_t asize, size;
-} git_buf;
+/* typedef struct {
+ *  	char   *ptr;
+ *  	size_t asize, size;
+ * } git_buf;
+ */
 
 extern char git_buf__initbuf[];
 extern char git_buf__oom[];
 
+/* Use to initialize buffer structure when git_buf is on stack */
 #define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
 
+GIT_INLINE(bool) git_buf_is_allocated(const git_buf *buf)
+{
+	return (buf->ptr != NULL && buf->asize > 0);
+}
+
 /**
  * Initialize a git_buf structure.
  *
@@ -32,27 +40,16 @@
 /**
  * Attempt to grow the buffer to hold at least `target_size` bytes.
  *
- * If the allocation fails, this will return an error.  If mark_oom is true,
+ * If the allocation fails, this will return an error.  If `mark_oom` is true,
  * this will mark the buffer as invalid for future operations; if false,
  * existing buffer content will be preserved, but calling code must handle
- * that buffer was not expanded.
+ * that buffer was not expanded.  If `preserve_external` is true, then any
+ * existing data pointed to be `ptr` even if `asize` is zero will be copied
+ * into the newly allocated buffer.
  */
-extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom);
+extern int git_buf_try_grow(
+	git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external);
 
-/**
- * Grow the buffer to hold at least `target_size` bytes.
- *
- * If the allocation fails, this will return an error and the buffer will be
- * marked as invalid for future operations, invaliding contents.
- *
- * @return 0 on success or -1 on failure
- */
-GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
-{
-	return git_buf_try_grow(buf, target_size, true);
-}
-
-extern void git_buf_free(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);
@@ -81,7 +78,6 @@
  * return code of these functions and call them in a series then just call
  * git_buf_oom at the end.
  */
-int git_buf_set(git_buf *buf, const char *data, size_t len);
 int git_buf_sets(git_buf *buf, const char *string);
 int git_buf_putc(git_buf *buf, char c);
 int git_buf_put(git_buf *buf, const char *data, size_t len);
@@ -91,6 +87,7 @@
 void git_buf_clear(git_buf *buf);
 void git_buf_consume(git_buf *buf, const char *end);
 void git_buf_truncate(git_buf *buf, size_t len);
+void git_buf_shorten(git_buf *buf, size_t amount);
 void git_buf_rtruncate_at_char(git_buf *path, char separator);
 
 int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
diff --git a/src/cc-compat.h b/src/cc-compat.h
index a5e4ce1..37f1ea8 100644
--- a/src/cc-compat.h
+++ b/src/cc-compat.h
@@ -54,8 +54,12 @@
 
 #if defined (_MSC_VER)
 	typedef unsigned char bool;
-#	define true 1
-#	define false 0
+#	ifndef true
+#		define true 1
+#	endif
+#	ifndef false
+#		define false 0
+#	endif
 #else
 #	include <stdbool.h>
 #endif
diff --git a/src/checkout.c b/src/checkout.c
index 8f9ec64..6d7e3cf 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -26,6 +26,8 @@
 #include "diff.h"
 #include "pathspec.h"
 #include "buf_text.h"
+#include "merge_file.h"
+#include "path.h"
 
 /* See docs/checkout-internals.md for more information */
 
@@ -35,21 +37,23 @@
 	CHECKOUT_ACTION__UPDATE_BLOB = 2,
 	CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
 	CHECKOUT_ACTION__CONFLICT = 8,
-	CHECKOUT_ACTION__MAX = 8,
-	CHECKOUT_ACTION__DEFER_REMOVE = 16,
+	CHECKOUT_ACTION__UPDATE_CONFLICT = 16,
+	CHECKOUT_ACTION__MAX = 16,
+	CHECKOUT_ACTION__DEFER_REMOVE = 32,
 	CHECKOUT_ACTION__REMOVE_AND_UPDATE =
 		(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
 };
 
 typedef struct {
 	git_repository *repo;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_checkout_opts opts;
 	bool opts_free_baseline;
 	char *pfx;
 	git_index *index;
 	git_pool pool;
 	git_vector removes;
+	git_vector conflicts;
 	git_buf path;
 	size_t workdir_len;
 	unsigned int strategy;
@@ -59,6 +63,16 @@
 	size_t completed_steps;
 } checkout_data;
 
+typedef struct {
+	const git_index_entry *ancestor;
+	const git_index_entry *ours;
+	const git_index_entry *theirs;
+
+	int name_collision:1,
+		directoryfile:1,
+		one_to_two:1;
+} checkout_conflictdata;
+
 static int checkout_notify(
 	checkout_data *data,
 	git_checkout_notify_t why,
@@ -246,10 +260,10 @@
 	bool remove = false;
 	git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
 
-	if (!git_pathspec_match_path(
+	if (!git_pathspec__match(
 			pathspec, wd->path,
 			(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
-			git_iterator_ignore_case(workdir), NULL))
+			git_iterator_ignore_case(workdir), NULL, NULL))
 		return 0;
 
 	/* check if item is tracked in the index but not in the checkout diff */
@@ -592,6 +606,359 @@
 	return error;
 }
 
+GIT_INLINE(int) checkout_idxentry_cmp(
+	const git_index_entry *a,
+	const git_index_entry *b)
+{
+	if (!a && !b)
+		return 0;
+	else if (!a && b)
+		return -1;
+	else if(a && !b)
+		return 1;
+	else
+		return strcmp(a->path, b->path);
+}
+
+static int checkout_conflictdata_cmp(const void *a, const void *b)
+{
+	const checkout_conflictdata *ca = a;
+	const checkout_conflictdata *cb = b;
+	int diff;
+
+	if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
+		(diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
+		diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
+
+	return diff;
+}
+
+int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx)
+{
+	checkout_conflictdata *conflict;
+
+	if ((conflict = git_vector_get(conflicts, idx)) == NULL)
+		return -1;
+
+	if (conflict->ancestor || conflict->ours || conflict->theirs)
+		return 0;
+
+	git__free(conflict);
+	return 1;
+}
+
+GIT_INLINE(bool) conflict_pathspec_match(
+	checkout_data *data,
+	git_iterator *workdir,
+	git_vector *pathspec,
+	const git_index_entry *ancestor,
+	const git_index_entry *ours,
+	const git_index_entry *theirs)
+{
+	/* if the pathspec matches ours *or* theirs, proceed */
+	if (ours && git_pathspec__match(pathspec, ours->path,
+		(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+		git_iterator_ignore_case(workdir), NULL, NULL))
+		return true;
+
+	if (theirs && git_pathspec__match(pathspec, theirs->path,
+		(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+		git_iterator_ignore_case(workdir), NULL, NULL))
+		return true;
+
+	if (ancestor && git_pathspec__match(pathspec, ancestor->path,
+		(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+		git_iterator_ignore_case(workdir), NULL, NULL))
+		return true;
+
+	return false;
+}
+
+static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+{
+	git_index_conflict_iterator *iterator = NULL;
+	const git_index_entry *ancestor, *ours, *theirs;
+	checkout_conflictdata *conflict;
+	int error = 0;
+
+	if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
+		goto done;
+
+	data->conflicts._cmp = checkout_conflictdata_cmp;
+
+	/* Collect the conflicts */
+	while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
+		if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
+			continue;
+
+		conflict = git__calloc(1, sizeof(checkout_conflictdata));
+		GITERR_CHECK_ALLOC(conflict);
+
+		conflict->ancestor = ancestor;
+		conflict->ours = ours;
+		conflict->theirs = theirs;
+
+		git_vector_insert(&data->conflicts, conflict);
+	}
+
+	if (error == GIT_ITEROVER)
+		error = 0;
+
+done:
+	git_index_conflict_iterator_free(iterator);
+
+	return error;
+}
+
+GIT_INLINE(int) checkout_conflicts_cmp_entry(
+	const char *path,
+	const git_index_entry *entry)
+{
+	return strcmp((const char *)path, entry->path);
+}
+
+static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
+{
+	const char *path = p;
+	const checkout_conflictdata *conflict = c;
+
+	if (!conflict->ancestor)
+		return 1;
+
+	return checkout_conflicts_cmp_entry(path, conflict->ancestor);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_ancestor(
+	checkout_data *data,
+	const char *path)
+{
+	size_t pos;
+
+	if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
+		return NULL;
+
+	return git_vector_get(&data->conflicts, pos);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_branch(
+	checkout_data *data,
+	const char *path)
+{
+	checkout_conflictdata *conflict;
+	size_t i;
+
+	git_vector_foreach(&data->conflicts, i, conflict) {
+		int cmp = -1;
+
+		if (conflict->ancestor)
+			break;
+
+		if (conflict->ours)
+			cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
+		else if (conflict->theirs)
+			cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
+
+		if (cmp == 0)
+			return conflict;
+	}
+
+	return NULL;
+}
+
+static int checkout_conflicts_load_byname_entry(
+	checkout_conflictdata **ancestor_out,
+	checkout_conflictdata **ours_out,
+	checkout_conflictdata **theirs_out,
+	checkout_data *data,
+	const git_index_name_entry *name_entry)
+{
+	checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
+	int error = 0;
+
+	*ancestor_out = NULL;
+	*ours_out = NULL;
+	*theirs_out = NULL;
+
+	if (!name_entry->ancestor) {
+		giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
+		error = -1;
+		goto done;
+	}
+
+	if (!name_entry->ours && !name_entry->theirs) {
+		giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
+		error = -1;
+		goto done;
+	}
+
+	if ((ancestor = checkout_conflicts_search_ancestor(data,
+		name_entry->ancestor)) == NULL) {
+		giterr_set(GITERR_INDEX,
+			"A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
+			name_entry->ancestor);
+		error = -1;
+		goto done;
+	}
+
+	if (name_entry->ours) {
+		if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
+			ours = ancestor;
+		else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
+			ours->ours == NULL) {
+			giterr_set(GITERR_INDEX,
+				"A NAME entry referenced our entry '%s' which does not exist in the main index",
+				name_entry->ours);
+			error = -1;
+			goto done;
+		}
+	}
+
+	if (name_entry->theirs) {
+		if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
+			theirs = ancestor;
+		else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
+			theirs = ours;
+		else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
+			theirs->theirs == NULL) {
+			giterr_set(GITERR_INDEX,
+				"A NAME entry referenced their entry '%s' which does not exist in the main index",
+				name_entry->theirs);
+			error = -1;
+			goto done;
+		}
+	}
+
+	*ancestor_out = ancestor;
+	*ours_out = ours;
+	*theirs_out = theirs;
+
+done:
+	return error;
+}
+
+static int checkout_conflicts_coalesce_renames(
+	checkout_data *data)
+{
+	const git_index_name_entry *name_entry;
+	checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
+	size_t i, names;
+	int error = 0;
+
+	/* Juggle entries based on renames */
+	names = git_index_name_entrycount(data->index);
+	
+	for (i = 0; i < names; i++) {
+		name_entry = git_index_name_get_byindex(data->index, i);
+
+		if ((error = checkout_conflicts_load_byname_entry(
+			&ancestor_conflict, &our_conflict, &their_conflict,
+			data, name_entry)) < 0)
+			goto done;
+
+		if (our_conflict && our_conflict != ancestor_conflict) {
+			ancestor_conflict->ours = our_conflict->ours;
+			our_conflict->ours = NULL;
+
+			if (our_conflict->theirs)
+				our_conflict->name_collision = 1;
+
+			if (our_conflict->name_collision)
+				ancestor_conflict->name_collision = 1;
+		}
+
+		if (their_conflict && their_conflict != ancestor_conflict) {
+			ancestor_conflict->theirs = their_conflict->theirs;
+			their_conflict->theirs = NULL;
+
+			if (their_conflict->ours)
+				their_conflict->name_collision = 1;
+
+			if (their_conflict->name_collision)
+				ancestor_conflict->name_collision = 1;
+		}
+
+		if (our_conflict && our_conflict != ancestor_conflict &&
+			their_conflict && their_conflict != ancestor_conflict)
+			ancestor_conflict->one_to_two = 1;
+	}
+
+	git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty);
+
+done:
+	return error;
+}
+
+static int checkout_conflicts_mark_directoryfile(
+	checkout_data *data)
+{
+	checkout_conflictdata *conflict;
+	const git_index_entry *entry;
+	size_t i, j, len;
+	const char *path;
+	int prefixed, error = 0;
+
+	len = git_index_entrycount(data->index);
+
+	/* Find d/f conflicts */
+	git_vector_foreach(&data->conflicts, i, conflict) {
+		if ((conflict->ours && conflict->theirs) ||
+			(!conflict->ours && !conflict->theirs))
+			continue;
+
+		path = conflict->ours ?
+			conflict->ours->path : conflict->theirs->path;
+
+		if ((error = git_index_find(&j, data->index, path)) < 0) {
+			if (error == GIT_ENOTFOUND)
+				giterr_set(GITERR_INDEX,
+					"Index inconsistency, could not find entry for expected conflict '%s'", path);
+
+			goto done;
+		}
+
+		for (; j < len; j++) {
+			if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
+				giterr_set(GITERR_INDEX,
+					"Index inconsistency, truncated index while loading expected conflict '%s'", path);
+				error = -1;
+				goto done;
+			}
+
+			prefixed = git_path_equal_or_prefixed(path, entry->path);
+
+			if (prefixed == GIT_PATH_EQUAL)
+				continue;
+
+			if (prefixed == GIT_PATH_PREFIX)
+				conflict->directoryfile = 1;
+
+			break;
+		}
+	}
+
+done:
+	return error;
+}
+
+static int checkout_get_conflicts(
+	checkout_data *data,
+	git_iterator *workdir,
+	git_vector *pathspec)
+{
+	int error = 0;
+
+	if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
+		return 0;
+
+	if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
+		(error = checkout_conflicts_coalesce_renames(data)) < 0 ||
+		(error = checkout_conflicts_mark_directoryfile(data)) < 0)
+		goto done;
+
+done:
+	return error;
+}
+
 static int checkout_get_actions(
 	uint32_t **actions_ptr,
 	size_t **counts_ptr,
@@ -607,7 +974,7 @@
 	uint32_t *actions = NULL;
 
 	if (data->opts.paths.count > 0 &&
-		git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
+		git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
 		return -1;
 
 	if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
@@ -659,7 +1026,13 @@
 		goto fail;
 	}
 
-	git_pathspec_free(&pathspec);
+
+	if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0)
+		goto fail;
+
+	counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts);
+
+	git_pathspec__vfree(&pathspec);
 	git_pool_clear(&pathpool);
 
 	return 0;
@@ -670,7 +1043,7 @@
 	*actions_ptr = NULL;
 	git__free(actions);
 
-	git_pathspec_free(&pathspec);
+	git_pathspec__vfree(&pathspec);
 	git_pool_clear(&pathpool);
 
 	return error;
@@ -678,7 +1051,7 @@
 
 static int buffer_to_file(
 	struct stat *st,
-	git_buf *buffer,
+	git_buf *buf,
 	const char *path,
 	mode_t dir_mode,
 	int file_open_flags,
@@ -690,80 +1063,52 @@
 		return error;
 
 	if ((error = git_futils_writebuffer(
-			buffer, path, file_open_flags, file_mode)) < 0)
+			buf, path, file_open_flags, file_mode)) < 0)
 		return error;
 
-	if (st != NULL && (error = p_stat(path, st)) < 0) {
-		giterr_set(GITERR_OS, "Error while statting '%s'", path);
-		return error;
-	}
+	if (st != NULL && (error = p_stat(path, st)) < 0)
+		giterr_set(GITERR_OS, "Error statting '%s'", path);
 
-	if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) {
+	else if (GIT_PERMS_IS_EXEC(file_mode) &&
+			(error = p_chmod(path, file_mode)) < 0)
 		giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
-		return error;
-	}
 
-	return 0;
+	return error;
 }
 
 static int blob_content_to_file(
 	struct stat *st,
 	git_blob *blob,
 	const char *path,
+	const char * hint_path,
 	mode_t entry_filemode,
 	git_checkout_opts *opts)
 {
-	int error = -1, nb_filters = 0;
-	mode_t file_mode = opts->file_mode;
-	bool dont_free_filtered;
-	git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
-	git_vector filters = GIT_VECTOR_INIT;
+	int error = 0;
+	mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode;
+	git_buf out = GIT_BUF_INIT;
+	git_filter_list *fl = NULL;
 
-	/* Create a fake git_buf from the blob raw data... */
-	filtered.ptr  = (void *)git_blob_rawcontent(blob);
-	filtered.size = (size_t)git_blob_rawsize(blob);
-	/* ... and make sure it doesn't get unexpectedly freed */
-	dont_free_filtered = true;
+	if (hint_path == NULL)
+		hint_path = path;
 
-	if (!opts->disable_filters &&
-		!git_buf_text_is_binary(&filtered) &&
-		(nb_filters = git_filters_load(
-			&filters,
-			git_object_owner((git_object *)blob),
-			path,
-			GIT_FILTER_TO_WORKTREE)) > 0)
-	{
-		/* reset 'filtered' so it can be a filter target */
-		git_buf_init(&filtered, 0);
-		dont_free_filtered = false;
-	}
-
-	if (nb_filters < 0)
-		return nb_filters;
-
-	if (nb_filters > 0)	 {
-		if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
-			goto cleanup;
-
-		if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
-			goto cleanup;
-	}
-
-	/* Allow overriding of file mode */
-	if (!file_mode)
-		file_mode = entry_filemode;
-
-	error = buffer_to_file(
-		st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
+	if (!opts->disable_filters)
+		error = git_filter_list_load(
+			&fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
 
 	if (!error)
+		error = git_filter_list_apply_to_blob(&out, fl, blob);
+
+	git_filter_list_free(fl);
+
+	if (!error) {
+		error = buffer_to_file(
+			st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode);
+
 		st->st_mode = entry_filemode;
 
-cleanup:
-	git_filters_free(&filters);
-	git_buf_free(&unfiltered);
-	if (!dont_free_filtered)
-		git_buf_free(&filtered);
+		git_buf_free(&out);
+	}
 
 	return error;
 }
@@ -815,7 +1160,8 @@
 
 	memset(&entry, 0, sizeof(entry));
 	entry.path = (char *)file->path; /* cast to prevent warning */
-	git_index_entry__init_from_stat(&entry, st);
+	git_index_entry__init_from_stat(
+		&entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE));
 	git_oid_cpy(&entry.oid, &file->oid);
 
 	return git_index_add(data->index, &entry);
@@ -917,34 +1263,26 @@
 	return 0;
 }
 
-static int checkout_blob(
+static int checkout_write_content(
 	checkout_data *data,
-	const git_diff_file *file)
+	const git_oid *oid,
+	const char *full_path,
+	const char *hint_path,
+	unsigned int mode,
+	struct stat *st)
 {
 	int error = 0;
 	git_blob *blob;
-	struct stat st;
 
-	git_buf_truncate(&data->path, data->workdir_len);
-	if (git_buf_puts(&data->path, file->path) < 0)
-		return -1;
-
-	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
-		int rval = checkout_safe_for_update_only(
-			git_buf_cstr(&data->path), file->mode);
-		if (rval <= 0)
-			return rval;
-	}
-
-	if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
+	if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
 		return error;
 
-	if (S_ISLNK(file->mode))
+	if (S_ISLNK(mode))
 		error = blob_content_to_link(
-			&st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
+			st, blob, full_path, data->opts.dir_mode, data->can_symlink);
 	else
 		error = blob_content_to_file(
-			&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
+			st, blob, full_path, hint_path, mode, &data->opts);
 
 	git_blob_free(blob);
 
@@ -959,6 +1297,30 @@
 		error = 0;
 	}
 
+	return error;
+}
+
+static int checkout_blob(
+	checkout_data *data,
+	const git_diff_file *file)
+{
+	int error = 0;
+	struct stat st;
+
+	git_buf_truncate(&data->path, data->workdir_len);
+	if (git_buf_puts(&data->path, file->path) < 0)
+		return -1;
+
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
+		int rval = checkout_safe_for_update_only(
+			git_buf_cstr(&data->path), file->mode);
+		if (rval <= 0)
+			return rval;
+	}
+
+	error = checkout_write_content(
+		data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st);
+
 	/* update the index unless prevented */
 	if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
 		error = checkout_update_index(data, file, &st);
@@ -1129,8 +1491,278 @@
 	return error;
 }
 
+
+static int conflict_entry_name(
+	git_buf *out,
+	const char *side_name,
+	const char *filename)
+{
+	if (git_buf_puts(out, side_name) < 0 ||
+		git_buf_putc(out, ':') < 0 ||
+		git_buf_puts(out, filename) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int checkout_path_suffixed(git_buf *path, const char *suffix)
+{
+	size_t path_len;
+	int i = 0, error = 0;
+
+	if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0)
+		return -1;
+
+	path_len = git_buf_len(path);
+
+	while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) {
+		git_buf_truncate(path, path_len);
+
+		if ((error = git_buf_putc(path, '_')) < 0 ||
+			(error = git_buf_printf(path, "%d", i)) < 0)
+			return error;
+
+		i++;
+	}
+
+	if (i == INT_MAX) {
+		git_buf_truncate(path, path_len);
+
+		giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
+		return GIT_EEXISTS;
+	}
+
+	return 0;
+}
+
+static int checkout_write_entry(
+	checkout_data *data,
+	checkout_conflictdata *conflict,
+	const git_index_entry *side)
+{
+	const char *hint_path = NULL, *suffix;
+	struct stat st;
+	int error;
+
+	assert (side == conflict->ours || side == conflict->theirs);
+
+	git_buf_truncate(&data->path, data->workdir_len);
+	if (git_buf_puts(&data->path, side->path) < 0)
+		return -1;
+
+	if ((conflict->name_collision || conflict->directoryfile) &&
+		(data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
+		(data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
+
+		if (side == conflict->ours)
+			suffix = data->opts.our_label ? data->opts.our_label :
+				"ours";
+		else
+			suffix = data->opts.their_label ? data->opts.their_label :
+				"theirs";
+
+		if (checkout_path_suffixed(&data->path, suffix) < 0)
+			return -1;
+
+		hint_path = side->path;
+	}
+
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+		(error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
+		return error;
+
+	return checkout_write_content(data,
+		&side->oid, git_buf_cstr(&data->path), hint_path, side->mode, &st);
+}
+
+static int checkout_write_entries(
+	checkout_data *data,
+	checkout_conflictdata *conflict)
+{
+	int error = 0;
+
+	if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
+		error = checkout_write_entry(data, conflict, conflict->theirs);
+
+	return error;
+}
+
+static int checkout_merge_path(
+	git_buf *out,
+	checkout_data *data,
+	checkout_conflictdata *conflict,
+	git_merge_file_result *result)
+{
+	const char *our_label_raw, *their_label_raw, *suffix;
+	int error = 0;
+
+	if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
+		return error;
+
+	/* Most conflicts simply use the filename in the index */
+	if (!conflict->name_collision)
+		return 0;
+
+	/* Rename 2->1 conflicts need the branch name appended */
+	our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
+	their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
+	suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
+
+	if ((error = checkout_path_suffixed(out, suffix)) < 0)
+		return error;
+
+	return 0;
+}
+
+static int checkout_write_merge(
+	checkout_data *data,
+	checkout_conflictdata *conflict)
+{
+	git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
+		path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
+	git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+		ours = GIT_MERGE_FILE_INPUT_INIT,
+		theirs = GIT_MERGE_FILE_INPUT_INIT;
+	git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
+	git_filebuf output = GIT_FILEBUF_INIT;
+	int error = 0;
+
+	if ((conflict->ancestor &&
+		(error = git_merge_file_input_from_index_entry(
+		&ancestor, data->repo, conflict->ancestor)) < 0) ||
+		(error = git_merge_file_input_from_index_entry(
+		&ours, data->repo, conflict->ours)) < 0 ||
+		(error = git_merge_file_input_from_index_entry(
+		&theirs, data->repo, conflict->theirs)) < 0)
+		goto done;
+
+	ancestor.label = NULL;
+	ours.label = data->opts.our_label ? data->opts.our_label : "ours";
+	theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
+
+	/* If all the paths are identical, decorate the diff3 file with the branch
+	 * names.  Otherwise, append branch_name:path.
+	 */
+	if (conflict->ours && conflict->theirs &&
+		strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
+
+		if ((error = conflict_entry_name(
+			&our_label, ours.label, conflict->ours->path)) < 0 ||
+			(error = conflict_entry_name(
+			&their_label, theirs.label, conflict->theirs->path)) < 0)
+			goto done;
+
+		ours.label = git_buf_cstr(&our_label);
+		theirs.label = git_buf_cstr(&their_label);
+	}
+
+	if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0)
+		goto done;
+
+	if (result.path == NULL || result.mode == 0) {
+		giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
+		error = GIT_EMERGECONFLICT;
+		goto done;
+	}
+
+	if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
+		goto done;
+
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+		(error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
+		goto done;
+
+	if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
+		(error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
+		(error = git_filebuf_write(&output, result.data, result.len)) < 0 ||
+		(error = git_filebuf_commit(&output)) < 0)
+		goto done;
+
+done:
+	git_buf_free(&our_label);
+	git_buf_free(&their_label);
+
+	git_merge_file_input_free(&ancestor);
+	git_merge_file_input_free(&ours);
+	git_merge_file_input_free(&theirs);
+	git_merge_file_result_free(&result);
+	git_buf_free(&path_workdir);
+	git_buf_free(&path_suffixed);
+
+	return error;
+}
+
+static int checkout_create_conflicts(checkout_data *data)
+{
+	checkout_conflictdata *conflict;
+	size_t i;
+	int error = 0;
+
+	git_vector_foreach(&data->conflicts, i, conflict) {
+		/* Both deleted: nothing to do */
+		if (conflict->ours == NULL && conflict->theirs == NULL)
+			error = 0;
+
+		else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+			conflict->ours)
+			error = checkout_write_entry(data, conflict, conflict->ours);
+		else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+			conflict->theirs)
+			error = checkout_write_entry(data, conflict, conflict->theirs);
+
+		/* Ignore the other side of name collisions. */
+		else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+			!conflict->ours && conflict->name_collision)
+			error = 0;
+		else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+			!conflict->theirs && conflict->name_collision)
+			error = 0;
+
+		/* For modify/delete, name collisions and d/f conflicts, write
+		 * the file (potentially with the name mangled.
+		 */
+		else if (conflict->ours != NULL && conflict->theirs == NULL)
+			error = checkout_write_entry(data, conflict, conflict->ours);
+		else if (conflict->ours == NULL && conflict->theirs != NULL)
+			error = checkout_write_entry(data, conflict, conflict->theirs);
+
+		/* Add/add conflicts and rename 1->2 conflicts, write the
+		 * ours/theirs sides (potentially name mangled).
+		 */
+		else if (conflict->one_to_two)
+			error = checkout_write_entries(data, conflict);
+
+		/* If all sides are links, write the ours side */
+		else if (S_ISLNK(conflict->ours->mode) &&
+			S_ISLNK(conflict->theirs->mode))
+			error = checkout_write_entry(data, conflict, conflict->ours);
+		/* Link/file conflicts, write the file side */
+		else if (S_ISLNK(conflict->ours->mode))
+			error = checkout_write_entry(data, conflict, conflict->theirs);
+		else if (S_ISLNK(conflict->theirs->mode))
+			error = checkout_write_entry(data, conflict, conflict->ours);
+
+		else
+			error = checkout_write_merge(data, conflict);
+
+		if (error)
+			break;
+
+		data->completed_steps++;
+		report_progress(data,
+			conflict->ours ? conflict->ours->path :
+			(conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
+	}
+
+	return error;
+}
+
+
 static void checkout_data_clear(checkout_data *data)
 {
+	checkout_conflictdata *conflict;
+	size_t i;
+
 	if (data->opts_free_baseline) {
 		git_tree_free(data->opts.baseline);
 		data->opts.baseline = NULL;
@@ -1139,6 +1771,11 @@
 	git_vector_free(&data->removes);
 	git_pool_clear(&data->pool);
 
+	git_vector_foreach(&data->conflicts, i, conflict)
+		git__free(conflict);
+
+	git_vector_free(&data->conflicts);
+
 	git__free(data->pfx);
 	data->pfx = NULL;
 
@@ -1151,7 +1788,7 @@
 static int checkout_data_init(
 	checkout_data *data,
 	git_iterator *target,
-	git_checkout_opts *proposed)
+	const git_checkout_opts *proposed)
 {
 	int error = 0;
 	git_repository *repo = git_iterator_owner(target);
@@ -1200,10 +1837,20 @@
 		} else {
 			/* otherwise, grab and reload the index */
 			if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
-				(error = git_index_read(data->index)) < 0)
+				(error = git_index_read(data->index, true)) < 0)
 				goto cleanup;
 
-			/* clear the REUC when doing a tree or commit checkout */
+			/* cannot checkout if unresolved conflicts exist */
+			if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
+				git_index_has_conflicts(data->index)) {
+				error = GIT_EMERGECONFLICT;
+				giterr_set(GITERR_CHECKOUT,
+					"unresolved conflicts exist in the index");
+				goto cleanup;
+			}
+
+			/* clean conflict data when doing a tree or commit checkout */
+			git_index_name_clear(data->index);
 			git_index_reuc_clear(data->index);
 		}
 	}
@@ -1235,7 +1882,7 @@
 
 		error = checkout_lookup_head_tree(&data->opts.baseline, repo);
 
-		if (error == GIT_EORPHANEDHEAD) {
+		if (error == GIT_EUNBORNBRANCH) {
 			error = 0;
 			giterr_clear();
 		}
@@ -1245,6 +1892,7 @@
 	}
 
 	if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
+		(error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
 		(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
 		(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
 		(error = git_path_to_dir(&data->path)) < 0)
@@ -1261,7 +1909,7 @@
 
 int git_checkout_iterator(
 	git_iterator *target,
-	git_checkout_opts *opts)
+	const git_checkout_opts *opts)
 {
 	int error = 0;
 	git_iterator *baseline = NULL, *workdir = NULL;
@@ -1315,14 +1963,16 @@
 		goto cleanup;
 
 	/* Loop through diff (and working directory iterator) building a list of
-	 * actions to be taken, plus look for conflicts and send notifications.
+	 * actions to be taken, plus look for conflicts and send notifications,
+	 * then loop through conflicts.
 	 */
 	if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
 		goto cleanup;
 
 	data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
 		counts[CHECKOUT_ACTION__UPDATE_BLOB] +
-		counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
+		counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
+		counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
 
 	report_progress(&data, NULL); /* establish 0 baseline */
 
@@ -1341,6 +1991,10 @@
 		(error = checkout_create_submodules(actions, &data)) < 0)
 		goto cleanup;
 
+	if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
+		(error = checkout_create_conflicts(&data)) < 0)
+		goto cleanup;
+
 	assert(data.completed_steps == data.total_steps);
 
 cleanup:
@@ -1351,7 +2005,7 @@
 		(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
 		error = git_index_write(data.index);
 
-	git_diff_list_free(data.diff);
+	git_diff_free(data.diff);
 	git_iterator_free(workdir);
 	git_iterator_free(baseline);
 	git__free(actions);
@@ -1364,7 +2018,7 @@
 int git_checkout_index(
 	git_repository *repo,
 	git_index *index,
-	git_checkout_opts *opts)
+	const git_checkout_opts *opts)
 {
 	int error;
 	git_iterator *index_i;
@@ -1399,7 +2053,7 @@
 int git_checkout_tree(
 	git_repository *repo,
 	const git_object *treeish,
-	git_checkout_opts *opts)
+	const git_checkout_opts *opts)
 {
 	int error;
 	git_tree *tree = NULL;
@@ -1419,10 +2073,21 @@
 	if (!repo)
 		repo = git_object_owner(treeish);
 
-	if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
-		giterr_set(
-			GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
-		return -1;
+	if (treeish) {
+		if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
+			giterr_set(
+				GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
+			return -1;
+		}
+	}
+	else {
+		if ((error = checkout_lookup_head_tree(&tree, repo)) < 0) {
+			if (error != GIT_EUNBORNBRANCH)
+				giterr_set(
+					GITERR_CHECKOUT,
+					"HEAD could not be peeled to a tree and no treeish given");
+			return error;
+		}
 	}
 
 	if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
@@ -1436,20 +2101,8 @@
 
 int git_checkout_head(
 	git_repository *repo,
-	git_checkout_opts *opts)
+	const git_checkout_opts *opts)
 {
-	int error;
-	git_tree *head = NULL;
-	git_iterator *head_i = NULL;
-
 	assert(repo);
-
-	if (!(error = checkout_lookup_head_tree(&head, repo)) &&
-		!(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
-		error = git_checkout_iterator(head_i, opts);
-
-	git_iterator_free(head_i);
-	git_tree_free(head);
-
-	return error;
+	return git_checkout_tree(repo, NULL, opts);
 }
diff --git a/src/checkout.h b/src/checkout.h
index b1dc80c..6d71868 100644
--- a/src/checkout.h
+++ b/src/checkout.h
@@ -19,6 +19,6 @@
  */
 extern int git_checkout_iterator(
 	git_iterator *target,
-	git_checkout_opts *opts);
+	const git_checkout_opts *opts);
 
 #endif
diff --git a/src/clone.c b/src/clone.c
index 5b6c6f7..23aacd4 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -176,25 +176,20 @@
 	return error;
 }
 
-static int get_head_callback(git_remote_head *head, void *payload)
-{
-	git_remote_head **destination = (git_remote_head **)payload;
-
-	/* Save the first entry, and terminate the enumeration */
-	*destination = head;
-	return 1;
-}
-
 static int update_head_to_remote(git_repository *repo, git_remote *remote)
 {
 	int retcode = -1;
+	size_t refs_len;
 	git_refspec dummy_spec;
-	git_remote_head *remote_head;
+	const git_remote_head *remote_head, **refs;
 	struct head_info head_info;
 	git_buf remote_master_name = GIT_BUF_INIT;
 
+	if (git_remote_ls(&refs, &refs_len, remote) < 0)
+		return -1;
+
 	/* Did we just clone an empty repository? */
-	if (remote->refs.length == 0) {
+	if (refs_len == 0) {
 		return setup_tracking_config(
 			repo,
 			"master",
@@ -202,12 +197,8 @@
 			GIT_REFS_HEADS_MASTER_FILE);
 	}
 
-	/* Get the remote's HEAD. This is always the first ref in remote->refs. */
-	remote_head = NULL;
-	
-	if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
-		return -1;
-
+	/* Get the remote's HEAD. This is always the first ref in the list. */
+	remote_head = refs[0];
 	assert(remote_head);
 
 	git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
@@ -220,7 +211,7 @@
 		memset(&dummy_spec, 0, sizeof(git_refspec));
 		head_info.refspec = &dummy_spec;
 	}
-	
+
 	/* Determine the remote tracking reference name from the local master */
 	if (git_refspec_transform_r(
 		&remote_master_name,
@@ -270,23 +261,23 @@
 
 static int update_head_to_branch(
 		git_repository *repo,
-		const git_clone_options *options)
+		const char *remote_name,
+		const char *branch)
 {
 	int retcode;
 	git_buf remote_branch_name = GIT_BUF_INIT;
 	git_reference* remote_ref = NULL;
 
-	assert(options->checkout_branch);
+	assert(remote_name && branch);
 
 	if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
-		options->remote_name, options->checkout_branch)) < 0 )
+		remote_name, branch)) < 0 )
 		goto cleanup;
 
 	if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
 		goto cleanup;
 
-	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
-		options->checkout_branch);
+	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch);
 
 cleanup:
 	git_reference_free(remote_ref);
@@ -306,41 +297,18 @@
 {
 	int error;
 	git_remote *origin = NULL;
+	const char *name;
 
-	if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
+	name = options->remote_name ? options->remote_name : "origin";
+	if ((error = git_remote_create(&origin, repo, name, url)) < 0)
 		goto on_error;
 
-	git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
-			options->cred_acquire_payload);
-	git_remote_set_autotag(origin, options->remote_autotag);
-	/*
-	 * Don't write FETCH_HEAD, we'll check out the remote tracking
-	 * branch ourselves based on the server's default.
-	 */
-	git_remote_set_update_fetchhead(origin, 0);
+	if (options->ignore_cert_errors)
+		git_remote_check_cert(origin, 0);
 
-	if (options->remote_callbacks &&
-	    (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
+	if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0)
 		goto on_error;
 
-	if (options->fetch_spec) {
-		git_remote_clear_refspecs(origin);
-		if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
-			goto on_error;
-	}
-
-	if (options->push_spec &&
-	    (error = git_remote_add_push(origin, options->push_spec)) < 0)
-		goto on_error;
-
-	if (options->pushurl &&
-	    (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
-		goto on_error;
-
-	if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) {
-        git_remote_check_cert(origin, 0);
-    }
-
 	if ((error = git_remote_save(origin)) < 0)
 		goto on_error;
 
@@ -352,59 +320,10 @@
 	return error;
 }
 
-
-static int setup_remotes_and_fetch(
-		git_repository *repo,
-		const char *url,
-		const git_clone_options *options)
-{
-	int retcode = GIT_ERROR;
-	git_remote *origin = NULL;
-
-	/* Construct an origin remote */
-	if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
-		goto on_error;
-
-	git_remote_set_update_fetchhead(origin, 0);
-
-	/* If the download_tags value has not been specified, then make sure to
-		* download tags as well. It is set here because we want to download tags
-		* on the initial clone, but do not want to persist the value in the
-		* configuration file.
-		*/
-	if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO &&
-		((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
-		goto on_error;
-
-	/* Connect and download everything */
-	if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
-		goto on_error;
-
-	if ((retcode = git_remote_download(origin, options->fetch_progress_cb,
-		options->fetch_progress_payload)) < 0)
-		goto on_error;
-
-	/* Create "origin/foo" branches for all remote branches */
-	if ((retcode = git_remote_update_tips(origin)) < 0)
-		goto on_error;
-
-	/* Point HEAD to the requested branch */
-	if (options->checkout_branch)
-		retcode = update_head_to_branch(repo, options);
-	/* Point HEAD to the same ref as the remote's head */
-	else
-		retcode = update_head_to_remote(repo, origin);
-
-on_error:
-	git_remote_free(origin);
-	return retcode;
-}
-
-
 static bool should_checkout(
 	git_repository *repo,
 	bool is_bare,
-	git_checkout_opts *opts)
+	const git_checkout_opts *opts)
 {
 	if (is_bare)
 		return false;
@@ -415,64 +334,102 @@
 	if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
 		return false;
 
-	return !git_repository_head_orphan(repo);
+	return !git_repository_head_unborn(repo);
 }
 
-static void normalize_options(git_clone_options *dst, const git_clone_options *src)
+int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
 {
-	git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
-	if (!src) src = &default_options;
+	int error = 0, old_fetchhead;
+	git_strarray refspecs;
 
-	*dst = *src;
+	assert(repo && remote);
 
-	/* Provide defaults for null pointers */
-	if (!dst->remote_name) dst->remote_name = "origin";
+	if (!git_repository_is_empty(repo)) {
+		giterr_set(GITERR_INVALID, "the repository is not empty");
+		return -1;
+	}
+
+
+	if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0)
+		return error;
+
+	if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
+		return error;
+
+	old_fetchhead = git_remote_update_fetchhead(remote);
+	git_remote_set_update_fetchhead(remote, 0);
+
+	if ((error = git_remote_fetch(remote)) < 0)
+		goto cleanup;
+
+	if (branch)
+		error = update_head_to_branch(repo, git_remote_name(remote), branch);
+	/* Point HEAD to the same ref as the remote's head */
+	else
+		error = update_head_to_remote(repo, remote);
+
+	if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+		error = git_checkout_head(repo, co_opts);
+
+cleanup:
+	git_remote_set_update_fetchhead(remote, old_fetchhead);
+	/* Go back to the original refspecs */
+	if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) {
+		git_strarray_free(&refspecs);
+		return -1;
+	}
+
+	git_strarray_free(&refspecs);
+
+	return error;
 }
 
 int git_clone(
 	git_repository **out,
 	const char *url,
 	const char *local_path,
-	const git_clone_options *options)
+	const git_clone_options *_options)
 {
-	int retcode = GIT_ERROR;
+	int error = 0;
 	git_repository *repo = NULL;
-	git_clone_options normOptions;
-	int remove_directory_on_failure = 0;
+	git_remote *origin;
+	git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+	uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
 
 	assert(out && url && local_path);
 
-	normalize_options(&normOptions, options);
-	GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
+	if (_options)
+		memcpy(&options, _options, sizeof(git_clone_options));
+
+	GITERR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
 
 	/* Only clone to a new directory or an empty directory */
 	if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
 		giterr_set(GITERR_INVALID,
 			"'%s' exists and is not an empty directory", local_path);
-		return GIT_ERROR;
+		return GIT_EEXISTS;
 	}
 
-	/* Only remove the directory on failure if we create it */
-	remove_directory_on_failure = !git_path_exists(local_path);
+	/* Only remove the root directory on failure if we create it */
+	if (git_path_exists(local_path))
+		rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
 
-	if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
-		if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
-			/* Failed to fetch; clean up */
-			git_repository_free(repo);
+	if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
+		return error;
 
-			if (remove_directory_on_failure)
-				git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
-			else
-				git_futils_cleanupdir_r(local_path);
+	if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
+		error = git_clone_into(
+			repo, origin, &options.checkout_opts, options.checkout_branch);
 
-		} else {
-			*out = repo;
-			retcode = 0;
-		}
+		git_remote_free(origin);
 	}
 
-	if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
-		retcode = git_checkout_head(*out, &normOptions.checkout_opts);
+	if (error < 0) {
+		git_repository_free(repo);
+		repo = NULL;
+		(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+	}
 
-	return retcode;
+	*out = repo;
+	return error;
 }
diff --git a/src/commit.c b/src/commit.c
index 1ab9b34..91b60bb 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -19,30 +19,19 @@
 
 #include <stdarg.h>
 
-static void clear_parents(git_commit *commit)
-{
-	size_t i;
-
-	for (i = 0; i < commit->parent_ids.length; ++i) {
-		git_oid *parent = git_vector_get(&commit->parent_ids, i);
-		git__free(parent);
-	}
-
-	git_vector_clear(&commit->parent_ids);
-}
-
 void git_commit__free(void *_commit)
 {
 	git_commit *commit = _commit;
 
-	clear_parents(commit);
-	git_vector_free(&commit->parent_ids);
+	git_array_clear(commit->parent_ids);
 
 	git_signature_free(commit->author);
 	git_signature_free(commit->committer);
 
-	git__free(commit->message);
+	git__free(commit->raw_header);
+	git__free(commit->raw_message);
 	git__free(commit->message_encoding);
+
 	git__free(commit);
 }
 
@@ -171,12 +160,35 @@
 int git_commit__parse(void *_commit, git_odb_object *odb_obj)
 {
 	git_commit *commit = _commit;
-	const char *buffer = git_odb_object_data(odb_obj);
-	const char *buffer_end = buffer + git_odb_object_size(odb_obj);
+	const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
+	const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
 	git_oid parent_id;
+	uint32_t parent_count = 0;
+	size_t header_len;
 
-	if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
-		return -1;
+	/* find end-of-header (counting parents as we go) */
+	for (buffer = buffer_start; buffer < buffer_end; ++buffer) {
+		if (!strncmp("\n\n", buffer, 2)) {
+			++buffer;
+			break;
+		}
+		if (!strncmp("\nparent ", buffer, strlen("\nparent ")))
+			++parent_count;
+	}
+
+	header_len = buffer - buffer_start;
+	commit->raw_header = git__strndup(buffer_start, header_len);
+	GITERR_CHECK_ALLOC(commit->raw_header);
+
+	/* point "buffer" to header data */
+	buffer = commit->raw_header;
+	buffer_end = commit->raw_header + header_len;
+
+	if (parent_count < 1)
+		parent_count = 1;
+
+	git_array_init_to_size(commit->parent_ids, parent_count);
+	GITERR_CHECK_ARRAY(commit->parent_ids);
 
 	if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
 		goto bad_buffer;
@@ -186,13 +198,10 @@
 	 */
 
 	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
-		git_oid *new_id = git__malloc(sizeof(git_oid));
+		git_oid *new_id = git_array_alloc(commit->parent_ids);
 		GITERR_CHECK_ALLOC(new_id);
 
 		git_oid_cpy(new_id, &parent_id);
-
-		if (git_vector_insert(&commit->parent_ids, new_id) < 0)
-			return -1;
 	}
 
 	commit->author = git__malloc(sizeof(git_signature));
@@ -208,8 +217,8 @@
 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
 		return -1;
 
-	/* Parse add'l header entries until blank line found */
-	while (buffer < buffer_end && *buffer != '\n') {
+	/* Parse add'l header entries */
+	while (buffer < buffer_end) {
 		const char *eoln = buffer;
 		while (eoln < buffer_end && *eoln != '\n')
 			++eoln;
@@ -223,18 +232,21 @@
 
 		if (eoln < buffer_end && *eoln == '\n')
 			++eoln;
-
 		buffer = eoln;
 	}
 
-	/* buffer is now at the end of the header, double-check and move forward into the message */
-	if (buffer < buffer_end && *buffer == '\n')
-		buffer++;
+	/* point "buffer" to data after header */
+	buffer = git_odb_object_data(odb_obj);
+	buffer_end = buffer + git_odb_object_size(odb_obj);
 
-	/* parse commit message */
+	buffer += header_len;
+	if (*buffer == '\n')
+		++buffer;
+
+	/* extract commit message */
 	if (buffer <= buffer_end) {
-		commit->message = git__strndup(buffer, buffer_end - buffer);
-		GITERR_CHECK_ALLOC(commit->message);
+		commit->raw_message = git__strndup(buffer, buffer_end - buffer);
+		GITERR_CHECK_ALLOC(commit->raw_message);
 	}
 
 	return 0;
@@ -253,13 +265,27 @@
 
 GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
 GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
-GIT_COMMIT_GETTER(const char *, message, commit->message)
+GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message)
 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
+GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
-GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length)
+GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
 GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
 
+const char *git_commit_message(const git_commit *commit)
+{
+	const char *message = commit->raw_message;
+
+	assert(commit);
+
+	/* trim leading newlines from raw message */
+	while (*message && *message == '\n')
+		++message;
+
+	return message;
+}
+
 int git_commit_tree(git_tree **tree_out, const git_commit *commit)
 {
 	assert(commit);
@@ -271,7 +297,7 @@
 {
 	assert(commit);
 
-	return git_vector_get(&commit->parent_ids, n);
+	return git_array_get(commit->parent_ids, n);
 }
 
 int git_commit_parent(
diff --git a/src/commit.h b/src/commit.h
index d0981b1..d452e29 100644
--- a/src/commit.h
+++ b/src/commit.h
@@ -10,21 +10,22 @@
 #include "git2/commit.h"
 #include "tree.h"
 #include "repository.h"
-#include "vector.h"
+#include "array.h"
 
 #include <time.h>
 
 struct git_commit {
 	git_object object;
 
-	git_vector parent_ids;
+	git_array_t(git_oid) parent_ids;
 	git_oid tree_id;
 
 	git_signature *author;
 	git_signature *committer;
 
 	char *message_encoding;
-	char *message;
+	char *raw_message;
+	char *raw_header;
 };
 
 void git_commit__free(void *commit);
diff --git a/src/commit_list.c b/src/commit_list.c
index bd5b520..64416e5 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -36,7 +36,7 @@
 	git_commit_list *p;
 
 	while ((p = *pp) != NULL) {
-		if (git_commit_list_time_cmp(p->item, item) < 0)
+		if (git_commit_list_time_cmp(p->item, item) > 0)
 			break;
 
 		pp = &p->next;
diff --git a/src/common.h b/src/common.h
index 02d9ce9..159d31b 100644
--- a/src/common.h
+++ b/src/common.h
@@ -74,6 +74,30 @@
 int giterr_set_regex(const regex_t *regex, int error_code);
 
 /**
+ * Gets the system error code for this thread.
+ */
+GIT_INLINE(int) giterr_system_last(void)
+{
+#ifdef GIT_WIN32
+	return GetLastError();
+#else
+	return errno;
+#endif
+}
+
+/**
+ * Sets the system error code for this thread.
+ */
+GIT_INLINE(void) giterr_system_set(int code)
+{
+#ifdef GIT_WIN32
+	SetLastError(code);
+#else
+	errno = code;
+#endif
+}
+
+/**
  * Check a versioned structure for validity
  */
 GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expected_max, const char *name)
diff --git a/src/config.c b/src/config.c
index 068c402..0d94713 100644
--- a/src/config.c
+++ b/src/config.c
@@ -315,30 +315,241 @@
  * Loop over all the variables
  */
 
+typedef struct {
+	git_config_iterator parent;
+	git_config_iterator *current;
+	const git_config *cfg;
+	regex_t regex;
+	int has_regex;
+	size_t i;
+} all_iter;
+
+static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
+{
+	file_internal *internal;
+
+	for (; i > 0; --i) {
+		internal = git_vector_get(&cfg->files, i - 1);
+		if (!internal || !internal->file)
+			continue;
+
+		*out = i;
+		return 0;
+	}
+
+	return -1;
+}
+
+static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+	file_internal *internal;
+	git_config_backend *backend;
+	size_t i;
+	int error = 0;
+
+	if (iter->current != NULL &&
+	    (error = iter->current->next(entry, iter->current)) == 0) {
+		return 0;
+	}
+
+	if (error < 0 && error != GIT_ITEROVER)
+		return error;
+
+	do {
+		if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+			return GIT_ITEROVER;
+
+		internal = git_vector_get(&iter->cfg->files, i - 1);
+		backend = internal->file;
+		iter->i = i - 1;
+
+		if (iter->current)
+			iter->current->free(iter->current);
+
+		iter->current = NULL;
+		error = backend->iterator(&iter->current, backend);
+		if (error == GIT_ENOTFOUND)
+			continue;
+
+		if (error < 0)
+			return error;
+
+		error = iter->current->next(entry, iter->current);
+		/* If this backend is empty, then keep going */
+		if (error == GIT_ITEROVER)
+			continue;
+
+		return error;
+
+	} while(1);
+
+	return GIT_ITEROVER;
+}
+
+static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+	int error;
+	all_iter *iter = (all_iter *) _iter;
+
+	/*
+	 * We use the "normal" function to grab the next one across
+	 * backends and then apply the regex
+	 */
+	while ((error = all_iter_next(entry, _iter)) == 0) {
+		/* skip non-matching keys if regexp was provided */
+		if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
+			continue;
+
+		/* and simply return if we like the entry's name */
+		return 0;
+	}
+
+	return error;
+}
+
+static void all_iter_free(git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+
+	if (iter->current)
+		iter->current->free(iter->current);
+
+	git__free(iter);
+}
+
+static void all_iter_glob_free(git_config_iterator *_iter)
+{
+	all_iter *iter = (all_iter *) _iter;
+
+	regfree(&iter->regex);
+	all_iter_free(_iter);
+}
+
+int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+{
+	all_iter *iter;
+
+	iter = git__calloc(1, sizeof(all_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	iter->parent.free = all_iter_free;
+	iter->parent.next = all_iter_next;
+
+	iter->i = cfg->files.length;
+	iter->cfg = cfg;
+
+	*out = (git_config_iterator *) iter;
+
+	return 0;
+}
+
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+{
+	all_iter *iter;
+	int result;
+
+	if (regexp == NULL)
+		return git_config_iterator_new(out, cfg);
+
+	iter = git__calloc(1, sizeof(all_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
+		giterr_set_regex(&iter->regex, result);
+		regfree(&iter->regex);
+		return -1;
+	}
+
+	iter->parent.next = all_iter_glob_next;
+	iter->parent.free = all_iter_glob_free;
+	iter->i = cfg->files.length;
+	iter->cfg = cfg;
+
+	*out = (git_config_iterator *) iter;
+
+	return 0;
+}
+
 int git_config_foreach(
 	const git_config *cfg, git_config_foreach_cb cb, void *payload)
 {
 	return git_config_foreach_match(cfg, NULL, cb, payload);
 }
 
+int git_config_backend_foreach_match(
+	git_config_backend *backend,
+	const char *regexp,
+	int (*fn)(const git_config_entry *, void *),
+	void *data)
+{
+	git_config_entry *entry;
+	git_config_iterator* iter;
+	regex_t regex;
+	int result = 0;
+
+	if (regexp != NULL) {
+		if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+			giterr_set_regex(&regex, result);
+			regfree(&regex);
+			return -1;
+		}
+	}
+
+	if ((result = backend->iterator(&iter, backend)) < 0) {
+		iter = NULL;
+		return -1;
+	}
+
+	while(!(iter->next(&entry, iter) < 0)) {
+		/* skip non-matching keys if regexp was provided */
+		if (regexp && regexec(&regex, entry->name, 0, NULL, 0) != 0)
+			continue;
+
+		/* abort iterator on non-zero return value */
+		if (fn(entry, data)) {
+			giterr_clear();
+			result = GIT_EUSER;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	if (regexp != NULL)
+		regfree(&regex);
+
+	iter->free(iter);
+
+	return result;
+}
+
 int git_config_foreach_match(
 	const git_config *cfg,
 	const char *regexp,
 	git_config_foreach_cb cb,
 	void *payload)
 {
-	int ret = 0;
-	size_t i;
-	file_internal *internal;
-	git_config_backend *file;
+	int error;
+	git_config_iterator *iter;
+	git_config_entry *entry;
 
-	for (i = 0; i < cfg->files.length && ret == 0; ++i) {
-		internal = git_vector_get(&cfg->files, i);
-		file = internal->file;
-		ret = file->foreach(file, regexp, cb, payload);
+	if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+		return error;
+
+	while ((error = git_config_next(&entry, iter)) == 0) {
+		if(cb(entry, payload)) {
+			giterr_clear();
+			error = GIT_EUSER;
+			break;
+		}
 	}
 
-	return ret;
+	git_config_iterator_free(iter);
+
+	if (error == GIT_ITEROVER)
+		error = 0;
+
+	return error;
 }
 
 /**************
@@ -528,31 +739,114 @@
 	return config_error_notfound(name);
 }
 
-int git_config_get_multivar(
+int git_config_get_multivar_foreach(
 	const git_config *cfg, const char *name, const char *regexp,
 	git_config_foreach_cb cb, void *payload)
 {
-	file_internal *internal;
-	git_config_backend *file;
-	int ret = GIT_ENOTFOUND;
-	size_t i;
+	int err, found;
+	git_config_iterator *iter;
+	git_config_entry *entry;
 
-	/*
-	 * This loop runs the "wrong" way 'round because we need to
-	 * look at every value from the most general to most specific
-	 */
-	for (i = cfg->files.length; i > 0; --i) {
-		internal = git_vector_get(&cfg->files, i - 1);
-		if (!internal || !internal->file)
-			continue;
-		file = internal->file;
+	if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+		return err;
 
-		ret = file->get_multivar(file, name, regexp, cb, payload);
-		if (ret < 0 && ret != GIT_ENOTFOUND)
-			return ret;
+	found = 0;
+	while ((err = iter->next(&entry, iter)) == 0) {
+		found = 1;
+		if(cb(entry, payload)) {
+			iter->free(iter);
+			return GIT_EUSER;
+		}
 	}
 
-	return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0;
+	iter->free(iter);
+	if (err == GIT_ITEROVER)
+		err = 0;
+
+	if (found == 0 && err == 0)
+		err = config_error_notfound(name);
+
+	return err;
+}
+
+typedef struct {
+	git_config_iterator parent;
+	git_config_iterator *iter;
+	char *name;
+	regex_t regex;
+	int have_regex;
+} multivar_iter;
+
+static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+	multivar_iter *iter = (multivar_iter *) _iter;
+	int error = 0;
+
+	while ((error = iter->iter->next(entry, iter->iter)) == 0) {
+		if (git__strcmp(iter->name, (*entry)->name))
+			continue;
+
+		if (!iter->have_regex)
+			return 0;
+
+		if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
+			return 0;
+	}
+
+	return error;
+}
+
+void multivar_iter_free(git_config_iterator *_iter)
+{
+	multivar_iter *iter = (multivar_iter *) _iter;
+
+	iter->iter->free(iter->iter);
+
+	git__free(iter->name);
+	regfree(&iter->regex);
+	git__free(iter);
+}
+
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+{
+	multivar_iter *iter = NULL;
+	git_config_iterator *inner = NULL;
+	int error;
+
+	if ((error = git_config_iterator_new(&inner, cfg)) < 0)
+		return error;
+
+	iter = git__calloc(1, sizeof(multivar_iter));
+	GITERR_CHECK_ALLOC(iter);
+
+	if ((error = git_config__normalize_name(name, &iter->name)) < 0)
+		goto on_error;
+
+	if (regexp != NULL) {
+		error = regcomp(&iter->regex, regexp, REG_EXTENDED);
+		if (error < 0) {
+			giterr_set_regex(&iter->regex, error);
+			error = -1;
+			regfree(&iter->regex);
+			goto on_error;
+		}
+
+		iter->have_regex = 1;
+	}
+
+	iter->iter = inner;
+	iter->parent.free = multivar_iter_free;
+	iter->parent.next = multivar_iter_next;
+
+	*out = (git_config_iterator *) iter;
+
+	return 0;
+
+on_error:
+
+	inner->free(inner);
+	git__free(iter);
+	return error;
 }
 
 int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
@@ -568,6 +862,29 @@
 	return file->set_multivar(file, name, regexp, value);
 }
 
+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;
+
+	return file->del_multivar(file, name, regexp);
+}
+
+int git_config_next(git_config_entry **entry, git_config_iterator *iter)
+{
+	return iter->next(entry, iter);
+}
+
+void git_config_iterator_free(git_config_iterator *iter)
+{
+	iter->free(iter);
+}
+
 static int git_config__find_file_to_path(
 	char *out, size_t outlen, int (*find)(git_buf *buf))
 {
@@ -811,6 +1128,41 @@
 	return -1;
 }
 
+/* Take something the user gave us and make it nice for our hash function */
+int git_config__normalize_name(const char *in, char **out)
+{
+	char *name, *fdot, *ldot;
+
+	assert(in && out);
+
+	name = git__strdup(in);
+	GITERR_CHECK_ALLOC(name);
+
+	fdot = strchr(name, '.');
+	ldot = strrchr(name, '.');
+
+	if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
+		goto invalid;
+
+	/* Validate and downcase up to first dot and after last dot */
+	if (git_config_file_normalize_section(name, fdot) < 0 ||
+		git_config_file_normalize_section(ldot + 1, NULL) < 0)
+		goto invalid;
+
+	/* If there is a middle range, make sure it doesn't have newlines */
+	while (fdot < ldot)
+		if (*fdot++ == '\n')
+			goto invalid;
+
+	*out = name;
+	return 0;
+
+invalid:
+	git__free(name);
+	giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
+	return GIT_EINVALIDSPEC;
+}
+
 struct rename_data {
 	git_config *config;
 	git_buf *name;
diff --git a/src/config.h b/src/config.h
index c5c11ae..01e8465 100644
--- a/src/config.h
+++ b/src/config.h
@@ -47,6 +47,9 @@
  * @param out the new backend
  * @param path where the config file is located
  */
-extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
+extern int git_config_file__ondisk(git_config_backend **out, const char *path);
+
+extern int git_config__normalize_name(const char *in, char **out);
+
 
 #endif
diff --git a/src/config_cache.c b/src/config_cache.c
index 84de3a5..6808521 100644
--- a/src/config_cache.c
+++ b/src/config_cache.c
@@ -67,6 +67,7 @@
 	{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
 	{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
 	{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
+	{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
 };
 
 int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
diff --git a/src/config_file.c b/src/config_file.c
index dec9521..15c8de4 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -15,6 +15,7 @@
 #include "git2/sys/config.h"
 #include "git2/types.h"
 #include "strmap.h"
+#include "array.h"
 
 #include <ctype.h>
 #include <sys/types.h>
@@ -25,8 +26,18 @@
 typedef struct cvar_t {
 	struct cvar_t *next;
 	git_config_entry *entry;
+	int included; /* whether this is part of [include] */
 } cvar_t;
 
+typedef struct git_config_file_iter {
+	git_config_iterator parent;
+	git_strmap_iter iter;
+	cvar_t* next_var;
+} git_config_file_iter;
+
+/* Max depth for [include] directives */
+#define MAX_INCLUDE_DEPTH 10
+
 #define CVAR_LIST_HEAD(list) ((list)->head)
 
 #define CVAR_LIST_TAIL(list) ((list)->tail)
@@ -65,34 +76,37 @@
 		 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
 		 (iter) = (tmp))
 
+struct reader {
+	time_t file_mtime;
+	size_t file_size;
+	char *file_path;
+	git_buf buffer;
+	char *read_ptr;
+	int line_number;
+	int eof;
+};
+
 typedef struct {
 	git_config_backend parent;
 
 	git_strmap *values;
 
-	struct {
-		git_buf buffer;
-		char *read_ptr;
-		int line_number;
-		int eof;
-	} reader;
+	git_array_t(struct reader) readers;
 
 	char  *file_path;
-	time_t file_mtime;
-	size_t file_size;
 
 	git_config_level_t level;
 } diskfile_backend;
 
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level);
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth);
+static int parse_variable(struct reader *reader, char **var_name, char **var_value);
 static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
 static char *escape_value(const char *ptr);
 
-static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+static void set_parse_error(struct reader *reader, int col, const char *error_str)
 {
 	giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
-		error_str, backend->file_path, backend->reader.line_number, col);
+		error_str, reader->file_path, reader->line_number, col);
 }
 
 static void cvar_free(cvar_t *var)
@@ -106,6 +120,18 @@
 	git__free(var);
 }
 
+static int cvar_length(cvar_t *var)
+{
+	int length = 0;
+
+	while (var) {
+		length++;
+		var = var->next;
+	}
+
+	return length;
+}
+
 int git_config_file_normalize_section(char *start, char *end)
 {
 	char *scan;
@@ -118,7 +144,7 @@
 		if (end && scan >= end)
 			break;
 		if (isalnum(*scan))
-			*scan = tolower(*scan);
+			*scan = (char)tolower(*scan);
 		else if (*scan != '-' || scan == start)
 			return GIT_EINVALIDSPEC;
 	}
@@ -129,41 +155,6 @@
 	return 0;
 }
 
-/* Take something the user gave us and make it nice for our hash function */
-static int normalize_name(const char *in, char **out)
-{
-	char *name, *fdot, *ldot;
-
-	assert(in && out);
-
-	name = git__strdup(in);
-	GITERR_CHECK_ALLOC(name);
-
-	fdot = strchr(name, '.');
-	ldot = strrchr(name, '.');
-
-	if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
-		goto invalid;
-
-	/* Validate and downcase up to first dot and after last dot */
-	if (git_config_file_normalize_section(name, fdot) < 0 ||
-		git_config_file_normalize_section(ldot + 1, NULL) < 0)
-		goto invalid;
-
-	/* If there is a middle range, make sure it doesn't have newlines */
-	while (fdot < ldot)
-		if (*fdot++ == '\n')
-			goto invalid;
-
-	*out = name;
-	return 0;
-
-invalid:
-	git__free(name);
-	giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
-	return GIT_EINVALIDSPEC;
-}
-
 static void free_vars(git_strmap *values)
 {
 	cvar_t *var = NULL;
@@ -184,6 +175,7 @@
 static int config_open(git_config_backend *cfg, git_config_level_t level)
 {
 	int res;
+	struct reader *reader;
 	diskfile_backend *b = (diskfile_backend *)cfg;
 
 	b->level = level;
@@ -191,32 +183,52 @@
 	b->values = git_strmap_alloc();
 	GITERR_CHECK_ALLOC(b->values);
 
-	git_buf_init(&b->reader.buffer, 0);
+	git_array_init(b->readers);
+	reader = git_array_alloc(b->readers);
+	memset(reader, 0, sizeof(struct reader));
+
+	reader->file_path = git__strdup(b->file_path);
+	GITERR_CHECK_ALLOC(reader->file_path);
+
+	git_buf_init(&reader->buffer, 0);
 	res = git_futils_readbuffer_updated(
-		&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
+		&reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
 
 	/* It's fine if the file doesn't exist */
 	if (res == GIT_ENOTFOUND)
 		return 0;
 
-	if (res < 0 || (res = config_parse(b, level)) < 0) {
+	if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) {
 		free_vars(b->values);
 		b->values = NULL;
 	}
 
-	git_buf_free(&b->reader.buffer);
+	reader = git_array_get(b->readers, 0);
+	git_buf_free(&reader->buffer);
 	return res;
 }
 
 static int config_refresh(git_config_backend *cfg)
 {
-	int res, updated = 0;
+	int res = 0, updated = 0, any_updated = 0;
 	diskfile_backend *b = (diskfile_backend *)cfg;
 	git_strmap *old_values;
+	struct reader *reader;
+	uint32_t i;
 
-	res = git_futils_readbuffer_updated(
-		&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
-	if (res < 0 || !updated)
+	for (i = 0; i < git_array_size(b->readers); i++) {
+		reader = git_array_get(b->readers, i);
+		res = git_futils_readbuffer_updated(
+			&reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated);
+
+		if (res < 0)
+			return (res == GIT_ENOTFOUND) ? 0 : res;
+
+		if (updated)
+			any_updated = 1;
+	}
+
+	if (!any_updated)
 		return (res == GIT_ENOTFOUND) ? 0 : res;
 
 	/* need to reload - store old values and prep for reload */
@@ -224,74 +236,88 @@
 	b->values = git_strmap_alloc();
 	GITERR_CHECK_ALLOC(b->values);
 
-	if ((res = config_parse(b, b->level)) < 0) {
+	if ((res = config_parse(b, reader, b->level, 0)) < 0) {
 		free_vars(b->values);
 		b->values = old_values;
 	} else {
 		free_vars(old_values);
 	}
 
-	git_buf_free(&b->reader.buffer);
+	git_buf_free(&reader->buffer);
 	return res;
 }
 
 static void backend_free(git_config_backend *_backend)
 {
 	diskfile_backend *backend = (diskfile_backend *)_backend;
+	uint32_t i;
 
 	if (backend == NULL)
 		return;
 
+	for (i = 0; i < git_array_size(backend->readers); i++) {
+		struct reader *r = git_array_get(backend->readers, i);
+		git__free(r->file_path);
+	}
+	git_array_clear(backend->readers);
+
 	git__free(backend->file_path);
 	free_vars(backend->values);
 	git__free(backend);
 }
 
-static int file_foreach(
-	git_config_backend *backend,
-	const char *regexp,
-	int (*fn)(const git_config_entry *, void *),
-	void *data)
+static void config_iterator_free(
+	git_config_iterator* iter)
 {
-	diskfile_backend *b = (diskfile_backend *)backend;
-	cvar_t *var, *next_var;
-	const char *key;
-	regex_t regex;
-	int result = 0;
+	git__free(iter);
+}
 
-	if (!b->values)
-		return 0;
+static int config_iterator_next(
+	git_config_entry **entry,
+	git_config_iterator *iter)
+{
+	git_config_file_iter *it = (git_config_file_iter *) iter;
+	diskfile_backend *b = (diskfile_backend *) it->parent.backend;
+	int err = 0;
+	cvar_t * var;
 
-	if (regexp != NULL) {
-		if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
-			giterr_set_regex(&regex, result);
-			regfree(&regex);
-			return -1;
-		}
+	if (it->next_var == NULL) {
+		err = git_strmap_next((void**) &var, &(it->iter), b->values);
+	} else {
+		var = it->next_var;
 	}
 
-	git_strmap_foreach(b->values, key, var,
-		for (; var != NULL; var = next_var) {
-			next_var = CVAR_LIST_NEXT(var);
+	if (err < 0) {
+		it->next_var = NULL;
+		return err;
+	}
 
-			/* skip non-matching keys if regexp was provided */
-			if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
-				continue;
+	*entry = var->entry;
+	it->next_var = CVAR_LIST_NEXT(var);
 
-			/* abort iterator on non-zero return value */
-			if (fn(var->entry, data)) {
-				giterr_clear();
-				result = GIT_EUSER;
-				goto cleanup;
-			}
-		}
-	);
+	return 0;
+}
 
-cleanup:
-	if (regexp != NULL)
-		regfree(&regex);
+static int config_iterator_new(
+	git_config_iterator **iter,
+	struct git_config_backend* backend)
+{
+	diskfile_backend *b = (diskfile_backend *)backend;
+	git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
 
-	return result;
+	GIT_UNUSED(b);
+
+	GITERR_CHECK_ALLOC(it);
+
+	it->parent.backend = backend;
+	it->iter = git_strmap_begin(b->values);
+	it->next_var = NULL;
+
+	it->parent.next = config_iterator_next;
+	it->parent.free = config_iterator_free;
+	*iter = (git_config_iterator *) it;
+
+	return 0;
 }
 
 static int config_set(git_config_backend *cfg, const char *name, const char *value)
@@ -302,7 +328,7 @@
 	khiter_t pos;
 	int rval, ret;
 
-	if ((rval = normalize_name(name, &key)) < 0)
+	if ((rval = git_config__normalize_name(name, &key)) < 0)
 		return rval;
 
 	/*
@@ -359,10 +385,10 @@
 		GITERR_CHECK_ALLOC(esc_value);
 	}
 
-	if (config_write(b, key, NULL, esc_value) < 0) {
+	if ((ret = config_write(b, key, NULL, esc_value)) < 0) {
 		git__free(esc_value);
 		cvar_free(var);
-		return -1;
+		return ret;
 	}
 
 	git__free(esc_value);
@@ -384,8 +410,9 @@
 	char *key;
 	khiter_t pos;
 	int error;
+	cvar_t *var;
 
-	if ((error = normalize_name(name, &key)) < 0)
+	if ((error = git_config__normalize_name(name, &key)) < 0)
 		return error;
 
 	pos = git_strmap_lookup_index(b->values, key);
@@ -395,71 +422,11 @@
 	if (!git_strmap_valid_index(b->values, pos))
 		return GIT_ENOTFOUND;
 
-	*out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry;
-
-	return 0;
-}
-
-static int config_get_multivar(
-	git_config_backend *cfg,
-	const char *name,
-	const char *regex_str,
-	int (*fn)(const git_config_entry *, void *),
-	void *data)
-{
-	cvar_t *var;
-	diskfile_backend *b = (diskfile_backend *)cfg;
-	char *key;
-	khiter_t pos;
-	int error;
-
-	if ((error = normalize_name(name, &key)) < 0)
-		return error;
-
-	pos = git_strmap_lookup_index(b->values, key);
-	git__free(key);
-
-	if (!git_strmap_valid_index(b->values, pos))
-		return GIT_ENOTFOUND;
-
 	var = git_strmap_value_at(b->values, pos);
+	while (var->next)
+		var = var->next;
 
-	if (regex_str != NULL) {
-		regex_t regex;
-		int result;
-
-		/* regex matching; build the regex */
-		result = regcomp(&regex, regex_str, REG_EXTENDED);
-		if (result < 0) {
-			giterr_set_regex(&regex, result);
-			regfree(&regex);
-			return -1;
-		}
-
-		/* and throw the callback only on the variables that
-		 * match the regex */
-		do {
-			if (regexec(&regex, var->entry->value, 0, NULL, 0) == 0) {
-				/* early termination by the user is not an error;
-				 * just break and return successfully */
-				if (fn(var->entry, data) < 0)
-					break;
-			}
-
-			var = var->next;
-		} while (var != NULL);
-		regfree(&regex);
-	} else {
-		/* no regex; go through all the variables */
-		do {
-			/* early termination by the user is not an error;
-			 * just break and return successfully */
-			if (fn(var->entry, data) < 0)
-				break;
-
-			var = var->next;
-		} while (var != NULL);
-	}
+	*out = var->entry;
 
 	return 0;
 }
@@ -477,7 +444,7 @@
 
 	assert(regexp);
 
-	if ((result = normalize_name(name, &key)) < 0)
+	if ((result = git_config__normalize_name(name, &key)) < 0)
 		return result;
 
 	pos = git_strmap_lookup_index(b->values, key);
@@ -550,7 +517,7 @@
 	int result;
 	khiter_t pos;
 
-	if ((result = normalize_name(name, &key)) < 0)
+	if ((result = git_config__normalize_name(name, &key)) < 0)
 		return result;
 
 	pos = git_strmap_lookup_index(b->values, key);
@@ -576,6 +543,80 @@
 	return result;
 }
 
+static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
+{
+	cvar_t *var, *prev = NULL, *new_head = NULL;
+	cvar_t **to_delete;
+	int to_delete_idx;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	regex_t preg;
+	int result;
+	khiter_t pos;
+
+	if ((result = git_config__normalize_name(name, &key)) < 0)
+		return result;
+
+	pos = git_strmap_lookup_index(b->values, key);
+
+	if (!git_strmap_valid_index(b->values, pos)) {
+		giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
+		git__free(key);
+		return GIT_ENOTFOUND;
+	}
+
+	var = git_strmap_value_at(b->values, pos);
+
+	result = regcomp(&preg, regexp, REG_EXTENDED);
+	if (result < 0) {
+		git__free(key);
+		giterr_set_regex(&preg, result);
+		regfree(&preg);
+		return -1;
+	}
+
+	to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *));
+	GITERR_CHECK_ALLOC(to_delete);
+	to_delete_idx = 0;
+
+	while (var != NULL) {
+		cvar_t *next = var->next;
+
+		if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
+			// If we are past the head, reattach previous node to next one,
+			// otherwise set the new head for the strmap.
+			if (prev != NULL) {
+				prev->next = next;
+			} else {
+				new_head = next;
+			}
+
+			to_delete[to_delete_idx++] = var;
+		} else {
+			prev = var;
+		}
+
+		var = next;
+	}
+
+	if (new_head != NULL) {
+		git_strmap_set_value_at(b->values, pos, new_head);
+	} else {
+		git_strmap_delete_at(b->values, pos);
+	}
+
+	if (to_delete_idx > 0)
+		result = config_write(b, key, &preg, NULL);
+
+	while (to_delete_idx-- > 0)
+		cvar_free(to_delete[to_delete_idx]);
+
+	git__free(key);
+	git__free(to_delete);
+	regfree(&preg);
+	return result;
+}
+
 int git_config_file__ondisk(git_config_backend **out, const char *path)
 {
 	diskfile_backend *backend;
@@ -590,11 +631,11 @@
 
 	backend->parent.open = config_open;
 	backend->parent.get = config_get;
-	backend->parent.get_multivar = config_get_multivar;
 	backend->parent.set = config_set;
 	backend->parent.set_multivar = config_set_multivar;
 	backend->parent.del = config_delete;
-	backend->parent.foreach = file_foreach;
+	backend->parent.del_multivar = config_delete_multivar;
+	backend->parent.iterator = config_iterator_new;
 	backend->parent.refresh = config_refresh;
 	backend->parent.free = backend_free;
 
@@ -603,26 +644,26 @@
 	return 0;
 }
 
-static int cfg_getchar_raw(diskfile_backend *cfg)
+static int reader_getchar_raw(struct reader *reader)
 {
 	int c;
 
-	c = *cfg->reader.read_ptr++;
+	c = *reader->read_ptr++;
 
 	/*
 	Win 32 line breaks: if we find a \r\n sequence,
 	return only the \n as a newline
 	*/
-	if (c == '\r' && *cfg->reader.read_ptr == '\n') {
-		cfg->reader.read_ptr++;
+	if (c == '\r' && *reader->read_ptr == '\n') {
+		reader->read_ptr++;
 		c = '\n';
 	}
 
 	if (c == '\n')
-		cfg->reader.line_number++;
+		reader->line_number++;
 
 	if (c == 0) {
-		cfg->reader.eof = 1;
+		reader->eof = 1;
 		c = '\n';
 	}
 
@@ -632,21 +673,23 @@
 #define SKIP_WHITESPACE (1 << 1)
 #define SKIP_COMMENTS (1 << 2)
 
-static int cfg_getchar(diskfile_backend *cfg_file, int flags)
+static int reader_getchar(struct reader *reader, int flags)
 {
 	const int skip_whitespace = (flags & SKIP_WHITESPACE);
 	const int skip_comments = (flags & SKIP_COMMENTS);
 	int c;
 
-	assert(cfg_file->reader.read_ptr);
+	assert(reader->read_ptr);
 
-	do c = cfg_getchar_raw(cfg_file);
-	while (skip_whitespace && git__isspace(c) &&
-	       !cfg_file->reader.eof);
+	do {
+		c = reader_getchar_raw(reader);
+	} while (skip_whitespace && git__isspace(c) &&
+	       !reader->eof);
 
 	if (skip_comments && (c == '#' || c == ';')) {
-		do c = cfg_getchar_raw(cfg_file);
-		while (c != '\n');
+		do {
+			c = reader_getchar_raw(reader);
+		} while (c != '\n');
 	}
 
 	return c;
@@ -655,23 +698,23 @@
 /*
  * Read the next char, but don't move the reading pointer.
  */
-static int cfg_peek(diskfile_backend *cfg, int flags)
+static int reader_peek(struct reader *reader, int flags)
 {
 	void *old_read_ptr;
 	int old_lineno, old_eof;
 	int ret;
 
-	assert(cfg->reader.read_ptr);
+	assert(reader->read_ptr);
 
-	old_read_ptr = cfg->reader.read_ptr;
-	old_lineno = cfg->reader.line_number;
-	old_eof = cfg->reader.eof;
+	old_read_ptr = reader->read_ptr;
+	old_lineno = reader->line_number;
+	old_eof = reader->eof;
 
-	ret = cfg_getchar(cfg, flags);
+	ret = reader_getchar(reader, flags);
 
-	cfg->reader.read_ptr = old_read_ptr;
-	cfg->reader.line_number = old_lineno;
-	cfg->reader.eof = old_eof;
+	reader->read_ptr = old_read_ptr;
+	reader->line_number = old_lineno;
+	reader->eof = old_eof;
 
 	return ret;
 }
@@ -679,13 +722,13 @@
 /*
  * Read and consume a line, returning it in newly-allocated memory.
  */
-static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
+static char *reader_readline(struct reader *reader, bool skip_whitespace)
 {
 	char *line = NULL;
 	char *line_src, *line_end;
 	size_t line_len;
 
-	line_src = cfg->reader.read_ptr;
+	line_src = reader->read_ptr;
 
 	if (skip_whitespace) {
 		/* Skip empty empty lines */
@@ -714,10 +757,10 @@
 		line_end++;
 
 	if (*line_end == '\0')
-		cfg->reader.eof = 1;
+		reader->eof = 1;
 
-	cfg->reader.line_number++;
-	cfg->reader.read_ptr = line_end;
+	reader->line_number++;
+	reader->read_ptr = line_end;
 
 	return line;
 }
@@ -725,11 +768,11 @@
 /*
  * Consume a line, without storing it anywhere
  */
-static void cfg_consume_line(diskfile_backend *cfg)
+static void reader_consume_line(struct reader *reader)
 {
 	char *line_start, *line_end;
 
-	line_start = cfg->reader.read_ptr;
+	line_start = reader->read_ptr;
 	line_end = strchr(line_start, '\n');
 	/* No newline at EOF */
 	if(line_end == NULL){
@@ -740,10 +783,10 @@
 		line_end++;
 
 	if (*line_end == '\0')
-		cfg->reader.eof = 1;
+		reader->eof = 1;
 
-	cfg->reader.line_number++;
-	cfg->reader.read_ptr = line_end;
+	reader->line_number++;
+	reader->read_ptr = line_end;
 }
 
 GIT_INLINE(int) config_keychar(int c)
@@ -751,12 +794,11 @@
 	return isalnum(c) || c == '-';
 }
 
-static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
+static int parse_section_header_ext(struct reader *reader, const char *line, const char *base_name, char **section_name)
 {
 	int c, rpos;
 	char *first_quote, *last_quote;
 	git_buf buf = GIT_BUF_INIT;
-	int quote_marks;
 	/*
 	 * base_name is what came before the space. We should be at the
 	 * first quotation mark, except for now, line isn't being kept in
@@ -767,7 +809,7 @@
 	last_quote = strrchr(line, '"');
 
 	if (last_quote - first_quote == 0) {
-		set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+		set_parse_error(reader, 0, "Missing closing quotation mark in section header");
 		return -1;
 	}
 
@@ -775,37 +817,30 @@
 	git_buf_printf(&buf, "%s.", base_name);
 
 	rpos = 0;
-	quote_marks = 0;
 
 	line = first_quote;
-	c = line[rpos++];
+	c = line[++rpos];
 
 	/*
 	 * At the end of each iteration, whatever is stored in c will be
 	 * added to the string. In case of error, jump to out
 	 */
 	do {
-		if (quote_marks == 2) {
-			set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
-			git_buf_free(&buf);
-			return -1;
-		}
 
 		switch (c) {
+		case 0:
+			set_parse_error(reader, 0, "Unexpected end-of-line in section header");
+			git_buf_free(&buf);
+			return -1;
+
 		case '"':
-			++quote_marks;
-			continue;
+			goto end_parse;
 
 		case '\\':
-			c = line[rpos++];
+			c = line[++rpos];
 
-			switch (c) {
-			case '"':
-			case '\\':
-				break;
-
-			default:
-				set_parse_error(cfg, rpos, "Unsupported escape sequence");
+			if (c == 0) {
+				set_parse_error(reader, rpos, "Unexpected end-of-line in section header");
 				git_buf_free(&buf);
 				return -1;
 			}
@@ -814,29 +849,37 @@
 			break;
 		}
 
-		git_buf_putc(&buf, c);
-	} while ((c = line[rpos++]) != ']');
+		git_buf_putc(&buf, (char)c);
+		c = line[++rpos];
+	} while (line + rpos < last_quote);
+
+end_parse:
+	if (line[rpos] != '"' || line[rpos + 1] != ']') {
+		set_parse_error(reader, rpos, "Unexpected text after closing quotes");
+		git_buf_free(&buf);
+		return -1;
+	}
 
 	*section_name = git_buf_detach(&buf);
 	return 0;
 }
 
-static int parse_section_header(diskfile_backend *cfg, char **section_out)
+static int parse_section_header(struct reader *reader, char **section_out)
 {
 	char *name, *name_end;
 	int name_length, c, pos;
 	int result;
 	char *line;
 
-	line = cfg_readline(cfg, true);
+	line = reader_readline(reader, true);
 	if (line == NULL)
 		return -1;
 
 	/* find the end of the variable's name */
-	name_end = strchr(line, ']');
+	name_end = strrchr(line, ']');
 	if (name_end == NULL) {
 		git__free(line);
-		set_parse_error(cfg, 0, "Missing ']' in section header");
+		set_parse_error(reader, 0, "Missing ']' in section header");
 		return -1;
 	}
 
@@ -855,14 +898,14 @@
 	do {
 		if (git__isspace(c)){
 			name[name_length] = '\0';
-			result = parse_section_header_ext(cfg, line, name, section_out);
+			result = parse_section_header_ext(reader, line, name, section_out);
 			git__free(line);
 			git__free(name);
 			return result;
 		}
 
 		if (!config_keychar(c) && c != '.') {
-			set_parse_error(cfg, pos, "Unexpected character in header");
+			set_parse_error(reader, pos, "Unexpected character in header");
 			goto fail_parse;
 		}
 
@@ -871,7 +914,7 @@
 	} while ((c = line[pos++]) != ']');
 
 	if (line[pos - 1] != ']') {
-		set_parse_error(cfg, pos, "Unexpected end of file");
+		set_parse_error(reader, pos, "Unexpected end of file");
 		goto fail_parse;
 	}
 
@@ -888,14 +931,14 @@
 	return -1;
 }
 
-static int skip_bom(diskfile_backend *cfg)
+static int skip_bom(struct reader *reader)
 {
 	git_bom_t bom;
 	int bom_offset = git_buf_text_detect_bom(&bom,
-		&cfg->reader.buffer, cfg->reader.read_ptr - cfg->reader.buffer.ptr);
+		&reader->buffer, reader->read_ptr - reader->buffer.ptr);
 
 	if (bom == GIT_BOM_UTF8)
-		cfg->reader.read_ptr += bom_offset;
+		reader->read_ptr += bom_offset;
 
 	/* TODO: reference implementation is pretty stupid with BoM */
 
@@ -965,7 +1008,16 @@
 	return quote_count;
 }
 
-static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
+static int included_path(git_buf *out, const char *dir, const char *path)
+{
+	/* From the user's home */
+	if (path[0] == '~' && path[1] == '/')
+		return git_futils_find_global_file(out, &path[1]);
+
+	return git_path_join_unrooted(out, path, dir, NULL);
+}
+
+static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth)
 {
 	int c;
 	char *current_section = NULL;
@@ -975,39 +1027,46 @@
 	git_buf buf = GIT_BUF_INIT;
 	int result = 0;
 	khiter_t pos;
+	uint32_t reader_idx;
 
+	if (depth >= MAX_INCLUDE_DEPTH) {
+		giterr_set(GITERR_CONFIG, "Maximum config include depth reached");
+		return -1;
+	}
+
+	reader_idx = git_array_size(cfg_file->readers) - 1;
 	/* Initialize the reading position */
-	cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
-	cfg_file->reader.eof = 0;
+	reader->read_ptr = reader->buffer.ptr;
+	reader->eof = 0;
 
 	/* If the file is empty, there's nothing for us to do */
-	if (*cfg_file->reader.read_ptr == '\0')
+	if (*reader->read_ptr == '\0')
 		return 0;
 
-	skip_bom(cfg_file);
+	skip_bom(reader);
 
-	while (result == 0 && !cfg_file->reader.eof) {
+	while (result == 0 && !reader->eof) {
 
-		c = cfg_peek(cfg_file, SKIP_WHITESPACE);
+		c = reader_peek(reader, SKIP_WHITESPACE);
 
 		switch (c) {
 		case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
-			cfg_file->reader.eof = 1;
+			reader->eof = 1;
 			break;
 
 		case '[': /* section header, new section begins */
 			git__free(current_section);
 			current_section = NULL;
-			result = parse_section_header(cfg_file, &current_section);
+			result = parse_section_header(reader, &current_section);
 			break;
 
 		case ';':
 		case '#':
-			cfg_consume_line(cfg_file);
+			reader_consume_line(reader);
 			break;
 
 		default: /* assume variable declaration */
-			result = parse_variable(cfg_file, &var_name, &var_value);
+			result = parse_variable(reader, &var_name, &var_value);
 			if (result < 0)
 				break;
 
@@ -1028,6 +1087,7 @@
 			var->entry->name = git_buf_detach(&buf);
 			var->entry->value = var_value;
 			var->entry->level = level;
+			var->included = !!depth;
 
 			/* Add or append the new config option */
 			pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
@@ -1044,6 +1104,42 @@
 				existing->next = var;
 			}
 
+			if (!git__strcmp(var->entry->name, "include.path")) {
+				struct reader *r;
+				git_buf path = GIT_BUF_INIT;
+				char *dir;
+				uint32_t index;
+
+				r = git_array_alloc(cfg_file->readers);
+				/* The reader may have been reallocated */
+				reader = git_array_get(cfg_file->readers, reader_idx);
+				memset(r, 0, sizeof(struct reader));
+				if ((result = git_path_dirname_r(&path, reader->file_path)) < 0)
+					break;
+
+				/* We need to know out index in the array, as the next config_parse call may realloc */
+				index = git_array_size(cfg_file->readers) - 1;
+				dir = git_buf_detach(&path);
+				result = included_path(&path, dir, var->entry->value);
+				git__free(dir);
+
+				if (result < 0)
+					break;
+
+				r->file_path = git_buf_detach(&path);
+				git_buf_init(&r->buffer, 0);
+				if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime,
+									    &r->file_size, NULL)) < 0)
+					break;
+
+				result = config_parse(cfg_file, r, level, depth+1);
+				r = git_array_get(cfg_file->readers, index);
+				git_buf_free(&r->buffer);
+
+				if (result < 0)
+					break;
+			}
+
 			break;
 		}
 	}
@@ -1082,6 +1178,24 @@
 	return result;
 }
 
+static const char *quotes_for_value(const char *value)
+{
+	const char *ptr;
+
+	if (value[0] == ' ' || value[0] == '\0')
+		return "\"";
+
+	for (ptr = value; *ptr; ++ptr) {
+		if (*ptr == ';' || *ptr == '#')
+			return "\"";
+	}
+
+	if (ptr[-1] == ' ')
+		return "\"";
+
+	return "";
+}
+
 /*
  * This is pretty much the parsing, except we write out anything we don't have
  */
@@ -1089,38 +1203,44 @@
 {
 	int result, c;
 	int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
-	const char *pre_end = NULL, *post_start = NULL, *data_start;
+	const char *pre_end = NULL, *post_start = NULL, *data_start, *write_start;
 	char *current_section = NULL, *section, *name, *ldot;
 	git_filebuf file = GIT_FILEBUF_INIT;
+	struct reader *reader = git_array_get(cfg->readers, 0);
 
 	/* We need to read in our own config file */
-	result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
+	result = git_futils_readbuffer(&reader->buffer, cfg->file_path);
 
 	/* Initialise the reading position */
 	if (result == GIT_ENOTFOUND) {
-		cfg->reader.read_ptr = NULL;
-		cfg->reader.eof = 1;
+		reader->read_ptr = NULL;
+		reader->eof = 1;
 		data_start = NULL;
-		git_buf_clear(&cfg->reader.buffer);
+		git_buf_clear(&reader->buffer);
 	} else if (result == 0) {
-		cfg->reader.read_ptr = cfg->reader.buffer.ptr;
-		cfg->reader.eof = 0;
-		data_start = cfg->reader.read_ptr;
+		reader->read_ptr = reader->buffer.ptr;
+		reader->eof = 0;
+		data_start = reader->read_ptr;
 	} else {
 		return -1; /* OS error when reading the file */
 	}
 
-	/* Lock the file */
-	if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
-		return -1;
+	write_start = data_start;
 
-	skip_bom(cfg);
+	/* Lock the file */
+	if ((result = git_filebuf_open(
+		&file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
+			git_buf_free(&reader->buffer);
+			return result;
+	}
+
+	skip_bom(reader);
 	ldot = strrchr(key, '.');
 	name = ldot + 1;
 	section = git__strndup(key, ldot - key);
 
-	while (!cfg->reader.eof) {
-		c = cfg_peek(cfg, SKIP_WHITESPACE);
+	while (!reader->eof) {
+		c = reader_peek(reader, SKIP_WHITESPACE);
 
 		if (c == '\0') { /* We've arrived at the end of the file */
 			break;
@@ -1133,11 +1253,11 @@
 			 * new section. If we actually want to replace it, the
 			 * default case will take care of updating them.
 			 */
-			pre_end = post_start = cfg->reader.read_ptr;
+			pre_end = post_start = reader->read_ptr;
 
 			git__free(current_section);
 			current_section = NULL;
-			if (parse_section_header(cfg, &current_section) < 0)
+			if (parse_section_header(reader, &current_section) < 0)
 				goto rewrite_fail;
 
 			/* Keep track of when it stops matching */
@@ -1146,7 +1266,7 @@
 		}
 
 		else if (c == ';' || c == '#') {
-			cfg_consume_line(cfg);
+			reader_consume_line(reader);
 		}
 
 		else {
@@ -1162,15 +1282,15 @@
 			 */
 			if (!section_matches) {
 				if (!last_section_matched) {
-					cfg_consume_line(cfg);
+					reader_consume_line(reader);
 					continue;
 				}
 			} else {
 				int has_matched = 0;
 				char *var_name, *var_value;
 
-				pre_end = cfg->reader.read_ptr;
-				if (parse_variable(cfg, &var_name, &var_value) < 0)
+				pre_end = reader->read_ptr;
+				if (parse_variable(reader, &var_name, &var_value) < 0)
 					goto rewrite_fail;
 
 				/* First try to match the name of the variable */
@@ -1189,23 +1309,28 @@
 				if (!has_matched)
 					continue;
 
-				post_start = cfg->reader.read_ptr;
+				post_start = reader->read_ptr;
 			}
 
 			/* We've found the variable we wanted to change, so
 			 * write anything up to it */
-			git_filebuf_write(&file, data_start, pre_end - data_start);
+			git_filebuf_write(&file, write_start, pre_end - write_start);
 			preg_replaced = 1;
 
 			/* Then replace the variable. If the value is NULL, it
 			 * means we want to delete it, so don't write anything. */
 			if (value != NULL) {
-				git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+				const char *q = quotes_for_value(value);
+				git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
 			}
 
-			/* multiline variable? we need to keep reading lines to match */
-			if (preg != NULL) {
-				data_start = post_start;
+			/*
+			 * If we have a multivar, we should keep looking for entries,
+			 * but only if we're in the right section. Otherwise we'll end up
+			 * looping on the edge of a matching and a non-matching section.
+			 */
+			if (section_matches && preg != NULL) {
+				write_start = post_start;
 				continue;
 			}
 
@@ -1232,12 +1357,14 @@
 	 */
 	if (write_trailer) {
 		/* Write out rest of the file */
-		git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+		git_filebuf_write(&file, post_start, reader->buffer.size - (post_start - data_start));
 	} else {
 		if (preg_replaced) {
-			git_filebuf_printf(&file, "\n%s", data_start);
+			git_filebuf_printf(&file, "\n%s", write_start);
 		} else {
-			git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+			const char *q;
+
+			git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size);
 
 			/* And now if we just need to add a variable */
 			if (!section_matches && write_section(&file, section) < 0)
@@ -1253,10 +1380,11 @@
 			}
 
 			/* If we are here, there is at least a section line */
-			if (cfg->reader.buffer.size > 0 && *(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n')
+			if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n')
 				git_filebuf_write(&file, "\n", 1);
 
-			git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+			q = quotes_for_value(value);
+			git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q);
 		}
 	}
 
@@ -1264,10 +1392,10 @@
 	git__free(current_section);
 
 	/* refresh stats - if this errors, then commit will error too */
-	(void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
+	(void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
 
-	result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
-	git_buf_free(&cfg->reader.buffer);
+	result = git_filebuf_commit(&file);
+	git_buf_free(&reader->buffer);
 
 	return result;
 
@@ -1276,7 +1404,7 @@
 	git__free(current_section);
 
 	git_filebuf_cleanup(&file);
-	git_buf_free(&cfg->reader.buffer);
+	git_buf_free(&reader->buffer);
 	return -1;
 }
 
@@ -1293,6 +1421,9 @@
 	assert(ptr);
 
 	len = strlen(ptr);
+	if (!len)
+		return git__calloc(1, sizeof(char));
+
 	git_buf_grow(&buf, len);
 
 	while (*ptr != '\0') {
@@ -1365,19 +1496,19 @@
 	return (end > str) && (count & 1);
 }
 
-static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
+static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes)
 {
 	char *line = NULL, *proc_line = NULL;
 	int quote_count;
 
 	/* Check that the next line exists */
-	line = cfg_readline(cfg, false);
+	line = reader_readline(reader, false);
 	if (line == NULL)
 		return -1;
 
 	/* We've reached the end of the file, there is input missing */
 	if (line[0] == '\0') {
-		set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+		set_parse_error(reader, 0, "Unexpected end of file while parsing multine var");
 		git__free(line);
 		return -1;
 	}
@@ -1387,7 +1518,7 @@
 	/* If it was just a comment, pretend it didn't exist */
 	if (line[0] == '\0') {
 		git__free(line);
-		return parse_multiline_variable(cfg, value, quote_count);
+		return parse_multiline_variable(reader, value, quote_count);
 		/* TODO: unbounded recursion. This **could** be exploitable */
 	}
 
@@ -1395,7 +1526,7 @@
 	 * standard, this character **has** to be last one in the buf, with
 	 * no whitespace after it */
 	assert(is_multiline_var(value->ptr));
-	git_buf_truncate(value, git_buf_len(value) - 1);
+	git_buf_shorten(value, 1);
 
 	proc_line = fixup_line(line, in_quotes);
 	if (proc_line == NULL) {
@@ -1412,19 +1543,19 @@
 	 * keep putting stuff in the buffer
 	 */
 	if (is_multiline_var(value->ptr))
-		return parse_multiline_variable(cfg, value, quote_count);
+		return parse_multiline_variable(reader, value, quote_count);
 
 	return 0;
 }
 
-static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
+static int parse_variable(struct reader *reader, char **var_name, char **var_value)
 {
 	const char *var_end = NULL;
 	const char *value_start = NULL;
 	char *line;
 	int quote_count;
 
-	line = cfg_readline(cfg, true);
+	line = reader_readline(reader, true);
 	if (line == NULL)
 		return -1;
 
@@ -1459,7 +1590,7 @@
 			GITERR_CHECK_ALLOC(proc_line);
 			git_buf_puts(&multi_value, proc_line);
 			git__free(proc_line);
-			if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
+			if (parse_multiline_variable(reader, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
 				git__free(*var_name);
 				git__free(line);
 				git_buf_free(&multi_value);
diff --git a/src/config_file.h b/src/config_file.h
index 7445859..d4a1a40 100644
--- a/src/config_file.h
+++ b/src/config_file.h
@@ -42,7 +42,7 @@
 	int (*fn)(const git_config_entry *entry, void *data),
 	void *data)
 {
-	return cfg->foreach(cfg, NULL, fn, data);
+	return git_config_backend_foreach_match(cfg, NULL, fn, data);
 }
 
 GIT_INLINE(int) git_config_file_foreach_match(
@@ -51,7 +51,7 @@
 	int (*fn)(const git_config_entry *entry, void *data),
 	void *data)
 {
-	return cfg->foreach(cfg, regexp, fn, data);
+	return git_config_backend_foreach_match(cfg, regexp, fn, data);
 }
 
 extern int git_config_file_normalize_section(char *start, char *end);
diff --git a/src/crlf.c b/src/crlf.c
index 65039f9..b25c02cc 100644
--- a/src/crlf.c
+++ b/src/crlf.c
@@ -8,6 +8,7 @@
 #include "git2/attr.h"
 #include "git2/blob.h"
 #include "git2/index.h"
+#include "git2/sys/filter.h"
 
 #include "common.h"
 #include "fileops.h"
@@ -19,13 +20,11 @@
 struct crlf_attrs {
 	int crlf_action;
 	int eol;
+	int auto_crlf;
 };
 
 struct crlf_filter {
 	git_filter f;
-	struct crlf_attrs attrs;
-	git_repository *repo;
-	char path[GIT_FLEX_ARRAY];
 };
 
 static int check_crlf(const char *value)
@@ -76,41 +75,10 @@
 	return ca->crlf_action;
 }
 
-static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+static int has_cr_in_index(const git_filter_source *src)
 {
-#define NUM_CONV_ATTRS 3
-
-	static const char *attr_names[NUM_CONV_ATTRS] = {
-		"crlf", "eol", "text",
-	};
-
-	const char *attr_vals[NUM_CONV_ATTRS];
-	int error;
-
-	error = git_attr_get_many(attr_vals,
-		repo, 0, path, NUM_CONV_ATTRS, attr_names);
-
-	if (error == GIT_ENOTFOUND) {
-		ca->crlf_action = GIT_CRLF_GUESS;
-		ca->eol = GIT_EOL_UNSET;
-		return 0;
-	}
-
-	if (error == 0) {
-		ca->crlf_action = check_crlf(attr_vals[2]); /* text */
-		if (ca->crlf_action == GIT_CRLF_GUESS)
-			ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
-
-		ca->eol = check_eol(attr_vals[1]); /* eol */
-		return 0;
-	}
-
-	return -1;
-}
-
-static int has_cr_in_index(git_filter *self)
-{
-	struct crlf_filter *filter = (struct crlf_filter *)self;
+	git_repository *repo = git_filter_source_repo(src);
+	const char *path = git_filter_source_path(src);
 	git_index *index;
 	const git_index_entry *entry;
 	git_blob *blob;
@@ -118,19 +86,22 @@
 	git_off_t blobsize;
 	bool found_cr;
 
-	if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+	if (!path)
+		return false;
+
+	if (git_repository_index__weakptr(&index, repo) < 0) {
 		giterr_clear();
 		return false;
 	}
 
-	if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
-		!(entry = git_index_get_bypath(index, filter->path, 1)))
+	if (!(entry = git_index_get_bypath(index, path, 0)) &&
+		!(entry = git_index_get_bypath(index, path, 1)))
 		return false;
 
 	if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
 		return true;
 
-	if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+	if (git_blob_lookup(&blob, repo, &entry->oid) < 0)
 		return false;
 
 	blobcontent = git_blob_rawcontent(blob);
@@ -147,27 +118,24 @@
 }
 
 static int crlf_apply_to_odb(
-	git_filter *self, git_buf *dest, const git_buf *source)
+	struct crlf_attrs *ca,
+	git_buf *to,
+	const git_buf *from,
+	const git_filter_source *src)
 {
-	struct crlf_filter *filter = (struct crlf_filter *)self;
-
-	assert(self && dest && source);
-
 	/* Empty file? Nothing to do */
-	if (git_buf_len(source) == 0)
+	if (!git_buf_len(from))
 		return 0;
 
 	/* Heuristics to see if we can skip the conversion.
 	 * Straight from Core Git.
 	 */
-	if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
-		filter->attrs.crlf_action == GIT_CRLF_GUESS) {
-
+	if (ca->crlf_action == GIT_CRLF_AUTO || ca->crlf_action == GIT_CRLF_GUESS) {
 		git_buf_text_stats stats;
 
-		/* Check heuristics for binary vs text... */
-		if (git_buf_text_gather_stats(&stats, source, false))
-			return -1;
+		/* Check heuristics for binary vs text - returns true if binary */
+		if (git_buf_text_gather_stats(&stats, from, false))
+			return GIT_PASSTHROUGH;
 
 		/*
 		 * We're currently not going to even try to convert stuff
@@ -175,28 +143,28 @@
 		 * stuff?
 		 */
 		if (stats.cr != stats.crlf)
-			return -1;
+			return GIT_PASSTHROUGH;
 
-		if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+		if (ca->crlf_action == GIT_CRLF_GUESS) {
 			/*
 			 * If the file in the index has any CR in it, do not convert.
 			 * This is the new safer autocrlf handling.
 			 */
-			if (has_cr_in_index(self))
-				return -1;
+			if (has_cr_in_index(src))
+				return GIT_PASSTHROUGH;
 		}
 
 		if (!stats.cr)
-			return -1;
+			return GIT_PASSTHROUGH;
 	}
 
 	/* Actually drop the carriage returns */
-	return git_buf_text_crlf_to_lf(dest, source);
+	return git_buf_text_crlf_to_lf(to, from);
 }
 
-static const char *line_ending(struct crlf_filter *filter)
+static const char *line_ending(struct crlf_attrs *ca)
 {
-	switch (filter->attrs.crlf_action) {
+	switch (ca->crlf_action) {
 	case GIT_CRLF_BINARY:
 	case GIT_CRLF_INPUT:
 		return "\n";
@@ -213,11 +181,9 @@
 		goto line_ending_error;
 	}
 
-	switch (filter->attrs.eol) {
+	switch (ca->eol) {
 	case GIT_EOL_UNSET:
-		return GIT_EOL_NATIVE == GIT_EOL_CRLF
-			? "\r\n"
-			: "\n";
+		return GIT_EOL_NATIVE == GIT_EOL_CRLF ? "\r\n" : "\n";
 
 	case GIT_EOL_CRLF:
 		return "\r\n";
@@ -235,41 +201,64 @@
 }
 
 static int crlf_apply_to_workdir(
-	git_filter *self, git_buf *dest, const git_buf *source)
+	struct crlf_attrs *ca, git_buf *to, const git_buf *from)
 {
-	struct crlf_filter *filter = (struct crlf_filter *)self;
 	const char *workdir_ending = NULL;
 
-	assert(self && dest && source);
-
 	/* Empty file? Nothing to do. */
-	if (git_buf_len(source) == 0)
-		return -1;
+	if (git_buf_len(from) == 0)
+		return 0;
+
+	/* Don't filter binary files */
+	if (git_buf_text_is_binary(from))
+		return GIT_PASSTHROUGH;
 
 	/* Determine proper line ending */
-	workdir_ending = line_ending(filter);
+	workdir_ending = line_ending(ca);
 	if (!workdir_ending)
 		return -1;
-	if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
-		return -1;
 
-	/* for now, only lf->crlf conversion is supported here */
-	assert(!strcmp("\r\n", workdir_ending));
-	return git_buf_text_lf_to_crlf(dest, source);
+	if (!strcmp("\n", workdir_ending)) {
+		if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf)
+			return GIT_PASSTHROUGH;
+
+		if (git_buf_find(from, '\r') < 0)
+			return GIT_PASSTHROUGH;
+
+		if (git_buf_text_crlf_to_lf(to, from) < 0)
+			return -1;
+	} else {
+		/* only other supported option is lf->crlf conversion */
+		assert(!strcmp("\r\n", workdir_ending));
+
+		if (git_buf_text_lf_to_crlf(to, from) < 0)
+			return -1;
+	}
+
+	return 0;
 }
 
-static int find_and_add_filter(
-	git_vector *filters, git_repository *repo, const char *path,
-	int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source))
+static int crlf_check(
+	git_filter        *self,
+	void              **payload, /* points to NULL ptr on entry, may be set */
+	const git_filter_source *src,
+	const char **attr_values)
 {
-	struct crlf_attrs ca;
-	struct crlf_filter *filter;
-	size_t pathlen;
 	int error;
+	struct crlf_attrs ca;
 
-	/* Load gitattributes for the path */
-	if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
-		return error;
+	GIT_UNUSED(self);
+
+	if (!attr_values) {
+		ca.crlf_action = GIT_CRLF_GUESS;
+		ca.eol = GIT_EOL_UNSET;
+	} else {
+		ca.crlf_action = check_crlf(attr_values[2]); /* text */
+		if (ca.crlf_action == GIT_CRLF_GUESS)
+			ca.crlf_action = check_crlf(attr_values[0]); /* clrf */
+		ca.eol = check_eol(attr_values[1]); /* eol */
+	}
+	ca.auto_crlf = GIT_AUTO_CRLF_DEFAULT;
 
 	/*
 	 * Use the core Git logic to see if we should perform CRLF for this file
@@ -278,41 +267,64 @@
 	ca.crlf_action = crlf_input_action(&ca);
 
 	if (ca.crlf_action == GIT_CRLF_BINARY)
-		return 0;
+		return GIT_PASSTHROUGH;
 
 	if (ca.crlf_action == GIT_CRLF_GUESS) {
-		int auto_crlf;
-
-		if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+		error = git_repository__cvar(
+			&ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF);
+		if (error < 0)
 			return error;
 
-		if (auto_crlf == GIT_AUTO_CRLF_FALSE)
-			return 0;
+		if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE)
+			return GIT_PASSTHROUGH;
 	}
 
-	/* If we're good, we create a new filter object and push it
-	 * into the filters array */
-	pathlen = strlen(path);
-	filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
-	GITERR_CHECK_ALLOC(filter);
+	*payload = git__malloc(sizeof(ca));
+	GITERR_CHECK_ALLOC(*payload);
+	memcpy(*payload, &ca, sizeof(ca));
 
-	filter->f.apply = apply;
-	filter->f.do_free = NULL;
-	memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
-	filter->repo = repo;
-	memcpy(filter->path, path, pathlen + 1);
-
-	return git_vector_insert(filters, filter);
+	return 0;
 }
 
-int git_filter_add__crlf_to_odb(
-	git_vector *filters, git_repository *repo, const char *path)
+static int crlf_apply(
+	git_filter    *self,
+	void         **payload, /* may be read and/or set */
+	git_buf       *to,
+	const git_buf *from,
+	const git_filter_source *src)
 {
-	return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
+	/* initialize payload in case `check` was bypassed */
+	if (!*payload) {
+		int error = crlf_check(self, payload, src, NULL);
+		if (error < 0 && error != GIT_PASSTHROUGH)
+			return error;
+	}
+
+	if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+		return crlf_apply_to_workdir(*payload, to, from);
+	else
+		return crlf_apply_to_odb(*payload, to, from, src);
 }
 
-int git_filter_add__crlf_to_workdir(
-	git_vector *filters, git_repository *repo, const char *path)
+static void crlf_cleanup(
+	git_filter *self,
+	void       *payload)
 {
-	return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
+	GIT_UNUSED(self);
+	git__free(payload);
+}
+
+git_filter *git_crlf_filter_new(void)
+{
+	struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
+
+	f->f.version = GIT_FILTER_VERSION;
+	f->f.attributes = "crlf eol text";
+	f->f.initialize = NULL;
+	f->f.shutdown = git_filter_free;
+	f->f.check    = crlf_check;
+	f->f.apply    = crlf_apply;
+	f->f.cleanup  = crlf_cleanup;
+
+	return (git_filter *)f;
 }
diff --git a/src/date.c b/src/date.c
index 48841e4..7849c2f 100644
--- a/src/date.c
+++ b/src/date.c
@@ -823,15 +823,13 @@
 }
 
 static git_time_t approxidate_str(const char *date,
-									const struct timeval *tv,
-									int *error_ret)
+	time_t time_sec,
+	int *error_ret)
 {
 	int number = 0;
 	int touched = 0;
 	struct tm tm = {0}, now;
-	time_t time_sec;
 
-	time_sec = tv->tv_sec;
 	p_localtime_r(&time_sec, &tm);
 	now = tm;
 
@@ -861,7 +859,7 @@
 
 int git__date_parse(git_time_t *out, const char *date)
 {
-	struct timeval tv;
+	time_t time_sec;
 	git_time_t timestamp;
 	int offset, error_ret=0;
 
@@ -870,7 +868,9 @@
 		return 0;
 	}
 
-	p_gettimeofday(&tv, NULL);
-	*out = approxidate_str(date, &tv, &error_ret);
+	if (time(&time_sec) == -1)
+		return -1;
+
+	*out = approxidate_str(date, time_sec, &error_ret);
    return error_ret;
 }
diff --git a/src/diff.c b/src/diff.c
index 26e1174..4c33a02 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -13,6 +13,7 @@
 #include "pathspec.h"
 #include "index.h"
 #include "odb.h"
+#include "submodule.h"
 
 #define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
 #define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
@@ -20,7 +21,7 @@
 	(VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
 
 static git_diff_delta *diff_delta__alloc(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_delta_t status,
 	const char *path)
 {
@@ -49,7 +50,7 @@
 }
 
 static int diff_notify(
-	const git_diff_list *diff,
+	const git_diff *diff,
 	const git_diff_delta *delta,
 	const char *matched_pathspec)
 {
@@ -61,14 +62,17 @@
 }
 
 static int diff_delta__from_one(
-	git_diff_list *diff,
-	git_delta_t   status,
+	git_diff *diff,
+	git_delta_t status,
 	const git_index_entry *entry)
 {
 	git_diff_delta *delta;
 	const char *matched_pathspec;
 	int notify_res;
 
+	if ((entry->flags & GIT_IDXENTRY_VALID) != 0)
+		return 0;
+
 	if (status == GIT_DELTA_IGNORED &&
 		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
 		return 0;
@@ -77,15 +81,11 @@
 		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
 		return 0;
 
-	if (entry->mode == GIT_FILEMODE_COMMIT &&
-		DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
-		return 0;
-
-	if (!git_pathspec_match_path(
+	if (!git_pathspec__match(
 			&diff->pathspec, entry->path,
 			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
-			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
-			&matched_pathspec))
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
+			&matched_pathspec, NULL))
 		return 0;
 
 	delta = diff_delta__alloc(diff, status, entry->path);
@@ -93,6 +93,7 @@
 
 	/* This fn is just for single-sided diffs */
 	assert(status != GIT_DELTA_MODIFIED);
+	delta->nfiles = 1;
 
 	if (delta->status == GIT_DELTA_DELETED) {
 		delta->old_file.mode = entry->mode;
@@ -123,7 +124,7 @@
 }
 
 static int diff_delta__from_two(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_delta_t   status,
 	const git_index_entry *old_entry,
 	uint32_t old_mode,
@@ -140,11 +141,6 @@
 		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
 		return 0;
 
-	if (old_entry->mode == GIT_FILEMODE_COMMIT &&
-		new_entry->mode == GIT_FILEMODE_COMMIT &&
-		DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
-		return 0;
-
 	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
 		uint32_t temp_mode = old_mode;
 		const git_index_entry *temp_entry = old_entry;
@@ -156,6 +152,7 @@
 
 	delta = diff_delta__alloc(diff, status, canonical_path);
 	GITERR_CHECK_ALLOC(delta);
+	delta->nfiles = 2;
 
 	git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
 	delta->old_file.size = old_entry->file_size;
@@ -189,7 +186,7 @@
 }
 
 static git_diff_delta *diff_delta__last_for_item(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_index_entry *item)
 {
 	git_diff_delta *delta = git_vector_last(&diff->deltas);
@@ -247,6 +244,11 @@
 	return str;
 }
 
+const char *git_diff_delta__path(const git_diff_delta *delta)
+{
+	return diff_delta__path(delta);
+}
+
 int git_diff_delta__cmp(const void *a, const void *b)
 {
 	const git_diff_delta *da = a, *db = b;
@@ -261,6 +263,26 @@
 	return val ? val : ((int)da->status - (int)db->status);
 }
 
+GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
+{
+	return delta->old_file.path ?
+		delta->old_file.path : delta->new_file.path;
+}
+
+int git_diff_delta__i2w_cmp(const void *a, const void *b)
+{
+	const git_diff_delta *da = a, *db = b;
+	int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+	return val ? val : ((int)da->status - (int)db->status);
+}
+
+int git_diff_delta__i2w_casecmp(const void *a, const void *b)
+{
+	const git_diff_delta *da = a, *db = b;
+	int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+	return val ? val : ((int)da->status - (int)db->status);
+}
+
 bool git_diff_delta__should_skip(
 	const git_diff_options *opts, const git_diff_delta *delta)
 {
@@ -323,13 +345,13 @@
 	return pfx;
 }
 
-static git_diff_list *diff_list_alloc(
+static git_diff *diff_list_alloc(
 	git_repository *repo,
 	git_iterator *old_iter,
 	git_iterator *new_iter)
 {
 	git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
+	git_diff *diff = git__calloc(1, sizeof(git_diff));
 	if (!diff)
 		return NULL;
 
@@ -343,7 +365,7 @@
 
 	if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
 		git_pool_init(&diff->pool, 1, 0) < 0) {
-		git_diff_list_free(diff);
+		git_diff_free(diff);
 		return NULL;
 	}
 
@@ -351,14 +373,14 @@
 	 * the ignore_case bit set */
 	if (!git_iterator_ignore_case(old_iter) &&
 		!git_iterator_ignore_case(new_iter)) {
-		diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
+		diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
 
 		diff->strcomp    = git__strcmp;
 		diff->strncomp   = git__strncmp;
 		diff->pfxcomp    = git__prefixcmp;
 		diff->entrycomp  = git_index_entry__cmp;
 	} else {
-		diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+		diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
 
 		diff->strcomp    = git__strcasecmp;
 		diff->strncomp   = git__strncasecmp;
@@ -372,7 +394,7 @@
 }
 
 static int diff_list_apply_options(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_diff_options *opts)
 {
 	git_config *cfg;
@@ -382,12 +404,12 @@
 
 	if (opts) {
 		/* copy user options (except case sensitivity info from iterators) */
-		bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
+		bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
 		memcpy(&diff->opts, opts, sizeof(diff->opts));
-		DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
+		DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
 
 		/* initialize pathspec from options */
-		if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0)
+		if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
 			return -1;
 	}
 
@@ -396,7 +418,7 @@
 		diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
 
 	/* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
-	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT))
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
 		diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
 
 	/* load config values that affect diff behavior */
@@ -407,7 +429,7 @@
 		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
 
 	if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val)
-		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT;
 
 	if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
 		!git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val)
@@ -423,10 +445,28 @@
 
 	/* If not given explicit `opts`, check `diff.xyz` configs */
 	if (!opts) {
-		diff->opts.context_lines = config_int(cfg, "diff.context", 3);
+		int context = config_int(cfg, "diff.context", 3);
+		diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3;
 
-		if (config_bool(cfg, "diff.ignoreSubmodules", 0))
-			diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
+		/* add other defaults here */
+	}
+
+	/* Reverse src info if diff is reversed */
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+		git_iterator_type_t tmp_src = diff->old_src;
+		diff->old_src = diff->new_src;
+		diff->new_src = tmp_src;
+	}
+
+	/* if ignore_submodules not explicitly set, check diff config */
+	if (diff->opts.ignore_submodules <= 0) {
+		const char *str;
+
+		if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0)
+			giterr_clear();
+		else if (str != NULL &&
+			git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0)
+			giterr_clear();
 	}
 
 	/* if either prefix is not set, figure out appropriate value */
@@ -454,15 +494,15 @@
 		return -1;
 
 	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
-		const char *swap = diff->opts.old_prefix;
-		diff->opts.old_prefix = diff->opts.new_prefix;
-		diff->opts.new_prefix = swap;
+		const char *tmp_prefix = diff->opts.old_prefix;
+		diff->opts.old_prefix  = diff->opts.new_prefix;
+		diff->opts.new_prefix  = tmp_prefix;
 	}
 
 	return 0;
 }
 
-static void diff_list_free(git_diff_list *diff)
+static void diff_list_free(git_diff *diff)
 {
 	git_diff_delta *delta;
 	unsigned int i;
@@ -473,14 +513,14 @@
 	}
 	git_vector_free(&diff->deltas);
 
-	git_pathspec_free(&diff->pathspec);
+	git_pathspec__vfree(&diff->pathspec);
 	git_pool_clear(&diff->pool);
 
 	git__memzero(diff, sizeof(*diff));
 	git__free(diff);
 }
 
-void git_diff_list_free(git_diff_list *diff)
+void git_diff_free(git_diff *diff)
 {
 	if (!diff)
 		return;
@@ -488,7 +528,7 @@
 	GIT_REFCOUNT_DEC(diff, diff_list_free);
 }
 
-void git_diff_list_addref(git_diff_list *diff)
+void git_diff_addref(git_diff *diff)
 {
 	GIT_REFCOUNT_INC(diff);
 }
@@ -541,21 +581,21 @@
 		giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
 		result = -1;
 	} else {
-		git_vector filters = GIT_VECTOR_INIT;
+		git_filter_list *fl = NULL;
 
-		result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
-		if (result >= 0) {
+		result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB);
+		if (!result) {
 			int fd = git_futils_open_ro(full_path.ptr);
 			if (fd < 0)
 				result = fd;
 			else {
 				result = git_odb__hashfd_filtered(
-					oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
+					oid, fd, (size_t)size, GIT_OBJ_BLOB, fl);
 				p_close(fd);
 			}
-		}
 
-		git_filters_free(&filters);
+			git_filter_list_free(fl);
+		}
 	}
 
 cleanup:
@@ -584,45 +624,54 @@
 static int maybe_modified_submodule(
 	git_delta_t *status,
 	git_oid *found_oid,
-	git_diff_list *diff,
+	git_diff *diff,
 	diff_in_progress *info)
 {
 	int error = 0;
 	git_submodule *sub;
 	unsigned int sm_status = 0;
-	const git_oid *sm_oid;
+	git_submodule_ignore_t ign = diff->opts.ignore_submodules;
 
 	*status = GIT_DELTA_UNMODIFIED;
 
-	if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) &&
-		!(error = git_submodule_lookup(
-			  &sub, diff->repo, info->nitem->path)) &&
-		git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL &&
-		!(error = git_submodule_status(&sm_status, sub)))
-	{
-		/* check IS_WD_UNMODIFIED because this case is only used
-		 * when the new side of the diff is the working directory
-		 */
-		if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
-			*status = GIT_DELTA_MODIFIED;
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
+		ign == GIT_SUBMODULE_IGNORE_ALL)
+		return 0;
 
-		/* grab OID while we are here */
-		if (git_oid_iszero(&info->nitem->oid) &&
-			(sm_oid = git_submodule_wd_id(sub)) != NULL)
-			git_oid_cpy(found_oid, sm_oid);
+	if ((error = git_submodule_lookup(
+			&sub, diff->repo, info->nitem->path)) < 0) {
+
+		/* GIT_EEXISTS means dir with .git in it was found - ignore it */
+		if (error == GIT_EEXISTS) {
+			giterr_clear();
+			error = 0;
+		}
+		return error;
 	}
 
-	/* GIT_EEXISTS means a dir with .git in it was found - ignore it */
-	if (error == GIT_EEXISTS) {
-		giterr_clear();
-		error = 0;
-	}
+	if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
+		return 0;
 
-	return error;
+	if ((error = git_submodule__status(
+			&sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
+		return error;
+
+	/* check IS_WD_UNMODIFIED because this case is only used
+	 * when the new side of the diff is the working directory
+	 */
+	if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
+		*status = GIT_DELTA_MODIFIED;
+
+	/* now that we have a HEAD OID, check if HEAD moved */
+	if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
+		!git_oid_equal(&info->oitem->oid, found_oid))
+		*status = GIT_DELTA_MODIFIED;
+
+	return 0;
 }
 
 static int maybe_modified(
-	git_diff_list *diff,
+	git_diff *diff,
 	diff_in_progress *info)
 {
 	git_oid noid;
@@ -634,11 +683,11 @@
 	bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
 	const char *matched_pathspec;
 
-	if (!git_pathspec_match_path(
+	if (!git_pathspec__match(
 			&diff->pathspec, oitem->path,
 			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
-			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
-			&matched_pathspec))
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
+			&matched_pathspec, NULL))
 		return 0;
 
 	memset(&noid, 0, sizeof(noid));
@@ -655,9 +704,8 @@
 		nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
 
 	/* support "assume unchanged" (poorly, b/c we still stat everything) */
-	if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
-		status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
-			GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
+	if ((oitem->flags & GIT_IDXENTRY_VALID) != 0)
+		status = GIT_DELTA_UNMODIFIED;
 
 	/* support "skip worktree" index bit */
 	else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
@@ -719,7 +767,7 @@
 	/* if we got here and decided that the files are modified, but we
 	 * haven't calculated the OID of the new item, then calculate it now
 	 */
-	if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
+	if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) {
 		if (git_oid_iszero(&noid)) {
 			if (git_diff__oid_for_file(diff->repo,
 					nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
@@ -741,7 +789,7 @@
 }
 
 static bool entry_is_prefixed(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_index_entry *item,
 	const git_index_entry *prefix_item)
 {
@@ -758,7 +806,7 @@
 }
 
 static int diff_scan_inside_untracked_dir(
-	git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
+	git_diff *diff, diff_in_progress *info, git_delta_t *delta_type)
 {
 	int error = 0;
 	git_buf base = GIT_BUF_INIT;
@@ -824,7 +872,7 @@
 }
 
 static int handle_unmatched_new_item(
-	git_diff_list *diff, diff_in_progress *info)
+	git_diff *diff, diff_in_progress *info)
 {
 	int error = 0;
 	const git_index_entry *nitem = info->nitem;
@@ -842,7 +890,7 @@
 			git_buf_clear(&info->ignore_prefix);
 	}
 
-	if (S_ISDIR(nitem->mode)) {
+	if (nitem->mode == GIT_FILEMODE_TREE) {
 		bool recurse_into_dir = contains_oitem;
 
 		/* if not already inside an ignored dir, check if this is ignored */
@@ -873,7 +921,7 @@
 		 */
 		if (!recurse_into_dir &&
 			delta_type == GIT_DELTA_UNTRACKED &&
-			DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
+			DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
 		{
 			git_diff_delta *last;
 
@@ -946,6 +994,16 @@
 	else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
 		delta_type = GIT_DELTA_ADDED;
 
+	else if (nitem->mode == GIT_FILEMODE_COMMIT) {
+		git_submodule *sm;
+
+		/* ignore things that are not actual submodules */
+		if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) {
+			giterr_clear();
+			delta_type = GIT_DELTA_IGNORED;
+		}
+	}
+
 	/* Actually create the record for this item if necessary */
 	if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
 		return error;
@@ -969,7 +1027,7 @@
 }
 
 static int handle_unmatched_old_item(
-	git_diff_list *diff, diff_in_progress *info)
+	git_diff *diff, diff_in_progress *info)
 {
 	int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
 	if (error < 0)
@@ -1001,7 +1059,7 @@
 }
 
 static int handle_matched_item(
-	git_diff_list *diff, diff_in_progress *info)
+	git_diff *diff, diff_in_progress *info)
 {
 	int error = 0;
 
@@ -1016,7 +1074,7 @@
 }
 
 int git_diff__from_iterators(
-	git_diff_list **diff_ptr,
+	git_diff **diff_ptr,
 	git_repository *repo,
 	git_iterator *old_iter,
 	git_iterator *new_iter,
@@ -1024,7 +1082,7 @@
 {
 	int error = 0;
 	diff_in_progress info;
-	git_diff_list *diff;
+	git_diff *diff;
 
 	*diff_ptr = NULL;
 
@@ -1037,7 +1095,7 @@
 	git_buf_init(&info.ignore_prefix, 0);
 
 	/* make iterators have matching icase behavior */
-	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
 		if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
 			(error = git_iterator_set_ignore_case(new_iter, true)) < 0)
 			goto cleanup;
@@ -1085,7 +1143,7 @@
 	if (!error)
 		*diff_ptr = diff;
 	else
-		git_diff_list_free(diff);
+		git_diff_free(diff);
 
 	git_buf_free(&info.ignore_prefix);
 
@@ -1102,7 +1160,7 @@
 } while (0)
 
 int git_diff_tree_to_tree(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	git_tree *new_tree,
@@ -1117,7 +1175,7 @@
 	 * currently case insensitive, unless the user explicitly asked
 	 * for case insensitivity
 	 */
-	if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
+	if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
 		iflag = GIT_ITERATOR_IGNORE_CASE;
 
 	DIFF_FROM_ITERATORS(
@@ -1128,8 +1186,19 @@
 	return error;
 }
 
+static int diff_load_index(git_index **index, git_repository *repo)
+{
+	int error = git_repository_index__weakptr(index, repo);
+
+	/* reload the repository index when user did not pass one in */
+	if (!error && git_index_read(*index, false) < 0)
+		giterr_clear();
+
+	return error;
+}
+
 int git_diff_tree_to_index(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	git_index *index,
@@ -1140,7 +1209,7 @@
 
 	assert(diff && repo);
 
-	if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+	if (!index && (error = diff_load_index(&index, repo)) < 0)
 		return error;
 
 	if (index->ignore_case) {
@@ -1157,9 +1226,9 @@
 		git_index__set_ignore_case(index, true);
 
 		if (!error) {
-			git_diff_list *d = *diff;
+			git_diff *d = *diff;
 
-			d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+			d->opts.flags |= GIT_DIFF_IGNORE_CASE;
 			d->strcomp    = git__strcasecmp;
 			d->strncomp   = git__strncasecmp;
 			d->pfxcomp    = git__prefixcmp_icase;
@@ -1174,7 +1243,7 @@
 }
 
 int git_diff_index_to_workdir(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_index *index,
 	const git_diff_options *opts)
@@ -1183,7 +1252,7 @@
 
 	assert(diff && repo);
 
-	if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+	if (!index && (error = diff_load_index(&index, repo)) < 0)
 		return error;
 
 	DIFF_FROM_ITERATORS(
@@ -1195,9 +1264,8 @@
 	return error;
 }
 
-
 int git_diff_tree_to_workdir(
-	git_diff_list **diff,
+	git_diff **diff,
 	git_repository *repo,
 	git_tree *old_tree,
 	const git_diff_options *opts)
@@ -1215,16 +1283,60 @@
 	return error;
 }
 
-size_t git_diff_num_deltas(git_diff_list *diff)
+int git_diff_tree_to_workdir_with_index(
+	git_diff **diff,
+	git_repository *repo,
+	git_tree *old_tree,
+	const git_diff_options *opts)
 {
-	assert(diff);
-	return (size_t)diff->deltas.length;
+	int error = 0;
+	git_diff *d1 = NULL, *d2 = NULL;
+	git_index *index = NULL;
+
+	assert(diff && repo);
+
+	if ((error = diff_load_index(&index, repo)) < 0)
+		return error;
+
+	if (!(error = git_diff_tree_to_index(&d1, repo, old_tree, index, opts)) &&
+		!(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
+		error = git_diff_merge(d1, d2);
+
+	git_diff_free(d2);
+
+	if (error) {
+		git_diff_free(d1);
+		d1 = NULL;
+	}
+
+	*diff = d1;
+	return error;
 }
 
-size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
+int git_diff_options_init(git_diff_options *options, unsigned int version)
+{
+	git_diff_options template = GIT_DIFF_OPTIONS_INIT;
+
+	if (version != template.version) {
+		giterr_set(GITERR_INVALID,
+			"Invalid version %d for git_diff_options", (int)version);
+		return -1;
+	}
+
+	memcpy(options, &template, sizeof(*options));
+	return 0;
+}
+
+size_t git_diff_num_deltas(const git_diff *diff)
+{
+	assert(diff);
+	return diff->deltas.length;
+}
+
+size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
 {
 	size_t i, count = 0;
-	git_diff_delta *delta;
+	const git_diff_delta *delta;
 
 	assert(diff);
 
@@ -1235,9 +1347,20 @@
 	return count;
 }
 
+const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
+{
+	assert(diff);
+	return git_vector_get(&diff->deltas, idx);
+}
+
+int git_diff_is_sorted_icase(const git_diff *diff)
+{
+	return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+}
+
 int git_diff__paired_foreach(
-	git_diff_list *head2idx,
-	git_diff_list *idx2wd,
+	git_diff *head2idx,
+	git_diff *idx2wd,
 	int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
 	void *payload)
 {
@@ -1245,10 +1368,12 @@
 	git_diff_delta *h2i, *i2w;
 	size_t i, j, i_max, j_max;
 	int (*strcomp)(const char *, const char *) = git__strcmp;
-	bool icase_mismatch;
+	bool h2i_icase, i2w_icase, icase_mismatch;
 
 	i_max = head2idx ? head2idx->deltas.length : 0;
 	j_max = idx2wd ? idx2wd->deltas.length : 0;
+	if (!i_max && !j_max)
+		return 0;
 
 	/* At some point, tree-to-index diffs will probably never ignore case,
 	 * even if that isn't true now.  Index-to-workdir diffs may or may not
@@ -1258,24 +1383,35 @@
 	 * Therefore the main thing we need to do here is make sure the diffs
 	 * are traversed in a compatible order.  To do this, we temporarily
 	 * resort a mismatched diff to get the order correct.
+	 *
+	 * In order to traverse renames in the index->workdir, we need to
+	 * ensure that we compare the index name on both sides, so we
+	 * always sort by the old name in the i2w list.
 	 */
-	icase_mismatch =
-		(head2idx != NULL && idx2wd != NULL &&
-		 ((head2idx->opts.flags ^ idx2wd->opts.flags) & GIT_DIFF_DELTAS_ARE_ICASE));
+	h2i_icase = head2idx != NULL &&
+		(head2idx->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
 
-	/* force case-sensitive delta sort */
-	if (icase_mismatch) {
-		if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
-			git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
-			git_vector_sort(&head2idx->deltas);
-		} else {
-			git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__cmp);
-			git_vector_sort(&idx2wd->deltas);
-		}
+	i2w_icase = idx2wd != NULL &&
+		(idx2wd->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+
+	icase_mismatch =
+		(head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
+
+	if (icase_mismatch && h2i_icase) {
+		git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
+		git_vector_sort(&head2idx->deltas);
 	}
-	else if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE)
+
+	if (i2w_icase && !icase_mismatch) {
 		strcomp = git__strcasecmp;
 
+		git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
+		git_vector_sort(&idx2wd->deltas);
+	} else if (idx2wd != NULL) {
+		git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
+		git_vector_sort(&idx2wd->deltas);
+	}
+
 	for (i = 0, j = 0; i < i_max || j < j_max; ) {
 		h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
 		i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
@@ -1299,14 +1435,16 @@
 	}
 
 	/* restore case-insensitive delta sort */
-	if (icase_mismatch) {
-		if (head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) {
-			git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
-			git_vector_sort(&head2idx->deltas);
-		} else {
-			git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__casecmp);
-			git_vector_sort(&idx2wd->deltas);
-		}
+	if (icase_mismatch && h2i_icase) {
+		git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
+		git_vector_sort(&head2idx->deltas);
+	}
+
+	/* restore idx2wd sort by new path */
+	if (idx2wd != NULL) {
+		git_vector_set_cmp(&idx2wd->deltas,
+			i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
+		git_vector_sort(&idx2wd->deltas);
 	}
 
 	return 0;
diff --git a/src/diff.h b/src/diff.h
index 6ef03ee..2c9298a 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -16,13 +16,14 @@
 #include "iterator.h"
 #include "repository.h"
 #include "pool.h"
+#include "odb.h"
 
 #define DIFF_OLD_PREFIX_DEFAULT "a/"
 #define DIFF_NEW_PREFIX_DEFAULT "b/"
 
 enum {
 	GIT_DIFFCAPS_HAS_SYMLINKS     = (1 << 0), /* symlinks on platform? */
-	GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+	GIT_DIFFCAPS_IGNORE_STAT      = (1 << 1), /* use stat? */
 	GIT_DIFFCAPS_TRUST_MODE_BITS  = (1 << 2), /* use st_mode? */
 	GIT_DIFFCAPS_TRUST_CTIME      = (1 << 3), /* use st_ctime? */
 	GIT_DIFFCAPS_USE_DEV          = (1 << 4), /* use st_dev? */
@@ -51,7 +52,7 @@
 
 #define GIT_DIFF__VERBOSE  (1 << 30)
 
-struct git_diff_list {
+struct git_diff {
 	git_refcount     rc;
 	git_repository   *repo;
 	git_diff_options opts;
@@ -71,27 +72,36 @@
 extern void git_diff__cleanup_modes(
 	uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
 
-extern void git_diff_list_addref(git_diff_list *diff);
+extern void git_diff_addref(git_diff *diff);
 
 extern int git_diff_delta__cmp(const void *a, const void *b);
 extern int git_diff_delta__casecmp(const void *a, const void *b);
 
+extern const char *git_diff_delta__path(const git_diff_delta *delta);
+
 extern bool git_diff_delta__should_skip(
 	const git_diff_options *opts, const git_diff_delta *delta);
 
+extern int git_diff_delta__format_file_header(
+	git_buf *out,
+	const git_diff_delta *delta,
+	const char *oldpfx,
+	const char *newpfx,
+	int oid_strlen);
+
 extern int git_diff__oid_for_file(
 	git_repository *, const char *, uint16_t, git_off_t, git_oid *);
 
 extern int git_diff__from_iterators(
-	git_diff_list **diff_ptr,
+	git_diff **diff_ptr,
 	git_repository *repo,
 	git_iterator *old_iter,
 	git_iterator *new_iter,
 	const git_diff_options *opts);
 
 extern int git_diff__paired_foreach(
-	git_diff_list *idx2head,
-	git_diff_list *wd2idx,
+	git_diff *idx2head,
+	git_diff *wd2idx,
 	int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
 	void *payload);
 
@@ -106,5 +116,33 @@
 extern int git_diff_find_similar__calc_similarity(
 	int *score, void *siga, void *sigb, void *payload);
 
+/*
+ * Sometimes a git_diff_file will have a zero size; this attempts to
+ * fill in the size without loading the blob if possible.  If that is
+ * not possible, then it will return the git_odb_object that had to be
+ * loaded and the caller can use it or dispose of it as needed.
+ */
+GIT_INLINE(int) git_diff_file__resolve_zero_size(
+	git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
+{
+	int error;
+	git_odb *odb;
+	size_t len;
+	git_otype type;
+
+	if ((error = git_repository_odb(&odb, repo)) < 0)
+		return error;
+
+	error = git_odb__read_header_or_object(
+		odb_obj, &len, &type, odb, &file->oid);
+
+	git_odb_free(odb);
+
+	if (!error)
+		file->size = (git_off_t)len;
+
+	return error;
+}
+
 #endif
 
diff --git a/src/diff_driver.c b/src/diff_driver.c
index 469be0d..bd5a8fb 100644
--- a/src/diff_driver.c
+++ b/src/diff_driver.c
@@ -187,7 +187,7 @@
 
 	git_buf_truncate(&name, namelen + strlen("diff.."));
 	git_buf_put(&name, "xfuncname", strlen("xfuncname"));
-	if ((error = git_config_get_multivar(
+	if ((error = git_config_get_multivar_foreach(
 			cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
 		if (error != GIT_ENOTFOUND)
 			goto done;
@@ -196,7 +196,7 @@
 
 	git_buf_truncate(&name, namelen + strlen("diff.."));
 	git_buf_put(&name, "funcname", strlen("funcname"));
-	if ((error = git_config_get_multivar(
+	if ((error = git_config_get_multivar_foreach(
 			cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
 		if (error != GIT_ENOTFOUND)
 			goto done;
@@ -373,10 +373,11 @@
 		!ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size))
 		return -1;
 
-	git_buf_truncate(&ctxt->line, (size_t)out_size);
-	git_buf_copy_cstr(out, (size_t)out_size, &ctxt->line);
+	if (out_size > (long)ctxt->line.size)
+		out_size = (long)ctxt->line.size;
+	memcpy(out, ctxt->line.ptr, (size_t)out_size);
 
-	return (long)ctxt->line.size;
+	return out_size;
 }
 
 void git_diff_find_context_init(
diff --git a/src/diff_file.c b/src/diff_file.c
index 9d06daa..a4c8641 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -88,7 +88,7 @@
 
 int git_diff_file_content__init_from_diff(
 	git_diff_file_content *fc,
-	git_diff_list *diff,
+	git_diff *diff,
 	size_t delta_index,
 	bool use_old)
 {
@@ -110,7 +110,7 @@
 		has_data = use_old; break;
 	case GIT_DELTA_UNTRACKED:
 		has_data = !use_old &&
-			(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
+			(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
 		break;
 	case GIT_DELTA_MODIFIED:
 	case GIT_DELTA_COPIED:
@@ -241,19 +241,9 @@
 
 	/* if we don't know size, try to peek at object header first */
 	if (!fc->file->size) {
-		git_odb *odb;
-		size_t len;
-		git_otype type;
-
-		if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
-			error = git_odb__read_header_or_object(
-				&odb_obj, &len, &type, odb, &fc->file->oid);
-			git_odb_free(odb);
-		}
-		if (error)
+		if ((error = git_diff_file__resolve_zero_size(
+				fc->file, &odb_obj, fc->repo)) < 0)
 			return error;
-
-		fc->file->size = len;
 	}
 
 	if (diff_file_content_binary_by_size(fc))
@@ -306,9 +296,9 @@
 	git_diff_file_content *fc, git_buf *path)
 {
 	int error = 0;
-	git_vector filters = GIT_VECTOR_INIT;
-	git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+	git_filter_list *fl = NULL;
 	git_file fd = git_futils_open_ro(git_buf_cstr(path));
+	git_buf raw = GIT_BUF_INIT;
 
 	if (fd < 0)
 		return fd;
@@ -320,41 +310,38 @@
 	if (diff_file_content_binary_by_size(fc))
 		goto cleanup;
 
-	if ((error = git_filters_load(
-			&filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
+	if ((error = git_filter_list_load(
+			&fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
 		goto cleanup;
-	/* error >= is a filter count */
 
-	if (error == 0) {
+	/* if there are no filters, try to mmap the file */
+	if (fl == NULL) {
 		if (!(error = git_futils_mmap_ro(
-				&fc->map, fd, 0, (size_t)fc->file->size)))
+				&fc->map, fd, 0, (size_t)fc->file->size))) {
 			fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
-		else /* fall through to try readbuffer below */
-			giterr_clear();
-	}
-
-	if (error != 0) {
-		error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
-		if (error < 0)
 			goto cleanup;
-
-		if (!filters.length)
-			git_buf_swap(&filtered, &raw);
-		else
-			error = git_filters_apply(&filtered, &raw, &filters);
-
-		if (!error) {
-			fc->map.len  = git_buf_len(&filtered);
-			fc->map.data = git_buf_detach(&filtered);
-			fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
 		}
 
+		/* if mmap failed, fall through to try readbuffer below */
+		giterr_clear();
+	}
+
+	if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
+		git_buf out = GIT_BUF_INIT;
+
+		error = git_filter_list_apply_to_data(&out, fl, &raw);
+
 		git_buf_free(&raw);
-		git_buf_free(&filtered);
+
+		if (!error) {
+			fc->map.len  = out.size;
+			fc->map.data = out.ptr;
+			fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+		}
 	}
 
 cleanup:
-	git_filters_free(&filters);
+	git_filter_list_free(fl);
 	p_close(fd);
 
 	return error;
@@ -417,6 +404,9 @@
 
 void git_diff_file_content__unload(git_diff_file_content *fc)
 {
+	if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
+		return;
+
 	if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
 		git__free(fc->map.data);
 		fc->map.data = "";
diff --git a/src/diff_file.h b/src/diff_file.h
index fb08cca..84bf255 100644
--- a/src/diff_file.h
+++ b/src/diff_file.h
@@ -27,7 +27,7 @@
 
 extern int git_diff_file_content__init_from_diff(
 	git_diff_file_content *fc,
-	git_diff_list *diff,
+	git_diff *diff,
 	size_t delta_index,
 	bool use_old);
 
diff --git a/src/diff_patch.c b/src/diff_patch.c
index 9060d0a..cc49d68 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -10,38 +10,27 @@
 #include "diff_driver.h"
 #include "diff_patch.h"
 #include "diff_xdiff.h"
-
-/* cached information about a single span in a diff */
-typedef struct diff_patch_line diff_patch_line;
-struct diff_patch_line {
-	const char *ptr;
-	size_t len;
-	size_t lines, oldno, newno;
-	char origin;
-};
+#include "fileops.h"
 
 /* cached information about a hunk in a diff */
 typedef struct diff_patch_hunk diff_patch_hunk;
 struct diff_patch_hunk {
-	git_diff_range range;
-	char   header[128];
-	size_t header_len;
+	git_diff_hunk hunk;
 	size_t line_start;
 	size_t line_count;
 };
 
-struct git_diff_patch {
+struct git_patch {
 	git_refcount rc;
-	git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
+	git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
 	git_diff_delta *delta;
 	size_t delta_index;
 	git_diff_file_content ofile;
 	git_diff_file_content nfile;
 	uint32_t flags;
 	git_array_t(diff_patch_hunk) hunks;
-	git_array_t(diff_patch_line) lines;
-	size_t oldno, newno;
-	size_t content_size;
+	git_array_t(git_diff_line)   lines;
+	size_t content_size, context_size, header_size;
 	git_pool flattened;
 };
 
@@ -54,12 +43,13 @@
 	GIT_DIFF_PATCH_FLATTENED   = (1 << 5),
 };
 
-static void diff_output_init(git_diff_output*, const git_diff_options*,
-	git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
+static void diff_output_init(
+	git_diff_output*, const git_diff_options*,
+	git_diff_file_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
 
-static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
+static void diff_output_to_patch(git_diff_output *, git_patch *);
 
-static void diff_patch_update_binary(git_diff_patch *patch)
+static void diff_patch_update_binary(git_patch *patch)
 {
 	if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
 		return;
@@ -73,21 +63,21 @@
 		patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
 }
 
-static void diff_patch_init_common(git_diff_patch *patch)
+static void diff_patch_init_common(git_patch *patch)
 {
 	diff_patch_update_binary(patch);
 
 	if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
-		patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
+		patch->flags |= GIT_DIFF_PATCH_LOADED; /* LOADED but not DIFFABLE */
 
 	patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
 
 	if (patch->diff)
-		git_diff_list_addref(patch->diff);
+		git_diff_addref(patch->diff);
 }
 
 static int diff_patch_init_from_diff(
-	git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
+	git_patch *patch, git_diff *diff, size_t delta_index)
 {
 	int error = 0;
 
@@ -108,12 +98,10 @@
 }
 
 static int diff_patch_alloc_from_diff(
-	git_diff_patch **out,
-	git_diff_list *diff,
-	size_t delta_index)
+	git_patch **out, git_diff *diff, size_t delta_index)
 {
 	int error;
-	git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
+	git_patch *patch = git__calloc(1, sizeof(git_patch));
 	GITERR_CHECK_ALLOC(patch);
 
 	if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
@@ -128,7 +116,7 @@
 	return error;
 }
 
-static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_load(git_patch *patch, git_diff_output *output)
 {
 	int error = 0;
 	bool incomplete_data;
@@ -175,9 +163,12 @@
 			goto cleanup;
 	}
 
-	/* if we were previously missing an oid, update MODIFIED->UNMODIFIED */
+	/* if previously missing an oid, and now that we have it the two sides
+	 * are the same (and not submodules), update MODIFIED -> UNMODIFIED
+	 */
 	if (incomplete_data &&
 		patch->ofile.file->mode == patch->nfile.file->mode &&
+		patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
 		git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) &&
 		patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
 		patch->delta->status = GIT_DELTA_UNMODIFIED;
@@ -203,7 +194,7 @@
 }
 
 static int diff_patch_file_callback(
-	git_diff_patch *patch, git_diff_output *output)
+	git_patch *patch, git_diff_output *output)
 {
 	float progress;
 
@@ -219,13 +210,17 @@
 	return output->error;
 }
 
-static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
+static int diff_patch_generate(git_patch *patch, git_diff_output *output)
 {
 	int error = 0;
 
 	if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
 		return 0;
 
+	/* if we are not looking at the hunks and lines, don't do the diff */
+	if (!output->hunk_cb && !output->data_cb)
+		return 0;
+
 	if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
 		(error = diff_patch_load(patch, output)) < 0)
 		return error;
@@ -240,7 +235,7 @@
 	return error;
 }
 
-static void diff_patch_free(git_diff_patch *patch)
+static void diff_patch_free(git_patch *patch)
 {
 	git_diff_file_content__clear(&patch->ofile);
 	git_diff_file_content__clear(&patch->nfile);
@@ -248,7 +243,7 @@
 	git_array_clear(patch->lines);
 	git_array_clear(patch->hunks);
 
-	git_diff_list_free(patch->diff); /* decrements refcount */
+	git_diff_free(patch->diff); /* decrements refcount */
 	patch->diff = NULL;
 
 	git_pool_clear(&patch->flattened);
@@ -257,7 +252,7 @@
 		git__free(patch);
 }
 
-static int diff_required(git_diff_list *diff, const char *action)
+static int diff_required(git_diff *diff, const char *action)
 {
 	if (diff)
 		return 0;
@@ -266,22 +261,22 @@
 }
 
 int git_diff_foreach(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb data_cb,
+	git_diff_line_cb data_cb,
 	void *payload)
 {
 	int error = 0;
 	git_xdiff_output xo;
 	size_t idx;
-	git_diff_patch patch;
+	git_patch patch;
 
 	if (diff_required(diff, "git_diff_foreach") < 0)
 		return -1;
 
-	diff_output_init((git_diff_output *)&xo,
-		&diff->opts, file_cb, hunk_cb, data_cb, payload);
+	diff_output_init(
+		&xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload);
 	git_xdiff_init(&xo, &diff->opts);
 
 	git_vector_foreach(&diff->deltas, idx, patch.delta) {
@@ -292,12 +287,12 @@
 
 		if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) {
 
-			error = diff_patch_file_callback(&patch, (git_diff_output *)&xo);
+			error = diff_patch_file_callback(&patch, &xo.output);
 
 			if (!error)
-				error = diff_patch_generate(&patch, (git_diff_output *)&xo);
+				error = diff_patch_generate(&patch, &xo.output);
 
-			git_diff_patch_free(&patch);
+			git_patch_free(&patch);
 		}
 
 		if (error < 0)
@@ -310,7 +305,7 @@
 }
 
 typedef struct {
-	git_diff_patch patch;
+	git_patch patch;
 	git_diff_delta delta;
 	char paths[GIT_FLEX_ARRAY];
 } diff_patch_with_delta;
@@ -318,7 +313,7 @@
 static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
 {
 	int error = 0;
-	git_diff_patch *patch = &pd->patch;
+	git_patch *patch = &pd->patch;
 	bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
 	bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
 
@@ -422,7 +417,7 @@
 	const git_diff_options *opts,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb data_cb,
+	git_diff_line_cb data_cb,
 	void *payload)
 {
 	int error = 0;
@@ -433,7 +428,7 @@
 	memset(&xo, 0, sizeof(xo));
 
 	diff_output_init(
-		(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+		&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
 	git_xdiff_init(&xo, opts);
 
 	if (!old_path && new_path)
@@ -444,13 +439,13 @@
 	error = diff_patch_from_blobs(
 		&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
 
-	git_diff_patch_free((git_diff_patch *)&pd);
+	git_patch_free(&pd.patch);
 
 	return error;
 }
 
-int git_diff_patch_from_blobs(
-	git_diff_patch **out,
+int git_patch_from_blobs(
+	git_patch **out,
 	const git_blob *old_blob,
 	const char *old_path,
 	const git_blob *new_blob,
@@ -469,16 +464,16 @@
 
 	memset(&xo, 0, sizeof(xo));
 
-	diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+	diff_output_to_patch(&xo.output, &pd->patch);
 	git_xdiff_init(&xo, opts);
 
 	error = diff_patch_from_blobs(
 		pd, &xo, old_blob, old_path, new_blob, new_path, opts);
 
 	if (!error)
-		*out = (git_diff_patch *)pd;
+		*out = (git_patch *)pd;
 	else
-		git_diff_patch_free((git_diff_patch *)pd);
+		git_patch_free((git_patch *)pd);
 
 	return error;
 }
@@ -534,7 +529,7 @@
 	const git_diff_options *opts,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb data_cb,
+	git_diff_line_cb data_cb,
 	void *payload)
 {
 	int error = 0;
@@ -545,7 +540,7 @@
 	memset(&xo, 0, sizeof(xo));
 
 	diff_output_init(
-		(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+		&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
 	git_xdiff_init(&xo, opts);
 
 	if (!old_path && buf_path)
@@ -556,13 +551,13 @@
 	error = diff_patch_from_blob_and_buffer(
 		&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
 
-	git_diff_patch_free((git_diff_patch *)&pd);
+	git_patch_free(&pd.patch);
 
 	return error;
 }
 
-int git_diff_patch_from_blob_and_buffer(
-	git_diff_patch **out,
+int git_patch_from_blob_and_buffer(
+	git_patch **out,
 	const git_blob *old_blob,
 	const char *old_path,
 	const char *buf,
@@ -582,35 +577,31 @@
 
 	memset(&xo, 0, sizeof(xo));
 
-	diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+	diff_output_to_patch(&xo.output, &pd->patch);
 	git_xdiff_init(&xo, opts);
 
 	error = diff_patch_from_blob_and_buffer(
 		pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
 
 	if (!error)
-		*out = (git_diff_patch *)pd;
+		*out = (git_patch *)pd;
 	else
-		git_diff_patch_free((git_diff_patch *)pd);
+		git_patch_free((git_patch *)pd);
 
 	return error;
 }
 
-int git_diff_get_patch(
-	git_diff_patch **patch_ptr,
-	const git_diff_delta **delta_ptr,
-	git_diff_list *diff,
-	size_t idx)
+int git_patch_from_diff(
+	git_patch **patch_ptr, git_diff *diff, size_t idx)
 {
 	int error = 0;
 	git_xdiff_output xo;
 	git_diff_delta *delta = NULL;
-	git_diff_patch *patch = NULL;
+	git_patch *patch = NULL;
 
 	if (patch_ptr) *patch_ptr = NULL;
-	if (delta_ptr) *delta_ptr = NULL;
 
-	if (diff_required(diff, "git_diff_get_patch") < 0)
+	if (diff_required(diff, "git_patch_from_diff") < 0)
 		return -1;
 
 	delta = git_vector_get(&diff->deltas, idx);
@@ -619,9 +610,6 @@
 		return GIT_ENOTFOUND;
 	}
 
-	if (delta_ptr)
-		*delta_ptr = delta;
-
 	if (git_diff_delta__should_skip(&diff->opts, delta))
 		return 0;
 
@@ -634,13 +622,13 @@
 	if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
 		return error;
 
-	diff_output_to_patch((git_diff_output *)&xo, patch);
+	diff_output_to_patch(&xo.output, patch);
 	git_xdiff_init(&xo, &diff->opts);
 
-	error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
+	error = diff_patch_file_callback(patch, &xo.output);
 
 	if (!error)
-		error = diff_patch_generate(patch, (git_diff_output *)&xo);
+		error = diff_patch_generate(patch, &xo.output);
 
 	if (!error) {
 		/* if cumulative diff size is < 0.5 total size, flatten the patch */
@@ -648,7 +636,7 @@
 	}
 
 	if (error || !patch_ptr)
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	else
 		*patch_ptr = patch;
 
@@ -657,36 +645,36 @@
 	return error;
 }
 
-void git_diff_patch_free(git_diff_patch *patch)
+void git_patch_free(git_patch *patch)
 {
 	if (patch)
 		GIT_REFCOUNT_DEC(patch, diff_patch_free);
 }
 
-const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
+const git_diff_delta *git_patch_get_delta(git_patch *patch)
 {
 	assert(patch);
 	return patch->delta;
 }
 
-size_t git_diff_patch_num_hunks(git_diff_patch *patch)
+size_t git_patch_num_hunks(git_patch *patch)
 {
 	assert(patch);
 	return git_array_size(patch->hunks);
 }
 
-int git_diff_patch_line_stats(
+int git_patch_line_stats(
 	size_t *total_ctxt,
 	size_t *total_adds,
 	size_t *total_dels,
-	const git_diff_patch *patch)
+	const git_patch *patch)
 {
 	size_t totals[3], idx;
 
 	memset(totals, 0, sizeof(totals));
 
 	for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
-		diff_patch_line *line = git_array_get(patch->lines, idx);
+		git_diff_line *line = git_array_get(patch->lines, idx);
 		if (!line)
 			continue;
 
@@ -718,12 +706,10 @@
 	return GIT_ENOTFOUND;
 }
 
-int git_diff_patch_get_hunk(
-	const git_diff_range **range,
-	const char **header,
-	size_t *header_len,
+int git_patch_get_hunk(
+	const git_diff_hunk **out,
 	size_t *lines_in_hunk,
-	git_diff_patch *patch,
+	git_patch *patch,
 	size_t hunk_idx)
 {
 	diff_patch_hunk *hunk;
@@ -732,21 +718,17 @@
 	hunk = git_array_get(patch->hunks, hunk_idx);
 
 	if (!hunk) {
-		if (range) *range = NULL;
-		if (header) *header = NULL;
-		if (header_len) *header_len = 0;
+		if (out) *out = NULL;
 		if (lines_in_hunk) *lines_in_hunk = 0;
 		return diff_error_outofrange("hunk");
 	}
 
-	if (range) *range = &hunk->range;
-	if (header) *header = hunk->header;
-	if (header_len) *header_len = hunk->header_len;
+	if (out) *out = &hunk->hunk;
 	if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
 	return 0;
 }
 
-int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
+int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
 {
 	diff_patch_hunk *hunk;
 	assert(patch);
@@ -756,82 +738,96 @@
 	return (int)hunk->line_count;
 }
 
-int git_diff_patch_get_line_in_hunk(
-	char *line_origin,
-	const char **content,
-	size_t *content_len,
-	int *old_lineno,
-	int *new_lineno,
-	git_diff_patch *patch,
+int git_patch_get_line_in_hunk(
+	const git_diff_line **out,
+	git_patch *patch,
 	size_t hunk_idx,
 	size_t line_of_hunk)
 {
 	diff_patch_hunk *hunk;
-	diff_patch_line *line;
-	const char *thing;
+	git_diff_line *line;
 
 	assert(patch);
 
 	if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
-		thing = "hunk";
-		goto notfound;
+		if (out) *out = NULL;
+		return diff_error_outofrange("hunk");
 	}
 
 	if (line_of_hunk >= hunk->line_count ||
 		!(line = git_array_get(
 			patch->lines, hunk->line_start + line_of_hunk))) {
-		thing = "line";
-		goto notfound;
+		if (out) *out = NULL;
+		return diff_error_outofrange("line");
 	}
 
-	if (line_origin) *line_origin = line->origin;
-	if (content) *content = line->ptr;
-	if (content_len) *content_len = line->len;
-	if (old_lineno) *old_lineno = (int)line->oldno;
-	if (new_lineno) *new_lineno = (int)line->newno;
-
+	if (out) *out = line;
 	return 0;
-
-notfound:
-	if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
-	if (content) *content = NULL;
-	if (content_len) *content_len = 0;
-	if (old_lineno) *old_lineno = -1;
-	if (new_lineno) *new_lineno = -1;
-
-	return diff_error_outofrange(thing);
 }
 
-git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
+size_t git_patch_size(
+	git_patch *patch,
+	int include_context,
+	int include_hunk_headers,
+	int include_file_headers)
+{
+	size_t out;
+
+	assert(patch);
+
+	out = patch->content_size;
+
+	if (!include_context)
+		out -= patch->context_size;
+
+	if (include_hunk_headers)
+		out += patch->header_size;
+
+	if (include_file_headers) {
+		git_buf file_header = GIT_BUF_INIT;
+
+		if (git_diff_delta__format_file_header(
+				&file_header, patch->delta, NULL, NULL, 0) < 0)
+			giterr_clear();
+		else
+			out += git_buf_len(&file_header);
+
+		git_buf_free(&file_header);
+	}
+
+	return out;
+}
+
+git_diff *git_patch__diff(git_patch *patch)
 {
 	return patch->diff;
 }
 
-git_diff_driver *git_diff_patch__driver(git_diff_patch *patch)
+git_diff_driver *git_patch__driver(git_patch *patch)
 {
 	/* ofile driver is representative for whole patch */
 	return patch->ofile.driver;
 }
 
-void git_diff_patch__old_data(
-	char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__old_data(
+	char **ptr, size_t *len, git_patch *patch)
 {
 	*ptr = patch->ofile.map.data;
 	*len = patch->ofile.map.len;
 }
 
-void git_diff_patch__new_data(
-	char **ptr, size_t *len, git_diff_patch *patch)
+void git_patch__new_data(
+	char **ptr, size_t *len, git_patch *patch)
 {
 	*ptr = patch->nfile.map.data;
 	*len = patch->nfile.map.len;
 }
 
-int git_diff_patch__invoke_callbacks(
-	git_diff_patch *patch,
+int git_patch__invoke_callbacks(
+	git_patch *patch,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
+	git_diff_line_cb line_cb,
 	void *payload)
 {
 	int error = 0;
@@ -846,18 +842,16 @@
 	for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
 		diff_patch_hunk *h = git_array_get(patch->hunks, i);
 
-		error = hunk_cb(
-			patch->delta, &h->range, h->header, h->header_len, payload);
+		error = hunk_cb(patch->delta, &h->hunk, payload);
 
 		if (!line_cb)
 			continue;
 
 		for (j = 0; !error && j < h->line_count; ++j) {
-			diff_patch_line *l =
+			git_diff_line *l =
 				git_array_get(patch->lines, h->line_start + j);
 
-			error = line_cb(
-				patch->delta, &h->range, l->origin, l->ptr, l->len, payload);
+			error = line_cb(patch->delta, &h->hunk, l, payload);
 		}
 	}
 
@@ -876,12 +870,10 @@
 
 static int diff_patch_hunk_cb(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	const char *header,
-	size_t header_len,
+	const git_diff_hunk *hunk_,
 	void *payload)
 {
-	git_diff_patch *patch = payload;
+	git_patch *patch = payload;
 	diff_patch_hunk *hunk;
 
 	GIT_UNUSED(delta);
@@ -889,36 +881,28 @@
 	hunk = git_array_alloc(patch->hunks);
 	GITERR_CHECK_ALLOC(hunk);
 
-	memcpy(&hunk->range, range, sizeof(hunk->range));
+	memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
 
-	assert(header_len + 1 < sizeof(hunk->header));
-	memcpy(&hunk->header, header, header_len);
-	hunk->header[header_len] = '\0';
-	hunk->header_len = header_len;
+	patch->header_size += hunk_->header_len;
 
 	hunk->line_start = git_array_size(patch->lines);
 	hunk->line_count = 0;
 
-	patch->oldno = range->old_start;
-	patch->newno = range->new_start;
-
 	return 0;
 }
 
 static int diff_patch_line_cb(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin,
-	const char *content,
-	size_t content_len,
+	const git_diff_hunk *hunk_,
+	const git_diff_line *line_,
 	void *payload)
 {
-	git_diff_patch *patch = payload;
+	git_patch *patch = payload;
 	diff_patch_hunk *hunk;
-	diff_patch_line *line;
+	git_diff_line   *line;
 
 	GIT_UNUSED(delta);
-	GIT_UNUSED(range);
+	GIT_UNUSED(hunk_);
 
 	hunk = git_array_last(patch->hunks);
 	GITERR_CHECK_ALLOC(hunk);
@@ -926,39 +910,20 @@
 	line = git_array_alloc(patch->lines);
 	GITERR_CHECK_ALLOC(line);
 
-	line->ptr = content;
-	line->len = content_len;
-	line->origin = line_origin;
-
-	patch->content_size += content_len;
+	memcpy(line, line_, sizeof(*line));
 
 	/* do some bookkeeping so we can provide old/new line numbers */
 
-	for (line->lines = 0; content_len > 0; --content_len) {
-		if (*content++ == '\n')
-			++line->lines;
-	}
+	patch->content_size += line->content_len;
 
-	switch (line_origin) {
-	case GIT_DIFF_LINE_ADDITION:
-	case GIT_DIFF_LINE_DEL_EOFNL:
-		line->oldno = -1;
-		line->newno = patch->newno;
-		patch->newno += line->lines;
-		break;
-	case GIT_DIFF_LINE_DELETION:
-	case GIT_DIFF_LINE_ADD_EOFNL:
-		line->oldno = patch->oldno;
-		line->newno = -1;
-		patch->oldno += line->lines;
-		break;
-	default:
-		line->oldno = patch->oldno;
-		line->newno = patch->newno;
-		patch->oldno += line->lines;
-		patch->newno += line->lines;
-		break;
-	}
+	if (line->origin == GIT_DIFF_LINE_ADDITION ||
+		line->origin == GIT_DIFF_LINE_DELETION)
+		patch->content_size += 1;
+	else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
+		patch->content_size += 1;
+		patch->context_size += line->content_len + 1;
+	} else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+		patch->context_size += line->content_len;
 
 	hunk->line_count++;
 
@@ -970,7 +935,7 @@
 	const git_diff_options *opts,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb data_cb,
+	git_diff_line_cb data_cb,
 	void *payload)
 {
 	GIT_UNUSED(opts);
@@ -983,7 +948,7 @@
 	out->payload = payload;
 }
 
-static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
+static void diff_output_to_patch(git_diff_output *out, git_patch *patch)
 {
 	diff_output_init(
 		out, NULL,
diff --git a/src/diff_patch.h b/src/diff_patch.h
index 56af146..df2ba4c 100644
--- a/src/diff_patch.h
+++ b/src/diff_patch.h
@@ -11,19 +11,20 @@
 #include "diff.h"
 #include "diff_file.h"
 #include "array.h"
+#include "git2/patch.h"
 
-extern git_diff_list *git_diff_patch__diff(git_diff_patch *);
+extern git_diff *git_patch__diff(git_patch *);
 
-extern git_diff_driver *git_diff_patch__driver(git_diff_patch *);
+extern git_diff_driver *git_patch__driver(git_patch *);
 
-extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *);
-extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *);
+extern void git_patch__old_data(char **, size_t *, git_patch *);
+extern void git_patch__new_data(char **, size_t *, git_patch *);
 
-extern int git_diff_patch__invoke_callbacks(
-	git_diff_patch *patch,
+extern int git_patch__invoke_callbacks(
+	git_patch *patch,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
+	git_diff_line_cb line_cb,
 	void *payload);
 
 typedef struct git_diff_output git_diff_output;
@@ -31,7 +32,7 @@
 	/* these callbacks are issued with the diff data */
 	git_diff_file_cb file_cb;
 	git_diff_hunk_cb hunk_cb;
-	git_diff_data_cb data_cb;
+	git_diff_line_cb data_cb;
 	void *payload;
 
 	/* this records the actual error in cases where it may be obscured */
@@ -40,7 +41,7 @@
 	/* this callback is used to do the diff and drive the other callbacks.
 	 * see diff_xdiff.h for how to use this in practice for now.
 	 */
-	int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
+	int (*diff_cb)(git_diff_output *output, git_patch *patch);
 };
 
 #endif
diff --git a/src/diff_print.c b/src/diff_print.c
index 0de5488..b04b115 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -7,26 +7,39 @@
 #include "common.h"
 #include "diff.h"
 #include "diff_patch.h"
-#include "buffer.h"
+#include "fileops.h"
 
 typedef struct {
-	git_diff_list *diff;
-	git_diff_data_cb print_cb;
+	git_diff *diff;
+	git_diff_format_t format;
+	git_diff_line_cb print_cb;
 	void *payload;
 	git_buf *buf;
+	uint32_t flags;
 	int oid_strlen;
+	git_diff_line line;
 } diff_print_info;
 
 static int diff_print_info_init(
 	diff_print_info *pi,
-	git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
+	git_buf *out,
+	git_diff *diff,
+	git_diff_format_t format,
+	git_diff_line_cb cb,
+	void *payload)
 {
 	pi->diff     = diff;
+	pi->format   = format;
 	pi->print_cb = cb;
 	pi->payload  = payload;
 	pi->buf      = out;
 
-	if (!diff || !diff->repo)
+	if (diff)
+		pi->flags = diff->opts.flags;
+
+	if (diff && diff->opts.oid_abbrev != 0)
+		pi->oid_strlen = diff->opts.oid_abbrev;
+	else if (!diff || !diff->repo)
 		pi->oid_strlen = GIT_ABBREV_DEFAULT;
 	else if (git_repository__cvar(
 		&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
@@ -39,6 +52,11 @@
 	else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
 		pi->oid_strlen = GIT_OID_HEXSZ + 1;
 
+	memset(&pi->line, 0, sizeof(pi->line));
+	pi->line.old_lineno = -1;
+	pi->line.new_lineno = -1;
+	pi->line.num_lines  = 1;
+
 	return 0;
 }
 
@@ -46,7 +64,7 @@
 {
 	if (S_ISDIR(mode))
 		return '/';
-	else if (mode & 0100) /* -V536 */
+	else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
 		/* in git, modes are very regular, so we must have 0100755 mode */
 		return '*';
 	else
@@ -77,7 +95,35 @@
 	return GIT_EUSER;
 }
 
-static int diff_print_one_compact(
+static int diff_print_one_name_only(
+	const git_diff_delta *delta, float progress, void *data)
+{
+	diff_print_info *pi = data;
+	git_buf *out = pi->buf;
+
+	GIT_UNUSED(progress);
+
+	if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
+		delta->status == GIT_DELTA_UNMODIFIED)
+		return 0;
+
+	git_buf_clear(out);
+
+	if (git_buf_puts(out, delta->new_file.path) < 0 ||
+		git_buf_putc(out, '\n'))
+		return -1;
+
+	pi->line.origin      = GIT_DIFF_LINE_FILE_HDR;
+	pi->line.content     = git_buf_cstr(out);
+	pi->line.content_len = git_buf_len(out);
+
+	if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
+		return callback_error();
+
+	return 0;
+}
+
+static int diff_print_one_name_status(
 	const git_diff_delta *delta, float progress, void *data)
 {
 	diff_print_info *pi = data;
@@ -88,7 +134,7 @@
 
 	GIT_UNUSED(progress);
 
-	if (code == ' ')
+	if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
 		return 0;
 
 	old_suffix = diff_pick_suffix(delta->old_file.mode);
@@ -98,12 +144,12 @@
 
 	if (delta->old_file.path != delta->new_file.path &&
 		strcomp(delta->old_file.path,delta->new_file.path) != 0)
-		git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
+		git_buf_printf(out, "%c\t%s%c %s%c\n", code,
 			delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
 	else if (delta->old_file.mode != delta->new_file.mode &&
 		delta->old_file.mode != 0 && delta->new_file.mode != 0)
-		git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
-			delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+		git_buf_printf(out, "%c\t%s%c %s%c\n", code,
+			delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
 	else if (old_suffix != ' ')
 		git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
 	else
@@ -112,31 +158,16 @@
 	if (git_buf_oom(out))
 		return -1;
 
-	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
-			git_buf_cstr(out), git_buf_len(out), pi->payload))
+	pi->line.origin      = GIT_DIFF_LINE_FILE_HDR;
+	pi->line.content     = git_buf_cstr(out);
+	pi->line.content_len = git_buf_len(out);
+
+	if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
 		return callback_error();
 
 	return 0;
 }
 
-/* print a git_diff_list to a print callback in compact format */
-int git_diff_print_compact(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
-	void *payload)
-{
-	int error;
-	git_buf buf = GIT_BUF_INIT;
-	diff_print_info pi;
-
-	if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
-		error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
-
-	git_buf_free(&buf);
-
-	return error;
-}
-
 static int diff_print_one_raw(
 	const git_diff_delta *delta, float progress, void *data)
 {
@@ -147,7 +178,7 @@
 
 	GIT_UNUSED(progress);
 
-	if (code == ' ')
+	if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
 		return 0;
 
 	git_buf_clear(out);
@@ -173,38 +204,23 @@
 	if (git_buf_oom(out))
 		return -1;
 
-	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
-			git_buf_cstr(out), git_buf_len(out), pi->payload))
+	pi->line.origin      = GIT_DIFF_LINE_FILE_HDR;
+	pi->line.content     = git_buf_cstr(out);
+	pi->line.content_len = git_buf_len(out);
+
+	if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
 		return callback_error();
 
 	return 0;
 }
 
-/* print a git_diff_list to a print callback in raw output format */
-int git_diff_print_raw(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
-	void *payload)
+static int diff_print_oid_range(
+	git_buf *out, const git_diff_delta *delta, int oid_strlen)
 {
-	int error;
-	git_buf buf = GIT_BUF_INIT;
-	diff_print_info pi;
-
-	if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
-		error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
-
-	git_buf_free(&buf);
-
-	return error;
-}
-
-static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
-{
-	git_buf *out = pi->buf;
 	char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
 
-	git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
-	git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
+	git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid);
+	git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid);
 
 	/* TODO: Match git diff more closely */
 	if (delta->old_file.mode == delta->new_file.mode) {
@@ -228,36 +244,15 @@
 	return 0;
 }
 
-static int diff_print_patch_file(
-	const git_diff_delta *delta, float progress, void *data)
+static int diff_delta_format_with_paths(
+	git_buf *out,
+	const git_diff_delta *delta,
+	const char *oldpfx,
+	const char *newpfx,
+	const char *template)
 {
-	diff_print_info *pi = data;
-	const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
 	const char *oldpath = delta->old_file.path;
-	const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
 	const char *newpath = delta->new_file.path;
-	uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
-
-	GIT_UNUSED(progress);
-
-	if (S_ISDIR(delta->new_file.mode) ||
-		delta->status == GIT_DELTA_UNMODIFIED ||
-		delta->status == GIT_DELTA_IGNORED ||
-		(delta->status == GIT_DELTA_UNTRACKED &&
-		 (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
-		return 0;
-
-	if (!oldpfx)
-		oldpfx = DIFF_OLD_PREFIX_DEFAULT;
-	if (!newpfx)
-		newpfx = DIFF_NEW_PREFIX_DEFAULT;
-
-	git_buf_clear(pi->buf);
-	git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
-		oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
-
-	if (diff_print_oid_range(pi, delta) < 0)
-		return -1;
 
 	if (git_oid_iszero(&delta->old_file.oid)) {
 		oldpfx = "";
@@ -268,30 +263,83 @@
 		newpath = "/dev/null";
 	}
 
-	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
-		git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
-		git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
-	}
+	return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
+}
 
-	if (git_buf_oom(pi->buf))
+int git_diff_delta__format_file_header(
+	git_buf *out,
+	const git_diff_delta *delta,
+	const char *oldpfx,
+	const char *newpfx,
+	int oid_strlen)
+{
+	if (!oldpfx)
+		oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+	if (!newpfx)
+		newpfx = DIFF_NEW_PREFIX_DEFAULT;
+	if (!oid_strlen)
+		oid_strlen = GIT_ABBREV_DEFAULT + 1;
+
+	git_buf_clear(out);
+
+	git_buf_printf(out, "diff --git %s%s %s%s\n",
+		oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+
+	if (diff_print_oid_range(out, delta, oid_strlen) < 0)
 		return -1;
 
-	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
-			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+		diff_delta_format_with_paths(
+			out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
+
+	return git_buf_oom(out) ? -1 : 0;
+}
+
+static int diff_print_patch_file(
+	const git_diff_delta *delta, float progress, void *data)
+{
+	diff_print_info *pi = data;
+	const char *oldpfx =
+		pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+	const char *newpfx =
+		pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+
+	GIT_UNUSED(progress);
+
+	if (S_ISDIR(delta->new_file.mode) ||
+		delta->status == GIT_DELTA_UNMODIFIED ||
+		delta->status == GIT_DELTA_IGNORED ||
+		(delta->status == GIT_DELTA_UNTRACKED &&
+		 (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
+		return 0;
+
+	if (git_diff_delta__format_file_header(
+			pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
+		return -1;
+
+	pi->line.origin      = GIT_DIFF_LINE_FILE_HDR;
+	pi->line.content     = git_buf_cstr(pi->buf);
+	pi->line.content_len = git_buf_len(pi->buf);
+
+	if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
 		return callback_error();
 
 	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
 		return 0;
 
 	git_buf_clear(pi->buf);
-	git_buf_printf(
-		pi->buf, "Binary files %s%s and %s%s differ\n",
-		oldpfx, oldpath, newpfx, newpath);
-	if (git_buf_oom(pi->buf))
+
+	if (diff_delta_format_with_paths(
+			pi->buf, delta, oldpfx, newpfx,
+			"Binary files %s%s and %s%s differ\n") < 0)
 		return -1;
 
-	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
-			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	pi->line.origin      = GIT_DIFF_LINE_BINARY;
+	pi->line.content     = git_buf_cstr(pi->buf);
+	pi->line.content_len = git_buf_len(pi->buf);
+	pi->line.num_lines   = 1;
+
+	if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
 		return callback_error();
 
 	return 0;
@@ -299,9 +347,7 @@
 
 static int diff_print_patch_hunk(
 	const git_diff_delta *d,
-	const git_diff_range *r,
-	const char *header,
-	size_t header_len,
+	const git_diff_hunk *h,
 	void *data)
 {
 	diff_print_info *pi = data;
@@ -309,12 +355,11 @@
 	if (S_ISDIR(d->new_file.mode))
 		return 0;
 
-	git_buf_clear(pi->buf);
-	if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
-		return -1;
+	pi->line.origin      = GIT_DIFF_LINE_HUNK_HDR;
+	pi->line.content     = h->header;
+	pi->line.content_len = h->header_len;
 
-	if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
-			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	if (pi->print_cb(d, h, &pi->line, pi->payload))
 		return callback_error();
 
 	return 0;
@@ -322,10 +367,8 @@
 
 static int diff_print_patch_line(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin, /* GIT_DIFF_LINE value from above */
-	const char *content,
-	size_t content_len,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
 	void *data)
 {
 	diff_print_info *pi = data;
@@ -333,49 +376,63 @@
 	if (S_ISDIR(delta->new_file.mode))
 		return 0;
 
-	git_buf_clear(pi->buf);
-
-	if (line_origin == GIT_DIFF_LINE_ADDITION ||
-		line_origin == GIT_DIFF_LINE_DELETION ||
-		line_origin == GIT_DIFF_LINE_CONTEXT)
-		git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
-	else if (content_len > 0)
-		git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
-
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-	if (pi->print_cb(delta, range, line_origin,
-			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	if (pi->print_cb(delta, hunk, line, pi->payload))
 		return callback_error();
 
 	return 0;
 }
 
-/* print a git_diff_list to an output callback in patch format */
-int git_diff_print_patch(
-	git_diff_list *diff,
-	git_diff_data_cb print_cb,
+/* print a git_diff to an output callback */
+int git_diff_print(
+	git_diff *diff,
+	git_diff_format_t format,
+	git_diff_line_cb print_cb,
 	void *payload)
 {
 	int error;
 	git_buf buf = GIT_BUF_INIT;
 	diff_print_info pi;
+	git_diff_file_cb print_file = NULL;
+	git_diff_hunk_cb print_hunk = NULL;
+	git_diff_line_cb print_line = NULL;
 
-	if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+	switch (format) {
+	case GIT_DIFF_FORMAT_PATCH:
+		print_file = diff_print_patch_file;
+		print_hunk = diff_print_patch_hunk;
+		print_line = diff_print_patch_line;
+		break;
+	case GIT_DIFF_FORMAT_PATCH_HEADER:
+		print_file = diff_print_patch_file;
+		break;
+	case GIT_DIFF_FORMAT_RAW:
+		print_file = diff_print_one_raw;
+		break;
+	case GIT_DIFF_FORMAT_NAME_ONLY:
+		print_file = diff_print_one_name_only;
+		break;
+	case GIT_DIFF_FORMAT_NAME_STATUS:
+		print_file = diff_print_one_name_status;
+		break;
+	default:
+		giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
+		return -1;
+	}
+
+	if (!(error = diff_print_info_init(
+			&pi, &buf, diff, format, print_cb, payload)))
 		error = git_diff_foreach(
-			diff, diff_print_patch_file, diff_print_patch_hunk,
-			diff_print_patch_line, &pi);
+			diff, print_file, print_hunk, print_line, &pi);
 
 	git_buf_free(&buf);
 
 	return error;
 }
 
-/* print a git_diff_patch to an output callback */
-int git_diff_patch_print(
-	git_diff_patch *patch,
-	git_diff_data_cb print_cb,
+/* print a git_patch to an output callback */
+int git_patch_print(
+	git_patch *patch,
+	git_diff_line_cb print_cb,
 	void *payload)
 {
 	int error;
@@ -385,8 +442,9 @@
 	assert(patch && print_cb);
 
 	if (!(error = diff_print_info_init(
-			&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
-		error = git_diff_patch__invoke_callbacks(
+			&pi, &temp, git_patch__diff(patch),
+			GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
+		error = git_patch__invoke_callbacks(
 			patch, diff_print_patch_file, diff_print_patch_hunk,
 			diff_print_patch_line, &pi);
 
@@ -397,26 +455,30 @@
 
 static int diff_print_to_buffer_cb(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin,
-	const char *content,
-	size_t content_len,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
 	void *payload)
 {
 	git_buf *output = payload;
-	GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
-	return git_buf_put(output, content, content_len);
+	GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+	if (line->origin == GIT_DIFF_LINE_ADDITION ||
+		line->origin == GIT_DIFF_LINE_DELETION ||
+		line->origin == GIT_DIFF_LINE_CONTEXT)
+		git_buf_putc(output, line->origin);
+
+	return git_buf_put(output, line->content, line->content_len);
 }
 
-/* print a git_diff_patch to a string buffer */
-int git_diff_patch_to_str(
+/* print a git_patch to a string buffer */
+int git_patch_to_str(
 	char **string,
-	git_diff_patch *patch)
+	git_patch *patch)
 {
 	int error;
 	git_buf output = GIT_BUF_INIT;
 
-	error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
+	error = git_patch_print(patch, diff_print_to_buffer_cb, &output);
 
 	/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
 	 * meaning a memory allocation failure, so just map to -1...
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 8c4e96e..28a9cc7 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -46,7 +46,9 @@
 }
 
 static git_diff_delta *diff_delta__merge_like_cgit(
-	const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+	const git_diff_delta *a,
+	const git_diff_delta *b,
+	git_pool *pool)
 {
 	git_diff_delta *dup;
 
@@ -96,15 +98,46 @@
 	return dup;
 }
 
-int git_diff_merge(
-	git_diff_list *onto,
-	const git_diff_list *from)
+static git_diff_delta *diff_delta__merge_like_cgit_reversed(
+	const git_diff_delta *a,
+	const git_diff_delta *b,
+	git_pool *pool)
+{
+	git_diff_delta *dup;
+
+	/* reversed version of above logic */
+
+	if (a->status == GIT_DELTA_UNMODIFIED)
+		return diff_delta__dup(b, pool);
+
+	if ((dup = diff_delta__dup(a, pool)) == NULL)
+		return NULL;
+
+	if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED)
+		return dup;
+
+	if (dup->status == GIT_DELTA_DELETED) {
+		if (b->status == GIT_DELTA_ADDED)
+			dup->status = GIT_DELTA_UNMODIFIED;
+	} else {
+		dup->status = b->status;
+	}
+
+	git_oid_cpy(&dup->old_file.oid, &b->old_file.oid);
+	dup->old_file.mode  = b->old_file.mode;
+	dup->old_file.size  = b->old_file.size;
+	dup->old_file.flags = b->old_file.flags;
+
+	return dup;
+}
+
+int git_diff_merge(git_diff *onto, const git_diff *from)
 {
 	int error = 0;
 	git_pool onto_pool;
 	git_vector onto_new;
 	git_diff_delta *delta;
-	bool ignore_case = false;
+	bool ignore_case, reversed;
 	unsigned int i, j;
 
 	assert(onto && from);
@@ -112,26 +145,26 @@
 	if (!from->deltas.length)
 		return 0;
 
+	ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
+	reversed    = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0);
+
+	if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
+		reversed    != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
+		giterr_set(GITERR_INVALID,
+			"Attempt to merge diffs created with conflicting options");
+		return -1;
+	}
+
 	if (git_vector_init(
 			&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
 		git_pool_init(&onto_pool, 1, 0) < 0)
 		return -1;
 
-	if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
-		(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
-	{
-		ignore_case = true;
-
-		/* This function currently only supports merging diff lists that
-		 * are sorted identically. */
-		assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
-				(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
-	}
-
 	for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
 		git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
 		const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
-		int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
+		int cmp = !f ? -1 : !o ? 1 :
+			STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
 
 		if (cmp < 0) {
 			delta = diff_delta__dup(o, &onto_pool);
@@ -140,7 +173,9 @@
 			delta = diff_delta__dup(f, &onto_pool);
 			j++;
 		} else {
-			delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
+			delta = reversed ?
+				diff_delta__merge_like_cgit_reversed(o, f, &onto_pool) :
+				diff_delta__merge_like_cgit(o, f, &onto_pool);
 			i++;
 			j++;
 		}
@@ -160,7 +195,11 @@
 	if (!error) {
 		git_vector_swap(&onto->deltas, &onto_new);
 		git_pool_swap(&onto->pool, &onto_pool);
-		onto->new_src = from->new_src;
+
+		if ((onto->opts.flags & GIT_DIFF_REVERSE) != 0)
+			onto->old_src = from->old_src;
+		else
+			onto->new_src = from->new_src;
 
 		/* prefix strings also come from old pool, so recreate those.*/
 		onto->opts.old_prefix =
@@ -230,9 +269,9 @@
 #define DEFAULT_RENAME_LIMIT 200
 
 static int normalize_find_opts(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_diff_find_options *opts,
-	git_diff_find_options *given)
+	const git_diff_find_options *given)
 {
 	git_config *cfg = NULL;
 
@@ -328,7 +367,7 @@
 }
 
 static int apply_splits_and_deletes(
-	git_diff_list *diff, size_t expected_size, bool actually_split)
+	git_diff *diff, size_t expected_size, bool actually_split)
 {
 	git_vector onto = GIT_VECTOR_INIT;
 	size_t i;
@@ -350,6 +389,7 @@
 				goto on_error;
 
 			deleted->status = GIT_DELTA_DELETED;
+			deleted->nfiles = 1;
 			memset(&deleted->new_file, 0, sizeof(deleted->new_file));
 			deleted->new_file.path = deleted->old_file.path;
 			deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -361,6 +401,7 @@
 				delta->status = GIT_DELTA_UNTRACKED;
 			else
 				delta->status = GIT_DELTA_ADDED;
+			delta->nfiles = 1;
 			memset(&delta->old_file, 0, sizeof(delta->old_file));
 			delta->old_file.path = delta->new_file.path;
 			delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
@@ -402,63 +443,105 @@
 	return -1;
 }
 
-GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
+GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
 {
 	git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
 	return (idx & 1) ? &delta->new_file : &delta->old_file;
 }
 
-static int similarity_calc(
-	git_diff_list *diff,
+typedef struct {
+	size_t idx;
+	git_iterator_type_t src;
+	git_repository *repo;
+	git_diff_file *file;
+	git_buf data;
+	git_odb_object *odb_obj;
+	git_blob *blob;
+} similarity_info;
+
+static int similarity_init(
+	similarity_info *info, git_diff *diff, size_t file_idx)
+{
+	info->idx  = file_idx;
+	info->src  = (file_idx & 1) ? diff->new_src : diff->old_src;
+	info->repo = diff->repo;
+	info->file = similarity_get_file(diff, file_idx);
+	info->odb_obj = NULL;
+	info->blob = NULL;
+	git_buf_init(&info->data, 0);
+
+	if (info->file->size > 0)
+		return 0;
+
+	return git_diff_file__resolve_zero_size(
+		info->file, &info->odb_obj, info->repo);
+}
+
+static int similarity_sig(
+	similarity_info *info,
 	const git_diff_find_options *opts,
-	size_t file_idx,
 	void **cache)
 {
 	int error = 0;
-	git_diff_file *file = similarity_get_file(diff, file_idx);
-	git_iterator_type_t src = (file_idx & 1) ? diff->new_src : diff->old_src;
+	git_diff_file *file = info->file;
 
-	if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
-		git_buf path = GIT_BUF_INIT;
-
-		/* TODO: apply wd-to-odb filters to file data if necessary */
-
+	if (info->src == GIT_ITERATOR_TYPE_WORKDIR) {
 		if ((error = git_buf_joinpath(
-				 &path, git_repository_workdir(diff->repo), file->path)) < 0)
+			&info->data, git_repository_workdir(info->repo), file->path)) < 0)
 			return error;
 
 		/* if path is not a regular file, just skip this item */
-		if (git_path_isfile(path.ptr))
-			error = opts->metric->file_signature(
-				&cache[file_idx], file, path.ptr, opts->metric->payload);
+		if (!git_path_isfile(info->data.ptr))
+			return 0;
 
-		git_buf_free(&path);
-	} else { /* compute hashsig from blob buffer */
-		git_blob *blob = NULL;
-		git_off_t blobsize;
+		/* TODO: apply wd-to-odb filters to file data if necessary */
 
-		/* TODO: add max size threshold a la diff? */
+		error = opts->metric->file_signature(
+			&cache[info->idx], info->file,
+			info->data.ptr, opts->metric->payload);
+	} else {
+		/* if we didn't initially know the size, we might have an odb_obj
+		 * around from earlier, so convert that, otherwise load the blob now
+		 */
+		if (info->odb_obj != NULL)
+			error = git_object__from_odb_object(
+				(git_object **)&info->blob, info->repo,
+				info->odb_obj, GIT_OBJ_BLOB);
+		else
+			error = git_blob_lookup(&info->blob, info->repo, &file->oid);
 
-		if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
+		if (error < 0) {
 			/* if lookup fails, just skip this item in similarity calc */
 			giterr_clear();
-			return 0;
+		} else {
+			size_t sz;
+
+			/* index size may not be actual blob size if filtered */
+			if (file->size != git_blob_rawsize(info->blob))
+				file->size = git_blob_rawsize(info->blob);
+
+			sz = (size_t)(git__is_sizet(file->size) ? file->size : -1);
+
+			error = opts->metric->buffer_signature(
+				&cache[info->idx], info->file,
+				git_blob_rawcontent(info->blob), sz, opts->metric->payload);
 		}
-
-		blobsize = git_blob_rawsize(blob);
-		if (!git__is_sizet(blobsize)) /* ? what to do ? */
-			blobsize = (size_t)-1;
-
-		error = opts->metric->buffer_signature(
-			&cache[file_idx], file, git_blob_rawcontent(blob),
-			(size_t)blobsize, opts->metric->payload);
-
-		git_blob_free(blob);
 	}
 
 	return error;
 }
 
+static void similarity_unload(similarity_info *info)
+{
+	if (info->odb_obj)
+		git_odb_object_free(info->odb_obj);
+
+	if (info->blob)
+		git_blob_free(info->blob);
+	else
+		git_buf_free(&info->data);
+}
+
 #define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
 
 /* - score < 0 means files cannot be compared
@@ -467,7 +550,7 @@
  */
 static int similarity_measure(
 	int *score,
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_diff_find_options *opts,
 	void **cache,
 	size_t a_idx,
@@ -476,6 +559,8 @@
 	git_diff_file *a_file = similarity_get_file(diff, a_idx);
 	git_diff_file *b_file = similarity_get_file(diff, b_idx);
 	bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
+	int error = 0;
+	similarity_info a_info, b_info;
 
 	*score = -1;
 
@@ -510,23 +595,48 @@
 		return 0;
 	}
 
+	memset(&a_info, 0, sizeof(a_info));
+	memset(&b_info, 0, sizeof(b_info));
+
+	/* set up similarity data (will try to update missing file sizes) */
+	if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
+		return error;
+	if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
+		goto cleanup;
+
+	/* check if file sizes are nowhere near each other */
+	if (a_file->size > 127 &&
+		b_file->size > 127 &&
+		(a_file->size > (b_file->size << 3) ||
+		 b_file->size > (a_file->size << 3)))
+		goto cleanup;
+
 	/* update signature cache if needed */
-	if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
-		return -1;
-	if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
-		return -1;
+	if (!cache[a_idx]) {
+		if ((error = similarity_sig(&a_info, opts, cache)) < 0)
+			goto cleanup;
+	}
+	if (!cache[b_idx]) {
+		if ((error = similarity_sig(&b_info, opts, cache)) < 0)
+			goto cleanup;
+	}
 
-	/* some metrics may not wish to process this file (too big / too small) */
-	if (!cache[a_idx] || !cache[b_idx])
-		return 0;
+	/* calculate similarity provided that the metric choose to process
+	 * both the a and b files (some may not if file is too big, etc).
+	 */
+	if (cache[a_idx] && cache[b_idx])
+		error = opts->metric->similarity(
+			score, cache[a_idx], cache[b_idx], opts->metric->payload);
 
-	/* compare signatures */
-	return opts->metric->similarity(
-		score, cache[a_idx], cache[b_idx], opts->metric->payload);
+cleanup:
+	similarity_unload(&a_info);
+	similarity_unload(&b_info);
+
+	return error;
 }
 
 static int calc_self_similarity(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_diff_find_options *opts,
 	size_t delta_idx,
 	void **cache)
@@ -543,7 +653,7 @@
 		return error;
 
 	if (similarity >= 0) {
-		delta->similarity = (uint32_t)similarity;
+		delta->similarity = (uint16_t)similarity;
 		delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
 	}
 
@@ -551,7 +661,7 @@
 }
 
 static bool is_rename_target(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_diff_find_options *opts,
 	size_t delta_idx,
 	void **cache)
@@ -590,11 +700,13 @@
 		return false;
 
 	case GIT_DELTA_UNTRACKED:
-	case GIT_DELTA_IGNORED:
 		if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
 			return false;
 		break;
 
+	case GIT_DELTA_IGNORED:
+		return false;
+
 	default: /* all other status values should be checked */
 		break;
 	}
@@ -604,7 +716,7 @@
 }
 
 static bool is_rename_source(
-	git_diff_list *diff,
+	git_diff *diff,
 	const git_diff_find_options *opts,
 	size_t delta_idx,
 	void **cache)
@@ -674,42 +786,49 @@
 }
 
 GIT_INLINE(void) delta_make_rename(
-	git_diff_delta *to, const git_diff_delta *from, uint32_t similarity)
+	git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
 {
 	to->status     = GIT_DELTA_RENAMED;
 	to->similarity = similarity;
+	to->nfiles     = 2;
 	memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
 	to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
 }
 
 typedef struct {
-	uint32_t idx;
-	uint32_t similarity;
+	size_t   idx;
+	uint16_t similarity;
 } diff_find_match;
 
 int git_diff_find_similar(
-	git_diff_list *diff,
-	git_diff_find_options *given_opts)
+	git_diff *diff,
+	const git_diff_find_options *given_opts)
 {
-	size_t i, j, sigcache_size;
-	int error = 0, similarity;
-	git_diff_delta *from, *to;
+	size_t s, t;
+	int error = 0, result;
+	uint16_t similarity;
+	git_diff_delta *src, *tgt;
 	git_diff_find_options opts;
-	size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0;
+	size_t num_deltas, num_srcs = 0, num_tgts = 0;
+	size_t tried_srcs = 0, tried_tgts = 0;
 	size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
 	void **sigcache; /* cache of similarity metric file signatures */
-	diff_find_match *match_srcs = NULL, *match_tgts = NULL, *best_match;
+	diff_find_match *tgt2src = NULL;
+	diff_find_match *src2tgt = NULL;
+	diff_find_match *tgt2src_copy = NULL;
+	diff_find_match *best_match;
 	git_diff_file swap;
 
 	if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
 		return error;
 
+	num_deltas = diff->deltas.length;
+
 	/* TODO: maybe abort if deltas.length > rename_limit ??? */
-	if (!git__is_uint32(diff->deltas.length))
+	if (!git__is_uint32(num_deltas))
 		return 0;
 
-	sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */
-	sigcache = git__calloc(sigcache_size, sizeof(void *));
+	sigcache = git__calloc(num_deltas * 2, sizeof(void *));
 	GITERR_CHECK_ALLOC(sigcache);
 
 	/* Label rename sources and targets
@@ -717,22 +836,30 @@
 	 * This will also set self-similarity scores for MODIFIED files and
 	 * mark them for splitting if break-rewrites is enabled
 	 */
-	git_vector_foreach(&diff->deltas, i, to) {
-		if (is_rename_source(diff, &opts, i, sigcache))
+	git_vector_foreach(&diff->deltas, t, tgt) {
+		if (is_rename_source(diff, &opts, t, sigcache))
 			++num_srcs;
 
-		if (is_rename_target(diff, &opts, i, sigcache))
+		if (is_rename_target(diff, &opts, t, sigcache))
 			++num_tgts;
+
+		if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
+			num_rewrites++;
 	}
 
 	/* if there are no candidate srcs or tgts, we're done */
 	if (!num_srcs || !num_tgts)
 		goto cleanup;
 
-	match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match));
-	GITERR_CHECK_ALLOC(match_tgts);
-	match_srcs = git__calloc(diff->deltas.length, sizeof(diff_find_match));
-	GITERR_CHECK_ALLOC(match_srcs);
+	src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
+	GITERR_CHECK_ALLOC(src2tgt);
+	tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
+	GITERR_CHECK_ALLOC(tgt2src);
+
+	if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
+		tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
+		GITERR_CHECK_ALLOC(tgt2src_copy);
+	}
 
 	/*
 	 * Find best-fit matches for rename / copy candidates
@@ -741,47 +868,62 @@
 find_best_matches:
 	tried_tgts = num_bumped = 0;
 
-	git_vector_foreach(&diff->deltas, i, to) {
+	git_vector_foreach(&diff->deltas, t, tgt) {
 		/* skip things that are not rename targets */
-		if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+		if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
 			continue;
 
 		tried_srcs = 0;
 
-		git_vector_foreach(&diff->deltas, j, from) {
+		git_vector_foreach(&diff->deltas, s, src) {
 			/* skip things that are not rename sources */
-			if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
+			if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
 				continue;
 
 			/* calculate similarity for this pair and find best match */
-			if (i == j)
-				similarity = -1; /* don't measure self-similarity here */
+			if (s == t)
+				result = -1; /* don't measure self-similarity here */
 			else if ((error = similarity_measure(
-				&similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0)
+				&result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
 				goto cleanup;
 
-			/* if this pairing is better for the src and the tgt, keep it */
-			if (similarity > 0 &&
-				match_tgts[i].similarity < (uint32_t)similarity &&
-				match_srcs[j].similarity < (uint32_t)similarity)
+			if (result < 0)
+				continue;
+			similarity = (uint16_t)result;
+
+			/* is this a better rename? */
+			if (tgt2src[t].similarity < similarity &&
+				src2tgt[s].similarity < similarity)
 			{
-				if (match_tgts[i].similarity > 0) {
-					match_tgts[match_srcs[j].idx].similarity = 0;
-					match_srcs[match_tgts[i].idx].similarity = 0;
-					++num_bumped;
+				/* eject old mapping */
+				if (src2tgt[s].similarity > 0) {
+					tgt2src[src2tgt[s].idx].similarity = 0;
+					num_bumped++;
+				}
+				if (tgt2src[t].similarity > 0) {
+					src2tgt[tgt2src[t].idx].similarity = 0;
+					num_bumped++;
 				}
 
-				match_tgts[i].similarity = (uint32_t)similarity;
-				match_tgts[i].idx = (uint32_t)j;
+				/* write new mapping */
+				tgt2src[t].idx = s;
+				tgt2src[t].similarity = similarity;
+				src2tgt[s].idx = t;
+				src2tgt[s].similarity = similarity;
+			}
 
-				match_srcs[j].similarity = (uint32_t)similarity;
-				match_srcs[j].idx = (uint32_t)i;
+			/* keep best absolute match for copies */
+			if (tgt2src_copy != NULL &&
+				tgt2src_copy[t].similarity < similarity)
+			{
+				tgt2src_copy[t].idx = s;
+				tgt2src_copy[t].similarity = similarity;
 			}
 
 			if (++tried_srcs >= num_srcs)
 				break;
 
-			/* cap on maximum targets we'll examine (per "to" file) */
+			/* cap on maximum targets we'll examine (per "tgt" file) */
 			if (tried_srcs > opts.rename_limit)
 				break;
 		}
@@ -799,18 +941,21 @@
 
 	tried_tgts = 0;
 
-	git_vector_foreach(&diff->deltas, i, to) {
+	git_vector_foreach(&diff->deltas, t, tgt) {
 		/* skip things that are not rename targets */
-		if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+		if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
 			continue;
 
 		/* check if this delta was the target of a similarity */
-		best_match = &match_tgts[i];
-		if (!best_match->similarity)
+		if (tgt2src[t].similarity)
+			best_match = &tgt2src[t];
+		else if (tgt2src_copy && tgt2src_copy[t].similarity)
+			best_match = &tgt2src_copy[t];
+		else
 			continue;
 
-		j = best_match->idx;
-		from = GIT_VECTOR_GET(&diff->deltas, j);
+		s = best_match->idx;
+		src = GIT_VECTOR_GET(&diff->deltas, s);
 
 		/* possible scenarios:
 		 * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
@@ -820,101 +965,114 @@
 		 * 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
 		 */
 
-		if (from->status == GIT_DELTA_DELETED) {
+		if (src->status == GIT_DELTA_DELETED) {
 
-			if (delta_is_new_only(to)) {
+			if (delta_is_new_only(tgt)) {
 
 				if (best_match->similarity < opts.rename_threshold)
 					continue;
 
-				delta_make_rename(to, from, best_match->similarity);
+				delta_make_rename(tgt, src, best_match->similarity);
 
-				from->flags |= GIT_DIFF_FLAG__TO_DELETE;
+				src->flags |= GIT_DIFF_FLAG__TO_DELETE;
 				num_rewrites++;
 			} else {
-				assert(delta_is_split(to));
+				assert(delta_is_split(tgt));
 
 				if (best_match->similarity < opts.rename_from_rewrite_threshold)
 					continue;
 
-				memcpy(&swap, &to->old_file, sizeof(swap));
+				memcpy(&swap, &tgt->old_file, sizeof(swap));
 
-				delta_make_rename(to, from, best_match->similarity);
+				delta_make_rename(tgt, src, best_match->similarity);
 				num_rewrites--;
 
-				from->status = GIT_DELTA_DELETED;
-				memcpy(&from->old_file, &swap, sizeof(from->old_file));
-				memset(&from->new_file, 0, sizeof(from->new_file));
-				from->new_file.path = from->old_file.path;
-				from->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+				assert(src->status == GIT_DELTA_DELETED);
+				memcpy(&src->old_file, &swap, sizeof(src->old_file));
+				memset(&src->new_file, 0, sizeof(src->new_file));
+				src->new_file.path = src->old_file.path;
+				src->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
 
 				num_updates++;
+
+				if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+					/* what used to be at src t is now at src s */
+					tgt2src[src2tgt[t].idx].idx = s;
+				}
 			}
 		}
 
-		else if (delta_is_split(from)) {
+		else if (delta_is_split(src)) {
 
-			if (delta_is_new_only(to)) {
+			if (delta_is_new_only(tgt)) {
 
 				if (best_match->similarity < opts.rename_threshold)
 					continue;
 
-				delta_make_rename(to, from, best_match->similarity);
+				delta_make_rename(tgt, src, best_match->similarity);
 
-				from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
+				src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
 					GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
-				memset(&from->old_file, 0, sizeof(from->old_file));
-				from->old_file.path = from->new_file.path;
-				from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+				src->nfiles = 1;
+				memset(&src->old_file, 0, sizeof(src->old_file));
+				src->old_file.path = src->new_file.path;
+				src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
 
-				from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+				src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
 				num_rewrites--;
 
 				num_updates++;
 			} else {
-				assert(delta_is_split(from));
+				assert(delta_is_split(src));
 
 				if (best_match->similarity < opts.rename_from_rewrite_threshold)
 					continue;
 
-				memcpy(&swap, &to->old_file, sizeof(swap));
+				memcpy(&swap, &tgt->old_file, sizeof(swap));
 
-				delta_make_rename(to, from, best_match->similarity);
+				delta_make_rename(tgt, src, best_match->similarity);
 				num_rewrites--;
 				num_updates++;
 
-				memcpy(&from->old_file, &swap, sizeof(from->old_file));
+				memcpy(&src->old_file, &swap, sizeof(src->old_file));
 
 				/* if we've just swapped the new element into the correct
 				 * place, clear the SPLIT flag
 				 */
-				if (match_tgts[j].idx == i &&
-					match_tgts[j].similarity >
+				if (tgt2src[s].idx == t &&
+					tgt2src[s].similarity >
 					opts.rename_from_rewrite_threshold) {
-
-					from->status     = GIT_DELTA_RENAMED;
-					from->similarity = match_tgts[j].similarity;
-					match_tgts[j].similarity = 0;
-					from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+					src->status     = GIT_DELTA_RENAMED;
+					src->similarity = tgt2src[s].similarity;
+					tgt2src[s].similarity = 0;
+					src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
 					num_rewrites--;
 				}
 				/* otherwise, if we just overwrote a source, update mapping */
-				else if (j > i && match_srcs[i].similarity > 0) {
-					match_tgts[match_srcs[i].idx].idx = j;
+				else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+					/* what used to be at src t is now at src s */
+					tgt2src[src2tgt[t].idx].idx = s;
 				}
 
 				num_updates++;
 			}
 		}
 
-		else if (delta_is_new_only(to)) {
-			if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
-				best_match->similarity < opts.copy_threshold)
+		else if (delta_is_new_only(tgt)) {
+			if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES))
 				continue;
 
-			to->status     = GIT_DELTA_COPIED;
-			to->similarity = best_match->similarity;
-			memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+			if (tgt2src_copy[t].similarity < opts.copy_threshold)
+				continue;
+
+			/* always use best possible source for copy */
+			best_match = &tgt2src_copy[t];
+			src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
+
+			tgt->status     = GIT_DELTA_COPIED;
+			tgt->similarity = best_match->similarity;
+			tgt->nfiles     = 2;
+			memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
 
 			num_updates++;
 		}
@@ -927,15 +1085,17 @@
 	if (num_rewrites > 0 || num_updates > 0)
 		error = apply_splits_and_deletes(
 			diff, diff->deltas.length - num_rewrites,
-			FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
+			FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
+			!FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
 
 cleanup:
-	git__free(match_srcs);
-	git__free(match_tgts);
+	git__free(tgt2src);
+	git__free(src2tgt);
+	git__free(tgt2src_copy);
 
-	for (i = 0; i < sigcache_size; ++i) {
-		if (sigcache[i] != NULL)
-			opts.metric->free_signature(sigcache[i], opts.metric->payload);
+	for (t = 0; t < num_deltas * 2; ++t) {
+		if (sigcache[t] != NULL)
+			opts.metric->free_signature(sigcache[t], opts.metric->payload);
 	}
 	git__free(sigcache);
 
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index 7694fb9..e0bc11f 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -24,26 +24,26 @@
 	return (digits > 0) ? 0 : -1;
 }
 
-static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
+static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
 {
 	/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
 	if (*header != '@')
 		return -1;
-	if (git_xdiff_scan_int(&header, &range->old_start) < 0)
+	if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
 		return -1;
 	if (*header == ',') {
-		if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
+		if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
 			return -1;
 	} else
-		range->old_lines = 1;
-	if (git_xdiff_scan_int(&header, &range->new_start) < 0)
+		hunk->old_lines = 1;
+	if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
 		return -1;
 	if (*header == ',') {
-		if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
+		if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
 			return -1;
 	} else
-		range->new_lines = 1;
-	if (range->old_start < 0 || range->new_start < 0)
+		hunk->new_lines = 1;
+	if (hunk->old_start < 0 || hunk->new_start < 0)
 		return -1;
 
 	return 0;
@@ -51,38 +51,104 @@
 
 typedef struct {
 	git_xdiff_output *xo;
-	git_diff_patch *patch;
-	git_diff_range range;
+	git_patch *patch;
+	git_diff_hunk hunk;
+	int old_lineno, new_lineno;
+	mmfile_t xd_old_data, xd_new_data;
 } git_xdiff_info;
 
+static int diff_update_lines(
+	git_xdiff_info *info,
+	git_diff_line *line,
+	const char *content,
+	size_t content_len)
+{
+	const char *scan = content, *scan_end = content + content_len;
+
+	for (line->num_lines = 0; scan < scan_end; ++scan)
+		if (*scan == '\n')
+			++line->num_lines;
+
+	line->content     = content;
+	line->content_len = content_len;
+
+	/* expect " "/"-"/"+", then data */
+	switch (line->origin) {
+	case GIT_DIFF_LINE_ADDITION:
+	case GIT_DIFF_LINE_DEL_EOFNL:
+		line->old_lineno = -1;
+		line->new_lineno = info->new_lineno;
+		info->new_lineno += (int)line->num_lines;
+		break;
+	case GIT_DIFF_LINE_DELETION:
+	case GIT_DIFF_LINE_ADD_EOFNL:
+		line->old_lineno = info->old_lineno;
+		line->new_lineno = -1;
+		info->old_lineno += (int)line->num_lines;
+		break;
+	case GIT_DIFF_LINE_CONTEXT:
+	case GIT_DIFF_LINE_CONTEXT_EOFNL:
+		line->old_lineno = info->old_lineno;
+		line->new_lineno = info->new_lineno;
+		info->old_lineno += (int)line->num_lines;
+		info->new_lineno += (int)line->num_lines;
+		break;
+	default:
+		giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
+			(unsigned int)line->origin);
+		return -1;
+	}
+
+	return 0;
+}
+
 static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
 {
 	git_xdiff_info *info = priv;
-	git_diff_patch *patch = info->patch;
-	const git_diff_delta *delta = git_diff_patch_delta(patch);
+	git_patch *patch = info->patch;
+	const git_diff_delta *delta = git_patch_get_delta(patch);
 	git_diff_output *output = &info->xo->output;
+	git_diff_line line;
 
 	if (len == 1) {
-		output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
+		output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
 		if (output->error < 0)
 			return output->error;
 
+		info->hunk.header_len = bufs[0].size;
+		if (info->hunk.header_len >= sizeof(info->hunk.header))
+			info->hunk.header_len = sizeof(info->hunk.header) - 1;
+		memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
+		info->hunk.header[info->hunk.header_len] = '\0';
+
 		if (output->hunk_cb != NULL &&
-			output->hunk_cb(delta, &info->range,
-				bufs[0].ptr, bufs[0].size, output->payload))
+			output->hunk_cb(delta, &info->hunk, output->payload))
 			output->error = GIT_EUSER;
+
+		info->old_lineno = info->hunk.old_start;
+		info->new_lineno = info->hunk.new_start;
 	}
 
 	if (len == 2 || len == 3) {
 		/* expect " "/"-"/"+", then data */
-		char origin =
+		line.origin =
 			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
 			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
 			GIT_DIFF_LINE_CONTEXT;
 
-		if (output->data_cb != NULL &&
-			output->data_cb(delta, &info->range,
-				origin, bufs[1].ptr, bufs[1].size, output->payload))
+		if (line.origin == GIT_DIFF_LINE_ADDITION)
+			line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
+		else if (line.origin == GIT_DIFF_LINE_DELETION)
+			line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
+		else
+			line.content_offset = -1;
+
+		output->error = diff_update_lines(
+			info, &line, bufs[1].ptr, bufs[1].size);
+
+		if (!output->error &&
+			output->data_cb != NULL &&
+			output->data_cb(delta, &info->hunk, &line, output->payload))
 			output->error = GIT_EUSER;
 	}
 
@@ -92,26 +158,30 @@
 		 * If we have a '-' and a third buf, then we have removed a line
 		 * with out a newline but added a blank line, so ADD_EOFNL.
 		 */
-		char origin =
+		line.origin =
 			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
 			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
 			GIT_DIFF_LINE_CONTEXT_EOFNL;
 
-		if (output->data_cb != NULL &&
-			output->data_cb(delta, &info->range,
-				origin, bufs[2].ptr, bufs[2].size, output->payload))
+		line.content_offset = -1;
+
+		output->error = diff_update_lines(
+			info, &line, bufs[2].ptr, bufs[2].size);
+
+		if (!output->error &&
+			output->data_cb != NULL &&
+			output->data_cb(delta, &info->hunk, &line, output->payload))
 			output->error = GIT_EUSER;
 	}
 
 	return output->error;
 }
 
-static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
+static int git_xdiff(git_diff_output *output, git_patch *patch)
 {
 	git_xdiff_output *xo = (git_xdiff_output *)output;
 	git_xdiff_info info;
 	git_diff_find_context_payload findctxt;
-	mmfile_t xd_old_data, xd_new_data;
 
 	memset(&info, 0, sizeof(info));
 	info.patch = patch;
@@ -120,7 +190,7 @@
 	xo->callback.priv = &info;
 
 	git_diff_find_context_init(
-		&xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
+		&xo->config.find_func, &findctxt, git_patch__driver(patch));
 	xo->config.find_func_priv = &findctxt;
 
 	if (xo->config.find_func != NULL)
@@ -132,10 +202,10 @@
 	 * updates are needed to xo->params.flags
 	 */
 
-	git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
-	git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
+	git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
+	git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
 
-	xdl_diff(&xd_old_data, &xd_new_data,
+	xdl_diff(&info.xd_old_data, &info.xd_new_data,
 		&xo->params, &xo->config, &xo->callback);
 
 	git_diff_find_context_clear(&findctxt);
@@ -145,7 +215,7 @@
 
 void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
 {
-	uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+	uint32_t flags = opts ? opts->flags : 0;
 
 	xo->output.diff_cb = git_xdiff;
 
@@ -161,6 +231,11 @@
 	if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
 		xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
 
+	if (flags & GIT_DIFF_PATIENCE)
+		xo->params.flags |= XDF_PATIENCE_DIFF;
+	if (flags & GIT_DIFF_MINIMAL)
+		xo->params.flags |= XDF_NEED_MINIMAL;
+
 	memset(&xo->callback, 0, sizeof(xo->callback));
 	xo->callback.outf = git_xdiff_cb;
 }
diff --git a/src/errors.c b/src/errors.c
index e2629f6..d04da4c 100644
--- a/src/errors.c
+++ b/src/errors.c
@@ -112,8 +112,25 @@
 #endif
 }
 
+int giterr_detach(git_error *cpy)
+{
+	git_error *error = GIT_GLOBAL->last_error;
+
+	assert(cpy);
+
+	if (!error)
+		return -1;
+
+	cpy->message = error->message;
+	cpy->klass = error->klass;
+
+	error->message = NULL;
+	giterr_clear();
+
+	return 0;
+}
+
 const git_error *giterr_last(void)
 {
 	return GIT_GLOBAL->last_error;
 }
-
diff --git a/src/fetch.c b/src/fetch.c
index 03fad5f..2765918 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -19,55 +19,47 @@
 #include "repository.h"
 #include "refs.h"
 
-struct filter_payload {
-	git_remote *remote;
-	const git_refspec *spec, *tagspec;
-	git_odb *odb;
-	int found_head;
-};
-
-static int filter_ref__cb(git_remote_head *head, void *payload)
+static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec)
 {
-	struct filter_payload *p = payload;
 	int match = 0;
 
 	if (!git_reference_is_valid_name(head->name))
 		return 0;
 
-	if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
-		p->found_head = 1;
-	else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
 		/*
 		 * If tagopt is --tags, then we only use the default
 		 * tags refspec and ignore the remote's
 		 */
-		if (git_refspec_src_matches(p->tagspec, head->name))
+		if (git_refspec_src_matches(tagspec, head->name))
 			match = 1;
 		else
 			return 0;
-	} else if (git_remote__matching_refspec(p->remote, head->name))
+	} else if (git_remote__matching_refspec(remote, head->name))
 			match = 1;
 
 	if (!match)
 		return 0;
 
 	/* If we have the object, mark it so we don't ask for it */
-	if (git_odb_exists(p->odb, &head->oid))
+	if (git_odb_exists(odb, &head->oid))
 		head->local = 1;
 	else
-		p->remote->need_pack = 1;
+		remote->need_pack = 1;
 
-	return git_vector_insert(&p->remote->refs, head);
+	return git_vector_insert(&remote->refs, head);
 }
 
 static int filter_wants(git_remote *remote)
 {
-	struct filter_payload p;
-	git_refspec tagspec;
-	int error = -1;
+	git_remote_head **heads;
+	git_refspec tagspec, head;
+	int error = 0;
+	git_odb *odb;
+	size_t i, heads_len;
 
 	git_vector_clear(&remote->refs);
-	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+	if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0)
 		return error;
 
 	/*
@@ -76,14 +68,27 @@
 	 * not interested in any particular branch but just the remote's
 	 * HEAD, which will be stored in FETCH_HEAD after the fetch.
 	 */
-	p.tagspec = &tagspec;
-	p.found_head = 0;
-	p.remote = remote;
+	if (remote->active_refspecs.length == 0) {
+		if ((error = git_refspec__parse(&head, "HEAD", true)) < 0)
+			goto cleanup;
 
-	if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+		error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs);
+		git_refspec__free(&head);
+
+		if (error < 0)
+			goto cleanup;
+	}
+
+	if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
 		goto cleanup;
 
-	error = git_remote_ls(remote, filter_ref__cb, &p);
+	if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
+		goto cleanup;
+
+	for (i = 0; i < heads_len; i++) {
+		if ((error = maybe_want(remote, heads[i], odb, &tagspec)) < 0)
+			break;
+	}
 
 cleanup:
 	git_refspec__free(&tagspec);
@@ -106,7 +111,7 @@
 	}
 
 	/* Don't try to negotiate when we don't want anything */
-	if (remote->refs.length == 0 || !remote->need_pack)
+	if (!remote->need_pack)
 		return 0;
 
 	/*
@@ -119,15 +124,13 @@
 		remote->refs.length);
 }
 
-int git_fetch_download_pack(
-	git_remote *remote,
-	git_transfer_progress_callback progress_cb,
-	void *progress_payload)
+int git_fetch_download_pack(git_remote *remote)
 {
 	git_transport *t = remote->transport;
 
 	if(!remote->need_pack)
 		return 0;
 
-	return t->download_pack(t, remote->repo, &remote->stats, progress_cb, progress_payload);
+	return t->download_pack(t, remote->repo, &remote->stats,
+				remote->callbacks.transfer_progress, remote->callbacks.payload);
 }
diff --git a/src/fetch.h b/src/fetch.h
index 059251d..9605da1 100644
--- a/src/fetch.h
+++ b/src/fetch.h
@@ -11,10 +11,7 @@
 
 int git_fetch_negotiate(git_remote *remote);
 
-int git_fetch_download_pack(
-		git_remote *remote,
-		git_transfer_progress_callback progress_cb,
-		void *progress_payload);
+int git_fetch_download_pack(git_remote *remote);
 
 int git_fetch__download_pack(
 		git_transport *t,
diff --git a/src/fetchhead.c b/src/fetchhead.c
index 4dcebb8..67089d1 100644
--- a/src/fetchhead.c
+++ b/src/fetchhead.c
@@ -74,6 +74,7 @@
 {
 	char oid[GIT_OID_HEXSZ + 1];
 	const char *type, *name;
+	int head = 0;
 
 	assert(file && fetchhead_ref);
 
@@ -87,11 +88,16 @@
 		GIT_REFS_TAGS_DIR) == 0) {
 		type = "tag ";
 		name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
+	} else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
+		head = 1;
 	} else {
 		type = "";
 		name = fetchhead_ref->ref_name;
 	}
 
+	if (head)
+		return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
+
 	return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
 		oid,
 		(fetchhead_ref->is_merge) ? "" : "not-for-merge",
@@ -112,7 +118,7 @@
 	if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
 		return -1;
 
-	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE) < 0) {
+	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
 		git_buf_free(&path);
 		return -1;
 	}
@@ -124,7 +130,7 @@
 	git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
 		fetchhead_ref_write(&file, fetchhead_ref);
 
-	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
+	return git_filebuf_commit(&file);
 }
 
 static int fetchhead_ref_parse(
diff --git a/src/filebuf.c b/src/filebuf.c
index 246ae34..9c3dae8 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -10,8 +10,6 @@
 #include "filebuf.h"
 #include "fileops.h"
 
-#define GIT_LOCK_FILE_MODE 0644
-
 static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
 
 enum buferr_t {
@@ -44,7 +42,7 @@
 	}
 }
 
-static int lock_file(git_filebuf *file, int flags)
+static int lock_file(git_filebuf *file, int flags, mode_t mode)
 {
 	if (git_path_exists(file->path_lock) == true) {
 		if (flags & GIT_FILEBUF_FORCE)
@@ -53,20 +51,20 @@
 			giterr_clear(); /* actual OS error code just confuses */
 			giterr_set(GITERR_OS,
 				"Failed to lock file '%s' for writing", file->path_lock);
-			return -1;
+			return GIT_ELOCKED;
 		}
 	}
 
 	/* create path to the file buffer is required */
 	if (flags & GIT_FILEBUF_FORCE) {
 		/* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
-		file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE);
+		file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode);
 	} else {
-		file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE);
+		file->fd = git_futils_creat_locked(file->path_lock, mode);
 	}
 
 	if (file->fd < 0)
-		return -1;
+		return file->fd;
 
 	file->fd_is_open = true;
 
@@ -195,9 +193,9 @@
 	return 0;
 }
 
-int git_filebuf_open(git_filebuf *file, const char *path, int flags)
+int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
 {
-	int compression;
+	int compression, error = -1;
 	size_t path_len;
 
 	/* opening an already open buffer is a programming error;
@@ -255,7 +253,7 @@
 		git_buf tmp_path = GIT_BUF_INIT;
 
 		/* Open the file as temporary for locking */
-		file->fd = git_futils_mktmp(&tmp_path, path);
+		file->fd = git_futils_mktmp(&tmp_path, path, mode);
 
 		if (file->fd < 0) {
 			git_buf_free(&tmp_path);
@@ -282,7 +280,7 @@
 		memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
 
 		/* open the file for locking */
-		if (lock_file(file, flags) < 0)
+		if ((error = lock_file(file, flags, mode)) < 0)
 			goto cleanup;
 	}
 
@@ -290,7 +288,7 @@
 
 cleanup:
 	git_filebuf_cleanup(file);
-	return -1;
+	return error;
 }
 
 int git_filebuf_hash(git_oid *oid, git_filebuf *file)
@@ -309,16 +307,16 @@
 	return 0;
 }
 
-int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
+int git_filebuf_commit_at(git_filebuf *file, const char *path)
 {
 	git__free(file->path_original);
 	file->path_original = git__strdup(path);
 	GITERR_CHECK_ALLOC(file->path_original);
 
-	return git_filebuf_commit(file, mode);
+	return git_filebuf_commit(file);
 }
 
-int git_filebuf_commit(git_filebuf *file, mode_t mode)
+int git_filebuf_commit(git_filebuf *file)
 {
 	/* temporary files cannot be committed */
 	assert(file && file->path_original);
@@ -338,11 +336,6 @@
 
 	file->fd = -1;
 
-	if (p_chmod(file->path_lock, mode)) {
-		giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
-		goto on_error;
-	}
-
 	p_unlink(file->path_original);
 
 	if (p_rename(file->path_lock, file->path_original) < 0) {
diff --git a/src/filebuf.h b/src/filebuf.h
index 823af81..044af54 100644
--- a/src/filebuf.h
+++ b/src/filebuf.h
@@ -77,9 +77,9 @@
 int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
 int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
 
-int git_filebuf_open(git_filebuf *lock, const char *path, int flags);
-int git_filebuf_commit(git_filebuf *lock, mode_t mode);
-int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
+int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
+int git_filebuf_commit(git_filebuf *lock);
+int git_filebuf_commit_at(git_filebuf *lock, const char *path);
 void git_filebuf_cleanup(git_filebuf *lock);
 int git_filebuf_hash(git_oid *oid, git_filebuf *file);
 int git_filebuf_flush(git_filebuf *file);
diff --git a/src/fileops.c b/src/fileops.c
index d5f6acf..5763b37 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -6,6 +6,7 @@
  */
 #include "common.h"
 #include "fileops.h"
+#include "global.h"
 #include <ctype.h>
 #if GIT_WIN32
 #include "win32/findfile.h"
@@ -18,9 +19,12 @@
 		GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
 }
 
-int git_futils_mktmp(git_buf *path_out, const char *filename)
+int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode)
 {
 	int fd;
+	mode_t mask;
+
+	p_umask(mask = p_umask(0));
 
 	git_buf_sets(path_out, filename);
 	git_buf_puts(path_out, "_git2_XXXXXX");
@@ -34,6 +38,12 @@
 		return -1;
 	}
 
+	if (p_chmod(path_out->ptr, (mode & ~mask))) {
+		giterr_set(GITERR_OS,
+			"Failed to set permissions on file '%s'", path_out->ptr);
+		return -1;
+	}
+
 	return fd;
 }
 
@@ -55,22 +65,12 @@
 
 int git_futils_creat_locked(const char *path, const mode_t mode)
 {
-	int fd;
-
-#ifdef GIT_WIN32
-	wchar_t buf[GIT_WIN_PATH];
-
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
-	fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
+	int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC |
 		O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#else
-	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC |
-		O_EXCL | O_BINARY | O_CLOEXEC, mode);
-#endif
 
 	if (fd < 0) {
 		giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
-		return -1;
+		return errno == EEXIST ? GIT_ELOCKED : -1;
 	}
 
 	return fd;
@@ -87,11 +87,8 @@
 int git_futils_open_ro(const char *path)
 {
 	int fd = p_open(path, O_RDONLY);
-	if (fd < 0) {
-		if (errno == ENOENT || errno == ENOTDIR)
-			fd = GIT_ENOTFOUND;
-		giterr_set(GITERR_OS, "Failed to open '%s'", path);
-	}
+	if (fd < 0)
+		return git_path_set_error(errno, path, "open");
 	return fd;
 }
 
@@ -110,7 +107,7 @@
 mode_t git_futils_canonical_mode(mode_t raw_mode)
 {
 	if (S_ISREG(raw_mode))
-		return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
+		return S_IFREG | GIT_PERMS_CANONICAL(raw_mode);
 	else if (S_ISLNK(raw_mode))
 		return S_IFLNK;
 	else if (S_ISGITLINK(raw_mode))
@@ -156,11 +153,16 @@
 	if (updated != NULL)
 		*updated = 0;
 
-	if ((fd = git_futils_open_ro(path)) < 0)
-		return fd;
+	if (p_stat(path, &st) < 0)
+		return git_path_set_error(errno, path, "stat");
 
-	if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
-		p_close(fd);
+
+	if (S_ISDIR(st.st_mode)) {
+		giterr_set(GITERR_INVALID, "requested file is a directory");
+		return GIT_ENOTFOUND;
+	}
+
+	if (!git__is_sizet(st.st_size+1)) {
 		giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
 		return -1;
 	}
@@ -177,7 +179,6 @@
 		changed = true;
 
 	if (!changed) {
-		p_close(fd);
 		return 0;
 	}
 
@@ -186,6 +187,9 @@
 	if (size != NULL)
 		*size = (size_t)st.st_size;
 
+	if ((fd = git_futils_open_ro(path)) < 0)
+		return fd;
+
 	if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
 		p_close(fd);
 		return -1;
@@ -222,6 +226,7 @@
 	if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
 		giterr_set(GITERR_OS, "Could not write to '%s'", path);
 		(void)p_close(fd);
+		return error;
 	}
 
 	if ((error = p_close(fd)) < 0)
@@ -347,12 +352,11 @@
 
 		/* make directory */
 		if (p_mkdir(make_path.ptr, mode) < 0) {
-			int tmp_errno = errno;
+			int tmp_errno = giterr_system_last();
 
 			/* ignore error if directory already exists */
-			if (p_stat(make_path.ptr, &st) < 0 ||
-				!(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
-				errno = tmp_errno;
+			if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+				giterr_system_set(tmp_errno);
 				giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
 				goto done;
 			}
@@ -400,8 +404,11 @@
 	size_t baselen;
 	uint32_t flags;
 	int error;
+	int depth;
 } futils__rmdir_data;
 
+#define FUTILS_MAX_DEPTH 100
+
 static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
 {
 	if (filemsg)
@@ -440,51 +447,60 @@
 
 static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
 {
-	struct stat st;
 	futils__rmdir_data *data = opaque;
+	int error = data->error;
+	struct stat st;
 
-	if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
+	if (data->depth > FUTILS_MAX_DEPTH)
+		error = futils__error_cannot_rmdir(
+			path->ptr, "directory nesting too deep");
+
+	else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
 		if (errno == ENOENT)
-			data->error = 0;
+			error = 0;
 		else if (errno == ENOTDIR) {
 			/* asked to remove a/b/c/d/e and a/b is a normal file */
 			if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
-				data->error = futils__rm_first_parent(path, data->base);
+				error = futils__rm_first_parent(path, data->base);
 			else
 				futils__error_cannot_rmdir(
 					path->ptr, "parent is not directory");
 		}
 		else
-			futils__error_cannot_rmdir(path->ptr, "cannot access");
+			error = git_path_set_error(errno, path->ptr, "rmdir");
 	}
 
 	else if (S_ISDIR(st.st_mode)) {
-		int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
+		data->depth++;
+
+		error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
 		if (error < 0)
 			return (error == GIT_EUSER) ? data->error : error;
 
-		data->error = p_rmdir(path->ptr);
+		data->depth--;
 
-		if (data->error < 0) {
+		if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
+			return data->error;
+
+		if ((error = p_rmdir(path->ptr)) < 0) {
 			if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
 				(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
-				data->error = 0;
+				error = 0;
 			else
-				futils__error_cannot_rmdir(path->ptr, NULL);
+				error = git_path_set_error(errno, path->ptr, "rmdir");
 		}
 	}
 
 	else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
-		data->error = p_unlink(path->ptr);
-
-		if (data->error < 0)
-			futils__error_cannot_rmdir(path->ptr, "cannot be removed");
+		if (p_unlink(path->ptr) < 0)
+			error = git_path_set_error(errno, path->ptr, "remove");
 	}
 
 	else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
-		data->error = futils__error_cannot_rmdir(path->ptr, "still present");
+		error = futils__error_cannot_rmdir(path->ptr, "still present");
 
-	return data->error;
+	data->error = error;
+	return error;
 }
 
 static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
@@ -507,7 +523,7 @@
 			giterr_clear();
 			error = GIT_ITEROVER;
 		} else {
-			futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
+			error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
 		}
 	}
 
@@ -519,7 +535,7 @@
 {
 	int error;
 	git_buf fullpath = GIT_BUF_INIT;
-	futils__rmdir_data data;
+	futils__rmdir_data data = { 0 };
 
 	/* build path and find "root" where we should start calling mkdir */
 	if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
@@ -528,7 +544,6 @@
 	data.base    = base ? base : "";
 	data.baselen = base ? strlen(base) : 0;
 	data.flags   = flags;
-	data.error   = 0;
 
 	error = futils__rmdir_recurs_foreach(&data, &fullpath);
 
@@ -546,46 +561,11 @@
 	return error;
 }
 
-int git_futils_cleanupdir_r(const char *path)
-{
-	int error;
-	git_buf fullpath = GIT_BUF_INIT;
-	futils__rmdir_data data;
-
-	if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
-		goto clean_up;
-
-	data.base    = "";
-	data.baselen = 0;
-	data.flags   = GIT_RMDIR_REMOVE_FILES;
-	data.error   = 0;
-
-	if (!git_path_exists(path)) {
-		giterr_set(GITERR_OS, "Path does not exist: %s" , path);
-		error = GIT_ERROR;
-		goto clean_up;
-	}
-
-	if (!git_path_isdir(path)) {
-		giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
-		error = GIT_ERROR;
-		goto clean_up;
-	}
-
-	error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
-	if (error == GIT_EUSER)
-		error = data.error;
-
-clean_up:
-	git_buf_free(&fullpath);
-	return error;
-}
-
 
 static int git_futils_guess_system_dirs(git_buf *out)
 {
 #ifdef GIT_WIN32
-	return git_win32__find_system_dirs(out);
+	return git_win32__find_system_dirs(out, L"etc\\");
 #else
 	return git_buf_sets(out, "/etc");
 #endif
@@ -617,17 +597,48 @@
 #endif
 }
 
+static int git_futils_guess_template_dirs(git_buf *out)
+{
+#ifdef GIT_WIN32
+	return git_win32__find_system_dirs(out, L"share\\git-core\\templates");
+#else
+	return git_buf_sets(out, "/usr/share/git-core/templates");
+#endif
+}
+
 typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
 
 static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
-	{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
+	{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
 
 static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
 	git_futils_guess_system_dirs,
 	git_futils_guess_global_dirs,
 	git_futils_guess_xdg_dirs,
+	git_futils_guess_template_dirs,
 };
 
+void git_futils_dirs_global_shutdown(void)
+{
+	int i;
+	for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
+		git_buf_free(&git_futils__dirs[i]);
+}
+
+int git_futils_dirs_global_init(void)
+{
+	git_futils_dir_t i;
+	const git_buf *path;
+	int error = 0;
+
+	for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
+		error = git_futils_dirs_get(&path, i);
+
+	git__on_shutdown(git_futils_dirs_global_shutdown);
+
+	return error;
+}
+
 static int git_futils_check_selector(git_futils_dir_t which)
 {
 	if (which < GIT_FUTILS_DIR__MAX)
@@ -707,13 +718,6 @@
 	return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
 }
 
-void git_futils_dirs_free(void)
-{
-	int i;
-	for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
-		git_buf_free(&git_futils__dirs[i]);
-}
-
 static int git_futils_find_in_dirlist(
 	git_buf *path, const char *name, git_futils_dir_t which, const char *label)
 {
@@ -734,7 +738,8 @@
 			continue;
 
 		GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
-		GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
+		if (name)
+			GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
 
 		if (git_path_exists(path->ptr))
 			return 0;
@@ -763,6 +768,12 @@
 		path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
 }
 
+int git_futils_find_template_dir(git_buf *path)
+{
+	return git_futils_find_in_dirlist(
+		path, NULL, GIT_FUTILS_DIR_TEMPLATE, "template");
+}
+
 int git_futils_fake_symlink(const char *old, const char *new)
 {
 	int retcode = GIT_ERROR;
@@ -807,11 +818,8 @@
 		return ifd;
 
 	if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
-		if (errno == ENOENT || errno == ENOTDIR)
-			ofd = GIT_ENOTFOUND;
-		giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
 		p_close(ifd);
-		return ofd;
+		return git_path_set_error(errno, to, "open for writing");
 	}
 
 	return cp_by_fd(ifd, ofd, true);
@@ -850,6 +858,7 @@
 	uint32_t flags;
 	uint32_t mkdir_flags;
 	mode_t dirmode;
+	int error;
 } cp_r_info;
 
 #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
@@ -888,20 +897,22 @@
 		return 0;
 
 	if (git_buf_joinpath(
-			&info->to, info->to_root, from->ptr + info->from_prefix) < 0)
-		return -1;
+			&info->to, info->to_root, from->ptr + info->from_prefix) < 0) {
+		error = -1;
+		goto exit;
+	}
 
-	if (p_lstat(info->to.ptr, &to_st) < 0) {
-		if (errno != ENOENT && errno != ENOTDIR) {
-			giterr_set(GITERR_OS,
-				"Could not access %s while copying files", info->to.ptr);
-			return -1;
-		}
-	} else
+	if (!(error = git_path_lstat(info->to.ptr, &to_st)))
 		exists = true;
+	else if (error != GIT_ENOTFOUND)
+		goto exit;
+	else {
+		giterr_clear();
+		error = 0;
+	}
 
 	if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
-		return error;
+		goto exit;
 
 	if (S_ISDIR(from_st.st_mode)) {
 		mode_t oldmode = info->dirmode;
@@ -915,13 +926,17 @@
 			error = _cp_r_mkdir(info, from);
 
 		/* recurse onto target directory */
-		if (!error && (!exists || S_ISDIR(to_st.st_mode)))
-			error = git_path_direach(from, _cp_r_callback, info);
+		if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
+			error = git_path_direach(from, 0, _cp_r_callback, info);
+
+			if (error == GIT_EUSER)
+				error = info->error;
+		}
 
 		if (oldmode != 0)
 			info->dirmode = oldmode;
 
-		return error;
+		goto exit;
 	}
 
 	if (exists) {
@@ -931,7 +946,8 @@
 		if (p_unlink(info->to.ptr) < 0) {
 			giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
 				info->to.ptr);
-			return -1;
+			error = -1;
+			goto exit;
 		}
 	}
 
@@ -944,7 +960,7 @@
 	/* Make container directory on demand if needed */
 	if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
 		(error = _cp_r_mkdir(info, from)) < 0)
-		return error;
+		goto exit;
 
 	/* make symlink or regular file */
 	if (S_ISLNK(from_st.st_mode))
@@ -953,11 +969,13 @@
 		mode_t usemode = from_st.st_mode;
 
 		if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
-			usemode = (usemode & 0111) ? 0777 : 0666;
+			usemode = GIT_PERMS_FOR_WRITE(usemode);
 
 		error = git_futils_cp(from->ptr, info->to.ptr, usemode);
 	}
 
+exit:
+	info->error = error;
 	return error;
 }
 
@@ -978,6 +996,7 @@
 	info.flags   = flags;
 	info.dirmode = dirmode;
 	info.from_prefix = path.size;
+	info.error = 0;
 	git_buf_init(&info.to, 0);
 
 	/* precalculate mkdir flags */
@@ -999,6 +1018,9 @@
 	git_buf_free(&path);
 	git_buf_free(&info.to);
 
+	if (error == GIT_EUSER)
+		error = info.error;
+
 	return error;
 }
 
diff --git a/src/fileops.h b/src/fileops.h
index f4e059c..636c9b6 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -115,12 +115,7 @@
  * * GIT_RMDIR_EMPTY_PARENTS   - remove containing directories up to base
  *       if removing this item leaves them empty
  * * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
- *
- * The old values translate into the new as follows:
- *
- * * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
- * * GIT_DIRREMOVAL_FILES_AND_DIRS  ~= GIT_RMDIR_REMOVE_FILES
- * * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
+ * * GIT_RMDIR_SKIP_ROOT       - don't remove root directory itself
  */
 typedef enum {
 	GIT_RMDIR_EMPTY_HIERARCHY = 0,
@@ -128,6 +123,7 @@
 	GIT_RMDIR_SKIP_NONEMPTY   = (1 << 1),
 	GIT_RMDIR_EMPTY_PARENTS   = (1 << 2),
 	GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
+	GIT_RMDIR_SKIP_ROOT       = (1 << 4),
 } git_futils_rmdir_flags;
 
 /**
@@ -141,19 +137,11 @@
 extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
 
 /**
- * Remove all files and directories beneath the specified path.
- *
- * @param path Path to the top level directory to process.
- * @return 0 on success; -1 on error.
- */
-extern int git_futils_cleanupdir_r(const char *path);
-
-/**
  * Create and open a temporary file with a `_git2_` suffix.
  * Writes the filename into path_out.
  * @return On success, an open file descriptor, else an error code < 0.
  */
-extern int git_futils_mktmp(git_buf *path_out, const char *filename);
+extern int git_futils_mktmp(git_buf *path_out, const char *filename, mode_t mode);
 
 /**
  * Move a file on the filesystem, create the
@@ -223,9 +211,13 @@
  */
 extern git_off_t git_futils_filesize(git_file fd);
 
+#define GIT_PERMS_IS_EXEC(MODE)		(((MODE) & 0111) != 0)
+#define GIT_PERMS_CANONICAL(MODE)	(GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
+#define GIT_PERMS_FOR_WRITE(MODE)   (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
+
 #define GIT_MODE_PERMS_MASK			0777
-#define GIT_CANONICAL_PERMS(MODE)	(((MODE) & 0100) ? 0755 : 0644)
-#define GIT_MODE_TYPE(MODE)			((MODE) & ~GIT_MODE_PERMS_MASK)
+#define GIT_MODE_TYPE_MASK			0170000
+#define GIT_MODE_TYPE(MODE)			((MODE) & GIT_MODE_TYPE_MASK)
 #define GIT_MODE_ISBLOB(MODE)		(GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
 
 /**
@@ -244,7 +236,7 @@
  * @param out buffer to populate with the mapping information.
  * @param fd open descriptor to configure the mapping from.
  * @param begin first byte to map, this should be page aligned.
- * @param end number of bytes to map.
+ * @param len number of bytes to map.
  * @return
  * - 0 on success;
  * - -1 on error.
@@ -278,7 +270,7 @@
 /**
  * Find a "global" file (i.e. one in a user's home directory).
  *
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
  * @param filename name of file to find in the home directory
  * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
  */
@@ -287,7 +279,7 @@
 /**
  * Find an "XDG" file (i.e. one in user's XDG config path).
  *
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
  * @param filename name of file to find in the home directory
  * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
  */
@@ -296,20 +288,36 @@
 /**
  * Find a "system" file (i.e. one shared for all users of the system).
  *
- * @param pathbuf buffer to write the full path into
+ * @param path buffer to write the full path into
  * @param filename name of file to find in the home directory
  * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
  */
 extern int git_futils_find_system_file(git_buf *path, const char *filename);
 
+/**
+ * Find template directory.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_futils_find_template_dir(git_buf *path);
+
 typedef enum {
 	GIT_FUTILS_DIR_SYSTEM = 0,
 	GIT_FUTILS_DIR_GLOBAL = 1,
 	GIT_FUTILS_DIR_XDG    = 2,
-	GIT_FUTILS_DIR__MAX   = 3,
+	GIT_FUTILS_DIR_TEMPLATE = 3,
+	GIT_FUTILS_DIR__MAX   = 4,
 } git_futils_dir_t;
 
 /**
+ * Configures global data for configuration file search paths.
+ *
+ * @return 0 on success, <0 on failure
+ */
+extern int git_futils_dirs_global_init(void);
+
+/**
  * Get the search path for global/system/xdg files
  *
  * @param out pointer to git_buf containing search path
@@ -343,11 +351,6 @@
 extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
 
 /**
- * Release / reset all search paths
- */
-extern void git_futils_dirs_free(void);
-
-/**
  * Create a "fake" symlink (text file containing the target path).
  *
  * @param new symlink file to be created
@@ -396,4 +399,9 @@
 extern void git_futils_filestamp_set(
 	git_futils_filestamp *tgt, const git_futils_filestamp *src);
 
+/**
+ * Free the configuration file search paths.
+ */
+extern void git_futils_dirs_global_shutdown(void);
+
 #endif /* INCLUDE_fileops_h__ */
diff --git a/src/filter.c b/src/filter.c
index 9f749dc..9f866fe 100644
--- a/src/filter.c
+++ b/src/filter.c
@@ -10,68 +10,594 @@
 #include "hash.h"
 #include "filter.h"
 #include "repository.h"
+#include "global.h"
+#include "git2/sys/filter.h"
 #include "git2/config.h"
 #include "blob.h"
+#include "attr_file.h"
+#include "array.h"
 
-int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+struct git_filter_source {
+	git_repository *repo;
+	const char     *path;
+	git_oid         oid;  /* zero if unknown (which is likely) */
+	uint16_t        filemode; /* zero if unknown */
+	git_filter_mode_t mode;
+};
+
+typedef struct {
+	git_filter *filter;
+	void *payload;
+} git_filter_entry;
+
+struct git_filter_list {
+	git_array_t(git_filter_entry) filters;
+	git_filter_source source;
+	char path[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+	const char *filter_name;
+	git_filter *filter;
+	int priority;
+	int initialized;
+	size_t nattrs, nmatches;
+	char *attrdata;
+	const char *attrs[GIT_FLEX_ARRAY];
+} git_filter_def;
+
+static int filter_def_priority_cmp(const void *a, const void *b)
+{
+	int pa = ((const git_filter_def *)a)->priority;
+	int pb = ((const git_filter_def *)b)->priority;
+	return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
+}
+
+struct filter_registry {
+	git_vector filters;
+};
+
+static struct filter_registry *git__filter_registry = NULL;
+
+static void filter_registry_shutdown(void)
+{
+	struct filter_registry *reg = NULL;
+	size_t pos;
+	git_filter_def *fdef;
+
+	if ((reg = git__swap(git__filter_registry, NULL)) == NULL)
+		return;
+
+	git_vector_foreach(&reg->filters, pos, fdef) {
+		if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+			fdef->filter->shutdown(fdef->filter);
+			fdef->initialized = false;
+		}
+
+		git__free(fdef->attrdata);
+		git__free(fdef);
+	}
+
+	git_vector_free(&reg->filters);
+	git__free(reg);
+}
+
+static int filter_registry_initialize(void)
+{
+	int error = 0;
+	struct filter_registry *reg;
+
+	if (git__filter_registry)
+		return 0;
+
+	reg = git__calloc(1, sizeof(struct filter_registry));
+	GITERR_CHECK_ALLOC(reg);
+
+	if ((error = git_vector_init(
+			&reg->filters, 2, filter_def_priority_cmp)) < 0)
+		goto cleanup;
+
+	reg = git__compare_and_swap(&git__filter_registry, NULL, reg);
+	if (reg != NULL)
+		goto cleanup;
+
+	git__on_shutdown(filter_registry_shutdown);
+
+	/* try to register both default filters */
+	{
+		git_filter *crlf = git_crlf_filter_new();
+		git_filter *ident = git_ident_filter_new();
+
+		if (crlf && git_filter_register(
+				GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0)
+			crlf = NULL;
+		if (ident && git_filter_register(
+				GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
+			ident = NULL;
+
+		if (!crlf || !ident)
+			return -1;
+	}
+
+	return 0;
+
+cleanup:
+	git_vector_free(&reg->filters);
+	git__free(reg);
+	return error;
+}
+
+static int filter_def_scan_attrs(
+	git_buf *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
+{
+	const char *start, *scan = attr_str;
+	int has_eq;
+
+	*nattr = *nmatch = 0;
+
+	if (!scan)
+		return 0;
+
+	while (*scan) {
+		while (git__isspace(*scan)) scan++;
+
+		for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
+			if (*scan == '=')
+				has_eq = 1;
+		}
+
+		if (scan > start) {
+			(*nattr)++;
+			if (has_eq || *start == '-' || *start == '+' || *start == '!')
+				(*nmatch)++;
+
+			if (has_eq)
+				git_buf_putc(attrs, '=');
+			git_buf_put(attrs, start, scan - start);
+			git_buf_putc(attrs, '\0');
+		}
+	}
+
+	return 0;
+}
+
+static void filter_def_set_attrs(git_filter_def *fdef)
+{
+	char *scan = fdef->attrdata;
+	size_t i;
+
+	for (i = 0; i < fdef->nattrs; ++i) {
+		const char *name, *value;
+
+		switch (*scan) {
+		case '=':
+			name = scan + 1;
+			for (scan++; *scan != '='; scan++) /* find '=' */;
+			*scan++ = '\0';
+			value = scan;
+			break;
+		case '-':
+			name = scan + 1; value = git_attr__false; break;
+		case '+':
+			name = scan + 1; value = git_attr__true;  break;
+		case '!':
+			name = scan + 1; value = git_attr__unset; break;
+		default:
+			name = scan;     value = NULL; break;
+		}
+
+		fdef->attrs[i] = name;
+		fdef->attrs[i + fdef->nattrs] = value;
+
+		scan += strlen(scan) + 1;
+	}
+}
+
+static int filter_def_name_key_check(const void *key, const void *fdef)
+{
+	const char *name =
+		fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
+	return name ? git__strcmp(key, name) : -1;
+}
+
+static int filter_def_filter_key_check(const void *key, const void *fdef)
+{
+	const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
+	return (key == filter) ? 0 : -1;
+}
+
+static int filter_registry_find(size_t *pos, const char *name)
+{
+	return git_vector_search2(
+		pos, &git__filter_registry->filters, filter_def_name_key_check, name);
+}
+
+static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
+{
+	git_filter_def *fdef = NULL;
+
+	if (!filter_registry_find(pos, name))
+		fdef = git_vector_get(&git__filter_registry->filters, *pos);
+
+	return fdef;
+}
+
+int git_filter_register(
+	const char *name, git_filter *filter, int priority)
+{
+	git_filter_def *fdef;
+	size_t nattr = 0, nmatch = 0;
+	git_buf attrs = GIT_BUF_INIT;
+
+	if (filter_registry_initialize() < 0)
+		return -1;
+
+	if (!filter_registry_find(NULL, name)) {
+		giterr_set(
+			GITERR_FILTER, "Attempt to reregister existing filter '%s'", name);
+		return GIT_EEXISTS;
+	}
+
+	if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
+		return -1;
+
+	fdef = git__calloc(
+		sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1);
+	GITERR_CHECK_ALLOC(fdef);
+
+	fdef->filter_name = name;
+	fdef->filter      = filter;
+	fdef->priority    = priority;
+	fdef->nattrs      = nattr;
+	fdef->nmatches    = nmatch;
+	fdef->attrdata    = git_buf_detach(&attrs);
+
+	filter_def_set_attrs(fdef);
+
+	if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) {
+		git__free(fdef->attrdata);
+		git__free(fdef);
+		return -1;
+	}
+
+	git_vector_sort(&git__filter_registry->filters);
+	return 0;
+}
+
+int git_filter_unregister(const char *name)
+{
+	size_t pos;
+	git_filter_def *fdef;
+
+	/* cannot unregister default filters */
+	if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
+		giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name);
+		return -1;
+	}
+
+	if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
+		giterr_set(GITERR_FILTER, "Cannot find filter '%s' to unregister", name);
+		return GIT_ENOTFOUND;
+	}
+
+	(void)git_vector_remove(&git__filter_registry->filters, pos);
+
+	if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+		fdef->filter->shutdown(fdef->filter);
+		fdef->initialized = false;
+	}
+
+	git__free(fdef->attrdata);
+	git__free(fdef);
+
+	return 0;
+}
+
+static int filter_initialize(git_filter_def *fdef)
+{
+	int error = 0;
+
+	if (!fdef->initialized &&
+		fdef->filter &&
+		fdef->filter->initialize &&
+		(error = fdef->filter->initialize(fdef->filter)) < 0)
+	{
+		/* auto-unregister if initialize fails */
+		git_filter_unregister(fdef->filter_name);
+		return error;
+	}
+
+	fdef->initialized = true;
+	return 0;
+}
+
+git_filter *git_filter_lookup(const char *name)
+{
+	size_t pos;
+	git_filter_def *fdef;
+
+	if (filter_registry_initialize() < 0)
+		return NULL;
+
+	if ((fdef = filter_registry_lookup(&pos, name)) == NULL)
+		return NULL;
+
+	if (!fdef->initialized && filter_initialize(fdef) < 0)
+		return NULL;
+
+	return fdef->filter;
+}
+
+void git_filter_free(git_filter *filter)
+{
+	git__free(filter);
+}
+
+git_repository *git_filter_source_repo(const git_filter_source *src)
+{
+	return src->repo;
+}
+
+const char *git_filter_source_path(const git_filter_source *src)
+{
+	return src->path;
+}
+
+uint16_t git_filter_source_filemode(const git_filter_source *src)
+{
+	return src->filemode;
+}
+
+const git_oid *git_filter_source_id(const git_filter_source *src)
+{
+	return git_oid_iszero(&src->oid) ? NULL : &src->oid;
+}
+
+git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
+{
+	return src->mode;
+}
+
+static int filter_list_new(
+	git_filter_list **out, const git_filter_source *src)
+{
+	git_filter_list *fl = NULL;
+	size_t pathlen = src->path ? strlen(src->path) : 0;
+
+	fl = git__calloc(1, sizeof(git_filter_list) + pathlen + 1);
+	GITERR_CHECK_ALLOC(fl);
+
+	if (src->path)
+		memcpy(fl->path, src->path, pathlen);
+	fl->source.repo = src->repo;
+	fl->source.path = fl->path;
+	fl->source.mode = src->mode;
+
+	*out = fl;
+	return 0;
+}
+
+static int filter_list_check_attributes(
+	const char ***out, git_filter_def *fdef, const git_filter_source *src)
 {
 	int error;
-
-	if (mode == GIT_FILTER_TO_ODB) {
-		/* Load the CRLF cleanup filter when writing to the ODB */
-		error = git_filter_add__crlf_to_odb(filters, repo, path);
-		if (error < 0)
-			return error;
-	} else {
-		error = git_filter_add__crlf_to_workdir(filters, repo, path);
-		if (error < 0)
-			return error;
-	}
-
-	return (int)filters->length;
-}
-
-void git_filters_free(git_vector *filters)
-{
 	size_t i;
-	git_filter *filter;
+	const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
+	GITERR_CHECK_ALLOC(strs);
 
-	git_vector_foreach(filters, i, filter) {
-		if (filter->do_free != NULL)
-			filter->do_free(filter);
-		else
-			git__free(filter);
-	}
+	error = git_attr_get_many(
+		strs, src->repo, 0, src->path, fdef->nattrs, fdef->attrs);
 
-	git_vector_free(filters);
-}
-
-int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
-{
-	size_t i;
-	unsigned int src;
-	git_buf *dbuffer[2];
-
-	dbuffer[0] = source;
-	dbuffer[1] = dest;
-
-	src = 0;
-
-	if (git_buf_len(source) == 0) {
-		git_buf_clear(dest);
+	/* if no values were found but no matches are needed, it's okay! */
+	if (error == GIT_ENOTFOUND && !fdef->nmatches) {
+		giterr_clear();
+		git__free((void *)strs);
 		return 0;
 	}
 
-	/* Pre-grow the destination buffer to more or less the size
-	 * we expect it to have */
-	if (git_buf_grow(dest, git_buf_len(source)) < 0)
+	for (i = 0; !error && i < fdef->nattrs; ++i) {
+		const char *want = fdef->attrs[fdef->nattrs + i];
+		git_attr_t want_type, found_type;
+
+		if (!want)
+			continue;
+
+		want_type  = git_attr_value(want);
+		found_type = git_attr_value(strs[i]);
+
+		if (want_type != found_type ||
+			(want_type == GIT_ATTR_VALUE_T && strcmp(want, strs[i])))
+			error = GIT_ENOTFOUND;
+	}
+
+	if (error)
+		git__free((void *)strs);
+	else
+		*out = strs;
+
+	return error;
+}
+
+int git_filter_list_new(
+	git_filter_list **out, git_repository *repo, git_filter_mode_t mode)
+{
+	git_filter_source src = { 0 };
+	src.repo = repo;
+	src.path = NULL;
+	src.mode = mode;
+	return filter_list_new(out, &src);
+}
+
+int git_filter_list_load(
+	git_filter_list **filters,
+	git_repository *repo,
+	git_blob *blob, /* can be NULL */
+	const char *path,
+	git_filter_mode_t mode)
+{
+	int error = 0;
+	git_filter_list *fl = NULL;
+	git_filter_source src = { 0 };
+	git_filter_entry *fe;
+	size_t idx;
+	git_filter_def *fdef;
+
+	if (filter_registry_initialize() < 0)
 		return -1;
 
-	for (i = 0; i < filters->length; ++i) {
-		git_filter *filter = git_vector_get(filters, i);
-		unsigned int dst = 1 - src;
+	src.repo = repo;
+	src.path = path;
+	src.mode = mode;
+	if (blob)
+		git_oid_cpy(&src.oid, git_blob_id(blob));
 
-		git_buf_clear(dbuffer[dst]);
+	git_vector_foreach(&git__filter_registry->filters, idx, fdef) {
+		const char **values = NULL;
+		void *payload = NULL;
+
+		if (!fdef || !fdef->filter)
+			continue;
+
+		if (fdef->nattrs > 0) {
+			error = filter_list_check_attributes(&values, fdef, &src);
+			if (error == GIT_ENOTFOUND) {
+				error = 0;
+				continue;
+			} else if (error < 0)
+				break;
+		}
+
+		if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+			break;
+
+		if (fdef->filter->check)
+			error = fdef->filter->check(
+				fdef->filter, &payload, &src, values);
+
+		git__free((void *)values);
+
+		if (error == GIT_PASSTHROUGH)
+			error = 0;
+		else if (error < 0)
+			break;
+		else {
+			if (!fl && (error = filter_list_new(&fl, &src)) < 0)
+				return error;
+
+			fe = git_array_alloc(fl->filters);
+			GITERR_CHECK_ALLOC(fe);
+			fe->filter  = fdef->filter;
+			fe->payload = payload;
+		}
+	}
+
+	if (error && fl != NULL) {
+		git_array_clear(fl->filters);
+		git__free(fl);
+		fl = NULL;
+	}
+
+	*filters = fl;
+	return error;
+}
+
+void git_filter_list_free(git_filter_list *fl)
+{
+	uint32_t i;
+
+	if (!fl)
+		return;
+
+	for (i = 0; i < git_array_size(fl->filters); ++i) {
+		git_filter_entry *fe = git_array_get(fl->filters, i);
+		if (fe->filter->cleanup)
+			fe->filter->cleanup(fe->filter, fe->payload);
+	}
+
+	git_array_clear(fl->filters);
+	git__free(fl);
+}
+
+int git_filter_list_push(
+	git_filter_list *fl, git_filter *filter, void *payload)
+{
+	int error = 0;
+	size_t pos;
+	git_filter_def *fdef;
+	git_filter_entry *fe;
+
+	assert(fl && filter);
+
+	if (git_vector_search2(
+			&pos, &git__filter_registry->filters,
+			filter_def_filter_key_check, filter) < 0) {
+		giterr_set(GITERR_FILTER, "Cannot use an unregistered filter");
+		return -1;
+	}
+
+	fdef = git_vector_get(&git__filter_registry->filters, pos);
+
+	if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+		return error;
+
+	fe = git_array_alloc(fl->filters);
+	GITERR_CHECK_ALLOC(fe);
+	fe->filter  = filter;
+	fe->payload = payload;
+
+	return 0;
+}
+
+size_t git_filter_list_length(const git_filter_list *fl)
+{
+	return fl ? git_array_size(fl->filters) : 0;
+}
+
+static int filter_list_out_buffer_from_raw(
+	git_buf *out, const void *ptr, size_t size)
+{
+	if (git_buf_is_allocated(out))
+		git_buf_free(out);
+
+	if (!size) {
+		git_buf_init(out, 0);
+	} else {
+		out->ptr   = (char *)ptr;
+		out->asize = 0;
+		out->size  = size;
+	}
+
+	return 0;
+}
+
+int git_filter_list_apply_to_data(
+	git_buf *tgt, git_filter_list *fl, git_buf *src)
+{
+	int error = 0;
+	uint32_t i;
+	git_buf *dbuffer[2], local = GIT_BUF_INIT;
+	unsigned int si = 0;
+
+	if (!fl)
+		return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size);
+
+	dbuffer[0] = src;
+	dbuffer[1] = tgt;
+
+	/* if `src` buffer is reallocable, then use it, otherwise copy it */
+	if (!git_buf_is_allocated(src)) {
+		if (git_buf_set(&local, src->ptr, src->size) < 0)
+			return -1;
+		dbuffer[0] = &local;
+	}
+
+	for (i = 0; i < git_array_size(fl->filters); ++i) {
+		unsigned int di = 1 - si;
+		uint32_t fidx = (fl->source.mode == GIT_FILTER_TO_WORKTREE) ?
+			i : git_array_size(fl->filters) - 1 - i;
+		git_filter_entry *fe = git_array_get(fl->filters, fidx);
+
+		dbuffer[di]->size = 0;
 
 		/* Apply the filter from dbuffer[src] to the other buffer;
 		 * if the filtering is canceled by the user mid-filter,
@@ -79,16 +605,72 @@
 		 * of the double buffering (so that the text goes through
 		 * cleanly).
 		 */
-		if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
-			src = dst;
 
-		if (git_buf_oom(dbuffer[dst]))
-			return -1;
+		error = fe->filter->apply(
+			fe->filter, &fe->payload, dbuffer[di], dbuffer[si], &fl->source);
+
+		if (error == GIT_PASSTHROUGH) {
+			/* PASSTHROUGH means filter decided not to process the buffer */
+			error = 0;
+		} else if (!error) {
+			git_buf_shorten(dbuffer[di], 0); /* force NUL termination */
+			si = di; /* swap buffers */
+		} else {
+			tgt->size = 0;
+			return error;
+		}
 	}
 
 	/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
-	if (src != 1)
-		git_buf_swap(dest, source);
+	if (si != 1)
+		git_buf_swap(dbuffer[0], dbuffer[1]);
+
+	git_buf_free(&local); /* don't leak if we allocated locally */
 
 	return 0;
 }
+
+int git_filter_list_apply_to_file(
+	git_buf *out,
+	git_filter_list *filters,
+	git_repository *repo,
+	const char *path)
+{
+	int error;
+	const char *base = repo ? git_repository_workdir(repo) : NULL;
+	git_buf abspath = GIT_BUF_INIT, raw = GIT_BUF_INIT;
+
+	if (!(error = git_path_join_unrooted(&abspath, path, base, NULL)) &&
+		!(error = git_futils_readbuffer(&raw, abspath.ptr)))
+	{
+		error = git_filter_list_apply_to_data(out, filters, &raw);
+
+		git_buf_free(&raw);
+	}
+
+	git_buf_free(&abspath);
+	return error;
+}
+
+int git_filter_list_apply_to_blob(
+	git_buf *out,
+	git_filter_list *filters,
+	git_blob *blob)
+{
+	git_buf in = GIT_BUF_INIT;
+	git_off_t rawsize = git_blob_rawsize(blob);
+
+	if (!git__is_sizet(rawsize)) {
+		giterr_set(GITERR_OS, "Blob is too large to filter");
+		return -1;
+	}
+
+	in.ptr   = (char *)git_blob_rawcontent(blob);
+	in.asize = 0;
+	in.size  = (size_t)rawsize;
+
+	if (filters)
+		git_oid_cpy(&filters->source.oid, git_blob_id(blob));
+
+	return git_filter_list_apply_to_data(out, filters, &in);
+}
diff --git a/src/filter.h b/src/filter.h
index 42a44eb..d0ace0f 100644
--- a/src/filter.h
+++ b/src/filter.h
@@ -8,19 +8,7 @@
 #define INCLUDE_filter_h__
 
 #include "common.h"
-#include "buffer.h"
-#include "git2/odb.h"
-#include "git2/repository.h"
-
-typedef struct git_filter {
-	int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
-	void (*do_free)(struct git_filter *self);
-} git_filter;
-
-typedef enum {
-	GIT_FILTER_TO_WORKTREE,
-	GIT_FILTER_TO_ODB
-} git_filter_mode;
+#include "git2/filter.h"
 
 typedef enum {
 	GIT_CRLF_GUESS = -1,
@@ -31,64 +19,13 @@
 	GIT_CRLF_AUTO,
 } git_crlf_t;
 
-/*
- * FILTER API
- */
-
-/*
- * For any given path in the working directory, fill the `filters`
- * array with the relevant filters that need to be applied.
- *
- * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
- * filters that will be used when checking out a file to the working
- * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
- * a file to the ODB.
- *
- * @param filters Vector where to store all the loaded filters
- * @param repo Repository object that contains `path`
- * @param path Relative path of the file to be filtered
- * @param mode Filtering direction (WT->ODB or ODB->WT)
- * @return the number of filters loaded for the file (0 if the file
- *	doesn't need filtering), or a negative error code
- */
-extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
-
-/*
- * Apply one or more filters to a file.
- *
- * The file must have been loaded as a `git_buf` object. Both the `source`
- * and `dest` buffers are owned by the caller and must be freed once
- * they are no longer needed.
- *
- * NOTE: Because of the double-buffering schema, the `source` buffer that contains
- * the original file may be tampered once the filtering is complete. Regardless, 
- * the `dest` buffer will always contain the final result of the filtering
- *
- * @param dest Buffer to store the result of the filtering
- * @param source Buffer containing the document to filter
- * @param filters A non-empty vector of filters as supplied by `git_filters_load`
- * @return 0 on success, an error code otherwise
- */
-extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
-
-/*
- * Free the `filters` array generated by `git_filters_load`.
- *
- * Note that this frees both the array and its contents. The array will
- * be clean/reusable after this call.
- *
- * @param filters A filters array as supplied by `git_filters_load`
- */
-extern void git_filters_free(git_vector *filters);
+extern void git_filter_free(git_filter *filter);
 
 /*
  * Available filters
  */
 
-/* Strip CRLF, from Worktree to ODB */
-extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
-
-/* Add CRLF, from ODB to worktree */
-extern int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path);
+extern git_filter *git_crlf_filter_new(void);
+extern git_filter *git_ident_filter_new(void);
 
 #endif
diff --git a/src/global.c b/src/global.c
index 2d40ca2..7d39c6f 100644
--- a/src/global.c
+++ b/src/global.c
@@ -14,6 +14,28 @@
 
 git_mutex git__mwindow_mutex;
 
+#define MAX_SHUTDOWN_CB 8
+
+git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
+git_atomic git__n_shutdown_callbacks;
+
+void git__on_shutdown(git_global_shutdown_fn callback)
+{
+	int count = git_atomic_inc(&git__n_shutdown_callbacks);
+	assert(count <= MAX_SHUTDOWN_CB);
+	git__shutdown_callbacks[count - 1] = callback;
+}
+
+static void git__shutdown(void)
+{
+	int pos;
+
+	while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) {
+		if (git__shutdown_callbacks[pos])
+			git__shutdown_callbacks[pos]();
+	}
+}
+
 /**
  * Handle the global state with TLS
  *
@@ -51,47 +73,69 @@
 #if defined(GIT_THREADS) && defined(GIT_WIN32)
 
 static DWORD _tls_index;
-static int _tls_init = 0;
+static DWORD _mutex = 0;
+static DWORD _n_inits = 0;
 
-int git_threads_init(void)
+static int synchronized_threads_init()
 {
 	int error;
 
-	if (_tls_init)
-		return 0;
-
 	_tls_index = TlsAlloc();
 	if (git_mutex_init(&git__mwindow_mutex))
 		return -1;
 
 	/* Initialize any other subsystems that have global state */
 	if ((error = git_hash_global_init()) >= 0)
-		_tls_init = 1;
+		error = git_futils_dirs_global_init();
 
-	if (error == 0)
-		_tls_init = 1;
-
-	GIT_MEMORY_BARRIER;
+	win32_pthread_initialize();
 
 	return error;
 }
 
+int git_threads_init(void)
+{
+	int error = 0;
+
+	/* Enter the lock */
+	while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
+
+	/* Only do work on a 0 -> 1 transition of the refcount */
+	if (1 == ++_n_inits)
+		error = synchronized_threads_init();
+
+	/* Exit the lock */
+	InterlockedExchange(&_mutex, 0);
+
+	return error;
+}
+
+static void synchronized_threads_shutdown()
+{
+	/* Shut down any subsystems that have global state */
+	git__shutdown();
+	TlsFree(_tls_index);
+	git_mutex_free(&git__mwindow_mutex);
+}
+
 void git_threads_shutdown(void)
 {
-	TlsFree(_tls_index);
-	_tls_init = 0;
-	git_mutex_free(&git__mwindow_mutex);
+	/* Enter the lock */
+	while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
 
-	/* Shut down any subsystems that have global state */
-	git_hash_global_shutdown();
-	git_futils_dirs_free();
+	/* Only do work on a 1 -> 0 transition of the refcount */
+	if (0 == --_n_inits)
+		synchronized_threads_shutdown();
+
+	/* Exit the lock */
+	InterlockedExchange(&_mutex, 0);
 }
 
 git_global_st *git__global_state(void)
 {
 	void *ptr;
 
-	assert(_tls_init);
+	assert(_n_inits);
 
 	if ((ptr = TlsGetValue(_tls_index)) != NULL)
 		return ptr;
@@ -108,55 +152,58 @@
 #elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
 
 static pthread_key_t _tls_key;
-static int _tls_init = 0;
+static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
+static git_atomic git__n_inits;
+int init_error = 0;
 
 static void cb__free_status(void *st)
 {
 	git__free(st);
 }
 
-int git_threads_init(void)
+static void init_once(void)
 {
-	int error = 0;
-
-	if (_tls_init)
-		return 0;
-
-	if (git_mutex_init(&git__mwindow_mutex))
-		return -1;
+	if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
+		return;
 	pthread_key_create(&_tls_key, &cb__free_status);
 
 	/* Initialize any other subsystems that have global state */
-	if ((error = git_hash_global_init()) >= 0)
-		_tls_init = 1;
+	if ((init_error = git_hash_global_init()) >= 0)
+		init_error = git_futils_dirs_global_init();
 
 	GIT_MEMORY_BARRIER;
+}
 
-	return error;
+int git_threads_init(void)
+{
+	pthread_once(&_once_init, init_once);
+	git_atomic_inc(&git__n_inits);
+	return init_error;
 }
 
 void git_threads_shutdown(void)
 {
-	if (_tls_init) {
-		void *ptr = pthread_getspecific(_tls_key);
-		pthread_setspecific(_tls_key, NULL);
-		git__free(ptr);
-	}
+	pthread_once_t new_once = PTHREAD_ONCE_INIT;
 
-	pthread_key_delete(_tls_key);
-	_tls_init = 0;
-	git_mutex_free(&git__mwindow_mutex);
+	if (git_atomic_dec(&git__n_inits) > 0) return;
 
 	/* Shut down any subsystems that have global state */
-	git_hash_global_shutdown();
-	git_futils_dirs_free();
+	git__shutdown();
+
+	void *ptr = pthread_getspecific(_tls_key);
+	pthread_setspecific(_tls_key, NULL);
+	git__free(ptr);
+
+	pthread_key_delete(_tls_key);
+	git_mutex_free(&git__mwindow_mutex);
+	_once_init = new_once;
 }
 
 git_global_st *git__global_state(void)
 {
 	void *ptr;
 
-	assert(_tls_init);
+	assert(git__n_inits.val);
 
 	if ((ptr = pthread_getspecific(_tls_key)) != NULL)
 		return ptr;
@@ -176,15 +223,14 @@
 
 int git_threads_init(void)
 {
-	/* noop */ 
+	/* noop */
 	return 0;
 }
 
 void git_threads_shutdown(void)
 {
 	/* Shut down any subsystems that have global state */
-	git_hash_global_shutdown();
-	git_futils_dirs_free();
+	git__shutdown();
 }
 
 git_global_st *git__global_state(void)
diff --git a/src/global.h b/src/global.h
index badbc08..7782503 100644
--- a/src/global.h
+++ b/src/global.h
@@ -21,4 +21,8 @@
 
 #define GIT_GLOBAL (git__global_state())
 
+typedef void (*git_global_shutdown_fn)(void);
+
+extern void git__on_shutdown(git_global_shutdown_fn callback);
+
 #endif
diff --git a/src/hash.h b/src/hash.h
index 5b84898..c47f335 100644
--- a/src/hash.h
+++ b/src/hash.h
@@ -13,8 +13,6 @@
 typedef struct git_hash_ctx git_hash_ctx;
 
 int git_hash_global_init(void);
-void git_hash_global_shutdown(void);
-
 int git_hash_ctx_init(git_hash_ctx *ctx);
 void git_hash_ctx_cleanup(git_hash_ctx *ctx);
 
diff --git a/src/hash/hash_generic.h b/src/hash/hash_generic.h
index 6b60c98..daeb1cd 100644
--- a/src/hash/hash_generic.h
+++ b/src/hash/hash_generic.h
@@ -17,7 +17,6 @@
 };
 
 #define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
 #define git_hash_ctx_init(ctx) git_hash_init(ctx)
 #define git_hash_ctx_cleanup(ctx)
 
diff --git a/src/hash/hash_openssl.h b/src/hash/hash_openssl.h
index f83279a..9a55d47 100644
--- a/src/hash/hash_openssl.h
+++ b/src/hash/hash_openssl.h
@@ -17,7 +17,6 @@
 };
 
 #define git_hash_global_init() 0
-#define git_hash_global_shutdown() /* noop */
 #define git_hash_ctx_init(ctx) git_hash_init(ctx)
 #define git_hash_ctx_cleanup(ctx)
 
diff --git a/src/hash/hash_win32.c b/src/hash/hash_win32.c
index 43d54ca..bb22313 100644
--- a/src/hash/hash_win32.c
+++ b/src/hash/hash_win32.c
@@ -20,33 +20,16 @@
 /* Initialize CNG, if available */
 GIT_INLINE(int) hash_cng_prov_init(void)
 {
-	OSVERSIONINFOEX version_test = {0};
-	DWORD version_test_mask;
-	DWORDLONG version_condition_mask = 0;
 	char dll_path[MAX_PATH];
 	DWORD dll_path_len, size_len;
 
-	return -1;
-
 	/* Only use CNG on Windows 2008 / Vista SP1  or better (Windows 6.0 SP1) */
-	version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
-	version_test.dwMajorVersion = 6;
-	version_test.dwMinorVersion = 0;
-	version_test.wServicePackMajor = 1;
-	version_test.wServicePackMinor = 0;
-
-	version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
-
-	VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
-	VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
-	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
-	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
-
-	if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+	if (!git_has_win32_version(6, 0, 1))
 		return -1;
 
 	/* Load bcrypt.dll explicitly from the system directory */
-	if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH ||
+	if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
+		dll_path_len > MAX_PATH ||
 		StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
 		StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
 		(hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
@@ -106,7 +89,15 @@
 	hash_prov.type = INVALID;
 }
 
-int git_hash_global_init()
+static void git_hash_global_shutdown(void)
+{
+	if (hash_prov.type == CNG)
+		hash_cng_prov_shutdown();
+	else if(hash_prov.type == CRYPTOAPI)
+		hash_cryptoapi_prov_shutdown();
+}
+
+int git_hash_global_init(void)
 {
 	int error = 0;
 
@@ -116,15 +107,9 @@
 	if ((error = hash_cng_prov_init()) < 0)
 		error = hash_cryptoapi_prov_init();
 
-	return error;	
-}
+	git__on_shutdown(git_hash_global_shutdown);
 
-void git_hash_global_shutdown()
-{
-	if (hash_prov.type == CNG)
-		hash_cng_prov_shutdown();
-	else if(hash_prov.type == CRYPTOAPI)
-		hash_cryptoapi_prov_shutdown();
+	return error;
 }
 
 /* CryptoAPI: available in Windows XP and newer */
diff --git a/src/hashsig.c b/src/hashsig.c
index ab8d8b3..109f966 100644
--- a/src/hashsig.c
+++ b/src/hashsig.c
@@ -13,12 +13,15 @@
 
 #define HASHSIG_SCALE 100
 
-#define HASHSIG_HASH_WINDOW 32
-#define HASHSIG_HASH_START	0
+#define HASHSIG_MAX_RUN 80
+#define HASHSIG_HASH_START	0x012345678ABCDEF0LL
 #define HASHSIG_HASH_SHIFT  5
-#define HASHSIG_HASH_MASK   0x7FFFFFFF
+
+#define HASHSIG_HASH_MIX(S,CH) \
+	(S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
 
 #define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
+#define HASHSIG_HEAP_MIN_SIZE 4
 
 typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
 
@@ -28,14 +31,6 @@
 	hashsig_t values[HASHSIG_HEAP_SIZE];
 } hashsig_heap;
 
-typedef struct {
-	hashsig_state state, shift_n;
-	char window[HASHSIG_HASH_WINDOW];
-	int win_len, win_pos, saw_lf;
-} hashsig_in_progress;
-
-#define HASHSIG_IN_PROGRESS_INIT { HASHSIG_HASH_START, 1, {0}, 0, 0, 1 }
-
 struct git_hashsig {
 	hashsig_heap mins;
 	hashsig_heap maxs;
@@ -43,8 +38,8 @@
 	int considered;
 };
 
-#define HEAP_LCHILD_OF(I) (((I)*2)+1)
-#define HEAP_RCHILD_OF(I) (((I)*2)+2)
+#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
+#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
 #define HEAP_PARENT_OF(I) (((I)-1)>>1)
 
 static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
@@ -115,134 +110,109 @@
 
 static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
 {
-	/* if heap is full, pop top if new element should replace it */
-	if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
-		h->size--;
-		h->values[0] = h->values[h->size];
-		hashsig_heap_down(h, 0);
-	}
-
 	/* if heap is not full, insert new element */
 	if (h->size < h->asize) {
 		h->values[h->size++] = val;
 		hashsig_heap_up(h, h->size - 1);
 	}
+
+	/* if heap is full, pop top if new element should replace it */
+	else if (h->cmp(&val, &h->values[0], NULL) > 0) {
+		h->size--;
+		h->values[0] = h->values[h->size];
+		hashsig_heap_down(h, 0);
+	}
+
 }
 
-GIT_INLINE(bool) hashsig_include_char(
-	char ch, git_hashsig_option_t opt, int *saw_lf)
+typedef struct {
+	int use_ignores;
+	uint8_t ignore_ch[256];
+} hashsig_in_progress;
+
+static void hashsig_in_progress_init(
+	hashsig_in_progress *prog, git_hashsig *sig)
 {
-	if ((opt & GIT_HASHSIG_IGNORE_WHITESPACE) && git__isspace(ch))
-		return false;
+	int i;
 
-	if (opt & GIT_HASHSIG_SMART_WHITESPACE) {
-		if (ch == '\r' || (*saw_lf && git__isspace(ch)))
-			return false;
-
-		*saw_lf = (ch == '\n');
+	switch (sig->opt) {
+	case GIT_HASHSIG_IGNORE_WHITESPACE:
+		for (i = 0; i < 256; ++i)
+			prog->ignore_ch[i] = git__isspace_nonlf(i);
+		prog->use_ignores = 1;
+		break;
+	case GIT_HASHSIG_SMART_WHITESPACE:
+		for (i = 0; i < 256; ++i)
+			prog->ignore_ch[i] = git__isspace(i);
+		prog->use_ignores = 1;
+		break;
+	default:
+		memset(prog, 0, sizeof(*prog));
+		break;
 	}
-
-	return true;
 }
 
-static void hashsig_initial_window(
-	git_hashsig *sig,
-	const char **data,
-	size_t size,
-	hashsig_in_progress *prog)
-{
-	hashsig_state state, shift_n;
-	int win_len;
-	const char *scan, *end;
-
-	/* init until we have processed at least HASHSIG_HASH_WINDOW data */
-
-	if (prog->win_len >= HASHSIG_HASH_WINDOW)
-		return;
-
-	state   = prog->state;
-	win_len = prog->win_len;
-	shift_n = prog->shift_n;
-
-	scan = *data;
-	end  = scan + size;
-
-	while (scan < end && win_len < HASHSIG_HASH_WINDOW) {
-		char ch = *scan++;
-
-		if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
-			continue;
-
-		state = (state * HASHSIG_HASH_SHIFT + ch) & HASHSIG_HASH_MASK;
-
-		if (!win_len)
-			shift_n = 1;
-		else
-			shift_n = (shift_n * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
-
-		prog->window[win_len++] = ch;
-	}
-
-	/* insert initial hash if we just finished */
-
-	if (win_len == HASHSIG_HASH_WINDOW) {
-		hashsig_heap_insert(&sig->mins, (hashsig_t)state);
-		hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
-		sig->considered = 1;
-	}
-
-	prog->state   = state;
-	prog->win_len = win_len;
-	prog->shift_n = shift_n;
-
-	*data = scan;
-}
+#define HASHSIG_IN_PROGRESS_INIT { 1 }
 
 static int hashsig_add_hashes(
 	git_hashsig *sig,
-	const char *data,
+	const uint8_t *data,
 	size_t size,
 	hashsig_in_progress *prog)
 {
-	const char *scan = data, *end = data + size;
-	hashsig_state state, shift_n, rmv;
+	const uint8_t *scan = data, *end = data + size;
+	hashsig_state state = HASHSIG_HASH_START;
+	int use_ignores = prog->use_ignores, len;
+	uint8_t ch;
 
-	if (prog->win_len < HASHSIG_HASH_WINDOW)
-		hashsig_initial_window(sig, &scan, size, prog);
+	while (scan < end) {
+		state = HASHSIG_HASH_START;
 
-	state   = prog->state;
-	shift_n = prog->shift_n;
+		for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
+			ch = *scan;
 
-	/* advance window, adding new chars and removing old */
+			if (use_ignores)
+				for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
+					++scan;
+			else if (sig->opt != GIT_HASHSIG_NORMAL)
+				for (; scan < end && ch == '\r'; ch = *scan)
+					++scan;
 
-	for (; scan < end; ++scan) {
-		char ch = *scan;
+			/* peek at next character to decide what to do next */
+			if (sig->opt == GIT_HASHSIG_SMART_WHITESPACE)
+				use_ignores = (ch == '\n');
 
-		if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
-			continue;
+			if (scan >= end)
+				break;
+			++scan;
 
-		rmv = shift_n * prog->window[prog->win_pos];
+			/* check run terminator */
+			if (ch == '\n' || ch == '\0')
+				break;
 
-		state = (state - rmv) & HASHSIG_HASH_MASK;
-		state = (state * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
-		state = (state + ch) & HASHSIG_HASH_MASK;
+			++len;
+			HASHSIG_HASH_MIX(state, ch);
+		}
 
-		hashsig_heap_insert(&sig->mins, (hashsig_t)state);
-		hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
-		sig->considered++;
+		if (len > 0) {
+			hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+			hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
 
-		prog->window[prog->win_pos] = ch;
-		prog->win_pos = (prog->win_pos + 1) % HASHSIG_HASH_WINDOW;
+			sig->considered++;
+
+			while (scan < end && (*scan == '\n' || !*scan))
+				++scan;
+		}
 	}
 
-	prog->state = state;
+	prog->use_ignores = use_ignores;
 
 	return 0;
 }
 
 static int hashsig_finalize_hashes(git_hashsig *sig)
 {
-	if (sig->mins.size < HASHSIG_HEAP_SIZE) {
+	if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE) {
 		giterr_set(GITERR_INVALID,
 			"File too small for similarity signature calculation");
 		return GIT_EBUFS;
@@ -274,11 +244,13 @@
 	git_hashsig_option_t opts)
 {
 	int error;
-	hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+	hashsig_in_progress prog;
 	git_hashsig *sig = hashsig_alloc(opts);
 	GITERR_CHECK_ALLOC(sig);
 
-	error = hashsig_add_hashes(sig, buf, buflen, &prog);
+	hashsig_in_progress_init(&prog, sig);
+
+	error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
 
 	if (!error)
 		error = hashsig_finalize_hashes(sig);
@@ -296,10 +268,10 @@
 	const char *path,
 	git_hashsig_option_t opts)
 {
-	char buf[4096];
+	uint8_t buf[0x1000];
 	ssize_t buflen = 0;
 	int error = 0, fd;
-	hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+	hashsig_in_progress prog;
 	git_hashsig *sig = hashsig_alloc(opts);
 	GITERR_CHECK_ALLOC(sig);
 
@@ -308,6 +280,8 @@
 		return fd;
 	}
 
+	hashsig_in_progress_init(&prog, sig);
+
 	while (!error) {
 		if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
 			if ((error = (int)buflen) < 0)
@@ -362,6 +336,12 @@
 
 int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
 {
-	return (hashsig_heap_compare(&a->mins, &b->mins) +
-			hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
+	/* if we have fewer than the maximum number of elements, then just use
+	 * one array since the two arrays will be the same
+	 */
+	if (a->mins.size < HASHSIG_HEAP_SIZE)
+		return hashsig_heap_compare(&a->mins, &b->mins);
+	else
+		return (hashsig_heap_compare(&a->mins, &b->mins) +
+				hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
 }
diff --git a/src/ident.c b/src/ident.c
new file mode 100644
index 0000000..5163087
--- /dev/null
+++ b/src/ident.c
@@ -0,0 +1,125 @@
+/*
+ * 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 "git2/sys/filter.h"
+#include "filter.h"
+#include "buffer.h"
+#include "buf_text.h"
+
+static int ident_find_id(
+	const char **id_start, const char **id_end, const char *start, size_t len)
+{
+	const char *end = start + len, *found = NULL;
+
+	while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
+		size_t remaining = (size_t)(end - found) - 1;
+		if (remaining < 3)
+			return GIT_ENOTFOUND;
+
+		start = found + 1;
+		len   = remaining;
+
+		if (start[0] == 'I' && start[1] == 'd')
+			break;
+	}
+
+	if (len < 3 || !found)
+		return GIT_ENOTFOUND;
+	*id_start = found;
+
+	if ((found = memchr(start + 2, '$', len - 2)) == NULL)
+		return GIT_ENOTFOUND;
+
+	*id_end = found + 1;
+	return 0;
+}
+
+static int ident_insert_id(
+	git_buf *to, const git_buf *from, const git_filter_source *src)
+{
+	char oid[GIT_OID_HEXSZ+1];
+	const char *id_start, *id_end, *from_end = from->ptr + from->size;
+	size_t need_size;
+
+	/* replace $Id$ with blob id */
+
+	if (!git_filter_source_id(src))
+		return GIT_PASSTHROUGH;
+
+	git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
+
+	if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+		return GIT_PASSTHROUGH;
+
+	need_size = (size_t)(id_start - from->ptr) +
+		5 /* "$Id: " */ + GIT_OID_HEXSZ + 1 /* "$" */ +
+		(size_t)(from_end - id_end);
+
+	if (git_buf_grow(to, need_size) < 0)
+		return -1;
+
+	git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+	git_buf_put(to, "$Id: ", 5);
+	git_buf_put(to, oid, GIT_OID_HEXSZ);
+	git_buf_putc(to, '$');
+	git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+	return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_remove_id(
+	git_buf *to, const git_buf *from)
+{
+	const char *id_start, *id_end, *from_end = from->ptr + from->size;
+	size_t need_size;
+
+	if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+		return GIT_PASSTHROUGH;
+
+	need_size = (size_t)(id_start - from->ptr) +
+		4 /* "$Id$" */ + (size_t)(from_end - id_end);
+
+	if (git_buf_grow(to, need_size) < 0)
+		return -1;
+
+	git_buf_set(to, from->ptr, (size_t)(id_start - from->ptr));
+	git_buf_put(to, "$Id$", 4);
+	git_buf_put(to, id_end, (size_t)(from_end - id_end));
+
+	return git_buf_oom(to) ? -1 : 0;
+}
+
+static int ident_apply(
+	git_filter     *self,
+	void          **payload,
+	git_buf        *to,
+	const git_buf  *from,
+	const git_filter_source *src)
+{
+	GIT_UNUSED(self); GIT_UNUSED(payload);
+
+	/* Don't filter binary files */
+	if (git_buf_text_is_binary(from))
+		return GIT_PASSTHROUGH;
+
+	if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+		return ident_insert_id(to, from, src);
+	else
+		return ident_remove_id(to, from);
+}
+
+git_filter *git_ident_filter_new(void)
+{
+	git_filter *f = git__calloc(1, sizeof(git_filter));
+
+	f->version = GIT_FILTER_VERSION;
+	f->attributes = "+ident"; /* apply to files with ident attribute set */
+	f->shutdown = git_filter_free;
+	f->apply    = ident_apply;
+
+	return f;
+}
diff --git a/src/ignore.c b/src/ignore.c
index cc90b0c..27d7c7e 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -1,4 +1,5 @@
 #include "git2/ignore.h"
+#include "common.h"
 #include "ignore.h"
 #include "attr.h"
 #include "path.h"
@@ -37,7 +38,7 @@
 			GITERR_CHECK_ALLOC(match);
 		}
 
-		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
 
 		if (!(error = git_attr_fnmatch__parse(
 			match, ignores->pool, context, &scan)))
@@ -159,17 +160,36 @@
 {
 	if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
 		return -1;
-	else
-		return push_ignore_file(
-			ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+
+	return push_ignore_file(
+		ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
 }
 
 int git_ignore__pop_dir(git_ignores *ign)
 {
 	if (ign->ign_path.length > 0) {
 		git_attr_file *file = git_vector_last(&ign->ign_path);
-		if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
+		const char *start, *end, *scan;
+		size_t keylen;
+
+		/* - ign->dir looks something like "a/b" (or "a/b/c/d")
+		 * - file->key looks something like "0#a/b/.gitignore
+		 *
+		 * We are popping the last directory off ign->dir.  We also want to
+		 * remove the file from the vector if the directory part of the key
+		 * matches the ign->dir path.  We need to test if the "a/b" part of
+		 * the file key matches the path we are about to pop.
+		 */
+
+		for (start = end = scan = &file->key[2]; *scan; ++scan)
+			if (*scan == '/')
+				end = scan; /* point 'end' to last '/' in key */
+		keylen = (end - start) + 1;
+
+		if (ign->dir.size >= keylen &&
+			!memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen))
 			git_vector_pop(&ign->ign_path);
+
 		git_buf_rtruncate_at_char(&ign->dir, '/');
 	}
 	return 0;
@@ -298,12 +318,9 @@
 		path.full.size = (tail - path.full.ptr);
 		path.is_dir = (tail == end) ? full_is_dir : true;
 
-		/* update ignores for new path fragment */
-		if (path.basename == path.path)
-			error = git_ignore__for_path(repo, path.path, &ignores);
-		else
-			error = git_ignore__push_dir(&ignores, path.basename);
-		if (error < 0)
+		/* initialize ignores the first time through */
+		if (path.basename == path.path &&
+			(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
 			break;
 
 		/* first process builtins - success means path was found */
@@ -327,6 +344,10 @@
 		if (tail == end)
 			break;
 
+		/* now add this directory to list of ignores */
+		if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
+			break;
+
 		/* reinstate divider in path */
 		*tail = '/';
 		while (*tail == '/') tail++;
diff --git a/src/ignore.h b/src/ignore.h
index cc114b0..851c824 100644
--- a/src/ignore.h
+++ b/src/ignore.h
@@ -24,14 +24,15 @@
  */
 typedef struct {
 	git_repository *repo;
-	git_buf dir;
+	git_buf dir; /* current directory reflected in ign_path */
 	git_attr_file *ign_internal;
 	git_vector ign_path;
 	git_vector ign_global;
 	int ignore_case;
 } git_ignores;
 
-extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
+extern int git_ignore__for_path(
+	git_repository *repo, const char *path, git_ignores *ign);
 
 extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
 
diff --git a/src/index.c b/src/index.c
index 1d46779..09e7b23 100644
--- a/src/index.c
+++ b/src/index.c
@@ -16,6 +16,7 @@
 #include "iterator.h"
 #include "pathspec.h"
 #include "ignore.h"
+#include "blob.h"
 
 #include "git2/odb.h"
 #include "git2/oid.h"
@@ -101,8 +102,6 @@
 static bool is_index_extended(git_index *index);
 static int write_index(git_index *index, git_filebuf *file);
 
-static int index_find(size_t *at_pos, git_index *index, const char *path, int stage);
-
 static void index_entry_free(git_index_entry *entry);
 static void index_entry_reuc_free(git_index_reuc_entry *reuc);
 
@@ -114,7 +113,7 @@
 
 	ret = strcmp(srch_key->path, entry->path);
 
-	if (ret == 0)
+	if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
 		ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
 
 	return ret;
@@ -128,7 +127,7 @@
 
 	ret = strcasecmp(srch_key->path, entry->path);
 
-	if (ret == 0)
+	if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
 		ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
 
 	return ret;
@@ -261,6 +260,22 @@
 	return strcasecmp(info_a->path, info_b->path);
 }
 
+static void index_entry_reuc_free(git_index_reuc_entry *reuc)
+{
+	if (!reuc)
+		return;
+	git__free(reuc->path);
+	git__free(reuc);
+}
+
+static void index_entry_free(git_index_entry *entry)
+{
+	if (!entry)
+		return;
+	git__free(entry->path);
+	git__free(entry);
+}
+
 static unsigned int index_create_mode(unsigned int mode)
 {
 	if (S_ISLNK(mode))
@@ -269,7 +284,7 @@
 	if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
 		return (S_IFLNK | S_IFDIR);
 
-	return S_IFREG | ((mode & 0100) ? 0755 : 0644);
+	return S_IFREG | GIT_PERMS_CANONICAL(mode);
 }
 
 static unsigned int index_merge_mode(
@@ -306,6 +321,7 @@
 int git_index_open(git_index **index_out, const char *index_path)
 {
 	git_index *index;
+	int error;
 
 	assert(index_out);
 
@@ -331,10 +347,15 @@
 	index->entries_search_path = index_srch_path;
 	index->reuc_search = reuc_srch;
 
+	if ((index_path != NULL) && ((error = git_index_read(index, true)) < 0)) {
+		git_index_free(index);
+		return error;
+	}
+
 	*index_out = index;
 	GIT_REFCOUNT_INC(index);
 
-	return (index_path != NULL) ? git_index_read(index) : 0;
+	return 0;
 }
 
 int git_index_new(git_index **out)
@@ -367,11 +388,8 @@
 {
 	size_t i;
 
-	for (i = 0; i < entries->length; ++i) {
-		git_index_entry *e = git_vector_get(entries, i);
-		git__free(e->path);
-		git__free(e);
-	}
+	for (i = 0; i < entries->length; ++i)
+		index_entry_free(git__swap(entries->contents[i], NULL));
 
 	git_vector_clear(entries);
 }
@@ -398,7 +416,7 @@
 
 int git_index_set_caps(git_index *index, unsigned int caps)
 {
-	int old_ignore_case;
+	unsigned int old_ignore_case;
 
 	assert(index);
 
@@ -426,7 +444,7 @@
 	}
 
 	if (old_ignore_case != index->ignore_case) {
-		git_index__set_ignore_case(index, index->ignore_case);
+		git_index__set_ignore_case(index, (bool)index->ignore_case);
 	}
 
 	return 0;
@@ -439,24 +457,26 @@
 			(index->no_symlinks ? GIT_INDEXCAP_NO_SYMLINKS : 0));
 }
 
-int git_index_read(git_index *index)
+int git_index_read(git_index *index, int force)
 {
 	int error = 0, updated;
 	git_buf buffer = GIT_BUF_INIT;
-	git_futils_filestamp stamp = {0};
+	git_futils_filestamp stamp = index->stamp;
 
 	if (!index->index_file_path)
 		return create_index_error(-1,
 			"Failed to read index: The index is in-memory only");
 
-	if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
-		git_index_clear(index);
-		index->on_disk = 0;
+	index->on_disk = git_path_exists(index->index_file_path);
+
+	if (!index->on_disk) {
+		if (force)
+			git_index_clear(index);
 		return 0;
 	}
 
 	updated = git_futils_filestamp_check(&stamp, index->index_file_path);
-	if (updated <= 0)
+	if (updated < 0 || (!updated && !force))
 		return updated;
 
 	error = git_futils_readbuffer(&buffer, index->index_file_path);
@@ -486,15 +506,19 @@
 	git_vector_sort(&index->reuc);
 
 	if ((error = git_filebuf_open(
-			 &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
+		&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) {
+		if (error == GIT_ELOCKED)
+			giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrrent or crashed process");
+
 		return error;
+	}
 
 	if ((error = write_index(index, &file)) < 0) {
 		git_filebuf_cleanup(&file);
 		return error;
 	}
 
-	if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
+	if ((error = git_filebuf_commit(&file)) < 0)
 		return error;
 
 	error = git_futils_filestamp_check(&index->stamp, index->index_file_path);
@@ -505,6 +529,12 @@
 	return 0;
 }
 
+const char * git_index_path(git_index *index)
+{
+	assert(index);
+	return index->index_file_path;
+}
+
 int git_index_write_tree(git_oid *oid, git_index *index)
 {
 	git_repository *repo;
@@ -549,7 +579,7 @@
 
 	git_vector_sort(&index->entries);
 
-	if (index_find(&pos, index, path, stage) < 0) {
+	if (git_index__find(&pos, index, path, stage) < 0) {
 		giterr_set(GITERR_INDEX, "Index does not contain %s", path);
 		return NULL;
 	}
@@ -557,7 +587,8 @@
 	return git_index_get_byindex(index, pos);
 }
 
-void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
+void git_index_entry__init_from_stat(
+	git_index_entry *entry, struct stat *st, bool trust_mode)
 {
 	entry->ctime.seconds = (git_time_t)st->st_ctime;
 	entry->mtime.seconds = (git_time_t)st->st_mtime;
@@ -565,7 +596,8 @@
 	/* entry->ctime.nanoseconds = st->st_ctimensec; */
 	entry->dev  = st->st_rdev;
 	entry->ino  = st->st_ino;
-	entry->mode = index_create_mode(st->st_mode);
+	entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
+		index_create_mode(0666) : index_create_mode(st->st_mode);
 	entry->uid  = st->st_uid;
 	entry->gid  = st->st_gid;
 	entry->file_size = st->st_size;
@@ -587,48 +619,29 @@
 	return strcasecmp(entry_a->path, entry_b->path);
 }
 
-static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
+static int index_entry_init(
+	git_index_entry **entry_out, git_index *index, const char *rel_path)
 {
+	int error = 0;
 	git_index_entry *entry = NULL;
 	struct stat st;
 	git_oid oid;
-	const char *workdir;
-	git_buf full_path = GIT_BUF_INIT;
-	int error;
 
 	if (INDEX_OWNER(index) == NULL)
 		return create_index_error(-1,
 			"Could not initialize index entry. "
 			"Index is not backed up by an existing repository.");
 
-	workdir = git_repository_workdir(INDEX_OWNER(index));
-
-	if (!workdir)
-		return create_index_error(GIT_EBAREREPO,
-			"Could not initialize index entry. Repository is bare");
-
-	if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
-		return error;
-
-	if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
-		git_buf_free(&full_path);
-		return error;
-	}
-
-	git_buf_free(&full_path); /* done with full path */
-
-	/* There is no need to validate the rel_path here, since it will be
-	 * immediately validated by the call to git_blob_create_fromfile.
-	 */
-
-	/* write the blob to disk and get the oid */
-	if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
+	/* write the blob to disk and get the oid and stat info */
+	error = git_blob__create_from_paths(
+		&oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
+	if (error < 0)
 		return error;
 
 	entry = git__calloc(1, sizeof(git_index_entry));
 	GITERR_CHECK_ALLOC(entry);
 
-	git_index_entry__init_from_stat(entry, &st);
+	git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
 
 	entry->oid = oid;
 	entry->path = git__strdup(rel_path);
@@ -670,15 +683,6 @@
 	return 0;
 }
 
-static void index_entry_reuc_free(git_index_reuc_entry *reuc)
-{
-	if (!reuc)
-		return;
-
-	git__free(reuc->path);
-	git__free(reuc);
-}
-
 static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
 {
 	git_index_entry *entry;
@@ -697,14 +701,6 @@
 	return entry;
 }
 
-static void index_entry_free(git_index_entry *entry)
-{
-	if (!entry)
-		return;
-	git__free(entry->path);
-	git__free(entry);
-}
-
 static int index_insert(git_index *index, git_index_entry *entry, int replace)
 {
 	size_t path_length, position;
@@ -723,7 +719,8 @@
 		entry->flags |= GIT_IDXENTRY_NAMEMASK;
 
 	/* look if an entry with this path already exists */
-	if (!index_find(&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
+	if (!git_index__find(
+			&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
 		existing = (git_index_entry **)&index->entries.contents[position];
 
 		/* update filemode to existing values if stat is not trusted */
@@ -835,7 +832,7 @@
 
 	git_vector_sort(&index->entries);
 
-	if (index_find(&position, index, path, stage) < 0) {
+	if (git_index__find(&position, index, path, stage) < 0) {
 		giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d",
 			path, stage);
 		return GIT_ENOTFOUND;
@@ -891,7 +888,8 @@
 	return error;
 }
 
-static int index_find(size_t *at_pos, git_index *index, const char *path, int stage)
+int git_index__find(
+	size_t *at_pos, git_index *index, const char *path, int stage)
 {
 	struct entry_srch_key srch_key;
 
@@ -900,7 +898,8 @@
 	srch_key.path = path;
 	srch_key.stage = stage;
 
-	return git_vector_bsearch2(at_pos, &index->entries, index->entries_search, &srch_key);
+	return git_vector_bsearch2(
+		at_pos, &index->entries, index->entries_search, &srch_key);
 }
 
 int git_index_find(size_t *at_pos, git_index *index, const char *path)
@@ -1357,14 +1356,11 @@
 void git_index_reuc_clear(git_index *index)
 {
 	size_t i;
-	git_index_reuc_entry *reuc;
 
 	assert(index);
 
-	git_vector_foreach(&index->reuc, i, reuc) {
-		git__free(reuc->path);
-		git__free(reuc);
-	}
+	for (i = 0; i < index->reuc.length; ++i)
+		index_entry_reuc_free(git__swap(index->reuc.contents[i], NULL));
 
 	git_vector_clear(&index->reuc);
 }
@@ -1389,7 +1385,7 @@
 	while (size) {
 		git_index_reuc_entry *lost;
 
-		len = strlen(buffer) + 1;
+		len = p_strnlen(buffer, size) + 1;
 		if (size <= len)
 			return index_error_invalid("reading reuc entries");
 
@@ -1409,14 +1405,18 @@
 
 			if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
 				!endptr || endptr == buffer || *endptr ||
-				(unsigned)tmp > UINT_MAX)
+				(unsigned)tmp > UINT_MAX) {
+				index_entry_reuc_free(lost);
 				return index_error_invalid("reading reuc entry stage");
+			}
 
 			lost->mode[i] = tmp;
 
 			len = (endptr + 1) - buffer;
-			if (size <= len)
+			if (size <= len) {
+				index_entry_reuc_free(lost);
 				return index_error_invalid("reading reuc entry stage");
+			}
 
 			size -= len;
 			buffer += len;
@@ -1426,8 +1426,10 @@
 		for (i = 0; i < 3; i++) {
 			if (!lost->mode[i])
 				continue;
-			if (size < 20)
+			if (size < 20) {
+				index_entry_reuc_free(lost);
 				return index_error_invalid("reading reuc entry oid");
+			}
 
 			git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
 			size -= 20;
@@ -1456,7 +1458,7 @@
 		return -1;
 
 #define read_conflict_name(ptr) \
-	len = strlen(buffer) + 1; \
+	len = p_strnlen(buffer, size) + 1; \
 	if (size < len) \
 		return index_error_invalid("reading conflict name entries"); \
 	\
@@ -1583,7 +1585,8 @@
 
 	total_size = dest.extension_size + sizeof(struct index_extension);
 
-	if (buffer_size < total_size ||
+	if (dest.extension_size > total_size ||
+		buffer_size < total_size ||
 		buffer_size - total_size < INDEX_FOOTER_SIZE)
 		return 0;
 
@@ -1958,8 +1961,9 @@
 }
 
 typedef struct read_tree_data {
-	git_index *index;
 	git_vector *old_entries;
+	git_vector *new_entries;
+	git_vector_cmp entries_search;
 } read_tree_data;
 
 static int read_tree_cb(
@@ -1990,7 +1994,7 @@
 		skey.stage = 0;
 
 		if (!git_vector_bsearch2(
-				&pos, data->old_entries, data->index->entries_search, &skey) &&
+				&pos, data->old_entries, data->entries_search, &skey) &&
 			(old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
 			entry->mode == old_entry->mode &&
 			git_oid_equal(&entry->oid, &old_entry->oid))
@@ -2008,7 +2012,7 @@
 	entry->path = git_buf_detach(&path);
 	git_buf_free(&path);
 
-	if (git_vector_insert(&data->index->entries, entry) < 0) {
+	if (git_vector_insert(data->new_entries, entry) < 0) {
 		index_entry_free(entry);
 		return -1;
 	}
@@ -2022,22 +2026,22 @@
 	git_vector entries = GIT_VECTOR_INIT;
 	read_tree_data data;
 
+	git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
+
+	data.old_entries = &index->entries;
+	data.new_entries = &entries;
+	data.entries_search = index->entries_search;
+
 	git_vector_sort(&index->entries);
 
-	git_vector_set_cmp(&entries, index->entries._cmp);
-	git_vector_swap(&entries, &index->entries);
-
-	git_index_clear(index);
-
-	data.index = index;
-	data.old_entries = &entries;
-
 	error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data);
 
-	index_entries_free(&entries);
-	git_vector_free(&entries);
+	git_vector_sort(&entries);
 
-	git_vector_sort(&index->entries);
+	git_index_clear(index);
+
+	git_vector_swap(&entries, &index->entries);
+	git_vector_free(&entries);
 
 	return error;
 }
@@ -2059,7 +2063,7 @@
 	git_iterator *wditer = NULL;
 	const git_index_entry *wd = NULL;
 	git_index_entry *entry;
-	git_pathspec_context ps;
+	git_pathspec ps;
 	const char *match;
 	size_t existing;
 	bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
@@ -2080,7 +2084,7 @@
 	if (git_repository__cvar(&ignorecase, repo, GIT_CVAR_IGNORECASE) < 0)
 		return -1;
 
-	if ((error = git_pathspec_context_init(&ps, paths)) < 0)
+	if ((error = git_pathspec__init(&ps, paths)) < 0)
 		return error;
 
 	/* optionally check that pathspec doesn't mention any ignored files */
@@ -2097,14 +2101,14 @@
 	while (!(error = git_iterator_advance(&wd, wditer))) {
 
 		/* check if path actually matches */
-		if (!git_pathspec_match_path(
-				&ps.pathspec, wd->path, no_fnmatch, ignorecase, &match))
+		if (!git_pathspec__match(
+				&ps.pathspec, wd->path, no_fnmatch, (bool)ignorecase, &match, NULL))
 			continue;
 
 		/* skip ignored items that are not already in the index */
 		if ((flags & GIT_INDEX_ADD_FORCE) == 0 &&
 			git_iterator_current_is_ignored(wditer) &&
-			index_find(&existing, index, wd->path, 0) < 0)
+			git_index__find(&existing, index, wd->path, 0) < 0)
 			continue;
 
 		/* issue notification callback if requested */
@@ -2154,7 +2158,7 @@
 
 cleanup:
 	git_iterator_free(wditer);
-	git_pathspec_context_free(&ps);
+	git_pathspec__clear(&ps);
 
 	return error;
 }
@@ -2174,13 +2178,13 @@
 {
 	int error = 0;
 	size_t i;
-	git_pathspec_context ps;
+	git_pathspec ps;
 	const char *match;
 	git_buf path = GIT_BUF_INIT;
 
 	assert(index);
 
-	if ((error = git_pathspec_context_init(&ps, paths)) < 0)
+	if ((error = git_pathspec__init(&ps, paths)) < 0)
 		return error;
 
 	git_vector_sort(&index->entries);
@@ -2189,8 +2193,9 @@
 		git_index_entry *entry = git_vector_get(&index->entries, i);
 
 		/* check if path actually matches */
-		if (!git_pathspec_match_path(
-				&ps.pathspec, entry->path, false, index->ignore_case, &match))
+		if (!git_pathspec__match(
+				&ps.pathspec, entry->path, false, (bool)index->ignore_case,
+				&match, NULL))
 			continue;
 
 		/* issue notification callback if requested */
@@ -2237,7 +2242,7 @@
 	}
 
 	git_buf_free(&path);
-	git_pathspec_context_free(&ps);
+	git_pathspec__clear(&ps);
 
 	return error;
 }
diff --git a/src/index.h b/src/index.h
index a59107a..4c448fa 100644
--- a/src/index.h
+++ b/src/index.h
@@ -47,13 +47,17 @@
 	size_t cur;
 };
 
-extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
+extern void git_index_entry__init_from_stat(
+	git_index_entry *entry, struct stat *st, bool trust_mode);
 
 extern size_t git_index__prefix_position(git_index *index, const char *path);
 
 extern int git_index_entry__cmp(const void *a, const void *b);
 extern int git_index_entry__cmp_icase(const void *a, const void *b);
 
+extern int git_index__find(
+	size_t *at_pos, git_index *index, const char *path, int stage);
+
 extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
 
 #endif
diff --git a/src/indexer.c b/src/indexer.c
index 1b5339f..df1ce7c 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -18,6 +18,7 @@
 #include "filebuf.h"
 #include "oid.h"
 #include "oidmap.h"
+#include "compress.h"
 
 #define UINT31_MAX (0x7FFFFFFF)
 
@@ -28,13 +29,15 @@
 	uint64_t offset_long;
 };
 
-struct git_indexer_stream {
+struct git_indexer {
 	unsigned int parsed_header :1,
 		opened_pack :1,
 		have_stream :1,
 		have_delta :1;
+	struct git_pack_header hdr;
 	struct git_pack_file *pack;
 	git_filebuf pack_file;
+	unsigned int mode;
 	git_off_t off;
 	git_off_t entry_start;
 	git_packfile_stream stream;
@@ -47,13 +50,21 @@
 	git_transfer_progress_callback progress_cb;
 	void *progress_payload;
 	char objbuf[8*1024];
+
+	/* Needed to look up objects which we want to inject to fix a thin pack */
+	git_odb *odb;
+
+	/* Fields for calculating the packfile trailer (hash of everything before it) */
+	char inbuf[GIT_OID_RAWSZ];
+	size_t inbuf_len;
+	git_hash_ctx trailer;
 };
 
 struct delta_info {
 	git_off_t delta_off;
 };
 
-const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx)
+const git_oid *git_indexer_hash(const git_indexer *idx)
 {
 	return &idx->hash;
 }
@@ -106,28 +117,34 @@
 	return git_oid__cmp(&entrya->oid, &entryb->oid);
 }
 
-int git_indexer_stream_new(
-		git_indexer_stream **out,
+int git_indexer_new(
+		git_indexer **out,
 		const char *prefix,
+		unsigned int mode,
+		git_odb *odb,
 		git_transfer_progress_callback progress_cb,
 		void *progress_payload)
 {
-	git_indexer_stream *idx;
+	git_indexer *idx;
 	git_buf path = GIT_BUF_INIT;
 	static const char suff[] = "/pack";
 	int error;
 
-	idx = git__calloc(1, sizeof(git_indexer_stream));
+	idx = git__calloc(1, sizeof(git_indexer));
 	GITERR_CHECK_ALLOC(idx);
+	idx->odb = odb;
 	idx->progress_cb = progress_cb;
 	idx->progress_payload = progress_payload;
+	idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
+	git_hash_ctx_init(&idx->trailer);
 
 	error = git_buf_joinpath(&path, prefix, suff);
 	if (error < 0)
 		goto cleanup;
 
 	error = git_filebuf_open(&idx->pack_file, path.ptr,
-				 GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);
+		GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER,
+		idx->mode);
 	git_buf_free(&path);
 	if (error < 0)
 		goto cleanup;
@@ -143,7 +160,7 @@
 }
 
 /* Try to store the delta so we can try to resolve it later */
-static int store_delta(git_indexer_stream *idx)
+static int store_delta(git_indexer *idx)
 {
 	struct delta_info *delta;
 
@@ -166,7 +183,7 @@
 	git_hash_update(ctx, buffer, hdrlen);
 }
 
-static int hash_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
 {
 	ssize_t read;
 
@@ -186,7 +203,7 @@
 }
 
 /* In order to create the packfile stream, we need to skip over the delta base description */
-static int advance_delta_offset(git_indexer_stream *idx, git_otype type)
+static int advance_delta_offset(git_indexer *idx, git_otype type)
 {
 	git_mwindow *w = NULL;
 
@@ -205,7 +222,7 @@
 }
 
 /* Read from the stream and discard any output */
-static int read_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+static int read_object_stream(git_indexer *idx, git_packfile_stream *stream)
 {
 	ssize_t read;
 
@@ -245,7 +262,7 @@
 	return 0;
 }
 
-static int store_object(git_indexer_stream *idx)
+static int store_object(git_indexer *idx)
 {
 	int i, error;
 	khiter_t k;
@@ -303,17 +320,10 @@
 	return -1;
 }
 
-static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
 {
 	int i, error;
 	khiter_t k;
-	git_oid oid;
-	size_t entry_size;
-	struct entry *entry;
-	struct git_pack_entry *pentry;
-
-	entry = git__calloc(1, sizeof(*entry));
-	GITERR_CHECK_ALLOC(entry);
 
 	if (entry_start > UINT31_MAX) {
 		entry->offset = UINT32_MAX;
@@ -322,25 +332,43 @@
 		entry->offset = (uint32_t)entry_start;
 	}
 
-	/* FIXME: Parse the object instead of hashing it */
+	pentry->offset = entry_start;
+	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+	if (!error)
+		return -1;
+
+	kh_value(idx->pack->idx_cache, k) = pentry;
+
+	/* Add the object to the list */
+	if (git_vector_insert(&idx->objects, entry) < 0)
+		return -1;
+
+	for (i = entry->oid.id[0]; i < 256; ++i) {
+		idx->fanout[i]++;
+	}
+
+	return 0;
+}
+
+static int hash_and_save(git_indexer *idx, git_rawobj *obj, git_off_t entry_start)
+{
+	git_oid oid;
+	size_t entry_size;
+	struct entry *entry;
+	struct git_pack_entry *pentry;
+
+	entry = git__calloc(1, sizeof(*entry));
+	GITERR_CHECK_ALLOC(entry);
+
 	if (git_odb__hashobj(&oid, obj) < 0) {
 		giterr_set(GITERR_INDEXER, "Failed to hash object");
-		return -1;
+		goto on_error;
 	}
 
 	pentry = git__calloc(1, sizeof(struct git_pack_entry));
 	GITERR_CHECK_ALLOC(pentry);
 
 	git_oid_cpy(&pentry->sha1, &oid);
-	pentry->offset = entry_start;
-	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
-	if (!error) {
-		git__free(pentry);
-		goto on_error;
-	}
-
-	kh_value(idx->pack->idx_cache, k) = pentry;
-
 	git_oid_cpy(&entry->oid, &oid);
 	entry->crc = crc32(0L, Z_NULL, 0);
 
@@ -348,15 +376,7 @@
 	if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
 		goto on_error;
 
-	/* Add the object to the list */
-	if (git_vector_insert(&idx->objects, entry) < 0)
-		goto on_error;
-
-	for (i = oid.id[0]; i < 256; ++i) {
-		idx->fanout[i]++;
-	}
-
-	return 0;
+	return save_entry(idx, entry, pentry, entry_start);
 
 on_error:
 	git__free(entry);
@@ -364,17 +384,54 @@
 	return -1;
 }
 
-static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
+static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats)
 {
 	if (!idx->progress_cb) return 0;
 	return idx->progress_cb(stats, idx->progress_payload);
 }
 
-int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
+/* Hash everything but the last 20B of input */
+static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
+{
+	size_t to_expell, to_keep;
+
+	if (size == 0)
+		return;
+
+	/* Easy case, dump the buffer and the data minus the last 20 bytes */
+	if (size >= GIT_OID_RAWSZ) {
+		git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
+		git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
+
+		data += size - GIT_OID_RAWSZ;
+		memcpy(idx->inbuf, data, GIT_OID_RAWSZ);
+		idx->inbuf_len = GIT_OID_RAWSZ;
+		return;
+	}
+
+	/* We can just append */
+	if (idx->inbuf_len + size <= GIT_OID_RAWSZ) {
+		memcpy(idx->inbuf + idx->inbuf_len, data, size);
+		idx->inbuf_len += size;
+		return;
+	}
+
+	/* We need to partially drain the buffer and then append */
+	to_keep   = GIT_OID_RAWSZ - size;
+	to_expell = idx->inbuf_len - to_keep;
+
+	git_hash_update(&idx->trailer, idx->inbuf, to_expell);
+
+	memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
+	memcpy(idx->inbuf + to_keep, data, size);
+	idx->inbuf_len += size - to_expell;
+}
+
+int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats)
 {
 	int error = -1;
-	struct git_pack_header hdr;
 	size_t processed;
+	struct git_pack_header *hdr = &idx->hdr;
 	git_mwindow_file *mwf = &idx->pack->mwf;
 
 	assert(idx && data && stats);
@@ -384,6 +441,8 @@
 	if (git_filebuf_write(&idx->pack_file, data, size) < 0)
 		return -1;
 
+	hash_partially(idx, data, (int)size);
+
 	/* Make sure we set the new size of the pack */
 	if (idx->opened_pack) {
 		idx->pack->mwf.size += size;
@@ -399,14 +458,14 @@
 	if (!idx->parsed_header) {
 		unsigned int total_objects;
 
-		if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+		if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
 			return 0;
 
-		if (parse_header(&hdr, idx->pack) < 0)
+		if (parse_header(&idx->hdr, idx->pack) < 0)
 			return -1;
 
 		idx->parsed_header = 1;
-		idx->nr_objects = ntohl(hdr.hdr_entries);
+		idx->nr_objects = ntohl(hdr->hdr_entries);
 		idx->off = sizeof(struct git_pack_header);
 
 		/* for now, limit to 2^32 objects */
@@ -427,6 +486,9 @@
 			return -1;
 
 		stats->received_objects = 0;
+		stats->local_objects = 0;
+		stats->total_deltas = 0;
+		stats->indexed_deltas = 0;
 		processed = stats->indexed_objects = 0;
 		stats->total_objects = total_objects;
 		do_progress_callback(idx, stats);
@@ -512,6 +574,7 @@
 		stats->received_objects++;
 
 		if (do_progress_callback(idx, stats) != 0) {
+			giterr_clear();
 			error = GIT_EUSER;
 			goto on_error;
 		}
@@ -524,7 +587,7 @@
 	return error;
 }
 
-static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
+static int index_path(git_buf *path, git_indexer *idx, const char *suffix)
 {
 	const char prefix[] = "pack-";
 	size_t slash = (size_t)path->size;
@@ -546,68 +609,306 @@
 	return git_buf_oom(path) ? -1 : 0;
 }
 
-static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
+/**
+ * Rewind the packfile by the trailer, as we might need to fix the
+ * packfile by injecting objects at the tail and must overwrite it.
+ */
+static git_off_t seek_back_trailer(git_indexer *idx)
+{
+	git_off_t off;
+
+	if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
+		return -1;
+
+	idx->pack->mwf.size -= GIT_OID_RAWSZ;
+	git_mwindow_free_all(&idx->pack->mwf);
+
+	return off;
+}
+
+static int inject_object(git_indexer *idx, git_oid *id)
+{
+	git_odb_object *obj;
+	struct entry *entry;
+	struct git_pack_entry *pentry;
+	git_oid foo = {{0}};
+	unsigned char hdr[64];
+	git_buf buf = GIT_BUF_INIT;
+	git_off_t entry_start;
+	const void *data;
+	size_t len, hdr_len;
+	int error;
+
+	entry = git__calloc(1, sizeof(*entry));
+	GITERR_CHECK_ALLOC(entry);
+
+	entry_start = seek_back_trailer(idx);
+
+	if (git_odb_read(&obj, idx->odb, id) < 0)
+		return -1;
+
+	data = git_odb_object_data(obj);
+	len = git_odb_object_size(obj);
+
+	entry->crc = crc32(0L, Z_NULL, 0);
+
+	/* Write out the object header */
+	hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
+	git_filebuf_write(&idx->pack_file, hdr, hdr_len);
+	idx->pack->mwf.size += hdr_len;
+	entry->crc = crc32(entry->crc, hdr, hdr_len);
+
+	if ((error = git__compress(&buf, data, len)) < 0)
+		goto cleanup;
+
+	/* And then the compressed object */
+	git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
+	idx->pack->mwf.size += buf.size;
+	entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
+	git_buf_free(&buf);
+
+	/* Write a fake trailer so the pack functions play ball */
+	if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
+		goto cleanup;
+
+	idx->pack->mwf.size += GIT_OID_RAWSZ;
+
+	pentry = git__calloc(1, sizeof(struct git_pack_entry));
+	GITERR_CHECK_ALLOC(pentry);
+
+	git_oid_cpy(&pentry->sha1, id);
+	git_oid_cpy(&entry->oid, id);
+	idx->off = entry_start + hdr_len + len;
+
+	if ((error = save_entry(idx, entry, pentry, entry_start)) < 0)
+		git__free(pentry);
+
+cleanup:
+	git_odb_object_free(obj);
+	return error;
+}
+
+static int fix_thin_pack(git_indexer *idx, git_transfer_progress *stats)
+{
+	int error, found_ref_delta = 0;
+	unsigned int i;
+	struct delta_info *delta;
+	size_t size;
+	git_otype type;
+	git_mwindow *w = NULL;
+	git_off_t curpos;
+	unsigned char *base_info;
+	unsigned int left = 0;
+	git_oid base;
+
+	assert(git_vector_length(&idx->deltas) > 0);
+
+	if (idx->odb == NULL) {
+		giterr_set(GITERR_INDEXER, "cannot fix a thin pack without an ODB");
+		return -1;
+	}
+
+	/* Loop until we find the first REF delta */
+	git_vector_foreach(&idx->deltas, i, delta) {
+		curpos = delta->delta_off;
+		error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
+		git_mwindow_close(&w);
+		if (error < 0)
+			return error;
+
+		if (type == GIT_OBJ_REF_DELTA) {
+			found_ref_delta = 1;
+			break;
+		}
+	}
+
+	if (!found_ref_delta) {
+		giterr_set(GITERR_INDEXER, "no REF_DELTA found, cannot inject object");
+		return -1;
+	}
+
+	/* curpos now points to the base information, which is an OID */
+	base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
+	if (base_info == NULL) {
+		giterr_set(GITERR_INDEXER, "failed to map delta information");
+		return -1;
+	}
+
+	git_oid_fromraw(&base, base_info);
+	git_mwindow_close(&w);
+
+	if (inject_object(idx, &base) < 0)
+		return -1;
+
+	stats->local_objects++;
+
+	return 0;
+}
+
+static int resolve_deltas(git_indexer *idx, git_transfer_progress *stats)
 {
 	unsigned int i;
 	struct delta_info *delta;
+	int progressed = 0;
 
-	git_vector_foreach(&idx->deltas, i, delta) {
-		git_rawobj obj;
+	while (idx->deltas.length > 0) {
+		progressed = 0;
+		git_vector_foreach(&idx->deltas, i, delta) {
+			git_rawobj obj;
 
-		idx->off = delta->delta_off;
-		if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+			idx->off = delta->delta_off;
+			if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+				continue;
+
+			if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+				continue;
+
+			git__free(obj.data);
+			stats->indexed_objects++;
+			stats->indexed_deltas++;
+			progressed = 1;
+			do_progress_callback(idx, stats);
+
+			/*
+			 * Remove this delta from the list and
+			 * decrease i so we don't skip over the next
+			 * delta.
+			 */
+			git_vector_remove(&idx->deltas, i);
+			git__free(delta);
+			i--;
+		}
+
+		if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
+			giterr_set(GITERR_INDEXER, "missing delta bases");
 			return -1;
-
-		if (hash_and_save(idx, &obj, delta->delta_off) < 0)
-			return -1;
-
-		git__free(obj.data);
-		stats->indexed_objects++;
-		do_progress_callback(idx, stats);
+		}
 	}
 
 	return 0;
 }
 
-int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats)
+static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *stats)
+{
+	void *ptr;
+	size_t chunk = 1024*1024;
+	git_off_t hashed = 0;
+	git_mwindow *w = NULL;
+	git_mwindow_file *mwf;
+	unsigned int left;
+	git_hash_ctx *ctx;
+
+	mwf = &idx->pack->mwf;
+	ctx = &idx->trailer;
+
+	git_hash_ctx_init(ctx);
+	git_mwindow_free_all(mwf);
+
+	/* Update the header to include the numer of local objects we injected */
+	idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
+	if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
+		giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
+		return -1;
+	}
+
+	if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
+		giterr_set(GITERR_OS, "failed to update the pack header");
+		return -1;
+	}
+
+	/*
+	 * We now use the same technique as before to determine the
+	 * hash. We keep reading up to the end and let
+	 * hash_partially() keep the existing trailer out of the
+	 * calculation.
+	 */
+	idx->inbuf_len = 0;
+	while (hashed < mwf->size) {
+		ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
+		if (ptr == NULL)
+			return -1;
+
+		hash_partially(idx, ptr, left);
+		hashed += left;
+
+		git_mwindow_close(&w);
+	}
+
+	return 0;
+}
+
+int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
 {
 	git_mwindow *w = NULL;
 	unsigned int i, long_offsets = 0, left;
 	struct git_pack_idx_header hdr;
 	git_buf filename = GIT_BUF_INIT;
 	struct entry *entry;
-	void *packfile_hash;
-	git_oid file_hash;
+	git_oid trailer_hash, file_hash;
 	git_hash_ctx ctx;
 	git_filebuf index_file = {0};
+	void *packfile_trailer;
 
 	if (git_hash_ctx_init(&ctx) < 0)
 		return -1;
 
 	/* Test for this before resolve_deltas(), as it plays with idx->off */
-	if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
-		giterr_set(GITERR_INDEXER, "Indexing error: unexpected data at the end of the pack");
+	if (idx->off < idx->pack->mwf.size - 20) {
+		giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack");
 		return -1;
 	}
 
-	if (idx->deltas.length > 0)
-		if (resolve_deltas(idx, stats) < 0)
-			return -1;
+	packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+	if (packfile_trailer == NULL) {
+		git_mwindow_close(&w);
+		goto on_error;
+	}
+
+	/* Compare the packfile trailer as it was sent to us and what we calculated */
+	git_oid_fromraw(&file_hash, packfile_trailer);
+	git_mwindow_close(&w);
+
+	git_hash_final(&trailer_hash, &idx->trailer);
+	if (git_oid_cmp(&file_hash, &trailer_hash)) {
+		giterr_set(GITERR_INDEXER, "packfile trailer mismatch");
+		return -1;
+	}
+
+	/* Freeze the number of deltas */
+	stats->total_deltas = stats->total_objects - stats->indexed_objects;
+
+	if (resolve_deltas(idx, stats) < 0)
+		return -1;
 
 	if (stats->indexed_objects != stats->total_objects) {
-		giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+		giterr_set(GITERR_INDEXER, "early EOF");
 		return -1;
 	}
 
+	if (stats->local_objects > 0) {
+		if (update_header_and_rehash(idx, stats) < 0)
+			return -1;
+
+		git_hash_final(&trailer_hash, &idx->trailer);
+		if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
+			return -1;
+
+		if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
+			giterr_set(GITERR_OS, "failed to update pack trailer");
+			return -1;
+		}
+	}
+
 	git_vector_sort(&idx->objects);
 
 	git_buf_sets(&filename, idx->pack->pack_name);
-	git_buf_truncate(&filename, filename.size - strlen("pack"));
+	git_buf_shorten(&filename, strlen("pack"));
 	git_buf_puts(&filename, "idx");
 	if (git_buf_oom(&filename))
 		return -1;
 
-	if (git_filebuf_open(&index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
+	if (git_filebuf_open(&index_file, filename.ptr,
+		GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0)
 		goto on_error;
 
 	/* Write out the header */
@@ -658,30 +959,22 @@
 		git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
 	}
 
-	/* Write out the packfile trailer */
-	packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
-	if (packfile_hash == NULL) {
-		git_mwindow_close(&w);
-		goto on_error;
-	}
-
-	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
-	git_mwindow_close(&w);
-
-	git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
-
-	/* Write out the packfile trailer to the idx file as well */
-	if (git_filebuf_hash(&file_hash, &index_file) < 0)
+	/* Write out the packfile trailer to the index */
+	if (git_filebuf_write(&index_file, &trailer_hash, GIT_OID_RAWSZ) < 0)
 		goto on_error;
 
-	git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
+	/* Write out the hash of the idx */
+	if (git_filebuf_hash(&trailer_hash, &index_file) < 0)
+		goto on_error;
+
+	git_filebuf_write(&index_file, &trailer_hash, sizeof(git_oid));
 
 	/* Figure out what the final name should be */
-	if (index_path_stream(&filename, idx, ".idx") < 0)
+	if (index_path(&filename, idx, ".idx") < 0)
 		goto on_error;
 
 	/* Commit file */
-	if (git_filebuf_commit_at(&index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+	if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
 		goto on_error;
 
 	git_mwindow_free_all(&idx->pack->mwf);
@@ -689,10 +982,10 @@
 	p_close(idx->pack->mwf.fd);
 	idx->pack->mwf.fd = -1;
 
-	if (index_path_stream(&filename, idx, ".pack") < 0)
+	if (index_path(&filename, idx, ".pack") < 0)
 		goto on_error;
 	/* And don't forget to rename the packfile to its new place. */
-	if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+	if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0)
 		return -1;
 
 	git_buf_free(&filename);
@@ -706,7 +999,7 @@
 	return -1;
 }
 
-void git_indexer_stream_free(git_indexer_stream *idx)
+void git_indexer_free(git_indexer *idx)
 {
 	khiter_t k;
 	unsigned int i;
diff --git a/src/iterator.c b/src/iterator.c
index 5917f63..8646399 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -893,6 +893,7 @@
 	git_index_entry entry;
 	git_buf path;
 	size_t root_len;
+	uint32_t dirload_flags;
 	int depth;
 
 	int (*enter_dir_cb)(fs_iterator *self);
@@ -986,12 +987,25 @@
 	GITERR_CHECK_ALLOC(ff);
 
 	error = git_path_dirload_with_stat(
-		fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
+		fi->path.ptr, fi->root_len, fi->dirload_flags,
 		fi->base.start, fi->base.end, &ff->entries);
 
 	if (error < 0) {
+		git_error last_error = {0};
+
+		giterr_detach(&last_error);
+
+		/* these callbacks may clear the error message */
 		fs_iterator__free_frame(ff);
 		fs_iterator__advance_over(NULL, (git_iterator *)fi);
+		/* next time return value we skipped to */
+		fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+
+		if (last_error.message) {
+			giterr_set_str(last_error.klass, last_error.message);
+			free(last_error.message);
+		}
+
 		return error;
 	}
 
@@ -1174,7 +1188,7 @@
 		return GIT_ITEROVER;
 
 	fi->entry.path = ps->path;
-	git_index_entry__init_from_stat(&fi->entry, &ps->st);
+	git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
 
 	/* need different mode here to keep directories during iteration */
 	fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
@@ -1207,6 +1221,11 @@
 	}
 	fi->root_len = fi->path.size;
 
+	fi->dirload_flags =
+		(iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
+		(iterator__flag(fi, PRECOMPOSE_UNICODE) ?
+			GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
+
 	if ((error = fs_iterator__expand_dir(fi)) < 0) {
 		if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
 			giterr_clear();
@@ -1329,7 +1348,7 @@
 	const char *start,
 	const char *end)
 {
-	int error;
+	int error, precompose = 0;
 	workdir_iterator *wi;
 
 	if (!repo_workdir) {
@@ -1350,12 +1369,18 @@
 	wi->fi.update_entry_cb = workdir_iterator__update_entry;
 
 	if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
-		(error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
+		(error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
 	{
 		git_iterator_free((git_iterator *)wi);
 		return error;
 	}
 
+	/* try to look up precompose and set flag if appropriate */
+	if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
+		giterr_clear();
+	else if (precompose)
+		wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+
 	return fs_iterator__initialize(out, &wi->fi, repo_workdir);
 }
 
diff --git a/src/iterator.h b/src/iterator.h
index ea88fa6..751e139 100644
--- a/src/iterator.h
+++ b/src/iterator.h
@@ -24,13 +24,15 @@
 
 typedef enum {
 	/** ignore case for entry sort order */
-	GIT_ITERATOR_IGNORE_CASE = (1 << 0),
+	GIT_ITERATOR_IGNORE_CASE = (1u << 0),
 	/** force case sensitivity for entry sort order */
-	GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
+	GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
 	/** return tree items in addition to blob items */
-	GIT_ITERATOR_INCLUDE_TREES    = (1 << 2),
+	GIT_ITERATOR_INCLUDE_TREES    = (1u << 2),
 	/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
-	GIT_ITERATOR_DONT_AUTOEXPAND  = (1 << 3),
+	GIT_ITERATOR_DONT_AUTOEXPAND  = (1u << 3),
+	/** convert precomposed unicode to decomposed unicode */
+	GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
 } git_iterator_flag_t;
 
 typedef struct {
diff --git a/src/merge.c b/src/merge.c
index 82d2e6f..1158679 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -58,7 +58,7 @@
 
 /* Merge base computation */
 
-int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
+int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
 {
 	git_revwalk *walk;
 	git_vector list;
@@ -285,7 +285,7 @@
 		if ((error = git_oid_fromstr(&oid, line)) < 0)
 			goto cleanup;
 
-		if (cb(&oid, payload) < 0) {
+		if (cb(&oid, payload) != 0) {
 			error = GIT_EUSER;
 			goto cleanup;
 		}
@@ -1615,17 +1615,14 @@
 {
 	git_filebuf file = GIT_FILEBUF_INIT;
 	git_buf file_path = GIT_BUF_INIT;
-	char orig_oid_str[GIT_OID_HEXSZ + 1];
 	int error = 0;
 
 	assert(repo && our_head);
 
-	git_oid_tostr(orig_oid_str, GIT_OID_HEXSZ+1, &our_head->oid);
-
 	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 &&
-		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) == 0 &&
-		(error = git_filebuf_printf(&file, "%s\n", orig_oid_str)) == 0)
-		error = git_filebuf_commit(&file, 0666);
+		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
+		(error = git_filebuf_printf(&file, "%s\n", our_head->oid_str)) == 0)
+		error = git_filebuf_commit(&file);
 
 	if (error < 0)
 		git_filebuf_cleanup(&file);
@@ -1642,24 +1639,21 @@
 {
 	git_filebuf file = GIT_FILEBUF_INIT;
 	git_buf file_path = GIT_BUF_INIT;
-	char merge_oid_str[GIT_OID_HEXSZ + 1];
 	size_t i;
 	int error = 0;
 
 	assert(repo && heads);
 
 	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_HEAD_FILE)) < 0 ||
-		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
+		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
 		goto cleanup;
 
 	for (i = 0; i < heads_len; i++) {
-		git_oid_tostr(merge_oid_str, GIT_OID_HEXSZ+1, &heads[i]->oid);
-
-		if ((error = git_filebuf_printf(&file, "%s\n", merge_oid_str)) < 0)
+		if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->oid_str)) < 0)
 			goto cleanup;
 	}
 
-	error = git_filebuf_commit(&file, 0666);
+	error = git_filebuf_commit(&file);
 
 cleanup:
 	if (error < 0)
@@ -1682,10 +1676,21 @@
 	assert(repo);
 
 	if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 ||
-		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 0)
+		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0)
 		goto cleanup;
 
-	error = git_filebuf_commit(&file, 0666);
+	/*
+	 * no-ff is the only thing allowed here at present.  One would
+	 * presume they would be space-delimited when there are more, but
+	 * this needs to be revisited.
+	 */
+
+	if (flags & GIT_MERGE_NO_FASTFORWARD) {
+		if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
+			goto cleanup;
+	}
+
+	error = git_filebuf_commit(&file);
 
 cleanup:
 	if (error < 0)
@@ -1890,7 +1895,6 @@
 {
 	git_filebuf file = GIT_FILEBUF_INIT;
 	git_buf file_path = GIT_BUF_INIT;
-	char oid_str[GIT_OID_HEXSZ + 1];
 	struct merge_msg_entry *entries;
 	git_vector matching = GIT_VECTOR_INIT;
 	size_t i;
@@ -1902,14 +1906,16 @@
 	entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
 	GITERR_CHECK_ALLOC(entries); 
 
-	if (git_vector_init(&matching, heads_len, NULL) < 0)
+	if (git_vector_init(&matching, heads_len, NULL) < 0) {
+		git__free(entries);
 		return -1;
+	}
 
 	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 ||
-		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE)) < 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;
 
@@ -1928,10 +1934,9 @@
 		if (!msg_entry_is_oid(&entries[i]))
 			break;
 
-		git_oid_fmt(oid_str, &entries[i].merge_head->oid);
-		oid_str[GIT_OID_HEXSZ] = '\0';
-
-		if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", oid_str)) < 0)
+		if ((error = git_filebuf_printf(&file,
+			"%scommit '%s'", (i > 0) ? "; " : "",
+			entries[i].merge_head->oid_str)) < 0)
 			goto cleanup;
 
 		entries[i].written = 1;
@@ -1978,15 +1983,13 @@
 		if (merge_msg_entry_written(&entries[i]))
 			continue;
 
-		git_oid_fmt(oid_str, &entries[i].merge_head->oid);
-		oid_str[GIT_OID_HEXSZ] = '\0';
-
-		if ((error = git_filebuf_printf(&file, "; commit '%s'", oid_str)) < 0)
+		if ((error = git_filebuf_printf(&file, "; commit '%s'",
+			entries[i].merge_head->oid_str)) < 0)
 			goto cleanup;
 	}
 
 	if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
-		(error = git_filebuf_commit(&file, 0666)) < 0)
+		(error = git_filebuf_commit(&file)) < 0)
 		goto cleanup;
 
 cleanup:
@@ -2001,6 +2004,477 @@
 	return error;
 }
 
+/* Merge branches */
+
+static int merge_ancestor_head(
+	git_merge_head **ancestor_head,
+	git_repository *repo,
+	const git_merge_head *our_head,
+	const git_merge_head **their_heads,
+	size_t their_heads_len)
+{
+	git_oid *oids, ancestor_oid;
+	size_t i;
+	int error = 0;
+
+	assert(repo && our_head && their_heads);
+
+	oids = git__calloc(their_heads_len + 1, sizeof(git_oid));
+	GITERR_CHECK_ALLOC(oids);
+
+	git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
+
+	for (i = 0; i < their_heads_len; i++)
+		git_oid_cpy(&oids[i + 1], &their_heads[i]->oid);
+
+	if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
+		goto on_error;
+
+	error = git_merge_head_from_oid(ancestor_head, repo, &ancestor_oid);
+
+on_error:
+	git__free(oids);
+	return error;
+}
+
+GIT_INLINE(bool) merge_check_uptodate(
+	git_merge_result *result,
+	const git_merge_head *ancestor_head,
+	const git_merge_head *their_head)
+{
+	if (git_oid_cmp(&ancestor_head->oid, &their_head->oid) == 0) {
+		result->is_uptodate = 1;
+		return true;
+	}
+
+	return false;
+}
+
+GIT_INLINE(bool) merge_check_fastforward(
+	git_merge_result *result,
+	const git_merge_head *ancestor_head,
+	const git_merge_head *our_head,
+	const git_merge_head *their_head,
+	unsigned int flags)
+{
+	if ((flags & GIT_MERGE_NO_FASTFORWARD) == 0 &&
+		git_oid_cmp(&ancestor_head->oid, &our_head->oid) == 0) {
+		result->is_fastforward = 1;
+		git_oid_cpy(&result->fastforward_oid, &their_head->oid);
+
+		return true;
+	}
+
+	return false;
+}
+
+const char *merge_their_label(const char *branchname)
+{
+	const char *slash;
+
+	if ((slash = strrchr(branchname, '/')) == NULL)
+		return branchname;
+
+	if (*(slash+1) == '\0')
+		return "theirs";
+
+	return slash+1;
+}
+
+static int merge_normalize_opts(
+	git_repository *repo,
+	git_merge_opts *opts,
+	const git_merge_opts *given,
+	size_t their_heads_len,
+	const git_merge_head **their_heads)
+{
+	int error = 0;
+	unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE |
+		GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+	GIT_UNUSED(repo);
+
+	if (given != NULL)
+		memcpy(opts, given, sizeof(git_merge_opts));
+	else {
+		git_merge_opts default_opts = GIT_MERGE_OPTS_INIT;
+		memcpy(opts, &default_opts, sizeof(git_merge_opts));
+	}
+
+	if (!opts->checkout_opts.checkout_strategy)
+		opts->checkout_opts.checkout_strategy = default_checkout_strategy;
+
+	if (!opts->checkout_opts.our_label)
+		opts->checkout_opts.our_label = "HEAD";
+
+	if (!opts->checkout_opts.their_label) {
+		if (their_heads_len == 1 && their_heads[0]->ref_name)
+			opts->checkout_opts.their_label = merge_their_label(their_heads[0]->ref_name);
+		else if (their_heads_len == 1)
+			opts->checkout_opts.their_label = their_heads[0]->oid_str;
+		else
+			opts->checkout_opts.their_label = "theirs";
+	}
+
+	return error;
+}
+
+static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new)
+{
+	git_tree *head_tree = NULL;
+	git_iterator *iter_head = NULL, *iter_new = NULL;
+	git_diff *merged_list = NULL;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_delta *delta;
+	size_t i;
+	const git_index_entry *e;
+	char *path;
+	int error = 0;
+
+	if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+		(error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+		(error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+		(error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
+		goto done;
+
+	git_vector_foreach(&merged_list->deltas, i, delta) {
+		path = git__strdup(delta->new_file.path);
+		GITERR_CHECK_ALLOC(path);
+
+		if ((error = git_vector_insert(paths, path)) < 0)
+			goto on_error;
+	}
+
+	for (i = 0; i < git_index_entrycount(index_new); i++) {
+		e = git_index_get_byindex(index_new, i);
+
+		if (git_index_entry_stage(e) != 0 &&
+			(git_vector_last(paths) == NULL ||
+			strcmp(git_vector_last(paths), e->path) != 0)) {
+
+			path = git__strdup(e->path);
+			GITERR_CHECK_ALLOC(path);
+
+			if ((error = git_vector_insert(paths, path)) < 0)
+				goto on_error;
+		}
+	}
+
+	goto done;
+
+on_error:
+	git_vector_foreach(paths, i, path)
+		git__free(path);
+
+	git_vector_clear(paths);
+
+done:
+	git_tree_free(head_tree);
+	git_iterator_free(iter_head);
+	git_iterator_free(iter_new);
+	git_diff_free(merged_list);
+
+	return error;
+}
+
+static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+	git_tree *head_tree = NULL;
+	git_index *index_repo = NULL;
+	git_iterator *iter_repo = NULL, *iter_new = NULL;
+	git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
+	git_diff_delta *delta;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_vector staged_paths = GIT_VECTOR_INIT;
+	size_t i;
+	int error = 0;
+
+	GIT_UNUSED(merged_paths);
+
+	*conflicts = 0;
+
+	/* No staged changes may exist unless the change staged is identical to
+	 * the result of the merge.  This allows one to apply to merge manually,
+	 * then run merge.  Any other staged change would be overwritten by
+	 * a reset merge.
+	 */
+	if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+		(error = git_repository_index(&index_repo, repo)) < 0 ||
+		(error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
+		goto done;
+
+	if (staged_diff_list->deltas.length == 0)
+		goto done;
+
+	git_vector_foreach(&staged_diff_list->deltas, i, delta) {
+		if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
+			goto done;
+	}
+
+	opts.pathspec.count = staged_paths.length;
+	opts.pathspec.strings = (char **)staged_paths.contents;
+
+	if ((error = git_iterator_for_index(&iter_repo, index_repo, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+		(error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
+		(error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
+		goto done;
+
+	*conflicts = index_diff_list->deltas.length;
+
+done:
+	git_tree_free(head_tree);
+	git_index_free(index_repo);
+	git_iterator_free(iter_repo);
+	git_iterator_free(iter_new);
+	git_diff_free(staged_diff_list);
+	git_diff_free(index_diff_list);
+	git_vector_free(&staged_paths);
+
+	return error;
+}
+
+static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+	git_tree *head_tree = NULL;
+	git_diff *wd_diff_list = NULL;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	int error = 0;
+
+	GIT_UNUSED(index_new);
+
+	*conflicts = 0;
+
+	opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+	if ((error = git_repository_head_tree(&head_tree, repo)) < 0)
+		goto done;
+
+	/* Workdir changes may exist iff they do not conflict with changes that
+	 * will be applied by the merge (including conflicts).  Ensure that there
+	 * are no changes in the workdir to these paths.
+	 */
+	opts.pathspec.count = merged_paths->length;
+	opts.pathspec.strings = (char **)merged_paths->contents;
+
+	if ((error = git_diff_tree_to_workdir(&wd_diff_list, repo, head_tree, &opts)) < 0)
+		goto done;
+
+	*conflicts = wd_diff_list->deltas.length;
+
+done:
+	git_tree_free(head_tree);
+	git_diff_free(wd_diff_list);
+
+	return error;
+}
+
+static int merge_indexes(git_repository *repo, git_index *index_new)
+{
+	git_index *index_repo;
+	unsigned int index_repo_caps;
+	git_vector paths = GIT_VECTOR_INIT;
+	size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i;
+	char *path;
+	const git_index_entry *e;
+	const git_index_name_entry *name;
+	const git_index_reuc_entry *reuc;
+	int error = 0;
+
+	if ((error = git_repository_index(&index_repo, repo)) < 0)
+		goto done;
+
+	/* Set the index to case sensitive to handle the merge */
+	index_repo_caps = git_index_caps(index_repo);
+
+	if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0)
+		goto done;
+
+	/* Make sure the index and workdir state do not prevent merging */
+	if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 ||
+		(error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
+		(error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
+		goto done;
+
+	if ((conflicts = index_conflicts + wd_conflicts) > 0) {
+		giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge",
+			conflicts, (conflicts != 1) ? "s" : "");
+		error = GIT_EMERGECONFLICT;
+
+		goto done;
+	}
+
+	/* Update the new index */
+	git_vector_foreach(&paths, i, path) {
+		if ((e = git_index_get_bypath(index_new, path, 0)) != NULL)
+			error = git_index_add(index_repo, e);
+		else
+			error = git_index_remove(index_repo, path, 0);
+	}
+
+	/* Add conflicts */
+	git_index_conflict_cleanup(index_repo);
+
+	for (i = 0; i < git_index_entrycount(index_new); i++) {
+		e = git_index_get_byindex(index_new, i);
+
+		if (git_index_entry_stage(e) != 0 &&
+			(error = git_index_add(index_repo, e)) < 0)
+			goto done;
+	}
+
+	/* Add name entries */
+	git_index_name_clear(index_repo);
+
+	for (i = 0; i < git_index_name_entrycount(index_new); i++) {
+		name = git_index_name_get_byindex(index_new, i);
+
+		if ((error = git_index_name_add(index_repo,
+			name->ancestor, name->ours, name->theirs)) < 0)
+			goto done;
+	}
+
+	/* Add the reuc */
+	git_index_reuc_clear(index_repo);
+
+	for (i = 0; i < git_index_reuc_entrycount(index_new); i++) {
+		reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i);
+
+		if ((error = git_index_reuc_add(index_repo, reuc->path,
+			reuc->mode[0], &reuc->oid[0],
+			reuc->mode[1], &reuc->oid[1],
+			reuc->mode[2], &reuc->oid[2])) < 0)
+			goto done;
+	}
+
+done:
+	if (index_repo != NULL)
+        git_index_set_caps(index_repo, index_repo_caps);
+
+	git_index_free(index_repo);
+
+	git_vector_foreach(&paths, i, path)
+		git__free(path);
+
+	git_vector_free(&paths);
+
+	return error;
+}
+
+int git_merge(
+	git_merge_result **out,
+	git_repository *repo,
+	const git_merge_head **their_heads,
+	size_t their_heads_len,
+	const git_merge_opts *given_opts)
+{
+	git_merge_result *result;
+	git_merge_opts opts;
+	git_reference *our_ref = NULL;
+	git_merge_head *ancestor_head = NULL, *our_head = NULL;
+	git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL;
+	git_index *index_new = NULL, *index_repo = NULL;
+	size_t i;
+	int error = 0;
+
+	assert(out && repo && their_heads);
+
+	*out = NULL;
+
+	if (their_heads_len != 1) {
+		giterr_set(GITERR_MERGE, "Can only merge a single branch");
+		return -1;
+	}
+
+	result = git__calloc(1, sizeof(git_merge_result));
+	GITERR_CHECK_ALLOC(result);
+
+	their_trees = git__calloc(their_heads_len, sizeof(git_tree *));
+	GITERR_CHECK_ALLOC(their_trees);
+
+	if ((error = merge_normalize_opts(repo, &opts, given_opts, their_heads_len, their_heads)) < 0)
+		goto on_error;
+
+	if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
+		goto on_error;
+
+	if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
+		(error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0)
+		goto on_error;
+
+	if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0 &&
+		error != GIT_ENOTFOUND)
+		goto on_error;
+
+	if (their_heads_len == 1 &&
+		ancestor_head != NULL &&
+		(merge_check_uptodate(result, ancestor_head, their_heads[0]) ||
+		merge_check_fastforward(result, ancestor_head, our_head, their_heads[0], opts.merge_flags))) {
+		*out = result;
+		goto done;
+	}
+
+	/* If FASTFORWARD_ONLY is specified, fail. */
+	if ((opts.merge_flags & GIT_MERGE_FASTFORWARD_ONLY) ==
+		GIT_MERGE_FASTFORWARD_ONLY) {
+		giterr_set(GITERR_MERGE, "Not a fast-forward.");
+		error = GIT_ENONFASTFORWARD;
+		goto on_error;
+	}
+
+	/* Write the merge files to the repository. */
+	if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len, opts.merge_flags)) < 0)
+		goto on_error;
+
+	if (ancestor_head != NULL &&
+		(error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0)
+			goto on_error;
+
+	if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0)
+		goto on_error;
+
+	for (i = 0; i < their_heads_len; i++) {
+		if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0)
+			goto on_error;
+	}
+
+	/* TODO: recursive, octopus, etc... */
+
+	if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 ||
+		(error = merge_indexes(repo, index_new)) < 0 ||
+		(error = git_repository_index(&index_repo, repo)) < 0 ||
+		(error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0)
+		goto on_error;
+
+	result->index = index_new;
+
+	*out = result;
+	goto done;
+
+on_error:
+	git_repository_merge_cleanup(repo);
+
+	git_index_free(index_new);
+	git__free(result);
+
+done:
+	git_index_free(index_repo);
+
+	git_tree_free(ancestor_tree);
+	git_tree_free(our_tree);
+
+	for (i = 0; i < their_heads_len; i++)
+		git_tree_free(their_trees[i]);
+
+	git__free(their_trees);
+
+	git_merge_head_free(our_head);
+	git_merge_head_free(ancestor_head);
+
+	git_reference_free(our_ref);
+
+	return error;
+}
+
 int git_merge__setup(
 	git_repository *repo,
 	const git_merge_head *our_head,
@@ -2011,7 +2485,7 @@
 	int error = 0;
 
 	assert (repo && our_head && heads);
-	
+
 	if ((error = write_orig_head(repo, our_head)) == 0 &&
 		(error = write_merge_head(repo, heads, heads_len)) == 0 &&
 		(error = write_merge_mode(repo, flags)) == 0) {
@@ -2054,6 +2528,41 @@
 	return error;
 }
 
+/* Merge result data */
+
+int git_merge_result_is_uptodate(git_merge_result *merge_result)
+{
+	assert(merge_result);
+
+	return merge_result->is_uptodate;
+}
+
+int git_merge_result_is_fastforward(git_merge_result *merge_result)
+{
+	assert(merge_result);
+
+	return merge_result->is_fastforward;
+}
+
+int git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result)
+{
+	assert(out && merge_result);
+
+	git_oid_cpy(out, &merge_result->fastforward_oid);
+	return 0;
+}
+
+void git_merge_result_free(git_merge_result *merge_result)
+{
+	if (merge_result == NULL)
+		return;
+
+	git_index_free(merge_result->index);
+	merge_result->index = NULL;
+
+	git__free(merge_result);
+}
+
 /* Merge heads are the input to merge */
 
 static int merge_head_init(
@@ -2085,6 +2594,9 @@
 
 	git_oid_cpy(&head->oid, oid);
 
+	git_oid_fmt(head->oid_str, oid);
+	head->oid_str[GIT_OID_HEXSZ] = '\0';
+
 	if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) {
 		git_merge_head_free(head);
 		return error;
diff --git a/src/merge.h b/src/merge.h
index ba6725d..d7d1c67 100644
--- a/src/merge.h
+++ b/src/merge.h
@@ -16,6 +16,7 @@
 
 #define GIT_MERGE_MSG_FILE		"MERGE_MSG"
 #define GIT_MERGE_MODE_FILE		"MERGE_MODE"
+#define GIT_MERGE_FILE_MODE		0666
 
 #define GIT_MERGE_TREE_RENAME_THRESHOLD	50
 #define GIT_MERGE_TREE_TARGET_LIMIT		1000
@@ -113,9 +114,20 @@
 	char *remote_url;
 
 	git_oid oid;
+	char oid_str[GIT_OID_HEXSZ+1];
 	git_commit *commit;
 };
 
+/** Internal structure for merge results */
+struct git_merge_result {
+	bool is_uptodate;
+
+	bool is_fastforward;
+	git_oid fastforward_oid;
+
+	git_index *index;
+};
+
 int git_merge__bases_many(
 	git_commit_list **out,
 	git_revwalk *walk,
diff --git a/src/merge_file.c b/src/merge_file.c
index c3477cc..48fc46e 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -47,7 +47,7 @@
 	 * assume executable.  Otherwise, if any mode changed from the ancestor,
 	 * use that one.
 	 */
-	if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
+	if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
 		if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
 			theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
 			return GIT_FILEMODE_BLOB_EXECUTABLE;
diff --git a/src/netops.c b/src/netops.c
index 69179dd..ad27d84 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -19,10 +19,6 @@
 #	endif
 #endif
 
-#ifdef __FreeBSD__
-#	include <netinet/in.h>
-#endif
-
 #ifdef GIT_SSL
 # include <openssl/ssl.h>
 # include <openssl/err.h>
@@ -36,6 +32,7 @@
 #include "netops.h"
 #include "posix.h"
 #include "buffer.h"
+#include "http_parser.h"
 
 #ifdef GIT_WIN32
 static void net_set_error(const char *str)
@@ -577,55 +574,161 @@
 	return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
 }
 
+static const char *prefix_http = "http://";
+static const char *prefix_https = "https://";
+
+int gitno_connection_data_from_url(
+		gitno_connection_data *data,
+		const char *url,
+		const char *service_suffix)
+{
+	int error = -1;
+	const char *default_port = NULL, *path_search_start = NULL;
+	char *original_host = NULL;
+
+	/* service_suffix is optional */
+	assert(data && url);
+
+	/* Save these for comparison later */
+	original_host = data->host;
+	data->host = NULL;
+	gitno_connection_data_free_ptrs(data);
+
+	if (!git__prefixcmp(url, prefix_http)) {
+		path_search_start = url + strlen(prefix_http);
+		default_port = "80";
+
+		if (data->use_ssl) {
+			giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed");
+			goto cleanup;
+		}
+	} else if (!git__prefixcmp(url, prefix_https)) {
+		path_search_start = url + strlen(prefix_https);
+		default_port = "443";
+		data->use_ssl = true;
+	} else if (url[0] == '/')
+		default_port = data->use_ssl ? "443" : "80";
+
+	if (!default_port) {
+		giterr_set(GITERR_NET, "Unrecognized URL prefix");
+		goto cleanup;
+	}
+
+	error = gitno_extract_url_parts(
+		&data->host, &data->port, &data->path, &data->user, &data->pass,
+		url, default_port);
+
+	if (url[0] == '/') {
+		/* Relative redirect; reuse original host name and port */
+		path_search_start = url;
+		git__free(data->host);
+		data->host = original_host;
+		original_host = NULL;
+	}
+
+	if (!error) {
+		const char *path = strchr(path_search_start, '/');
+		size_t pathlen = strlen(path);
+		size_t suffixlen = service_suffix ? strlen(service_suffix) : 0;
+
+		if (suffixlen &&
+		    !memcmp(path + pathlen - suffixlen, service_suffix, suffixlen)) {
+			git__free(data->path);
+			data->path = git__strndup(path, pathlen - suffixlen);
+		} else {
+			git__free(data->path);
+			data->path = git__strdup(path);
+		}
+
+		/* Check for errors in the resulting data */
+		if (original_host && url[0] != '/' && strcmp(original_host, data->host)) {
+			giterr_set(GITERR_NET, "Cross host redirect not allowed");
+			error = -1;
+		}
+	}
+
+cleanup:
+	if (original_host) git__free(original_host);
+	return error;
+}
+
+void gitno_connection_data_free_ptrs(gitno_connection_data *d)
+{
+	git__free(d->host); d->host = NULL;
+	git__free(d->port); d->port = NULL;
+	git__free(d->path); d->path = NULL;
+	git__free(d->user); d->user = NULL;
+	git__free(d->pass); d->pass = NULL;
+}
+
+#define hex2c(c) ((c | 32) % 39 - 9)
+static char* unescape(char *str)
+{
+	int x, y;
+	int len = (int)strlen(str);
+
+	for (x=y=0; str[y]; ++x, ++y) {
+		if ((str[x] = str[y]) == '%') {
+			if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) {
+				str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]);
+				y += 2;
+			}
+		}
+	}
+	str[x] = '\0';
+	return str;
+}
+
 int gitno_extract_url_parts(
 		char **host,
 		char **port,
+		char **path,
 		char **username,
 		char **password,
 		const char *url,
 		const char *default_port)
 {
-	char *colon, *slash, *at, *end;
-	const char *start;
+	struct http_parser_url u = {0};
+	const char *_host, *_port, *_path, *_userinfo;
 
-	/*
-	 *
-	 * ==> [user[:pass]@]hostname.tld[:port]/resource
-	 */
-
-	colon = strchr(url, ':');
-	slash = strchr(url, '/');
-	at = strchr(url, '@');
-
-	if (slash == NULL) {
-		giterr_set(GITERR_NET, "Malformed URL: missing /");
-		return -1;
+	if (http_parser_parse_url(url, strlen(url), false, &u)) {
+		giterr_set(GITERR_NET, "Malformed URL '%s'", url);
+		return GIT_EINVALIDSPEC;
 	}
 
-	start = url;
-	if (at && at < slash) {
-		start = at+1;
-		*username = git__substrdup(url, at - url);
+	_host = url+u.field_data[UF_HOST].off;
+	_port = url+u.field_data[UF_PORT].off;
+	_path = url+u.field_data[UF_PATH].off;
+	_userinfo = url+u.field_data[UF_USERINFO].off;
+
+	if (u.field_set & (1 << UF_HOST)) {
+		*host = git__substrdup(_host, u.field_data[UF_HOST].len);
+		GITERR_CHECK_ALLOC(*host);
 	}
 
-	if (colon && colon < at) {
-		git__free(*username);
-		*username = git__substrdup(url, colon-url);
-		*password = git__substrdup(colon+1, at-colon-1);
-		colon = strchr(at, ':');
-	}
-
-	if (colon == NULL) {
+	if (u.field_set & (1 << UF_PORT))
+		*port = git__substrdup(_port, u.field_data[UF_PORT].len);
+	else
 		*port = git__strdup(default_port);
-	} else {
-		*port = git__substrdup(colon + 1, slash - colon - 1);
-	}
 	GITERR_CHECK_ALLOC(*port);
 
-	end = colon == NULL ? slash : colon;
+	if (u.field_set & (1 << UF_PATH)) {
+		*path = git__substrdup(_path, u.field_data[UF_PATH].len);
+		GITERR_CHECK_ALLOC(*path);
+	}
 
-	*host = git__substrdup(start, end - start);
-	GITERR_CHECK_ALLOC(*host);
+	if (u.field_set & (1 << UF_USERINFO)) {
+		const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len);
+		if (colon) {
+			*username = unescape(git__substrdup(_userinfo, colon - _userinfo));
+			*password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
+			GITERR_CHECK_ALLOC(*password);
+		} else {
+			*username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
+		}
+		GITERR_CHECK_ALLOC(*username);
+
+	}
 
 	return 0;
 }
diff --git a/src/netops.h b/src/netops.h
index d352bf3..666d66b 100644
--- a/src/netops.h
+++ b/src/netops.h
@@ -66,9 +66,33 @@
 int gitno_close(gitno_socket *s);
 int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
 
+typedef struct gitno_connection_data {
+	char *host;
+	char *port;
+	char *path;
+	char *user;
+	char *pass;
+	bool use_ssl;
+} gitno_connection_data;
+
+/*
+ * This replaces all the pointers in `data` with freshly-allocated strings,
+ * that the caller is responsible for freeing.
+ * `gitno_connection_data_free_ptrs` is good for this.
+ */
+
+int gitno_connection_data_from_url(
+		gitno_connection_data *data,
+		const char *url,
+		const char *service_suffix);
+
+/* This frees all the pointers IN the struct, but not the struct itself. */
+void gitno_connection_data_free_ptrs(gitno_connection_data *data);
+
 int gitno_extract_url_parts(
 		char **host,
 		char **port,
+		char **path,
 		char **username,
 		char **password,
 		const char *url,
diff --git a/src/object.c b/src/object.c
index 9b8ccdd..3fc984b 100644
--- a/src/object.c
+++ b/src/object.c
@@ -364,3 +364,38 @@
 	*dest = source;
 	return 0;
 }
+
+int git_object_lookup_bypath(
+		git_object **out,
+		const git_object *treeish,
+		const char *path,
+		git_otype type)
+{
+	int error = -1;
+	git_tree *tree = NULL;
+	git_tree_entry *entry = NULL;
+
+	assert(out && treeish && path);
+
+	if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE) < 0) ||
+		 (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
+	{
+		goto cleanup;
+	}
+
+	if (type != GIT_OBJ_ANY && git_tree_entry_type(entry) != type)
+	{
+		giterr_set(GITERR_OBJECT,
+				"object at path '%s' is not of the asked-for type %d",
+				path, type);
+		error = GIT_EINVALIDSPEC;
+		goto cleanup;
+	}
+
+	error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);
+
+cleanup:
+	git_tree_entry_free(entry);
+	git_tree_free(tree);
+	return error;
+}
diff --git a/src/odb.c b/src/odb.c
index 8e62efd..b208b27 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -124,6 +124,13 @@
 	return object->cached.type;
 }
 
+int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
+{
+	git_cached_obj_incref(source);
+	*dest = source;
+	return 0;
+}
+
 void git_odb_object_free(git_odb_object *object)
 {
 	if (object == NULL)
@@ -168,7 +175,6 @@
 		error = -1;
 
 		goto done;
-		return -1;
 	}
 
 	error = git_hash_final(out, &ctx);
@@ -179,28 +185,30 @@
 }
 
 int git_odb__hashfd_filtered(
-	git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
+	git_oid *out, git_file fd, size_t size, git_otype type, git_filter_list *fl)
 {
 	int error;
 	git_buf raw = GIT_BUF_INIT;
-	git_buf filtered = GIT_BUF_INIT;
 
-	if (!filters || !filters->length)
+	if (!fl)
 		return git_odb__hashfd(out, fd, size, type);
 
 	/* size of data is used in header, so we have to read the whole file
 	 * into memory to apply filters before beginning to calculate the hash
 	 */
 
-	if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
-		error = git_filters_apply(&filtered, &raw, filters);
+	if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
+		git_buf post = GIT_BUF_INIT;
 
-	git_buf_free(&raw);
+		error = git_filter_list_apply_to_data(&post, fl, &raw);
 
-	if (!error)
-		error = git_odb_hash(out, filtered.ptr, filtered.size, type);
+		git_buf_free(&raw);
 
-	git_buf_free(&filtered);
+		if (!error)
+			error = git_odb_hash(out, post.ptr, post.size, type);
+
+		git_buf_free(&post);
+	}
 
 	return error;
 }
@@ -232,6 +240,7 @@
 		link_data[size] = '\0';
 		if (read_len != (ssize_t)size) {
 			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+			git__free(link_data);
 			return -1;
 		}
 
@@ -290,10 +299,10 @@
 	git_otype type;
 } fake_wstream;
 
-static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream)
+static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid)
 {
 	fake_wstream *stream = (fake_wstream *)_stream;
-	return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type);
+	return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type);
 }
 
 static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
@@ -444,7 +453,7 @@
 		return 0;
 	}
 
-	giterr_set(GITERR_ODB, "No ODB backend loaded at index " PRIuZ, pos);
+	giterr_set(GITERR_ODB, "No ODB backend loaded at index %" PRIuZ, pos);
 	return GIT_ENOTFOUND;
 }
 
@@ -483,7 +492,7 @@
 #endif
 
 	/* add the loose object backend */
-	if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
+	if (git_odb_backend_loose(&loose, objects_dir, -1, 0, 0, 0) < 0 ||
 		add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates, inode) < 0)
 		return -1;
 
@@ -607,7 +616,6 @@
 	git_odb_object *object;
 	size_t i;
 	bool found = false;
-	bool refreshed = false;
 
 	assert(db && id);
 
@@ -616,23 +624,12 @@
 		return (int)true;
 	}
 
-attempt_lookup:
 	for (i = 0; i < db->backends.length && !found; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
 
 		if (b->exists != NULL)
-			found = b->exists(b, id);
-	}
-
-	if (!found && !refreshed) {
-		if (git_odb_refresh(db) < 0) {
-			giterr_clear();
-			return (int)false;
-		}
-
-		refreshed = true;
-		goto attempt_lookup;
+			found = (bool)b->exists(b, id);
 	}
 
 	return (int)found;
@@ -699,7 +696,6 @@
 {
 	size_t i, reads = 0;
 	int error;
-	bool refreshed = false;
 	git_rawobj raw;
 	git_odb_object *object;
 
@@ -709,7 +705,6 @@
 	if (*out != NULL)
 		return 0;
 
-attempt_lookup:
 	error = GIT_ENOTFOUND;
 
 	for (i = 0; i < db->backends.length && error < 0; ++i) {
@@ -722,14 +717,6 @@
 		}
 	}
 
-	if (error == GIT_ENOTFOUND && !refreshed) {
-		if ((error = git_odb_refresh(db)) < 0)
-			return error;
-
-		refreshed = true;
-		goto attempt_lookup;
-	}
-
 	if (error && error != GIT_PASSTHROUGH) {
 		if (!reads)
 			return git_odb__error_notfound("no match for id", id);
@@ -751,7 +738,7 @@
 	git_oid found_full_oid = {{0}};
 	git_rawobj raw;
 	void *data = NULL;
-	bool found = false, refreshed = false;
+	bool found = false;
 	git_odb_object *object;
 
 	assert(out && db);
@@ -768,7 +755,6 @@
 			return 0;
 	}
 
-attempt_lookup:
 	for (i = 0; i < db->backends.length; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -785,22 +771,16 @@
 			git__free(data);
 			data = raw.data;
 
-			if (found && git_oid__cmp(&full_oid, &found_full_oid))
+			if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
+				git__free(raw.data);
 				return git_odb__error_ambiguous("multiple matches for prefix");
+			}
 
 			found_full_oid = full_oid;
 			found = true;
 		}
 	}
 
-	if (!found && !refreshed) {
-		if ((error = git_odb_refresh(db)) < 0)
-			return error;
-
-		refreshed = true;
-		goto attempt_lookup;
-	}
-
 	if (!found)
 		return git_odb__error_notfound("no match for prefix", short_id);
 
@@ -848,7 +828,7 @@
 			continue;
 
 		if (b->write != NULL)
-			error = b->write(oid, b, data, len, type);
+			error = b->write(b, oid, data, len, type);
 	}
 
 	if (!error || error == GIT_PASSTHROUGH)
@@ -862,17 +842,27 @@
 		return error;
 
 	stream->write(stream, data, len);
-	error = stream->finalize_write(oid, stream);
-	stream->free(stream);
+	error = stream->finalize_write(stream, oid);
+	git_odb_stream_free(stream);
 
 	return error;
 }
 
+static void hash_header(git_hash_ctx *ctx, size_t size, git_otype type)
+{
+	char header[64];
+	int hdrlen;
+
+	hdrlen = git_odb__format_object_header(header, sizeof(header), size, type);
+	git_hash_update(ctx, header, hdrlen);
+}
+
 int git_odb_open_wstream(
 	git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
 {
 	size_t i, writes = 0;
 	int error = GIT_ERROR;
+	git_hash_ctx *ctx;
 
 	assert(stream && db);
 
@@ -898,9 +888,71 @@
 	if (error < 0 && !writes)
 		error = git_odb__error_unsupported_in_backend("write object");
 
+	ctx = git__malloc(sizeof(git_hash_ctx));
+	GITERR_CHECK_ALLOC(ctx);
+
+
+	git_hash_ctx_init(ctx);
+	hash_header(ctx, size, type);
+	(*stream)->hash_ctx = ctx;
+
+	(*stream)->declared_size = size;
+	(*stream)->received_bytes = 0;
+
 	return error;
 }
 
+static int git_odb_stream__invalid_length(
+	const git_odb_stream *stream,
+	const char *action)
+{
+	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);		
+
+	return -1;
+}
+
+int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
+{
+	git_hash_update(stream->hash_ctx, buffer, len);
+
+	stream->received_bytes += len;
+
+	if (stream->received_bytes > stream->declared_size)
+		return git_odb_stream__invalid_length(stream,
+			"stream_write()");
+
+	return stream->write(stream, buffer, len);
+}
+
+int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
+{
+	if (stream->received_bytes != stream->declared_size)
+		return git_odb_stream__invalid_length(stream,
+			"stream_finalize_write()");
+
+	git_hash_final(out, stream->hash_ctx);
+
+	if (git_odb_exists(stream->backend->odb, out))
+		return 0;
+
+	return stream->finalize_write(stream, out);
+}
+
+int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len)
+{
+	return stream->read(stream, buffer, len);
+}
+
+void git_odb_stream_free(git_odb_stream *stream)
+{
+	git__free(stream->hash_ctx);
+	stream->free(stream);
+}
+
 int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
 {
 	size_t i, reads = 0;
@@ -943,7 +995,7 @@
 
 		if (b->writepack != NULL) {
 			++writes;
-			error = b->writepack(out, b, progress_cb, progress_payload);
+			error = b->writepack(out, b, db, progress_cb, progress_payload);
 		}
 	}
 
diff --git a/src/odb.h b/src/odb.h
index 0d9f9e2..61dd9a7 100644
--- a/src/odb.h
+++ b/src/odb.h
@@ -14,6 +14,7 @@
 #include "vector.h"
 #include "cache.h"
 #include "posix.h"
+#include "filter.h"
 
 #define GIT_OBJECTS_DIR "objects/"
 #define GIT_OBJECT_DIR_MODE 0777
@@ -66,7 +67,7 @@
  * Acts just like git_odb__hashfd with the addition of filters...
  */
 int git_odb__hashfd_filtered(
-	git_oid *out, git_file fd, size_t len, git_otype type, git_vector *filters);
+	git_oid *out, git_file fd, size_t len, git_otype type, git_filter_list *fl);
 
 /*
  * Hash a `path`, assuming it could be a POSIX symlink: if the path is a
diff --git a/src/odb_loose.c b/src/odb_loose.c
index 76ed8e2..ced272b 100644
--- a/src/odb_loose.c
+++ b/src/odb_loose.c
@@ -33,6 +33,8 @@
 
 	int object_zlib_level; /** loose object zlib compression level. */
 	int fsync_object_files; /** loose object file fsync flag. */
+	mode_t object_file_mode;
+	mode_t object_dir_mode;
 
 	size_t objects_dirlen;
 	char objects_dir[GIT_FLEX_ARRAY];
@@ -79,7 +81,7 @@
 static int object_mkdir(const git_buf *name, const loose_backend *be)
 {
 	return git_futils_mkdir(
-		name->ptr + be->objects_dirlen, be->objects_dir, GIT_OBJECT_DIR_MODE,
+		name->ptr + be->objects_dirlen, be->objects_dir, be->object_dir_mode,
 		GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
 }
 
@@ -499,7 +501,7 @@
 	}
 
 	if (sstate->found > 1)
-		return git_odb__error_ambiguous("multiple matches in loose objects");
+		return GIT_EAMBIGUOUS;
 
 	return 0;
 }
@@ -544,13 +546,17 @@
 
 	/* Explore directory to find a unique object matching short_oid */
 	error = git_path_direach(
-		object_location, fn_locate_object_short_oid, &state);
-	if (error)
+		object_location, 0, fn_locate_object_short_oid, &state);
+
+	if (error && error != GIT_EUSER)
 		return error;
 
 	if (!state.found)
 		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
 
+	if (state.found > 1)
+		return git_odb__error_ambiguous("multiple matches in loose objects");
+
 	/* Convert obtained hex formatted oid to raw */
 	error = git_oid_fromstr(res_oid, (char *)state.res_oid);
 	if (error)
@@ -641,10 +647,12 @@
 {
 	int error = 0;
 
+	assert(len <= GIT_OID_HEXSZ);
+
 	if (len < GIT_OID_MINPREFIXLEN)
 		error = git_odb__error_ambiguous("prefix length too short");
 
-	else if (len >= GIT_OID_HEXSZ) {
+	else if (len == GIT_OID_HEXSZ) {
 		/* We can fall back to regular read method */
 		error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
 		if (!error)
@@ -739,7 +747,7 @@
 {
 	struct foreach_state *state = (struct foreach_state *) _state;
 
-	return git_path_direach(path, foreach_object_dir_cb, state);
+	return git_path_direach(path, 0, foreach_object_dir_cb, state);
 }
 
 static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
@@ -762,34 +770,26 @@
 	state.data = data;
 	state.dir_len = git_buf_len(&buf);
 
-	error = git_path_direach(&buf, foreach_cb, &state);
+	error = git_path_direach(&buf, 0, foreach_cb, &state);
 
 	git_buf_free(&buf);
 
 	return state.cb_error ? state.cb_error : error;
 }
 
-static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
+static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid)
 {
 	loose_writestream *stream = (loose_writestream *)_stream;
 	loose_backend *backend = (loose_backend *)_stream->backend;
 	git_buf final_path = GIT_BUF_INIT;
 	int error = 0;
 
-	if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
-		object_file_name(&final_path, backend, oid) < 0 ||
+	if (object_file_name(&final_path, backend, oid) < 0 ||
 		object_mkdir(&final_path, backend) < 0)
 		error = -1;
-	/*
-	 * Don't try to add an existing object to the repository. This
-	 * is what git does and allows us to sidestep the fact that
-	 * we're not allowed to overwrite a read-only file on Windows.
-	 */
-	else if (git_path_exists(final_path.ptr) == true)
-		git_filebuf_cleanup(&stream->fbuf);
 	else
 		error = git_filebuf_commit_at(
-			&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
+			&stream->fbuf, final_path.ptr);
 
 	git_buf_free(&final_path);
 
@@ -810,17 +810,6 @@
 	git__free(stream);
 }
 
-static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
-{
-	const char *type_str = git_object_type2string(obj_type);
-	int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
-
-	assert(len > 0);				/* otherwise snprintf() is broken */
-	assert(((size_t)len) < n);		/* otherwise the caller is broken! */
-
-	return len+1;
-}
-
 static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
 {
 	loose_backend *backend;
@@ -834,7 +823,7 @@
 	backend = (loose_backend *)_backend;
 	*stream_out = NULL;
 
-	hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
+	hdrlen = git_odb__format_object_header(hdr, sizeof(hdr), length, type);
 
 	stream = git__calloc(1, sizeof(loose_writestream));
 	GITERR_CHECK_ALLOC(stream);
@@ -848,9 +837,9 @@
 
 	if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
 		git_filebuf_open(&stream->fbuf, tmp_path.ptr,
-			GIT_FILEBUF_HASH_CONTENTS |
 			GIT_FILEBUF_TEMPORARY |
-			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+			backend->object_file_mode) < 0 ||
 		stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
 	{
 		git_filebuf_cleanup(&stream->fbuf);
@@ -863,7 +852,7 @@
 	return !stream ? -1 : 0;
 }
 
-static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
+static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type)
 {
 	int error = 0, header_len;
 	git_buf final_path = GIT_BUF_INIT;
@@ -874,12 +863,13 @@
 	backend = (loose_backend *)_backend;
 
 	/* prepare the header for the file */
-	header_len = format_object_header(header, sizeof(header), len, type);
+	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)) < 0)
+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT),
+			backend->object_file_mode) < 0)
 	{
 		error = -1;
 		goto cleanup;
@@ -890,7 +880,7 @@
 
 	if (object_file_name(&final_path, backend, oid) < 0 ||
 		object_mkdir(&final_path, backend) < 0 ||
-		git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
+		git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
 		error = -1;
 
 cleanup:
@@ -913,7 +903,9 @@
 	git_odb_backend **backend_out,
 	const char *objects_dir,
 	int compression_level,
-	int do_fsync)
+	int do_fsync,
+	unsigned int dir_mode,
+	unsigned int file_mode)
 {
 	loose_backend *backend;
 	size_t objects_dirlen;
@@ -934,8 +926,16 @@
 	if (compression_level < 0)
 		compression_level = Z_BEST_SPEED;
 
+	if (dir_mode == 0)
+		dir_mode = GIT_OBJECT_DIR_MODE;
+
+	if (file_mode == 0)
+		file_mode = GIT_OBJECT_FILE_MODE;
+
 	backend->object_zlib_level = compression_level;
 	backend->fsync_object_files = do_fsync;
+	backend->object_dir_mode = dir_mode;
+	backend->object_file_mode = file_mode;
 
 	backend->parent.read = &loose_backend__read;
 	backend->parent.write = &loose_backend__write;
diff --git a/src/odb_pack.c b/src/odb_pack.c
index eec7925..fd2ca0f 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -29,7 +29,7 @@
 
 struct pack_writepack {
 	struct git_odb_writepack parent;
-	git_indexer_stream *indexer_stream;
+	git_indexer *indexer;
 };
 
 /**
@@ -259,23 +259,26 @@
 	return git_odb__error_notfound("failed to find pack entry", oid);
 }
 
-static unsigned pack_entry_find_prefix_inner(
-		struct git_pack_entry *e,
-		struct pack_backend *backend,
-		const git_oid *short_oid,
-		size_t len,
-		struct git_pack_file *last_found)
+static int pack_entry_find_prefix(
+	struct git_pack_entry *e,
+	struct pack_backend *backend,
+	const git_oid *short_oid,
+	size_t len)
 {
 	int error;
 	size_t i;
-	unsigned found = 0;
+	git_oid found_full_oid = {{0}};
+	bool found = false;
+	struct git_pack_file *last_found = backend->last_found;
 
 	if (last_found) {
 		error = git_pack_entry_find(e, last_found, short_oid, len);
 		if (error == GIT_EAMBIGUOUS)
 			return error;
-		if (!error)
-			found = 1;
+		if (!error) {
+			git_oid_cpy(&found_full_oid, &e->sha1);
+			found = true;
+		}
 	}
 
 	for (i = 0; i < backend->packs.length; ++i) {
@@ -289,28 +292,16 @@
 		if (error == GIT_EAMBIGUOUS)
 			return error;
 		if (!error) {
-			if (++found > 1)
-				break;
+			if (found && git_oid_cmp(&e->sha1, &found_full_oid))
+				return git_odb__error_ambiguous("found multiple pack entries");
+			git_oid_cpy(&found_full_oid, &e->sha1);
+			found = true;
 			backend->last_found = p;
 		}
 	}
 
-	return found;
-}
-
-static int pack_entry_find_prefix(
-	struct git_pack_entry *e,
-	struct pack_backend *backend,
-	const git_oid *short_oid,
-	size_t len)
-{
-	struct git_pack_file *last_found = backend->last_found;
-	unsigned int found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
-
 	if (!found)
 		return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
-	else if (found > 1)
-		return git_odb__error_ambiguous("found multiple pack entries");
 	else
 		return 0;
 }
@@ -340,19 +331,20 @@
 	git_buf_sets(&path, backend->pack_folder);
 
 	/* reload all packs */
-	error = git_path_direach(&path, packfile_load__cb, (void *)backend);
+	error = git_path_direach(&path, 0, packfile_load__cb, backend);
 
 	git_buf_free(&path);
 
 	if (error < 0)
-		return error;
+		return -1;
 
 	git_vector_sort(&backend->packs);
 	return 0;
 }
 
-
-static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid)
+static int pack_backend__read_header_internal(
+	size_t *len_p, git_otype *type_p,
+	struct git_odb_backend *backend, const git_oid *oid)
 {
 	struct git_pack_entry e;
 	int error;
@@ -365,7 +357,26 @@
 	return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
 }
 
-static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
+static int pack_backend__read_header(
+	size_t *len_p, git_otype *type_p,
+	struct git_odb_backend *backend, const git_oid *oid)
+{
+	int error;
+
+	error = pack_backend__read_header_internal(len_p, type_p, backend, oid);
+
+	if (error != GIT_ENOTFOUND)
+		return error;
+
+	if ((error = pack_backend__refresh(backend)) < 0)
+		return error;
+
+	return pack_backend__read_header_internal(len_p, type_p, backend, oid);
+}
+
+static int pack_backend__read_internal(
+	void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
 {
 	struct git_pack_entry e;
 	git_rawobj raw;
@@ -382,7 +393,24 @@
 	return 0;
 }
 
-static int pack_backend__read_prefix(
+static int pack_backend__read(
+	void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
+{
+	int error;
+
+	error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
+
+	if (error != GIT_ENOTFOUND)
+		return error;
+
+	if ((error = pack_backend__refresh(backend)) < 0)
+		return error;
+
+	return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
+}
+
+static int pack_backend__read_prefix_internal(
 	git_oid *out_oid,
 	void **buffer_p,
 	size_t *len_p,
@@ -419,9 +447,45 @@
 	return error;
 }
 
+static int pack_backend__read_prefix(
+	git_oid *out_oid,
+	void **buffer_p,
+	size_t *len_p,
+	git_otype *type_p,
+	git_odb_backend *backend,
+	const git_oid *short_oid,
+	size_t len)
+{
+	int error;
+
+	error = pack_backend__read_prefix_internal(
+		out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
+
+	if (error != GIT_ENOTFOUND)
+		return error;
+
+	if ((error = pack_backend__refresh(backend)) < 0)
+		return error;
+
+	return pack_backend__read_prefix_internal(
+		out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
+}
+
 static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
 {
 	struct git_pack_entry e;
+	int error;
+
+	error = pack_entry_find(&e, (struct pack_backend *)backend, oid);
+
+	if (error != GIT_ENOTFOUND)
+		return error == 0;
+
+	if ((error = pack_backend__refresh(backend)) < 0) {
+		giterr_clear();
+		return (int)false;
+	}
+
 	return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
 }
 
@@ -447,13 +511,13 @@
 	return 0;
 }
 
-static int pack_backend__writepack_add(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
+static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
 {
 	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
 
 	assert(writepack);
 
-	return git_indexer_stream_add(writepack->indexer_stream, data, size, stats);
+	return git_indexer_append(writepack->indexer, data, size, stats);
 }
 
 static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
@@ -462,7 +526,7 @@
 
 	assert(writepack);
 
-	return git_indexer_stream_finalize(writepack->indexer_stream, stats);
+	return git_indexer_commit(writepack->indexer, stats);
 }
 
 static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
@@ -471,12 +535,13 @@
 
 	assert(writepack);
 
-	git_indexer_stream_free(writepack->indexer_stream);
+	git_indexer_free(writepack->indexer);
 	git__free(writepack);
 }
 
 static int pack_backend__writepack(struct git_odb_writepack **out,
 	git_odb_backend *_backend,
+        git_odb *odb,
 	git_transfer_progress_callback progress_cb,
 	void *progress_payload)
 {
@@ -492,14 +557,14 @@
 	writepack = git__calloc(1, sizeof(struct pack_writepack));
 	GITERR_CHECK_ALLOC(writepack);
 
-	if (git_indexer_stream_new(&writepack->indexer_stream,
-		backend->pack_folder, progress_cb, progress_payload) < 0) {
+	if (git_indexer_new(&writepack->indexer,
+		backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) {
 		git__free(writepack);
 		return -1;
 	}
 
 	writepack->parent.backend = _backend;
-	writepack->parent.add = pack_backend__writepack_add;
+	writepack->parent.append = pack_backend__writepack_append;
 	writepack->parent.commit = pack_backend__writepack_commit;
 	writepack->parent.free = pack_backend__writepack_free;
 
diff --git a/src/oid.c b/src/oid.c
index 8300e46..d56b6af 100644
--- a/src/oid.c
+++ b/src/oid.c
@@ -211,7 +211,7 @@
 	for (a = oid_a->id; *str && (a - oid_a->id) < GIT_OID_RAWSZ; ++a) {
 		if ((hexval = git__fromhex(*str++)) < 0)
 			return -1;
-		strval = hexval << 4;
+		strval = (unsigned char)(hexval << 4);
 		if (*str) {
 			if ((hexval = git__fromhex(*str++)) < 0)
 				return -1;
@@ -369,8 +369,10 @@
 	bool is_leaf;
 	node_index idx;
 
-	if (os->full)
+	if (os->full) {
+		giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
 		return -1;
+	}
 
 	if (text_oid == NULL)
 		return os->min_length;
@@ -396,12 +398,19 @@
 			node->tail = NULL;
 
 			node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
-			GITERR_CHECK_ALLOC(node);
+			if (node == NULL) {
+				if (os->full)
+					giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
+				return -1;
+			}
 		}
 
 		if (node->children[c] == 0) {
-			if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)
+			if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
+				if (os->full)
+					giterr_set(GITERR_INVALID, "Unable to shorten OID - OID set full");
 				return -1;
+			}
 			break;
 		}
 
diff --git a/src/oid.h b/src/oid.h
index 077d0a4..cfe7ca1 100644
--- a/src/oid.h
+++ b/src/oid.h
@@ -9,6 +9,18 @@
 
 #include "git2/oid.h"
 
+GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2)
+{
+	int i;
+
+	for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
+		if (*sha1 != *sha2)
+			return *sha1 - *sha2;
+	}
+
+	return 0;
+}
+
 /*
  * Compare two oid structures.
  *
@@ -18,16 +30,7 @@
  */
 GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
 {
-	const unsigned char *sha1 = a->id;
-	const unsigned char *sha2 = b->id;
-	int i;
-
-	for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
-		if (*sha1 != *sha2)
-			return *sha1 - *sha2;
-	}
-
-	return 0;
+	return git_oid__hashcmp(a->id, b->id);
 }
 
 #endif
diff --git a/src/pack-objects.c b/src/pack-objects.c
index 500104c..2d62507 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -14,6 +14,7 @@
 #include "pack.h"
 #include "thread-utils.h"
 #include "tree.h"
+#include "util.h"
 
 #include "git2/pack.h"
 #include "git2/commit.h"
@@ -25,7 +26,7 @@
 	git_pobject *object;
 	void *data;
 	struct git_delta_index *index;
-	unsigned int depth;
+	int depth;
 };
 
 struct tree_walk_context {
@@ -34,7 +35,7 @@
 };
 
 struct pack_write_context {
-	git_indexer_stream *indexer;
+	git_indexer *indexer;
 	git_transfer_progress *stats;
 };
 
@@ -57,6 +58,9 @@
 #define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
 #define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
 
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
+
 static unsigned name_hash(const char *name)
 {
 	unsigned c, hash = 0;
@@ -213,41 +217,19 @@
 	kh_value(pb->object_ix, pos) = po;
 
 	pb->done = false;
-	return 0;
-}
 
-/*
- * The per-object header is a pretty dense thing, which is
- *  - first byte: low four bits are "size",
- *    then three bits of "type",
- *    with the high bit being "size continues".
- *  - each byte afterwards: low seven bits are size continuation,
- *    with the high bit being "size continues"
- */
-static int gen_pack_object_header(
-		unsigned char *hdr,
-		unsigned long size,
-		git_otype type)
-{
-	unsigned char *hdr_base;
-	unsigned char c;
-
-	assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
-
-	/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
-
-	c = (unsigned char)((type << 4) | (size & 15));
-	size >>= 4;
-	hdr_base = hdr;
-
-	while (size) {
-		*hdr++ = c | 0x80;
-		c = size & 0x7f;
-		size >>= 7;
+	if (pb->progress_cb) {
+		double current_time = git__timer();
+		if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+			pb->last_progress_report_time = current_time;
+			if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) {
+				giterr_clear();
+				return GIT_EUSER;
+			}
+		}
 	}
-	*hdr++ = c;
 
-	return (int)(hdr - hdr_base);
+	return 0;
 }
 
 static int get_delta(void **out, git_odb *odb, git_pobject *po)
@@ -290,7 +272,7 @@
 	git_buf zbuf = GIT_BUF_INIT;
 	git_otype type;
 	unsigned char hdr[10];
-	unsigned int hdr_len;
+	size_t hdr_len;
 	unsigned long size;
 	void *data;
 
@@ -311,7 +293,7 @@
 	}
 
 	/* Write header */
-	hdr_len = gen_pack_object_header(hdr, size, type);
+	hdr_len = git_packfile__object_header(hdr, size, type);
 
 	if (git_buf_put(buf, (char *)hdr, hdr_len) < 0)
 		goto on_error;
@@ -505,8 +487,10 @@
 	/*
 	 * Mark objects that are at the tip of tags.
 	 */
-	if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0)
+	if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
+		git__free(wo);
 		return NULL;
+	}
 
 	/*
 	 * Give the objects in the original recency order until
@@ -576,50 +560,52 @@
 	git_buf buf = GIT_BUF_INIT;
 	enum write_one_status status;
 	struct git_pack_header ph;
+	git_oid entry_oid;
 	unsigned int i = 0;
+	int error = 0;
 
 	write_order = compute_write_order(pb);
-	if (write_order == NULL)
-		goto on_error;
+	if (write_order == NULL) {
+		error = -1;
+		goto done;
+	}
 
 	/* Write pack header */
 	ph.hdr_signature = htonl(PACK_SIGNATURE);
 	ph.hdr_version = htonl(PACK_VERSION);
 	ph.hdr_entries = htonl(pb->nr_objects);
 
-	if (cb(&ph, sizeof(ph), data) < 0)
-		goto on_error;
+	if ((error = cb(&ph, sizeof(ph), data)) < 0)
+		goto done;
 
-	if (git_hash_update(&pb->ctx, &ph, sizeof(ph)) < 0)
-		goto on_error;
+	if ((error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
+		goto done;
 
 	pb->nr_remaining = pb->nr_objects;
 	do {
 		pb->nr_written = 0;
 		for ( ; i < pb->nr_objects; ++i) {
 			po = write_order[i];
-			if (write_one(&buf, pb, po, &status) < 0)
-				goto on_error;
-			if (cb(buf.ptr, buf.size, data) < 0)
-				goto on_error;
+			if ((error = write_one(&buf, pb, po, &status)) < 0)
+				goto done;
+			if ((error = cb(buf.ptr, buf.size, data)) < 0)
+				goto done;
 			git_buf_clear(&buf);
 		}
 
 		pb->nr_remaining -= pb->nr_written;
 	} while (pb->nr_remaining && i < pb->nr_objects);
 
+
+	if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0)
+		goto done;
+
+	error = cb(entry_oid.id, GIT_OID_RAWSZ, data);
+
+done:
 	git__free(write_order);
 	git_buf_free(&buf);
-
-	if (git_hash_final(&pb->pack_oid, &pb->ctx) < 0)
-		goto on_error;
-
-	return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
-
-on_error:
-	git__free(write_order);
-	git_buf_free(&buf);
-	return -1;
+	return error;
 }
 
 static int write_pack_buf(void *buf, size_t size, void *data)
@@ -674,7 +660,7 @@
 }
 
 static int try_delta(git_packbuilder *pb, struct unpacked *trg,
-		     struct unpacked *src, unsigned int max_depth,
+		     struct unpacked *src, int max_depth,
 		     unsigned long *mem_usage, int *ret)
 {
 	git_pobject *trg_object = trg->object;
@@ -839,7 +825,7 @@
 
 static int find_deltas(git_packbuilder *pb, git_pobject **list,
 		       unsigned int *list_size, unsigned int window,
-		       unsigned int depth)
+		       int depth)
 {
 	git_pobject *po;
 	git_buf zbuf = GIT_BUF_INIT;
@@ -854,8 +840,7 @@
 
 	for (;;) {
 		struct unpacked *n = array + idx;
-		unsigned int max_depth;
-		int j, best_base = -1;
+		int max_depth, j, best_base = -1;
 
 		git_packbuilder__progress_lock(pb);
 		if (!*list_size) {
@@ -1048,7 +1033,7 @@
 
 static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
 			  unsigned int list_size, unsigned int window,
-			  unsigned int depth)
+			  int depth)
 {
 	struct thread_params *p;
 	int i, ret, active_threads = 0;
@@ -1205,6 +1190,13 @@
 	if (pb->nr_objects == 0 || pb->done)
 		return 0; /* nothing to do */
 
+	/*
+	 * Although we do not report progress during deltafication, we
+	 * at least report that we are in the deltafication stage
+	 */
+	if (pb->progress_cb)
+			pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
+
 	delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
 	GITERR_CHECK_ALLOC(delta_list);
 
@@ -1250,40 +1242,48 @@
 static int write_cb(void *buf, size_t len, void *payload)
 {
 	struct pack_write_context *ctx = payload;
-	return git_indexer_stream_add(ctx->indexer, buf, len, ctx->stats);
+	return git_indexer_append(ctx->indexer, buf, len, ctx->stats);
 }
 
 int git_packbuilder_write(
 	git_packbuilder *pb,
 	const char *path,
+	unsigned int mode,
 	git_transfer_progress_callback progress_cb,
 	void *progress_cb_payload)
 {
-	git_indexer_stream *indexer;
+	git_indexer *indexer;
 	git_transfer_progress stats;
 	struct pack_write_context ctx;
 
 	PREPARE_PACK;
 
-	if (git_indexer_stream_new(
-		&indexer, path, progress_cb, progress_cb_payload) < 0)
+	if (git_indexer_new(
+		&indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0)
 		return -1;
 
 	ctx.indexer = indexer;
 	ctx.stats = &stats;
 
 	if (git_packbuilder_foreach(pb, write_cb, &ctx) < 0 ||
-		git_indexer_stream_finalize(indexer, &stats) < 0) {
-		git_indexer_stream_free(indexer);
+		git_indexer_commit(indexer, &stats) < 0) {
+		git_indexer_free(indexer);
 		return -1;
 	}
 
-	git_indexer_stream_free(indexer);
+	git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
+
+	git_indexer_free(indexer);
 	return 0;
 }
 
 #undef PREPARE_PACK
 
+const git_oid *git_packbuilder_hash(git_packbuilder *pb)
+{
+	return &pb->pack_oid;
+}
+
 static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload)
 {
 	struct tree_walk_context *ctx = payload;
@@ -1346,6 +1346,17 @@
 	return pb->nr_written;
 }
 
+int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
+{
+	if (!pb)
+		return -1;
+
+	pb->progress_cb = progress_cb;
+	pb->progress_cb_payload = progress_cb_payload;
+
+	return 0;
+}
+
 void git_packbuilder_free(git_packbuilder *pb)
 {
 	if (pb == NULL)
diff --git a/src/pack-objects.h b/src/pack-objects.h
index 8e7ba7f..0c94a5a 100644
--- a/src/pack-objects.h
+++ b/src/pack-objects.h
@@ -16,6 +16,7 @@
 #include "netops.h"
 
 #include "git2/oid.h"
+#include "git2/pack.h"
 
 #define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
 #define GIT_PACK_DEPTH 50 /* max delta depth */
@@ -79,6 +80,10 @@
 
 	int nr_threads; /* nr of threads to use */
 
+	git_packbuilder_progress progress_cb;
+	void *progress_cb_payload;
+	double last_progress_report_time; /* the time progress was last reported */
+
 	bool done;
 };
 
diff --git a/src/pack.c b/src/pack.c
index 7ce7099..644b2d4 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -329,8 +329,10 @@
 	memcpy(idx_name, p->pack_name, base_len);
 	memcpy(idx_name + base_len, ".idx", sizeof(".idx"));
 
-	if ((error = git_mutex_lock(&p->lock)) < 0)
+	if ((error = git_mutex_lock(&p->lock)) < 0) {
+		git__free(idx_name);
 		return error;
+	}
 
 	if (p->index_version == -1)
 		error = pack_index_check(idx_name, p);
@@ -362,6 +364,38 @@
 	return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
  }
 
+/*
+ * The per-object header is a pretty dense thing, which is
+ *  - first byte: low four bits are "size",
+ *    then three bits of "type",
+ *    with the high bit being "size continues".
+ *  - each byte afterwards: low seven bits are size continuation,
+ *    with the high bit being "size continues"
+ */
+size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type)
+{
+	unsigned char *hdr_base;
+	unsigned char c;
+
+	assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
+
+	/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
+
+	c = (unsigned char)((type << 4) | (size & 15));
+	size >>= 4;
+	hdr_base = hdr;
+
+	while (size) {
+		*hdr++ = c | 0x80;
+		c = size & 0x7f;
+		size >>= 7;
+	}
+	*hdr++ = c;
+
+	return (hdr - hdr_base);
+}
+
+
 static int packfile_unpack_header1(
 		unsigned long *usedp,
 		size_t *sizep,
@@ -820,7 +854,7 @@
 
 	git_mwindow_free_all(&p->mwf);
 
-	if (p->mwf.fd != -1)
+	if (p->mwf.fd >= 0)
 		p_close(p->mwf.fd);
 
 	pack_index_free(p);
@@ -903,7 +937,8 @@
 cleanup:
 	giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
 
-	p_close(p->mwf.fd);
+	if (p->mwf.fd >= 0)
+		p_close(p->mwf.fd);
 	p->mwf.fd = -1;
 
 	git_mutex_unlock(&p->lock);
@@ -1107,8 +1142,11 @@
 		short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
 #endif
 
-	/* Use git.git lookup code */
+#ifdef GIT_USE_LOOKUP
 	pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
+#else
+	pos = sha1_position(index, stride, lo, hi, short_oid->id);
+#endif
 
 	if (pos >= 0) {
 		/* An object matching exactly the oid was found */
diff --git a/src/pack.h b/src/pack.h
index aeeac9c..28146ab 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -112,6 +112,8 @@
 	git_mwindow *mw;
 } git_packfile_stream;
 
+size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type);
+
 int git_packfile_unpack_header(
 		size_t *size_p,
 		git_otype *type_p,
diff --git a/src/path.c b/src/path.c
index 6437979..750dd3e 100644
--- a/src/path.c
+++ b/src/path.c
@@ -8,7 +8,6 @@
 #include "path.h"
 #include "posix.h"
 #ifdef GIT_WIN32
-#include "win32/dir.h"
 #include "win32/posix.h"
 #else
 #include <dirent.h>
@@ -243,8 +242,8 @@
 
 #ifdef GIT_WIN32
 	/* Are we dealing with a windows network path? */
-	else if ((path[0] == '/' && path[1] == '/') ||
-		(path[0] == '\\' && path[1] == '\\'))
+	else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
+		(path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
 	{
 		offset += 2;
 
@@ -484,74 +483,90 @@
 
 bool git_path_is_empty_dir(const char *path)
 {
-	git_buf pathbuf = GIT_BUF_INIT;
 	HANDLE hFind = INVALID_HANDLE_VALUE;
-	wchar_t wbuf[GIT_WIN_PATH];
+	git_win32_path wbuf;
+	int wbufsz;
 	WIN32_FIND_DATAW ffd;
 	bool retval = true;
 
-	if (!git_path_isdir(path)) return false;
+	if (!git_path_isdir(path))
+		return false;
 
-	git_buf_printf(&pathbuf, "%s\\*", path);
-	git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf));
+	wbufsz = git_win32_path_from_c(wbuf, path);
+	if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
+		return false;
+	memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
 
 	hFind = FindFirstFileW(wbuf, &ffd);
-	if (INVALID_HANDLE_VALUE == hFind) {
-		giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+	if (INVALID_HANDLE_VALUE == hFind)
 		return false;
-	}
 
 	do {
 		if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
 			retval = false;
+			break;
 		}
 	} while (FindNextFileW(hFind, &ffd) != 0);
 
 	FindClose(hFind);
-	git_buf_free(&pathbuf);
 	return retval;
 }
 
 #else
 
+static int path_found_entry(void *payload, git_buf *path)
+{
+	GIT_UNUSED(payload);
+	return !git_path_is_dot_or_dotdot(path->ptr);
+}
+
 bool git_path_is_empty_dir(const char *path)
 {
-	DIR *dir = NULL;
-	struct dirent *e;
-	bool retval = true;
+	int error;
+	git_buf dir = GIT_BUF_INIT;
 
-	if (!git_path_isdir(path)) return false;
-
-	dir = opendir(path);
-	if (!dir) {
-		giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+	if (!git_path_isdir(path))
 		return false;
-	}
 
-	while ((e = readdir(dir)) != NULL) {
-		if (!git_path_is_dot_or_dotdot(e->d_name)) {
-			giterr_set(GITERR_INVALID,
-						  "'%s' exists and is not an empty directory", path);
-			retval = false;
-			break;
-		}
-	}
-	closedir(dir);
+	if (!(error = git_buf_sets(&dir, path)))
+		error = git_path_direach(&dir, 0, path_found_entry, NULL);
 
-	return retval;
+	git_buf_free(&dir);
+
+	return !error;
 }
+
 #endif
 
+int git_path_set_error(int errno_value, const char *path, const char *action)
+{
+	switch (errno_value) {
+	case ENOENT:
+	case ENOTDIR:
+		giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
+		return GIT_ENOTFOUND;
+
+	case EINVAL:
+	case ENAMETOOLONG:
+		giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
+		return GIT_EINVALIDSPEC;
+
+	case EEXIST:
+		giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
+		return GIT_EEXISTS;
+
+	default:
+		giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
+		return -1;
+	}
+}
+
 int git_path_lstat(const char *path, struct stat *st)
 {
-	int err = 0;
+	if (p_lstat(path, st) == 0)
+		return 0;
 
-	if (p_lstat(path, st) < 0) {
-		err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
-		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
-	}
-
-	return err;
+	return git_path_set_error(errno, path, "stat");
 }
 
 static bool _check_dir_contents(
@@ -564,7 +579,7 @@
 	size_t sub_size = strlen(sub);
 
 	/* leave base valid even if we could not make space for subdir */
-	if (git_buf_try_grow(dir, dir_size + sub_size + 2, false) < 0)
+	if (git_buf_try_grow(dir, dir_size + sub_size + 2, false, false) < 0)
 		return false;
 
 	/* save excursion */
@@ -603,7 +618,7 @@
 	}
 
 	/* call dirname if this is not a directory */
-	if (!error && git_path_isdir(dir->ptr) == false)
+	if (!error) /* && git_path_isdir(dir->ptr) == false) */
 		error = git_path_dirname_r(dir, dir->ptr);
 
 	if (!error)
@@ -645,12 +660,33 @@
 			/* do nothing with singleton dot */;
 
 		else if (len == 2 && from[0] == '.' && from[1] == '.') {
-			while (to > base && to[-1] == '/') to--;
-			while (to > base && to[-1] != '/') to--;
-		}
+			/* error out if trying to up one from a hard base */
+			if (to == base && ceiling != 0) {
+				giterr_set(GITERR_INVALID,
+					"Cannot strip root component off url");
+				return -1;
+			}
 
-		else {
-			if (*next == '/')
+			/* no more path segments to strip,
+			 * use '../' as a new base path */
+			if (to == base) {
+				if (*next == '/')
+					len++;
+
+				if (to != from)
+					memmove(to, from, len);
+
+				to += len;
+				/* this is now the base, can't back up from a
+				 * relative prefix */
+				base = to;
+			} else {
+				/* back up a path segment */
+				while (to > base && to[-1] == '/') to--;
+				while (to > base && to[-1] != '/') to--;
+			}
+		} else {
+			if (*next == '/' && *from != '/')
 				len++;
 
 			if (to != from)
@@ -702,14 +738,108 @@
 	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
 }
 
+bool git_path_has_non_ascii(const char *path, size_t pathlen)
+{
+	const uint8_t *scan = (const uint8_t *)path, *end;
+
+	for (end = scan + pathlen; scan < end; ++scan)
+		if (*scan & 0x80)
+			return true;
+
+	return false;
+}
+
+#ifdef GIT_USE_ICONV
+
+int git_path_iconv_init_precompose(git_path_iconv_t *ic)
+{
+	git_buf_init(&ic->buf, 0);
+	ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
+	return 0;
+}
+
+void git_path_iconv_clear(git_path_iconv_t *ic)
+{
+	if (ic) {
+		if (ic->map != (iconv_t)-1)
+			iconv_close(ic->map);
+		git_buf_free(&ic->buf);
+	}
+}
+
+int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
+{
+	char *nfd = *in, *nfc;
+	size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
+	int retry = 1;
+
+	if (!ic || ic->map == (iconv_t)-1 ||
+		!git_path_has_non_ascii(*in, *inlen))
+		return 0;
+
+	while (1) {
+		if (git_buf_grow(&ic->buf, wantlen) < 0)
+			return -1;
+
+		nfc    = ic->buf.ptr   + ic->buf.size;
+		nfclen = ic->buf.asize - ic->buf.size;
+
+		rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
+
+		ic->buf.size = (nfc - ic->buf.ptr);
+
+		if (rv != (size_t)-1)
+			break;
+
+		if (errno != E2BIG)
+			goto fail;
+
+		/* make space for 2x the remaining data to be converted
+		 * (with per retry overhead to avoid infinite loops)
+		 */
+		wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
+
+		if (retry++ > 4)
+			goto fail;
+	}
+
+	ic->buf.ptr[ic->buf.size] = '\0';
+
+	*in    = ic->buf.ptr;
+	*inlen = ic->buf.size;
+
+	return 0;
+
+fail:
+	giterr_set(GITERR_OS, "Unable to convert unicode path data");
+	return -1;
+}
+
+#endif
+
+#if defined(__sun) || defined(__GNU__)
+typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
+#else
+typedef struct dirent path_dirent_data;
+#endif
+
 int git_path_direach(
 	git_buf *path,
+	uint32_t flags,
 	int (*fn)(void *, git_buf *),
 	void *arg)
 {
+	int error = 0;
 	ssize_t wd_len;
 	DIR *dir;
-	struct dirent *de, *de_buf;
+	path_dirent_data de_data;
+	struct dirent *de, *de_buf = (struct dirent *)&de_data;
+
+	(void)flags;
+
+#ifdef GIT_USE_ICONV
+	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
 
 	if (git_path_to_dir(path) < 0)
 		return -1;
@@ -721,64 +851,80 @@
 		return -1;
 	}
 
-#if defined(__sun) || defined(__GNU__)
-	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
-	de_buf = git__malloc(sizeof(struct dirent));
+#ifdef GIT_USE_ICONV
+	if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+		(void)git_path_iconv_init_precompose(&ic);
 #endif
 
 	while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
-		int result;
+		char *de_path = de->d_name;
+		size_t de_len = strlen(de_path);
 
-		if (git_path_is_dot_or_dotdot(de->d_name))
+		if (git_path_is_dot_or_dotdot(de_path))
 			continue;
 
-		if (git_buf_puts(path, de->d_name) < 0) {
-			closedir(dir);
-			git__free(de_buf);
-			return -1;
-		}
+#ifdef GIT_USE_ICONV
+		if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
+			break;
+#endif
+				
+		if ((error = git_buf_put(path, de_path, de_len)) < 0)
+			break;
 
-		result = fn(arg, path);
+		error = fn(arg, path);
 
 		git_buf_truncate(path, wd_len); /* restore path */
 
-		if (result < 0) {
-			closedir(dir);
-			git__free(de_buf);
-			return -1;
+		if (error) {
+			error = GIT_EUSER;
+			break;
 		}
 	}
 
 	closedir(dir);
-	git__free(de_buf);
-	return 0;
+
+#ifdef GIT_USE_ICONV
+	git_path_iconv_clear(&ic);
+#endif
+
+	return error;
 }
 
 int git_path_dirload(
 	const char *path,
 	size_t prefix_len,
 	size_t alloc_extra,
+	unsigned int flags,
 	git_vector *contents)
 {
 	int error, need_slash;
 	DIR *dir;
-	struct dirent *de, *de_buf;
 	size_t path_len;
+	path_dirent_data de_data;
+	struct dirent *de, *de_buf = (struct dirent *)&de_data;
 
-	assert(path != NULL && contents != NULL);
+	(void)flags;
+
+#ifdef GIT_USE_ICONV
+	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
+	assert(path && contents);
+
 	path_len = strlen(path);
-	assert(path_len > 0 && path_len >= prefix_len);
 
+	if (!path_len || path_len < prefix_len) {
+		giterr_set(GITERR_INVALID, "Invalid directory path '%s'", path);
+		return -1;
+	}
 	if ((dir = opendir(path)) == NULL) {
 		giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
 		return -1;
 	}
 
-#if defined(__sun) || defined(__GNU__)
-	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
-#else
-	de_buf = git__malloc(sizeof(struct dirent));
+#ifdef GIT_USE_ICONV
+	if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+		(void)git_path_iconv_init_precompose(&ic);
 #endif
 
 	path += prefix_len;
@@ -786,34 +932,38 @@
 	need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
 
 	while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
-		char *entry_path;
-		size_t entry_len;
+		char *entry_path, *de_path = de->d_name;
+		size_t alloc_size, de_len = strlen(de_path);
 
-		if (git_path_is_dot_or_dotdot(de->d_name))
+		if (git_path_is_dot_or_dotdot(de_path))
 			continue;
 
-		entry_len = strlen(de->d_name);
+#ifdef GIT_USE_ICONV
+		if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
+			break;
+#endif
 
-		entry_path = git__malloc(
-			path_len + need_slash + entry_len + 1 + alloc_extra);
-		GITERR_CHECK_ALLOC(entry_path);
+		alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
+		if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
+			error = -1;
+			break;
+		}
 
 		if (path_len)
 			memcpy(entry_path, path, path_len);
 		if (need_slash)
 			entry_path[path_len] = '/';
-		memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
-		entry_path[path_len + need_slash + entry_len] = '\0';
+		memcpy(&entry_path[path_len + need_slash], de_path, de_len);
 
-		if (git_vector_insert(contents, entry_path) < 0) {
-			closedir(dir);
-			git__free(de_buf);
-			return -1;
-		}
+		if ((error = git_vector_insert(contents, entry_path)) < 0)
+			break;
 	}
 
 	closedir(dir);
-	git__free(de_buf);
+
+#ifdef GIT_USE_ICONV
+	git_path_iconv_clear(&ic);
+#endif
 
 	if (error != 0)
 		giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
@@ -836,7 +986,7 @@
 int git_path_dirload_with_stat(
 	const char *path,
 	size_t prefix_len,
-	bool ignore_case,
+	unsigned int flags,
 	const char *start_stat,
 	const char *end_stat,
 	git_vector *contents)
@@ -853,13 +1003,14 @@
 		return -1;
 
 	error = git_path_dirload(
-		path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+		path, prefix_len, sizeof(git_path_with_stat) + 1, flags, contents);
 	if (error < 0) {
 		git_buf_free(&full);
 		return error;
 	}
 
-	strncomp = ignore_case ? git__strncasecmp : git__strncmp;
+	strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
+		git__strncasecmp : git__strncmp;
 
 	/* stat struct at start of git_path_with_stat, so shift path text */
 	git_vector_foreach(contents, i, ps) {
@@ -880,8 +1031,16 @@
 		git_buf_truncate(&full, prefix_len);
 
 		if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
-			(error = git_path_lstat(full.ptr, &ps->st)) < 0)
+			(error = git_path_lstat(full.ptr, &ps->st)) < 0) {
+			if (error == GIT_ENOTFOUND) {
+				giterr_clear();
+				error = 0;
+				git_vector_remove(contents, i--);
+				continue;
+			}
+
 			break;
+		}
 
 		if (S_ISDIR(ps->st.st_mode)) {
 			if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0)
diff --git a/src/path.h b/src/path.h
index ead4fa3..3daafd2 100644
--- a/src/path.h
+++ b/src/path.h
@@ -175,7 +175,6 @@
  *
  * @param parent Directory path that might contain subdir
  * @param subdir Subdirectory name to look for in parent
- * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
  * @return true if subdirectory exists, false otherwise.
  */
 extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
@@ -185,7 +184,6 @@
  *
  * @param dir Directory path that might contain file
  * @param file File name to look for in parent
- * @param append_if_exists If true, then file will be appended to the path if it does exist
  * @return true if file exists, false otherwise.
  */
 extern bool git_path_contains_file(git_buf *dir, const char *file);
@@ -244,21 +242,28 @@
  */
 extern int git_path_apply_relative(git_buf *target, const char *relpath);
 
+enum {
+	GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
+	GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
+};
+
 /**
  * Walk each directory entry, except '.' and '..', calling fn(state).
  *
- * @param pathbuf buffer the function reads the initial directory
+ * @param pathbuf Buffer the function reads the initial directory
  * 		path from, and updates with each successive entry's name.
- * @param fn function to invoke with each entry. The first arg is
- *		the input state and the second arg is pathbuf. The function
- *		may modify the pathbuf, but only by appending new text.
- * @param state to pass to fn as the first arg.
+ * @param flags Combination of GIT_PATH_DIR flags.
+ * @param callback Callback for each entry. Passed the `payload` and each
+ *		successive path inside the directory as a full path.  This may
+ *		safely append text to the pathbuf if needed.
+ * @param payload Passed to callback as first argument.
  * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 extern int git_path_direach(
 	git_buf *pathbuf,
-	int (*fn)(void *, git_buf *),
-	void *state);
+	uint32_t flags,
+	int (*callback)(void *payload, git_buf *path),
+	void *payload);
 
 /**
  * Sort function to order two paths
@@ -278,19 +283,19 @@
  * @param pathbuf Buffer the function reads the directory from and
  *		and updates with each successive name.
  * @param ceiling Prefix of path at which to stop walking up.  If NULL,
- *      this will walk all the way up to the root.  If not a prefix of
- *      pathbuf, the callback will be invoked a single time on the
- *      original input path.
- * @param fn Function to invoke on each path.  The first arg is the
- *		input satte and the second arg is the pathbuf.  The function
- *		should not modify the pathbuf.
+ *		this will walk all the way up to the root.  If not a prefix of
+ *		pathbuf, the callback will be invoked a single time on the
+ *		original input path.
+ * @param callback Function to invoke on each path.  Passed the `payload`
+ *		and the buffer containing the current path.  The path should not
+ *		be modified in any way.
  * @param state Passed to fn as the first ath.
  */
 extern int git_path_walk_up(
 	git_buf *pathbuf,
 	const char *ceiling,
-	int (*fn)(void *state, git_buf *),
-	void *state);
+	int (*callback)(void *payload, git_buf *path),
+	void *payload);
 
 /**
  * Load all directory entries (except '.' and '..') into a vector.
@@ -306,12 +311,14 @@
  * 		prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
  * @param alloc_extra Extra bytes to add to each string allocation in
  * 		case you want to append anything funny.
+ * @param flags Combination of GIT_PATH_DIR flags.
  * @param contents Vector to fill with directory entry names.
  */
 extern int git_path_dirload(
 	const char *path,
 	size_t prefix_len,
 	size_t alloc_extra,
+	uint32_t flags,
 	git_vector *contents);
 
 
@@ -338,7 +345,7 @@
  *
  * @param path The directory to read from
  * @param prefix_len The trailing part of path to prefix to entry paths
- * @param ignore_case How to sort and compare paths with start/end limits
+ * @param flags GIT_PATH_DIR flags from above
  * @param start_stat As optimization, only stat values after this prefix
  * @param end_stat As optimization, only stat values before this prefix
  * @param contents Vector to fill with git_path_with_stat structures
@@ -346,9 +353,78 @@
 extern int git_path_dirload_with_stat(
 	const char *path,
 	size_t prefix_len,
-	bool ignore_case,
+	uint32_t flags,
 	const char *start_stat,
 	const char *end_stat,
 	git_vector *contents);
 
+enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
+
+/*
+ * Determines if a path is equal to or potentially a child of another.
+ * @param parent The possible parent
+ * @param child The possible child
+ */
+GIT_INLINE(int) git_path_equal_or_prefixed(
+	const char *parent,
+	const char *child)
+{
+	const char *p = parent, *c = child;
+
+	while (*p && *c) {
+		if (*p++ != *c++)
+			return GIT_PATH_NOTEQUAL;
+	}
+
+	if (*p != '\0')
+		return GIT_PATH_NOTEQUAL;
+	if (*c == '\0')
+		return GIT_PATH_EQUAL;
+	if (*c == '/')
+		return GIT_PATH_PREFIX;
+
+	return GIT_PATH_NOTEQUAL;
+}
+
+/* translate errno to libgit2 error code and set error message */
+extern int git_path_set_error(
+	int errno_value, const char *path, const char *action);
+
+/* check if non-ascii characters are present in filename */
+extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
+
+#define GIT_PATH_REPO_ENCODING "UTF-8"
+
+#ifdef __APPLE__
+#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
+#else
+#define GIT_PATH_NATIVE_ENCODING "UTF-8"
+#endif
+
+#ifdef GIT_USE_ICONV
+
+#include <iconv.h>
+
+typedef struct {
+	iconv_t map;
+	git_buf buf;
+} git_path_iconv_t;
+
+#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
+
+/* Init iconv data for converting decomposed UTF-8 to precomposed */
+extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
+
+/* Clear allocated iconv data */
+extern void git_path_iconv_clear(git_path_iconv_t *ic);
+
+/*
+ * Rewrite `in` buffer using iconv map if necessary, replacing `in`
+ * pointer internal iconv buffer if rewrite happened.  The `in` pointer
+ * will be left unchanged if no rewrite was needed.
+ */
+extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
+
+#endif /* GIT_USE_ICONV */
+
 #endif
diff --git a/src/pathspec.c b/src/pathspec.c
index f029836..1e7e65e 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -5,9 +5,16 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
+#include "git2/pathspec.h"
+#include "git2/diff.h"
 #include "pathspec.h"
 #include "buf_text.h"
 #include "attr_file.h"
+#include "iterator.h"
+#include "repository.h"
+#include "index.h"
+#include "bitvec.h"
+#include "diff.h"
 
 /* what is the common non-wildcard prefix for all items in the pathspec */
 char *git_pathspec_prefix(const git_strarray *pathspec)
@@ -56,7 +63,7 @@
 }
 
 /* build a vector of fnmatch patterns to evaluate efficiently */
-int git_pathspec_init(
+int git_pathspec__vinit(
 	git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
 {
 	size_t i;
@@ -76,7 +83,7 @@
 		if (!match)
 			return -1;
 
-		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
 
 		ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
 		if (ret == GIT_ENOTFOUND) {
@@ -93,7 +100,7 @@
 }
 
 /* free data from the pathspec vector */
-void git_pathspec_free(git_vector *vspec)
+void git_pathspec__vfree(git_vector *vspec)
 {
 	git_attr_fnmatch *match;
 	unsigned int i;
@@ -106,88 +113,612 @@
 	git_vector_free(vspec);
 }
 
+struct pathspec_match_context {
+	int fnmatch_flags;
+	int (*strcomp)(const char *, const char *);
+	int (*strncomp)(const char *, const char *, size_t);
+};
+
+static void pathspec_match_context_init(
+	struct pathspec_match_context *ctxt,
+	bool disable_fnmatch,
+	bool casefold)
+{
+	if (disable_fnmatch)
+		ctxt->fnmatch_flags = -1;
+	else if (casefold)
+		ctxt->fnmatch_flags = FNM_CASEFOLD;
+	else
+		ctxt->fnmatch_flags = 0;
+
+	if (casefold) {
+		ctxt->strcomp  = git__strcasecmp;
+		ctxt->strncomp = git__strncasecmp;
+	} else {
+		ctxt->strcomp  = git__strcmp;
+		ctxt->strncomp = git__strncmp;
+	}
+}
+
+static int pathspec_match_one(
+	const git_attr_fnmatch *match,
+	struct pathspec_match_context *ctxt,
+	const char *path)
+{
+	int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
+
+	if (result == FNM_NOMATCH)
+		result = ctxt->strcomp(match->pattern, path) ? FNM_NOMATCH : 0;
+
+	if (ctxt->fnmatch_flags >= 0 && result == FNM_NOMATCH)
+		result = p_fnmatch(match->pattern, path, ctxt->fnmatch_flags);
+
+	/* if we didn't match, look for exact dirname prefix match */
+	if (result == FNM_NOMATCH &&
+		(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+		ctxt->strncomp(path, match->pattern, match->length) == 0 &&
+		path[match->length] == '/')
+		result = 0;
+
+	/* if we didn't match and this is a negative match, check for exact
+	 * match of filename with leading '!'
+	 */
+	if (result == FNM_NOMATCH &&
+		(match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
+		*path == '!' &&
+		ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
+		(!path[match->length + 1] || path[match->length + 1] == '/'))
+		return 1;
+
+	if (result == 0)
+		return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
+	return -1;
+}
+
+static int git_pathspec__match_at(
+	size_t *matched_at,
+	const git_vector *vspec,
+	struct pathspec_match_context *ctxt,
+	const char *path0,
+	const char *path1)
+{
+	int result = GIT_ENOTFOUND;
+	size_t i = 0;
+	const git_attr_fnmatch *match;
+
+	git_vector_foreach(vspec, i, match) {
+		if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
+			break;
+		if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
+			break;
+	}
+
+	*matched_at = i;
+	return result;
+}
+
 /* match a path against the vectorized pathspec */
-bool git_pathspec_match_path(
-	git_vector *vspec,
+bool git_pathspec__match(
+	const git_vector *vspec,
 	const char *path,
 	bool disable_fnmatch,
 	bool casefold,
-	const char **matched_pathspec)
+	const char **matched_pathspec,
+	size_t *matched_at)
 {
-	size_t i;
-	git_attr_fnmatch *match;
-	int fnmatch_flags = 0;
-	int (*use_strcmp)(const char *, const char *);
-	int (*use_strncmp)(const char *, const char *, size_t);
+	int result;
+	size_t pos;
+	struct pathspec_match_context ctxt;
 
 	if (matched_pathspec)
 		*matched_pathspec = NULL;
+	if (matched_at)
+		*matched_at = GIT_PATHSPEC_NOMATCH;
 
 	if (!vspec || !vspec->length)
 		return true;
 
-	if (disable_fnmatch)
-		fnmatch_flags = -1;
-	else if (casefold)
-		fnmatch_flags = FNM_CASEFOLD;
+	pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
 
-	if (casefold) {
-		use_strcmp  = git__strcasecmp;
-		use_strncmp = git__strncasecmp;
-	} else {
-		use_strcmp  = git__strcmp;
-		use_strncmp = git__strncmp;
-	}
-
-	git_vector_foreach(vspec, i, match) {
-		int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
-
-		if (result == FNM_NOMATCH)
-			result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
-
-		if (fnmatch_flags >= 0 && result == FNM_NOMATCH)
-			result = p_fnmatch(match->pattern, path, fnmatch_flags);
-
-		/* if we didn't match, look for exact dirname prefix match */
-		if (result == FNM_NOMATCH &&
-			(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
-			use_strncmp(path, match->pattern, match->length) == 0 &&
-			path[match->length] == '/')
-			result = 0;
-
-		if (result == 0) {
-			if (matched_pathspec)
-				*matched_pathspec = match->pattern;
-
-			return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+	result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
+	if (result >= 0) {
+		if (matched_pathspec) {
+			const git_attr_fnmatch *match = git_vector_get(vspec, pos);
+			*matched_pathspec = match->pattern;
 		}
+
+		if (matched_at)
+			*matched_at = pos;
 	}
 
-	return false;
+	return (result > 0);
 }
 
 
-int git_pathspec_context_init(
-	git_pathspec_context *ctxt, const git_strarray *paths)
+int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
 {
 	int error = 0;
 
-	memset(ctxt, 0, sizeof(*ctxt));
+	memset(ps, 0, sizeof(*ps));
 
-	ctxt->prefix = git_pathspec_prefix(paths);
+	ps->prefix = git_pathspec_prefix(paths);
 
-	if ((error = git_pool_init(&ctxt->pool, 1, 0)) < 0 ||
-		(error = git_pathspec_init(&ctxt->pathspec, paths, &ctxt->pool)) < 0)
-		git_pathspec_context_free(ctxt);
+	if ((error = git_pool_init(&ps->pool, 1, 0)) < 0 ||
+		(error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
+		git_pathspec__clear(ps);
 
 	return error;
 }
 
-void git_pathspec_context_free(
-	git_pathspec_context *ctxt)
+void git_pathspec__clear(git_pathspec *ps)
 {
-	git__free(ctxt->prefix);
-	git_pathspec_free(&ctxt->pathspec);
-	git_pool_clear(&ctxt->pool);
-	memset(ctxt, 0, sizeof(*ctxt));
+	git__free(ps->prefix);
+	git_pathspec__vfree(&ps->pathspec);
+	git_pool_clear(&ps->pool);
+	memset(ps, 0, sizeof(*ps));
 }
+
+int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
+{
+	int error = 0;
+	git_pathspec *ps = git__malloc(sizeof(git_pathspec));
+	GITERR_CHECK_ALLOC(ps);
+
+	if ((error = git_pathspec__init(ps, pathspec)) < 0) {
+		git__free(ps);
+		return error;
+	}
+
+	GIT_REFCOUNT_INC(ps);
+	*out = ps;
+	return 0;
+}
+
+static void pathspec_free(git_pathspec *ps)
+{
+	git_pathspec__clear(ps);
+	git__free(ps);
+}
+
+void git_pathspec_free(git_pathspec *ps)
+{
+	if (!ps)
+		return;
+	GIT_REFCOUNT_DEC(ps, pathspec_free);
+}
+
+int git_pathspec_matches_path(
+	const git_pathspec *ps, uint32_t flags, const char *path)
+{
+	bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
+	bool casefold =  (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
+
+	assert(ps && path);
+
+	return (0 != git_pathspec__match(
+		&ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
+}
+
+static void pathspec_match_free(git_pathspec_match_list *m)
+{
+	git_pathspec_free(m->pathspec);
+	m->pathspec = NULL;
+
+	git_array_clear(m->matches);
+	git_array_clear(m->failures);
+	git_pool_clear(&m->pool);
+	git__free(m);
+}
+
+static git_pathspec_match_list *pathspec_match_alloc(
+	git_pathspec *ps, int datatype)
+{
+	git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
+
+	if (m != NULL && git_pool_init(&m->pool, 1, 0) < 0) {
+		pathspec_match_free(m);
+		m = NULL;
+	}
+
+	/* need to keep reference to pathspec and increment refcount because
+	 * failures array stores pointers to the pattern strings of the
+	 * pathspec that had no matches
+	 */
+	GIT_REFCOUNT_INC(ps);
+	m->pathspec = ps;
+	m->datatype = datatype;
+
+	return m;
+}
+
+GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
+{
+	if (!git_bitvec_get(used, pos)) {
+		git_bitvec_set(used, pos, true);
+		return 1;
+	}
+
+	return 0;
+}
+
+static size_t pathspec_mark_remaining(
+	git_bitvec *used,
+	git_vector *patterns,
+	struct pathspec_match_context *ctxt,
+	size_t start,
+	const char *path0,
+	const char *path1)
+{
+	size_t count = 0;
+
+	if (path1 == path0)
+		path1 = NULL;
+
+	for (; start < patterns->length; ++start) {
+		const git_attr_fnmatch *pat = git_vector_get(patterns, start);
+
+		if (git_bitvec_get(used, start))
+			continue;
+
+		if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
+			count += pathspec_mark_pattern(used, start);
+		else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
+			count += pathspec_mark_pattern(used, start);
+	}
+
+	return count;
+}
+
+static int pathspec_build_failure_array(
+	git_pathspec_string_array_t *failures,
+	git_vector *patterns,
+	git_bitvec *used,
+	git_pool *pool)
+{
+	size_t pos;
+	char **failed;
+	const git_attr_fnmatch *pat;
+
+	for (pos = 0; pos < patterns->length; ++pos) {
+		if (git_bitvec_get(used, pos))
+			continue;
+
+		if ((failed = git_array_alloc(*failures)) == NULL)
+			return -1;
+
+		pat = git_vector_get(patterns, pos);
+
+		if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int pathspec_match_from_iterator(
+	git_pathspec_match_list **out,
+	git_iterator *iter,
+	uint32_t flags,
+	git_pathspec *ps)
+{
+	int error = 0;
+	git_pathspec_match_list *m = NULL;
+	const git_index_entry *entry = NULL;
+	struct pathspec_match_context ctxt;
+	git_vector *patterns = &ps->pathspec;
+	bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+	bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+	size_t pos, used_ct = 0, found_files = 0;
+	git_index *index = NULL;
+	git_bitvec used_patterns;
+	char **file;
+
+	if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+		return -1;
+
+	if (out) {
+		*out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
+		GITERR_CHECK_ALLOC(m);
+	}
+
+	if ((error = git_iterator_reset(iter, ps->prefix, ps->prefix)) < 0)
+		goto done;
+
+	if (git_iterator_type(iter) == GIT_ITERATOR_TYPE_WORKDIR &&
+		(error = git_repository_index__weakptr(
+			&index, git_iterator_owner(iter))) < 0)
+		goto done;
+
+	pathspec_match_context_init(
+		&ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+		git_iterator_ignore_case(iter));
+
+	while (!(error = git_iterator_advance(&entry, iter))) {
+		/* search for match with entry->path */
+		int result = git_pathspec__match_at(
+			&pos, patterns, &ctxt, entry->path, NULL);
+
+		/* no matches for this path */
+		if (result < 0)
+			continue;
+
+		/* if result was a negative pattern match, then don't list file */
+		if (!result) {
+			used_ct += pathspec_mark_pattern(&used_patterns, pos);
+			continue;
+		}
+
+		/* check if path is ignored and untracked */
+		if (index != NULL &&
+			git_iterator_current_is_ignored(iter) &&
+			git_index__find(NULL, index, entry->path, GIT_INDEX_STAGE_ANY) < 0)
+			continue;
+
+		/* mark the matched pattern as used */
+		used_ct += pathspec_mark_pattern(&used_patterns, pos);
+		++found_files;
+
+		/* if find_failures is on, check if any later patterns also match */
+		if (find_failures && used_ct < patterns->length)
+			used_ct += pathspec_mark_remaining(
+				&used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
+
+		/* if only looking at failures, exit early or just continue */
+		if (failures_only || !out) {
+			if (used_ct == patterns->length)
+				break;
+			continue;
+		}
+
+		/* insert matched path into matches array */
+		if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
+			(*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
+			error = -1;
+			goto done;
+		}
+	}
+
+	if (error < 0 && error != GIT_ITEROVER)
+		goto done;
+	error = 0;
+
+	/* insert patterns that had no matches into failures array */
+	if (find_failures && used_ct < patterns->length &&
+		(error = pathspec_build_failure_array(
+			&m->failures, patterns, &used_patterns, &m->pool)) < 0)
+		goto done;
+
+	/* if every pattern failed to match, then we have failed */
+	if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
+		giterr_set(GITERR_INVALID, "No matching files were found");
+		error = GIT_ENOTFOUND;
+	}
+
+done:
+	git_bitvec_free(&used_patterns);
+
+	if (error < 0) {
+		pathspec_match_free(m);
+		if (out) *out = NULL;
+	}
+
+	return error;
+}
+
+static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
+{
+	git_iterator_flag_t f = 0;
+
+	if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
+		f |= GIT_ITERATOR_IGNORE_CASE;
+	else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
+		f |= GIT_ITERATOR_DONT_IGNORE_CASE;
+
+	return f;
+}
+
+int git_pathspec_match_workdir(
+	git_pathspec_match_list **out,
+	git_repository *repo,
+	uint32_t flags,
+	git_pathspec *ps)
+{
+	int error = 0;
+	git_iterator *iter;
+
+	assert(repo);
+
+	if (!(error = git_iterator_for_workdir(
+			&iter, repo, pathspec_match_iter_flags(flags), NULL, NULL))) {
+
+		error = pathspec_match_from_iterator(out, iter, flags, ps);
+
+		git_iterator_free(iter);
+	}
+
+	return error;
+}
+
+int git_pathspec_match_index(
+	git_pathspec_match_list **out,
+	git_index *index,
+	uint32_t flags,
+	git_pathspec *ps)
+{
+	int error = 0;
+	git_iterator *iter;
+
+	assert(index);
+
+	if (!(error = git_iterator_for_index(
+			&iter, index, pathspec_match_iter_flags(flags), NULL, NULL))) {
+
+		error = pathspec_match_from_iterator(out, iter, flags, ps);
+
+		git_iterator_free(iter);
+	}
+
+	return error;
+}
+
+int git_pathspec_match_tree(
+	git_pathspec_match_list **out,
+	git_tree *tree,
+	uint32_t flags,
+	git_pathspec *ps)
+{
+	int error = 0;
+	git_iterator *iter;
+
+	assert(tree);
+
+	if (!(error = git_iterator_for_tree(
+			&iter, tree, pathspec_match_iter_flags(flags), NULL, NULL))) {
+
+		error = pathspec_match_from_iterator(out, iter, flags, ps);
+
+		git_iterator_free(iter);
+	}
+
+	return error;
+}
+
+int git_pathspec_match_diff(
+	git_pathspec_match_list **out,
+	git_diff *diff,
+	uint32_t flags,
+	git_pathspec *ps)
+{
+	int error = 0;
+	git_pathspec_match_list *m = NULL;
+	struct pathspec_match_context ctxt;
+	git_vector *patterns = &ps->pathspec;
+	bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+	bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+	size_t i, pos, used_ct = 0, found_deltas = 0;
+	const git_diff_delta *delta, **match;
+	git_bitvec used_patterns;
+
+	assert(diff);
+
+	if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+		return -1;
+
+	if (out) {
+		*out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
+		GITERR_CHECK_ALLOC(m);
+	}
+
+	pathspec_match_context_init(
+		&ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+		git_diff_is_sorted_icase(diff));
+
+	git_vector_foreach(&diff->deltas, i, delta) {
+		/* search for match with delta */
+		int result = git_pathspec__match_at(
+			&pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
+
+		/* no matches for this path */
+		if (result < 0)
+			continue;
+
+		/* mark the matched pattern as used */
+		used_ct += pathspec_mark_pattern(&used_patterns, pos);
+
+		/* if result was a negative pattern match, then don't list file */
+		if (!result)
+			continue;
+
+		++found_deltas;
+
+		/* if find_failures is on, check if any later patterns also match */
+		if (find_failures && used_ct < patterns->length)
+			used_ct += pathspec_mark_remaining(
+				&used_patterns, patterns, &ctxt, pos + 1,
+				delta->old_file.path, delta->new_file.path);
+
+		/* if only looking at failures, exit early or just continue */
+		if (failures_only || !out) {
+			if (used_ct == patterns->length)
+				break;
+			continue;
+		}
+
+		/* insert matched delta into matches array */
+		if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
+			error = -1;
+			goto done;
+		} else {
+			*match = delta;
+		}
+	}
+
+	/* insert patterns that had no matches into failures array */
+	if (find_failures && used_ct < patterns->length &&
+		(error = pathspec_build_failure_array(
+			&m->failures, patterns, &used_patterns, &m->pool)) < 0)
+		goto done;
+
+	/* if every pattern failed to match, then we have failed */
+	if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
+		giterr_set(GITERR_INVALID, "No matching deltas were found");
+		error = GIT_ENOTFOUND;
+	}
+
+done:
+	git_bitvec_free(&used_patterns);
+
+	if (error < 0) {
+		pathspec_match_free(m);
+		if (out) *out = NULL;
+	}
+
+	return error;
+}
+
+void git_pathspec_match_list_free(git_pathspec_match_list *m)
+{
+	if (m)
+		pathspec_match_free(m);
+}
+
+size_t git_pathspec_match_list_entrycount(
+	const git_pathspec_match_list *m)
+{
+	return m ? git_array_size(m->matches) : 0;
+}
+
+const char *git_pathspec_match_list_entry(
+	const git_pathspec_match_list *m, size_t pos)
+{
+	if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
+		!git_array_valid_index(m->matches, pos))
+		return NULL;
+
+	return *((const char **)git_array_get(m->matches, pos));
+}
+
+const git_diff_delta *git_pathspec_match_list_diff_entry(
+	const git_pathspec_match_list *m, size_t pos)
+{
+	if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
+		!git_array_valid_index(m->matches, pos))
+		return NULL;
+
+	return *((const git_diff_delta **)git_array_get(m->matches, pos));
+}
+
+size_t git_pathspec_match_list_failed_entrycount(
+	const git_pathspec_match_list *m)
+{
+	return m ? git_array_size(m->failures) : 0;
+}
+
+const char * git_pathspec_match_list_failed_entry(
+	const git_pathspec_match_list *m, size_t pos)
+{
+	char **entry = m ? git_array_get(m->failures, pos) : NULL;
+
+	return entry ? *entry : NULL;
+}
+
diff --git a/src/pathspec.h b/src/pathspec.h
index f6509df4..40cd21c 100644
--- a/src/pathspec.h
+++ b/src/pathspec.h
@@ -8,9 +8,35 @@
 #define INCLUDE_pathspec_h__
 
 #include "common.h"
+#include <git2/pathspec.h>
 #include "buffer.h"
 #include "vector.h"
 #include "pool.h"
+#include "array.h"
+
+/* public compiled pathspec */
+struct git_pathspec {
+	git_refcount rc;
+	char *prefix;
+	git_vector pathspec;
+	git_pool pool;
+};
+
+enum {
+	PATHSPEC_DATATYPE_STRINGS = 0,
+	PATHSPEC_DATATYPE_DIFF = 1,
+};
+
+typedef git_array_t(char *) git_pathspec_string_array_t;
+
+/* public interface to pathspec matching */
+struct git_pathspec_match_list {
+	git_pathspec *pathspec;
+	git_array_t(void *) matches;
+	git_pathspec_string_array_t failures;
+	git_pool pool;
+	int datatype;
+};
 
 /* what is the common non-wildcard prefix for all items in the pathspec */
 extern char *git_pathspec_prefix(const git_strarray *pathspec);
@@ -19,36 +45,31 @@
 extern bool git_pathspec_is_empty(const git_strarray *pathspec);
 
 /* build a vector of fnmatch patterns to evaluate efficiently */
-extern int git_pathspec_init(
+extern int git_pathspec__vinit(
 	git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
 
 /* free data from the pathspec vector */
-extern void git_pathspec_free(git_vector *vspec);
+extern void git_pathspec__vfree(git_vector *vspec);
+
+#define GIT_PATHSPEC_NOMATCH ((size_t)-1)
 
 /*
  * Match a path against the vectorized pathspec.
  * The matched pathspec is passed back into the `matched_pathspec` parameter,
  * unless it is passed as NULL by the caller.
  */
-extern bool git_pathspec_match_path(
-	git_vector *vspec,
+extern bool git_pathspec__match(
+	const git_vector *vspec,
 	const char *path,
 	bool disable_fnmatch,
 	bool casefold,
-	const char **matched_pathspec);
+	const char **matched_pathspec,
+	size_t *matched_at);
 
 /* easy pathspec setup */
 
-typedef struct {
-	char *prefix;
-	git_vector pathspec;
-	git_pool pool;
-} git_pathspec_context;
+extern int git_pathspec__init(git_pathspec *ps, const git_strarray *paths);
 
-extern int git_pathspec_context_init(
-	git_pathspec_context *ctxt, const git_strarray *paths);
-
-extern void git_pathspec_context_free(
-	git_pathspec_context *ctxt);
+extern void git_pathspec__clear(git_pathspec *ps);
 
 #endif
diff --git a/src/posix.h b/src/posix.h
index 40bcc1a..14c92b9 100644
--- a/src/posix.h
+++ b/src/posix.h
@@ -39,7 +39,7 @@
  *
  * Some of the methods are slightly wrapped to provide
  * saner defaults. Some of these methods are emulated
- * in Windows platforns.
+ * in Windows platforms.
  *
  * Use your manpages to check the docs on these.
  */
@@ -71,16 +71,12 @@
 
 #define p_localtime_r localtime_r
 #define p_gmtime_r gmtime_r
-#define p_gettimeofday gettimeofday
 
 #else
 
 typedef SOCKET GIT_SOCKET;
-struct timezone;
 extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
 extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
-extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
-
 
 #endif
 
@@ -93,6 +89,10 @@
 #	include "unix/posix.h"
 #endif
 
+#ifndef __MINGW32__
+# define p_strnlen strnlen
+#endif
+
 #ifdef NO_READDIR_R
 #	include <dirent.h>
 GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
diff --git a/src/pqueue.h b/src/pqueue.h
index ed71392..9061f82 100644
--- a/src/pqueue.h
+++ b/src/pqueue.h
@@ -48,7 +48,7 @@
  *			should be preallocated
  * @param cmppri the callback function to compare two nodes of the queue
  *
- * @Return the handle or NULL for insufficent memory
+ * @return the handle or NULL for insufficent memory
  */
 int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);
 
@@ -83,8 +83,7 @@
 
 /**
  * pop the highest-ranking item from the queue.
- * @param p the queue
- * @param d where to copy the entry to
+ * @param q the queue
  * @return NULL on error, otherwise the entry
  */
 void *git_pqueue_pop(git_pqueue *q);
@@ -93,7 +92,6 @@
 /**
  * access highest-ranking item without removing it.
  * @param q the queue
- * @param d the entry
  * @return NULL on error, otherwise the entry
  */
 void *git_pqueue_peek(git_pqueue *q);
diff --git a/src/push.c b/src/push.c
index 452d717..3c9d5bb 100644
--- a/src/push.c
+++ b/src/push.c
@@ -70,6 +70,25 @@
 	return 0;
 }
 
+int git_push_set_callbacks(
+	git_push *push,
+	git_packbuilder_progress pack_progress_cb,
+	void *pack_progress_cb_payload,
+	git_push_transfer_progress transfer_progress_cb,
+	void *transfer_progress_cb_payload)
+{
+	if (!push)
+		return -1;
+
+	push->pack_progress_cb = pack_progress_cb;
+	push->pack_progress_cb_payload = pack_progress_cb_payload;
+
+	push->transfer_progress_cb = transfer_progress_cb;
+	push->transfer_progress_cb_payload = transfer_progress_cb_payload;
+
+	return 0;
+}
+
 static void free_refspec(push_spec *spec)
 {
 	if (spec == NULL)
@@ -233,6 +252,37 @@
 	return error;
 }
 
+/**
+ * Insert all tags until we find a non-tag object, which is returned
+ * in `out`.
+ */
+static int enqueue_tag(git_object **out, git_push *push, git_oid *id)
+{
+	git_object *obj = NULL, *target = NULL;
+	int error;
+
+	if ((error = git_object_lookup(&obj, push->repo, id, GIT_OBJ_TAG)) < 0)
+		return error;
+
+	while (git_object_type(obj) == GIT_OBJ_TAG) {
+		if ((error = git_packbuilder_insert(push->pb, git_object_id(obj), NULL)) < 0)
+			break;
+
+		if ((error = git_tag_target(&target, (git_tag *) obj)) < 0)
+			break;
+
+		git_object_free(obj);
+		obj = target;
+	}
+
+	if (error < 0)
+		git_object_free(obj);
+	else
+		*out = obj;
+
+	return error;
+}
+
 static int revwalk(git_vector *commits, git_push *push)
 {
 	git_remote_head *head;
@@ -265,21 +315,11 @@
 			goto on_error;
 
 		if (type == GIT_OBJ_TAG) {
-			git_tag *tag;
 			git_object *target;
 
-			if (git_packbuilder_insert(push->pb, &spec->loid, NULL) < 0)
+			if ((error = enqueue_tag(&target, push, &spec->loid)) < 0)
 				goto on_error;
 
-			if (git_tag_lookup(&tag, push->repo, &spec->loid) < 0)
-				goto on_error;
-
-			if (git_tag_peel(&target, tag) < 0) {
-				git_tag_free(tag);
-				goto on_error;
-			}
-			git_tag_free(tag);
-
 			if (git_object_type(target) == GIT_OBJ_COMMIT) {
 				if (git_revwalk_push(rw, git_object_id(target)) < 0) {
 					git_object_free(target);
@@ -542,7 +582,7 @@
 
 static int do_push(git_push *push)
 {
-	int error;
+	int error = 0;
 	git_transport *transport = push->remote->transport;
 
 	if (!transport->push) {
@@ -562,28 +602,36 @@
 
 	git_packbuilder_set_threads(push->pb, push->pb_parallelism);
 
+	if (push->pack_progress_cb)
+		if ((error = git_packbuilder_set_callbacks(push->pb, push->pack_progress_cb, push->pack_progress_cb_payload)) < 0)
+			goto on_error;
+
 	if ((error = calculate_work(push)) < 0 ||
 		(error = queue_objects(push)) < 0 ||
 		(error = transport->push(transport, push)) < 0)
 		goto on_error;
 
-	error = 0;
-
 on_error:
 	git_packbuilder_free(push->pb);
 	return error;
 }
 
-static int cb_filter_refs(git_remote_head *ref, void *data)
-{
-	git_remote *remote = (git_remote *) data;
-	return git_vector_insert(&remote->refs, ref);
-}
-
 static int filter_refs(git_remote *remote)
 {
+	const git_remote_head **heads;
+	size_t heads_len, i;
+
 	git_vector_clear(&remote->refs);
-	return git_remote_ls(remote, cb_filter_refs, remote);
+
+	if (git_remote_ls(&heads, &heads_len, remote) < 0)
+		return -1;
+
+	for (i = 0; i < heads_len; i++) {
+		if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0)
+			return -1;
+	}
+
+	return 0;
 }
 
 int git_push_finish(git_push *push)
diff --git a/src/push.h b/src/push.h
index e982b83..6c8bf72 100644
--- a/src/push.h
+++ b/src/push.h
@@ -39,6 +39,11 @@
 
 	/* options */
 	unsigned pb_parallelism;
+
+	git_packbuilder_progress pack_progress_cb;
+	void *pack_progress_cb_payload;
+	git_push_transfer_progress transfer_progress_cb;
+	void *transfer_progress_cb_payload;
 };
 
 /**
diff --git a/src/refdb.c b/src/refdb.c
index 4de7188..adb5880 100644
--- a/src/refdb.c
+++ b/src/refdb.c
@@ -16,6 +16,7 @@
 #include "hash.h"
 #include "refdb.h"
 #include "refs.h"
+#include "reflog.h"
 
 int git_refdb_new(git_refdb **out, git_repository *repo)
 {
@@ -201,5 +202,20 @@
 int git_refdb_delete(struct git_refdb *db, const char *ref_name)
 {
 	assert(db && db->backend);
-	return db->backend->delete(db->backend, ref_name);
+	return db->backend->del(db->backend, ref_name);
+}
+
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name)
+{
+	int error;
+
+	assert(db && db->backend);
+
+	if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
+		return error;
+
+	GIT_REFCOUNT_INC(db);
+	(*out)->db = db;
+
+	return 0;
 }
diff --git a/src/refdb.h b/src/refdb.h
index 3aea37b..0ee60d9 100644
--- a/src/refdb.h
+++ b/src/refdb.h
@@ -43,4 +43,8 @@
 int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
 int git_refdb_delete(git_refdb *refdb, const char *ref_name);
 
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db,  const char *name);
+int git_refdb_reflog_write(git_reflog *reflog);
+
+
 #endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index b9e283a..62d5c10 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -15,12 +15,15 @@
 #include "refdb.h"
 #include "refdb_fs.h"
 #include "iterator.h"
+#include "sortedcache.h"
+#include "signature.h"
 
 #include <git2/tag.h>
 #include <git2/object.h>
 #include <git2/refdb.h>
 #include <git2/sys/refdb_backend.h>
 #include <git2/sys/refs.h>
+#include <git2/sys/reflog.h>
 
 GIT__USE_STRMAP;
 
@@ -53,243 +56,151 @@
 	git_repository *repo;
 	char *path;
 
-	git_refcache refcache;
+	git_sortedcache *refcache;
 	int peeling_mode;
+	git_iterator_flag_t iterator_flags;
+	uint32_t direach_flags;
 } refdb_fs_backend;
 
-static int reference_read(
-	git_buf *file_content,
-	time_t *mtime,
-	const char *repo_path,
-	const char *ref_name,
-	int *updated)
+static int packref_cmp(const void *a_, const void *b_)
 {
-	git_buf path = GIT_BUF_INIT;
-	int result;
-
-	assert(file_content && repo_path && ref_name);
-
-	/* Determine the full path of the file */
-	if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
-		return -1;
-
-	result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated);
-	git_buf_free(&path);
-
-	return result;
+	const struct packref *a = a_, *b = b_;
+	return strcmp(a->name, b->name);
 }
 
-static int packed_parse_oid(
-	struct packref **ref_out,
-	const char **buffer_out,
-	const char *buffer_end)
+static int packed_reload(refdb_fs_backend *backend)
 {
-	struct packref *ref = NULL;
+	int error;
+	git_buf packedrefs = GIT_BUF_INIT;
+	char *scan, *eof, *eol;
 
-	const char *buffer = *buffer_out;
-	const char *refname_begin, *refname_end;
-
-	size_t refname_len;
-	git_oid id;
-
-	refname_begin = (buffer + GIT_OID_HEXSZ + 1);
-	if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
-		goto corrupt;
-
-	/* Is this a valid object id? */
-	if (git_oid_fromstr(&id, buffer) < 0)
-		goto corrupt;
-
-	refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
-	if (refname_end == NULL)
-		refname_end = buffer_end;
-
-	if (refname_end[-1] == '\r')
-		refname_end--;
-
-	refname_len = refname_end - refname_begin;
-
-	ref = git__calloc(1, sizeof(struct packref) + refname_len + 1);
-	GITERR_CHECK_ALLOC(ref);
-
-	memcpy(ref->name, refname_begin, refname_len);
-	ref->name[refname_len] = 0;
-
-	git_oid_cpy(&ref->oid, &id);
-
-	*ref_out = ref;
-	*buffer_out = refname_end + 1;
-	return 0;
-
-corrupt:
-	git__free(ref);
-	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
-	return -1;
-}
-
-static int packed_parse_peel(
-	struct packref *tag_ref,
-	const char **buffer_out,
-	const char *buffer_end)
-{
-	const char *buffer = *buffer_out + 1;
-
-	assert(buffer[-1] == '^');
-
-	/* Ensure it's not the first entry of the file */
-	if (tag_ref == NULL)
-		goto corrupt;
-
-	if (buffer + GIT_OID_HEXSZ > buffer_end)
-		goto corrupt;
-
-	/* Is this a valid object id? */
-	if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
-		goto corrupt;
-
-	buffer = buffer + GIT_OID_HEXSZ;
-	if (*buffer == '\r')
-		buffer++;
-
-	if (buffer != buffer_end) {
-		if (*buffer == '\n')
-			buffer++;
-		else
-			goto corrupt;
-	}
-
-	tag_ref->flags |= PACKREF_HAS_PEEL;
-	*buffer_out = buffer;
-	return 0;
-
-corrupt:
-	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
-	return -1;
-}
-
-static int packed_load(refdb_fs_backend *backend)
-{
-	int result, updated;
-	git_buf packfile = GIT_BUF_INIT;
-	const char *buffer_start, *buffer_end;
-	git_refcache *ref_cache = &backend->refcache;
-
-	/* First we make sure we have allocated the hash table */
-	if (ref_cache->packfile == NULL) {
-		ref_cache->packfile = git_strmap_alloc();
-		GITERR_CHECK_ALLOC(ref_cache->packfile);
-	}
-
-	if (backend->path == NULL)
+	if (!backend->path)
 		return 0;
 
-	result = reference_read(&packfile, &ref_cache->packfile_time,
-		backend->path, GIT_PACKEDREFS_FILE, &updated);
+	error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
 
 	/*
-	 * If we couldn't find the file, we need to clear the table and
-	 * return. On any other error, we return that error. If everything
-	 * went fine and the file wasn't updated, then there's nothing new
-	 * for us here, so just return. Anything else means we need to
-	 * refresh the packed refs.
+	 * If we can't find the packed-refs, clear table and return.
+	 * Any other error just gets passed through.
+	 * If no error, and file wasn't changed, just return.
+	 * Anything else means we need to refresh the packed refs.
 	 */
-	if (result == GIT_ENOTFOUND) {
-		git_strmap_clear(ref_cache->packfile);
-		return 0;
+	if (error <= 0) {
+		if (error == GIT_ENOTFOUND) {
+			git_sortedcache_clear(backend->refcache, true);
+			giterr_clear();
+			error = 0;
+		}
+		return error;
 	}
 
-	if (result < 0)
-		return -1;
+	/* At this point, refresh the packed refs from the loaded buffer. */
 
-	if (!updated)
-		return 0;
+	git_sortedcache_clear(backend->refcache, false);
 
-	/*
-	 * At this point, we want to refresh the packed refs. We already
-	 * have the contents in our buffer.
-	 */
-	git_strmap_clear(ref_cache->packfile);
-
-	buffer_start = (const char *)packfile.ptr;
-	buffer_end = (const char *)(buffer_start) + packfile.size;
+	scan = (char *)packedrefs.ptr;
+	eof  = scan + packedrefs.size;
 
 	backend->peeling_mode = PEELING_NONE;
 
-	if (buffer_start[0] == '#') {
+	if (*scan == '#') {
 		static const char *traits_header = "# pack-refs with: ";
 
-		if (git__prefixcmp(buffer_start, traits_header) == 0) {
-			char *traits = (char *)buffer_start + strlen(traits_header);
-			char *traits_end = strchr(traits, '\n');
+		if (git__prefixcmp(scan, traits_header) == 0) {
+			scan += strlen(traits_header);
+			eol = strchr(scan, '\n');
 
-			if (traits_end == NULL)
+			if (!eol)
 				goto parse_failed;
+			*eol = '\0';
 
-			*traits_end = '\0';
-
-			if (strstr(traits, " fully-peeled ") != NULL) {
+			if (strstr(scan, " fully-peeled ") != NULL) {
 				backend->peeling_mode = PEELING_FULL;
-			} else if (strstr(traits, " peeled ") != NULL) {
+			} else if (strstr(scan, " peeled ") != NULL) {
 				backend->peeling_mode = PEELING_STANDARD;
 			}
 
-			buffer_start = traits_end + 1;
+			scan = eol + 1;
 		}
 	}
 
-	while (buffer_start < buffer_end && buffer_start[0] == '#') {
-		buffer_start = strchr(buffer_start, '\n');
-		if (buffer_start == NULL)
+	while (scan < eof && *scan == '#') {
+		if (!(eol = strchr(scan, '\n')))
 			goto parse_failed;
-
-		buffer_start++;
+		scan = eol + 1;
 	}
 
-	while (buffer_start < buffer_end) {
-		int err;
-		struct packref *ref = NULL;
+	while (scan < eof) {
+		struct packref *ref;
+		git_oid oid;
 
-		if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
+		/* parse "<OID> <refname>\n" */
+
+		if (git_oid_fromstr(&oid, scan) < 0)
 			goto parse_failed;
+		scan += GIT_OID_HEXSZ;
 
-		if (buffer_start[0] == '^') {
-			if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
+		if (*scan++ != ' ')
+			goto parse_failed;
+		if (!(eol = strchr(scan, '\n')))
+			goto parse_failed;
+		*eol = '\0';
+		if (eol[-1] == '\r')
+			eol[-1] = '\0';
+
+		if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
+			goto parse_failed;
+		scan = eol + 1;
+
+		git_oid_cpy(&ref->oid, &oid);
+
+		/* look for optional "^<OID>\n" */
+
+		if (*scan == '^') {
+			if (git_oid_fromstr(&oid, scan + 1) < 0)
 				goto parse_failed;
-		} else if (backend->peeling_mode == PEELING_FULL ||
-							(backend->peeling_mode == PEELING_STANDARD &&
-							git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0)) {
-			ref->flags |= PACKREF_CANNOT_PEEL;
-		}
+			scan += GIT_OID_HEXSZ + 1;
 
-		git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
-		if (err < 0)
-			goto parse_failed;
+			if (scan < eof) {
+				if (!(eol = strchr(scan, '\n')))
+					goto parse_failed;
+				scan = eol + 1;
+			}
+
+			git_oid_cpy(&ref->peel, &oid);
+			ref->flags |= PACKREF_HAS_PEEL;
+		}
+		else if (backend->peeling_mode == PEELING_FULL ||
+				(backend->peeling_mode == PEELING_STANDARD &&
+				 git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
+			ref->flags |= PACKREF_CANNOT_PEEL;
 	}
 
-	git_buf_free(&packfile);
+	git_sortedcache_wunlock(backend->refcache);
+	git_buf_free(&packedrefs);
+
 	return 0;
 
 parse_failed:
-	git_strmap_free(ref_cache->packfile);
-	ref_cache->packfile = NULL;
-	git_buf_free(&packfile);
+	giterr_set(GITERR_REFERENCE, "Corrupted packed references file");
+
+	git_sortedcache_clear(backend->refcache, false);
+	git_sortedcache_wunlock(backend->refcache);
+	git_buf_free(&packedrefs);
+
 	return -1;
 }
 
-static int loose_parse_oid(git_oid *oid, const char *filename, git_buf *file_content)
+static int loose_parse_oid(
+	git_oid *oid, const char *filename, git_buf *file_content)
 {
-	size_t len;
-	const char *str;
+	const char *str = git_buf_cstr(file_content);
 
-	len = git_buf_len(file_content);
-	if (len < GIT_OID_HEXSZ)
+	if (git_buf_len(file_content) < GIT_OID_HEXSZ)
 		goto corrupted;
 
-	/* str is guranteed to be zero-terminated */
-	str = git_buf_cstr(file_content);
-
 	/* we need to get 40 OID characters from the file */
-	if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0)
+	if (git_oid_fromstr(oid, str) < 0)
 		goto corrupted;
 
 	/* If the file is longer than 40 chars, the 41st must be a space */
@@ -302,68 +213,72 @@
 	return -1;
 }
 
-static int loose_lookup_to_packfile(
-	struct packref **ref_out,
-	refdb_fs_backend *backend,
-	const char *name)
+static int loose_readbuffer(git_buf *buf, const char *base, const char *path)
 {
-	git_buf ref_file = GIT_BUF_INIT;
-	struct packref *ref = NULL;
-	size_t name_len;
+	int error;
 
-	*ref_out = NULL;
+	/* build full path to file */
+	if ((error = git_buf_joinpath(buf, base, path)) < 0 ||
+		(error = git_futils_readbuffer(buf, buf->ptr)) < 0)
+		git_buf_free(buf);
 
-	if (reference_read(&ref_file, NULL, backend->path, name, NULL) < 0)
-		return -1;
-
-	git_buf_rtrim(&ref_file);
-
-	name_len = strlen(name);
-	ref = git__calloc(1, sizeof(struct packref) + name_len + 1);
-	GITERR_CHECK_ALLOC(ref);
-
-	memcpy(ref->name, name, name_len);
-	ref->name[name_len] = 0;
-
-	if (loose_parse_oid(&ref->oid, name, &ref_file) < 0) {
-		git_buf_free(&ref_file);
-		git__free(ref);
-		return -1;
-	}
-
-	ref->flags = PACKREF_WAS_LOOSE;
-
-	*ref_out = ref;
-	git_buf_free(&ref_file);
-	return 0;
+	return error;
 }
 
+static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
+{
+	int error = 0;
+	git_buf ref_file = GIT_BUF_INIT;
+	struct packref *ref = NULL;
+	git_oid oid;
+
+	/* 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) {
+		giterr_clear();
+		goto done;
+	}
+
+	/* skip symbolic refs */
+	if (!git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF))
+		goto done;
+
+	/* parse OID from file */
+	if ((error = loose_parse_oid(&oid, name, &ref_file)) < 0)
+		goto done;
+
+	git_sortedcache_wlock(backend->refcache);
+
+	if (!(error = git_sortedcache_upsert(
+			(void **)&ref, backend->refcache, name))) {
+
+		git_oid_cpy(&ref->oid, &oid);
+		ref->flags = PACKREF_WAS_LOOSE;
+	}
+
+	git_sortedcache_wunlock(backend->refcache);
+
+done:
+	git_buf_free(&ref_file);
+	return error;
+}
 
 static int _dirent_loose_load(void *data, git_buf *full_path)
 {
 	refdb_fs_backend *backend = (refdb_fs_backend *)data;
-	void *old_ref = NULL;
-	struct packref *ref;
 	const char *file_path;
-	int err;
 
-	if (git_path_isdir(full_path->ptr) == true)
-		return git_path_direach(full_path, _dirent_loose_load, backend);
+	if (git__suffixcmp(full_path->ptr, ".lock") == 0)
+		return 0;
+
+	if (git_path_isdir(full_path->ptr))
+		return git_path_direach(
+			full_path, backend->direach_flags, _dirent_loose_load, backend);
 
 	file_path = full_path->ptr + strlen(backend->path);
 
-	if (loose_lookup_to_packfile(&ref, backend, file_path) < 0)
-		return -1;
-
-	git_strmap_insert2(
-		backend->refcache.packfile, ref->name, ref, old_ref, err);
-	if (err < 0) {
-		git__free(ref);
-		return -1;
-	}
-
-	git__free(old_ref);
-	return 0;
+	return loose_lookup_to_packfile(backend, file_path);
 }
 
 /*
@@ -374,11 +289,8 @@
  */
 static int packed_loadloose(refdb_fs_backend *backend)
 {
+	int error;
 	git_buf refs_path = GIT_BUF_INIT;
-	int result;
-
-	/* the packfile must have been previously loaded! */
-	assert(backend->refcache.packfile);
 
 	if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
 		return -1;
@@ -388,10 +300,12 @@
 	 * This will overwrite any old packed entries with their
 	 * updated loose versions
 	 */
-	result = git_path_direach(&refs_path, _dirent_loose_load, backend);
+	error = git_path_direach(
+		&refs_path, backend->direach_flags, _dirent_loose_load, backend);
+
 	git_buf_free(&refs_path);
 
-	return result;
+	return (error == GIT_EUSER) ? -1 : error;
 }
 
 static int refdb_fs_backend__exists(
@@ -399,23 +313,17 @@
 	git_refdb_backend *_backend,
 	const char *ref_name)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_buf ref_path = GIT_BUF_INIT;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
-	if (packed_load(backend) < 0)
+	if (packed_reload(backend) < 0 ||
+		git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
 		return -1;
 
-	if (git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
-		return -1;
-
-	if (git_path_isfile(ref_path.ptr) == true ||
-		git_strmap_exists(backend->refcache.packfile, ref_path.ptr))
-		*exists = 1;
-	else
-		*exists = 0;
+	*exists = git_path_isfile(ref_path.ptr) ||
+		(git_sortedcache_lookup(backend->refcache, ref_name) != NULL);
 
 	git_buf_free(&ref_path);
 	return 0;
@@ -447,64 +355,39 @@
 	refdb_fs_backend *backend,
 	const char *ref_name)
 {
-	const char *target;
-	git_oid oid;
 	git_buf ref_file = GIT_BUF_INIT;
 	int error = 0;
 
-	error = reference_read(&ref_file, NULL, backend->path, ref_name, NULL);
+	if (out)
+		*out = NULL;
 
-	if (error < 0)
-		goto done;
+	if ((error = loose_readbuffer(&ref_file, backend->path, ref_name)) < 0)
+		/* cannot read loose ref file - gah */;
+	else if (git__prefixcmp(git_buf_cstr(&ref_file), GIT_SYMREF) == 0) {
+		const char *target;
 
-	if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
 		git_buf_rtrim(&ref_file);
 
-		if ((target = loose_parse_symbolic(&ref_file)) == NULL) {
+		if (!(target = loose_parse_symbolic(&ref_file)))
 			error = -1;
-			goto done;
-		}
-
-		*out = git_reference__alloc_symbolic(ref_name, target);
+		else if (out != NULL)
+			*out = git_reference__alloc_symbolic(ref_name, target);
 	} else {
-		if ((error = loose_parse_oid(&oid, ref_name, &ref_file)) < 0)
-			goto done;
+		git_oid oid;
 
-		*out = git_reference__alloc(ref_name, &oid, NULL);
+		if (!(error = loose_parse_oid(&oid, ref_name, &ref_file)) &&
+			out != NULL)
+			*out = git_reference__alloc(ref_name, &oid, NULL);
 	}
 
-	if (*out == NULL)
-		error = -1;
-
-done:
 	git_buf_free(&ref_file);
 	return error;
 }
 
-static int packed_map_entry(
-	struct packref **entry,
-	khiter_t *pos,
-	refdb_fs_backend *backend,
-	const char *ref_name)
+static int ref_error_notfound(const char *name)
 {
-	git_strmap *packfile_refs;
-
-	if (packed_load(backend) < 0)
-		return -1;
-
-	/* Look up on the packfile */
-	packfile_refs = backend->refcache.packfile;
-
-	*pos = git_strmap_lookup_index(packfile_refs, ref_name);
-
-	if (!git_strmap_valid_index(packfile_refs, *pos)) {
-		giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name);
-		return GIT_ENOTFOUND;
-	}
-
-	*entry = git_strmap_value_at(packfile_refs, *pos);
-
-	return 0;
+	giterr_set(GITERR_REFERENCE, "Reference '%s' not found", name);
+	return GIT_ENOTFOUND;
 }
 
 static int packed_lookup(
@@ -512,18 +395,27 @@
 	refdb_fs_backend *backend,
 	const char *ref_name)
 {
-	struct packref *entry;
-	khiter_t pos;
 	int error = 0;
+	struct packref *entry;
 
-	if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0)
-		return error;
-
-	if ((*out = git_reference__alloc(ref_name,
-		&entry->oid, &entry->peel)) == NULL)
+	if (packed_reload(backend) < 0)
 		return -1;
 
-	return 0;
+	if (git_sortedcache_rlock(backend->refcache) < 0)
+		return -1;
+
+	entry = git_sortedcache_lookup(backend->refcache, ref_name);
+	if (!entry) {
+		error = ref_error_notfound(ref_name);
+	} else {
+		*out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
+		if (!*out)
+			error = -1;
+	}
+
+	git_sortedcache_runlock(backend->refcache);
+
+	return error;
 }
 
 static int refdb_fs_backend__lookup(
@@ -531,73 +423,68 @@
 	git_refdb_backend *_backend,
 	const char *ref_name)
 {
-	refdb_fs_backend *backend;
-	int result;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
+	int error;
 
-	assert(_backend);
+	assert(backend);
 
-	backend = (refdb_fs_backend *)_backend;
-
-	if ((result = loose_lookup(out, backend, ref_name)) == 0)
+	if (!(error = loose_lookup(out, backend, ref_name)))
 		return 0;
 
 	/* only try to lookup this reference on the packfile if it
 	 * wasn't found on the loose refs; not if there was a critical error */
-	if (result == GIT_ENOTFOUND) {
+	if (error == GIT_ENOTFOUND) {
 		giterr_clear();
-		result = packed_lookup(out, backend, ref_name);
+		error = packed_lookup(out, backend, ref_name);
 	}
 
-	return result;
+	return error;
 }
 
 typedef struct {
 	git_reference_iterator parent;
 
 	char *glob;
+
+	git_pool pool;
 	git_vector loose;
-	unsigned int loose_pos;
-	khiter_t packed_pos;
+
+	size_t loose_pos;
+	size_t packed_pos;
 } refdb_fs_iter;
 
 static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
 {
 	refdb_fs_iter *iter = (refdb_fs_iter *) _iter;
-	char *loose_path;
-	size_t i;
-
-	git_vector_foreach(&iter->loose, i, loose_path) {
-		git__free(loose_path);
-	}
 
 	git_vector_free(&iter->loose);
-
-	git__free(iter->glob);
+	git_pool_clear(&iter->pool);
 	git__free(iter);
 }
 
 static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
 {
-	git_strmap *packfile = backend->refcache.packfile;
+	int error = 0;
 	git_buf path = GIT_BUF_INIT;
-	git_iterator *fsit;
+	git_iterator *fsit = NULL;
 	const git_index_entry *entry = NULL;
 
 	if (!backend->path) /* do nothing if no path for loose refs */
 		return 0;
 
-	if (git_buf_printf(&path, "%s/refs", backend->path) < 0)
-		return -1;
+	if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
+		(error = git_iterator_for_filesystem(
+			&fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) {
+		git_buf_free(&path);
+		return error;
+	}
 
-	if (git_iterator_for_filesystem(&fsit, git_buf_cstr(&path), 0, NULL, NULL) < 0)
-		return -1;
+	error = git_buf_sets(&path, GIT_REFS_DIR);
 
-	git_vector_init(&iter->loose, 8, NULL);
-	git_buf_sets(&path, GIT_REFS_DIR);
-
-	while (!git_iterator_advance(&entry, fsit)) {
+	while (!error && !git_iterator_advance(&entry, fsit)) {
 		const char *ref_name;
-		khiter_t pos;
+		struct packref *ref;
+		char *ref_dup;
 
 		git_buf_truncate(&path, strlen(GIT_REFS_DIR));
 		git_buf_puts(&path, entry->path);
@@ -607,27 +494,32 @@
 			(iter->glob && p_fnmatch(iter->glob, ref_name, 0) != 0))
 			continue;
 
-		pos = git_strmap_lookup_index(packfile, ref_name);
-		if (git_strmap_valid_index(packfile, pos)) {
-			struct packref *ref = git_strmap_value_at(packfile, pos);
+		git_sortedcache_rlock(backend->refcache);
+		ref = git_sortedcache_lookup(backend->refcache, ref_name);
+		if (ref)
 			ref->flags |= PACKREF_SHADOWED;
-		}
+		git_sortedcache_runlock(backend->refcache);
 
-		git_vector_insert(&iter->loose, git__strdup(ref_name));
+		ref_dup = git_pool_strdup(&iter->pool, ref_name);
+		if (!ref_dup)
+			error = -1;
+		else
+			error = git_vector_insert(&iter->loose, ref_dup);
 	}
 
 	git_iterator_free(fsit);
 	git_buf_free(&path);
 
-	return 0;
+	return error;
 }
 
 static int refdb_fs_backend__iterator_next(
 	git_reference **out, git_reference_iterator *_iter)
 {
+	int error = GIT_ITEROVER;
 	refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
 	refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
-	git_strmap *packfile = backend->refcache.packfile;
+	struct packref *ref;
 
 	while (iter->loose_pos < iter->loose.length) {
 		const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
@@ -638,99 +530,102 @@
 		giterr_clear();
 	}
 
-	while (iter->packed_pos < kh_end(packfile)) {
-		struct packref *ref = NULL;
+	git_sortedcache_rlock(backend->refcache);
 
-		while (!kh_exist(packfile, iter->packed_pos)) {
-			iter->packed_pos++;
-			if (iter->packed_pos == kh_end(packfile))
-				return GIT_ITEROVER;
-		}
-
-		ref = kh_val(packfile, iter->packed_pos);
-		iter->packed_pos++;
+	while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
+		ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+		if (!ref) /* stop now if another thread deleted refs and we past end */
+			break;
 
 		if (ref->flags & PACKREF_SHADOWED)
 			continue;
-
 		if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
 			continue;
 
 		*out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
-		if (*out == NULL)
-			return -1;
-
-		return 0;
+		error = (*out != NULL) ? 0 : -1;
+		break;
 	}
 
-	return GIT_ITEROVER;
+	git_sortedcache_runlock(backend->refcache);
+	return error;
 }
 
 static int refdb_fs_backend__iterator_next_name(
 	const char **out, git_reference_iterator *_iter)
 {
+	int error = GIT_ITEROVER;
 	refdb_fs_iter *iter = (refdb_fs_iter *)_iter;
 	refdb_fs_backend *backend = (refdb_fs_backend *)iter->parent.db->backend;
-	git_strmap *packfile = backend->refcache.packfile;
+	struct packref *ref;
 
 	while (iter->loose_pos < iter->loose.length) {
 		const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
 
-		if (git_strmap_exists(packfile, path))
-			continue;
-
-		*out = path;
-		return 0;
-	}
-
-	while (iter->packed_pos < kh_end(packfile)) {
-		while (!kh_exist(packfile, iter->packed_pos)) {
-			iter->packed_pos++;
-			if (iter->packed_pos == kh_end(packfile))
-				return GIT_ITEROVER;
+		if (loose_lookup(NULL, backend, path) == 0) {
+			*out = path;
+			return 0;
 		}
 
-		*out = kh_key(packfile, iter->packed_pos);
-		iter->packed_pos++;
-
-		if (iter->glob && p_fnmatch(iter->glob, *out, 0) != 0)
-			continue;
-
-		return 0;
+		giterr_clear();
 	}
 
-	return GIT_ITEROVER;
+	git_sortedcache_rlock(backend->refcache);
+
+	while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) {
+		ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++);
+		if (!ref) /* stop now if another thread deleted refs and we past end */
+			break;
+
+		if (ref->flags & PACKREF_SHADOWED)
+			continue;
+		if (iter->glob && p_fnmatch(iter->glob, ref->name, 0) != 0)
+			continue;
+
+		*out = ref->name;
+		error = 0;
+		break;
+	}
+
+	git_sortedcache_runlock(backend->refcache);
+	return error;
 }
 
 static int refdb_fs_backend__iterator(
 	git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
 {
 	refdb_fs_iter *iter;
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
-	if (packed_load(backend) < 0)
+	if (packed_reload(backend) < 0)
 		return -1;
 
 	iter = git__calloc(1, sizeof(refdb_fs_iter));
 	GITERR_CHECK_ALLOC(iter);
 
-	if (glob != NULL)
-		iter->glob = git__strdup(glob);
+	if (git_pool_init(&iter->pool, 1, 0) < 0 ||
+		git_vector_init(&iter->loose, 8, NULL) < 0)
+		goto fail;
+
+	if (glob != NULL &&
+		(iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL)
+		goto fail;
 
 	iter->parent.next = refdb_fs_backend__iterator_next;
 	iter->parent.next_name = refdb_fs_backend__iterator_next_name;
 	iter->parent.free = refdb_fs_backend__iterator_free;
 
-	if (iter_load_loose_paths(backend, iter) < 0) {
-		refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
-		return -1;
-	}
+	if (iter_load_loose_paths(backend, iter) < 0)
+		goto fail;
 
 	*out = (git_reference_iterator *)iter;
 	return 0;
+
+fail:
+	refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
+	return -1;
 }
 
 static bool ref_is_available(
@@ -756,33 +651,40 @@
 	const char* old_ref,
 	int force)
 {
-	struct packref *this_ref;
+	size_t i;
 
-	if (packed_load(backend) < 0)
+	if (packed_reload(backend) < 0)
 		return -1;
 
 	if (!force) {
 		int exists;
 
-		if (refdb_fs_backend__exists(&exists, (git_refdb_backend *)backend, new_ref) < 0)
+		if (refdb_fs_backend__exists(
+				&exists, (git_refdb_backend *)backend, new_ref) < 0)
 			return -1;
 
 		if (exists) {
 			giterr_set(GITERR_REFERENCE,
 				"Failed to write reference '%s': a reference with "
-				" that name already exists.", new_ref);
+				"that name already exists.", new_ref);
 			return GIT_EEXISTS;
 		}
 	}
 
-	git_strmap_foreach_value(backend->refcache.packfile, this_ref, {
-		if (!ref_is_available(old_ref, new_ref, this_ref->name)) {
+	git_sortedcache_rlock(backend->refcache);
+
+	for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+		struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+
+		if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
+			git_sortedcache_runlock(backend->refcache);
 			giterr_set(GITERR_REFERENCE,
-				"The path to reference '%s' collides with an existing one", new_ref);
+				"Path to reference '%s' collides with existing one", new_ref);
 			return -1;
 		}
-	});
-	
+	}
+
+	git_sortedcache_runlock(backend->refcache);
 	return 0;
 }
 
@@ -800,7 +702,7 @@
 	if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
 		return -1;
 
-	if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
+	if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) {
 		git_buf_free(&ref_path);
 		return -1;
 	}
@@ -809,27 +711,16 @@
 
 	if (ref->type == GIT_REF_OID) {
 		char oid[GIT_OID_HEXSZ + 1];
-
-		git_oid_fmt(oid, &ref->target.oid);
-		oid[GIT_OID_HEXSZ] = '\0';
+		git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
 
 		git_filebuf_printf(&file, "%s\n", oid);
-
 	} else if (ref->type == GIT_REF_SYMBOLIC) {
 		git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
 	} else {
 		assert(0); /* don't let this happen */
 	}
 
-	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
-}
-
-static int packed_sort(const void *a, const void *b)
-{
-	const struct packref *ref_a = (const struct packref *)a;
-	const struct packref *ref_b = (const struct packref *)b;
-
-	return strcmp(ref_a->name, ref_b->name);
+	return git_filebuf_commit(&file);
 }
 
 /*
@@ -884,9 +775,7 @@
 static int packed_write_ref(struct packref *ref, git_filebuf *file)
 {
 	char oid[GIT_OID_HEXSZ + 1];
-
-	git_oid_fmt(oid, &ref->oid);
-	oid[GIT_OID_HEXSZ] = 0;
+	git_oid_nfmt(oid, sizeof(oid), &ref->oid);
 
 	/*
 	 * For references that peel to an object in the repo, we must
@@ -900,8 +789,7 @@
 	 */
 	if (ref->flags & PACKREF_HAS_PEEL) {
 		char peel[GIT_OID_HEXSZ + 1];
-		git_oid_fmt(peel, &ref->peel);
-		peel[GIT_OID_HEXSZ] = 0;
+		git_oid_nfmt(peel, sizeof(peel), &ref->peel);
 
 		if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
 			return -1;
@@ -924,31 +812,30 @@
  * is well-written, because we are destructing references
  * here otherwise.
  */
-static int packed_remove_loose(
-	refdb_fs_backend *backend,
-	git_vector *packing_list)
+static int packed_remove_loose(refdb_fs_backend *backend)
 {
 	size_t i;
 	git_buf full_path = GIT_BUF_INIT;
 	int failed = 0;
 
-	for (i = 0; i < packing_list->length; ++i) {
-		struct packref *ref = git_vector_get(packing_list, i);
+	/* backend->refcache is already locked when this is called */
 
-		if ((ref->flags & PACKREF_WAS_LOOSE) == 0)
+	for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+		struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+
+		if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
 			continue;
 
 		if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
 			return -1; /* critical; do not try to recover on oom */
 
-		if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
+		if (git_path_exists(full_path.ptr) && p_unlink(full_path.ptr) < 0) {
 			if (failed)
 				continue;
 
 			giterr_set(GITERR_REFERENCE,
 				"Failed to remove loose reference '%s' after packing: %s",
 				full_path.ptr, strerror(errno));
-
 			failed = 1;
 		}
 
@@ -969,85 +856,53 @@
  */
 static int packed_write(refdb_fs_backend *backend)
 {
+	git_sortedcache *refcache = backend->refcache;
 	git_filebuf pack_file = GIT_FILEBUF_INIT;
 	size_t i;
-	git_buf pack_file_path = GIT_BUF_INIT;
-	git_vector packing_list;
-	unsigned int total_refs;
 
-	assert(backend && backend->refcache.packfile);
-
-	total_refs =
-		(unsigned int)git_strmap_num_entries(backend->refcache.packfile);
-
-	if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
+	/* lock the cache to updates while we do this */
+	if (git_sortedcache_wlock(refcache) < 0)
 		return -1;
 
-	/* Load all the packfile into a vector */
-	{
-		struct packref *reference;
-
-		/* cannot fail: vector already has the right size */
-		git_strmap_foreach_value(backend->refcache.packfile, reference, {
-			git_vector_insert(&packing_list, reference);
-		});
-	}
-
-	/* sort the vector so the entries appear sorted on the packfile */
-	git_vector_sort(&packing_list);
-
-	/* Now we can open the file! */
-	if (git_buf_joinpath(&pack_file_path,
-		backend->path, GIT_PACKEDREFS_FILE) < 0)
-		goto cleanup_memory;
-
-	if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
-		goto cleanup_packfile;
+	/* Open the file! */
+	if (git_filebuf_open(&pack_file, git_sortedcache_path(refcache), 0, GIT_PACKEDREFS_FILE_MODE) < 0)
+		goto fail;
 
 	/* Packfiles have a header... apparently
 	 * This is in fact not required, but we might as well print it
 	 * just for kicks */
 	if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
-		goto cleanup_packfile;
+		goto fail;
 
-	for (i = 0; i < packing_list.length; ++i) {
-		struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
+	for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
+		struct packref *ref = git_sortedcache_entry(refcache, i);
 
 		if (packed_find_peel(backend, ref) < 0)
-			goto cleanup_packfile;
+			goto fail;
 
 		if (packed_write_ref(ref, &pack_file) < 0)
-			goto cleanup_packfile;
+			goto fail;
 	}
 
 	/* if we've written all the references properly, we can commit
 	 * the packfile to make the changes effective */
-	if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
-		goto cleanup_memory;
+	if (git_filebuf_commit(&pack_file) < 0)
+		goto fail;
 
 	/* when and only when the packfile has been properly written,
 	 * we can go ahead and remove the loose refs */
-	 if (packed_remove_loose(backend, &packing_list) < 0)
-		 goto cleanup_memory;
+	if (packed_remove_loose(backend) < 0)
+		goto fail;
 
-	 {
-		struct stat st;
-		if (p_stat(pack_file_path.ptr, &st) == 0)
-			backend->refcache.packfile_time = st.st_mtime;
-	 }
-
-	git_vector_free(&packing_list);
-	git_buf_free(&pack_file_path);
+	git_sortedcache_updated(refcache);
+	git_sortedcache_wunlock(refcache);
 
 	/* we're good now */
 	return 0;
 
-cleanup_packfile:
+fail:
 	git_filebuf_cleanup(&pack_file);
-
-cleanup_memory:
-	git_vector_free(&packing_list);
-	git_buf_free(&pack_file_path);
+	git_sortedcache_wunlock(refcache);
 
 	return -1;
 }
@@ -1057,11 +912,10 @@
 	const git_reference *ref,
 	int force)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	int error;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
 	error = reference_path_available(backend, ref->name, NULL, force);
 	if (error < 0)
@@ -1074,17 +928,13 @@
 	git_refdb_backend *_backend,
 	const char *ref_name)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_buf loose_path = GIT_BUF_INIT;
-	struct packref *pack_ref;
-	khiter_t pack_ref_pos;
+	size_t pack_pos;
 	int error = 0;
 	bool loose_deleted = 0;
 
-	assert(_backend);
-	assert(ref_name);
-
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend && ref_name);
 
 	/* If a loose reference exists, remove it from the filesystem */
 	if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0)
@@ -1100,19 +950,23 @@
 	if (error != 0)
 		return error;
 
+	if (packed_reload(backend) < 0)
+		return -1;
+
 	/* If a packed reference exists, remove it from the packfile and repack */
-	error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref_name);
+	if (git_sortedcache_wlock(backend->refcache) < 0)
+		return -1;
+
+	if (!(error = git_sortedcache_lookup_index(
+			&pack_pos, backend->refcache, ref_name)))
+		error = git_sortedcache_remove(backend->refcache, pack_pos);
+
+	git_sortedcache_wunlock(backend->refcache);
 
 	if (error == GIT_ENOTFOUND)
-		return loose_deleted ? 0 : GIT_ENOTFOUND;
+		return loose_deleted ? 0 : ref_error_notfound(ref_name);
 
-	if (error == 0) {
-		git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos);
-		git__free(pack_ref);
-		error = packed_write(backend);
-	}
-
-	return error;
+	return packed_write(backend);
 }
 
 static int refdb_fs_backend__rename(
@@ -1122,53 +976,44 @@
 	const char *new_name,
 	int force)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 	git_reference *old, *new;
 	int error;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
-	error = reference_path_available(backend, new_name, old_name, force);
-	if (error < 0)
+	if ((error = reference_path_available(
+			backend, new_name, old_name, force)) < 0 ||
+		(error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
 		return error;
 
-	error = refdb_fs_backend__lookup(&old, _backend, old_name);
-	if (error < 0)
-		return error;
-
-	error = refdb_fs_backend__delete(_backend, old_name);
-	if (error < 0) {
+	if ((error = refdb_fs_backend__delete(_backend, old_name)) < 0) {
 		git_reference_free(old);
 		return error;
 	}
 
-	new = realloc(old, sizeof(git_reference) + strlen(new_name) + 1);
-	memcpy(new->name, new_name, strlen(new_name) + 1);
+	new = git_reference__set_name(old, new_name);
+	if (!new) {
+		git_reference_free(old);
+		return -1;
+	}
 
-	error = loose_write(backend, new);
-	if (error < 0) {
+	if ((error = loose_write(backend, new)) < 0 || out == NULL) {
 		git_reference_free(new);
 		return error;
 	}
 
-	if (out) {
-		*out = new;
-	} else {
-		git_reference_free(new);
-	}
-
+	*out = new;
 	return 0;
 }
 
 static int refdb_fs_backend__compress(git_refdb_backend *_backend)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
-	if (packed_load(backend) < 0 || /* load the existing packfile */
+	if (packed_reload(backend) < 0 || /* load the existing packfile */
 		packed_loadloose(backend) < 0 || /* add all the loose refs */
 		packed_write(backend) < 0) /* write back to disk */
 		return -1;
@@ -1176,29 +1021,13 @@
 	return 0;
 }
 
-static void refcache_free(git_refcache *refs)
-{
-	assert(refs);
-
-	if (refs->packfile) {
-		struct packref *reference;
-
-		git_strmap_foreach_value(refs->packfile, reference, {
-			git__free(reference);
-		});
-
-		git_strmap_free(refs->packfile);
-	}
-}
-
 static void refdb_fs_backend__free(git_refdb_backend *_backend)
 {
-	refdb_fs_backend *backend;
+	refdb_fs_backend *backend = (refdb_fs_backend *)_backend;
 
-	assert(_backend);
-	backend = (refdb_fs_backend *)_backend;
+	assert(backend);
 
-	refcache_free(&backend->refcache);
+	git_sortedcache_free(backend->refcache);
 	git__free(backend->path);
 	git__free(backend);
 }
@@ -1222,7 +1051,7 @@
 	if (parts == NULL)
 		return -1;
 
-	/**
+	/*
 	 * From `man gitnamespaces`:
 	 *  namespaces which include a / will expand to a hierarchy
 	 *  of namespaces; for example, GIT_NAMESPACE=foo/bar will store
@@ -1239,15 +1068,355 @@
 	if (git_futils_mkdir_r(git_buf_cstr(path), repo->path_repository, 0777) < 0)
 		return -1;
 
-	/* Return the root of the namespaced path, i.e. without the trailing '/refs' */
+	/* Return root of the namespaced path, i.e. without the trailing '/refs' */
 	git_buf_rtruncate_at_char(path, '/');
 	return 0;
 }
 
+static int reflog_alloc(git_reflog **reflog, const char *name)
+{
+	git_reflog *log;
+
+	*reflog = NULL;
+
+	log = git__calloc(1, sizeof(git_reflog));
+	GITERR_CHECK_ALLOC(log);
+
+	log->ref_name = git__strdup(name);
+	GITERR_CHECK_ALLOC(log->ref_name);
+
+	if (git_vector_init(&log->entries, 0, NULL) < 0) {
+		git__free(log->ref_name);
+		git__free(log);
+		return -1;
+	}
+
+	*reflog = log;
+
+	return 0;
+}
+
+static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
+{
+	const char *ptr;
+	git_reflog_entry *entry;
+
+#define seek_forward(_increase) do { \
+	if (_increase >= buf_size) { \
+		giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+		goto fail; \
+	} \
+	buf += _increase; \
+	buf_size -= _increase; \
+	} while (0)
+
+	while (buf_size > GIT_REFLOG_SIZE_MIN) {
+		entry = git__calloc(1, sizeof(git_reflog_entry));
+		GITERR_CHECK_ALLOC(entry);
+
+		entry->committer = git__malloc(sizeof(git_signature));
+		GITERR_CHECK_ALLOC(entry->committer);
+
+		if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+			goto fail;
+		seek_forward(GIT_OID_HEXSZ + 1);
+
+		if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+			goto fail;
+		seek_forward(GIT_OID_HEXSZ + 1);
+
+		ptr = buf;
+
+		/* Seek forward to the end of the signature. */
+		while (*buf && *buf != '\t' && *buf != '\n')
+			seek_forward(1);
+
+		if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+			goto fail;
+
+		if (*buf == '\t') {
+			/* We got a message. Read everything till we reach LF. */
+			seek_forward(1);
+			ptr = buf;
+
+			while (*buf && *buf != '\n')
+				seek_forward(1);
+
+			entry->msg = git__strndup(ptr, buf - ptr);
+			GITERR_CHECK_ALLOC(entry->msg);
+		} else
+			entry->msg = NULL;
+
+		while (*buf && *buf == '\n' && buf_size > 1)
+			seek_forward(1);
+
+		if (git_vector_insert(&log->entries, entry) < 0)
+			goto fail;
+	}
+
+	return 0;
+
+#undef seek_forward
+
+fail:
+	if (entry)
+		git_reflog_entry__free(entry);
+
+	return -1;
+}
+
+static int create_new_reflog_file(const char *filepath)
+{
+	int fd, error;
+
+	if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+		return error;
+
+	if ((fd = p_open(filepath,
+			O_WRONLY | O_CREAT | O_TRUNC,
+			GIT_REFLOG_FILE_MODE)) < 0)
+		return -1;
+
+	return p_close(fd);
+}
+
+GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
+{
+	return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
+}
+
+static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
+{
+	int error = -1;
+	git_buf log_path = GIT_BUF_INIT;
+	git_buf log_file = GIT_BUF_INIT;
+	git_reflog *log = NULL;
+	git_repository *repo;
+	refdb_fs_backend *backend;
+
+	assert(out && _backend && name);
+
+	backend = (refdb_fs_backend *) _backend;
+	repo = backend->repo;
+
+	if (reflog_alloc(&log, name) < 0)
+		return -1;
+
+	if (retrieve_reflog_path(&log_path, repo, name) < 0)
+		goto cleanup;
+
+	error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
+	if (error < 0 && error != GIT_ENOTFOUND)
+		goto cleanup;
+
+	if ((error == GIT_ENOTFOUND) &&
+		((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
+		goto cleanup;
+ 
+	if ((error = reflog_parse(log,
+		git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
+		goto cleanup;
+
+	*out = log;
+	goto success;
+
+cleanup:
+	git_reflog_free(log);
+
+success:
+	git_buf_free(&log_file);
+	git_buf_free(&log_path);
+
+	return error;
+}
+
+static int serialize_reflog_entry(
+	git_buf *buf,
+	const git_oid *oid_old,
+	const git_oid *oid_new,
+	const git_signature *committer,
+	const char *msg)
+{
+	char raw_old[GIT_OID_HEXSZ+1];
+	char raw_new[GIT_OID_HEXSZ+1];
+
+	git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
+	git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
+
+	git_buf_clear(buf);
+
+	git_buf_puts(buf, raw_old);
+	git_buf_putc(buf, ' ');
+	git_buf_puts(buf, raw_new);
+
+	git_signature__writebuf(buf, " ", committer);
+
+	/* drop trailing LF */
+	git_buf_rtrim(buf);
+
+	if (msg) {
+		git_buf_putc(buf, '\t');
+		git_buf_puts(buf, msg);
+	}
+
+	git_buf_putc(buf, '\n');
+
+	return git_buf_oom(buf);
+}
+
+static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
+{
+	int error = -1;
+	unsigned int i;
+	git_reflog_entry *entry;
+	git_repository *repo;
+	refdb_fs_backend *backend;
+	git_buf log_path = GIT_BUF_INIT;
+	git_buf log = GIT_BUF_INIT;
+	git_filebuf fbuf = GIT_FILEBUF_INIT;
+
+	assert(_backend && reflog);
+
+	backend = (refdb_fs_backend *) _backend;
+	repo = backend->repo;
+
+	if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0)
+		return -1;
+
+	if (!git_path_isfile(git_buf_cstr(&log_path))) {
+		giterr_set(GITERR_INVALID,
+			"Log file for reference '%s' doesn't exist.", reflog->ref_name);
+		goto cleanup;
+	}
+
+	if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE)) < 0)
+		goto cleanup;
+
+	git_vector_foreach(&reflog->entries, i, entry) {
+		if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
+			goto cleanup;
+
+		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+			goto cleanup;
+	}
+
+	error = git_filebuf_commit(&fbuf);
+	goto success;
+
+cleanup:
+	git_filebuf_cleanup(&fbuf);
+
+success:
+	git_buf_free(&log);
+	git_buf_free(&log_path);
+	return error;
+}
+
+static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
+{
+	int error = 0, fd;
+	git_buf old_path = GIT_BUF_INIT;
+	git_buf new_path = GIT_BUF_INIT;
+	git_buf temp_path = GIT_BUF_INIT;
+	git_buf normalized = GIT_BUF_INIT;
+	git_repository *repo;
+	refdb_fs_backend *backend;
+
+	assert(_backend && old_name && new_name);
+
+	backend = (refdb_fs_backend *) _backend;
+	repo = backend->repo;
+
+	if ((error = git_reference__normalize_name(
+		&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
+			return error;
+
+	if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
+		return -1;
+
+	/*
+	 * Move the reflog to a temporary place. This two-phase renaming is required
+	 * in order to cope with funny renaming use cases when one tries to move a reference
+	 * to a partially colliding namespace:
+	 *  - a/b -> a/b/c
+	 *  - a/b/c/d -> a/b/c
+	 */
+	if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+		return -1;
+
+	if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	p_close(fd);
+
+	if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		error = -1;
+		goto cleanup;
+	}
+
+	if (git_path_isdir(git_buf_cstr(&new_path)) && 
+		(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		error = -1;
+	}
+
+cleanup:
+	git_buf_free(&temp_path);
+	git_buf_free(&old_path);
+	git_buf_free(&new_path);
+	git_buf_free(&normalized);
+
+	return error;
+}
+
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+
+	git_repository *repo;
+	refdb_fs_backend *backend;
+
+	assert(_backend && name);
+
+	backend = (refdb_fs_backend *) _backend;
+	repo = backend->repo;
+
+	error = retrieve_reflog_path(&path, repo, name);
+
+	if (!error && git_path_exists(path.ptr))
+		error = p_unlink(path.ptr);
+
+	git_buf_free(&path);
+
+	return error;
+
+}
+
 int git_refdb_backend_fs(
 	git_refdb_backend **backend_out,
 	git_repository *repository)
 {
+	int t = 0;
 	git_buf path = GIT_BUF_INIT;
 	refdb_fs_backend *backend;
 
@@ -1256,22 +1425,47 @@
 
 	backend->repo = repository;
 
-	if (setup_namespace(&path, repository) < 0) {
-		git__free(backend);
-		return -1;
-	}
+	if (setup_namespace(&path, repository) < 0)
+		goto fail;
 
 	backend->path = git_buf_detach(&path);
 
+	if (git_buf_joinpath(&path, backend->path, GIT_PACKEDREFS_FILE) < 0 ||
+		git_sortedcache_new(
+			&backend->refcache, offsetof(struct packref, name),
+			NULL, NULL, packref_cmp, git_buf_cstr(&path)) < 0)
+		goto fail;
+
+	git_buf_free(&path);
+
+	if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
+		backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
+		backend->direach_flags  |= GIT_PATH_DIR_IGNORE_CASE;
+	}
+	if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
+		backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+		backend->direach_flags  |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
+	}
+
 	backend->parent.exists = &refdb_fs_backend__exists;
 	backend->parent.lookup = &refdb_fs_backend__lookup;
 	backend->parent.iterator = &refdb_fs_backend__iterator;
 	backend->parent.write = &refdb_fs_backend__write;
-	backend->parent.delete = &refdb_fs_backend__delete;
+	backend->parent.del = &refdb_fs_backend__delete;
 	backend->parent.rename = &refdb_fs_backend__rename;
 	backend->parent.compress = &refdb_fs_backend__compress;
 	backend->parent.free = &refdb_fs_backend__free;
+	backend->parent.reflog_read = &refdb_reflog_fs__read;
+	backend->parent.reflog_write = &refdb_reflog_fs__write;
+	backend->parent.reflog_rename = &refdb_reflog_fs__rename;
+	backend->parent.reflog_delete = &refdb_reflog_fs__delete;
 
 	*backend_out = (git_refdb_backend *)backend;
 	return 0;
+
+fail:
+	git_buf_free(&path);
+	git__free(backend->path);
+	git__free(backend);
+	return -1;
 }
diff --git a/src/reflog.c b/src/reflog.c
index 4cc20d2..cebb87d 100644
--- a/src/reflog.c
+++ b/src/reflog.c
@@ -9,82 +9,16 @@
 #include "repository.h"
 #include "filebuf.h"
 #include "signature.h"
+#include "refdb.h"
 
-static int reflog_init(git_reflog **reflog, const git_reference *ref)
+#include <git2/sys/refdb_backend.h>
+
+git_reflog_entry *git_reflog_entry__alloc(void)
 {
-	git_reflog *log;
-
-	*reflog = NULL;
-
-	log = git__calloc(1, sizeof(git_reflog));
-	GITERR_CHECK_ALLOC(log);
-
-	log->ref_name = git__strdup(ref->name);
-	GITERR_CHECK_ALLOC(log->ref_name);
-
-	if (git_vector_init(&log->entries, 0, NULL) < 0) {
-		git__free(log->ref_name);
-		git__free(log);
-		return -1;
-	}
-
-	log->owner = git_reference_owner(ref);
-	*reflog = log;
-
-	return 0;
+	return git__calloc(1, sizeof(git_reflog_entry));
 }
 
-static int serialize_reflog_entry(
-	git_buf *buf,
-	const git_oid *oid_old,
-	const git_oid *oid_new,
-	const git_signature *committer,
-	const char *msg)
-{
-	char raw_old[GIT_OID_HEXSZ+1];
-	char raw_new[GIT_OID_HEXSZ+1];
-
-	git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
-	git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
-
-	git_buf_clear(buf);
-
-	git_buf_puts(buf, raw_old);
-	git_buf_putc(buf, ' ');
-	git_buf_puts(buf, raw_new);
-
-	git_signature__writebuf(buf, " ", committer);
-
-	/* drop trailing LF */
-	git_buf_rtrim(buf);
-
-	if (msg) {
-		git_buf_putc(buf, '\t');
-		git_buf_puts(buf, msg);
-	}
-
-	git_buf_putc(buf, '\n');
-
-	return git_buf_oom(buf);
-}
-
-static int reflog_entry_new(git_reflog_entry **entry)
-{
-	git_reflog_entry *e;
-
-	assert(entry);
-
-	e = git__malloc(sizeof(git_reflog_entry));
-	GITERR_CHECK_ALLOC(e);
-
-	memset(e, 0, sizeof(git_reflog_entry));
-
-	*entry = e;
-
-	return 0;
-}
-
-static void reflog_entry_free(git_reflog_entry *entry)
+void git_reflog_entry__free(git_reflog_entry *entry)
 {
 	git_signature_free(entry->committer);
 
@@ -92,75 +26,6 @@
 	git__free(entry);
 }
 
-static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
-{
-	const char *ptr;
-	git_reflog_entry *entry;
-
-#define seek_forward(_increase) do { \
-	if (_increase >= buf_size) { \
-		giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
-		goto fail; \
-	} \
-	buf += _increase; \
-	buf_size -= _increase; \
-	} while (0)
-
-	while (buf_size > GIT_REFLOG_SIZE_MIN) {
-		if (reflog_entry_new(&entry) < 0)
-			return -1;
-
-		entry->committer = git__malloc(sizeof(git_signature));
-		GITERR_CHECK_ALLOC(entry->committer);
-
-		if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
-			goto fail;
-		seek_forward(GIT_OID_HEXSZ + 1);
-
-		if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
-			goto fail;
-		seek_forward(GIT_OID_HEXSZ + 1);
-
-		ptr = buf;
-
-		/* Seek forward to the end of the signature. */
-		while (*buf && *buf != '\t' && *buf != '\n')
-			seek_forward(1);
-
-		if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
-			goto fail;
-
-		if (*buf == '\t') {
-			/* We got a message. Read everything till we reach LF. */
-			seek_forward(1);
-			ptr = buf;
-
-			while (*buf && *buf != '\n')
-				seek_forward(1);
-
-			entry->msg = git__strndup(ptr, buf - ptr);
-			GITERR_CHECK_ALLOC(entry->msg);
-		} else
-			entry->msg = NULL;
-
-		while (*buf && *buf == '\n' && buf_size > 1)
-			seek_forward(1);
-
-		if (git_vector_insert(&log->entries, entry) < 0)
-			goto fail;
-	}
-
-	return 0;
-
-#undef seek_forward
-
-fail:
-	if (entry)
-		reflog_entry_free(entry);
-
-	return -1;
-}
-
 void git_reflog_free(git_reflog *reflog)
 {
 	size_t i;
@@ -169,10 +34,13 @@
 	if (reflog == NULL)
 		return;
 
+	if (reflog->db)
+		GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
+
 	for (i=0; i < reflog->entries.length; i++) {
 		entry = git_vector_get(&reflog->entries, i);
 
-		reflog_entry_free(entry);
+		git_reflog_entry__free(entry);
 	}
 
 	git_vector_free(&reflog->entries);
@@ -180,115 +48,30 @@
 	git__free(reflog);
 }
 
-static int retrieve_reflog_path(git_buf *path, const git_reference *ref)
+int git_reflog_read(git_reflog **reflog, git_repository *repo,  const char *name)
 {
-	return git_buf_join_n(path, '/', 3,
-		git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name);
-}
+	git_refdb *refdb;
+	int error;
 
-static int create_new_reflog_file(const char *filepath)
-{
-	int fd, error;
+	assert(reflog && repo && name);
 
-	if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
 		return error;
 
-	if ((fd = p_open(filepath,
-			O_WRONLY | O_CREAT | O_TRUNC,
-			GIT_REFLOG_FILE_MODE)) < 0)
-		return -1;
-
-	return p_close(fd);
-}
-
-int git_reflog_read(git_reflog **reflog, const git_reference *ref)
-{
-	int error = -1;
-	git_buf log_path = GIT_BUF_INIT;
-	git_buf log_file = GIT_BUF_INIT;
-	git_reflog *log = NULL;
-
-	assert(reflog && ref);
-
-	*reflog = NULL;
-
-	if (reflog_init(&log, ref) < 0)
-		return -1;
-
-	if (retrieve_reflog_path(&log_path, ref) < 0)
-		goto cleanup;
-
-	error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
-	if (error < 0 && error != GIT_ENOTFOUND)
-		goto cleanup;
-
-	if ((error == GIT_ENOTFOUND) &&
-		((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
-		goto cleanup;
-
-	if ((error = reflog_parse(log,
-		git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
-		goto cleanup;
-
-	*reflog = log;
-	goto success;
-
-cleanup:
-	git_reflog_free(log);
-
-success:
-	git_buf_free(&log_file);
-	git_buf_free(&log_path);
-
-	return error;
+	return git_refdb_reflog_read(reflog, refdb, name);
 }
 
 int git_reflog_write(git_reflog *reflog)
 {
-	int error = -1;
-	unsigned int i;
-	git_reflog_entry *entry;
-	git_buf log_path = GIT_BUF_INIT;
-	git_buf log = GIT_BUF_INIT;
-	git_filebuf fbuf = GIT_FILEBUF_INIT;
+	git_refdb *db;
 
-	assert(reflog);
+	assert(reflog && reflog->db);
 
-	if (git_buf_join_n(&log_path, '/', 3,
-		git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0)
-		return -1;
-
-	if (!git_path_isfile(git_buf_cstr(&log_path))) {
-		giterr_set(GITERR_INVALID,
-			"Log file for reference '%s' doesn't exist.", reflog->ref_name);
-		goto cleanup;
-	}
-
-	if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
-		goto cleanup;
-
-	git_vector_foreach(&reflog->entries, i, entry) {
-		if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
-			goto cleanup;
-
-		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
-			goto cleanup;
-	}
-
-	error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
-	goto success;
-
-cleanup:
-	git_filebuf_cleanup(&fbuf);
-
-success:
-	git_buf_free(&log);
-	git_buf_free(&log_path);
-	return error;
+	db = reflog->db;
+	return db->backend->reflog_write(db->backend, reflog);
 }
 
-int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
-				const git_signature *committer, const char *msg)
+int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
 {
 	git_reflog_entry *entry;
 	const git_reflog_entry *previous;
@@ -296,8 +79,8 @@
 
 	assert(reflog && new_oid && committer);
 
-	if (reflog_entry_new(&entry) < 0)
-		return -1;
+	entry = git__calloc(1, sizeof(git_reflog_entry));
+	GITERR_CHECK_ALLOC(entry);
 
 	if ((entry->committer = git_signature_dup(committer)) == NULL)
 		goto cleanup;
@@ -333,94 +116,30 @@
 	return 0;
 
 cleanup:
-	reflog_entry_free(entry);
+	git_reflog_entry__free(entry);
 	return -1;
 }
 
-int git_reflog_rename(git_reference *ref, const char *new_name)
+int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
 {
-	int error = 0, fd;
-	git_buf old_path = GIT_BUF_INIT;
-	git_buf new_path = GIT_BUF_INIT;
-	git_buf temp_path = GIT_BUF_INIT;
-	git_buf normalized = GIT_BUF_INIT;
+	git_refdb *refdb;
+	int error;
 
-	assert(ref && new_name);
-
-	if ((error = git_reference__normalize_name(
-		&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
-			return error;
-
-	if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0)
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
 		return -1;
 
-	if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0)
-		return -1;
-
-	if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
-		return -1;
-
-	/*
-	 * Move the reflog to a temporary place. This two-phase renaming is required
-	 * in order to cope with funny renaming use cases when one tries to move a reference
-	 * to a partially colliding namespace:
-	 *  - a/b -> a/b/c
-	 *  - a/b/c/d -> a/b/c
-	 */
-	if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
-		return -1;
-
-	if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	p_close(fd);
-
-	if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
-		error = -1;
-		goto cleanup;
-	}
-
-	if (git_path_isdir(git_buf_cstr(&new_path)) && 
-		(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
-		error = -1;
-		goto cleanup;
-	}
-
-	if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
-		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
-		error = -1;
-	}
-
-cleanup:
-	git_buf_free(&temp_path);
-	git_buf_free(&old_path);
-	git_buf_free(&new_path);
-	git_buf_free(&normalized);
-
-	return error;
+	return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
 }
 
-int git_reflog_delete(git_reference *ref)
+int git_reflog_delete(git_repository *repo, const char *name)
 {
+	git_refdb *refdb;
 	int error;
-	git_buf path = GIT_BUF_INIT;
 
-	error = retrieve_reflog_path(&path, ref);
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+		return -1;
 
-	if (!error && git_path_exists(path.ptr))
-		error = p_unlink(path.ptr);
-
-	git_buf_free(&path);
-
-	return error;
+	return refdb->backend->reflog_delete(refdb->backend, name);
 }
 
 size_t git_reflog_entrycount(git_reflog *reflog)
@@ -429,11 +148,6 @@
 	return reflog->entries.length;
 }
 
-GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
-{
-	return (total - 1) - idx;
-}
-
 const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
 {
 	assert(reflog);
@@ -469,16 +183,11 @@
 	return entry->msg;
 }
 
-int git_reflog_drop(
-	git_reflog *reflog,
-	size_t idx,
-	int rewrite_previous_entry)
+int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
 {
 	size_t entrycount;
 	git_reflog_entry *entry, *previous;
 
-	assert(reflog);
-
 	entrycount = git_reflog_entrycount(reflog);
 
 	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
@@ -488,7 +197,7 @@
 		return GIT_ENOTFOUND;
 	}
 
-	reflog_entry_free(entry);
+	git_reflog_entry__free(entry);
 
 	if (git_vector_remove(
 			&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
@@ -521,3 +230,22 @@
 
 	return 0;
 }
+
+int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id,
+			 const git_signature *committer, const char *msg)
+{
+	int error;
+	git_reflog *reflog;
+
+	if ((error = git_reflog_read(&reflog, repo, name)) < 0)
+		return error;
+
+	if ((error = git_reflog_append(reflog, id, committer, msg)) < 0)
+		goto cleanup;
+
+	error = git_reflog_write(reflog);
+
+cleanup:
+	git_reflog_free(reflog);
+	return error;
+}
diff --git a/src/reflog.h b/src/reflog.h
index 9444ebd..2d31ae4 100644
--- a/src/reflog.h
+++ b/src/reflog.h
@@ -27,9 +27,14 @@
 };
 
 struct git_reflog {
+	git_refdb *db;
 	char *ref_name;
-	git_repository *owner;
 	git_vector entries;
 };
 
+GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
+{
+	return (total - 1) - idx;
+}
+
 #endif /* INCLUDE_reflog_h__ */
diff --git a/src/refs.c b/src/refs.c
index c0e460c..472a798 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -88,6 +88,17 @@
 	return ref;
 }
 
+git_reference *git_reference__set_name(
+	git_reference *ref, const char *name)
+{
+	size_t namelen = strlen(name);
+	git_reference *rewrite =
+		git__realloc(ref, sizeof(git_reference) + namelen + 1);
+	if (rewrite != NULL)
+		memcpy(rewrite->name, name, namelen + 1);
+	return rewrite;
+}
+
 void git_reference_free(git_reference *reference)
 {
 	if (reference == NULL)
@@ -127,6 +138,22 @@
 	return 0;
 }
 
+static int reference_normalize_for_repo(
+	char *out,
+	size_t out_size,
+	git_repository *repo,
+	const char *name)
+{
+	int precompose;
+	unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
+
+	if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
+		precompose)
+		flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
+
+	return git_reference_normalize_name(out, out_size, name, flags);
+}
+
 int git_reference_lookup_resolved(
 	git_reference **ref_out,
 	git_repository *repo,
@@ -148,13 +175,13 @@
 	else if (max_nesting < 0)
 		max_nesting = DEFAULT_NESTING_LEVEL;
 
-	strncpy(scan_name, name, GIT_REFNAME_MAX);
 	scan_type = GIT_REF_SYMBOLIC;
 
-	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
-		return -1;
+	if ((error = reference_normalize_for_repo(
+			scan_name, sizeof(scan_name), repo, name)) < 0)
+		return error;
 
-	if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
 		return error;
 
 	for (nesting = max_nesting;
@@ -162,7 +189,7 @@
 		 nesting--)
 	{
 		if (nesting != max_nesting) {
-			strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
+			strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
 			git_reference_free(ref);
 		}
 
@@ -456,7 +483,7 @@
 	if (reference_has_log < 0)
 		return reference_has_log;
 
-	if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+	if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0)
 		return error;
 
 	return 0;
@@ -700,17 +727,21 @@
 	return true;
 }
 
+/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
 int git_reference__normalize_name(
 	git_buf *buf,
 	const char *name,
 	unsigned int flags)
 {
-	// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
-
 	char *current;
 	int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
 	unsigned int process_flags;
 	bool normalize = (buf != NULL);
+
+#ifdef GIT_USE_ICONV
+	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
 	assert(name);
 
 	process_flags = flags;
@@ -722,6 +753,16 @@
 	if (normalize)
 		git_buf_clear(buf);
 
+#ifdef GIT_USE_ICONV
+	if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
+		size_t namelen = strlen(current);
+		if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
+			(error = git_path_iconv(&ic, &current, &namelen)) < 0)
+			goto cleanup;
+		error = GIT_EINVALIDSPEC;
+	}
+#endif
+
 	while (true) {
 		segment_len = ensure_segment_validity(current);
 		if (segment_len < 0) {
@@ -798,6 +839,10 @@
 	if (error && normalize)
 		git_buf_free(buf);
 
+#ifdef GIT_USE_ICONV
+	git_path_iconv_clear(&ic);
+#endif
+
 	return error;
 }
 
@@ -952,6 +997,17 @@
 	return git_reference__is_remote(ref->name);
 }
 
+int git_reference__is_tag(const char *ref_name)
+{
+	return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
+}
+
+int git_reference_is_tag(git_reference *ref)
+{
+	assert(ref);
+	return git_reference__is_tag(ref->name);
+}
+
 static int peel_error(int error, git_reference *ref, const char* msg)
 {
 	giterr_set(
@@ -961,9 +1017,9 @@
 }
 
 int git_reference_peel(
-		git_object **peeled,
-		git_reference *ref,
-		git_otype target_type)
+	git_object **peeled,
+	git_reference *ref,
+	git_otype target_type)
 {
 	git_reference *resolved = NULL;
 	git_object *target = NULL;
@@ -1005,24 +1061,19 @@
 	return error;
 }
 
-int git_reference__is_valid_name(
-	const char *refname,
-	unsigned int flags)
+int git_reference__is_valid_name(const char *refname, unsigned int flags)
 {
-	int error;
+	if (git_reference__normalize_name(NULL, refname, flags) < 0) {
+		giterr_clear();
+		return false;
+	}
 
-	error = git_reference__normalize_name(NULL, refname, flags) == 0;
-	giterr_clear();
-
-	return error;
+	return true;
 }
 
-int git_reference_is_valid_name(
-	const char *refname)
+int git_reference_is_valid_name(const char *refname)
 {
-	return git_reference__is_valid_name(
-		refname,
-		GIT_REF_FORMAT_ALLOW_ONELEVEL);
+	return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
 }
 
 const char *git_reference_shorthand(git_reference *ref)
diff --git a/src/refs.h b/src/refs.h
index f487ee3..80c7703 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -46,6 +46,8 @@
 #define GIT_STASH_FILE "stash"
 #define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
 
+#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE	(1u << 16)
+
 #define GIT_REFNAME_MAX 1024
 
 struct git_reference {
@@ -61,12 +63,15 @@
 	char name[0];
 };
 
+git_reference *git_reference__set_name(git_reference *ref, const char *name);
+
 int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
 int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
 int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid);
 int git_reference__is_valid_name(const char *refname, unsigned int flags);
 int git_reference__is_branch(const char *ref_name);
 int git_reference__is_remote(const char *ref_name);
+int git_reference__is_tag(const char *ref_name);
 
 /**
  * Lookup a reference by name and try to resolve to an OID.
diff --git a/src/refspec.c b/src/refspec.c
index a907df8..a973400 100644
--- a/src/refspec.c
+++ b/src/refspec.c
@@ -12,6 +12,7 @@
 #include "util.h"
 #include "posix.h"
 #include "refs.h"
+#include "vector.h"
 
 int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
 {
@@ -225,25 +226,31 @@
 	return refspec_transform_internal(out, outlen, spec->dst, spec->src, name);
 }
 
-static int refspec_transform(git_buf *out, const char *from, const char *to, const char *name)
+static int refspec_transform(
+	git_buf *out, const char *from, const char *to, const char *name)
 {
-	if (git_buf_sets(out, to) < 0)
+	size_t to_len   = to   ? strlen(to)   : 0;
+	size_t from_len = from ? strlen(from) : 0;
+	size_t name_len = name ? strlen(name) : 0;
+
+	if (git_buf_set(out, to, to_len) < 0)
 		return -1;
 
-	/*
-	 * No '*' at the end means that it's mapped to one specific
-	 * branch, so no actual transformation is needed.
-	 */
-	if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
-		return 0;
+	if (to_len > 0) {
+		/* No '*' at the end of 'to' means that refspec is mapped to one
+		 * specific branch, so no actual transformation is needed.
+		 */
+		if (out->ptr[to_len - 1] != '*')
+			return 0;
+		git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */
+	}
 
-	git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
-	git_buf_puts(out, name + strlen(from) - 1);
+	if (from_len > 0) /* ignore trailing '*' from 'from' */
+		from_len--;
+	if (from_len > name_len)
+		from_len = name_len;
 
-	if (git_buf_oom(out))
-		return -1;
-
-	return 0;
+	return git_buf_put(out, name + from_len, name_len - from_len);
 }
 
 int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
@@ -281,3 +288,70 @@
 
 	return spec->push;
 }
+
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs)
+{
+	git_buf buf = GIT_BUF_INIT;
+	size_t j, pos;
+	git_remote_head key;
+
+	const char* formatters[] = {
+		GIT_REFS_DIR "%s",
+		GIT_REFS_TAGS_DIR "%s",
+		GIT_REFS_HEADS_DIR "%s",
+		NULL
+	};
+
+	git_refspec *cur = git__calloc(1, sizeof(git_refspec));
+	GITERR_CHECK_ALLOC(cur);
+
+	cur->force = spec->force;
+	cur->push = spec->push;
+	cur->pattern = spec->pattern;
+	cur->matching = spec->matching;
+	cur->string = git__strdup(spec->string);
+
+	/* shorthand on the lhs */
+	if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
+		for (j = 0; formatters[j]; j++) {
+			git_buf_clear(&buf);
+			if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
+				return -1;
+
+			key.name = (char *) git_buf_cstr(&buf);
+			if (!git_vector_search(&pos, refs, &key)) {
+				/* we found something to match the shorthand, set src to that */
+				cur->src = git_buf_detach(&buf);
+			}
+		}
+	}
+
+	/* No shorthands found, copy over the name */
+	if (cur->src == NULL && spec->src != NULL) {
+		cur->src = git__strdup(spec->src);
+		GITERR_CHECK_ALLOC(cur->src);
+	}
+
+	if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
+		/* if it starts with "remotes" then we just prepend "refs/" */
+		if (!git__prefixcmp(spec->dst, "remotes/")) {
+			git_buf_puts(&buf, GIT_REFS_DIR);
+		} else {
+			git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
+		}
+
+		if (git_buf_puts(&buf, spec->dst) < 0)
+			return -1;
+
+		cur->dst = git_buf_detach(&buf);
+	}
+
+	git_buf_free(&buf);
+
+	if (cur->dst == NULL && spec->dst != NULL) {
+		cur->dst = git__strdup(spec->dst);
+		GITERR_CHECK_ALLOC(cur->dst);
+	}
+
+	return git_vector_insert(out, cur);
+}
diff --git a/src/refspec.h b/src/refspec.h
index 44d484c..51b7bfe 100644
--- a/src/refspec.h
+++ b/src/refspec.h
@@ -9,6 +9,7 @@
 
 #include "git2/refspec.h"
 #include "buffer.h"
+#include "vector.h"
 
 struct git_refspec {
 	char *string;
@@ -17,7 +18,6 @@
 	unsigned int force :1,
 		push : 1,
 		pattern :1,
-		dwim :1,
 		matching :1;
 };
 
@@ -63,4 +63,10 @@
  */
 int git_refspec_is_wildcard(const git_refspec *spec);
 
+/**
+ * DWIM `spec` with `refs` existing on the remote, append the dwim'ed
+ * result in `out`.
+ */
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs);
+
 #endif
diff --git a/src/remote.c b/src/remote.c
index 0e8354a..3d890a5 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -10,6 +10,7 @@
 #include "git2/oid.h"
 #include "git2/net.h"
 
+#include "common.h"
 #include "config.h"
 #include "repository.h"
 #include "remote.h"
@@ -18,7 +19,7 @@
 #include "refspec.h"
 #include "fetchhead.h"
 
-#include <regex.h>
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
 
 static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
 {
@@ -81,6 +82,37 @@
 	return error;
 }
 
+static int get_check_cert(int *out, git_repository *repo)
+{
+	git_config *cfg;
+	const char *val;
+	int error = 0;
+
+	assert(out && repo);
+
+	/* By default, we *DO* want to verify the certificate. */
+	*out = 1;
+
+	/* Go through the possible sources for SSL verification settings, from
+	 * most specific to least specific. */
+
+	/* GIT_SSL_NO_VERIFY environment variable */
+	if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL)
+		return git_config_parse_bool(out, val);
+
+	/* http.sslVerify config setting */
+	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+		return error;
+
+	if ((error = git_config_get_bool(out, cfg, "http.sslVerify")) == 0)
+		return 0;
+	else if (error != GIT_ENOTFOUND)
+		return error;
+
+	giterr_clear();
+	return 0;
+}
+
 static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
 {
 	git_remote *remote;
@@ -94,9 +126,11 @@
 	GITERR_CHECK_ALLOC(remote);
 
 	remote->repo = repo;
-	remote->check_cert = 1;
 	remote->update_fetchhead = 1;
 
+	if (get_check_cert(&remote->check_cert, repo) < 0)
+		goto on_error;
+
 	if (git_vector_init(&remote->refs, 32, NULL) < 0)
 		goto on_error;
 
@@ -183,6 +217,32 @@
 	return -1;
 }
 
+int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
+{
+	git_remote *remote = NULL;
+	int error;
+
+	if ((error = ensure_remote_name_is_valid(name)) < 0)
+		return error;
+
+	if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
+		return error;
+
+	if (create_internal(&remote, repo, name, url, fetch) < 0)
+		goto on_error;
+
+	if (git_remote_save(remote) < 0)
+		goto on_error;
+
+	*out = remote;
+
+	return 0;
+
+on_error:
+	git_remote_free(remote);
+	return -1;
+}
+
 int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
 {
 	int error;
@@ -208,7 +268,8 @@
 }
 
 static int get_optional_config(
-	git_config *config, git_buf *buf, git_config_foreach_cb cb, void *payload)
+	bool *found, git_config *config, git_buf *buf,
+	git_config_foreach_cb cb, void *payload)
 {
 	int error = 0;
 	const char *key = git_buf_cstr(buf);
@@ -217,10 +278,13 @@
 		return -1;
 
 	if (cb != NULL)
-		error = git_config_get_multivar(config, key, NULL, cb, payload);
+		error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
 	else
 		error = git_config_get_string(payload, config, key);
 
+	if (found)
+		*found = !error;
+
 	if (error == GIT_ENOTFOUND) {
 		giterr_clear();
 		error = 0;
@@ -240,6 +304,7 @@
 	int error = 0;
 	git_config *config;
 	struct refspec_cb_data data;
+	bool optional_setting_found = false, found;
 
 	assert(out && repo && name);
 
@@ -253,13 +318,16 @@
 	GITERR_CHECK_ALLOC(remote);
 
 	memset(remote, 0x0, sizeof(git_remote));
-	remote->check_cert = 1;
 	remote->update_fetchhead = 1;
 	remote->name = git__strdup(name);
 	GITERR_CHECK_ALLOC(remote->name);
 
+	if ((error = get_check_cert(&remote->check_cert, repo)) < 0)
+		goto cleanup;
+
 	if ((git_vector_init(&remote->refs, 32, NULL) < 0) ||
-	    (git_vector_init(&remote->refspecs, 2, NULL))) {
+	    (git_vector_init(&remote->refspecs, 2, NULL) < 0) ||
+	    (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) {
 		error = -1;
 		goto cleanup;
 	}
@@ -269,27 +337,33 @@
 		goto cleanup;
 	}
 
-	if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
+	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
 		goto cleanup;
 
-	if (strlen(val) == 0) {
-		giterr_set(GITERR_INVALID, "Malformed remote '%s' - missing URL", name);
-		error = -1;
-		goto cleanup;
-	}
+	optional_setting_found |= found;
 
 	remote->repo = repo;
-	remote->url = git__strdup(val);
-	GITERR_CHECK_ALLOC(remote->url);
+
+	if (found && strlen(val) > 0) {
+		remote->url = git__strdup(val);
+		GITERR_CHECK_ALLOC(remote->url);
+	}
 
 	val = NULL;
 	git_buf_clear(&buf);
 	git_buf_printf(&buf, "remote.%s.pushurl", name);
 
-	if ((error = get_optional_config(config, &buf, NULL, (void *)&val)) < 0)
+	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
 		goto cleanup;
 
-	if (val) {
+	optional_setting_found |= found;
+
+	if (!optional_setting_found) {
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	if (found && strlen(val) > 0) {
 		remote->pushurl = git__strdup(val);
 		GITERR_CHECK_ALLOC(remote->pushurl);
 	}
@@ -299,19 +373,23 @@
 	git_buf_clear(&buf);
 	git_buf_printf(&buf, "remote.%s.fetch", name);
 
-	if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0)
+	if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
 		goto cleanup;
 
 	data.fetch = false;
 	git_buf_clear(&buf);
 	git_buf_printf(&buf, "remote.%s.push", name);
 
-	if ((error = get_optional_config(config, &buf, refspec_cb, &data)) < 0)
+	if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
 		goto cleanup;
 
 	if (download_tags_value(remote, config) < 0)
 		goto cleanup;
 
+	/* Move the data over to where the matching functions can find them */
+	if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
+		goto cleanup;
+
 	*out = remote;
 
 cleanup:
@@ -326,20 +404,22 @@
 static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
 {
 	git_buf name = GIT_BUF_INIT;
-	int push;
+	unsigned int push;
 	const char *dir;
 	size_t i;
 	int error = 0;
+	const char *cname;
 
 	push = direction == GIT_DIRECTION_PUSH;
 	dir = push ? "push" : "fetch";
 
 	if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
 		return -1;
+	cname = git_buf_cstr(&name);
 
 	/* Clear out the existing config */
 	while (!error)
-		error = git_config_delete_entry(config, git_buf_cstr(&name));
+		error = git_config_delete_multivar(config, cname, ".*");
 
 	if (error != GIT_ENOTFOUND)
 		return error;
@@ -350,8 +430,11 @@
 		if (spec->push != push)
 			continue;
 
+		// "$^" is a unmatcheable regexp: it will not match anything at all, so
+		// all values will be considered new and we will not replace any
+		// present value.
 		if ((error = git_config_set_multivar(
-				config, git_buf_cstr(&name), "", spec->string)) < 0) {
+				config, cname, "$^", spec->string)) < 0) {
 			goto cleanup;
 		}
 	}
@@ -467,6 +550,12 @@
 	return remote->name;
 }
 
+git_repository *git_remote_owner(const git_remote *remote)
+{
+	assert(remote);
+	return remote->repo;
+}
+
 const char *git_remote_url(const git_remote *remote)
 {
 	assert(remote);
@@ -509,6 +598,8 @@
 {
 	assert(remote);
 
+	assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+
 	if (direction == GIT_DIRECTION_FETCH) {
 		return remote->url;
 	}
@@ -525,28 +616,32 @@
 	git_transport *t;
 	const char *url;
 	int flags = GIT_TRANSPORTFLAGS_NONE;
+	int error;
 
 	assert(remote);
 
 	t = remote->transport;
 
 	url = git_remote__urlfordirection(remote, direction);
-	if (url == NULL )
+	if (url == NULL) {
+		giterr_set(GITERR_INVALID,
+			"Malformed remote '%s' - missing URL", remote->name);
 		return -1;
+	}
 
 	/* A transport could have been supplied in advance with
 	 * git_remote_set_transport */
-	if (!t && git_transport_new(&t, remote, url) < 0)
-		return -1;
+	if (!t && (error = git_transport_new(&t, remote, url)) < 0)
+		return error;
 
 	if (t->set_callbacks &&
-		t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload) < 0)
+		(error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0)
 		goto on_error;
 
 	if (!remote->check_cert)
 		flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
 
-	if (t->connect(t, url, remote->cred_acquire_cb, remote->cred_acquire_payload, direction, flags) < 0)
+	if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) < 0)
 		goto on_error;
 
 	remote->transport = t;
@@ -559,20 +654,21 @@
 	if (t == remote->transport)
 		remote->transport = NULL;
 
-	return -1;
+	return error;
 }
 
-int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
+int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
 {
 	assert(remote);
 
-	return remote->transport->ls(remote->transport, list_cb, payload);
+	return remote->transport->ls(out, size, remote->transport);
 }
 
 int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
 {
 	git_config *cfg;
 	const char *val;
+	int error;
 
 	assert(remote);
 
@@ -581,8 +677,8 @@
 
 	*proxy_url = NULL;
 
-	if (git_repository_config__weakptr(&cfg, remote->repo) < 0)
-		return -1;
+	if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
+		return error;
 
 	/* Go through the possible sources for proxy configuration, from most specific
 	 * to least specific. */
@@ -591,28 +687,33 @@
 	if (remote->name && 0 != *(remote->name)) {
 		git_buf buf = GIT_BUF_INIT;
 
-		if (git_buf_printf(&buf, "remote.%s.proxy", remote->name) < 0)
-			return -1;
+		if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
+			return error;
 
-		if (!git_config_get_string(&val, cfg, git_buf_cstr(&buf)) &&
+		if ((error = git_config_get_string(&val, cfg, git_buf_cstr(&buf))) == 0 &&
 			val && ('\0' != *val)) {
 			git_buf_free(&buf);
 
 			*proxy_url = git__strdup(val);
 			GITERR_CHECK_ALLOC(*proxy_url);
 			return 0;
-		}
+		} else if (error != GIT_ENOTFOUND)
+			return error;
 
+		giterr_clear();
 		git_buf_free(&buf);
 	}
 
 	/* http.proxy config setting */
-	if (!git_config_get_string(&val, cfg, "http.proxy") &&
+	if ((error = git_config_get_string(&val, cfg, "http.proxy")) == 0 &&
 		val && ('\0' != *val)) {
 		*proxy_url = git__strdup(val);
 		GITERR_CHECK_ALLOC(*proxy_url);
 		return 0;
-	}
+	} else if (error != GIT_ENOTFOUND)
+		return error;
+
+	giterr_clear();
 
 	/* HTTP_PROXY / HTTPS_PROXY environment variables */
 	val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY");
@@ -626,69 +727,33 @@
 	return 0;
 }
 
-static int store_refs(git_remote_head *head, void *payload)
+/* DWIM `refspecs` based on `refs` and append the output to `out` */
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
 {
-	git_vector *refs = (git_vector *)payload;
-
-	return git_vector_insert(refs, head);
-}
-
-static int dwim_refspecs(git_vector *refspecs, git_vector *refs)
-{
-	git_buf buf = GIT_BUF_INIT;
+	size_t i;
 	git_refspec *spec;
-	size_t i, j, pos;
-	git_remote_head key;
-
-	const char* formatters[] = {
-		GIT_REFS_DIR "%s",
-		GIT_REFS_TAGS_DIR "%s",
-		GIT_REFS_HEADS_DIR "%s",
-		NULL
-	};
 
 	git_vector_foreach(refspecs, i, spec) {
-		if (spec->dwim)
-			continue;
-
-		/* shorthand on the lhs */
-		if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
-			for (j = 0; formatters[j]; j++) {
-				git_buf_clear(&buf);
-				if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
-					return -1;
-
-				key.name = (char *) git_buf_cstr(&buf);
-				if (!git_vector_search(&pos, refs, &key)) {
-					/* we found something to match the shorthand, set src to that */
-					git__free(spec->src);
-					spec->src = git_buf_detach(&buf);
-				}
-			}
-		}
-
-		if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
-			/* if it starts with "remotes" then we just prepend "refs/" */
-			if (!git__prefixcmp(spec->dst, "remotes/")) {
-				git_buf_puts(&buf, GIT_REFS_DIR);
-			} else {
-				git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
-			}
-
-			if (git_buf_puts(&buf, spec->dst) < 0)
-				return -1;
-
-			git__free(spec->dst);
-			spec->dst = git_buf_detach(&buf);
-		}
-
-		spec->dwim = 1;
+		if (git_refspec__dwim_one(out, spec, refs) < 0)
+			return -1;
 	}
 
-	git_buf_free(&buf);
 	return 0;
 }
 
+static void free_refspecs(git_vector *vec)
+{
+	size_t i;
+	git_refspec *spec;
+
+	git_vector_foreach(vec, i, spec) {
+		git_refspec__free(spec);
+		git__free(spec);
+	}
+
+	git_vector_clear(vec);
+}
+
 static int remote_head_cmp(const void *_a, const void *_b)
 {
 	const git_remote_head *a = (git_remote_head *) _a;
@@ -697,32 +762,65 @@
 	return git__strcmp_cb(a->name, b->name);
 }
 
-int git_remote_download(
-		git_remote *remote,
-		git_transfer_progress_callback progress_cb,
-		void *progress_payload)
+static int ls_to_vector(git_vector *out, git_remote *remote)
+{
+	git_remote_head **heads;
+	size_t heads_len, i;
+
+	if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
+		return -1;
+
+	if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
+		return -1;
+
+	for (i = 0; i < heads_len; i++) {
+		if (git_vector_insert(out, heads[i]) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int git_remote_download(git_remote *remote)
 {
 	int error;
 	git_vector refs;
 
 	assert(remote);
 
-	if (git_vector_init(&refs, 16, remote_head_cmp) < 0)
+	if (ls_to_vector(&refs, remote) < 0)
 		return -1;
 
-	if (git_remote_ls(remote, store_refs, &refs) < 0) {
-		return -1;
-	}
+	free_refspecs(&remote->active_refspecs);
 
-	error = dwim_refspecs(&remote->refspecs, &refs);
+	error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
 	git_vector_free(&refs);
+
 	if (error < 0)
 		return -1;
 
 	if ((error = git_fetch_negotiate(remote)) < 0)
 		return error;
 
-	return git_fetch_download_pack(remote, progress_cb, progress_payload);
+	return git_fetch_download_pack(remote);
+}
+
+int git_remote_fetch(git_remote *remote)
+{
+	int error;
+
+	/* Connect and download everything */
+	if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0)
+		return error;
+
+	if ((error = git_remote_download(remote)) < 0)
+		return error;
+
+	/* We don't need to be connected anymore */
+	git_remote_disconnect(remote);
+
+	/* Create "remote/foo" branches for all remote branches */
+	return git_remote_update_tips(remote);
 }
 
 static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
@@ -759,7 +857,7 @@
 		(!git_reference_is_branch(resolved_ref)) ||
 		(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
 		(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
-		/* Not an error if HEAD is orphaned or no tracking branch */
+		/* Not an error if HEAD is unborn or no tracking branch */
 		if (error == GIT_ENOTFOUND)
 			error = 0;
 
@@ -947,10 +1045,8 @@
 	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
 		return -1;
 
-	if (git_vector_init(&refs, 16, NULL) < 0)
-		return -1;
 
-	if ((error = git_remote_ls(remote, store_refs, &refs)) < 0)
+	if ((error = ls_to_vector(&refs, remote)) < 0)
 		goto out;
 
 	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
@@ -958,7 +1054,7 @@
 		goto out;
 	}
 
-	git_vector_foreach(&remote->refspecs, i, spec) {
+	git_vector_foreach(&remote->active_refspecs, i, spec) {
 		if (spec->push)
 			continue;
 
@@ -967,8 +1063,8 @@
 	}
 
 out:
-	git_refspec__free(&tagspec);
 	git_vector_free(&refs);
+	git_refspec__free(&tagspec);
 	return error;
 }
 
@@ -1001,9 +1097,6 @@
 
 void git_remote_free(git_remote *remote)
 {
-	git_refspec *spec;
-	size_t i;
-
 	if (remote == NULL)
 		return;
 
@@ -1016,67 +1109,55 @@
 
 	git_vector_free(&remote->refs);
 
-	git_vector_foreach(&remote->refspecs, i, spec) {
-		git_refspec__free(spec);
-		git__free(spec);
-	}
+	free_refspecs(&remote->refspecs);
 	git_vector_free(&remote->refspecs);
 
+	free_refspecs(&remote->active_refspecs);
+	git_vector_free(&remote->active_refspecs);
+
 	git__free(remote->url);
 	git__free(remote->pushurl);
 	git__free(remote->name);
 	git__free(remote);
 }
 
-struct cb_data {
-	git_vector *list;
-	regex_t *preg;
-};
-
-static int remote_list_cb(const git_config_entry *entry, void *data_)
+static int remote_list_cb(const git_config_entry *entry, void *payload)
 {
-	struct cb_data *data = (struct cb_data *)data_;
-	size_t nmatch = 2;
-	regmatch_t pmatch[2];
-	const char *name = entry->name;
+	git_vector *list = payload;
+	const char *name = entry->name + strlen("remote.");
+	size_t namelen = strlen(name);
+	char *remote_name;
 
-	if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
-		char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
-		GITERR_CHECK_ALLOC(remote_name);
+	/* we know name matches "remote.<stuff>.(push)?url" */
 
-		if (git_vector_insert(data->list, remote_name) < 0)
-			return -1;
-	}
+	if (!strcmp(&name[namelen - 4], ".url"))
+		remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
+	else
+		remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
+	GITERR_CHECK_ALLOC(remote_name);
 
-	return 0;
+	return git_vector_insert(list, remote_name);
 }
 
 int git_remote_list(git_strarray *remotes_list, git_repository *repo)
 {
 	git_config *cfg;
 	git_vector list;
-	regex_t preg;
-	struct cb_data data;
 	int error;
 
 	if (git_repository_config__weakptr(&cfg, repo) < 0)
 		return -1;
 
-	if (git_vector_init(&list, 4, NULL) < 0)
+	if (git_vector_init(&list, 4, git__strcmp_cb) < 0)
 		return -1;
 
-	if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
-		giterr_set(GITERR_OS, "Remote catch regex failed to compile");
-		return -1;
-	}
+	error = git_config_foreach_match(
+		cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
 
-	data.list = &list;
-	data.preg = &preg;
-	error = git_config_foreach(cfg, remote_list_cb, &data);
-	regfree(&preg);
 	if (error < 0) {
 		size_t i;
 		char *elem;
+
 		git_vector_foreach(&list, i, elem) {
 			git__free(elem);
 		}
@@ -1090,6 +1171,8 @@
 		return error;
 	}
 
+	git_vector_uniq(&list, git__free);
+
 	remotes_list->strings = (char **)list.contents;
 	remotes_list->count = list.length;
 
@@ -1103,7 +1186,7 @@
 	remote->check_cert = check;
 }
 
-int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
+int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
 {
 	assert(remote && callbacks);
 
@@ -1112,7 +1195,7 @@
 	memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
 
 	if (remote->transport && remote->transport->set_callbacks)
-		remote->transport->set_callbacks(remote->transport,
+		return remote->transport->set_callbacks(remote->transport,
 			remote->callbacks.progress,
 			NULL,
 			remote->callbacks.payload);
@@ -1120,17 +1203,6 @@
 	return 0;
 }
 
-void git_remote_set_cred_acquire_cb(
-	git_remote *remote,
-	git_cred_acquire_cb cred_acquire_cb,
-	void *payload)
-{
-	assert(remote);
-
-	remote->cred_acquire_cb = cred_acquire_cb;
-	remote->cred_acquire_payload = payload;
-}
-
 int git_remote_set_transport(git_remote *remote, git_transport *transport)
 {
 	assert(remote && transport);
@@ -1418,12 +1490,12 @@
 
 int git_remote_update_fetchhead(git_remote *remote)
 {
-	return remote->update_fetchhead;
+	return (remote->update_fetchhead != 0);
 }
 
 void git_remote_set_update_fetchhead(git_remote *remote, int value)
 {
-	remote->update_fetchhead = value;
+	remote->update_fetchhead = (value != 0);
 }
 
 int git_remote_is_valid_name(
@@ -1451,7 +1523,7 @@
 	git_refspec *spec;
 	size_t i;
 
-	git_vector_foreach(&remote->refspecs, i, spec) {
+	git_vector_foreach(&remote->active_refspecs, i, spec) {
 		if (spec->push)
 			continue;
 
@@ -1467,7 +1539,7 @@
 	git_refspec *spec;
 	size_t i;
 
-	git_vector_foreach(&remote->refspecs, i, spec) {
+	git_vector_foreach(&remote->active_refspecs, i, spec) {
 		if (spec->push)
 			continue;
 
@@ -1490,17 +1562,71 @@
 	git_vector_clear(&remote->refspecs);
 }
 
+static int add_and_dwim(git_remote *remote, const char *str, int push)
+{
+	git_refspec *spec;
+	git_vector *vec;
+
+	if (add_refspec(remote, str, !push) < 0)
+		return -1;
+
+	vec = &remote->refspecs;
+	spec = git_vector_get(vec, vec->length - 1);
+	return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
+}
+
 int git_remote_add_fetch(git_remote *remote, const char *refspec)
 {
-	return add_refspec(remote, refspec, true);
+	return add_and_dwim(remote, refspec, false);
 }
 
 int git_remote_add_push(git_remote *remote, const char *refspec)
 {
-	return add_refspec(remote, refspec, false);
+	return add_and_dwim(remote, refspec, true);
 }
 
-static int copy_refspecs(git_strarray *array, git_remote *remote, int push)
+static int set_refspecs(git_remote *remote, git_strarray *array, int push)
+{
+	git_vector *vec = &remote->refspecs;
+	git_refspec *spec;
+	size_t i;
+
+	/* Start by removing any refspecs of the same type */
+	for (i = 0; i < vec->length; i++) {
+		spec = git_vector_get(vec, i);
+		if (spec->push != push)
+			continue;
+
+		git_refspec__free(spec);
+		git__free(spec);
+		git_vector_remove(vec, i);
+		i--;
+	}
+
+	/* And now we add the new ones */
+
+	for (i = 0; i < array->count; i++) {
+		if (add_refspec(remote, array->strings[i], !push) < 0)
+			return -1;
+	}
+
+	free_refspecs(&remote->active_refspecs);
+	git_vector_clear(&remote->active_refspecs);
+
+	return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
+}
+
+int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
+{
+	return set_refspecs(remote, array, false);
+}
+
+int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array)
+{
+	return set_refspecs(remote, array, true);
+}
+
+static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
 {
 	size_t i;
 	git_vector refspecs;
@@ -1555,18 +1681,3 @@
 {
 	return git_vector_get(&remote->refspecs, n);
 }
-
-int git_remote_remove_refspec(git_remote *remote, size_t n)
-{
-	git_refspec *spec;
-
-	assert(remote);
-
-	spec = git_vector_get(&remote->refspecs, n);
-	if (spec) {
-		git_refspec__free(spec);
-		git__free(spec);
-	}
-
-	return git_vector_remove(&remote->refspecs, n);
-}
diff --git a/src/remote.h b/src/remote.h
index dce4803..4164a14 100644
--- a/src/remote.h
+++ b/src/remote.h
@@ -21,16 +21,15 @@
 	char *pushurl;
 	git_vector refs;
 	git_vector refspecs;
-	git_cred_acquire_cb cred_acquire_cb;
-	void *cred_acquire_payload;
+	git_vector active_refspecs;
 	git_transport *transport;
 	git_repository *repo;
 	git_remote_callbacks callbacks;
 	git_transfer_progress stats;
 	unsigned int need_pack;
 	git_remote_autotag_option_t download_tags;
-	unsigned int check_cert;
-	unsigned int update_fetchhead;
+	int check_cert;
+	int update_fetchhead;
 };
 
 const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
diff --git a/src/repository.c b/src/repository.c
index ed9469c..dcc02e4 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -33,8 +33,6 @@
 
 #define GIT_REPO_VERSION 0
 
-#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
-
 static void set_odb(git_repository *repo, git_odb *odb)
 {
 	if (odb) {
@@ -266,7 +264,7 @@
 			buf[--len] = '\0';
 
 		if (!strncmp(path, buf2, len) &&
-			path[len] == '/' &&
+			(path[len] == '/' || !path[len]) &&
 			len > max_len)
 		{
 			max_len = len;
@@ -322,17 +320,18 @@
 	git_buf path = GIT_BUF_INIT;
 	struct stat st;
 	dev_t initial_device = 0;
-	bool try_with_dot_git = false;
+	bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
 	int ceiling_offset;
 
 	git_buf_free(repo_path);
 
-	if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
+	if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
 		return error;
 
 	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
 
-	if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+	if (!try_with_dot_git &&
+		(error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
 		return error;
 
 	while (!error && !git_buf_len(repo_path)) {
@@ -384,7 +383,7 @@
 		try_with_dot_git = !try_with_dot_git;
 	}
 
-	if (!error && parent_path != NULL) {
+	if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
 		if (!git_buf_len(repo_path))
 			git_buf_clear(parent_path);
 		else {
@@ -460,7 +459,9 @@
 	repo->path_repository = git_buf_detach(&path);
 	GITERR_CHECK_ALLOC(repo->path_repository);
 
-	if ((error = load_config_data(repo)) < 0 ||
+	if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
+		repo->is_bare = 1;
+	else if ((error = load_config_data(repo)) < 0 ||
 		(error = load_workdir(repo, &parent)) < 0)
 	{
 		git_repository_free(repo);
@@ -815,7 +816,7 @@
 	const char *fmt;
 
 	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
-		git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
+		git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE) < 0)
 		goto fail;
 
 	if (!ref_name)
@@ -827,7 +828,7 @@
 		fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
 
 	if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
-		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
+		git_filebuf_commit(&ref) < 0)
 		goto fail;
 
 	git_buf_free(&ref_path);
@@ -842,10 +843,6 @@
 static bool is_chmod_supported(const char *file_path)
 {
 	struct stat st1, st2;
-	static int _is_supported = -1;
-
-	if (_is_supported > -1)
-		return _is_supported;
 
 	if (p_stat(file_path, &st1) < 0)
 		return false;
@@ -856,27 +853,19 @@
 	if (p_stat(file_path, &st2) < 0)
 		return false;
 
-	_is_supported = (st1.st_mode != st2.st_mode);
-
-	return _is_supported;
+	return (st1.st_mode != st2.st_mode);
 }
 
 static bool is_filesystem_case_insensitive(const char *gitdir_path)
 {
 	git_buf path = GIT_BUF_INIT;
-	static int _is_insensitive = -1;
+	int is_insensitive = -1;
 
-	if (_is_insensitive > -1)
-		return _is_insensitive;
+	if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
+		is_insensitive = git_path_exists(git_buf_cstr(&path));
 
-	if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
-		goto cleanup;
-
-	_is_insensitive = git_path_exists(git_buf_cstr(&path));
-
-cleanup:
 	git_buf_free(&path);
-	return _is_insensitive;
+	return is_insensitive;
 }
 
 static bool are_symlinks_supported(const char *wd_path)
@@ -884,26 +873,77 @@
 	git_buf path = GIT_BUF_INIT;
 	int fd;
 	struct stat st;
-	static int _symlinks_supported = -1;
+	int symlinks_supported = -1;
 
-	if (_symlinks_supported > -1)
-		return _symlinks_supported;
-
-	if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
+	if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 ||
 		p_close(fd) < 0 ||
 		p_unlink(path.ptr) < 0 ||
 		p_symlink("testing", path.ptr) < 0 ||
 		p_lstat(path.ptr, &st) < 0)
-		_symlinks_supported = false;
+		symlinks_supported = false;
 	else
-		_symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+		symlinks_supported = (S_ISLNK(st.st_mode) != 0);
 
 	(void)p_unlink(path.ptr);
 	git_buf_free(&path);
 
-	return _symlinks_supported;
+	return symlinks_supported;
 }
 
+#ifdef GIT_USE_ICONV
+
+static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
+static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
+
+/* Check if the platform is decomposing unicode data for us.  We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode.  Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
+ */
+static bool does_fs_decompose_unicode_paths(const char *wd_path)
+{
+	git_buf path = GIT_BUF_INIT;
+	int fd;
+	bool found_decomposed = false;
+	char tmp[6];
+
+	/* Create a file using a precomposed path and then try to find it
+	 * using the decomposed name.  If the lookup fails, then we will mark
+	 * that we should precompose unicode for this repository.
+	 */
+	if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
+		(fd = p_mkstemp(path.ptr)) < 0)
+		goto done;
+	p_close(fd);
+
+	/* record trailing digits generated by mkstemp */
+	memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
+
+	/* try to look up as NFD path */
+	if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
+		goto done;
+	memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+	found_decomposed = git_path_exists(path.ptr);
+
+	/* remove temporary file (using original precomposed path) */
+	if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
+		goto done;
+	memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
+
+	(void)p_unlink(path.ptr);
+
+done:
+	git_buf_free(&path);
+	return found_decomposed;
+}
+
+#endif
+
 static int create_empty_file(const char *path, mode_t mode)
 {
 	int fd;
@@ -921,71 +961,131 @@
 	return 0;
 }
 
+static int repo_local_config(
+	git_config **out,
+	git_buf *config_dir,
+	git_repository *repo,
+	const char *repo_dir)
+{
+	int error = 0;
+	git_config *parent;
+	const char *cfg_path;
+
+	if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+		return -1;
+	cfg_path = git_buf_cstr(config_dir);
+
+	/* make LOCAL config if missing */
+	if (!git_path_isfile(cfg_path) &&
+		(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
+		return error;
+
+	/* if no repo, just open that file directly */
+	if (!repo)
+		return git_config_open_ondisk(out, cfg_path);
+
+	/* otherwise, open parent config and get that level */
+	if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
+		return error;
+
+	if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+		giterr_clear();
+
+		if (!(error = git_config_add_file_ondisk(
+				parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
+			error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
+	}
+
+	git_config_free(parent);
+
+	return error;
+}
+
+static int repo_init_fs_configs(
+	git_config *cfg,
+	const char *cfg_path,
+	const char *repo_dir,
+	const char *work_dir,
+	bool update_ignorecase)
+{
+	int error = 0;
+
+	if (!work_dir)
+		work_dir = repo_dir;
+
+	if ((error = git_config_set_bool(
+			cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
+		return error;
+
+	if (!are_symlinks_supported(work_dir)) {
+		if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
+			return error;
+	} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
+		giterr_clear();
+
+	if (update_ignorecase) {
+		if (is_filesystem_case_insensitive(repo_dir)) {
+			if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
+				return error;
+		} else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
+			giterr_clear();
+	}
+
+#ifdef GIT_USE_ICONV
+	if ((error = git_config_set_bool(
+			cfg, "core.precomposeunicode",
+			does_fs_decompose_unicode_paths(work_dir))) < 0)
+		return error;
+#endif
+
+	return 0;
+}
+
 static int repo_init_config(
 	const char *repo_dir,
 	const char *work_dir,
-	git_repository_init_options *opts)
+	uint32_t flags,
+	uint32_t mode)
 {
 	int error = 0;
 	git_buf cfg_path = GIT_BUF_INIT;
 	git_config *config = NULL;
+	bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
+	bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
 
-#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
+	if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
+		goto cleanup;
+
+	if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
+		goto cleanup;
+
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
 	if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
 		goto cleanup; } while (0)
 
-	if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
-		return -1;
+	SET_REPO_CONFIG(bool, "core.bare", is_bare);
+	SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
 
-	if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
-		create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
-			git_buf_free(&cfg_path);
-			return -1;
-	}
-
-	if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
-		git_buf_free(&cfg_path);
-		return -1;
-	}
-
-	if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
-		(error = check_repositoryformatversion(config)) < 0)
+	if ((error = repo_init_fs_configs(
+			config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
 		goto cleanup;
 
-	SET_REPO_CONFIG(
-		bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
-	SET_REPO_CONFIG(
-		int32, "core.repositoryformatversion", GIT_REPO_VERSION);
-	SET_REPO_CONFIG(
-		bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
-
-	if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
+	if (!is_bare) {
 		SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
 
-		if (!are_symlinks_supported(work_dir))
-			SET_REPO_CONFIG(bool, "core.symlinks", false);
-
-		if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+		if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
 			SET_REPO_CONFIG(string, "core.worktree", work_dir);
-		}
-		else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+		else if (is_reinit) {
 			if (git_config_delete_entry(config, "core.worktree") < 0)
 				giterr_clear();
 		}
-	} else {
-		if (!are_symlinks_supported(repo_dir))
-			SET_REPO_CONFIG(bool, "core.symlinks", false);
 	}
 
-	if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
-		is_filesystem_case_insensitive(repo_dir))
-		SET_REPO_CONFIG(bool, "core.ignorecase", true);
-
-	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+	if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
 		SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
 		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
 	}
-	else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+	else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
 		SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
 		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
 	}
@@ -997,6 +1097,41 @@
 	return error;
 }
 
+static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
+{
+	git_repository *smrepo = NULL;
+	GIT_UNUSED(n); GIT_UNUSED(p);
+
+	if (git_submodule_open(&smrepo, sm) < 0 ||
+		git_repository_reinit_filesystem(smrepo, true) < 0)
+		giterr_clear();
+	git_repository_free(smrepo);
+
+	return 0;
+}
+
+int git_repository_reinit_filesystem(git_repository *repo, int recurse)
+{
+	int error = 0;
+	git_buf path = GIT_BUF_INIT;
+	git_config *config = NULL;
+	const char *repo_dir = git_repository_path(repo);
+
+	if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
+		error = repo_init_fs_configs(
+			config, path.ptr, repo_dir, git_repository_workdir(repo), true);
+
+	git_config_free(config);
+	git_buf_free(&path);
+
+	git_repository__cvar_cache_clear(repo);
+
+	if (!repo->is_bare && recurse)
+		(void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
+
+	return error;
+}
+
 static int repo_write_template(
 	const char *git_dir,
 	bool allow_overwrite,
@@ -1131,31 +1266,34 @@
 
 	/* Copy external template if requested */
 	if (external_tpl) {
-		git_config *cfg;
-		const char *tdir;
+		git_config *cfg = NULL;
+		const char *tdir = NULL;
+		bool default_template = false;
+		git_buf template_buf = GIT_BUF_INIT;
 
 		if (opts->template_path)
 			tdir = opts->template_path;
-		else if ((error = git_config_open_default(&cfg)) < 0)
-			return error;
-		else {
+		else if ((error = git_config_open_default(&cfg)) >= 0) {
 			error = git_config_get_string(&tdir, cfg, "init.templatedir");
-
-			git_config_free(cfg);
-
-			if (error && error != GIT_ENOTFOUND)
-				return error;
-
 			giterr_clear();
-			tdir = GIT_TEMPLATE_DIR;
 		}
 
-		error = git_futils_cp_r(tdir, repo_dir,
-			GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
-			GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+		if (!tdir) {
+			if (!(error = git_futils_find_template_dir(&template_buf)))
+				tdir = template_buf.ptr;
+			default_template = true;
+		}
+
+		if (tdir)
+			error = git_futils_cp_r(tdir, repo_dir,
+				GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
+				GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+
+		git_buf_free(&template_buf);
+		git_config_free(cfg);
 
 		if (error < 0) {
-			if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+			if (!default_template)
 				return error;
 
 			/* if template was default, ignore error and use internal */
@@ -1382,22 +1520,22 @@
 		opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
 
 		error = repo_init_config(
-			git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
+			repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
 
 		/* TODO: reinitialize the templates */
 	}
 	else {
 		if (!(error = repo_init_structure(
-				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
+				repo_path.ptr, wd_path.ptr, opts)) &&
 			!(error = repo_init_config(
-				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
+				repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
 			error = repo_init_create_head(
-				git_buf_cstr(&repo_path), opts->initial_head);
+				repo_path.ptr, opts->initial_head);
 	}
 	if (error < 0)
 		goto cleanup;
 
-	error = git_repository_open(out, git_buf_cstr(&repo_path));
+	error = git_repository_open(out, repo_path.ptr);
 
 	if (!error && opts->origin_url)
 		error = repo_init_create_origin(*out, opts->origin_url);
@@ -1448,10 +1586,10 @@
 	error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
 	git_reference_free(head);
 
-	return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
+	return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
 }
 
-int git_repository_head_orphan(git_repository *repo)
+int git_repository_head_unborn(git_repository *repo)
 {
 	git_reference *ref = NULL;
 	int error;
@@ -1459,7 +1597,7 @@
 	error = git_repository_head(&ref, repo);
 	git_reference_free(ref);
 
-	if (error == GIT_EORPHANEDHEAD)
+	if (error == GIT_EUNBORNBRANCH)
 		return 1;
 
 	if (error < 0)
@@ -1492,24 +1630,20 @@
 int git_repository_is_empty(git_repository *repo)
 {
 	git_reference *head = NULL;
-	int error;
+	int is_empty = 0;
 
 	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
 		return -1;
 
-	if (!(error = git_reference_type(head) == GIT_REF_SYMBOLIC))
-		goto cleanup;
+	if (git_reference_type(head) == GIT_REF_SYMBOLIC)
+		is_empty =
+			(strcmp(git_reference_symbolic_target(head),
+					GIT_REFS_HEADS_DIR "master") == 0) &&
+			repo_contains_no_reference(repo);
 
-	if (!(error = strcmp(
-		git_reference_symbolic_target(head),
-		GIT_REFS_HEADS_DIR "master") == 0))
-			goto cleanup;
-
-	error = repo_contains_no_reference(repo);
-
-cleanup:
 	git_reference_free(head);
-	return error < 0 ? -1 : error;
+
+	return is_empty;
 }
 
 const char *git_repository_path(git_repository *repo)
@@ -1650,7 +1784,7 @@
 	const char *as_path)
 {
 	int error;
-	git_vector filters = GIT_VECTOR_INIT;
+	git_filter_list *fl = NULL;
 	git_file fd = -1;
 	git_off_t len;
 	git_buf full_path = GIT_BUF_INIT;
@@ -1672,7 +1806,8 @@
 
 	/* passing empty string for "as_path" indicated --no-filters */
 	if (strlen(as_path) > 0) {
-		error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
+		error = git_filter_list_load(
+			&fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
 		if (error < 0)
 			return error;
 	} else {
@@ -1699,12 +1834,12 @@
 		goto cleanup;
 	}
 
-	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
+	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
 
 cleanup:
 	if (fd >= 0)
 		p_close(fd);
-	git_filters_free(&filters);
+	git_filter_list_free(fl);
 	git_buf_free(&full_path);
 
 	return error;
diff --git a/src/repository.h b/src/repository.h
index 12dc50d..832df3b 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -37,6 +37,7 @@
 	GIT_CVAR_IGNORESTAT,    /* core.ignorestat */
 	GIT_CVAR_TRUSTCTIME,    /* core.trustctime */
 	GIT_CVAR_ABBREV,        /* core.abbrev */
+	GIT_CVAR_PRECOMPOSE,    /* core.precomposeunicode */
 	GIT_CVAR_CACHE_MAX
 } git_cvar_cached;
 
@@ -86,6 +87,8 @@
 	GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
 	/* core.abbrev */
 	GIT_ABBREV_DEFAULT = 7,
+	/* core.precomposeunicode */
+	GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
 
 } git_cvar_value;
 
diff --git a/src/reset.c b/src/reset.c
index cea212a..a9780bf 100644
--- a/src/reset.c
+++ b/src/reset.c
@@ -24,10 +24,9 @@
 {
 	git_object *commit = NULL;
 	git_tree *tree = NULL;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	size_t i;
-	git_diff_delta *delta;
+	size_t i, max_i;
 	git_index_entry entry;
 	int error;
 	git_index *index = NULL;
@@ -58,7 +57,9 @@
 		&diff, repo, tree, index, &opts)) < 0)
 			goto cleanup;
 
-	git_vector_foreach(&diff->deltas, i, delta) {
+	for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
+		const git_diff_delta *delta = git_diff_get_delta(diff, i);
+
 		if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
 			goto cleanup;
 
@@ -85,7 +86,7 @@
 	git_object_free(commit);
 	git_tree_free(tree);
 	git_index_free(index);
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	return error;
 }
@@ -135,7 +136,7 @@
 
 	if (reset_type == GIT_RESET_HARD) {
 		/* overwrite working directory with HEAD */
-		opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+		opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
 
 		if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
 			goto cleanup;
diff --git a/src/revparse.c b/src/revparse.c
index bcfb084..c120b46 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -93,11 +93,7 @@
 	int error;
 	git_reference *ref;
 
-	error = maybe_sha(object_out, repo, spec);
-	if (!error)
-		return 0;
-
-	if (error < 0 && error != GIT_ENOTFOUND)
+	if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND)
 		return error;
 
 	error = git_reference_dwim(&ref, repo, spec);
@@ -112,24 +108,17 @@
 		return error;
 	}
 
-	if (error < 0 && error != GIT_ENOTFOUND)
+	if (error != GIT_ENOTFOUND)
 		return error;
 
-	error = maybe_abbrev(object_out, repo, spec);
-	if (!error)
-		return 0;
+	if ((strlen(spec) < GIT_OID_HEXSZ) &&
+		((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
+			return error;
 
-	if (error < 0 && error != GIT_ENOTFOUND)
+	if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
 		return error;
 
-	error = maybe_describe(object_out, repo, spec);
-	if (!error)
-		return 0;
-
-	if (error < 0 && error != GIT_ENOTFOUND)
-		return error;
-
-	giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
+	giterr_set(GITERR_REFERENCE, "Revspec '%s' not found.", spec);
 	return GIT_ENOTFOUND;
 }
 
@@ -171,7 +160,7 @@
 	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
 		goto cleanup;
 
-	if (git_reflog_read(&reflog, ref) < 0)
+	if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
 		goto cleanup;
 
 	numentries  = git_reflog_entrycount(reflog);
@@ -219,7 +208,7 @@
 	const git_reflog_entry *entry;
 	bool search_by_pos = (identifier <= 100000000);
 
-	if (git_reflog_read(&reflog, ref) < 0)
+	if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
 		return -1;
 
 	numentries = git_reflog_entrycount(reflog);
@@ -228,7 +217,7 @@
 		if (numentries < identifier + 1) {
 			giterr_set(
 				GITERR_REFERENCE,
-				"Reflog for '%s' has only "PRIuZ" entries, asked for "PRIuZ,
+				"Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
 				git_reference_name(ref), numentries, identifier);
 
 			error = GIT_ENOTFOUND;
@@ -685,6 +674,8 @@
 	git_reference *reference = NULL;
 	git_object *base_rev = NULL;
 
+	bool should_return_reference = true;
+
 	assert(object_out && reference_out && repo && spec);
 
 	*object_out = NULL;
@@ -693,6 +684,8 @@
 	while (spec[pos]) {
 		switch (spec[pos]) {
 		case '^':
+			should_return_reference = false;
+
 			if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
 				goto cleanup;
 
@@ -725,6 +718,8 @@
 		{
 			git_object *temp_object = NULL;
 
+			should_return_reference = false;
+
 			if ((error = extract_how_many(&n, spec, &pos)) < 0)
 				goto cleanup;
 
@@ -743,6 +738,8 @@
 		{
 			git_object *temp_object = NULL;
 
+			should_return_reference = false;
+
 			if ((error = extract_path(&buf, spec, &pos)) < 0)
 				goto cleanup;
 
@@ -807,6 +804,11 @@
 	if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
 		goto cleanup;
 
+	if (!should_return_reference) {
+		git_reference_free(reference);
+		reference = NULL;
+	}
+
 	*object_out = base_rev;
 	*reference_out = reference;
 	*identifier_len_out = identifier_len;
@@ -899,13 +901,9 @@
 			rstr++;
 		}
 
-		if ((error = git_revparse_single(&revspec->from, repo, lstr)) < 0) {
-			return error;
-		}
-
-		if ((error = git_revparse_single(&revspec->to, repo, rstr)) < 0) {
-			return error;
-		}
+		error = git_revparse_single(&revspec->from, repo, lstr);
+		if (!error)
+			error = git_revparse_single(&revspec->to, repo, rstr);
 
 		git__free((void*)lstr);
 	} else {
diff --git a/src/revwalk.c b/src/revwalk.c
index 528d02b..3dd14b4 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -14,8 +14,6 @@
 #include "git2/revparse.h"
 #include "merge.h"
 
-#include <regex.h>
-
 git_commit_list_node *git_revwalk__commit_lookup(
 	git_revwalk *walk, const git_oid *oid)
 {
@@ -41,28 +39,50 @@
 	return commit;
 }
 
-static void mark_uninteresting(git_commit_list_node *commit)
+static int mark_uninteresting(git_commit_list_node *commit)
 {
 	unsigned short i;
+	git_array_t(git_commit_list_node *) pending = GIT_ARRAY_INIT;
+	git_commit_list_node **tmp;
+
 	assert(commit);
 
-	commit->uninteresting = 1;
+	git_array_alloc(pending);
+	GITERR_CHECK_ARRAY(pending);
 
-	/* This means we've reached a merge base, so there's no need to walk any more */
-	if ((commit->flags & (RESULT | STALE)) == RESULT)
-		return;
+	do {
+		commit->uninteresting = 1;
 
-	for (i = 0; i < commit->out_degree; ++i)
-		if (!commit->parents[i]->uninteresting)
-			mark_uninteresting(commit->parents[i]);
+		/* This means we've reached a merge base, so there's no need to walk any more */
+		if ((commit->flags & (RESULT | STALE)) == RESULT) {
+			tmp = git_array_pop(pending);
+			commit = tmp ? *tmp : NULL;
+			continue;
+		}
+
+		for (i = 0; i < commit->out_degree; ++i)
+			if (!commit->parents[i]->uninteresting) {
+				git_commit_list_node **node = git_array_alloc(pending);
+				GITERR_CHECK_ALLOC(node);
+				*node = commit->parents[i];
+			}
+
+		tmp = git_array_pop(pending);
+		commit = tmp ? *tmp : NULL;
+
+	} while (git_array_size(pending) > 0);
+
+	git_array_clear(pending);
+
+	return 0;
 }
 
 static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int hide)
 {
 	int error;
 
-	if (hide)
-		mark_uninteresting(commit);
+	if (hide && mark_uninteresting(commit) < 0)
+		return -1;
 
 	if (commit->seen)
 		return 0;
@@ -77,10 +97,14 @@
 
 static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit)
 {
-	unsigned short i;
+	unsigned short i, max;
 	int error = 0;
 
-	for (i = 0; i < commit->out_degree && !error; ++i)
+	max = commit->out_degree;
+	if (walk->first_parent && commit->out_degree)
+		max = 1;
+
+	for (i = 0; i < max && !error; ++i)
 		error = process_commit(walk, commit->parents[i], commit->uninteresting);
 
 	return error;
@@ -155,48 +179,35 @@
 
 static int push_glob(git_revwalk *walk, const char *glob, int hide)
 {
+	int error = 0;
 	git_buf buf = GIT_BUF_INIT;
 	struct push_cb_data data;
-	regex_t preg;
+	size_t wildcard;
 
 	assert(walk && glob);
 
 	/* refs/ is implied if not given in the glob */
-	if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {
-		git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);
-	} else {
+	if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
+		git_buf_joinpath(&buf, GIT_REFS_DIR, glob);
+	else
 		git_buf_puts(&buf, glob);
-	}
 
 	/* If no '?', '*' or '[' exist, we append '/ *' to the glob */
-	memset(&preg, 0x0, sizeof(regex_t));
-	if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
-		giterr_set(GITERR_OS, "Regex failed to compile");
-		git_buf_free(&buf);
-		return -1;
-	}
-
-	if (regexec(&preg, glob, 0, NULL, 0))
-		git_buf_puts(&buf, "/*");
-
-	if (git_buf_oom(&buf))
-		goto on_error;
+	wildcard = strcspn(glob, "?*[");
+	if (!glob[wildcard])
+		git_buf_put(&buf, "/*", 2);
 
 	data.walk = walk;
 	data.hide = hide;
 
-	if (git_reference_foreach_glob(
-		walk->repo, git_buf_cstr(&buf), push_glob_cb, &data) < 0)
-		goto on_error;
+	if (git_buf_oom(&buf))
+		error = -1;
+	else
+		error = git_reference_foreach_glob(
+			walk->repo, git_buf_cstr(&buf), push_glob_cb, &data);
 
-	regfree(&preg);
 	git_buf_free(&buf);
-	return 0;
-
-on_error:
-	regfree(&preg);
-	git_buf_free(&buf);
-	return -1;
+	return error;
 }
 
 int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
@@ -311,7 +322,7 @@
 static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
 {
 	git_commit_list_node *next;
-	unsigned short i;
+	unsigned short i, max;
 
 	for (;;) {
 		next = git_commit_list_pop(&walk->iterator_topo);
@@ -325,7 +336,12 @@
 			continue;
 		}
 
-		for (i = 0; i < next->out_degree; ++i) {
+
+		max = next->out_degree;
+		if (walk->first_parent && next->out_degree)
+			max = 1;
+
+		for (i = 0; i < max; ++i) {
 			git_commit_list_node *parent = next->parents[i];
 
 			if (--parent->in_degree == 0 && parent->topo_delay) {
@@ -483,6 +499,11 @@
 	}
 }
 
+void git_revwalk_simplify_first_parent(git_revwalk *walk)
+{
+	walk->first_parent = 1;
+}
+
 int git_revwalk_next(git_oid *oid, git_revwalk *walk)
 {
 	int error;
diff --git a/src/revwalk.h b/src/revwalk.h
index 22696df..8c821d0 100644
--- a/src/revwalk.h
+++ b/src/revwalk.h
@@ -31,7 +31,8 @@
 	int (*get_next)(git_commit_list_node **, git_revwalk *);
 	int (*enqueue)(git_revwalk *, git_commit_list_node *);
 
-	unsigned walking:1;
+	unsigned walking:1,
+		first_parent: 1;
 	unsigned int sorting;
 
 	/* merge base calculation */
diff --git a/src/sha1_lookup.c b/src/sha1_lookup.c
index b7e66cc..c6b5613 100644
--- a/src/sha1_lookup.c
+++ b/src/sha1_lookup.c
@@ -9,6 +9,7 @@
 
 #include "sha1_lookup.h"
 #include "common.h"
+#include "oid.h"
 
 /*
  * Conventional binary search loop looks like this:
@@ -108,7 +109,54 @@
 			 * byte 0 thru (ofs-1) are the same between
 			 * lo and hi; ofs is the first byte that is
 			 * different.
+			 *
+			 * If ofs==20, then no bytes are different,
+			 * meaning we have entries with duplicate
+			 * keys. We know that we are in a solid run
+			 * of this entry (because the entries are
+			 * sorted, and our lo and hi are the same,
+			 * there can be nothing but this single key
+			 * in between). So we can stop the search.
+			 * Either one of these entries is it (and
+			 * we do not care which), or we do not have
+			 * it.
+			 *
+			 * Furthermore, we know that one of our
+			 * endpoints must be the edge of the run of
+			 * duplicates. For example, given this
+			 * sequence:
+			 *
+			 *     idx 0 1 2 3 4 5
+			 *     key A C C C C D
+			 *
+			 * If we are searching for "B", we might
+			 * hit the duplicate run at lo=1, hi=3
+			 * (e.g., by first mi=3, then mi=0). But we
+			 * can never have lo > 1, because B < C.
+			 * That is, if our key is less than the
+			 * run, we know that "lo" is the edge, but
+			 * we can say nothing of "hi". Similarly,
+			 * if our key is greater than the run, we
+			 * know that "hi" is the edge, but we can
+			 * say nothing of "lo".
+			 *
+			 * Therefore if we do not find it, we also
+			 * know where it would go if it did exist:
+			 * just on the far side of the edge that we
+			 * know about.
 			 */
+			if (ofs == 20) {
+				mi = lo;
+				mi_key = base + elem_size * mi + key_offset;
+				cmp = memcmp(mi_key, key, 20);
+				if (!cmp)
+					return mi;
+				if (cmp < 0)
+					return -1 - hi;
+				else
+					return -1 - lo;
+			}
+
 			hiv = hi_key[ofs_0];
 			if (ofs_0 < 19)
 				hiv = (hiv << 8) | hi_key[ofs_0+1];
@@ -176,3 +224,26 @@
 	} while (lo < hi);
 	return -((int)lo)-1;
 }
+
+int sha1_position(const void *table,
+			size_t stride,
+			unsigned lo, unsigned hi,
+			const unsigned char *key)
+{
+	const unsigned char *base = table;
+
+	do {
+		unsigned mi = (lo + hi) / 2;
+		int cmp = git_oid__hashcmp(base + mi * stride, key);
+
+		if (!cmp)
+			return mi;
+
+		if (cmp > 0)
+			hi = mi;
+		else
+			lo = mi+1;
+	} while (lo < hi);
+
+	return -((int)lo)-1;
+}
diff --git a/src/sha1_lookup.h b/src/sha1_lookup.h
index 9a35372..3799620 100644
--- a/src/sha1_lookup.h
+++ b/src/sha1_lookup.h
@@ -15,4 +15,9 @@
 			unsigned lo, unsigned hi, unsigned nr,
 			const unsigned char *key);
 
+int sha1_position(const void *table,
+			size_t stride,
+			unsigned lo, unsigned hi,
+			const unsigned char *key);
+
 #endif
diff --git a/src/signature.c b/src/signature.c
index 0a34ccf..ec51a42 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -74,7 +74,7 @@
 		git_signature_free(p);
 		return signature_error("Signature cannot have an empty name");
 	}
-		
+
 	p->when.time = time;
 	p->when.offset = offset;
 
@@ -84,8 +84,12 @@
 
 git_signature *git_signature_dup(const git_signature *sig)
 {
-	git_signature *new = git__calloc(1, sizeof(git_signature));
+	git_signature *new;
 
+	if (sig == NULL)
+		return NULL;
+
+	new = git__calloc(1, sizeof(git_signature));
 	if (new == NULL)
 		return NULL;
 
@@ -129,6 +133,23 @@
 	return 0;
 }
 
+int git_signature_default(git_signature **out, git_repository *repo)
+{
+	int error;
+	git_config *cfg;
+	const char *user_name, *user_email;
+
+	if ((error = git_repository_config(&cfg, repo)) < 0)
+		return error;
+
+	if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
+		!(error = git_config_get_string(&user_email, cfg, "user.email")))
+		error = git_signature_now(out, user_name, user_email);
+
+	git_config_free(cfg);
+	return error;
+}
+
 int git_signature__parse(git_signature *sig, const char **buffer_out,
 		const char *buffer_end, const char *header, char ender)
 {
diff --git a/src/sortedcache.c b/src/sortedcache.c
new file mode 100644
index 0000000..466e55d
--- /dev/null
+++ b/src/sortedcache.c
@@ -0,0 +1,380 @@
+#include "sortedcache.h"
+
+GIT__USE_STRMAP;
+
+int git_sortedcache_new(
+	git_sortedcache **out,
+	size_t item_path_offset,
+	git_sortedcache_free_item_fn free_item,
+	void *free_item_payload,
+	git_vector_cmp item_cmp,
+	const char *path)
+{
+	git_sortedcache *sc;
+	size_t pathlen;
+
+	pathlen = path ? strlen(path) : 0;
+
+	sc = git__calloc(sizeof(git_sortedcache) + pathlen + 1, 1);
+	GITERR_CHECK_ALLOC(sc);
+
+	if (git_pool_init(&sc->pool, 1, 0) < 0 ||
+		git_vector_init(&sc->items, 4, item_cmp) < 0 ||
+		(sc->map = git_strmap_alloc()) == NULL)
+		goto fail;
+
+	if (git_rwlock_init(&sc->lock)) {
+		giterr_set(GITERR_OS, "Failed to initialize lock");
+		goto fail;
+	}
+
+	sc->item_path_offset  = item_path_offset;
+	sc->free_item         = free_item;
+	sc->free_item_payload = free_item_payload;
+	GIT_REFCOUNT_INC(sc);
+	if (pathlen)
+		memcpy(sc->path, path, pathlen);
+
+	*out = sc;
+	return 0;
+
+fail:
+	if (sc->map)
+		git_strmap_free(sc->map);
+	git_vector_free(&sc->items);
+	git_pool_clear(&sc->pool);
+	git__free(sc);
+	return -1;
+}
+
+void git_sortedcache_incref(git_sortedcache *sc)
+{
+	GIT_REFCOUNT_INC(sc);
+}
+
+const char *git_sortedcache_path(git_sortedcache *sc)
+{
+	return sc->path;
+}
+
+static void sortedcache_clear(git_sortedcache *sc)
+{
+	git_strmap_clear(sc->map);
+
+	if (sc->free_item) {
+		size_t i;
+		void *item;
+
+		git_vector_foreach(&sc->items, i, item) {
+			sc->free_item(sc->free_item_payload, item);
+		}
+	}
+
+	git_vector_clear(&sc->items);
+
+	git_pool_clear(&sc->pool);
+}
+
+static void sortedcache_free(git_sortedcache *sc)
+{
+	/* acquire write lock to make sure everyone else is done */
+	if (git_sortedcache_wlock(sc) < 0)
+		return;
+
+	sortedcache_clear(sc);
+	git_vector_free(&sc->items);
+	git_strmap_free(sc->map);
+
+	git_sortedcache_wunlock(sc);
+
+	git_rwlock_free(&sc->lock);
+	git__free(sc);
+}
+
+void git_sortedcache_free(git_sortedcache *sc)
+{
+	if (!sc)
+		return;
+	GIT_REFCOUNT_DEC(sc, sortedcache_free);
+}
+
+static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item)
+{
+	git_sortedcache *sc = payload;
+	/* path will already have been copied by upsert */
+	memcpy(tgt_item, src_item, sc->item_path_offset);
+	return 0;
+}
+
+/* copy a sorted cache */
+int git_sortedcache_copy(
+	git_sortedcache **out,
+	git_sortedcache *src,
+	bool lock,
+	int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+	void *payload)
+{
+	int error = 0;
+	git_sortedcache *tgt;
+	size_t i;
+	void *src_item, *tgt_item;
+
+	/* just use memcpy if no special copy fn is passed in */
+	if (!copy_item) {
+		copy_item = sortedcache_copy_item;
+		payload   = src;
+	}
+
+	if ((error = git_sortedcache_new(
+			&tgt, src->item_path_offset,
+			src->free_item, src->free_item_payload,
+			src->items._cmp, src->path)) < 0)
+		return error;
+
+	if (lock && git_sortedcache_rlock(src) < 0) {
+		git_sortedcache_free(tgt);
+		return -1;
+	}
+
+	git_vector_foreach(&src->items, i, src_item) {
+		char *path = ((char *)src_item) + src->item_path_offset;
+
+		if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 ||
+			(error = copy_item(payload, tgt_item, src_item)) < 0)
+			break;
+	}
+
+	if (lock)
+		git_sortedcache_runlock(src);
+	if (error)
+		git_sortedcache_free(tgt);
+
+	*out = !error ? tgt : NULL;
+
+	return error;
+}
+
+/* lock sortedcache while making modifications */
+int git_sortedcache_wlock(git_sortedcache *sc)
+{
+	GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+	if (git_rwlock_wrlock(&sc->lock) < 0) {
+		giterr_set(GITERR_OS, "Unable to acquire write lock on cache");
+		return -1;
+	}
+	return 0;
+}
+
+/* unlock sorted cache when done with modifications */
+void git_sortedcache_wunlock(git_sortedcache *sc)
+{
+	git_vector_sort(&sc->items);
+	git_rwlock_wrunlock(&sc->lock);
+}
+
+/* lock sortedcache for read */
+int git_sortedcache_rlock(git_sortedcache *sc)
+{
+	GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+	if (git_rwlock_rdlock(&sc->lock) < 0) {
+		giterr_set(GITERR_OS, "Unable to acquire read lock on cache");
+		return -1;
+	}
+	return 0;
+}
+
+/* unlock sorted cache when done reading */
+void git_sortedcache_runlock(git_sortedcache *sc)
+{
+	GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+	git_rwlock_rdunlock(&sc->lock);
+}
+
+/* if the file has changed, lock cache and load file contents into buf;
+ * returns <0 on error, >0 if file has not changed
+ */
+int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf)
+{
+	int error, fd;
+
+	if ((error = git_sortedcache_wlock(sc)) < 0)
+		return error;
+
+	if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
+		goto unlock;
+
+	if (!git__is_sizet(sc->stamp.size)) {
+		giterr_set(GITERR_INVALID, "Unable to load file larger than size_t");
+		error = -1;
+		goto unlock;
+	}
+
+	if ((fd = git_futils_open_ro(sc->path)) < 0) {
+		error = fd;
+		goto unlock;
+	}
+
+	if (buf)
+		error = git_futils_readbuffer_fd(buf, fd, (size_t)sc->stamp.size);
+
+	(void)p_close(fd);
+
+	if (error < 0)
+		goto unlock;
+
+	return 1; /* return 1 -> file needs reload and was successfully loaded */
+
+unlock:
+	git_sortedcache_wunlock(sc);
+	return error;
+}
+
+void git_sortedcache_updated(git_sortedcache *sc)
+{
+	 /* update filestamp to latest value */
+	if (git_futils_filestamp_check(&sc->stamp, sc->path) < 0)
+		giterr_clear();
+}
+
+/* release all items in sorted cache */
+int git_sortedcache_clear(git_sortedcache *sc, bool wlock)
+{
+	if (wlock && git_sortedcache_wlock(sc) < 0)
+		return -1;
+
+	sortedcache_clear(sc);
+
+	if (wlock)
+		git_sortedcache_wunlock(sc);
+
+	return 0;
+}
+
+/* find and/or insert item, returning pointer to item data */
+int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key)
+{
+	int error = 0;
+	khiter_t pos;
+	void *item;
+	size_t keylen, itemlen;
+	char *item_key;
+
+	pos = git_strmap_lookup_index(sc->map, key);
+	if (git_strmap_valid_index(sc->map, pos)) {
+		item = git_strmap_value_at(sc->map, pos);
+		goto done;
+	}
+
+	keylen  = strlen(key);
+	itemlen = sc->item_path_offset + keylen + 1;
+	itemlen = (itemlen + 7) & ~7;
+
+	if ((item = git_pool_mallocz(&sc->pool, (uint32_t)itemlen)) == NULL) {
+		/* don't use GITERR_CHECK_ALLOC b/c of lock */
+		error = -1;
+		goto done;
+	}
+
+	/* one strange thing is that even if the vector or hash table insert
+	 * fail, there is no way to free the pool item so we just abandon it
+	 */
+
+	item_key = ((char *)item) + sc->item_path_offset;
+	memcpy(item_key, key, keylen);
+
+	pos = kh_put(str, 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;
+
+	error = git_vector_insert(&sc->items, item);
+	if (error < 0)
+		git_strmap_delete_at(sc->map, pos);
+
+done:
+	if (out)
+		*out = !error ? item : NULL;
+	return error;
+}
+
+/* lookup item by key */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key)
+{
+	khiter_t pos = git_strmap_lookup_index(sc->map, key);
+	if (git_strmap_valid_index(sc->map, pos))
+		return git_strmap_value_at(sc->map, pos);
+	return NULL;
+}
+
+/* find out how many items are in the cache */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc)
+{
+	return git_vector_length(&sc->items);
+}
+
+/* lookup item by index */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
+{
+	/* make sure the items are sorted so this gets the correct item */
+	if (!sc->items.sorted)
+		git_vector_sort(&sc->items);
+
+	return git_vector_get(&sc->items, pos);
+}
+
+/* helper struct so bsearch callback can know offset + key value for cmp */
+struct sortedcache_magic_key {
+	size_t offset;
+	const char *key;
+};
+
+static int sortedcache_magic_cmp(const void *key, const void *value)
+{
+	const struct sortedcache_magic_key *magic = key;
+	const char *value_key = ((const char *)value) + magic->offset;
+	return strcmp(magic->key, value_key);
+}
+
+/* lookup index of item by key */
+int git_sortedcache_lookup_index(
+	size_t *out, git_sortedcache *sc, const char *key)
+{
+	struct sortedcache_magic_key magic;
+
+	magic.offset = sc->item_path_offset;
+	magic.key    = key;
+
+	return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic);
+}
+
+/* remove entry from cache */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos)
+{
+	char *item;
+	khiter_t mappos;
+
+	/* because of pool allocation, this can't actually remove the item,
+	 * but we can remove it from the items vector and the hash table.
+	 */
+
+	if ((item = git_vector_get(&sc->items, pos)) == NULL) {
+		giterr_set(GITERR_INVALID, "Removing item out of range");
+		return GIT_ENOTFOUND;
+	}
+
+	(void)git_vector_remove(&sc->items, pos);
+
+	mappos = git_strmap_lookup_index(sc->map, item + sc->item_path_offset);
+	git_strmap_delete_at(sc->map, mappos);
+
+	if (sc->free_item)
+		sc->free_item(sc->free_item_payload, item);
+
+	return 0;
+}
+
diff --git a/src/sortedcache.h b/src/sortedcache.h
new file mode 100644
index 0000000..4cacad6
--- /dev/null
+++ b/src/sortedcache.h
@@ -0,0 +1,178 @@
+/*
+ * 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_sorted_cache_h__
+#define INCLUDE_sorted_cache_h__
+
+#include "util.h"
+#include "fileops.h"
+#include "vector.h"
+#include "thread-utils.h"
+#include "pool.h"
+#include "strmap.h"
+
+#include <stddef.h>
+
+/*
+ * The purpose of this data structure is to cache the parsed contents of a
+ * file (a.k.a. the backing file) where each item in the file can be
+ * identified by a key string and you want to both look them up by name
+ * and traverse them in sorted order.  Each item is assumed to itself end
+ * in a GIT_FLEX_ARRAY.
+ */
+
+typedef void (*git_sortedcache_free_item_fn)(void *payload, void *item);
+
+typedef struct {
+	git_refcount rc;
+	git_rwlock   lock;
+	size_t       item_path_offset;
+	git_sortedcache_free_item_fn free_item;
+	void         *free_item_payload;
+	git_pool     pool;
+	git_vector   items;
+	git_strmap   *map;
+	git_futils_filestamp stamp;
+	char         path[GIT_FLEX_ARRAY];
+} git_sortedcache;
+
+/* Create a new sortedcache
+ *
+ * Even though every sortedcache stores items with a GIT_FLEX_ARRAY at
+ * the end containing their key string, you have to provide the item_cmp
+ * sorting function because the sorting function doesn't get a payload
+ * and therefore can't know the offset to the item key string. :-(
+ *
+ * @param out The allocated git_sortedcache
+ * @param item_path_offset Offset to the GIT_FLEX_ARRAY item key in the
+ *        struct - use offsetof(struct mine, key-field) to get this
+ * @param free_item Optional callback to free each item
+ * @param free_item_payload Optional payload passed to free_item callback
+ * @param item_cmp Compare the keys of two items
+ * @param path The path to the backing store file for this cache; this
+ *        may be NULL.  The cache makes it easy to load this and check
+ *        if it has been modified since the last load and/or write.
+ */
+int git_sortedcache_new(
+	git_sortedcache **out,
+	size_t item_path_offset, /* use offsetof(struct, path-field) macro */
+	git_sortedcache_free_item_fn free_item,
+	void *free_item_payload,
+	git_vector_cmp item_cmp,
+	const char *path);
+
+/* Copy a sorted cache
+ *
+ * - `copy_item` can be NULL to just use memcpy
+ * - if `lock`, grabs read lock on `src` during copy and releases after
+ */
+int git_sortedcache_copy(
+	git_sortedcache **out,
+	git_sortedcache *src,
+	bool lock,
+	int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+	void *payload);
+
+/* Free sorted cache (first calling `free_item` callbacks)
+ *
+ * Don't call on a locked collection - it may acquire a write lock
+ */
+void git_sortedcache_free(git_sortedcache *sc);
+
+/* Increment reference count - balance with call to free */
+void git_sortedcache_incref(git_sortedcache *sc);
+
+/* Get the pathname associated with this cache at creation time */
+const char *git_sortedcache_path(git_sortedcache *sc);
+
+/*
+ * CACHE WRITE FUNCTIONS
+ *
+ * The following functions require you to have a writer lock to make the
+ * modification.  Some of the functions take a `wlock` parameter and
+ * will optionally lock and unlock for you if that is passed as true.
+ *
+ */
+
+/* Lock sortedcache for write */
+int git_sortedcache_wlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with write */
+void git_sortedcache_wunlock(git_sortedcache *sc);
+
+/* Lock cache and load backing file into a buffer.
+ *
+ * This grabs a write lock on the cache then looks at the modification
+ * time and size of the file on disk.
+ *
+ * If the file appears to have changed, this loads the file contents into
+ * the buffer and returns a positive value leaving the cache locked - the
+ * caller should parse the file content, update the cache as needed, then
+ * release the lock.  NOTE: In this case, the caller MUST unlock the cache.
+ *
+ * If the file appears to be unchanged, then this automatically releases
+ * the lock on the cache, clears the buffer, and returns 0.
+ *
+ * @return 0 if up-to-date, 1 if out-of-date, <0 on error
+ */
+int git_sortedcache_lockandload(git_sortedcache *sc, git_buf *buf);
+
+/* Refresh file timestamp after write completes
+ * You should already be holding the write lock when you call this.
+ */
+void git_sortedcache_updated(git_sortedcache *sc);
+
+/* Release all items in sorted cache
+ *
+ * If `wlock` is true, grabs write lock and releases when done, otherwise
+ * you should already be holding a write lock when you call this.
+ */
+int git_sortedcache_clear(git_sortedcache *sc, bool wlock);
+
+/* Find and/or insert item, returning pointer to item data.
+ * You should already be holding the write lock when you call this.
+ */
+int git_sortedcache_upsert(
+	void **out, git_sortedcache *sc, const char *key);
+
+/* Removes entry at pos from cache
+ * You should already be holding the write lock when you call this.
+ */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos);
+
+/*
+ * CACHE READ FUNCTIONS
+ *
+ * The following functions access items in the cache.  To prevent the
+ * results from being invalidated before they can be used, you should be
+ * holding either a read lock or a write lock when using these functions.
+ *
+ */
+
+/* Lock sortedcache for read */
+int git_sortedcache_rlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with read */
+void git_sortedcache_runlock(git_sortedcache *sc);
+
+/* Lookup item by key - returns NULL if not found */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key);
+
+/* Get how many items are in the cache
+ *
+ * You can call this function without holding a lock, but be aware
+ * that it may change before you use it.
+ */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc);
+
+/* Lookup item by index - returns NULL if out of range */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos);
+
+/* Lookup index of item by key - returns GIT_ENOTFOUND if not found */
+int git_sortedcache_lookup_index(
+	size_t *out, git_sortedcache *sc, const char *key);
+
+#endif
diff --git a/src/stash.c b/src/stash.c
index 1222634..083c2a4 100644
--- a/src/stash.c
+++ b/src/stash.c
@@ -27,7 +27,7 @@
 {
 	int error = git_repository_head(out, repo);
 
-	if (error == GIT_EORPHANEDHEAD)
+	if (error == GIT_EUNBORNBRANCH)
 		return create_error(error, "You do not have the initial commit yet.");
 
 	return error;
@@ -117,7 +117,7 @@
 static int commit_index(
 	git_commit **i_commit,
 	git_index *index,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message,
 	const git_commit *parent)
 {
@@ -153,65 +153,61 @@
 	return error;
 }
 
-struct cb_data {
-	git_index *index;
-
-	int error;
-
+struct stash_update_rules {
 	bool include_changed;
 	bool include_untracked;
 	bool include_ignored;
 };
 
-static int update_index_cb(
-	const git_diff_delta *delta,
-	float progress,
-	void *payload)
+static int stash_update_index_from_diff(
+	git_index *index,
+	const git_diff *diff,
+	struct stash_update_rules *data)
 {
-	struct cb_data *data = (struct cb_data *)payload;
-	const char *add_path = NULL;
+	int error = 0;
+	size_t d, max_d = git_diff_num_deltas(diff);
 
-	GIT_UNUSED(progress);
+	for (d = 0; !error && d < max_d; ++d) {
+		const char *add_path = NULL;
+		const git_diff_delta *delta = git_diff_get_delta(diff, d);
 
-	switch (delta->status) {
-	case GIT_DELTA_IGNORED:
-		if (data->include_ignored)
-			add_path = delta->new_file.path;
-		break;
-
-	case GIT_DELTA_UNTRACKED:
-		if (data->include_untracked)
-			add_path = delta->new_file.path;
-		break;
-
-	case GIT_DELTA_ADDED:
-	case GIT_DELTA_MODIFIED:
-		if (data->include_changed)
-			add_path = delta->new_file.path;
-		break;
-
-	case GIT_DELTA_DELETED:
-		if (!data->include_changed)
+		switch (delta->status) {
+		case GIT_DELTA_IGNORED:
+			if (data->include_ignored)
+				add_path = delta->new_file.path;
 			break;
-		if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
-			data->error = git_index_remove(
-				data->index, delta->old_file.path, 0);
-		break;
 
-	default:
-		/* Unimplemented */
-		giterr_set(
-			GITERR_INVALID,
-			"Cannot update index. Unimplemented status (%d)",
-			delta->status);
-		data->error = -1;
-		break;
+		case GIT_DELTA_UNTRACKED:
+			if (data->include_untracked)
+				add_path = delta->new_file.path;
+			break;
+
+		case GIT_DELTA_ADDED:
+		case GIT_DELTA_MODIFIED:
+			if (data->include_changed)
+				add_path = delta->new_file.path;
+			break;
+
+		case GIT_DELTA_DELETED:
+			if (data->include_changed &&
+				!git_index_find(NULL, index, delta->old_file.path))
+				error = git_index_remove(index, delta->old_file.path, 0);
+			break;
+
+		default:
+			/* Unimplemented */
+			giterr_set(
+				GITERR_INVALID,
+				"Cannot update index. Unimplemented status (%d)",
+				delta->status);
+			return -1;
+		}
+
+		if (add_path != NULL)
+			error = git_index_add_bypath(index, add_path);
 	}
 
-	if (add_path != NULL)
-		data->error = git_index_add_bypath(data->index, add_path);
-
-	return data->error;
+	return error;
 }
 
 static int build_untracked_tree(
@@ -221,15 +217,13 @@
 	uint32_t flags)
 {
 	git_tree *i_tree = NULL;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	struct cb_data data = {0};
+	struct stash_update_rules data = {0};
 	int error;
 
 	git_index_clear(index);
 
-	data.index = index;
-
 	if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
 		opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
 			GIT_DIFF_RECURSE_UNTRACKED_DIRS;
@@ -248,18 +242,13 @@
 			&diff, git_index_owner(index), i_tree, &opts)) < 0)
 		goto cleanup;
 
-	if ((error = git_diff_foreach(
-			diff, update_index_cb, NULL, NULL, &data)) < 0)
-	{
-		if (error == GIT_EUSER)
-			error = data.error;
+	if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
 		goto cleanup;
-	}
 
 	error = build_tree_from_index(tree_out, index);
 
 cleanup:
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(i_tree);
 	return error;
 }
@@ -267,7 +256,7 @@
 static int commit_untracked(
 	git_commit **u_commit,
 	git_index *index,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message,
 	git_commit *i_commit,
 	uint32_t flags)
@@ -311,41 +300,29 @@
 {
 	git_repository *repo = git_index_owner(index);
 	git_tree *b_tree = NULL;
-	git_diff_list *diff = NULL, *diff2 = NULL;
+	git_diff *diff = NULL;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	struct cb_data data = {0};
+	struct stash_update_rules data = {0};
 	int error;
 
+	opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
+
 	if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
 		goto cleanup;
 
-	if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
+	if ((error = git_diff_tree_to_workdir_with_index(
+			&diff, repo, b_tree, &opts)) < 0)
 		goto cleanup;
 
-	if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
-		goto cleanup;
-
-	if ((error = git_diff_merge(diff, diff2)) < 0)
-		goto cleanup;
-
-	data.index = index;
 	data.include_changed = true;
 
-	if ((error = git_diff_foreach(
-			diff, update_index_cb, NULL, NULL, &data)) < 0)
-	{
-		if (error == GIT_EUSER)
-			error = data.error;
+	if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
 		goto cleanup;
-	}
 
-
-	if ((error = build_tree_from_index(tree_out, index)) < 0)
-		goto cleanup;
+	error = build_tree_from_index(tree_out, index);
 
 cleanup:
-	git_diff_list_free(diff);
-	git_diff_list_free(diff2);
+	git_diff_free(diff);
 	git_tree_free(b_tree);
 
 	return error;
@@ -354,7 +331,7 @@
 static int commit_worktree(
 	git_oid *w_commit_oid,
 	git_index *index,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message,
 	git_commit *i_commit,
 	git_commit *b_commit,
@@ -431,17 +408,19 @@
 static int update_reflog(
 	git_oid *w_commit_oid,
 	git_repository *repo,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message)
 {
-	git_reference *stash = NULL;
+	git_reference *stash;
 	git_reflog *reflog = NULL;
 	int error;
 
 	if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
 		goto cleanup;
 
-	if ((error = git_reflog_read(&reflog, stash)) < 0)
+	git_reference_free(stash);
+
+	if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
 		goto cleanup;
 
 	if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
@@ -451,7 +430,6 @@
 		goto cleanup;
 
 cleanup:
-	git_reference_free(stash);
 	git_reflog_free(reflog);
 	return error;
 }
@@ -474,12 +452,14 @@
 	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
 
 	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+	opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
 	if (include_untracked_files)
-		opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+		opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
 		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
 
 	if (include_ignored_files)
-		opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
+		opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
 
 	error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
 
@@ -510,7 +490,7 @@
 int git_stash_save(
 	git_oid *out,
 	git_repository *repo,
-	git_signature *stasher,
+	const git_signature *stasher,
 	const char *message,
 	uint32_t flags)
 {
@@ -595,7 +575,7 @@
 	if (error < 0)
 		goto cleanup;
 
-	if ((error = git_reflog_read(&reflog, stash)) < 0)
+	if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
 		goto cleanup;
 
 	max = git_reflog_entrycount(reflog);
@@ -629,7 +609,7 @@
 	if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
 		return error;
 
-	if ((error = git_reflog_read(&reflog, stash)) < 0)
+	if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
 		goto cleanup;
 
 	max = git_reflog_entrycount(reflog);
diff --git a/src/status.c b/src/status.c
index e520c10..07fdcb5 100644
--- a/src/status.c
+++ b/src/status.c
@@ -52,7 +52,7 @@
 }
 
 static unsigned int workdir_delta2status(
-	git_diff_list *diff, git_diff_delta *idx2wd)
+	git_diff *diff, git_diff_delta *idx2wd)
 {
 	git_status_t st = GIT_STATUS_CURRENT;
 
@@ -225,24 +225,6 @@
 	return status;
 }
 
-/*
-static int newfile_cmp(const void *a, const void *b)
-{
-	const git_diff_delta *delta_a = a;
-	const git_diff_delta *delta_b = b;
-
-	return git__strcmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-
-static int newfile_casecmp(const void *a, const void *b)
-{
-	const git_diff_delta *delta_a = a;
-	const git_diff_delta *delta_b = b;
-
-	return git__strcasecmp(delta_a->new_file.path, delta_b->new_file.path);
-}
-*/
-
 int git_status_list_new(
 	git_status_list **out,
 	git_repository *repo,
@@ -251,14 +233,14 @@
 	git_index *index = NULL;
 	git_status_list *status = NULL;
 	git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
-	git_diff_find_options findopts_i2w = GIT_DIFF_FIND_OPTIONS_INIT;
+	git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
 	git_tree *head = NULL;
 	git_status_show_t show =
 		opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
 	int error = 0;
 	unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
 
-	assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+	assert(show <= GIT_STATUS_SHOW_WORKDIR_ONLY);
 
 	*out = NULL;
 
@@ -269,12 +251,17 @@
 		return error;
 
 	/* if there is no HEAD, that's okay - we'll make an empty iterator */
-	if (((error = git_repository_head_tree(&head, repo)) < 0) &&
-		error != GIT_ENOTFOUND && error != GIT_EORPHANEDHEAD) {
-		git_index_free(index); /* release index */
-		return error;
+	if ((error = git_repository_head_tree(&head, repo)) < 0) {
+		if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH)
+			goto done;
+		giterr_clear();
 	}
 
+	/* refresh index from disk unless prevented */
+	if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 &&
+		git_index_read(index, false) < 0)
+		giterr_clear();
+
 	status = git_status_list_alloc(index);
 	GITERR_CHECK_ALLOC(status);
 
@@ -284,6 +271,7 @@
 	}
 
 	diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+	findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
 
 	if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
 		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
@@ -300,37 +288,32 @@
 	if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
 		diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
 
-	findopts_i2w.flags |= GIT_DIFF_FIND_FOR_UNTRACKED;
+	if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
+		findopt.flags = findopt.flags |
+			GIT_DIFF_FIND_AND_BREAK_REWRITES |
+			GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+			GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
 
 	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
 		if ((error = git_diff_tree_to_index(
-				&status->head2idx, repo, head, NULL, &diffopt)) < 0)
+				&status->head2idx, repo, head, index, &diffopt)) < 0)
 			goto done;
 
 		if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
-			(error = git_diff_find_similar(status->head2idx, NULL)) < 0)
+			(error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
 			goto done;
 	}
 
 	if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
 		if ((error = git_diff_index_to_workdir(
-				&status->idx2wd, repo, NULL, &diffopt)) < 0)
+				&status->idx2wd, repo, index, &diffopt)) < 0)
 			goto done;
 
 		if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
-			(error = git_diff_find_similar(status->idx2wd, &findopts_i2w)) < 0)
+			(error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
 			goto done;
 	}
 
-	if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
-		if ((error = git_diff__paired_foreach(
-				status->head2idx, NULL, status_collect, status)) < 0)
-			goto done;
-
-		git_diff_list_free(status->head2idx);
-		status->head2idx = NULL;
-	}
-
 	if ((error = git_diff__paired_foreach(
 			status->head2idx, status->idx2wd, status_collect, status)) < 0)
 		goto done;
@@ -383,8 +366,8 @@
 	if (status == NULL)
 		return;
 
-	git_diff_list_free(status->head2idx);
-	git_diff_list_free(status->idx2wd);
+	git_diff_free(status->head2idx);
+	git_diff_free(status->idx2wd);
 
 	git_vector_foreach(&status->paired, i, status_entry)
 		git__free(status_entry);
diff --git a/src/status.h b/src/status.h
index b58e0eb..33008b8 100644
--- a/src/status.h
+++ b/src/status.h
@@ -14,8 +14,8 @@
 struct git_status_list {
 	git_status_options opts;
 
-	git_diff_list *head2idx;
-	git_diff_list *idx2wd;
+	git_diff *head2idx;
+	git_diff *idx2wd;
 
 	git_vector paired;
 };
diff --git a/src/strmap.c b/src/strmap.c
new file mode 100644
index 0000000..b26a13d
--- /dev/null
+++ b/src/strmap.c
@@ -0,0 +1,32 @@
+/*
+ * 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 "strmap.h"
+
+int git_strmap_next(
+	void **data,
+	git_strmap_iter* iter,
+	git_strmap *map)
+{
+	if (!map)
+		return GIT_ERROR;
+
+	while (*iter != git_strmap_end(map)) {
+		if (!(git_strmap_has_data(map, *iter))) {
+			++(*iter);
+			continue;
+		}
+
+		*data = git_strmap_value_at(map, *iter);
+
+		++(*iter);
+
+		return GIT_OK;
+	}
+
+	return GIT_ITEROVER;
+}
diff --git a/src/strmap.h b/src/strmap.h
index 44176a0..8276ab4 100644
--- a/src/strmap.h
+++ b/src/strmap.h
@@ -17,6 +17,7 @@
 
 __KHASH_TYPE(str, const char *, void *);
 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)
@@ -31,7 +32,9 @@
 #define git_strmap_valid_index(h, idx) (idx != kh_end(h))
 
 #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)
@@ -61,4 +64,12 @@
 #define git_strmap_foreach		kh_foreach
 #define git_strmap_foreach_value	kh_foreach_value
 
+#define git_strmap_begin		kh_begin
+#define git_strmap_end		kh_end
+
+int git_strmap_next(
+	void **data,
+	git_strmap_iter* iter,
+	git_strmap *map);
+
 #endif
diff --git a/src/submodule.c b/src/submodule.c
index 89eba2a..586494f 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -9,9 +9,7 @@
 #include "git2/config.h"
 #include "git2/sys/config.h"
 #include "git2/types.h"
-#include "git2/repository.h"
 #include "git2/index.h"
-#include "git2/submodule.h"
 #include "buffer.h"
 #include "buf_text.h"
 #include "vector.h"
@@ -32,6 +30,8 @@
 	{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
 	{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
 	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
+	{GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
+	{GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
 };
 
 static git_cvar_map _sm_ignore_map[] = {
@@ -39,6 +39,8 @@
 	{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
 	{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
 	{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+	{GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
+	{GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
 };
 
 static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
@@ -73,15 +75,11 @@
 static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
 static int lookup_head_remote(git_buf *url, git_repository *repo);
 static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
-static void submodule_release(git_submodule *sm, int decr);
-static int submodule_load_from_index(git_repository *, const git_index_entry *);
-static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
 static int submodule_load_from_config(const git_config_entry *, void *);
 static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
 static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
-static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
-static int submodule_index_status(unsigned int *status, git_submodule *sm);
-static int submodule_wd_status(unsigned int *status, git_submodule *sm);
+static void submodule_get_index_status(unsigned int *, git_submodule *);
+static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
 
 static int submodule_cmp(const void *a, const void *b)
 {
@@ -163,7 +161,7 @@
 		 * us from issuing a callback twice for a submodule where the name
 		 * and path are not the same.
 		 */
-		if (sm->refcount > 1) {
+		if (GIT_REFCOUNT_VAL(sm) > 1) {
 			if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND)
 				continue;
 			if ((error = git_vector_insert(&seen, sm)) < 0)
@@ -195,9 +193,7 @@
 	if (smcfg == NULL)
 		return;
 
-	git_strmap_foreach_value(smcfg, sm, {
-		submodule_release(sm,1);
-	});
+	git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); });
 	git_strmap_free(smcfg);
 }
 
@@ -338,7 +334,7 @@
 
 	assert(sm);
 
-	if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
+	if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
 		(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
 		return error;
 
@@ -348,7 +344,7 @@
 int git_submodule_add_to_index(git_submodule *sm, int write_index)
 {
 	int error;
-	git_repository *repo, *sm_repo = NULL;
+	git_repository *sm_repo = NULL;
 	git_index *index;
 	git_buf path = GIT_BUF_INIT;
 	git_commit *head;
@@ -357,14 +353,12 @@
 
 	assert(sm);
 
-	repo = sm->owner;
-
 	/* force reload of wd OID by git_submodule_open */
 	sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
 
-	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+	if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
 		(error = git_buf_joinpath(
-			&path, git_repository_workdir(repo), sm->path)) < 0 ||
+			&path, git_repository_workdir(sm->repo), sm->path)) < 0 ||
 		(error = git_submodule_open(&sm_repo, sm)) < 0)
 		goto cleanup;
 
@@ -378,7 +372,8 @@
 
 	memset(&entry, 0, sizeof(entry));
 	entry.path = sm->path;
-	git_index_entry__init_from_stat(&entry, &st);
+	git_index_entry__init_from_stat(
+		&entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
 
 	/* calling git_submodule_open will have set sm->wd_oid if possible */
 	if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
@@ -416,15 +411,34 @@
 	return error;
 }
 
+const char *git_submodule_ignore_to_str(git_submodule_ignore_t ignore)
+{
+	int i;
+	for (i = 0; i < (int)ARRAY_SIZE(_sm_ignore_map); ++i)
+		if (_sm_ignore_map[i].map_value == ignore)
+			return _sm_ignore_map[i].str_match;
+	return NULL;
+}
+
+const char *git_submodule_update_to_str(git_submodule_update_t update)
+{
+	int i;
+	for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
+		if (_sm_update_map[i].map_value == update)
+			return _sm_update_map[i].str_match;
+	return NULL;
+}
+
 int git_submodule_save(git_submodule *submodule)
 {
 	int error = 0;
 	git_config_backend *mods;
 	git_buf key = GIT_BUF_INIT;
+	const char *val;
 
 	assert(submodule);
 
-	mods = open_gitmodules(submodule->owner, true, NULL);
+	mods = open_gitmodules(submodule->repo, true, NULL);
 	if (!mods) {
 		giterr_set(GITERR_SUBMODULE,
 			"Adding submodules to a bare repository is not supported (for now)");
@@ -445,22 +459,14 @@
 		goto cleanup;
 
 	if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
-		submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
-	{
-		const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
-			NULL : _sm_update_map[submodule->update].str_match;
+		(val = git_submodule_update_to_str(submodule->update)) != NULL)
 		error = git_config_file_set_string(mods, key.ptr, val);
-	}
 	if (error < 0)
 		goto cleanup;
 
 	if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
-		submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
-	{
-		const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
-			NULL : _sm_ignore_map[submodule->ignore].str_match;
+		(val = git_submodule_ignore_to_str(submodule->ignore)) != NULL)
 		error = git_config_file_set_string(mods, key.ptr, val);
-	}
 	if (error < 0)
 		goto cleanup;
 
@@ -487,7 +493,7 @@
 git_repository *git_submodule_owner(git_submodule *submodule)
 {
 	assert(submodule);
-	return submodule->owner;
+	return submodule->repo;
 }
 
 const char *git_submodule_name(git_submodule *submodule)
@@ -544,11 +550,12 @@
 {
 	assert(submodule);
 
+	/* load unless we think we have a valid oid */
 	if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
 		git_repository *subrepo;
 
 		/* calling submodule open grabs the HEAD OID if possible */
-		if (!git_submodule_open(&subrepo, submodule))
+		if (!git_submodule_open_bare(&subrepo, submodule))
 			git_repository_free(subrepo);
 		else
 			giterr_clear();
@@ -563,7 +570,8 @@
 git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
 {
 	assert(submodule);
-	return submodule->ignore;
+	return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
+		GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
 }
 
 git_submodule_ignore_t git_submodule_set_ignore(
@@ -573,7 +581,7 @@
 
 	assert(submodule);
 
-	if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
+	if (ignore == GIT_SUBMODULE_IGNORE_RESET)
 		ignore = submodule->ignore_default;
 
 	old = submodule->ignore;
@@ -584,7 +592,8 @@
 git_submodule_update_t git_submodule_update(git_submodule *submodule)
 {
 	assert(submodule);
-	return submodule->update;
+	return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+		GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
 }
 
 git_submodule_update_t git_submodule_set_update(
@@ -594,7 +603,7 @@
 
 	assert(submodule);
 
-	if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
+	if (update == GIT_SUBMODULE_UPDATE_RESET)
 		update = submodule->update_default;
 
 	old = submodule->update;
@@ -625,6 +634,7 @@
 int git_submodule_init(git_submodule *submodule, int overwrite)
 {
 	int error;
+	const char *val;
 
 	/* write "submodule.NAME.url" */
 
@@ -641,14 +651,10 @@
 
 	/* write "submodule.NAME.update" if not default */
 
-	if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
-		error = submodule_update_config(
-			submodule, "update", NULL, (overwrite != 0), false);
-	else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
-		error = submodule_update_config(
-			submodule, "update",
-			_sm_update_map[submodule->update].str_match,
-			(overwrite != 0), false);
+	val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+		NULL : git_submodule_update_to_str(submodule->update);
+	error = submodule_update_config(
+		submodule, "update", val, (overwrite != 0), false);
 
 	return error;
 }
@@ -667,51 +673,70 @@
 		submodule, "url", submodule->url, true, true);
 }
 
-int git_submodule_open(
-	git_repository **subrepo,
-	git_submodule *submodule)
+static int git_submodule__open(
+	git_repository **subrepo, git_submodule *sm, bool bare)
 {
 	int error;
 	git_buf path = GIT_BUF_INIT;
-	git_repository *repo;
-	const char *workdir;
+	unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
+	const char *wd;
 
-	assert(submodule && subrepo);
+	assert(sm && subrepo);
 
-	repo = submodule->owner;
-	workdir = git_repository_workdir(repo);
+	if (git_repository__ensure_not_bare(
+			sm->repo, "open submodule repository") < 0)
+		return GIT_EBAREREPO;
 
-	if (!workdir) {
-		giterr_set(GITERR_REPOSITORY,
-			"Cannot open submodule repository in a bare repo");
-		return GIT_ENOTFOUND;
-	}
+	wd = git_repository_workdir(sm->repo);
 
-	if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
-		giterr_set(GITERR_REPOSITORY,
-			"Cannot open submodule repository that is not checked out");
-		return GIT_ENOTFOUND;
-	}
-
-	if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
+	if (git_buf_joinpath(&path, wd, sm->path) < 0 ||
+		git_buf_joinpath(&path, path.ptr, DOT_GIT) < 0)
 		return -1;
 
-	error = git_repository_open(subrepo, path.ptr);
+	sm->flags = sm->flags &
+		~(GIT_SUBMODULE_STATUS_IN_WD |
+		  GIT_SUBMODULE_STATUS__WD_OID_VALID |
+		  GIT_SUBMODULE_STATUS__WD_SCANNED);
+
+	if (bare)
+		flags |= GIT_REPOSITORY_OPEN_BARE;
+
+	error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
+
+	/* if we opened the submodule successfully, grab HEAD OID, etc. */
+	if (!error) {
+		sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
+			GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+		if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
+			sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
+		else
+			giterr_clear();
+	} else if (git_path_exists(path.ptr)) {
+		sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
+			GIT_SUBMODULE_STATUS_IN_WD;
+	} else {
+		git_buf_rtruncate_at_char(&path, '/'); /* remove "/.git" */
+
+		if (git_path_isdir(path.ptr))
+			sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
+	}
 
 	git_buf_free(&path);
 
-	/* if we have opened the submodule successfully, let's grab the HEAD OID */
-	if (!error) {
-		if (!git_reference_name_to_id(
-				&submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
-			submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
-		else
-			giterr_clear();
-	}
-
 	return error;
 }
 
+int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
+{
+	return git_submodule__open(subrepo, sm, true);
+}
+
+int git_submodule_open(git_repository **subrepo, git_submodule *sm)
+{
+	return git_submodule__open(subrepo, sm, false);
+}
+
 int git_submodule_reload_all(git_repository *repo)
 {
 	assert(repo);
@@ -719,74 +744,100 @@
 	return load_submodule_config(repo);
 }
 
+static void submodule_update_from_index_entry(
+	git_submodule *sm, const git_index_entry *ie)
+{
+	bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
+
+	if (!S_ISGITLINK(ie->mode)) {
+		if (!already_found)
+			sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+	} else {
+		if (already_found)
+			sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
+		else
+			git_oid_cpy(&sm->index_oid, &ie->oid);
+
+		sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
+			GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
+	}
+}
+
+static int submodule_update_index(git_submodule *sm)
+{
+	git_index *index;
+	const git_index_entry *ie;
+
+	if (git_repository_index__weakptr(&index, sm->repo) < 0)
+		return -1;
+
+	sm->flags = sm->flags &
+		~(GIT_SUBMODULE_STATUS_IN_INDEX |
+		  GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
+
+	if (!(ie = git_index_get_bypath(index, sm->path, 0)))
+		return 0;
+
+	submodule_update_from_index_entry(sm, ie);
+
+	return 0;
+}
+
+static void submodule_update_from_head_data(
+	git_submodule *sm, mode_t mode, const git_oid *id)
+{
+	if (!S_ISGITLINK(mode))
+		sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+	else {
+		git_oid_cpy(&sm->head_oid, id);
+
+		sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
+			GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
+	}
+}
+
+static int submodule_update_head(git_submodule *submodule)
+{
+	git_tree *head = NULL;
+	git_tree_entry *te = NULL;
+
+	submodule->flags = submodule->flags &
+		~(GIT_SUBMODULE_STATUS_IN_HEAD |
+		  GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
+
+	/* if we can't look up file in current head, then done */
+	if (git_repository_head_tree(&head, submodule->repo) < 0 ||
+		git_tree_entry_bypath(&te, head, submodule->path) < 0)
+		giterr_clear();
+	else
+		submodule_update_from_head_data(submodule, te->attr, &te->oid);
+
+	git_tree_entry_free(te);
+	git_tree_free(head);
+	return 0;
+}
+
 int git_submodule_reload(git_submodule *submodule)
 {
-	git_repository *repo;
-	git_index *index;
-	int error;
-	size_t pos;
-	git_tree *head;
+	int error = 0;
 	git_config_backend *mods;
 
 	assert(submodule);
 
 	/* refresh index data */
 
-	repo = submodule->owner;
-	if (git_repository_index__weakptr(&index, repo) < 0)
+	if (submodule_update_index(submodule) < 0)
 		return -1;
 
-	submodule->flags = submodule->flags &
-		~(GIT_SUBMODULE_STATUS_IN_INDEX |
-		  GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
-
-	if (!git_index_find(&pos, index, submodule->path)) {
-		const git_index_entry *entry = git_index_get_byindex(index, pos);
-
-		if (S_ISGITLINK(entry->mode)) {
-			if ((error = submodule_load_from_index(repo, entry)) < 0)
-				return error;
-		} else {
-			submodule_mode_mismatch(
-				repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
-		}
-	}
-
 	/* refresh HEAD tree data */
 
-	if (!(error = git_repository_head_tree(&head, repo))) {
-		git_tree_entry *te;
-
-		submodule->flags = submodule->flags &
-			~(GIT_SUBMODULE_STATUS_IN_HEAD |
-			  GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
-
-		if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
-
-			if (S_ISGITLINK(te->attr)) {
-				error = submodule_load_from_head(repo, submodule->path, &te->oid);
-			} else {
-				submodule_mode_mismatch(
-					repo, submodule->path,
-					GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
-			}
-
-			git_tree_entry_free(te);
-		}
-		else if (error == GIT_ENOTFOUND) {
-			giterr_clear();
-			error = 0;
-		}
-
-		git_tree_free(head);
-	}
-
-	if (error < 0)
-		return error;
+	if (submodule_update_head(submodule) < 0)
+		return -1;
 
 	/* refresh config data */
 
-	if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
+	mods = open_gitmodules(submodule->repo, false, NULL);
+	if (mods != NULL) {
 		git_buf path = GIT_BUF_INIT;
 
 		git_buf_sets(&path, "submodule\\.");
@@ -797,7 +848,7 @@
 			error = -1;
 		else
 			error = git_config_file_foreach_match(
-				mods, path.ptr, submodule_load_from_config, repo);
+				mods, path.ptr, submodule_load_from_config, submodule->repo);
 
 		git_buf_free(&path);
 		git_config_file_free(mods);
@@ -816,40 +867,92 @@
 	return error;
 }
 
-int git_submodule_status(
-	unsigned int *status,
-	git_submodule *submodule)
+static void submodule_copy_oid_maybe(
+	git_oid *tgt, const git_oid *src, bool valid)
 {
-	int error = 0;
-	unsigned int status_val;
-
-	assert(status && submodule);
-
-	status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
-
-	if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
-		if (!(error = submodule_index_status(&status_val, submodule)))
-			error = submodule_wd_status(&status_val, submodule);
+	if (tgt) {
+		if (valid)
+			memcpy(tgt, src, sizeof(*tgt));
+		else
+			memset(tgt, 0, sizeof(*tgt));
 	}
-
-	*status = status_val;
-
-	return error;
 }
 
-int git_submodule_location(
-	unsigned int *location_status,
-	git_submodule *submodule)
+int git_submodule__status(
+	unsigned int *out_status,
+	git_oid *out_head_id,
+	git_oid *out_index_id,
+	git_oid *out_wd_id,
+	git_submodule *sm,
+	git_submodule_ignore_t ign)
 {
-	assert(location_status && submodule);
+	unsigned int status;
+	git_repository *smrepo = NULL;
 
-	*location_status = submodule->flags &
-		(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX |
-		 GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD);
+	if (ign < GIT_SUBMODULE_IGNORE_NONE)
+		ign = sm->ignore;
+
+	/* only return location info if ignore == all */
+	if (ign == GIT_SUBMODULE_IGNORE_ALL) {
+		*out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
+		return 0;
+	}
+
+	/* refresh the index OID */
+	if (submodule_update_index(sm) < 0)
+		return -1;
+
+	/* refresh the HEAD OID */
+	if (submodule_update_head(sm) < 0)
+		return -1;
+
+	/* for ignore == dirty, don't scan the working directory */
+	if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
+		/* git_submodule_open_bare will load WD OID data */
+		if (git_submodule_open_bare(&smrepo, sm) < 0)
+			giterr_clear();
+		else
+			git_repository_free(smrepo);
+		smrepo = NULL;
+	} else if (git_submodule_open(&smrepo, sm) < 0) {
+		giterr_clear();
+		smrepo = NULL;
+	}
+
+	status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
+
+	submodule_get_index_status(&status, sm);
+	submodule_get_wd_status(&status, sm, smrepo, ign);
+
+	git_repository_free(smrepo);
+
+	*out_status = status;
+
+	submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
+		(sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
+	submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
+		(sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
+	submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
+		(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
 
 	return 0;
 }
 
+int git_submodule_status(unsigned int *status, git_submodule *sm)
+{
+	assert(status && sm);
+
+	return git_submodule__status(status, NULL, NULL, NULL, sm, 0);
+}
+
+int git_submodule_location(unsigned int *location, git_submodule *sm)
+{
+	assert(location && sm);
+
+	return git_submodule__status(
+		location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
+}
+
 
 /*
  * INTERNAL FUNCTIONS
@@ -857,54 +960,50 @@
 
 static git_submodule *submodule_alloc(git_repository *repo, const char *name)
 {
+	size_t namelen;
 	git_submodule *sm;
 
-	if (!name || !strlen(name)) {
+	if (!name || !(namelen = strlen(name))) {
 		giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
 		return NULL;
 	}
 
 	sm = git__calloc(1, sizeof(git_submodule));
 	if (sm == NULL)
-		goto fail;
+		return NULL;
 
-	sm->path = sm->name = git__strdup(name);
-	if (!sm->name)
-		goto fail;
+	sm->name = sm->path = git__strdup(name);
+	if (!sm->name) {
+		git__free(sm);
+		return NULL;
+	}
 
-	sm->owner = repo;
-	sm->refcount = 1;
+	GIT_REFCOUNT_INC(sm);
+	sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
+	sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
+	sm->repo   = repo;
 
 	return sm;
-
-fail:
-	submodule_release(sm, 0);
-	return NULL;
 }
 
-static void submodule_release(git_submodule *sm, int decr)
+static void submodule_release(git_submodule *sm)
 {
 	if (!sm)
 		return;
 
-	sm->refcount -= decr;
+	if (sm->path != sm->name)
+		git__free(sm->path);
+	git__free(sm->name);
+	git__free(sm->url);
+	git__memzero(sm, sizeof(*sm));
+	git__free(sm);
+}
 
-	if (sm->refcount == 0) {
-		if (sm->name != sm->path) {
-			git__free(sm->path);
-			sm->path = NULL;
-		}
-
-		git__free(sm->name);
-		sm->name = NULL;
-
-		git__free(sm->url);
-		sm->url = NULL;
-
-		sm->owner = NULL;
-
-		git__free(sm);
-	}
+void git_submodule_free(git_submodule *sm)
+{
+	if (!sm)
+		return;
+	GIT_REFCOUNT_DEC(sm, submodule_release);
 }
 
 static int submodule_get(
@@ -927,6 +1026,7 @@
 
 	if (!git_strmap_valid_index(smcfg, pos)) {
 		sm = submodule_alloc(repo, name);
+		GITERR_CHECK_ALLOC(sm);
 
 		/* insert value at name - if another thread beats us to it, then use
 		 * their record and release our own.
@@ -934,10 +1034,10 @@
 		pos = kh_put(str, smcfg, sm->name, &error);
 
 		if (error < 0) {
-			submodule_release(sm, 1);
+			git_submodule_free(sm);
 			sm = NULL;
 		} else if (error == 0) {
-			submodule_release(sm, 1);
+			git_submodule_free(sm);
 			sm = git_strmap_value_at(smcfg, pos);
 		} else {
 			git_strmap_set_value_at(smcfg, pos, sm);
@@ -951,43 +1051,6 @@
 	return (sm != NULL) ? 0 : -1;
 }
 
-static int submodule_load_from_index(
-	git_repository *repo, const git_index_entry *entry)
-{
-	git_submodule *sm;
-
-	if (submodule_get(&sm, repo, entry->path, NULL) < 0)
-		return -1;
-
-	if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
-		sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
-		return 0;
-	}
-
-	sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
-
-	git_oid_cpy(&sm->index_oid, &entry->oid);
-	sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
-
-	return 0;
-}
-
-static int submodule_load_from_head(
-	git_repository *repo, const char *path, const git_oid *oid)
-{
-	git_submodule *sm;
-
-	if (submodule_get(&sm, repo, path, NULL) < 0)
-		return -1;
-
-	sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
-
-	git_oid_cpy(&sm->head_oid, oid);
-	sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
-
-	return 0;
-}
-
 static int submodule_config_error(const char *property, const char *value)
 {
 	giterr_set(GITERR_INVALID,
@@ -995,6 +1058,34 @@
 	return -1;
 }
 
+int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
+{
+	int val;
+
+	if (git_config_lookup_map_value(
+			&val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
+		*out = GIT_SUBMODULE_IGNORE_NONE;
+		return submodule_config_error("ignore", value);
+	}
+
+	*out = (git_submodule_ignore_t)val;
+	return 0;
+}
+
+int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
+{
+	int val;
+
+	if (git_config_lookup_map_value(
+			&val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
+		*out = GIT_SUBMODULE_UPDATE_CHECKOUT;
+		return submodule_config_error("update", value);
+	}
+
+	*out = (git_submodule_update_t)val;
+	return 0;
+}
+
 static int submodule_load_from_config(
 	const git_config_entry *entry, void *data)
 {
@@ -1012,8 +1103,10 @@
 
 	namestart = key + strlen("submodule.");
 	property  = strrchr(namestart, '.');
-	if (property == NULL)
+
+	if (!property || (property == namestart))
 		return 0;
+
 	property++;
 	is_path = (strcasecmp(property, "path") == 0);
 
@@ -1047,11 +1140,11 @@
 		git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
 
 		if (error >= 0)
-			sm->refcount++; /* inserted under a new key */
+			GIT_REFCOUNT_INC(sm); /* inserted under a new key */
 
 		/* if we replaced an old module under this key, release the old one */
 		if (old_sm && ((git_submodule *)old_sm) != sm) {
-			submodule_release(old_sm, 1);
+			git_submodule_free(old_sm);
 			/* TODO: log warning about multiple submodules with same path */
 		}
 	}
@@ -1076,22 +1169,18 @@
 			return -1;
 	}
 	else if (strcasecmp(property, "update") == 0) {
-		int val;
-		if (git_config_lookup_map_value(
-			&val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
-			return submodule_config_error("update", value);
-		sm->update_default = sm->update = (git_submodule_update_t)val;
+		if (git_submodule_parse_update(&sm->update, value) < 0)
+			return -1;
+		sm->update_default = sm->update;
 	}
 	else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
 		if (git__parse_bool(&sm->fetch_recurse, value) < 0)
 			return submodule_config_error("fetchRecurseSubmodules", value);
 	}
 	else if (strcasecmp(property, "ignore") == 0) {
-		int val;
-		if (git_config_lookup_map_value(
-			&val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
-			return submodule_config_error("ignore", value);
-		sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
+		if (git_submodule_parse_ignore(&sm->ignore, value) < 0)
+			return -1;
+		sm->ignore_default = sm->ignore;
 	}
 	/* ignore other unknown submodule properties */
 
@@ -1101,13 +1190,15 @@
 static int submodule_load_from_wd_lite(
 	git_submodule *sm, const char *name, void *payload)
 {
-	git_repository *repo = git_submodule_owner(sm);
 	git_buf path = GIT_BUF_INIT;
 
 	GIT_UNUSED(name);
 	GIT_UNUSED(payload);
 
-	if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
+	if (git_repository_is_bare(sm->repo))
+		return 0;
+
+	if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0)
 		return -1;
 
 	if (git_path_isdir(path.ptr))
@@ -1121,18 +1212,6 @@
 	return 0;
 }
 
-static void submodule_mode_mismatch(
-	git_repository *repo, const char *path, unsigned int flag)
-{
-	khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
-
-	if (git_strmap_valid_index(repo->submodules, pos)) {
-		git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
-
-		sm->flags |= flag;
-	}
-}
-
 static int load_submodule_config_from_index(
 	git_repository *repo, git_oid *gitmodules_oid)
 {
@@ -1146,18 +1225,21 @@
 		return error;
 
 	while (!(error = git_iterator_advance(&entry, i))) {
+		khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
+		git_submodule *sm;
 
-		if (S_ISGITLINK(entry->mode)) {
-			error = submodule_load_from_index(repo, entry);
-			if (error < 0)
-				break;
-		} else {
-			submodule_mode_mismatch(
-				repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
+		if (git_strmap_valid_index(repo->submodules, pos)) {
+			sm = git_strmap_value_at(repo->submodules, pos);
 
-			if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
-				git_oid_cpy(gitmodules_oid, &entry->oid);
-		}
+			if (S_ISGITLINK(entry->mode))
+				submodule_update_from_index_entry(sm, entry);
+			else
+				sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+		} else if (S_ISGITLINK(entry->mode)) {
+			if (!submodule_get(&sm, repo, entry->path, NULL))
+				submodule_update_from_index_entry(sm, entry);
+		} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
+			git_oid_cpy(gitmodules_oid, &entry->oid);
 	}
 
 	if (error == GIT_ITEROVER)
@@ -1176,8 +1258,11 @@
 	git_iterator *i;
 	const git_index_entry *entry;
 
-	if ((error = git_repository_head_tree(&head, repo)) < 0)
-		return error;
+	/* if we can't look up current head, then there's no submodule in it */
+	if (git_repository_head_tree(&head, repo) < 0) {
+		giterr_clear();
+		return 0;
+	}
 
 	if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
 		git_tree_free(head);
@@ -1185,18 +1270,24 @@
 	}
 
 	while (!(error = git_iterator_advance(&entry, i))) {
+		khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path);
+		git_submodule *sm;
 
-		if (S_ISGITLINK(entry->mode)) {
-			error = submodule_load_from_head(repo, entry->path, &entry->oid);
-			if (error < 0)
-				break;
-		} else {
-			submodule_mode_mismatch(
-				repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
+		if (git_strmap_valid_index(repo->submodules, pos)) {
+			sm = git_strmap_value_at(repo->submodules, pos);
 
-			if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
-				git_oid_iszero(gitmodules_oid))
-				git_oid_cpy(gitmodules_oid, &entry->oid);
+			if (S_ISGITLINK(entry->mode))
+				submodule_update_from_head_data(
+					sm, entry->mode, &entry->oid);
+			else
+				sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+		} else if (S_ISGITLINK(entry->mode)) {
+			if (!submodule_get(&sm, repo, entry->path, NULL))
+				submodule_update_from_head_data(
+					sm, entry->mode, &entry->oid);
+		} else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
+				   git_oid_iszero(gitmodules_oid)) {
+			git_oid_cpy(gitmodules_oid, &entry->oid);
 		}
 	}
 
@@ -1381,7 +1472,7 @@
 
 	assert(submodule);
 
-	error = git_repository_config__weakptr(&config, submodule->owner);
+	error = git_repository_config__weakptr(&config, submodule->repo);
 	if (error < 0)
 		return error;
 
@@ -1409,11 +1500,13 @@
 	return error;
 }
 
-static int submodule_index_status(unsigned int *status, git_submodule *sm)
+static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
 {
 	const git_oid *head_oid  = git_submodule_head_id(sm);
 	const git_oid *index_oid = git_submodule_index_id(sm);
 
+	*status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
+
 	if (!head_oid) {
 		if (index_oid)
 			*status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
@@ -1422,27 +1515,23 @@
 		*status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
 	else if (!git_oid_equal(head_oid, index_oid))
 		*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
-
-	return 0;
 }
 
-static int submodule_wd_status(unsigned int *status, git_submodule *sm)
+static void submodule_get_wd_status(
+	unsigned int *status,
+	git_submodule *sm,
+	git_repository *sm_repo,
+	git_submodule_ignore_t ign)
 {
-	int error = 0;
-	const git_oid *wd_oid, *index_oid;
-	git_repository *sm_repo = NULL;
+	const git_oid *index_oid = git_submodule_index_id(sm);
+	const git_oid *wd_oid =
+		(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
+	git_tree *sm_head = NULL;
+	git_index *index = NULL;
+	git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff;
 
-	/* open repo now if we need it (so wd_id() call won't reopen) */
-	if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
-		 sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
-		(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
-	{
-		if ((error = git_submodule_open(&sm_repo, sm)) < 0)
-			return error;
-	}
-
-	index_oid = git_submodule_index_id(sm);
-	wd_oid    = git_submodule_wd_id(sm);
+	*status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
 
 	if (!index_oid) {
 		if (wd_oid)
@@ -1458,59 +1547,51 @@
 	else if (!git_oid_equal(index_oid, wd_oid))
 		*status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
 
-	if (sm_repo != NULL) {
-		git_tree *sm_head;
-		git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
-		git_diff_list *diff;
+	/* if we have no repo, then we're done */
+	if (!sm_repo)
+		return;
 
-		/* the diffs below could be optimized with an early termination
-		 * option to the git_diff functions, but for now this is sufficient
-		 * (and certainly no worse that what core git does).
-		 */
+	/* the diffs below could be optimized with an early termination
+	 * option to the git_diff functions, but for now this is sufficient
+	 * (and certainly no worse that what core git does).
+	 */
 
-		/* perform head-to-index diff on submodule */
+	if (ign == GIT_SUBMODULE_IGNORE_NONE)
+		opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
 
-		if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
-			return error;
+	(void)git_repository_index__weakptr(&index, sm_repo);
 
-		if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
-			opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
-
-		error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
-
-		if (!error) {
+	/* if we don't have an unborn head, check diff with index */
+	if (git_repository_head_tree(&sm_head, sm_repo) < 0)
+		giterr_clear();
+	else {
+		/* perform head to index diff on submodule */
+		if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
+			giterr_clear();
+		else {
 			if (git_diff_num_deltas(diff) > 0)
 				*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
-
-			git_diff_list_free(diff);
+			git_diff_free(diff);
 			diff = NULL;
 		}
 
 		git_tree_free(sm_head);
-
-		if (error < 0)
-			return error;
-
-		/* perform index-to-workdir diff on submodule */
-
-		error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
-
-		if (!error) {
-			size_t untracked =
-				git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
-
-			if (untracked > 0)
-				*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
-
-			if (git_diff_num_deltas(diff) != untracked)
-				*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
-
-			git_diff_list_free(diff);
-			diff = NULL;
-		}
-
-		git_repository_free(sm_repo);
 	}
 
-	return error;
+	/* perform index-to-workdir diff on submodule */
+	if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
+		giterr_clear();
+	else {
+		size_t untracked =
+			git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
+
+		if (untracked > 0)
+			*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
+
+		if (git_diff_num_deltas(diff) != untracked)
+			*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
+
+		git_diff_free(diff);
+		diff = NULL;
+	}
 }
diff --git a/src/submodule.h b/src/submodule.h
index ba8e251..b059375 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -7,6 +7,10 @@
 #ifndef INCLUDE_submodule_h__
 #define INCLUDE_submodule_h__
 
+#include "git2/submodule.h"
+#include "git2/repository.h"
+#include "fileops.h"
+
 /* Notes:
  *
  * Submodule information can be in four places: the index, the config files
@@ -44,44 +48,51 @@
  * an entry for every submodule found in the HEAD and index, and for every
  * submodule described in .gitmodules.  The fields are as follows:
  *
- * - `owner` is the git_repository containing this submodule
+ * - `rc` tracks the refcount of how many hash table entries in the
+ *   git_submodule_cache there are for this submodule.  It only comes into
+ *   play if the name and path of the submodule differ.
+ *
  * - `name` is the name of the submodule from .gitmodules.
  * - `path` is the path to the submodule from the repo root.  It is almost
  *    always the same as `name`.
  * - `url` is the url for the submodule.
- * - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
- * - `index_oid` is the SHA1 for the submodule recorded in the index.
- * - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
  * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `update_default` is the update value from the config
  * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `ignore_default` is the ignore value from the config
  * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
- * - `refcount` tracks how many hashmap entries there are for this submodule.
- *   It only comes into play if the name and path of the submodule differ.
- * - `flags` is for internal use, tracking where this submodule has been
- *   found (head, index, config, workdir) and other misc info about it.
+ *
+ * - `repo` is the parent repository that contains this submodule.
+ * - `flags` after for internal use, tracking where this submodule has been
+ *   found (head, index, config, workdir) and known status info, etc.
+ * - `head_oid` is the SHA1 for the submodule path in the repo HEAD.
+ * - `index_oid` is the SHA1 for the submodule recorded in the index.
+ * - `wd_oid` is the SHA1 for the HEAD of the checked out submodule.
  *
  * If the submodule has been added to .gitmodules but not yet git added,
- * then the `index_oid` will be valid and zero.  If the submodule has been
- * deleted, but the delete has not been committed yet, then the `index_oid`
- * will be set, but the `url` will be NULL.
+ * then the `index_oid` will be zero but still marked valid.  If the
+ * submodule has been deleted, but the delete has not been committed yet,
+ * then the `index_oid` will be set, but the `url` will be NULL.
  */
 struct git_submodule {
-	git_repository *owner;
-	char *name;
-	char *path; /* important: may point to same string data as "name" */
-	char *url;
-	uint32_t flags;
-	git_oid head_oid;
-	git_oid index_oid;
-	git_oid wd_oid;
+	git_refcount rc;
+
 	/* information from config */
+	char *name;
+	char *path; /* important: may just point to "name" string */
+	char *url;
 	git_submodule_update_t update;
 	git_submodule_update_t update_default;
 	git_submodule_ignore_t ignore;
 	git_submodule_ignore_t ignore_default;
 	int fetch_recurse;
+
 	/* internal information */
-	int refcount;
+	git_repository *repo;
+	uint32_t flags;
+	git_oid head_oid;
+	git_oid index_oid;
+	git_oid wd_oid;
 };
 
 /* Additional flags on top of public GIT_SUBMODULE_STATUS values */
@@ -99,4 +110,29 @@
 #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
 	((S) & ~(0xFFFFFFFFu << 20))
 
+/* Internal status fn returns status and optionally the various OIDs */
+extern int git_submodule__status(
+	unsigned int *out_status,
+	git_oid *out_head_id,
+	git_oid *out_index_id,
+	git_oid *out_wd_id,
+	git_submodule *sm,
+	git_submodule_ignore_t ign);
+
+/* Open submodule repository as bare repo for quick HEAD check, etc. */
+extern int git_submodule_open_bare(
+	git_repository **repo,
+	git_submodule *submodule);
+
+/* Release reference to submodule object - not currently for external use */
+extern void git_submodule_free(git_submodule *sm);
+
+extern int git_submodule_parse_ignore(
+	git_submodule_ignore_t *out, const char *value);
+extern int git_submodule_parse_update(
+	git_submodule_update_t *out, const char *value);
+
+extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t);
+extern const char *git_submodule_update_to_str(git_submodule_update_t);
+
 #endif
diff --git a/src/tag.c b/src/tag.c
index 71f4c1e..31a3c8b 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -366,10 +366,10 @@
 	if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
 		return -1;
 
-	stream->write(stream, buffer, strlen(buffer));
+	git_odb_stream_write(stream, buffer, strlen(buffer));
 
-	error = stream->finalize_write(oid, stream);
-	stream->free(stream);
+	error = git_odb_stream_finalize_write(oid, stream);
+	git_odb_stream_free(stream);
 
 	if (error < 0) {
 		git_buf_free(&ref_name);
diff --git a/src/thread-utils.h b/src/thread-utils.h
index 8314818..914c135 100644
--- a/src/thread-utils.h
+++ b/src/thread-utils.h
@@ -38,15 +38,11 @@
 
 #endif
 
-GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
-{
-	a->val = val;
-}
-
 #ifdef GIT_THREADS
 
 #define git_thread pthread_t
-#define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg)
+#define git_thread_create(thread, attr, start_routine, arg) \
+	pthread_create(thread, attr, start_routine, arg)
 #define git_thread_kill(thread) pthread_cancel(thread)
 #define git_thread_exit(status)	pthread_exit(status)
 #define git_thread_join(id, status) pthread_join(id, status)
@@ -66,6 +62,41 @@
 #define git_cond_signal(c)	pthread_cond_signal(c)
 #define git_cond_broadcast(c)	pthread_cond_broadcast(c)
 
+/* Pthread (-ish) rwlock
+ *
+ * This differs from normal pthreads rwlocks in two ways:
+ * 1. Separate APIs for releasing read locks and write locks (as
+ *    opposed to the pure POSIX API which only has one unlock fn)
+ * 2. You should not use recursive read locks (i.e. grabbing a read
+ *    lock in a thread that already holds a read lock) because the
+ *    Windows implementation doesn't support it
+ */
+#define git_rwlock pthread_rwlock_t
+#define git_rwlock_init(a)		pthread_rwlock_init(a, NULL)
+#define git_rwlock_rdlock(a)	pthread_rwlock_rdlock(a)
+#define git_rwlock_rdunlock(a)	pthread_rwlock_rdunlock(a)
+#define git_rwlock_wrlock(a)	pthread_rwlock_wrlock(a)
+#define git_rwlock_wrunlock(a)	pthread_rwlock_wrunlock(a)
+#define git_rwlock_free(a)		pthread_rwlock_destroy(a)
+#define GIT_RWLOCK_STATIC_INIT	PTHREAD_RWLOCK_INITIALIZER
+
+#ifndef GIT_WIN32
+#define pthread_rwlock_rdunlock pthread_rwlock_unlock
+#define pthread_rwlock_wrunlock pthread_rwlock_unlock
+#endif
+
+
+GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
+{
+#if defined(GIT_WIN32)
+	InterlockedExchange(&a->val, (LONG)val);
+#elif defined(__GNUC__)
+	__sync_lock_test_and_set(&a->val, val);
+#else
+#	error "Unsupported architecture for atomic operations"
+#endif
+}
+
 GIT_INLINE(int) git_atomic_inc(git_atomic *a)
 {
 #if defined(GIT_WIN32)
@@ -100,11 +131,11 @@
 }
 
 GIT_INLINE(void *) git___compare_and_swap(
-	volatile void **ptr, void *oldval, void *newval)
+	void * volatile *ptr, void *oldval, void *newval)
 {
 	volatile void *foundval;
 #if defined(GIT_WIN32)
-	foundval = InterlockedCompareExchangePointer(ptr, newval, oldval);
+	foundval = InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
 #elif defined(__GNUC__)
 	foundval = __sync_val_compare_and_swap(ptr, oldval, newval);
 #else
@@ -113,6 +144,16 @@
 	return (foundval == oldval) ? oldval : newval;
 }
 
+GIT_INLINE(volatile void *) git___swap(
+	void * volatile *ptr, void *newval)
+{
+#if defined(GIT_WIN32)
+	return InterlockedExchangePointer(ptr, newval);
+#else
+	return __sync_lock_test_and_set(ptr, newval);
+#endif
+}
+
 #ifdef GIT_ARCH_64
 
 GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
@@ -131,7 +172,7 @@
 #else
 
 #define git_thread unsigned int
-#define git_thread_create(thread, attr, start_routine, arg) (void)0
+#define git_thread_create(thread, attr, start_routine, arg) 0
 #define git_thread_kill(thread) (void)0
 #define git_thread_exit(status) (void)0
 #define git_thread_join(id, status) (void)0
@@ -151,6 +192,22 @@
 #define git_cond_signal(c) (void)0
 #define git_cond_broadcast(c) (void)0
 
+/* Pthreads rwlock */
+#define git_rwlock unsigned int
+#define git_rwlock_init(a)		0
+#define git_rwlock_rdlock(a)	0
+#define git_rwlock_rdunlock(a)	(void)0
+#define git_rwlock_wrlock(a)	0
+#define git_rwlock_wrunlock(a)	(void)0
+#define git_rwlock_free(a)		(void)0
+#define GIT_RWLOCK_STATIC_INIT	0
+
+
+GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
+{
+	a->val = val;
+}
+
 GIT_INLINE(int) git_atomic_inc(git_atomic *a)
 {
 	return ++a->val;
@@ -168,7 +225,7 @@
 }
 
 GIT_INLINE(void *) git___compare_and_swap(
-	volatile void **ptr, void *oldval, void *newval)
+	void * volatile *ptr, void *oldval, void *newval)
 {
 	if (*ptr == oldval)
 		*ptr = newval;
@@ -177,6 +234,14 @@
 	return oldval;
 }
 
+GIT_INLINE(volatile void *) git___swap(
+	void * volatile *ptr, void *newval)
+{
+	volatile void *old = *ptr;
+	*ptr = newval;
+	return old;
+}
+
 #ifdef GIT_ARCH_64
 
 GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
@@ -189,13 +254,18 @@
 
 #endif
 
+GIT_INLINE(int) git_atomic_get(git_atomic *a)
+{
+	return (int)a->val;
+}
+
 /* Atomically replace oldval with newval
  * @return oldval if it was replaced or newval if it was not
  */
 #define git__compare_and_swap(P,O,N) \
-	git___compare_and_swap((volatile void **)P, O, N)
+	git___compare_and_swap((void * volatile *)P, O, N)
 
-#define git__swap(ptr, val) git__compare_and_swap(&ptr, ptr, val)
+#define git__swap(ptr, val) (void *)git___swap((void * volatile *)&ptr, val)
 
 extern int git_online_cpus(void);
 
diff --git a/src/transport.c b/src/transport.c
index 37c244c..ff926b1 100644
--- a/src/transport.c
+++ b/src/transport.c
@@ -42,6 +42,8 @@
 	{NULL, 0, 0}
 };
 
+static git_vector additional_transports = GIT_VECTOR_INIT;
+
 #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
 
 static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
@@ -61,6 +63,14 @@
 			definition = definition_iter;
 	}
 
+	git_vector_foreach(&additional_transports, i, definition_iter) {
+		if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
+			continue;
+
+		if (definition_iter->priority > priority)
+			definition = definition_iter;
+	}
+
 #ifdef GIT_WIN32
 	/* On Windows, it might not be possible to discern between absolute local
 	 * and ssh paths - first check if this is a valid local path that points
@@ -73,7 +83,7 @@
 	/* It could be a SSH remote path. Check to see if there's a :
 	 * SSH is an unsupported transport mechanism in this version of libgit2 */
 	if (!definition && strrchr(url, ':'))
-		definition = &dummy_transport_definition; 
+		definition = &dummy_transport_definition;
 #else
 	/* For other systems, perform the SSH check first, to avoid going to the
 	 * filesystem if it is not necessary */
@@ -97,7 +107,7 @@
 
 	*callback = definition->fn;
 	*param = definition->param;
-	
+
 	return 0;
 }
 
@@ -135,6 +145,62 @@
 	return 0;
 }
 
+int git_transport_register(
+	const char *prefix,
+	unsigned priority,
+	git_transport_cb cb,
+	void *param)
+{
+	transport_definition *d;
+
+	d = git__calloc(sizeof(transport_definition), 1);
+	GITERR_CHECK_ALLOC(d);
+
+	d->prefix = git__strdup(prefix);
+
+	if (!d->prefix)
+		goto on_error;
+
+	d->priority = priority;
+	d->fn = cb;
+	d->param = param;
+
+	if (git_vector_insert(&additional_transports, d) < 0)
+		goto on_error;
+
+	return 0;
+
+on_error:
+	git__free(d->prefix);
+	git__free(d);
+	return -1;
+}
+
+int git_transport_unregister(
+	const char *prefix,
+	unsigned priority)
+{
+	transport_definition *d;
+	unsigned i;
+
+	git_vector_foreach(&additional_transports, i, d) {
+		if (d->priority == priority && !strcasecmp(d->prefix, prefix)) {
+			if (git_vector_remove(&additional_transports, i) < 0)
+				return -1;
+
+			git__free(d->prefix);
+			git__free(d);
+
+			if (!additional_transports.length)
+				git_vector_free(&additional_transports);
+
+			return 0;
+		}
+	}
+
+	return GIT_ENOTFOUND;
+}
+
 /* from remote.h */
 int git_remote_valid_url(const char *url)
 {
diff --git a/src/transports/cred.c b/src/transports/cred.c
index ba5de6e..05d2c8d 100644
--- a/src/transports/cred.c
+++ b/src/transports/cred.c
@@ -9,19 +9,49 @@
 #include "smart.h"
 #include "git2/cred_helpers.h"
 
+int git_cred_has_username(git_cred *cred)
+{
+	int ret = 0;
+
+	switch (cred->credtype) {
+	case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+		git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+		ret = !!c->username;
+		break;
+	}
+	case GIT_CREDTYPE_SSH_KEY: {
+		git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
+		ret = !!c->username;
+		break;
+	}
+	case GIT_CREDTYPE_SSH_CUSTOM: {
+		git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
+		ret = !!c->username;
+		break;
+	}
+	case GIT_CREDTYPE_DEFAULT: {
+		ret = 0;
+		break;
+	}
+	}
+
+	return ret;
+}
+
 static void plaintext_free(struct git_cred *cred)
 {
 	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
-	size_t pass_len = strlen(c->password);
 
 	git__free(c->username);
 
 	/* Zero the memory which previously held the password */
-	git__memzero(c->password, pass_len);
-	git__free(c->password);
+	if (c->password) {
+		size_t pass_len = strlen(c->password);
+		git__memzero(c->password, pass_len);
+		git__free(c->password);
+	}
 
-	memset(c, 0, sizeof(*c));
-
+	git__memzero(c, sizeof(*c));
 	git__free(c);
 }
 
@@ -32,8 +62,7 @@
 {
 	git_cred_userpass_plaintext *c;
 
-	if (!cred)
-		return -1;
+	assert(cred && username && password);
 
 	c = git__malloc(sizeof(git_cred_userpass_plaintext));
 	GITERR_CHECK_ALLOC(c);
@@ -59,53 +88,74 @@
 	return 0;
 }
 
-#ifdef GIT_SSH
-static void ssh_keyfile_passphrase_free(struct git_cred *cred)
+static void ssh_key_free(struct git_cred *cred)
 {
-	git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
-	size_t pass_len = strlen(c->passphrase);
+	git_cred_ssh_key *c =
+		(git_cred_ssh_key *)cred;
 
-    if (c->publickey) {
-        git__free(c->publickey);
-    }
-    
+	git__free(c->username);
+	git__free(c->publickey);
 	git__free(c->privatekey);
 
-    if (c->passphrase) {
-        /* Zero the memory which previously held the passphrase */
-        git__memzero(c->passphrase, pass_len);
-        git__free(c->passphrase);
-    }
+	if (c->passphrase) {
+		/* Zero the memory which previously held the passphrase */
+		size_t pass_len = strlen(c->passphrase);
+		git__memzero(c->passphrase, pass_len);
+		git__free(c->passphrase);
+	}
 
-	memset(c, 0, sizeof(*c));
+	git__memzero(c, sizeof(*c));
+	git__free(c);
+}
+
+static void ssh_custom_free(struct git_cred *cred)
+{
+	git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
+
+	git__free(c->username);
+	git__free(c->publickey);
+
+	git__memzero(c, sizeof(*c));
+	git__free(c);
+}
+
+static void default_free(struct git_cred *cred)
+{
+	git_cred_default *c = (git_cred_default *)cred;
 
 	git__free(c);
 }
 
-int git_cred_ssh_keyfile_passphrase_new(
+int git_cred_ssh_key_new(
 	git_cred **cred,
+	const char *username,
 	const char *publickey,
 	const char *privatekey,
 	const char *passphrase)
 {
-	git_cred_ssh_keyfile_passphrase *c;
+	git_cred_ssh_key *c;
 
 	assert(cred && privatekey);
 
-	c = git__calloc(1, sizeof(git_cred_ssh_keyfile_passphrase));
+	c = git__calloc(1, sizeof(git_cred_ssh_key));
 	GITERR_CHECK_ALLOC(c);
 
-	c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
-	c->parent.free = ssh_keyfile_passphrase_free;
-    
-    c->privatekey = git__strdup(privatekey);
+	c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
+	c->parent.free = ssh_key_free;
+
+	if (username) {
+		c->username = git__strdup(username);
+		GITERR_CHECK_ALLOC(c->username);
+	}
+
+	c->privatekey = git__strdup(privatekey);
 	GITERR_CHECK_ALLOC(c->privatekey);
-    
-    if (publickey) {
+
+	if (publickey) {
 		c->publickey = git__strdup(publickey);
 		GITERR_CHECK_ALLOC(c->publickey);
 	}
-	
+
 	if (passphrase) {
 		c->passphrase = git__strdup(passphrase);
 		GITERR_CHECK_ALLOC(c->passphrase);
@@ -115,48 +165,56 @@
 	return 0;
 }
 
-static void ssh_publickey_free(struct git_cred *cred)
-{
-	git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
-
-    git__free(c->publickey);
-
-    c->sign_callback = NULL;
-    c->sign_data = NULL;
-    
-	memset(c, 0, sizeof(*c));
-
-	git__free(c);
-}
-
-int git_cred_ssh_publickey_new(
+int git_cred_ssh_custom_new(
 	git_cred **cred,
+	const char *username,
 	const char *publickey,
-    size_t publickey_len,
-	LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*sign_callback)),
-    void *sign_data)
+	size_t publickey_len,
+	git_cred_sign_callback sign_callback,
+	void *sign_data)
 {
-	git_cred_ssh_publickey *c;
+	git_cred_ssh_custom *c;
 
-	if (!cred)
-		return -1;
+	assert(cred);
 
-	c = git__malloc(sizeof(git_cred_ssh_publickey));
+	c = git__calloc(1, sizeof(git_cred_ssh_custom));
 	GITERR_CHECK_ALLOC(c);
 
-	c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
-	c->parent.free = ssh_publickey_free;
-    
-    c->publickey = git__malloc(publickey_len);
-	GITERR_CHECK_ALLOC(c->publickey);
-	
-    memcpy(c->publickey, publickey, publickey_len);
-    
-    c->publickey_len = publickey_len;
-    c->sign_callback = sign_callback;
-    c->sign_data = sign_data;
+	c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM;
+	c->parent.free = ssh_custom_free;
+
+	if (username) {
+		c->username = git__strdup(username);
+		GITERR_CHECK_ALLOC(c->username);
+	}
+
+	if (publickey_len > 0) {
+		c->publickey = git__malloc(publickey_len);
+		GITERR_CHECK_ALLOC(c->publickey);
+
+		memcpy(c->publickey, publickey, publickey_len);
+	}
+
+	c->publickey_len = publickey_len;
+	c->sign_callback = sign_callback;
+	c->sign_data = sign_data;
 
 	*cred = &c->parent;
 	return 0;
 }
-#endif
+
+int git_cred_default_new(git_cred **cred)
+{
+	git_cred_default *c;
+
+	assert(cred);
+
+	c = git__calloc(1, sizeof(git_cred_default));
+	GITERR_CHECK_ALLOC(c);
+
+	c->credtype = GIT_CREDTYPE_DEFAULT;
+	c->free = default_free;
+
+	*cred = c;
+	return 0;
+}
diff --git a/src/transports/git.c b/src/transports/git.c
index 3a0b863..5dcd4ef 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -179,39 +179,33 @@
 	const char *url,
 	git_smart_subtransport_stream **stream)
 {
-	char *host, *port, *user=NULL, *pass=NULL;
+	char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+	const char *stream_url = url;
 	git_stream *s;
+	int error = -1;
 
 	*stream = NULL;
-
 	if (!git__prefixcmp(url, prefix_git))
-		url += strlen(prefix_git);
+		stream_url += strlen(prefix_git);
 
-	if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
+	if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0)
 		return -1;
 
 	s = (git_stream *)*stream;
 
-	if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
-		goto on_error;
+	if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+		if (!(error = gitno_connect(&s->socket, host, port, 0)))
+			t->current_stream = s;
 
-	if (gitno_connect(&s->socket, host, port, 0) < 0)
-		goto on_error;
-
-	t->current_stream = s;
-	git__free(host);
-	git__free(port);
-	git__free(user);
-	git__free(pass);
-	return 0;
-
-on_error:
-	if (*stream)
+		git__free(host);
+		git__free(port);
+		git__free(path);
+		git__free(user);
+		git__free(pass);
+	} else if (*stream)
 		git_stream_free(*stream);
 
-	git__free(host);
-	git__free(port);
-	return -1;
+	return error;
 }
 
 static int _git_uploadpack(
@@ -235,39 +229,33 @@
 	const char *url,
 	git_smart_subtransport_stream **stream)
 {
-	char *host, *port, *user=NULL, *pass=NULL;
+	char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
+	const char *stream_url = url;
 	git_stream *s;
+	int error;
 
 	*stream = NULL;
-
 	if (!git__prefixcmp(url, prefix_git))
-		url += strlen(prefix_git);
+		stream_url += strlen(prefix_git);
 
-	if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0)
+	if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0)
 		return -1;
 
 	s = (git_stream *)*stream;
 
-	if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
-		goto on_error;
+	if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) {
+		if (!(error = gitno_connect(&s->socket, host, port, 0)))
+			t->current_stream = s;
 
-	if (gitno_connect(&s->socket, host, port, 0) < 0)
-		goto on_error;
-
-	t->current_stream = s;
-	git__free(host);
-	git__free(port);
-	git__free(user);
-	git__free(pass);
-	return 0;
-
-on_error:
-	if (*stream)
+		git__free(host);
+		git__free(port);
+		git__free(path);
+		git__free(user);
+		git__free(pass);
+	} else if (*stream)
 		git_stream_free(*stream);
 
-	git__free(host);
-	git__free(port);
-	return -1;
+	return error;
 }
 
 static int _git_receivepack(
diff --git a/src/transports/http.c b/src/transports/http.c
index eca06ea..ace0d97 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -12,8 +12,6 @@
 #include "netops.h"
 #include "smart.h"
 
-static const char *prefix_http = "http://";
-static const char *prefix_https = "https://";
 static const char *upload_pack_service = "upload-pack";
 static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
 static const char *upload_pack_service_url = "/git-upload-pack";
@@ -59,16 +57,11 @@
 	git_smart_subtransport parent;
 	transport_smart *owner;
 	gitno_socket socket;
-	const char *path;
-	char *host;
-	char *port;
-	char *user_from_url;
-	char *pass_from_url;
+	gitno_connection_data connection_data;
 	git_cred *cred;
 	git_cred *url_cred;
 	http_authmechanism_t auth_mechanism;
-	unsigned connected : 1,
-		use_ssl : 1;
+	bool connected;
 
 	/* Parser structures */
 	http_parser parser;
@@ -125,18 +118,12 @@
 	size_t content_length)
 {
 	http_subtransport *t = OWNING_SUBTRANSPORT(s);
+	const char *path = t->connection_data.path ? t->connection_data.path : "/";
 
-	if (!t->path)
-		t->path = "/";
-
-	/* If we were redirected, make sure to respect that here */
-	if (s->redirect_url)
-		git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
-	else
-		git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+	git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
 
 	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
-	git_buf_printf(buf, "Host: %s\r\n", t->host);
+	git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
 
 	if (s->chunked || content_length > 0) {
 		git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
@@ -156,9 +143,9 @@
 		return -1;
 
 	/* Use url-parsed basic auth if username and password are both provided */
-	if (!t->cred && t->user_from_url && t->pass_from_url) {
-		if (!t->url_cred &&
-			 git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+	if (!t->cred && t->connection_data.user && t->connection_data.pass) {
+		if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred,
+					t->connection_data.user, t->connection_data.pass) < 0)
 			return -1;
 		if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
 	}
@@ -209,7 +196,7 @@
 	}
 	else if (!strcasecmp("Location", git_buf_cstr(name))) {
 		if (!t->location) {
-			t->location= git__strdup(git_buf_cstr(value));
+			t->location = git__strdup(git_buf_cstr(value));
 			GITERR_CHECK_ALLOC(t->location);
 		}
 	}
@@ -283,7 +270,7 @@
 
 			if (t->owner->cred_acquire_cb(&t->cred,
 					t->owner->url,
-					t->user_from_url,
+					t->connection_data.user,
 					allowed_types,
 					t->owner->cred_acquire_payload) < 0)
 				return PARSE_ERROR_GENERIC;
@@ -298,20 +285,18 @@
 	/* Check for a redirect.
 	 * Right now we only permit a redirect to the same hostname. */
 	if ((parser->status_code == 301 ||
-		 parser->status_code == 302 ||
-		 (parser->status_code == 303 && get_verb == s->verb) ||
-		 parser->status_code == 307) &&
-		t->location) {
+	     parser->status_code == 302 ||
+	     (parser->status_code == 303 && get_verb == s->verb) ||
+	     parser->status_code == 307) &&
+	    t->location) {
 
 		if (s->redirect_count >= 7) {
 			giterr_set(GITERR_NET, "Too many redirects");
 			return t->parse_error = PARSE_ERROR_GENERIC;
 		}
 
-		if (t->location[0] != '/') {
-			giterr_set(GITERR_NET, "Only relative redirects are supported");
+		if (gitno_connection_data_from_url(&t->connection_data, t->location, s->service_url) < 0)
 			return t->parse_error = PARSE_ERROR_GENERIC;
-		}
 
 		/* Set the redirect URL on the stream. This is a transfer of
 		 * ownership of the memory. */
@@ -468,7 +453,7 @@
 	if (t->socket.socket)
 		gitno_close(&t->socket);
 
-	if (t->use_ssl) {
+	if (t->connection_data.use_ssl) {
 		int tflags;
 
 		if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
@@ -480,7 +465,7 @@
 			flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
 	}
 
-	if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
+	if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0)
 		return -1;
 
 	t->connected = 1;
@@ -822,50 +807,30 @@
 	git_smart_service_t action)
 {
 	http_subtransport *t = (http_subtransport *)subtransport;
-	const char *default_port = NULL;
 	int ret;
 
 	if (!stream)
 		return -1;
 
-	if (!t->host || !t->port || !t->path) {
-		if (!git__prefixcmp(url, prefix_http)) {
-			url = url + strlen(prefix_http);
-			default_port = "80";
-		}
-
-		if (!git__prefixcmp(url, prefix_https)) {
-			url += strlen(prefix_https);
-			default_port = "443";
-			t->use_ssl = 1;
-		}
-
-		if (!default_port)
-			return -1;
-
-		if ((ret = gitno_extract_url_parts(&t->host, &t->port,
-						&t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
-			return ret;
-
-		t->path = strchr(url, '/');
-	}
+	if ((!t->connection_data.host || !t->connection_data.port || !t->connection_data.path) &&
+		 (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0)
+		return ret;
 
 	if (http_connect(t) < 0)
 		return -1;
 
-	switch (action)
-	{
-		case GIT_SERVICE_UPLOADPACK_LS:
-			return http_uploadpack_ls(t, stream);
+	switch (action) {
+	case GIT_SERVICE_UPLOADPACK_LS:
+		return http_uploadpack_ls(t, stream);
 
-		case GIT_SERVICE_UPLOADPACK:
-			return http_uploadpack(t, stream);
+	case GIT_SERVICE_UPLOADPACK:
+		return http_uploadpack(t, stream);
 
-		case GIT_SERVICE_RECEIVEPACK_LS:
-			return http_receivepack_ls(t, stream);
+	case GIT_SERVICE_RECEIVEPACK_LS:
+		return http_receivepack_ls(t, stream);
 
-		case GIT_SERVICE_RECEIVEPACK:
-			return http_receivepack(t, stream);
+	case GIT_SERVICE_RECEIVEPACK:
+		return http_receivepack(t, stream);
 	}
 
 	*stream = NULL;
@@ -893,25 +858,7 @@
 		t->url_cred = NULL;
 	}
 
-	if (t->host) {
-		git__free(t->host);
-		t->host = NULL;
-	}
-
-	if (t->port) {
-		git__free(t->port);
-		t->port = NULL;
-	}
-
-	if (t->user_from_url) {
-		git__free(t->user_from_url);
-		t->user_from_url = NULL;
-	}
-
-	if (t->pass_from_url) {
-		git__free(t->pass_from_url);
-		t->pass_from_url = NULL;
-	}
+	gitno_connection_data_free_ptrs(&t->connection_data);
 
 	return 0;
 }
diff --git a/src/transports/local.c b/src/transports/local.c
index 5500609..4502f02 100644
--- a/src/transports/local.c
+++ b/src/transports/local.c
@@ -119,15 +119,24 @@
 
 static int store_refs(transport_local *t)
 {
-	unsigned int i;
+	size_t i;
+	git_remote_head *head;
 	git_strarray ref_names = {0};
 
 	assert(t);
 
-	if (git_reference_list(&ref_names, t->repo) < 0 ||
-		git_vector_init(&t->refs, ref_names.count, NULL) < 0)
+	if (git_reference_list(&ref_names, t->repo) < 0)
 		goto on_error;
 
+	/* Clear all heads we might have fetched in a previous connect */
+	git_vector_foreach(&t->refs, i, head) {
+		git__free(head->name);
+		git__free(head);
+	}
+
+	/* Clear the vector so we can reuse it */
+	git_vector_clear(&t->refs);
+
 	/* Sort the references first */
 	git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
 
@@ -204,21 +213,17 @@
 	return 0;
 }
 
-static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
 {
 	transport_local *t = (transport_local *)transport;
-	unsigned int i;
-	git_remote_head *head = NULL;
 
 	if (!t->have_refs) {
 		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
 		return -1;
 	}
 
-	git_vector_foreach(&t->refs, i, head) {
-		if (list_cb(head, payload))
-			return GIT_EUSER;
-	}
+	*out = (const git_remote_head **) t->refs.contents;
+	*size = t->refs.length;
 
 	return 0;
 }
@@ -278,9 +283,9 @@
 		odb_obj_size, odb_obj_type)) < 0)
 		goto on_error;
 
-	if (odb_stream->write(odb_stream, (char *)git_odb_object_data(odb_obj),
+	if (git_odb_stream_write(odb_stream, (char *)git_odb_object_data(odb_obj),
 		odb_obj_size) < 0 ||
-		odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
+		git_odb_stream_finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
 		error = -1;
 	} else if (git_oid__cmp(&obj->id, &remote_odb_obj_oid) != 0) {
 		giterr_set(GITERR_ODB, "Error when writing object to remote odb "
@@ -289,7 +294,7 @@
 		error = -1;
 	}
 
-	odb_stream->free(odb_stream);
+	git_odb_stream_free(odb_stream);
 
 on_error:
 	git_odb_object_free(odb_obj);
@@ -352,7 +357,8 @@
 	   non-bare repo push support would require checking configs to see if
 	   we should override the default 'don't let this happen' behavior */
 	if (!remote_repo->is_bare) {
-		error = -1;
+		error = GIT_EBAREREPO;
+		giterr_set(GITERR_INVALID, "Local push doesn't (yet) support pushing to non-bare repos.");
 		goto on_error;
 	}
 
@@ -424,7 +430,7 @@
 
 		if (!url || t->parent.close(&t->parent) < 0 ||
 			t->parent.connect(&t->parent, url,
-			push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
+			push->remote->callbacks.credentials, NULL, GIT_DIRECTION_PUSH, flags))
 			goto on_error;
 	}
 
@@ -449,7 +455,7 @@
 	foreach_data *data = (foreach_data*)payload;
 
 	data->stats->received_bytes += len;
-	return data->writepack->add(data->writepack, buf, len, data->stats);
+	return data->writepack->append(data->writepack, buf, len, data->stats);
 }
 
 static int local_download_pack(
@@ -571,8 +577,6 @@
 static int local_close(git_transport *transport)
 {
 	transport_local *t = (transport_local *)transport;
-	size_t i;
-	git_remote_head *head;
 
 	t->connected = 0;
 
@@ -586,19 +590,21 @@
 		t->url = NULL;
 	}
 
-	git_vector_foreach(&t->refs, i, head) {
-		git__free(head->name);
-		git__free(head);
-	}
-
-	git_vector_free(&t->refs);
-
 	return 0;
 }
 
 static void local_free(git_transport *transport)
 {
 	transport_local *t = (transport_local *)transport;
+	size_t i;
+	git_remote_head *head;
+
+	git_vector_foreach(&t->refs, i, head) {
+		git__free(head->name);
+		git__free(head);
+	}
+
+	git_vector_free(&t->refs);
 
 	/* Close the transport, if it's still open. */
 	local_close(transport);
@@ -632,6 +638,7 @@
 	t->parent.read_flags = local_read_flags;
 	t->parent.cancel = local_cancel;
 
+	git_vector_init(&t->refs, 0, NULL);
 	t->owner = owner;
 
 	*out = (git_transport *) t;
diff --git a/src/transports/smart.c b/src/transports/smart.c
index 416eb22..5242beb 100644
--- a/src/transports/smart.c
+++ b/src/transports/smart.c
@@ -23,8 +23,13 @@
 
 	buf->offset += bytes_read;
 
-	if (t->packetsize_cb)
-		t->packetsize_cb(bytes_read, t->packetsize_payload);
+	if (t->packetsize_cb && !t->cancelled.val)
+		if (t->packetsize_cb(bytes_read, t->packetsize_payload)) {
+			git_atomic_set(&t->cancelled, 1);
+
+			giterr_clear();
+			return GIT_EUSER;
+		}
 
 	return (int)(buf->offset - old_len);
 }
@@ -58,6 +63,24 @@
 	return 0;
 }
 
+int git_smart__update_heads(transport_smart *t)
+{
+	size_t i;
+	git_pkt *pkt;
+
+	git_vector_clear(&t->heads);
+	git_vector_foreach(&t->refs, i, pkt) {
+		git_pkt_ref *ref = (git_pkt_ref *) pkt;
+		if (pkt->type != GIT_PKT_REF)
+			continue;
+
+		if (git_vector_insert(&t->heads, &ref->head) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
 static int git_smart__connect(
 	git_transport *transport,
 	const char *url,
@@ -135,6 +158,9 @@
 		git_pkt_free((git_pkt *)first);
 	}
 
+	/* Keep a list of heads for _ls */
+	git_smart__update_heads(t);
+
 	if (t->rpc && git_smart__reset_stream(t, false) < 0)
 		return -1;
 
@@ -144,28 +170,17 @@
 	return 0;
 }
 
-static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
 {
 	transport_smart *t = (transport_smart *)transport;
-	unsigned int i;
-	git_pkt *p = NULL;
 
 	if (!t->have_refs) {
 		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
 		return -1;
 	}
 
-	git_vector_foreach(&t->refs, i, p) {
-		git_pkt_ref *pkt = NULL;
-
-		if (p->type != GIT_PKT_REF)
-			continue;
-
-		pkt = (git_pkt_ref *)p;
-
-		if (list_cb(&pkt->head, payload))
-			return GIT_EUSER;
-	}
+	*out = (const git_remote_head **) t->heads.contents;
+	*size = t->heads.length;
 
 	return 0;
 }
@@ -288,6 +303,7 @@
 	/* Free the subtransport */
 	t->wrapped->free(t->wrapped);
 
+	git_vector_free(&t->heads);
 	git_vector_foreach(refs, i, p)
 		git_pkt_free(p);
 
@@ -335,6 +351,11 @@
 		return -1;
 	}
 
+	if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) {
+		git__free(t);
+		return -1;
+	}
+
 	if (definition->callback(&t->wrapped, &t->parent) < 0) {
 		git__free(t);
 		return -1;
diff --git a/src/transports/smart.h b/src/transports/smart.h
index c52401a..32f0be7 100644
--- a/src/transports/smart.h
+++ b/src/transports/smart.h
@@ -16,11 +16,13 @@
 
 #define GIT_CAP_OFS_DELTA "ofs-delta"
 #define GIT_CAP_MULTI_ACK "multi_ack"
+#define GIT_CAP_MULTI_ACK_DETAILED "multi_ack_detailed"
 #define GIT_CAP_SIDE_BAND "side-band"
 #define GIT_CAP_SIDE_BAND_64K "side-band-64k"
 #define GIT_CAP_INCLUDE_TAG "include-tag"
 #define GIT_CAP_DELETE_REFS "delete-refs"
 #define GIT_CAP_REPORT_STATUS "report-status"
+#define GIT_CAP_THIN_PACK "thin-pack"
 
 enum git_pkt_type {
 	GIT_PKT_CMD,
@@ -39,7 +41,7 @@
 	GIT_PKT_UNPACK,
 };
 
-/* Used for multi-ack */
+/* Used for multi_ack and mutli_ack_detailed */
 enum git_ack_status {
 	GIT_ACK_NONE,
 	GIT_ACK_CONTINUE,
@@ -112,14 +114,16 @@
 	int common:1,
 		ofs_delta:1,
 		multi_ack: 1,
+		multi_ack_detailed: 1,
 		side_band:1,
 		side_band_64k:1,
 		include_tag:1,
 		delete_refs:1,
-		report_status:1;
+		report_status:1,
+		thin_pack:1;
 } transport_smart_caps;
 
-typedef void (*packetsize_cb)(size_t received, void *payload);
+typedef int (*packetsize_cb)(size_t received, void *payload);
 
 typedef struct {
 	git_transport parent;
@@ -136,6 +140,7 @@
 	git_smart_subtransport_stream *current_stream;
 	transport_smart_caps caps;
 	git_vector refs;
+	git_vector heads;
 	git_vector common;
 	git_atomic cancelled;
 	packetsize_cb packetsize_cb;
@@ -169,6 +174,8 @@
 int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
 int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
 
+int git_smart__update_heads(transport_smart *t);
+
 /* smart_pkt.c */
 int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
 int git_pkt_buffer_flush(git_buf *buf);
diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c
index 99da375..2bb09c7 100644
--- a/src/transports/smart_pkt.c
+++ b/src/transports/smart_pkt.c
@@ -39,7 +39,7 @@
 	return 0;
 }
 
-/* the rest of the line will be useful for multi_ack */
+/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
 static int ack_pkt(git_pkt **out, const char *line, size_t len)
 {
 	git_pkt_ack *pkt;
@@ -62,6 +62,10 @@
 	if (len >= 7) {
 		if (!git__prefixcmp(line + 1, "continue"))
 			pkt->status = GIT_ACK_CONTINUE;
+		if (!git__prefixcmp(line + 1, "common"))
+			pkt->status = GIT_ACK_COMMON;
+		if (!git__prefixcmp(line + 1, "ready"))
+			pkt->status = GIT_ACK_READY;
 	}
 
 	*out = (git_pkt *) pkt;
@@ -456,22 +460,27 @@
 	char oid[GIT_OID_HEXSZ +1] = {0};
 	unsigned int len;
 
-	/* Prefer side-band-64k if the server supports both */
-	if (caps->side_band) {
-		if (caps->side_band_64k)
-			git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
-		else
-			git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
-	}
-	if (caps->ofs_delta)
-		git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
-
-	if (caps->multi_ack)
+	/* Prefer multi_ack_detailed */
+	if (caps->multi_ack_detailed)
+		git_buf_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
+	else if (caps->multi_ack)
 		git_buf_puts(&str, GIT_CAP_MULTI_ACK " ");
 
+	/* Prefer side-band-64k if the server supports both */
+	if (caps->side_band_64k)
+		git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
+	else if (caps->side_band)
+		git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
+
 	if (caps->include_tag)
 		git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
 
+	if (caps->thin_pack)
+		git_buf_puts(&str, GIT_CAP_THIN_PACK " ");
+
+	if (caps->ofs_delta)
+		git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
+
 	if (git_buf_oom(&str))
 		return -1;
 
diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c
index 6366167..3bf1f93 100644
--- a/src/transports/smart_protocol.c
+++ b/src/transports/smart_protocol.c
@@ -13,8 +13,11 @@
 #include "push.h"
 #include "pack-objects.h"
 #include "remote.h"
+#include "util.h"
 
 #define NETWORK_XFER_THRESHOLD (100*1024)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
 
 int git_smart__store_refs(transport_smart *t, int flushes)
 {
@@ -46,7 +49,7 @@
 
 		if (error == GIT_EBUFS) {
 			if ((recvd = gitno_recv(buf)) < 0)
-				return -1;
+				return recvd;
 
 			if (recvd == 0 && !flush) {
 				giterr_set(GITERR_NET, "Early EOF");
@@ -94,6 +97,13 @@
 			continue;
 		}
 
+		/* Keep multi_ack_detailed before multi_ack */
+		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
+			caps->common = caps->multi_ack_detailed = 1;
+			ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
+			continue;
+		}
+
 		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
 			caps->common = caps->multi_ack = 1;
 			ptr += strlen(GIT_CAP_MULTI_ACK);
@@ -125,6 +135,12 @@
 			continue;
 		}
 
+		if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
+			caps->common = caps->thin_pack = 1;
+			ptr += strlen(GIT_CAP_THIN_PACK);
+			continue;
+		}
+
 		/* We don't know this capability, so skip it */
 		ptr = strchr(ptr, ' ');
 	}
@@ -148,10 +164,10 @@
 			break; /* return the pkt */
 
 		if (error < 0 && error != GIT_EBUFS)
-			return -1;
+			return error;
 
 		if ((ret = gitno_recv(buf)) < 0)
-			return -1;
+			return ret;
 	} while (error);
 
 	gitno_consume(buf, line_end);
@@ -168,10 +184,11 @@
 {
 	git_pkt *pkt = NULL;
 	gitno_buffer *buf = &t->buffer;
+	int error;
 
 	do {
-		if (recv_pkt(&pkt, buf) < 0)
-			return -1;
+		if ((error = recv_pkt(&pkt, buf)) < 0)
+			return error;
 
 		if (pkt->type == GIT_PKT_ACK) {
 			if (git_vector_insert(&t->common, pkt) < 0)
@@ -211,6 +228,7 @@
 
 		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
 			continue;
+
 		if (git_revwalk_push(walk, git_reference_target(ref)) < 0)
 			goto on_error;
 
@@ -227,7 +245,33 @@
 	return -1;
 }
 
-int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
+static int wait_while_ack(gitno_buffer *buf)
+{
+	int error;
+	git_pkt_ack *pkt = NULL;
+
+	while (1) {
+		git__free(pkt);
+
+		if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
+			return error;
+
+		if (pkt->type == GIT_PKT_NAK)
+			break;
+
+		if (pkt->type == GIT_PKT_ACK &&
+		    (pkt->status != GIT_ACK_CONTINUE ||
+		     pkt->status != GIT_ACK_COMMON)) {
+			git__free(pkt);
+			return 0;
+		}
+	}
+
+	git__free(pkt);
+	return 0;
+}
+
+int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
 {
 	transport_smart *t = (transport_smart *)transport;
 	gitno_buffer *buf = &t->buffer;
@@ -237,19 +281,20 @@
 	unsigned int i;
 	git_oid oid;
 
-	/* No own logic, do our thing */
-	if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+	if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
 		return error;
 
 	if ((error = fetch_setup_walk(&walk, repo)) < 0)
 		goto on_error;
+
 	/*
-	 * We don't support any kind of ACK extensions, so the negotiation
-	 * boils down to sending what we have and listening for an ACK
-	 * every once in a while.
+	 * Our support for ACK extensions is simply to parse them. On
+	 * the first ACK we will accept that as enough common
+	 * objects. We give up if we haven't found an answer in the
+	 * first 256 we send.
 	 */
 	i = 0;
-	while (true) {
+	while (i < 256) {
 		error = git_revwalk_next(&oid, walk);
 
 		if (error < 0) {
@@ -278,7 +323,7 @@
 				goto on_error;
 
 			git_buf_clear(&data);
-			if (t->caps.multi_ack) {
+			if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
 				if ((error = store_common(t)) < 0)
 					goto on_error;
 			} else {
@@ -307,7 +352,7 @@
 			git_pkt_ack *pkt;
 			unsigned int i;
 
-			if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+			if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
 				goto on_error;
 
 			git_vector_foreach(&t->common, i, pkt) {
@@ -327,7 +372,7 @@
 		git_pkt_ack *pkt;
 		unsigned int i;
 
-		if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+		if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
 			goto on_error;
 
 		git_vector_foreach(&t->common, i, pkt) {
@@ -356,7 +401,7 @@
 	git_revwalk_free(walk);
 
 	/* Now let's eat up whatever the server gives us */
-	if (!t->caps.multi_ack) {
+	if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
 		pkt_type = recv_pkt(NULL, buf);
 
 		if (pkt_type < 0) {
@@ -366,22 +411,10 @@
 			return -1;
 		}
 	} else {
-		git_pkt_ack *pkt;
-		do {
-			if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
-				return error;
-
-			if (pkt->type == GIT_PKT_NAK ||
-			    (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) {
-				git__free(pkt);
-				break;
-			}
-
-			git__free(pkt);
-		} while (1);
+		error = wait_while_ack(buf);
 	}
 
-	return 0;
+	return error;
 
 on_error:
 	git_revwalk_free(walk);
@@ -399,16 +432,16 @@
 			return GIT_EUSER;
 		}
 
-		if (writepack->add(writepack, buf->data, buf->offset, stats) < 0)
+		if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
 			return -1;
 
 		gitno_consume_n(buf, buf->offset);
 
 		if ((recvd = gitno_recv(buf)) < 0)
-			return -1;
+			return recvd;
 	} while(recvd > 0);
 
-	if (writepack->commit(writepack, stats))
+	if (writepack->commit(writepack, stats) < 0)
 		return -1;
 
 	return 0;
@@ -422,7 +455,7 @@
 	size_t last_fired_bytes;
 };
 
-static void network_packetsize(size_t received, void *payload)
+static int network_packetsize(size_t received, void *payload)
 {
 	struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
 
@@ -432,8 +465,12 @@
 	/* Fire notification if the threshold is reached */
 	if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
 		npp->last_fired_bytes = npp->stats->received_bytes;
-		npp->callback(npp->stats, npp->payload);
+
+		if (npp->callback(npp->stats, npp->payload))
+			return GIT_EUSER;
 	}
+
+	return 0;
 }
 
 int git_smart__download_pack(
@@ -447,7 +484,7 @@
 	gitno_buffer *buf = &t->buffer;
 	git_odb *odb;
 	struct git_odb_writepack *writepack = NULL;
-	int error = -1;
+	int error = 0;
 	struct network_packetsize_payload npp = {0};
 
 	memset(stats, 0, sizeof(git_transfer_progress));
@@ -460,13 +497,14 @@
 		t->packetsize_payload = &npp;
 
 		/* We might have something in the buffer already from negotiate_fetch */
-		if (t->buffer.offset > 0)
-			t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
+		if (t->buffer.offset > 0 && !t->cancelled.val)
+			if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
+				git_atomic_set(&t->cancelled, 1);
 	}
 
 	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
 		((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
-		goto on_error;
+		goto done;
 
 	/*
 	 * If the remote doesn't support the side-band, we can feed
@@ -474,37 +512,46 @@
 	 * check which one belongs there.
 	 */
 	if (!t->caps.side_band && !t->caps.side_band_64k) {
-		if (no_sideband(t, writepack, buf, stats) < 0)
-			goto on_error;
-
-		goto on_success;
+		error = no_sideband(t, writepack, buf, stats);
+		goto done;
 	}
 
 	do {
 		git_pkt *pkt;
 
+		/* Check cancellation before network call */
 		if (t->cancelled.val) {
 			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
 			error = GIT_EUSER;
-			goto on_error;
+			goto done;
 		}
 
-		if (recv_pkt(&pkt, buf) < 0)
-			goto on_error;
+		if ((error = recv_pkt(&pkt, buf)) < 0)
+			goto done;
+
+		/* Check cancellation after network call */
+		if (t->cancelled.val) {
+			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+			error = GIT_EUSER;
+			goto done;
+		}
 
 		if (pkt->type == GIT_PKT_PROGRESS) {
 			if (t->progress_cb) {
 				git_pkt_progress *p = (git_pkt_progress *) pkt;
-				t->progress_cb(p->data, p->len, t->message_cb_payload);
+				if (t->progress_cb(p->data, p->len, t->message_cb_payload)) {
+					giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+					return GIT_EUSER;
+				}
 			}
 			git__free(pkt);
 		} else if (pkt->type == GIT_PKT_DATA) {
 			git_pkt_data *p = (git_pkt_data *) pkt;
-			error = writepack->add(writepack, p->data, p->len, stats);
+			error = writepack->append(writepack, p->data, p->len, stats);
 
 			git__free(pkt);
 			if (error < 0)
-				goto on_error;
+				goto done;
 		} else if (pkt->type == GIT_PKT_FLUSH) {
 			/* A flush indicates the end of the packfile */
 			git__free(pkt);
@@ -512,13 +559,9 @@
 		}
 	} while (1);
 
-	if (writepack->commit(writepack, stats) < 0)
-		goto on_error;
+	error = writepack->commit(writepack, stats);
 
-on_success:
-	error = 0;
-
-on_error:
+done:
 	if (writepack)
 		writepack->free(writepack);
 
@@ -656,7 +699,7 @@
 
 		if (error == GIT_EBUFS) {
 			if ((recvd = gitno_recv(buf)) < 0)
-				return -1;
+				return recvd;
 
 			if (recvd == 0) {
 				giterr_set(GITERR_NET, "Early EOF");
@@ -801,22 +844,56 @@
 	return 0;
 }
 
+struct push_packbuilder_payload
+{
+	git_smart_subtransport_stream *stream;
+	git_packbuilder *pb;
+	git_push_transfer_progress cb;
+	void *cb_payload;
+	size_t last_bytes;
+	double last_progress_report_time;
+};
+
 static int stream_thunk(void *buf, size_t size, void *data)
 {
-	git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
+	int error = 0;
+	struct push_packbuilder_payload *payload = data;
 
-	return s->write(s, (const char *)buf, size);
+	if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
+		return error;
+
+	if (payload->cb) {
+		double current_time = git__timer();
+		payload->last_bytes += size;
+
+		if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
+			payload->last_progress_report_time = current_time;
+			if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) {
+				giterr_clear();
+				error = GIT_EUSER;
+			}
+		}
+	}
+
+	return error;
 }
 
 int git_smart__push(git_transport *transport, git_push *push)
 {
 	transport_smart *t = (transport_smart *)transport;
-	git_smart_subtransport_stream *s;
+	struct push_packbuilder_payload packbuilder_payload = {0};
 	git_buf pktline = GIT_BUF_INIT;
-	int error = -1, need_pack = 0;
+	int error = 0, need_pack = 0;
 	push_spec *spec;
 	unsigned int i;
 
+	packbuilder_payload.pb = push->pb;
+
+	if (push->transfer_progress_cb) {
+		packbuilder_payload.cb = push->transfer_progress_cb;
+		packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
+	}
+
 #ifdef PUSH_DEBUG
 {
 	git_remote_head *head;
@@ -848,29 +925,36 @@
 		}
 	}
 
-	if (git_smart__get_push_stream(t, &s) < 0 ||
-		gen_pktline(&pktline, push) < 0 ||
-		s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
-		goto on_error;
+	if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
+		(error = gen_pktline(&pktline, push)) < 0 ||
+		(error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
+		goto done;
 
-	if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
-		goto on_error;
+	if (need_pack &&
+		(error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
+		goto done;
 
 	/* If we sent nothing or the server doesn't support report-status, then
 	 * we consider the pack to have been unpacked successfully */
 	if (!push->specs.length || !push->report_status)
 		push->unpack_ok = 1;
-	else if (parse_report(&t->buffer, push) < 0)
-		goto on_error;
+	else if ((error = parse_report(&t->buffer, push)) < 0)
+		goto done;
 
-	if (push->status.length &&
-		update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
-		goto on_error;
+	/* If progress is being reported write the final report */
+	if (push->transfer_progress_cb) {
+		push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
+	}
 
-	error = 0;
+	if (push->status.length) {
+		error = update_refs_from_report(&t->refs, &push->specs, &push->status);
+		if (error < 0)
+			goto done;
 
-on_error:
+		error = git_smart__update_heads(t);
+	}
+
+done:
 	git_buf_free(&pktline);
-
 	return error;
 }
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index a312c8d..4a905e3 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -5,19 +5,18 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#ifdef GIT_SSH
-
 #include "git2.h"
 #include "buffer.h"
 #include "netops.h"
 #include "smart.h"
 
+#ifdef GIT_SSH
+
 #include <libssh2.h>
 
 #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
 
 static const char prefix_ssh[] = "ssh://";
-static const char default_user[] = "git";
 static const char cmd_uploadpack[] = "git-upload-pack";
 static const char cmd_receivepack[] = "git-receive-pack";
 
@@ -38,6 +37,14 @@
 	git_cred *cred;
 } ssh_subtransport;
 
+static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
+{
+	char *ssherr;
+	libssh2_session_last_error(session, &ssherr, NULL, 0);
+
+	giterr_set(GITERR_SSH, "%s: %s", errmsg, ssherr);
+}
+
 /*
  * Create a git protocol request.
  *
@@ -46,27 +53,29 @@
 static int gen_proto(git_buf *request, const char *cmd, const char *url)
 {
 	char *repo;
-	
+
 	if (!git__prefixcmp(url, prefix_ssh)) {
 		url = url + strlen(prefix_ssh);
 		repo = strchr(url, '/');
 	} else {
 		repo = strchr(url, ':');
+		if (repo) repo++;
 	}
-	
+
 	if (!repo) {
+		giterr_set(GITERR_NET, "Malformed git protocol URL");
 		return -1;
 	}
-	
+
 	int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1;
-	
+
 	git_buf_grow(request, len);
 	git_buf_printf(request, "%s '%s'", cmd, repo);
 	git_buf_putc(request, '\0');
-	
+
 	if (git_buf_oom(request))
 		return -1;
-	
+
 	return 0;
 }
 
@@ -74,21 +83,19 @@
 {
 	int error;
 	git_buf request = GIT_BUF_INIT;
-	
+
 	error = gen_proto(&request, s->cmd, s->url);
 	if (error < 0)
 		goto cleanup;
-	
-	error = libssh2_channel_exec(
-		s->channel,
-		request.ptr
-	);
 
-	if (0 != error)
+	error = libssh2_channel_exec(s->channel, request.ptr);
+	if (error < LIBSSH2_ERROR_NONE) {
+		ssh_error(s->session, "SSH could not execute request");
 		goto cleanup;
-	
+	}
+
 	s->sent_command = 1;
-	
+
 cleanup:
 	git_buf_free(&request);
 	return error;
@@ -100,19 +107,21 @@
 	size_t buf_size,
 	size_t *bytes_read)
 {
+	int rc;
 	ssh_stream *s = (ssh_stream *)stream;
-	
+
 	*bytes_read = 0;
-	
+
 	if (!s->sent_command && send_command(s) < 0)
 		return -1;
-	
-	int rc = libssh2_channel_read(s->channel, buffer, buf_size);
-	if (rc < 0)
+
+	if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
+		ssh_error(s->session, "SSH could not read data");;
 		return -1;
-	
+	}
+
 	*bytes_read = rc;
-	
+
 	return 0;
 }
 
@@ -122,16 +131,16 @@
 	size_t len)
 {
 	ssh_stream *s = (ssh_stream *)stream;
-	
+
 	if (!s->sent_command && send_command(s) < 0)
 		return -1;
-	
-	int rc = libssh2_channel_write(s->channel, buffer, len);
-	if (rc < 0) {
+
+	if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) {
+		ssh_error(s->session, "SSH could not write data");
 		return -1;
 	}
-	
-	return rc;
+
+	return 0;
 }
 
 static void ssh_stream_free(git_smart_subtransport_stream *stream)
@@ -139,26 +148,27 @@
 	ssh_stream *s = (ssh_stream *)stream;
 	ssh_subtransport *t = OWNING_SUBTRANSPORT(s);
 	int ret;
-	
+
 	GIT_UNUSED(ret);
-	
+
 	t->current_stream = NULL;
-	
+
 	if (s->channel) {
 		libssh2_channel_close(s->channel);
-        libssh2_channel_free(s->channel);
-        s->channel = NULL;
+		libssh2_channel_free(s->channel);
+		s->channel = NULL;
 	}
-	
+
 	if (s->session) {
-		libssh2_session_free(s->session), s->session = NULL;
+		libssh2_session_free(s->session);
+		s->session = NULL;
 	}
-	
+
 	if (s->socket.socket) {
-		ret = gitno_close(&s->socket);
-		assert(!ret);
+		(void)gitno_close(&s->socket);
+		/* can't do anything here with error return value */
 	}
-	
+
 	git__free(s->url);
 	git__free(s);
 }
@@ -170,26 +180,25 @@
 	git_smart_subtransport_stream **stream)
 {
 	ssh_stream *s;
-	
-	if (!stream)
-		return -1;
-	
+
+	assert(stream);
+
 	s = git__calloc(sizeof(ssh_stream), 1);
 	GITERR_CHECK_ALLOC(s);
-	
+
 	s->parent.subtransport = &t->parent;
 	s->parent.read = ssh_stream_read;
 	s->parent.write = ssh_stream_write;
 	s->parent.free = ssh_stream_free;
-	
+
 	s->cmd = cmd;
+
 	s->url = git__strdup(url);
-	
 	if (!s->url) {
 		git__free(s);
 		return -1;
 	}
-	
+
 	*stream = &s->parent;
 	return 0;
 }
@@ -201,136 +210,127 @@
 {
 	char *colon, *at;
 	const char *start;
-    
-    colon = strchr(url, ':');
-	
-	if (colon == NULL) {
-		giterr_set(GITERR_NET, "Malformed URL: missing :");
-		return -1;
-	}
-	
+
+	colon = strchr(url, ':');
+
+
 	at = strchr(url, '@');
 	if (at) {
-		start = at+1;
+		start = at + 1;
 		*username = git__substrdup(url, at - url);
+		GITERR_CHECK_ALLOC(*username);
 	} else {
 		start = url;
-		*username = git__strdup(default_user);
+		*username = NULL;
 	}
-	
+
+	if (colon == NULL || (colon < start)) {
+		giterr_set(GITERR_NET, "Malformed URL");
+		return -1;
+	}
+
 	*host = git__substrdup(start, colon - start);
-	
+	GITERR_CHECK_ALLOC(*host);
+
 	return 0;
 }
 
 static int _git_ssh_authenticate_session(
 	LIBSSH2_SESSION* session,
 	const char *user,
-	git_cred* cred
-)
+	git_cred* cred)
 {
 	int rc;
+
 	do {
 		switch (cred->credtype) {
-			case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
-				git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
-				rc = libssh2_userauth_password(
-					session, 
-					c->username,
-					c->password
-				);
-				break;
-			}
-			case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
-				git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
-				rc = libssh2_userauth_publickey_fromfile(
-					session, 
-					user,
-					c->publickey,
-					c->privatekey,
-					c->passphrase
-				);
-				break;
-			}
-			case GIT_CREDTYPE_SSH_PUBLICKEY: {
-				git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
-				rc = libssh2_userauth_publickey(
-					session,
-					user,
-					(const unsigned char *)c->publickey,
-					c->publickey_len,
-					c->sign_callback,
-					&c->sign_data
-				);
-				break;
-			}
-			default:
-				rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+		case GIT_CREDTYPE_USERPASS_PLAINTEXT: {
+			git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+			user = c->username ? c->username : user;
+			rc = libssh2_userauth_password(session, user, c->password);
+			break;
 		}
-    } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-	
-    return rc;
-}
+		case GIT_CREDTYPE_SSH_KEY: {
+			git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
+			user = c->username ? c->username : user;
+			rc = libssh2_userauth_publickey_fromfile(
+				session, c->username, c->publickey, c->privatekey, c->passphrase);
+			break;
+		}
+		case GIT_CREDTYPE_SSH_CUSTOM: {
+			git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
 
-static int _git_ssh_session_create
-(
-	LIBSSH2_SESSION** session,
-	gitno_socket socket
-)
-{
-	if (!session) {
+			user = c->username ? c->username : user;
+			rc = libssh2_userauth_publickey(
+				session, c->username, (const unsigned char *)c->publickey,
+				c->publickey_len, c->sign_callback, &c->sign_data);
+			break;
+		}
+		default:
+			rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+		}
+	} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+	if (rc != LIBSSH2_ERROR_NONE) {
+		ssh_error(session, "Failed to authenticate SSH session");
 		return -1;
 	}
-	
-	LIBSSH2_SESSION* s = libssh2_session_init();
-    if (!s)
-        return -1;
-	
-    int rc = 0;
-    do {
-        rc = libssh2_session_startup(s, socket.socket);
-    } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
-	
-	if (0 != rc) {
-        goto on_error;
-    }
-	
-	libssh2_session_set_blocking(s, 1);
-	
-	*session = s;
-	
+
 	return 0;
-	
-on_error:
-	if (s) {
-		libssh2_session_free(s), s = NULL;
+}
+
+static int _git_ssh_session_create(
+	LIBSSH2_SESSION** session,
+	gitno_socket socket)
+{
+	int rc = 0;
+	LIBSSH2_SESSION* s;
+
+	assert(session);
+
+	s = libssh2_session_init();
+	if (!s) {
+		giterr_set(GITERR_NET, "Failed to initialize SSH session");
+		return -1;
 	}
-	
-	return -1;
+
+	do {
+		rc = libssh2_session_startup(s, socket.socket);
+	} while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+	if (rc != LIBSSH2_ERROR_NONE) {
+		ssh_error(s, "Failed to start SSH session");
+		libssh2_session_free(s);
+		return -1;
+	}
+
+	libssh2_session_set_blocking(s, 1);
+
+	*session = s;
+
+	return 0;
 }
 
 static int _git_ssh_setup_conn(
 	ssh_subtransport *t,
 	const char *url,
 	const char *cmd,
-	git_smart_subtransport_stream **stream
-)
+	git_smart_subtransport_stream **stream)
 {
-	char *host, *port=NULL, *user=NULL, *pass=NULL;
+	char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL;
 	const char *default_port="22";
 	ssh_stream *s;
 	LIBSSH2_SESSION* session=NULL;
 	LIBSSH2_CHANNEL* channel=NULL;
-	
+
 	*stream = NULL;
 	if (ssh_stream_alloc(t, url, cmd, stream) < 0)
 		return -1;
-	
+
 	s = (ssh_stream *)*stream;
-	
+
 	if (!git__prefixcmp(url, prefix_ssh)) {
-		url = url + strlen(prefix_ssh);
-		if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0)
+		if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0)
 			goto on_error;
 	} else {
 		if (git_ssh_extract_url_parts(&host, &user, url) < 0)
@@ -338,61 +338,78 @@
 		port = git__strdup(default_port);
 		GITERR_CHECK_ALLOC(port);
 	}
-	
+
 	if (gitno_connect(&s->socket, host, port, 0) < 0)
 		goto on_error;
-	
+
 	if (user && pass) {
 		if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0)
 			goto on_error;
-	} else {
-		if (t->owner->cred_acquire_cb(&t->cred,
-				t->owner->url,
-				user,
-				GIT_CREDTYPE_USERPASS_PLAINTEXT | GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
+	} else if (t->owner->cred_acquire_cb) {
+		if (t->owner->cred_acquire_cb(
+				&t->cred, t->owner->url, user,
+				GIT_CREDTYPE_USERPASS_PLAINTEXT |
+				GIT_CREDTYPE_SSH_KEY |
+				GIT_CREDTYPE_SSH_CUSTOM,
 				t->owner->cred_acquire_payload) < 0)
-			return -1;
+			goto on_error;
+
+		if (!t->cred) {
+			giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials");
+			goto on_error;
+		}
+	} else {
+		giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials");
+		goto on_error;
 	}
 	assert(t->cred);
-	
-	if (!user) {
-		user = git__strdup(default_user);
+
+	if (!user && !git_cred_has_username(t->cred)) {
+		giterr_set_str(GITERR_NET, "Cannot authenticate without a username");
+		goto on_error;
 	}
-	
+
 	if (_git_ssh_session_create(&session, s->socket) < 0)
 		goto on_error;
-	
-    if (_git_ssh_authenticate_session(session, user, t->cred) < 0)
+
+	if (_git_ssh_authenticate_session(session, user, t->cred) < 0)
 		goto on_error;
-	
+
 	channel = libssh2_channel_open_session(session);
-	if (!channel)
-        goto on_error;
-	
+	if (!channel) {
+		ssh_error(session, "Failed to open SSH channel");
+		goto on_error;
+	}
+
 	libssh2_channel_set_blocking(channel, 1);
-	
+
 	s->session = session;
 	s->channel = channel;
-	
+
 	t->current_stream = s;
 	git__free(host);
 	git__free(port);
+	git__free(path);
 	git__free(user);
 	git__free(pass);
 
 	return 0;
-	
+
 on_error:
+	s->session = NULL;
+	s->channel = NULL;
+	t->current_stream = NULL;
+
 	if (*stream)
 		ssh_stream_free(*stream);
-	
+
 	git__free(host);
 	git__free(port);
 	git__free(user);
 	git__free(pass);
 
 	if (session)
-		libssh2_session_free(session), session = NULL;
+		libssh2_session_free(session);
 
 	return -1;
 }
@@ -404,7 +421,7 @@
 {
 	if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0)
 		return -1;
-	
+
 	return 0;
 }
 
@@ -414,12 +431,12 @@
 	git_smart_subtransport_stream **stream)
 {
 	GIT_UNUSED(url);
-	
+
 	if (t->current_stream) {
 		*stream = &t->current_stream->parent;
 		return 0;
 	}
-	
+
 	giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
 	return -1;
 }
@@ -431,7 +448,7 @@
 {
 	if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0)
 		return -1;
-	
+
 	return 0;
 }
 
@@ -441,12 +458,12 @@
 	git_smart_subtransport_stream **stream)
 {
 	GIT_UNUSED(url);
-	
+
 	if (t->current_stream) {
 		*stream = &t->current_stream->parent;
 		return 0;
 	}
-	
+
 	giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
 	return -1;
 }
@@ -458,21 +475,21 @@
 	git_smart_service_t action)
 {
 	ssh_subtransport *t = (ssh_subtransport *) subtransport;
-	
+
 	switch (action) {
 		case GIT_SERVICE_UPLOADPACK_LS:
 			return ssh_uploadpack_ls(t, url, stream);
-			
+
 		case GIT_SERVICE_UPLOADPACK:
 			return ssh_uploadpack(t, url, stream);
-			
+
 		case GIT_SERVICE_RECEIVEPACK_LS:
 			return ssh_receivepack_ls(t, url, stream);
-			
+
 		case GIT_SERVICE_RECEIVEPACK:
 			return ssh_receivepack(t, url, stream);
 	}
-	
+
 	*stream = NULL;
 	return -1;
 }
@@ -480,40 +497,49 @@
 static int _ssh_close(git_smart_subtransport *subtransport)
 {
 	ssh_subtransport *t = (ssh_subtransport *) subtransport;
-	
+
 	assert(!t->current_stream);
-	
+
 	GIT_UNUSED(t);
-	
+
 	return 0;
 }
 
 static void _ssh_free(git_smart_subtransport *subtransport)
 {
 	ssh_subtransport *t = (ssh_subtransport *) subtransport;
-	
+
 	assert(!t->current_stream);
-	
+
 	git__free(t);
 }
+#endif
 
-int git_smart_subtransport_ssh(git_smart_subtransport **out, git_transport *owner)
+int git_smart_subtransport_ssh(
+	git_smart_subtransport **out, git_transport *owner)
 {
+#ifdef GIT_SSH
 	ssh_subtransport *t;
-	
-	if (!out)
-		return -1;
-	
+
+	assert(out);
+
 	t = git__calloc(sizeof(ssh_subtransport), 1);
 	GITERR_CHECK_ALLOC(t);
-	
+
 	t->owner = (transport_smart *)owner;
 	t->parent.action = _ssh_action;
 	t->parent.close = _ssh_close;
 	t->parent.free = _ssh_free;
-	
+
 	*out = (git_smart_subtransport *) t;
 	return 0;
-}
+#else
+	GIT_UNUSED(owner);
 
+	assert(out);
+	*out = NULL;
+
+	giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support");
+	return -1;
 #endif
+}
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 95e422d..673cd0f 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -52,6 +52,7 @@
 
 typedef enum {
 	GIT_WINHTTP_AUTH_BASIC = 1,
+	GIT_WINHTTP_AUTH_NEGOTIATE = 2,
 } winhttp_authmechanism_t;
 
 typedef struct {
@@ -73,17 +74,12 @@
 typedef struct {
 	git_smart_subtransport parent;
 	transport_smart *owner;
-	const char *path;
-	char *host;
-	char *port;
-	char *user_from_url;
-	char *pass_from_url;
+	gitno_connection_data connection_data;
 	git_cred *cred;
 	git_cred *url_cred;
 	int auth_mechanism;
 	HINTERNET session;
 	HINTERNET connection;
-	unsigned use_ssl : 1;
 } winhttp_subtransport;
 
 static int apply_basic_credential(HINTERNET request, git_cred *cred)
@@ -143,6 +139,22 @@
 	return error;
 }
 
+static int apply_default_credentials(HINTERNET request)
+{
+	/* If we are explicitly asked to deliver default credentials, turn set
+	 * the security level to low which will guarantee they are delivered.
+	 * The default is "medium" which applies to the intranet and sounds
+	 * like it would correspond to Internet Explorer security zones, but
+	 * in fact does not.
+	 */
+	DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+
+	if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
+		return -1;
+
+	return 0;
+}
+
 static int winhttp_stream_connect(winhttp_stream *s)
 {
 	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
@@ -152,9 +164,10 @@
 	wchar_t *types[] = { L"*/*", NULL };
 	BOOL peerdist = FALSE;
 	int error = -1, wide_len;
+	unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
 
 	/* Prepare URL */
-	git_buf_printf(&buf, "%s%s", t->path, s->service_url);
+	git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url);
 
 	if (git_buf_oom(&buf))
 		return -1;
@@ -187,7 +200,7 @@
 			NULL,
 			WINHTTP_NO_REFERER,
 			types,
-			t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
+			t->connection_data.use_ssl ? WINHTTP_FLAG_SECURE : 0);
 
 	if (!s->request) {
 		giterr_set(GITERR_OS, "Failed to open request");
@@ -195,7 +208,7 @@
 	}
 
 	/* Set proxy if necessary */
-	if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0)
+	if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0)
 		goto on_error;
 
 	if (proxy_url) {
@@ -244,6 +257,17 @@
 		git__free(proxy_wide);
 	}
 
+	/* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
+	 * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
+	 */
+	if (!WinHttpSetOption(s->request,
+		WINHTTP_OPTION_DISABLE_FEATURE,
+		&disable_redirects,
+		sizeof(disable_redirects))) {
+			giterr_set(GITERR_OS, "Failed to disable redirects");
+			goto on_error;
+	}
+
 	/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
 	 * adds itself. This option may not be supported by the underlying
 	 * platform, so we do not error-check it */
@@ -258,22 +282,39 @@
 		goto on_error;
 	}
 
-	/* Send Content-Type header -- only necessary on a POST */
 	if (post_verb == s->verb) {
+		/* Send Content-Type and Accept headers -- only necessary on a POST */
 		git_buf_clear(&buf);
-		if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
+		if (git_buf_printf(&buf,
+			"Content-Type: application/x-git-%s-request",
+			s->service) < 0)
 			goto on_error;
 
 		git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
 
-		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+			WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			goto on_error;
+		}
+
+		git_buf_clear(&buf);
+		if (git_buf_printf(&buf,
+			"Accept: application/x-git-%s-result",
+			s->service) < 0)
+			goto on_error;
+
+		git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
+
+		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+			WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
 			giterr_set(GITERR_OS, "Failed to add a header to the request");
 			goto on_error;
 		}
 	}
 
 	/* If requested, disable certificate validation */
-	if (t->use_ssl) {
+	if (t->connection_data.use_ssl) {
 		int flags;
 
 		if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
@@ -293,12 +334,17 @@
 		t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
 		apply_basic_credential(s->request, t->cred) < 0)
 		goto on_error;
+	else if (t->cred &&
+		t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
+		t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
+		apply_default_credentials(s->request) < 0)
+		goto on_error;
 
 	/* If no other credentials have been applied and the URL has username and
 	 * password, use those */
-	if (!t->cred && t->user_from_url && t->pass_from_url) {
+	if (!t->cred && t->connection_data.user && t->connection_data.pass) {
 		if (!t->url_cred &&
-			 git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+			git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
 			goto on_error;
 		if (apply_basic_credential(s->request, t->url_cred) < 0)
 			goto on_error;
@@ -337,6 +383,12 @@
 		*auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
 	}
 
+	if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
+		(WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
+		*allowed_types |= GIT_CREDTYPE_DEFAULT;
+		*auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
+	}
+
 	return 0;
 }
 
@@ -380,6 +432,50 @@
 	return 0;
 }
 
+static int winhttp_connect(
+	winhttp_subtransport *t,
+	const char *url)
+{
+	wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
+	git_win32_path host;
+	int32_t port;
+	const char *default_port = "80";
+
+	/* Prepare port */
+	if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0)
+		return -1;
+
+	/* Prepare host */
+	git_win32_path_from_c(host, t->connection_data.host);
+
+	/* Establish session */
+	t->session = WinHttpOpen(
+		ua,
+		WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+		WINHTTP_NO_PROXY_NAME,
+		WINHTTP_NO_PROXY_BYPASS,
+		0);
+
+	if (!t->session) {
+		giterr_set(GITERR_OS, "Failed to init WinHTTP");
+		return -1;
+	}
+
+	/* Establish connection */
+	t->connection = WinHttpConnect(
+		t->session,
+		host,
+		(INTERNET_PORT) port,
+		0);
+
+	if (!t->connection) {
+		giterr_set(GITERR_OS, "Failed to connect to host");
+		return -1;
+	}
+
+	return 0;
+}
+
 static int winhttp_stream_read(
 	git_smart_subtransport_stream *stream,
 	char *buffer,
@@ -511,50 +607,53 @@
 
 			/* Check for Windows 7. This workaround is only necessary on
 			 * Windows Vista and earlier. Windows 7 is version 6.1. */
-			if (!git_has_win32_version(6, 1)) {
-				wchar_t *location;
-				DWORD location_length;
-				int redirect_cmp;
+			wchar_t *location;
+			DWORD location_length;
+			char *location8;
 
-				/* OK, fetch the Location header from the redirect. */
-				if (WinHttpQueryHeaders(s->request,
-					WINHTTP_QUERY_LOCATION,
-					WINHTTP_HEADER_NAME_BY_INDEX,
-					WINHTTP_NO_OUTPUT_BUFFER,
-					&location_length,
-					WINHTTP_NO_HEADER_INDEX) ||
-					GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
-					giterr_set(GITERR_OS, "Failed to read Location header");
-					return -1;
-				}
-
-				location = git__malloc(location_length);
-				GITERR_CHECK_ALLOC(location);
-
-				if (!WinHttpQueryHeaders(s->request,
-					WINHTTP_QUERY_LOCATION,
-					WINHTTP_HEADER_NAME_BY_INDEX,
-					location,
-					&location_length,
-					WINHTTP_NO_HEADER_INDEX)) {
-					giterr_set(GITERR_OS, "Failed to read Location header");
-					git__free(location);
-					return -1;
-				}
-
-				/* Compare the Location header with the request URI */
-				redirect_cmp = wcscmp(location, s->request_uri);
-				git__free(location);
-
-				if (!redirect_cmp) {
-					/* Replay the request */
-					WinHttpCloseHandle(s->request);
-					s->request = NULL;
-					s->sent_request = 0;
-
-					goto replay;
-				}
+			/* OK, fetch the Location header from the redirect. */
+			if (WinHttpQueryHeaders(s->request,
+				WINHTTP_QUERY_LOCATION,
+				WINHTTP_HEADER_NAME_BY_INDEX,
+				WINHTTP_NO_OUTPUT_BUFFER,
+				&location_length,
+				WINHTTP_NO_HEADER_INDEX) ||
+				GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+				giterr_set(GITERR_OS, "Failed to read Location header");
+				return -1;
 			}
+
+			location = git__malloc(location_length);
+			location8 = git__malloc(location_length);
+			GITERR_CHECK_ALLOC(location);
+
+			if (!WinHttpQueryHeaders(s->request,
+				WINHTTP_QUERY_LOCATION,
+				WINHTTP_HEADER_NAME_BY_INDEX,
+				location,
+				&location_length,
+				WINHTTP_NO_HEADER_INDEX)) {
+				giterr_set(GITERR_OS, "Failed to read Location header");
+				git__free(location);
+				return -1;
+			}
+			git__utf16_to_8(location8, location_length, location);
+			git__free(location);
+
+			/* Replay the request */
+			WinHttpCloseHandle(s->request);
+			s->request = NULL;
+			s->sent_request = 0;
+
+			if (!git__prefixcmp_icase(location8, prefix_https)) {
+				/* Upgrade to secure connection; disconnect and start over */
+				if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0)
+					return -1;
+				winhttp_connect(t, location8);
+			}
+
+			git__free(location8);
+			goto replay;
 		}
 
 		/* Handle authentication failures */
@@ -568,8 +667,9 @@
 			if (allowed_types &&
 				(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
 
-				if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
-					return -1;
+				if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types, 
+					t->owner->cred_acquire_payload) < 0)
+					return GIT_EUSER;
 
 				assert(t->cred);
 
@@ -888,68 +988,6 @@
 	return 0;
 }
 
-static int winhttp_connect(
-	winhttp_subtransport *t,
-	const char *url)
-{
-	wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
-	wchar_t host[GIT_WIN_PATH];
-	int32_t port;
-	const char *default_port = "80";
-	int ret;
-
-	if (!git__prefixcmp(url, prefix_http)) {
-		url = url + strlen(prefix_http);
-		default_port = "80";
-	}
-
-	if (!git__prefixcmp(url, prefix_https)) {
-		url += strlen(prefix_https);
-		default_port = "443";
-		t->use_ssl = 1;
-	}
-
-	if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
-					&t->pass_from_url, url, default_port)) < 0)
-		return ret;
-
-	t->path = strchr(url, '/');
-
-	/* Prepare port */
-	if (git__strtol32(&port, t->port, NULL, 10) < 0)
-		return -1;
-
-	/* Prepare host */
-	git__utf8_to_16(host, GIT_WIN_PATH, t->host);
-
-	/* Establish session */
-	t->session = WinHttpOpen(
-			ua,
-			WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
-			WINHTTP_NO_PROXY_NAME,
-			WINHTTP_NO_PROXY_BYPASS,
-			0);
-
-	if (!t->session) {
-		giterr_set(GITERR_OS, "Failed to init WinHTTP");
-		return -1;
-	}
-	
-	/* Establish connection */
-	t->connection = WinHttpConnect(
-			t->session,
-			host,
-			port,
-			0);
-
-	if (!t->connection) {
-		giterr_set(GITERR_OS, "Failed to connect to host");
-		return -1;
-	}
-
-	return 0;
-}
-
 static int winhttp_uploadpack_ls(
 	winhttp_subtransport *t,
 	winhttp_stream *s)
@@ -989,7 +1027,7 @@
 {
 	/* WinHTTP only supports Transfer-Encoding: chunked
 	 * on Windows Vista (NT 6.0) and higher. */
-	s->chunked = git_has_win32_version(6, 0);
+	s->chunked = git_has_win32_version(6, 0, 0);
 
 	if (s->chunked)
 		s->parent.write = winhttp_stream_write_chunked;
@@ -1013,9 +1051,10 @@
 	winhttp_stream *s;
 	int ret = -1;
 
-	if (!t->connection &&
-		winhttp_connect(t, url) < 0)
-		return -1;
+	if (!t->connection)
+		if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 ||
+			 winhttp_connect(t, url) < 0)
+			return -1;
 
 	if (winhttp_stream_alloc(t, &s) < 0)
 		return -1;
@@ -1056,25 +1095,7 @@
 	winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
 	int ret = 0;
 
-	if (t->host) {
-		git__free(t->host);
-		t->host = NULL;
-	}
-
-	if (t->port) {
-		git__free(t->port);
-		t->port = NULL;
-	}
-
-	if (t->user_from_url) {
-		git__free(t->user_from_url);
-		t->user_from_url = NULL;
-	}
-
-	if (t->pass_from_url) {
-		git__free(t->pass_from_url);
-		t->pass_from_url = NULL;
-	}
+	gitno_connection_data_free_ptrs(&t->connection_data);
 
 	if (t->cred) {
 		t->cred->free(t->cred);
diff --git a/src/tree-cache.c b/src/tree-cache.c
index 97ffc2a..1d39971 100644
--- a/src/tree-cache.c
+++ b/src/tree-cache.c
@@ -138,9 +138,11 @@
 		tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));
 		GITERR_CHECK_ALLOC(tree->children);
 
+		memset(tree->children, 0x0, tree->children_count * sizeof(git_tree_cache *));
+
 		for (i = 0; i < tree->children_count; ++i) {
 			if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)
-				return -1;
+				goto corrupted;
 		}
 	}
 
@@ -150,7 +152,7 @@
 
  corrupted:
 	git_tree_cache_free(tree);
-	giterr_set(GITERR_INDEX, "Corruped TREE extension in index");
+	giterr_set(GITERR_INDEX, "Corrupted TREE extension in index");
 	return -1;
 }
 
@@ -162,7 +164,7 @@
 		return -1;
 
 	if (buffer < buffer_end) {
-		giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");
+		giterr_set(GITERR_INDEX, "Corrupted TREE extension in index (unexpected trailing data)");
 		return -1;
 	}
 
@@ -176,9 +178,12 @@
 	if (tree == NULL)
 		return;
 
-	for (i = 0; i < tree->children_count; ++i)
-		git_tree_cache_free(tree->children[i]);
+	if (tree->children != NULL) {
+		for (i = 0; i < tree->children_count; ++i)
+			git_tree_cache_free(tree->children[i]);
 
-	git__free(tree->children);
+		git__free(tree->children);
+	}
+
 	git__free(tree);
 }
diff --git a/src/tree.c b/src/tree.c
index 65d01b4..bb59ff8 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -10,7 +10,7 @@
 #include "tree.h"
 #include "git2/repository.h"
 #include "git2/object.h"
-#include "path.h"
+#include "fileops.h"
 #include "tree-cache.h"
 #include "index.h"
 
@@ -29,19 +29,19 @@
 GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
 {
 	/* Tree bits set, but it's not a commit */
-	if (filemode & GIT_FILEMODE_TREE && !(filemode & 0100000))
+	if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_TREE)
 		return GIT_FILEMODE_TREE;
 
-	/* If any of the x bits is set */
-	if (filemode & 0111)
+	/* If any of the x bits are set */
+	if (GIT_PERMS_IS_EXEC(filemode))
 		return GIT_FILEMODE_BLOB_EXECUTABLE;
 
 	/* 16XXXX means commit */
-	if ((filemode & GIT_FILEMODE_COMMIT) == GIT_FILEMODE_COMMIT)
+	if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT)
 		return GIT_FILEMODE_COMMIT;
 
 	/* 12XXXX means commit */
-	if ((filemode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
+	if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK)
 		return GIT_FILEMODE_LINK;
 
 	/* Otherwise, return a blob */
@@ -237,7 +237,12 @@
 
 git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
 {
-	return (git_filemode_t)entry->attr;
+	return normalize_filemode(entry->attr);
+}
+
+git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
+{
+	return entry->attr;
 }
 
 const char *git_tree_entry_name(const git_tree_entry *entry)
@@ -386,8 +391,6 @@
 		if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer)
 			return tree_error("Failed to parse tree. Can't parse filemode", NULL);
 
-		attr = normalize_filemode(attr); /* make sure to normalize the filemode */
-
 		if (*buffer++ != ' ')
 			return tree_error("Failed to parse tree. Object is corrupted", NULL);
 
@@ -881,8 +884,10 @@
 	git_vector_foreach(&tree->entries, i, entry) {
 		if (preorder) {
 			error = callback(path->ptr, entry, payload);
-			if (error > 0)
+			if (error > 0) {
+				error = 0;
 				continue;
+			}
 			if (error < 0) {
 				giterr_clear();
 				return GIT_EUSER;
@@ -905,11 +910,12 @@
 				return -1;
 
 			error = tree_walk(subtree, callback, path, payload, preorder);
+			git_tree_free(subtree);
+
 			if (error != 0)
 				break;
 
 			git_buf_truncate(path, path_len);
-			git_tree_free(subtree);
 		}
 
 		if (!preorder && callback(path->ptr, entry, payload) < 0) {
diff --git a/src/util.c b/src/util.c
index c543a3d..47516a8 100644
--- a/src/util.c
+++ b/src/util.c
@@ -33,6 +33,9 @@
 #if defined(GIT_SSL) || defined(GIT_WINHTTP)
 		| GIT_CAP_HTTPS
 #endif
+#if defined(GIT_SSH)
+		| GIT_CAP_SSH
+#endif
 	;
 }
 
@@ -114,6 +117,19 @@
 		*(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
 		*(va_arg(ap, ssize_t *)) = git_cache__max_storage;
 		break;
+
+	case GIT_OPT_GET_TEMPLATE_PATH:
+		{
+			char *out = va_arg(ap, char *);
+			size_t outlen = va_arg(ap, size_t);
+
+			error = git_futils_dirs_get_str(out, outlen, GIT_FUTILS_DIR_TEMPLATE);
+		}
+		break;
+
+	case GIT_OPT_SET_TEMPLATE_PATH:
+		error = git_futils_dirs_set(GIT_FUTILS_DIR_TEMPLATE, va_arg(ap, const char *));
+		break;
 	}
 
 	va_end(ap);
@@ -676,6 +692,9 @@
 {
 	char *scan, *pos = str;
 
+	if (!str)
+		return 0;
+
 	for (scan = str; *scan; pos++, scan++) {
 		if (*scan == '\\' && *(scan + 1) != '\0')
 			scan++; /* skip '\' but include next char */
@@ -707,8 +726,9 @@
 void git__qsort_r(
 	void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
 {
-#if defined(__MINGW32__) || defined(__OpenBSD__) || defined(AMIGA) || \
-	defined(__gnu_hurd__) || \
+#if defined(__MINGW32__) || defined(AMIGA) || \
+	defined(__OpenBSD__) || defined(__NetBSD__) || \
+	defined(__gnu_hurd__) || defined(__ANDROID_API__) || \
 	(__GLIBC__ == 2 && __GLIBC_MINOR__ < 8)
 	git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
 #elif defined(GIT_WIN32)
diff --git a/src/util.h b/src/util.h
index e008839..f9de909 100644
--- a/src/util.h
+++ b/src/util.h
@@ -55,6 +55,9 @@
 
 	ptr = (char*)git__malloc(length + 1);
 
+	if (!ptr)
+		return NULL;
+
 	if (length)
 		memcpy(ptr, str, length);
 
@@ -79,7 +82,10 @@
 	return new_ptr;
 }
 
-#define git__free(ptr) free(ptr)
+GIT_INLINE(void) git__free(void *ptr)
+{
+	free(ptr);
+}
 
 #define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
 	((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
@@ -221,6 +227,9 @@
 
 #define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
 
+#define GIT_REFCOUNT_VAL(r) git_atomic_get(&((git_refcount *)(r))->refcount)
+
+
 static signed char from_hex[] = {
 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
@@ -291,6 +300,11 @@
 	return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
 }
 
+GIT_INLINE(bool) git__isspace_nonlf(int c)
+{
+	return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
+}
+
 GIT_INLINE(bool) git__iswildcard(int c)
 {
 	return (c == '*' || c == '?' || c == '[');
@@ -339,4 +353,65 @@
 #endif
 }
 
+#ifdef GIT_WIN32
+
+GIT_INLINE(double) git__timer(void)
+{
+	/* We need the initial tick count to detect if the tick
+	 * count has rolled over. */
+	static DWORD initial_tick_count = 0;
+
+	/* GetTickCount returns the number of milliseconds that have
+	 * elapsed since the system was started. */
+	DWORD count = GetTickCount();
+
+	if(initial_tick_count == 0) {
+		initial_tick_count = count;
+	} else if (count < initial_tick_count) {
+		/* The tick count has rolled over - adjust for it. */
+		count = (0xFFFFFFFF - initial_tick_count) + count;
+	}
+
+	return (double) count / (double) 1000;
+}
+
+#elif __APPLE__
+
+#include <mach/mach_time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+   uint64_t time = mach_absolute_time();
+   static double scaling_factor = 0;
+
+   if (scaling_factor == 0) {
+       mach_timebase_info_data_t info;
+       (void)mach_timebase_info(&info);
+       scaling_factor = (double)info.numer / (double)info.denom;
+   }
+
+   return (double)time * scaling_factor / 1.0E-9;
+}
+
+#else
+
+#include <sys/time.h>
+
+GIT_INLINE(double) git__timer(void)
+{
+	struct timespec tp;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
+		return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9;
+	} else {
+		/* Fall back to using gettimeofday */
+		struct timeval tv;
+		struct timezone tz;
+		gettimeofday(&tv, &tz);
+		return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6;
+	}
+}
+
+#endif
+
 #endif /* INCLUDE_util_h__ */
diff --git a/src/vector.c b/src/vector.c
index 5ba2fab..362e7b0 100644
--- a/src/vector.c
+++ b/src/vector.c
@@ -220,7 +220,7 @@
 		v->length--;
 }
 
-void git_vector_uniq(git_vector *v)
+void git_vector_uniq(git_vector *v, void  (*git_free_cb)(void *))
 {
 	git_vector_cmp cmp;
 	size_t i, j;
@@ -232,9 +232,12 @@
 	cmp = v->_cmp ? v->_cmp : strict_comparison;
 
 	for (i = 0, j = 1 ; j < v->length; ++j)
-		if (!cmp(v->contents[i], v->contents[j]))
+		if (!cmp(v->contents[i], v->contents[j])) {
+			if (git_free_cb)
+				git_free_cb(v->contents[i]);
+
 			v->contents[i] = v->contents[j];
-		else
+		} else
 			v->contents[++i] = v->contents[j];
 
 	v->length -= j - i - 1;
diff --git a/src/vector.h b/src/vector.h
index 1bda9c9..279f5c6 100644
--- a/src/vector.h
+++ b/src/vector.h
@@ -55,6 +55,11 @@
 
 #define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
 
+GIT_INLINE(size_t) git_vector_length(const git_vector *v)
+{
+	return v->length;
+}
+
 GIT_INLINE(void *) git_vector_last(const git_vector *v)
 {
 	return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
@@ -71,7 +76,7 @@
 	int (*on_dup)(void **old, void *new));
 int git_vector_remove(git_vector *v, size_t idx);
 void git_vector_pop(git_vector *v);
-void git_vector_uniq(git_vector *v);
+void git_vector_uniq(git_vector *v, void  (*git_free_cb)(void *));
 void git_vector_remove_matching(
 	git_vector *v, int (*match)(const git_vector *v, size_t idx));
 
diff --git a/src/win32/dir.c b/src/win32/dir.c
index 8c51d83..f7859b7 100644
--- a/src/win32/dir.c
+++ b/src/win32/dir.c
@@ -5,8 +5,7 @@
  * a Linking Exception. For full terms see the included COPYING file.
  */
 #define GIT__WIN32_NO_WRAP_DIR
-#include "dir.h"
-#include "utf-conv.h"
+#include "posix.h"
 
 static int init_filter(char *filter, size_t n, const char *dir)
 {
@@ -25,36 +24,32 @@
 
 git__DIR *git__opendir(const char *dir)
 {
-	char filter[GIT_WIN_PATH];
-	wchar_t filter_w[GIT_WIN_PATH];
+	git_win32_path_as_utf8 filter;
+	git_win32_path filter_w;
 	git__DIR *new = NULL;
+	size_t dirlen;
 
 	if (!dir || !init_filter(filter, sizeof(filter), dir))
 		return NULL;
 
-	new = git__calloc(1, sizeof(*new));
+	dirlen = strlen(dir);
+
+	new = git__calloc(sizeof(*new) + dirlen + 1, 1);
 	if (!new)
 		return NULL;
+	memcpy(new->dir, dir, dirlen);
 
-	new->dir = git__strdup(dir);
-	if (!new->dir)
-		goto fail;
-
-	git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+	git_win32_path_from_c(filter_w, filter);
 	new->h = FindFirstFileW(filter_w, &new->f);
 
 	if (new->h == INVALID_HANDLE_VALUE) {
 		giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
-		goto fail;
+		git__free(new);
+		return NULL;
 	}
 
 	new->first = 1;
 	return new;
-
-fail:
-	git__free(new->dir);
-	git__free(new);
-	return NULL;
 }
 
 int git__readdir_ext(
@@ -80,7 +75,7 @@
 	if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
 		return -1;
 
-	git__utf16_to_8(entry->d_name, d->f.cFileName);
+	git_win32_path_to_c(entry->d_name, d->f.cFileName);
 	entry->d_ino = 0;
 
 	*result = entry;
@@ -101,8 +96,8 @@
 
 void git__rewinddir(git__DIR *d)
 {
-	char filter[GIT_WIN_PATH];
-	wchar_t filter_w[GIT_WIN_PATH];
+	git_win32_path_as_utf8 filter;
+	git_win32_path filter_w;
 
 	if (!d)
 		return;
@@ -116,7 +111,7 @@
 	if (!init_filter(filter, sizeof(filter), d->dir))
 		return;
 
-	git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
+	git_win32_path_from_c(filter_w, filter);
 	d->h = FindFirstFileW(filter_w, &d->f);
 
 	if (d->h == INVALID_HANDLE_VALUE)
@@ -134,8 +129,7 @@
 		FindClose(d->h);
 		d->h = INVALID_HANDLE_VALUE;
 	}
-	git__free(d->dir);
-	d->dir = NULL;
+
 	git__free(d);
 	return 0;
 }
diff --git a/src/win32/dir.h b/src/win32/dir.h
index 7696d46..24d48f6 100644
--- a/src/win32/dir.h
+++ b/src/win32/dir.h
@@ -11,15 +11,15 @@
 
 struct git__dirent {
 	int d_ino;
-	char d_name[261];
+	git_win32_path_as_utf8 d_name;
 };
 
 typedef struct {
 	HANDLE h;
 	WIN32_FIND_DATAW f;
 	struct git__dirent entry;
-	char *dir;
 	int first;
+	char dir[GIT_FLEX_ARRAY];
 } git__DIR;
 
 extern git__DIR *git__opendir(const char *);
diff --git a/src/win32/error.c b/src/win32/error.c
index a62a07e..bc598ae 100644
--- a/src/win32/error.c
+++ b/src/win32/error.c
@@ -47,7 +47,7 @@
 		(LPWSTR)&lpMsgBuf, 0, NULL)) {
 
 		/* Invalid code point check supported on Vista+ only */
-		if (git_has_win32_version(6, 0))
+		if (git_has_win32_version(6, 0, 0))
 			dwFlags = WC_ERR_INVALID_CHARS;
 		else
 			dwFlags = 0;
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index 5dd3de1..a9e812e 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -23,11 +23,11 @@
 	return s_root->len ? 0 : -1;
 }
 
-static int win32_path_utf16_to_8(git_buf *path_utf8, const wchar_t *path_utf16)
+static int win32_path_to_8(git_buf *path_utf8, const wchar_t *path)
 {
 	char temp_utf8[GIT_PATH_MAX];
 
-	git__utf16_to_8(temp_utf8, path_utf16);
+	git__utf16_to_8(temp_utf8, GIT_PATH_MAX, path);
 	git_path_mkposix(temp_utf8);
 
 	return git_buf_sets(path_utf8, temp_utf8);
@@ -53,7 +53,7 @@
 	if (*filename == '/' || *filename == '\\')
 		filename++;
 
-	git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename);
+	git__utf8_to_16(file_utf16 + root->len - 1, alloc_len - root->len, filename);
 
 	/* check access */
 	if (_waccess(file_utf16, F_OK) < 0) {
@@ -61,7 +61,7 @@
 		return GIT_ENOTFOUND;
 	}
 
-	win32_path_utf16_to_8(path, file_utf16);
+	win32_path_to_8(path, file_utf16);
 	git__free(file_utf16);
 
 	return 0;
@@ -86,7 +86,7 @@
 	return (path != base) ? path : NULL;
 }
 
-static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
+static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir)
 {
 	wchar_t *env = _wgetenv(L"PATH"), lastch;
 	struct git_win32__path root;
@@ -110,10 +110,10 @@
 		wcscpy(&root.path[root.len], gitexe);
 
 		if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
-			/* replace "bin\\" or "cmd\\" with "etc\\" */
-			wcscpy(&root.path[root.len - 4], L"etc\\");
+			/* replace "bin\\" or "cmd\\" with subdir */
+			wcscpy(&root.path[root.len - 4], subdir);
 
-			win32_path_utf16_to_8(buf, root.path);
+			win32_path_to_8(buf, root.path);
 			return 0;
 		}
 	}
@@ -122,7 +122,7 @@
 }
 
 static int win32_find_git_in_registry(
-	git_buf *buf, const HKEY hieve, const wchar_t *key)
+	git_buf *buf, const HKEY hieve, const wchar_t *key, const wchar_t *subdir)
 {
 	HKEY hKey;
 	DWORD dwType = REG_SZ;
@@ -130,9 +130,9 @@
 
 	assert(buf);
 
-	path16.len = 0;
+	path16.len = MAX_PATH;
 
-	if (RegOpenKeyExW(hieve, key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
+	if (RegOpenKeyExW(hieve, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
 		if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType,
 			(LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS)
 		{
@@ -143,10 +143,10 @@
 				return -1;
 			}
 
-			wcscat(path16.path, L"etc\\");
+			wcscat(path16.path, subdir);
 			path16.len += 4;
 
-			win32_path_utf16_to_8(buf, path16.path);
+			win32_path_to_8(buf, path16.path);
 		}
 
 		RegCloseKey(hKey);
@@ -156,7 +156,7 @@
 }
 
 static int win32_find_existing_dirs(
-	git_buf *out, const wchar_t *tmpl[], char *temp[])
+	git_buf *out, const wchar_t *tmpl[])
 {
 	struct git_win32__path path16;
 	git_buf buf = GIT_BUF_INIT;
@@ -168,7 +168,7 @@
 			path16.path[0] != L'%' &&
 			!_waccess(path16.path, F_OK))
 		{
-			win32_path_utf16_to_8(&buf, path16.path);
+			win32_path_to_8(&buf, path16.path);
 
 			if (buf.size)
 				git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
@@ -180,26 +180,26 @@
 	return (git_buf_oom(out) ? -1 : 0);
 }
 
-int git_win32__find_system_dirs(git_buf *out)
+int git_win32__find_system_dirs(git_buf *out, const wchar_t *subdir)
 {
 	git_buf buf = GIT_BUF_INIT;
 
 	/* directories where git.exe & git.cmd are found */
-	if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size)
+	if (!win32_find_git_in_path(&buf, L"git.exe", subdir) && buf.size)
 		git_buf_set(out, buf.ptr, buf.size);
 	else
 		git_buf_clear(out);
 
-	if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size)
+	if (!win32_find_git_in_path(&buf, L"git.cmd", subdir) && buf.size)
 		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
 
 	/* directories where git is installed according to registry */
 	if (!win32_find_git_in_registry(
-			&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size)
+			&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL, subdir) && buf.size)
 		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
 
 	if (!win32_find_git_in_registry(
-			&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size)
+			&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL, subdir) && buf.size)
 		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
 
 	git_buf_free(&buf);
@@ -209,7 +209,6 @@
 
 int git_win32__find_global_dirs(git_buf *out)
 {
-	char *temp[3];
 	static const wchar_t *global_tmpls[4] = {
 		L"%HOME%\\",
 		L"%HOMEDRIVE%%HOMEPATH%\\",
@@ -217,12 +216,11 @@
 		NULL,
 	};
 
-	return win32_find_existing_dirs(out, global_tmpls, temp);
+	return win32_find_existing_dirs(out, global_tmpls);
 }
 
 int git_win32__find_xdg_dirs(git_buf *out)
 {
-	char *temp[6];
 	static const wchar_t *global_tmpls[7] = {
 		L"%XDG_CONFIG_HOME%\\git",
 		L"%APPDATA%\\git",
@@ -233,5 +231,5 @@
 		NULL,
 	};
 
-	return win32_find_existing_dirs(out, global_tmpls, temp);
+	return win32_find_existing_dirs(out, global_tmpls);
 }
diff --git a/src/win32/findfile.h b/src/win32/findfile.h
index fc79e1b..11bf7e6 100644
--- a/src/win32/findfile.h
+++ b/src/win32/findfile.h
@@ -19,7 +19,7 @@
 extern int git_win32__find_file(
 	git_buf *path, const struct git_win32__path *root, const char *filename);
 
-extern int git_win32__find_system_dirs(git_buf *out);
+extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
 extern int git_win32__find_global_dirs(git_buf *out);
 extern int git_win32__find_xdg_dirs(git_buf *out);
 
diff --git a/src/win32/mingw-compat.h b/src/win32/mingw-compat.h
index 7b97b48..fe0abfb 100644
--- a/src/win32/mingw-compat.h
+++ b/src/win32/mingw-compat.h
@@ -19,6 +19,11 @@
 # define S_IFLNK _S_IFLNK
 # define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
 
+GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
+	const char *end = memchr(s, 0, maxlen);
+	return end ? (size_t)(end - s) : maxlen;
+}
+
 #endif
 
 #endif /* INCLUDE_mingw_compat__ */
diff --git a/src/win32/posix.h b/src/win32/posix.h
index c49c217..24cba23 100644
--- a/src/win32/posix.h
+++ b/src/win32/posix.h
@@ -8,7 +8,16 @@
 #define INCLUDE_posix__w32_h__
 
 #include "common.h"
+#include "../posix.h"
 #include "utf-conv.h"
+#include "dir.h"
+
+/* define some standard errnos that the runtime may be missing.  for example,
+ * mingw lacks EAFNOSUPPORT. */
+
+#ifndef EAFNOSUPPORT
+# define EAFNOSUPPORT (INT_MAX-1)
+#endif
 
 GIT_INLINE(int) p_link(const char *old, const char *new)
 {
@@ -20,9 +29,9 @@
 
 GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
 {
-	wchar_t buf[GIT_WIN_PATH];
+	git_win32_path buf;
 	GIT_UNUSED(mode);
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path_from_c(buf, path);
 	return _wmkdir(buf);
 }
 
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index f049744..18f717b 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -16,8 +16,8 @@
 
 int p_unlink(const char *path)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	_wchmod(buf, 0666);
 	return _wunlink(buf);
 }
@@ -59,10 +59,11 @@
 	const char *file_name, struct stat *buf, int posix_enotdir)
 {
 	WIN32_FILE_ATTRIBUTE_DATA fdata;
-	wchar_t fbuf[GIT_WIN_PATH], lastch;
+	git_win32_path fbuf;
+	wchar_t lastch;
 	int flen;
 
-	flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name);
+	flen = git_win32_path_from_c(fbuf, file_name);
 
 	/* truncate trailing slashes */
 	for (; flen > 0; --flen) {
@@ -90,6 +91,9 @@
 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
 			fMode |= S_IFLNK;
 
+		if ((fMode & (S_IFDIR | S_IFLNK)) == (S_IFDIR | S_IFLNK)) // junction
+			fMode ^= S_IFLNK;
+
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
@@ -105,10 +109,10 @@
 		 * the length of the path pointed to, which we expect everywhere else
 		 */
 		if (S_ISLNK(fMode)) {
-			char target[GIT_WIN_PATH];
+			git_win32_path_as_utf8 target;
 			int readlink_result;
 
-			readlink_result = p_readlink(file_name, target, GIT_WIN_PATH);
+			readlink_result = p_readlink(file_name, target, sizeof(target));
 
 			if (readlink_result == -1)
 				return -1;
@@ -121,8 +125,8 @@
 
 	errno = ENOENT;
 
-	/* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
-	 * file path is a regular file,otherwise ENOENT must be set.
+	/* To match POSIX behavior, set ENOTDIR when any of the folders in the
+	 * file path is a regular file, otherwise set ENOENT.
 	 */
 	if (posix_enotdir) {
 		/* scan up path until we find an existing item */
@@ -156,13 +160,22 @@
 	return do_lstat(filename, buf, 1);
 }
 
+
+/*
+ * Parts of the The p_readlink function are heavily inspired by the php 
+ * readlink function in link_win32.c
+ *
+ * Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
+ *
+ * For details of the PHP license see http://www.php.net/license/3_01.txt
+ */
 int p_readlink(const char *link, char *target, size_t target_len)
 {
 	typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);
 	static fpath_func pGetFinalPath = NULL;
 	HANDLE hFile;
 	DWORD dwRet;
-	wchar_t link_w[GIT_WIN_PATH];
+	git_win32_path link_w;
 	wchar_t* target_w;
 	int error = 0;
 
@@ -185,7 +198,7 @@
 		}
 	}
 
-	git__utf8_to_16(link_w, GIT_WIN_PATH, link);
+	git_win32_path_from_c(link_w, link);
 
 	hFile = CreateFileW(link_w,			// file to open
 			GENERIC_READ,			// open for reading
@@ -251,10 +264,10 @@
 
 int p_open(const char *path, int flags, ...)
 {
-	wchar_t buf[GIT_WIN_PATH];
+	git_win32_path buf;
 	mode_t mode = 0;
 
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path_from_c(buf, path);
 
 	if (flags & O_CREAT) {
 		va_list arg_list;
@@ -269,8 +282,8 @@
 
 int p_creat(const char *path, mode_t mode)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
 }
 
@@ -296,7 +309,7 @@
 
 int p_stat(const char* path, struct stat* buf)
 {
-	char target[GIT_WIN_PATH];
+	git_win32_path_as_utf8 target;
 	int error = 0;
 
 	error = do_lstat(path, buf, 0);
@@ -304,7 +317,7 @@
 	/* We need not do this in a loop to unwind chains of symlinks since
 	 * p_readlink calls GetFinalPathNameByHandle which does it for us. */
 	if (error >= 0 && S_ISLNK(buf->st_mode) &&
-		(error = p_readlink(path, target, GIT_WIN_PATH)) >= 0)
+		(error = p_readlink(path, target, sizeof(target))) >= 0)
 		error = do_lstat(target, buf, 0);
 
 	return error;
@@ -312,23 +325,23 @@
 
 int p_chdir(const char* path)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	return _wchdir(buf);
 }
 
 int p_chmod(const char* path, mode_t mode)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	return _wchmod(buf, mode);
 }
 
 int p_rmdir(const char* path)
 {
 	int error;
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 
 	error = _wrmdir(buf);
 
@@ -344,24 +357,24 @@
 
 int p_hide_directory__w32(const char *path)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1;
 }
 
 char *p_realpath(const char *orig_path, char *buffer)
 {
 	int ret;
-	wchar_t orig_path_w[GIT_WIN_PATH];
-	wchar_t buffer_w[GIT_WIN_PATH];
+	git_win32_path orig_path_w;
+	git_win32_path buffer_w;
 
-	git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path);
+	git_win32_path_from_c(orig_path_w, orig_path);
 
 	/* Implicitly use GetCurrentDirectory which can be a threading issue */
-	ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL);
+	ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL);
 
 	/* According to MSDN, a return value equals to zero means a failure. */
-	if (ret == 0 || ret > GIT_WIN_PATH)
+	if (ret == 0 || ret > GIT_WIN_PATH_UTF16)
 		buffer = NULL;
 
 	else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
@@ -445,18 +458,18 @@
 
 int p_access(const char* path, mode_t mode)
 {
-	wchar_t buf[GIT_WIN_PATH];
-	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	git_win32_path buf;
+	git_win32_path_from_c(buf, path);
 	return _waccess(buf, mode);
 }
 
 int p_rename(const char *from, const char *to)
 {
-	wchar_t wfrom[GIT_WIN_PATH];
-	wchar_t wto[GIT_WIN_PATH];
+	git_win32_path wfrom;
+	git_win32_path wto;
 
-	git__utf8_to_16(wfrom, GIT_WIN_PATH, from);
-	git__utf8_to_16(wto, GIT_WIN_PATH, to);
+	git_win32_path_from_c(wfrom, from);
+	git_win32_path_from_c(wto, to);
 	return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
 }
 
@@ -505,94 +518,40 @@
 	return result;
 }
 
-#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
-#define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
-#else
-#define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
-#endif
- 
-#ifndef _TIMEZONE_DEFINED
-#define _TIMEZONE_DEFINED
-struct timezone 
+int p_inet_pton(int af, const char *src, void *dst)
 {
-	int  tz_minuteswest; /* minutes W of Greenwich */
-	int  tz_dsttime;     /* type of dst correction */
-};
-#endif
+	struct sockaddr_storage sin;
+	void *addr;
+	int sin_len = sizeof(struct sockaddr_storage), addr_len;
+	int error = 0;
 
-int p_gettimeofday(struct timeval *tv, struct timezone *tz)
-{
-	FILETIME ft;
-	unsigned __int64 tmpres = 0;
-	static int tzflag;
-
-	if (NULL != tv)
-	{
-		GetSystemTimeAsFileTime(&ft);
-
-		tmpres |= ft.dwHighDateTime;
-		tmpres <<= 32;
-		tmpres |= ft.dwLowDateTime;
-
-		/*converting file time to unix epoch*/
-		tmpres /= 10;  /*convert into microseconds*/
-		tmpres -= DELTA_EPOCH_IN_MICROSECS;
-		tv->tv_sec = (long)(tmpres / 1000000UL);
-		tv->tv_usec = (long)(tmpres % 1000000UL);
-	}
-
-	if (NULL != tz)
-	{
-		 if (!tzflag)
-		{
-			_tzset();
-			tzflag++;
-		}
-		tz->tz_minuteswest = _timezone / 60;
-		tz->tz_dsttime = _daylight;
-	}
-
-	return 0;
-}
-
-int p_inet_pton(int af, const char* src, void* dst)
-{
-	union {
-		struct sockaddr_in6 sin6;
-		struct sockaddr_in sin;
-	} sa;
-	int srcsize;
-
-	switch(af)
-	{
-		case AF_INET:
-			sa.sin.sin_family = AF_INET;
-			srcsize = (int)sizeof(sa.sin);
-		break;
-		case AF_INET6:
-			sa.sin6.sin6_family = AF_INET6;
-			srcsize = (int)sizeof(sa.sin6);
-		break;
-		default:
-			errno = WSAEPFNOSUPPORT;
-			return -1;
-	}
-
-	if (WSAStringToAddress((LPSTR)src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0)
-	{
-		errno = WSAGetLastError();
+	if (af == AF_INET) {
+		addr = &((struct sockaddr_in *)&sin)->sin_addr;
+		addr_len = sizeof(struct in_addr);
+	} else if (af == AF_INET6) {
+		addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
+		addr_len = sizeof(struct in6_addr);
+	} else {
+		errno = EAFNOSUPPORT;
 		return -1;
 	}
 
-	switch(af)
-	{
-		case AF_INET:
-			memcpy(dst, &sa.sin.sin_addr, sizeof(sa.sin.sin_addr));
-		break;
-		case AF_INET6:
-			memcpy(dst, &sa.sin6.sin6_addr, sizeof(sa.sin6.sin6_addr));
-		break;
+	if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
+		memcpy(dst, addr, addr_len);
+		return 1;
 	}
 
-	return 1;
+	switch(WSAGetLastError()) {
+	case WSAEINVAL:
+		return 0;
+	case WSAEFAULT:
+		errno = ENOSPC;
+		return -1;
+	case WSA_NOT_ENOUGH_MEMORY:
+		errno = ENOMEM;
+		return -1;
+	}
+
+	errno = EINVAL;
+	return -1;
 }
diff --git a/src/win32/precompiled.h b/src/win32/precompiled.h
index 5de7e6f..cbfe988 100644
--- a/src/win32/precompiled.h
+++ b/src/win32/precompiled.h
@@ -1,4 +1,5 @@
 #include "git2.h"
+#include "common.h"
 
 #include <assert.h>
 #include <errno.h>
@@ -6,6 +7,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <fcntl.h>
+#include <time.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
diff --git a/src/win32/pthread.c b/src/win32/pthread.c
index 2f263b3..db89274 100644
--- a/src/win32/pthread.c
+++ b/src/win32/pthread.c
@@ -6,6 +6,7 @@
  */
 
 #include "pthread.h"
+#include "../global.h"
 
 int pthread_create(
 	pthread_t *GIT_RESTRICT thread,
@@ -127,9 +128,10 @@
 	return 0;
 }
 
-/* pthread_cond_broadcast is not implemented because doing so with just Win32 events
- * is quite complicated, and no caller in libgit2 uses it yet. */
-
+/* pthread_cond_broadcast is not implemented because doing so with just
+ * Win32 events is quite complicated, and no caller in libgit2 uses it
+ * yet.
+ */
 int pthread_num_processors_np(void)
 {
 	DWORD_PTR p, s;
@@ -142,3 +144,111 @@
 	return n ? n : 1;
 }
 
+
+static HINSTANCE win32_kernel32_dll;
+
+typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
+
+static win32_srwlock_fn win32_srwlock_initialize;
+static win32_srwlock_fn win32_srwlock_acquire_shared;
+static win32_srwlock_fn win32_srwlock_release_shared;
+static win32_srwlock_fn win32_srwlock_acquire_exclusive;
+static win32_srwlock_fn win32_srwlock_release_exclusive;
+
+int pthread_rwlock_init(
+	pthread_rwlock_t *GIT_RESTRICT lock,
+	const pthread_rwlockattr_t *GIT_RESTRICT attr)
+{
+	(void)attr;
+
+	if (win32_srwlock_initialize)
+		win32_srwlock_initialize(&lock->native.srwl);
+	else
+		InitializeCriticalSection(&lock->native.csec);
+
+	return 0;
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *lock)
+{
+	if (win32_srwlock_acquire_shared)
+		win32_srwlock_acquire_shared(&lock->native.srwl);
+	else
+		EnterCriticalSection(&lock->native.csec);
+
+	return 0;
+}
+
+int pthread_rwlock_rdunlock(pthread_rwlock_t *lock)
+{
+	if (win32_srwlock_release_shared)
+		win32_srwlock_release_shared(&lock->native.srwl);
+	else
+		LeaveCriticalSection(&lock->native.csec);
+
+	return 0;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *lock)
+{
+	if (win32_srwlock_acquire_exclusive)
+		win32_srwlock_acquire_exclusive(&lock->native.srwl);
+	else
+		EnterCriticalSection(&lock->native.csec);
+
+	return 0;
+}
+
+int pthread_rwlock_wrunlock(pthread_rwlock_t *lock)
+{
+	if (win32_srwlock_release_exclusive)
+		win32_srwlock_release_exclusive(&lock->native.srwl);
+	else
+		LeaveCriticalSection(&lock->native.csec);
+
+	return 0;
+}
+
+int pthread_rwlock_destroy(pthread_rwlock_t *lock)
+{
+	if (!win32_srwlock_initialize)
+		DeleteCriticalSection(&lock->native.csec);
+	git__memzero(lock, sizeof(*lock));
+	return 0;
+}
+
+
+static void win32_pthread_shutdown(void)
+{
+	if (win32_kernel32_dll) {
+		FreeLibrary(win32_kernel32_dll);
+		win32_kernel32_dll = NULL;
+	}
+}
+
+int win32_pthread_initialize(void)
+{
+	if (win32_kernel32_dll)
+		return 0;
+
+	win32_kernel32_dll = LoadLibrary("Kernel32.dll");
+	if (!win32_kernel32_dll) {
+		giterr_set(GITERR_OS, "Could not load Kernel32.dll!");
+		return -1;
+	}
+
+	win32_srwlock_initialize = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "InitializeSRWLock");
+	win32_srwlock_acquire_shared = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "AcquireSRWLockShared");
+	win32_srwlock_release_shared = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockShared");
+	win32_srwlock_acquire_exclusive = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "AcquireSRWLockExclusive");
+	win32_srwlock_release_exclusive = (win32_srwlock_fn)
+		GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive");
+
+	git__on_shutdown(win32_pthread_shutdown);
+
+	return 0;
+}
diff --git a/src/win32/pthread.h b/src/win32/pthread.h
index 8277ecf..af5b121 100644
--- a/src/win32/pthread.h
+++ b/src/win32/pthread.h
@@ -19,22 +19,34 @@
 typedef int pthread_mutexattr_t;
 typedef int pthread_condattr_t;
 typedef int pthread_attr_t;
+typedef int pthread_rwlockattr_t;
+
 typedef CRITICAL_SECTION pthread_mutex_t;
 typedef HANDLE pthread_t;
 typedef HANDLE pthread_cond_t;
 
-#define PTHREAD_MUTEX_INITIALIZER {(void*)-1};
+typedef struct { void *Ptr; } GIT_SRWLOCK;
+
+typedef struct {
+	union {
+		GIT_SRWLOCK srwl;
+		CRITICAL_SECTION csec;
+	} native;
+} pthread_rwlock_t;
+
+#define PTHREAD_MUTEX_INITIALIZER  {(void*)-1}
 
 int pthread_create(
-	pthread_t *GIT_RESTRICT,
-	const pthread_attr_t *GIT_RESTRICT,
+	pthread_t *GIT_RESTRICT thread,
+	const pthread_attr_t *GIT_RESTRICT attr,
 	void *(*start_routine)(void*),
-	void *__restrict);
+	void *GIT_RESTRICT arg);
 
 int pthread_join(pthread_t, void **);
 
 int pthread_mutex_init(
-	pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT);
+	pthread_mutex_t *GIT_RESTRICT mutex,
+	const pthread_mutexattr_t *GIT_RESTRICT mutexattr);
 int pthread_mutex_destroy(pthread_mutex_t *);
 int pthread_mutex_lock(pthread_mutex_t *);
 int pthread_mutex_unlock(pthread_mutex_t *);
@@ -47,4 +59,15 @@
 
 int pthread_num_processors_np(void);
 
+int pthread_rwlock_init(
+	pthread_rwlock_t *GIT_RESTRICT lock,
+	const pthread_rwlockattr_t *GIT_RESTRICT attr);
+int pthread_rwlock_rdlock(pthread_rwlock_t *);
+int pthread_rwlock_rdunlock(pthread_rwlock_t *);
+int pthread_rwlock_wrlock(pthread_rwlock_t *);
+int pthread_rwlock_wrunlock(pthread_rwlock_t *);
+int pthread_rwlock_destroy(pthread_rwlock_t *);
+
+extern int win32_pthread_initialize(void);
+
 #endif
diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c
index c06f3a8..d4dbfba 100644
--- a/src/win32/utf-conv.c
+++ b/src/win32/utf-conv.c
@@ -70,12 +70,12 @@
 }
 #endif
 
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src)
 {
-	return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length);
+	return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)dest_size);
 }
 
-int git__utf16_to_8(char *out, const wchar_t *input)
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src)
 {
-	return WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL);
+	return WideCharToMultiByte(CP_UTF8, 0, src, -1, dest, (int)dest_size, NULL, NULL);
 }
diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h
index 6cc9205..3af7758 100644
--- a/src/win32/utf-conv.h
+++ b/src/win32/utf-conv.h
@@ -4,16 +4,35 @@
  * 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 <wchar.h>
-
 #ifndef INCLUDE_git_utfconv_h__
 #define INCLUDE_git_utfconv_h__
 
-#define GIT_WIN_PATH (260 + 1)
+#include <wchar.h>
+#include "common.h"
 
-int git__utf8_to_16(wchar_t *dest, size_t length, const char *src);
-int git__utf16_to_8(char *dest, const wchar_t *src);
+/* Maximum characters in a Windows path plus one for NUL byte */
+#define GIT_WIN_PATH_UTF16 (260 + 1)
+
+/* Maximum bytes necessary to convert a full-length UTF16 path to UTF8 */
+#define GIT_WIN_PATH_UTF8  (260 * 4 + 1)
+
+typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
+
+typedef char git_win32_path_as_utf8[GIT_WIN_PATH_UTF8];
+
+/* dest_size is the size of dest in wchar_t's */
+int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src);
+/* dest_size is the size of dest in char's */
+int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src);
+
+GIT_INLINE(int) git_win32_path_from_c(git_win32_path dest, const char *src)
+{
+	return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src);
+}
+
+GIT_INLINE(int) git_win32_path_to_c(git_win32_path_as_utf8 dest, const wchar_t *src)
+{
+	return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src);
+}
 
 #endif
-
diff --git a/src/win32/version.h b/src/win32/version.h
index 483962b..7966769 100644
--- a/src/win32/version.h
+++ b/src/win32/version.h
@@ -9,12 +9,29 @@
 
 #include <windows.h>
 
-GIT_INLINE(int) git_has_win32_version(int major, int minor)
+GIT_INLINE(int) git_has_win32_version(int major, int minor, int service_pack)
 {
-	WORD wVersion = LOWORD(GetVersion());
+	OSVERSIONINFOEX version_test = {0};
+	DWORD version_test_mask;
+	DWORDLONG version_condition_mask = 0;
+	
+	version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+	version_test.dwMajorVersion = major;
+	version_test.dwMinorVersion = minor;
+	version_test.wServicePackMajor = (WORD)service_pack;
+	version_test.wServicePackMinor = 0;
 
-	return LOBYTE(wVersion) > major ||
-		(LOBYTE(wVersion) == major && HIBYTE(wVersion) >= minor);
+	version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
+
+	VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+	if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+		return 0;
+
+	return 1;
 }
 
 #endif
diff --git a/tests-clar/checkout/checkout_helpers.h b/tests-clar/checkout/checkout_helpers.h
deleted file mode 100644
index 0e8da31..0000000
--- a/tests-clar/checkout/checkout_helpers.h
+++ /dev/null
@@ -1,38 +0,0 @@
-#include "buffer.h"
-#include "git2/object.h"
-#include "git2/repository.h"
-
-extern void strip_cr_from_buf(git_buf *buf);
-extern void assert_on_branch(git_repository *repo, const char *branch);
-extern void reset_index_to_treeish(git_object *treeish);
-
-extern void check_file_contents_at_line(
-	const char *path, const char *expected,
-	const char *file, int line, const char *msg);
-
-extern void check_file_contents_nocr_at_line(
-	const char *path, const char *expected,
-	const char *file, int line, const char *msg);
-
-#define check_file_contents(PATH,EXP) \
-	check_file_contents_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
-
-#define check_file_contents_nocr(PATH,EXP) \
-	check_file_contents_nocr_at_line(PATH,EXP,__FILE__,__LINE__,"String mismatch: " #EXP " != " #PATH)
-
-typedef struct {
-	int n_conflicts;
-	int n_dirty;
-	int n_updates;
-	int n_untracked;
-	int n_ignored;
-	int debug;
-} checkout_counts;
-
-extern int checkout_count_callback(
-	git_checkout_notify_t why,
-	const char *path,
-	const git_diff_file *baseline,
-	const git_diff_file *target,
-	const git_diff_file *workdir,
-	void *payload);
diff --git a/tests-clar/checkout/crlf.c b/tests-clar/checkout/crlf.c
deleted file mode 100644
index 285b1f2..0000000
--- a/tests-clar/checkout/crlf.c
+++ /dev/null
@@ -1,147 +0,0 @@
-#include "clar_libgit2.h"
-#include "checkout_helpers.h"
-
-#include "git2/checkout.h"
-#include "repository.h"
-
-#define UTF8_BOM "\xEF\xBB\xBF"
-#define ALL_CRLF_TEXT_RAW	"crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
-#define ALL_LF_TEXT_RAW		"lf\nlf\nlf\nlf\nlf\n"
-#define MORE_CRLF_TEXT_RAW	"crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
-#define MORE_LF_TEXT_RAW	"lf\nlf\ncrlf\r\nlf\nlf\n"
-
-#define ALL_LF_TEXT_AS_CRLF		"lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
-#define MORE_CRLF_TEXT_AS_CRLF	"crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
-#define MORE_LF_TEXT_AS_CRLF	"lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
-
-static git_repository *g_repo;
-
-void test_checkout_crlf__initialize(void)
-{
-	g_repo = cl_git_sandbox_init("crlf");
-}
-
-void test_checkout_crlf__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-}
-
-void test_checkout_crlf__detect_crlf_autocrlf_false(void)
-{
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", false);
-
-	git_checkout_head(g_repo, &opts);
-
-	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
-	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
-{
-	git_index *index;
-	const git_index_entry *entry;
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", false);
-
-	git_checkout_head(g_repo, &opts);
-
-	git_repository_index(&index, g_repo);
-
-	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
-	cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
-
-	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
-	cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
-
-	git_index_free(index);
-}
-
-void test_checkout_crlf__detect_crlf_autocrlf_true(void)
-{
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
-	git_checkout_head(g_repo, &opts);
-
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
-	else
-		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
-
-	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__more_lf_autocrlf_true(void)
-{
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
-	git_checkout_head(g_repo, &opts);
-
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
-	else
-		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
-}
-
-void test_checkout_crlf__more_crlf_autocrlf_true(void)
-{
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
-	git_checkout_head(g_repo, &opts);
-
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
-	else
-		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
-}
-
-void test_checkout_crlf__all_crlf_autocrlf_true(void)
-{
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
-	git_checkout_head(g_repo, &opts);
-
-	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
-}
-
-void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
-{
-	git_index *index;
-	const git_index_entry *entry;
-	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
-
-	cl_repo_set_bool(g_repo, "core.autocrlf", true);
-
-	git_checkout_head(g_repo, &opts);
-
-	git_repository_index(&index, g_repo);
-
-	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
-
-	if (GIT_EOL_NATIVE == GIT_EOL_LF)
-		cl_assert_equal_sz(strlen(ALL_LF_TEXT_RAW), entry->file_size);
-	else
-		cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
-
-	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
-	cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
-
-	git_index_free(index);
-}
diff --git a/tests-clar/clar_libgit2.h b/tests-clar/clar_libgit2.h
deleted file mode 100644
index 3fcf45a..0000000
--- a/tests-clar/clar_libgit2.h
+++ /dev/null
@@ -1,75 +0,0 @@
-#ifndef __CLAR_LIBGIT2__
-#define __CLAR_LIBGIT2__
-
-#include "clar.h"
-#include <git2.h>
-#include "common.h"
-
-/**
- * Replace for `clar_must_pass` that passes the last library error as the
- * test failure message.
- *
- * Use this wrapper around all `git_` library calls that return error codes!
- */
-#define cl_git_pass(expr) do { \
-	int _lg2_error; \
-	giterr_clear(); \
-	if ((_lg2_error = (expr)) != 0) \
-		cl_git_report_failure(_lg2_error, __FILE__, __LINE__, "Function call failed: " #expr); \
-	} while (0)
-
-/**
- * Wrapper for `clar_must_fail` -- this one is
- * 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)
-
-void cl_git_report_failure(int, const char *, int, const char *);
-
-#define cl_assert_equal_sz(sz1,sz2) cl_assert_equal_i((int)sz1, (int)(sz2))
-
-/*
- * Some utility macros for building long strings
- */
-#define REP4(STR)	 STR STR STR STR
-#define REP15(STR)	 REP4(STR) REP4(STR) REP4(STR) STR STR STR
-#define REP16(STR)	 REP4(REP4(STR))
-#define REP256(STR)  REP16(REP16(STR))
-#define REP1024(STR) REP4(REP256(STR))
-
-/* Write the contents of a buffer to disk */
-void cl_git_mkfile(const char *filename, const char *content);
-void cl_git_append2file(const char *filename, const char *new_content);
-void cl_git_rewritefile(const char *filename, const char *new_content);
-void cl_git_write2file(const char *filename, const char *new_content, int flags, unsigned int mode);
-
-bool cl_toggle_filemode(const char *filename);
-bool cl_is_chmod_supported(void);
-
-/* Environment wrappers */
-char *cl_getenv(const char *name);
-int cl_setenv(const char *name, const char *value);
-
-/* Reliable rename */
-int cl_rename(const char *source, const char *dest);
-
-/* Git sandbox setup helpers */
-
-git_repository *cl_git_sandbox_init(const char *sandbox);
-void cl_git_sandbox_cleanup(void);
-git_repository *cl_git_sandbox_reopen(void);
-
-/* Local-repo url helpers */
-const char* cl_git_fixture_url(const char *fixturename);
-const char* cl_git_path_url(const char *path);
-
-/* Test repository cleaner */
-int cl_git_remove_placeholders(const char *directory_path, const char *filename);
-
-/* config setting helpers */
-void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
-
-#endif
diff --git a/tests-clar/clone/nonetwork.c b/tests-clar/clone/nonetwork.c
deleted file mode 100644
index 339b1e7..0000000
--- a/tests-clar/clone/nonetwork.c
+++ /dev/null
@@ -1,260 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "git2/clone.h"
-#include "remote.h"
-#include "fileops.h"
-#include "repository.h"
-
-#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
-
-static git_clone_options g_options;
-static git_repository *g_repo;
-static git_reference* g_ref;
-static git_remote* g_remote;
-
-void test_clone_nonetwork__initialize(void)
-{
-	git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
-
-	g_repo = NULL;
-
-	memset(&g_options, 0, sizeof(git_clone_options));
-	g_options.version = GIT_CLONE_OPTIONS_VERSION;
-	g_options.checkout_opts = dummy_opts;
-	g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
-}
-
-void test_clone_nonetwork__cleanup(void)
-{
-	if (g_repo) {
-		git_repository_free(g_repo);
-		g_repo = NULL;
-	}
-
-	if (g_ref) {
-		git_reference_free(g_ref);
-		g_ref = NULL;
-	}
-
-	if (g_remote) {
-		git_remote_free(g_remote);
-		g_remote = NULL;
-	}
-
-	cl_fixture_cleanup("./foo");
-}
-
-void test_clone_nonetwork__bad_url(void)
-{
-	/* Clone should clean up the mess if the URL isn't a git repository */
-	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
-	cl_assert(!git_path_exists("./foo"));
-	g_options.bare = true;
-	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
-	cl_assert(!git_path_exists("./foo"));
-}
-
-static int dont_call_me(void *state, git_buf *path)
-{
-	GIT_UNUSED(state);
-	GIT_UNUSED(path);
-	return GIT_ERROR;
-}
-
-void test_clone_nonetwork__do_not_clean_existing_directory(void)
-{
-	git_buf path_buf = GIT_BUF_INIT;
-
-	git_buf_put(&path_buf, "./foo", 5);
-
-	/* Clone should not remove the directory if it already exists, but
-	 * Should clean up entries it creates. */
-	p_mkdir("./foo", GIT_DIR_MODE);
-	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
-	cl_assert(git_path_exists("./foo"));
-
-	/* Make sure the directory is empty. */
-	cl_git_pass(git_path_direach(&path_buf,
-		dont_call_me,
-		NULL));
-
-	/* Try again with a bare repository. */
-	g_options.bare = true;
-	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
-	cl_assert(git_path_exists("./foo"));
-
-	/* Make sure the directory is empty. */
-	cl_git_pass(git_path_direach(&path_buf,
-		dont_call_me,
-		NULL));
-
-	git_buf_free(&path_buf);
-}
-
-void test_clone_nonetwork__local(void)
-{
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__local_absolute_path(void)
-{
-	const char *local_src;
-	local_src = cl_fixture("testrepo.git");
-	cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
-}
-
-void test_clone_nonetwork__local_bare(void)
-{
-	g_options.bare = true;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
-{
-	cl_git_mkfile("./foo", "Bar!");
-	cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
-{
-	p_mkdir("./foo", GIT_DIR_MODE);
-	cl_git_mkfile("./foo/bar", "Baz!");
-	cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__custom_origin_name(void)
-{
-	g_options.remote_name = "my_origin";
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
-}
-
-void test_clone_nonetwork__custom_push_url(void)
-{
-	const char *url = "http://example.com";
-
-	g_options.pushurl = url;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
-	cl_assert_equal_s(url, git_remote_pushurl(g_remote));
-}
-
-void test_clone_nonetwork__custom_fetch_spec(void)
-{
-	const git_refspec *actual_fs;
-	const char *spec = "+refs/heads/master:refs/heads/foo";
-
-	g_options.fetch_spec = spec;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
-	actual_fs = git_remote_get_refspec(g_remote, 0);
-	cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
-	cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
-
-	cl_git_pass(git_reference_lookup(&g_ref, g_repo, "refs/heads/foo"));
-}
-
-void test_clone_nonetwork__custom_push_spec(void)
-{
-	const git_refspec *actual_fs;
-	const char *spec = "+refs/heads/master:refs/heads/foo";
-
-	g_options.push_spec = spec;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
-	actual_fs = git_remote_get_refspec(g_remote, git_remote_refspec_count(g_remote) - 1);
-	cl_assert_equal_s("refs/heads/master", git_refspec_src(actual_fs));
-	cl_assert_equal_s("refs/heads/foo", git_refspec_dst(actual_fs));
-}
-
-void test_clone_nonetwork__custom_autotag(void)
-{
-	git_remote *origin;
-	git_strarray tags = {0};
-
-	g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_tag_list(&tags, g_repo));
-	cl_assert_equal_sz(0, tags.count);
-
-	cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-	cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_NONE, origin->download_tags);
-
-	git_strarray_free(&tags);
-	git_remote_free(origin);
-}
-
-void test_clone_nonetwork__custom_autotag_tags_all(void)
-{
-	git_strarray tags = {0};
-	git_remote *origin;
-
-	g_options.remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_remote_load(&origin, g_repo, "origin"));
-	cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_ALL, origin->download_tags);
-
-	git_strarray_free(&tags);
-	git_remote_free(origin);
-}
-
-void test_clone_nonetwork__cope_with_already_existing_directory(void)
-{
-	p_mkdir("./foo", GIT_DIR_MODE);
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-}
-
-void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
-{
-	git_buf path = GIT_BUF_INIT;
-
-	g_options.checkout_opts.checkout_strategy = 0;
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
-	cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
-
-	git_buf_free(&path);
-}
-
-void test_clone_nonetwork__can_checkout_given_branch(void)
-{
-	g_options.checkout_branch = "test";
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_assert_equal_i(0, git_repository_head_orphan(g_repo));
-
-	cl_git_pass(git_repository_head(&g_ref, g_repo));
-	cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
-}
-
-void test_clone_nonetwork__can_detached_head(void)
-{
-	git_object *obj;
-	git_repository *cloned;
-	git_reference *cloned_head;
-
-	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
-
-	cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
-	cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
-
-	cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
-
-	cl_assert(git_repository_head_detached(cloned));
-
-	cl_git_pass(git_repository_head(&cloned_head, cloned));
-	cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head)));
-
-	git_object_free(obj);
-	git_reference_free(cloned_head);
-	git_repository_free(cloned);
-
-	cl_fixture_cleanup("./foo1");
-}
diff --git a/tests-clar/config/multivar.c b/tests-clar/config/multivar.c
deleted file mode 100644
index 0bda6bc..0000000
--- a/tests-clar/config/multivar.c
+++ /dev/null
@@ -1,165 +0,0 @@
-#include "clar_libgit2.h"
-
-static const char *_name = "remote.fancy.url";
-
-void test_config_multivar__initialize(void)
-{
-	cl_fixture_sandbox("config");
-}
-
-void test_config_multivar__cleanup(void)
-{
-	cl_fixture_cleanup("config");
-}
-
-static int mv_read_cb(const git_config_entry *entry, void *data)
-{
-	int *n = (int *) data;
-
-	if (!strcmp(entry->name, _name))
-		(*n)++;
-
-	return 0;
-}
-
-void test_config_multivar__foreach(void)
-{
-	git_config *cfg;
-	int n = 0;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
-
-	cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
-	cl_assert(n == 2);
-
-	git_config_free(cfg);
-}
-
-static int cb(const git_config_entry *entry, void *data)
-{
-	int *n = (int *) data;
-
-	GIT_UNUSED(entry);
-
-	(*n)++;
-
-	return 0;
-}
-
-void test_config_multivar__get(void)
-{
-	git_config *cfg;
-	int n;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 2);
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, "example", cb, &n));
-	cl_assert(n == 1);
-
-	git_config_free(cfg);
-}
-
-void test_config_multivar__add(void)
-{
-	git_config *cfg;
-	int n;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-	cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 3);
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
-	cl_assert(n == 1);
-
-	git_config_free(cfg);
-
-	/* We know it works in memory, let's see if the file is written correctly */
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 3);
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
-	cl_assert(n == 1);
-
-	git_config_free(cfg);
-}
-
-void test_config_multivar__add_new(void)
-{
-	const char *var = "a.brand.new";
-	git_config *cfg;
-	int n;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, var, NULL, cb, &n));
-	cl_assert(n == 1);
-
-	git_config_free(cfg);
-}
-
-void test_config_multivar__replace(void)
-{
-	git_config *cfg;
-	int n;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 2);
-
-	cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 2);
-
-	git_config_free(cfg);
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, NULL, cb, &n));
-	cl_assert(n == 2);
-
-	git_config_free(cfg);
-}
-
-void test_config_multivar__replace_multiple(void)
-{
-	git_config *cfg;
-	int n;
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-	cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
-	cl_assert(n == 2);
-
-	git_config_free(cfg);
-
-	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
-
-	n = 0;
-	cl_git_pass(git_config_get_multivar(cfg, _name, "otherplace", cb, &n));
-	cl_assert(n == 2);
-
-	git_config_free(cfg);
-}
diff --git a/tests-clar/core/filebuf.c b/tests-clar/core/filebuf.c
deleted file mode 100644
index 4451c01..0000000
--- a/tests-clar/core/filebuf.c
+++ /dev/null
@@ -1,92 +0,0 @@
-#include "clar_libgit2.h"
-#include "filebuf.h"
-
-/* make sure git_filebuf_open doesn't delete an existing lock */
-void test_core_filebuf__0(void)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	int fd;
-	char test[] = "test", testlock[] = "test.lock";
-
-	fd = p_creat(testlock, 0744); //-V536
-
-	cl_must_pass(fd);
-	cl_must_pass(p_close(fd));
-
-	cl_git_fail(git_filebuf_open(&file, test, 0));
-	cl_assert(git_path_exists(testlock));
-
-	cl_must_pass(p_unlink(testlock));
-}
-
-
-/* make sure GIT_FILEBUF_APPEND works as expected */
-void test_core_filebuf__1(void)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	int fd;
-	char test[] = "test";
-
-	fd = p_creat(test, 0666); //-V536
-	cl_must_pass(fd);
-	cl_must_pass(p_write(fd, "libgit2 rocks\n", 14));
-	cl_must_pass(p_close(fd));
-
-	cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND));
-	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
-	cl_git_pass(git_filebuf_commit(&file, 0666));
-
-	cl_must_pass(p_unlink(test));
-}
-
-
-/* make sure git_filebuf_write writes large buffer correctly */
-void test_core_filebuf__2(void)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	char test[] = "test";
-	unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
-
-	memset(buf, 0xfe, sizeof(buf));
-
-	cl_git_pass(git_filebuf_open(&file, test, 0));
-	cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
-	cl_git_pass(git_filebuf_commit(&file, 0666));
-
-	cl_must_pass(p_unlink(test));
-}
-
-/* make sure git_filebuf_cleanup clears the buffer */
-void test_core_filebuf__4(void)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	char test[] = "test";
-
-	cl_assert(file.buffer == NULL);
-
-	cl_git_pass(git_filebuf_open(&file, test, 0));
-	cl_assert(file.buffer != NULL);
-
-	git_filebuf_cleanup(&file);
-	cl_assert(file.buffer == NULL);
-}
-
-
-/* make sure git_filebuf_commit clears the buffer */
-void test_core_filebuf__5(void)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	char test[] = "test";
-
-	cl_assert(file.buffer == NULL);
-
-	cl_git_pass(git_filebuf_open(&file, test, 0));
-	cl_assert(file.buffer != NULL);
-	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
-	cl_assert(file.buffer != NULL);
-
-	cl_git_pass(git_filebuf_commit(&file, 0666));
-	cl_assert(file.buffer == NULL);
-
-	cl_must_pass(p_unlink(test));
-}
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
deleted file mode 100644
index 4e23792..0000000
--- a/tests-clar/diff/diff_helpers.c
+++ /dev/null
@@ -1,220 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-
-git_tree *resolve_commit_oid_to_tree(
-	git_repository *repo,
-	const char *partial_oid)
-{
-	size_t len = strlen(partial_oid);
-	git_oid oid;
-	git_object *obj = NULL;
-	git_tree *tree = NULL;
-
-	if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
-		git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
-	cl_assert(obj);
-	if (git_object_type(obj) == GIT_OBJ_TREE)
-		return (git_tree *)obj;
-	cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
-	cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
-	git_object_free(obj);
-	return tree;
-}
-
-int diff_file_cb(
-	const git_diff_delta *delta,
-	float progress,
-	void *payload)
-{
-	diff_expects *e = payload;
-
-	if (e->debug)
-		fprintf(stderr, "%c %s (%.3f)\n",
-			git_diff_status_char(delta->status),
-			delta->old_file.path, progress);
-
-	if (e->names)
-		cl_assert_equal_s(e->names[e->files], delta->old_file.path);
-	if (e->statuses)
-		cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
-
-	e->files++;
-
-	if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
-		e->files_binary++;
-
-	cl_assert(delta->status <= GIT_DELTA_TYPECHANGE);
-
-	e->file_status[delta->status] += 1;
-
-	return 0;
-}
-
-int diff_print_file_cb(
-	const git_diff_delta *delta,
-	float progress,
-	void *payload)
-{
-	fprintf(stderr, "%c %s\n",
-		git_diff_status_char(delta->status), delta->old_file.path);
-	return diff_file_cb(delta, progress, payload);
-}
-
-int diff_hunk_cb(
-	const git_diff_delta *delta,
-	const git_diff_range *range,
-	const char *header,
-	size_t header_len,
-	void *payload)
-{
-	diff_expects *e = payload;
-
-	GIT_UNUSED(delta);
-	GIT_UNUSED(header);
-	GIT_UNUSED(header_len);
-
-	e->hunks++;
-	e->hunk_old_lines += range->old_lines;
-	e->hunk_new_lines += range->new_lines;
-	return 0;
-}
-
-int diff_line_cb(
-	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin,
-	const char *content,
-	size_t content_len,
-	void *payload)
-{
-	diff_expects *e = payload;
-
-	GIT_UNUSED(delta);
-	GIT_UNUSED(range);
-	GIT_UNUSED(content);
-	GIT_UNUSED(content_len);
-
-	e->lines++;
-	switch (line_origin) {
-	case GIT_DIFF_LINE_CONTEXT:
-	case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
-		e->line_ctxt++;
-		break;
-	case GIT_DIFF_LINE_ADDITION:
-	case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
-		e->line_adds++;
-		break;
-	case GIT_DIFF_LINE_DELETION:
-	case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
-		e->line_dels++;
-		break;
-	default:
-		break;
-	}
-	return 0;
-}
-
-int diff_foreach_via_iterator(
-	git_diff_list *diff,
-	git_diff_file_cb file_cb,
-	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
-	void *data)
-{
-	size_t d, num_d = git_diff_num_deltas(diff);
-
-	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
-		const git_diff_delta *delta;
-		size_t h, num_h;
-
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-		cl_assert(delta);
-
-		/* call file_cb for this file */
-		if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
-			git_diff_patch_free(patch);
-			goto abort;
-		}
-
-		/* if there are no changes, then the patch will be NULL */
-		if (!patch) {
-			cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
-					  (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
-			continue;
-		}
-
-		if (!hunk_cb && !line_cb) {
-			git_diff_patch_free(patch);
-			continue;
-		}
-
-		num_h = git_diff_patch_num_hunks(patch);
-
-		for (h = 0; h < num_h; h++) {
-			const git_diff_range *range;
-			const char *hdr;
-			size_t hdr_len, l, num_l;
-
-			cl_git_pass(git_diff_patch_get_hunk(
-				&range, &hdr, &hdr_len, &num_l, patch, h));
-
-			if (hunk_cb && hunk_cb(delta, range, hdr, hdr_len, data) != 0) {
-				git_diff_patch_free(patch);
-				goto abort;
-			}
-
-			for (l = 0; l < num_l; ++l) {
-				char origin;
-				const char *line;
-				size_t line_len;
-				int old_lineno, new_lineno;
-
-				cl_git_pass(git_diff_patch_get_line_in_hunk(
-					&origin, &line, &line_len, &old_lineno, &new_lineno,
-					patch, h, l));
-
-				if (line_cb &&
-					line_cb(delta, range, origin, line, line_len, data) != 0) {
-					git_diff_patch_free(patch);
-					goto abort;
-				}
-			}
-		}
-
-		git_diff_patch_free(patch);
-	}
-
-	return 0;
-
-abort:
-	giterr_clear();
-	return GIT_EUSER;
-}
-
-static int diff_print_cb(
-	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin, /**< GIT_DIFF_LINE_... value from above */
-	const char *content,
-	size_t content_len,
-	void *payload)
-{
-	GIT_UNUSED(payload);
-	GIT_UNUSED(delta);
-	GIT_UNUSED(range);
-	GIT_UNUSED(line_origin);
-	GIT_UNUSED(content_len);
-	fputs(content, (FILE *)payload);
-	return 0;
-}
-
-void diff_print(FILE *fp, git_diff_list *diff)
-{
-	cl_git_pass(git_diff_print_patch(diff, diff_print_cb, fp ? fp : stderr));
-}
-
-void diff_print_raw(FILE *fp, git_diff_list *diff)
-{
-	cl_git_pass(git_diff_print_raw(diff, diff_print_cb, fp ? fp : stderr));
-}
diff --git a/tests-clar/diff/drivers.c b/tests-clar/diff/drivers.c
deleted file mode 100644
index 06ab2ff..0000000
--- a/tests-clar/diff/drivers.c
+++ /dev/null
@@ -1,125 +0,0 @@
-#include "clar_libgit2.h"
-#include "diff_helpers.h"
-#include "repository.h"
-#include "diff_driver.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_drivers__initialize(void)
-{
-}
-
-void test_diff_drivers__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-	g_repo = NULL;
-}
-
-void test_diff_drivers__patterns(void)
-{
-	git_config *cfg;
-	const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
-	git_tree *one;
-	git_diff_list *diff;
-	git_diff_patch *patch;
-	char *text;
-	const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n   dreamed--too soon--it had sounded.\r\n \r\n                 -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
-	const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
-	const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n   dreamed--too soon--it had sounded.\r\n \r\n                 -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
-
-	g_repo = cl_git_sandbox_init("renames");
-
-	one = resolve_commit_oid_to_tree(g_repo, one_sha);
-
-	/* no diff */
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
-	git_diff_list_free(diff);
-
-	/* default diff */
-
-	cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected0, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	/* attribute diff set to false */
-
-	cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected1, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	/* attribute diff set to unconfigured value (should use default) */
-
-	cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected0, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	/* let's define that driver */
-
-	cl_git_pass(git_repository_config(&cfg, g_repo));
-	cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
-	git_config_free(cfg);
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected1, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	/* let's use a real driver with some regular expressions */
-
-	git_diff_driver_registry_free(g_repo->diff_drivers);
-	g_repo->diff_drivers = NULL;
-
-	cl_git_pass(git_repository_config(&cfg, g_repo));
-	cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
-	cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H"));
-	git_config_free(cfg);
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected2, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	git_tree_free(one);
-}
-
diff --git a/tests-clar/diff/patch.c b/tests-clar/diff/patch.c
deleted file mode 100644
index 3f14a0d..0000000
--- a/tests-clar/diff/patch.c
+++ /dev/null
@@ -1,559 +0,0 @@
-#include "clar_libgit2.h"
-#include "git2/sys/repository.h"
-
-#include "diff_helpers.h"
-#include "repository.h"
-#include "buf_text.h"
-
-static git_repository *g_repo = NULL;
-
-void test_diff_patch__initialize(void)
-{
-}
-
-void test_diff_patch__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-}
-
-#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
-	"deleted file mode 100644\n" \
-	"index e8ee89e..0000000\n" \
-	"--- a/subdir.txt\n" \
-	"+++ /dev/null\n"
-
-#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
-
-static int check_removal_cb(
-	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin,
-	const char *formatted_output,
-	size_t output_len,
-	void *payload)
-{
-	GIT_UNUSED(payload);
-	GIT_UNUSED(output_len);
-
-	switch (line_origin) {
-	case GIT_DIFF_LINE_FILE_HDR:
-		cl_assert_equal_s(EXPECTED_HEADER, formatted_output);
-		cl_assert(range == NULL);
-		goto check_delta;
-
-	case GIT_DIFF_LINE_HUNK_HDR:
-		cl_assert_equal_s(EXPECTED_HUNK, formatted_output);
-		/* Fall through */
-
-	case GIT_DIFF_LINE_CONTEXT:
-	case GIT_DIFF_LINE_DELETION:
-		goto check_range;
-
-	default:
-		/* unexpected code path */
-		return -1;
-	}
-
-check_range:
-	cl_assert(range != NULL);
-	cl_assert_equal_i(1, range->old_start);
-	cl_assert_equal_i(2, range->old_lines);
-	cl_assert_equal_i(0, range->new_start);
-	cl_assert_equal_i(0, range->new_lines);
-
-check_delta:
-	cl_assert_equal_s("subdir.txt", delta->old_file.path);
-	cl_assert_equal_s("subdir.txt", delta->new_file.path);
-	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
-
-	return 0;
-}
-
-void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
-{
-	/*
-	* $ git diff 26a125e..735b6a2
-	* diff --git a/subdir.txt b/subdir.txt
-	* deleted file mode 100644
-	* index e8ee89e..0000000
-	* --- a/subdir.txt
-	* +++ /dev/null
-	* @@ -1,2 +0,0 @@
-	* -Is it a bird?
-	* -Is it a plane?
-	*/
-
-	const char *one_sha = "26a125e";
-	const char *another_sha = "735b6a2";
-	git_tree *one, *another;
-	git_diff_list *diff;
-
-	g_repo = cl_git_sandbox_init("status");
-
-	one = resolve_commit_oid_to_tree(g_repo, one_sha);
-	another = resolve_commit_oid_to_tree(g_repo, another_sha);
-
-	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
-
-	cl_git_pass(git_diff_print_patch(diff, check_removal_cb, NULL));
-
-	git_diff_list_free(diff);
-
-	git_tree_free(another);
-	git_tree_free(one);
-}
-
-void test_diff_patch__to_string(void)
-{
-	const char *one_sha = "26a125e";
-	const char *another_sha = "735b6a2";
-	git_tree *one, *another;
-	git_diff_list *diff;
-	git_diff_patch *patch;
-	char *text;
-	const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
-
-	g_repo = cl_git_sandbox_init("status");
-
-	one = resolve_commit_oid_to_tree(g_repo, one_sha);
-	another = resolve_commit_oid_to_tree(g_repo, another_sha);
-
-	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-
-	cl_assert_equal_s(expected, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-	git_tree_free(another);
-	git_tree_free(one);
-}
-
-void test_diff_patch__config_options(void)
-{
-	const char *one_sha = "26a125e"; /* current HEAD */
-	git_tree *one;
-	git_config *cfg;
-	git_diff_list *diff;
-	git_diff_patch *patch;
-	char *text;
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	char *onefile = "staged_changes_modified_file";
-	const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
-	const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
-	const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
-	const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
-
-	g_repo = cl_git_sandbox_init("status");
-	cl_git_pass(git_repository_config(&cfg, g_repo));
-	one = resolve_commit_oid_to_tree(g_repo, one_sha);
-	opts.pathspec.count = 1;
-	opts.pathspec.strings = &onefile;
-
-
-	cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
-
-	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected1, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected2, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-
-	cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected3, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-
-	cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
-
-	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 0));
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
-	cl_assert_equal_s(expected4, text);
-
-	git__free(text);
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	git_tree_free(one);
-	git_config_free(cfg);
-}
-
-void test_diff_patch__hunks_have_correct_line_numbers(void)
-{
-	git_config *cfg;
-	git_tree *head;
-	git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff;
-	git_diff_patch *patch;
-	const git_diff_delta *delta;
-	const git_diff_range *range;
-	const char *hdr, *text;
-	size_t hdrlen, hunklen, textlen;
-	char origin;
-	int oldno, newno;
-	git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
-	const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n  -- Rudyard Kipling\n";
-
-	g_repo = cl_git_sandbox_init("renames");
-
-	cl_git_pass(git_config_new(&cfg));
-	git_repository_set_config(g_repo, cfg);
-
-	cl_git_pass(
-		git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
-
-	cl_git_rewritefile("renames/songof7cities.txt", new_content);
-
-	cl_git_pass(git_repository_head_tree(&head, g_repo));
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
-
-	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-	cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(patch));
-
-	/* check hunk 0 */
-
-	cl_git_pass(
-		git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
-
-	cl_assert_equal_i(18, (int)hunklen);
-
-	cl_assert_equal_i(6, (int)range->old_start);
-	cl_assert_equal_i(15, (int)range->old_lines);
-	cl_assert_equal_i(6, (int)range->new_start);
-	cl_assert_equal_i(9, (int)range->new_lines);
-
-	cl_assert_equal_i(18, (int)git_diff_patch_num_lines_in_hunk(patch, 0));
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 0));
-	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
-	cl_assert_equal_i(6, oldno);
-	cl_assert_equal_i(6, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
-	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
-	cl_assert_equal_i(9, oldno);
-	cl_assert_equal_i(-1, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 12));
-	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("This is some new text;\n", actual.ptr);
-	cl_assert_equal_i(-1, oldno);
-	cl_assert_equal_i(9, newno);
-
-	/* check hunk 1 */
-
-	cl_git_pass(
-		git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 1));
-
-	cl_assert_equal_i(18, (int)hunklen);
-
-	cl_assert_equal_i(31, (int)range->old_start);
-	cl_assert_equal_i(15, (int)range->old_lines);
-	cl_assert_equal_i(25, (int)range->new_start);
-	cl_assert_equal_i(9, (int)range->new_lines);
-
-	cl_assert_equal_i(18, (int)git_diff_patch_num_lines_in_hunk(patch, 1));
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 1, 0));
-	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
-	cl_assert_equal_i(31, oldno);
-	cl_assert_equal_i(25, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 1, 3));
-	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
-	cl_assert_equal_i(34, oldno);
-	cl_assert_equal_i(-1, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 1, 12));
-	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("Another replacement;\n", actual.ptr);
-	cl_assert_equal_i(-1, oldno);
-	cl_assert_equal_i(28, newno);
-
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	/* Let's check line numbers when there is no newline */
-
-	git_buf_rtrim(&old_content);
-	cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
-
-	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
-
-	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(patch));
-
-	/* check hunk 0 */
-
-	cl_git_pass(
-		git_diff_patch_get_hunk(&range, &hdr, &hdrlen, &hunklen, patch, 0));
-
-	cl_assert_equal_i(6, (int)hunklen);
-
-	cl_assert_equal_i(46, (int)range->old_start);
-	cl_assert_equal_i(4, (int)range->old_lines);
-	cl_assert_equal_i(46, (int)range->new_start);
-	cl_assert_equal_i(4, (int)range->new_lines);
-
-	cl_assert_equal_i(6, (int)git_diff_patch_num_lines_in_hunk(patch, 0));
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 1));
-	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
-	cl_assert_equal_i(47, oldno);
-	cl_assert_equal_i(47, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 2));
-	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("\n", actual.ptr);
-	cl_assert_equal_i(48, oldno);
-	cl_assert_equal_i(48, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 3));
-	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("  -- Rudyard Kipling\n", actual.ptr);
-	cl_assert_equal_i(49, oldno);
-	cl_assert_equal_i(-1, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 4));
-	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("  -- Rudyard Kipling", actual.ptr);
-	cl_assert_equal_i(-1, oldno);
-	cl_assert_equal_i(49, newno);
-
-	cl_git_pass(git_diff_patch_get_line_in_hunk(
-		&origin, &text, &textlen, &oldno, &newno, patch, 0, 5));
-	cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)origin);
-	cl_git_pass(git_buf_set(&actual, text, textlen));
-	cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
-	cl_assert_equal_i(-1, oldno);
-	cl_assert_equal_i(49, newno);
-
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-
-	git_buf_free(&actual);
-	git_buf_free(&old_content);
-	git_tree_free(head);
-	git_config_free(cfg);
-}
-
-static void check_single_patch_stats(
-	git_repository *repo, size_t hunks,
-	size_t adds, size_t dels, size_t ctxt,
-	const char *expected)
-{
-	git_diff_list *diff;
-	git_diff_patch *patch;
-	const git_diff_delta *delta;
-	size_t actual_ctxt, actual_adds, actual_dels;
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
-
-	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
-
-	cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
-	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
-
-	cl_assert_equal_i((int)hunks, (int)git_diff_patch_num_hunks(patch));
-
-	cl_git_pass( git_diff_patch_line_stats(
-		&actual_ctxt, &actual_adds, &actual_dels, patch) );
-
-	cl_assert_equal_sz(ctxt, actual_ctxt);
-	cl_assert_equal_sz(adds, actual_adds);
-	cl_assert_equal_sz(dels, actual_dels);
-
-	if (expected != NULL) {
-		char *text;
-		cl_git_pass(git_diff_patch_to_str(&text, patch));
-		cl_assert_equal_s(expected, text);
-		git__free(text);
-	}
-
-	/* walk lines in hunk with basic sanity checks */
-	for (; hunks > 0; --hunks) {
-		size_t i, max_i;
-		int lastoldno = -1, oldno, lastnewno = -1, newno;
-		char origin;
-
-		max_i = git_diff_patch_num_lines_in_hunk(patch, hunks - 1);
-
-		for (i = 0; i < max_i; ++i) {
-			int expected = 1;
-
-			cl_git_pass(git_diff_patch_get_line_in_hunk(
-				&origin, NULL, NULL, &oldno, &newno, patch, hunks - 1, i));
-
-			if (origin == GIT_DIFF_LINE_ADD_EOFNL ||
-				origin == GIT_DIFF_LINE_DEL_EOFNL ||
-				origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
-				expected = 0;
-
-			if (oldno >= 0) {
-				if (lastoldno >= 0)
-					cl_assert_equal_i(expected, oldno - lastoldno);
-				lastoldno = oldno;
-			}
-			if (newno >= 0) {
-				if (lastnewno >= 0)
-					cl_assert_equal_i(expected, newno - lastnewno);
-				lastnewno = newno;
-			}
-		}
-	}
-
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
-}
-
-void test_diff_patch__line_counts_with_eofnl(void)
-{
-	git_config *cfg;
-	git_buf content = GIT_BUF_INIT;
-	const char *end;
-	git_index *index;
-
-	g_repo = cl_git_sandbox_init("renames");
-
-	cl_git_pass(git_config_new(&cfg));
-	git_repository_set_config(g_repo, cfg);
-
-	cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
-
-	/* remove first line */
-
-	end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
-	git_buf_consume(&content, end);
-	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
-	check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL);
-
-	/* remove trailing whitespace */
-
-	git_buf_rtrim(&content);
-	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
-	check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL);
-
-	/* add trailing whitespace */
-
-	cl_git_pass(git_repository_index(&index, g_repo));
-	cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
-	cl_git_pass(git_index_write(index));
-	git_index_free(index);
-
-	cl_git_pass(git_buf_putc(&content, '\n'));
-	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
-	check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL);
-
-	/* no trailing whitespace as context line */
-
-	{
-		/* walk back a couple lines, make space and insert char */
-		char *scan = content.ptr + content.size;
-		int i;
-
-		for (i = 0; i < 5; ++i) {
-			for (--scan; scan > content.ptr && *scan != '\n'; --scan)
-				/* seek to prev \n */;
-		}
-		cl_assert(scan > content.ptr);
-
-		/* overwrite trailing \n with right-shifted content */
-		memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
-		/* insert '#' char into space we created */
-		scan[1] = '#';
-	}
-	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
-
-	check_single_patch_stats(
-		g_repo, 1, 1, 1, 6,
-		/* below is pasted output of 'git diff' with fn context removed */
-		"diff --git a/songof7cities.txt b/songof7cities.txt\n"
-		"index 378a7d9..3d0154e 100644\n"
-		"--- a/songof7cities.txt\n"
-		"+++ b/songof7cities.txt\n"
-		"@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
-		" \n"
-		" To the sound of trumpets shall their seed restore my Cities\n"
-		" Wealthy and well-weaponed, that once more may I behold\n"
-		"-All the world go softly when it walks before my Cities,\n"
-		"+#All the world go softly when it walks before my Cities,\n"
-		" And the horses and the chariots fleeing from them as of old!\n"
-		" \n"
-		"   -- Rudyard Kipling\n"
-		"\\ No newline at end of file\n");
-
-	git_buf_free(&content);
-	git_config_free(cfg);
-}
diff --git a/tests-clar/diff/submodules.c b/tests-clar/diff/submodules.c
deleted file mode 100644
index 6e52a63..0000000
--- a/tests-clar/diff/submodules.c
+++ /dev/null
@@ -1,169 +0,0 @@
-#include "clar_libgit2.h"
-#include "repository.h"
-#include "posix.h"
-#include "../submodule/submodule_helpers.h"
-
-static git_repository *g_repo = NULL;
-
-static void setup_submodules(void)
-{
-	g_repo = cl_git_sandbox_init("submodules");
-	cl_fixture_sandbox("testrepo.git");
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
-}
-
-static void setup_submodules2(void)
-{
-	g_repo = cl_git_sandbox_init("submod2");
-
-	cl_fixture_sandbox("submod2_target");
-	p_rename("submod2_target/.gitted", "submod2_target/.git");
-
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-	p_rename("submod2/not/.gitted", "submod2/not/.git");
-}
-
-void test_diff_submodules__initialize(void)
-{
-}
-
-void test_diff_submodules__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-
-	cl_fixture_cleanup("testrepo.git");
-	cl_fixture_cleanup("submod2_target");
-}
-
-static void check_diff_patches(git_diff_list *diff, const char **expected)
-{
-	const git_diff_delta *delta;
-	git_diff_patch *patch = NULL;
-	size_t d, num_d = git_diff_num_deltas(diff);
-	char *patch_text;
-
-	for (d = 0; d < num_d; ++d, git_diff_patch_free(patch)) {
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-
-		if (delta->status == GIT_DELTA_UNMODIFIED)
-			continue;
-
-		if (expected[d] && !strcmp(expected[d], "<SKIP>"))
-			continue;
-		if (expected[d] && !strcmp(expected[d], "<END>"))
-			cl_assert(0);
-
-		cl_git_pass(git_diff_patch_to_str(&patch_text, patch));
-
-		cl_assert_equal_s(expected[d], patch_text);
-		git__free(patch_text);
-	}
-
-	cl_assert(expected[d] && !strcmp(expected[d], "<END>"));
-}
-
-void test_diff_submodules__unmodified_submodule(void)
-{
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
-	static const char *expected[] = {
-		"<SKIP>", /* .gitmodules */
-		NULL, /* added */
-		NULL, /* ignored */
-		"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
-		NULL, /* testrepo.git */
-		NULL, /* unmodified */
-		NULL, /* untracked */
-		"<END>"
-	};
-
-	setup_submodules();
-
-	opts.flags = GIT_DIFF_INCLUDE_IGNORED |
-		GIT_DIFF_INCLUDE_UNTRACKED |
-		GIT_DIFF_INCLUDE_UNMODIFIED;
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-	check_diff_patches(diff, expected);
-	git_diff_list_free(diff);
-}
-
-void test_diff_submodules__dirty_submodule(void)
-{
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
-	static const char *expected[] = {
-		"<SKIP>", /* .gitmodules */
-		NULL, /* added */
-		NULL, /* ignored */
-		"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
-		"diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
-		NULL, /* unmodified */
-		NULL, /* untracked */
-		"<END>"
-	};
-
-	setup_submodules();
-
-	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
-	cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
-
-	opts.flags = GIT_DIFF_INCLUDE_IGNORED |
-		GIT_DIFF_INCLUDE_UNTRACKED |
-		GIT_DIFF_INCLUDE_UNMODIFIED;
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-	check_diff_patches(diff, expected);
-	git_diff_list_free(diff);
-}
-
-void test_diff_submodules__submod2_index_to_wd(void)
-{
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
-	static const char *expected[] = {
-		"<SKIP>", /* .gitmodules */
-		NULL, /* not-submodule */
-		NULL, /* not */
-		"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
-		"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
-		"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
-		"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
-		"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
-		"<END>"
-	};
-
-	setup_submodules2();
-
-	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
-
-	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
-	check_diff_patches(diff, expected);
-	git_diff_list_free(diff);
-}
-
-void test_diff_submodules__submod2_head_to_index(void)
-{
-	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_tree *head;
-	git_diff_list *diff = NULL;
-	static const char *expected[] = {
-		"<SKIP>", /* .gitmodules */
-		"diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
-		"<END>"
-	};
-
-	setup_submodules2();
-
-	cl_git_pass(git_repository_head_tree(&head, g_repo));
-
-	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
-
-	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
-	check_diff_patches(diff, expected);
-	git_diff_list_free(diff);
-
-	git_tree_free(head);
-}
diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c
deleted file mode 100644
index 3cb8a25..0000000
--- a/tests-clar/network/remote/local.c
+++ /dev/null
@@ -1,160 +0,0 @@
-#include "clar_libgit2.h"
-#include "buffer.h"
-#include "path.h"
-#include "posix.h"
-
-static git_repository *repo;
-static git_buf file_path_buf = GIT_BUF_INIT;
-static git_remote *remote;
-
-void test_network_remote_local__initialize(void)
-{
-	cl_git_pass(git_repository_init(&repo, "remotelocal/", 0));
-	cl_assert(repo != NULL);
-}
-
-void test_network_remote_local__cleanup(void)
-{
-	git_buf_free(&file_path_buf);
-
-	git_remote_free(remote);
-	remote = NULL;
-
-	git_repository_free(repo);
-	repo = NULL;
-
-	cl_fixture_cleanup("remotelocal");
-}
-
-static int count_ref__cb(git_remote_head *head, void *payload)
-{
-	int *count = (int *)payload;
-
-	(void)head;
-	(*count)++;
-
-	return 0;
-}
-
-static int ensure_peeled__cb(git_remote_head *head, void *payload)
-{
-	GIT_UNUSED(payload);
-
-	if(strcmp(head->name, "refs/tags/test^{}") != 0)
-		return 0;
-
-	return git_oid_streq(&head->oid, "e90810b8df3e80c413d903f631643c716887138d");
-}
-
-static void connect_to_local_repository(const char *local_repository)
-{
-	git_buf_sets(&file_path_buf, cl_git_path_url(local_repository));
-
-	cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf)));
-	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-
-}
-
-void test_network_remote_local__connected(void)
-{
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-	cl_assert(git_remote_connected(remote));
-
-	git_remote_disconnect(remote);
-	cl_assert(!git_remote_connected(remote));
-}
-
-void test_network_remote_local__retrieve_advertised_references(void)
-{
-	int how_many_refs = 0;
-
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-
-	cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
-
-	cl_assert_equal_i(how_many_refs, 28);
-}
-
-void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
-{
-	int how_many_refs = 0;
-
-	cl_fixture_sandbox("testrepo.git");
-	cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
-
-	connect_to_local_repository("spaced testrepo.git");
-
-	cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
-
-	cl_assert_equal_i(how_many_refs, 28);
-
-	git_remote_free(remote);	/* Disconnect from the "spaced repo" before the cleanup */
-	remote = NULL;
-
-	cl_fixture_cleanup("spaced testrepo.git");
-}
-
-void test_network_remote_local__nested_tags_are_completely_peeled(void)
-{
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-
-	cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL));
-}
-
-void test_network_remote_local__shorthand_fetch_refspec0(void)
-{
-	const char *refspec = "master:remotes/sloppy/master";
-	const char *refspec2 = "master:boh/sloppy/master";
-
-	git_reference *ref;
-
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-	cl_git_pass(git_remote_add_fetch(remote, refspec));
-	cl_git_pass(git_remote_add_fetch(remote, refspec2));
-
-	cl_git_pass(git_remote_download(remote, NULL, NULL));
-	cl_git_pass(git_remote_update_tips(remote));
-
-	cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
-	git_reference_free(ref);
-
-	cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
-	git_reference_free(ref);
-}
-
-void test_network_remote_local__shorthand_fetch_refspec1(void)
-{
-	const char *refspec = "master";
-	const char *refspec2 = "hard_tag";
-
-	git_reference *ref;
-
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-	git_remote_clear_refspecs(remote);
-	cl_git_pass(git_remote_add_fetch(remote, refspec));
-	cl_git_pass(git_remote_add_fetch(remote, refspec2));
-
-	cl_git_pass(git_remote_download(remote, NULL, NULL));
-	cl_git_pass(git_remote_update_tips(remote));
-
-	cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
-
-	cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
-}
-
-void test_network_remote_local__tagopt(void)
-{
-	git_reference *ref;
-
-	connect_to_local_repository(cl_fixture("testrepo.git"));
-	git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
-
-	cl_git_pass(git_remote_download(remote, NULL, NULL));
-	cl_git_pass(git_remote_update_tips(remote));
-
-
-	cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
-
-	cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
-	git_reference_free(ref);
-}
diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c
deleted file mode 100644
index 173e57d..0000000
--- a/tests-clar/network/urlparse.c
+++ /dev/null
@@ -1,82 +0,0 @@
-#include "clar_libgit2.h"
-#include "netops.h"
-
-char *host, *port, *user, *pass;
-
-void test_network_urlparse__initialize(void)
-{
-	host = port = user = pass = NULL;
-}
-
-void test_network_urlparse__cleanup(void)
-{
-#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
-	FREE_AND_NULL(host);
-	FREE_AND_NULL(port);
-	FREE_AND_NULL(user);
-	FREE_AND_NULL(pass);
-}
-
-void test_network_urlparse__trivial(void)
-{
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"example.com/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "8080");
-	cl_assert_equal_p(user, NULL);
-	cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user(void)
-{
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"user@example.com/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "8080");
-	cl_assert_equal_s(user, "user");
-	cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_pass(void)
-{
-	/* user:pass@hostname.tld/resource */
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"user:pass@example.com/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "8080");
-	cl_assert_equal_s(user, "user");
-	cl_assert_equal_s(pass, "pass");
-}
-
-void test_network_urlparse__port(void)
-{
-	/* hostname.tld:port/resource */
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"example.com:9191/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "9191");
-	cl_assert_equal_p(user, NULL);
-	cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_port(void)
-{
-	/* user@hostname.tld:port/resource */
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"user@example.com:9191/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "9191");
-	cl_assert_equal_s(user, "user");
-	cl_assert_equal_p(pass, NULL);
-}
-
-void test_network_urlparse__user_pass_port(void)
-{
-	/* user:pass@hostname.tld:port/resource */
-	cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass,
-				"user:pass@example.com:9191/resource", "8080"));
-	cl_assert_equal_s(host, "example.com");
-	cl_assert_equal_s(port, "9191");
-	cl_assert_equal_s(user, "user");
-	cl_assert_equal_s(pass, "pass");
-}
diff --git a/tests-clar/object/blob/filter.c b/tests-clar/object/blob/filter.c
deleted file mode 100644
index 042bdda..0000000
--- a/tests-clar/object/blob/filter.c
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "clar_libgit2.h"
-#include "posix.h"
-#include "blob.h"
-#include "filter.h"
-#include "buf_text.h"
-
-static git_repository *g_repo = NULL;
-#define NUM_TEST_OBJECTS 8
-static git_oid g_oids[NUM_TEST_OBJECTS];
-static const char *g_raw[NUM_TEST_OBJECTS] = {
-	"",
-	"foo\nbar\n",
-	"foo\rbar\r",
-	"foo\r\nbar\r\n",
-	"foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
-	"123\n\000\001\002\003\004abc\255\254\253\r\n",
-	"\xEF\xBB\xBFThis is UTF-8\n",
-	"\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
-};
-static git_off_t g_len[NUM_TEST_OBJECTS] = { -1, -1, -1, -1, -1, 17, -1, 12 };
-static git_buf_text_stats g_stats[NUM_TEST_OBJECTS] = {
-	{ 0, 0, 0, 0, 0, 0, 0 },
-	{ 0, 0, 0, 2, 0, 6, 0 },
-	{ 0, 0, 2, 0, 0, 6, 0 },
-	{ 0, 0, 2, 2, 2, 6, 0 },
-	{ 0, 0, 4, 4, 1, 31, 0 },
-	{ 0, 1, 1, 2, 1, 9, 5 },
-	{ GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
-	{ GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
-};
-static git_buf g_crlf_filtered[NUM_TEST_OBJECTS] = {
-	{ "", 0, 0 },
-	{ "foo\nbar\n", 0, 8 },
-	{ "foo\rbar\r", 0, 8 },
-	{ "foo\nbar\n", 0, 8 },
-	{ "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
-	{ "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
-	{ "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
-	{ "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
-};
-
-void test_object_blob_filter__initialize(void)
-{
-	int i;
-
-	cl_fixture_sandbox("empty_standard_repo");
-	cl_git_pass(p_rename(
-		"empty_standard_repo/.gitted", "empty_standard_repo/.git"));
-	cl_git_pass(git_repository_open(&g_repo, "empty_standard_repo"));
-
-	for (i = 0; i < NUM_TEST_OBJECTS; i++) {
-		size_t len = (g_len[i] < 0) ? strlen(g_raw[i]) : (size_t)g_len[i];
-		g_len[i] = (git_off_t)len;
-
-		cl_git_pass(
-			git_blob_create_frombuffer(&g_oids[i], g_repo, g_raw[i], len)
-		);
-	}
-}
-
-void test_object_blob_filter__cleanup(void)
-{
-	git_repository_free(g_repo);
-	g_repo = NULL;
-	cl_fixture_cleanup("empty_standard_repo");
-}
-
-void test_object_blob_filter__unfiltered(void)
-{
-	int i;
-	git_blob *blob;
-
-	for (i = 0; i < NUM_TEST_OBJECTS; i++) {
-		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
-		cl_assert(g_len[i] == git_blob_rawsize(blob));
-		cl_assert(memcmp(git_blob_rawcontent(blob), g_raw[i], (size_t)g_len[i]) == 0);
-		git_blob_free(blob);
-	}
-}
-
-void test_object_blob_filter__stats(void)
-{
-	int i;
-	git_blob *blob;
-	git_buf buf = GIT_BUF_INIT;
-	git_buf_text_stats stats;
-
-	for (i = 0; i < NUM_TEST_OBJECTS; i++) {
-		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
-		cl_git_pass(git_blob__getbuf(&buf, blob));
-		git_buf_text_gather_stats(&stats, &buf, false);
-		cl_assert(memcmp(&g_stats[i], &stats, sizeof(stats)) == 0);
-		git_blob_free(blob);
-	}
-
-	git_buf_free(&buf);
-}
-
-void test_object_blob_filter__to_odb(void)
-{
-	git_vector filters = GIT_VECTOR_INIT;
-	git_config *cfg;
-	int i;
-	git_blob *blob;
-	git_buf orig = GIT_BUF_INIT, out = GIT_BUF_INIT;
-
-	cl_git_pass(git_repository_config(&cfg, g_repo));
-	cl_assert(cfg);
-
-	git_attr_cache_flush(g_repo);
-	cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
-
-	cl_assert(git_filters_load(
-		&filters, g_repo, "filename.txt", GIT_FILTER_TO_ODB) > 0);
-	cl_assert(filters.length == 1);
-
-	for (i = 0; i < NUM_TEST_OBJECTS; i++) {
-		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_oids[i]));
-		cl_git_pass(git_blob__getbuf(&orig, blob));
-
-		cl_git_pass(git_filters_apply(&out, &orig, &filters));
-		cl_assert(git_buf_cmp(&out, &g_crlf_filtered[i]) == 0);
-
-		git_blob_free(blob);
-	}
-
-	git_filters_free(&filters);
-	git_buf_free(&orig);
-	git_buf_free(&out);
-	git_config_free(cfg);
-}
-
diff --git a/tests-clar/object/tree/walk.c b/tests-clar/object/tree/walk.c
deleted file mode 100644
index b7af492..0000000
--- a/tests-clar/object/tree/walk.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include "clar_libgit2.h"
-#include "tree.h"
-
-static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
-static git_repository *g_repo;
-
-void test_object_tree_walk__initialize(void)
-{
-   g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_object_tree_walk__cleanup(void)
-{
-   cl_git_sandbox_cleanup();
-}
-
-static int treewalk_count_cb(
-	const char *root, const git_tree_entry *entry, void *payload)
-{
-	int *count = payload;
-
-	GIT_UNUSED(root);
-	GIT_UNUSED(entry);
-
-	(*count) += 1;
-
-	return 0;
-}
-
-void test_object_tree_walk__0(void)
-{
-	git_oid id;
-	git_tree *tree;
-	int ct;
-
-	git_oid_fromstr(&id, tree_oid);
-
-	cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
-
-	ct = 0;
-	cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
-	cl_assert_equal_i(3, ct);
-
-	ct = 0;
-	cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
-	cl_assert_equal_i(3, ct);
-
-	git_tree_free(tree);
-}
-
-
-static int treewalk_stop_cb(
-	const char *root, const git_tree_entry *entry, void *payload)
-{
-	int *count = payload;
-
-	GIT_UNUSED(root);
-	GIT_UNUSED(entry);
-
-	(*count) += 1;
-
-	return (*count == 2) ? -1 : 0;
-}
-
-static int treewalk_stop_immediately_cb(
-	const char *root, const git_tree_entry *entry, void *payload)
-{
-	GIT_UNUSED(root);
-	GIT_UNUSED(entry);
-	GIT_UNUSED(payload);
-	return -100;
-}
-
-void test_object_tree_walk__1(void)
-{
-	git_oid id;
-	git_tree *tree;
-	int ct;
-
-	git_oid_fromstr(&id, tree_oid);
-
-	cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
-
-	ct = 0;
-	cl_assert_equal_i(
-		GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
-	cl_assert_equal_i(2, ct);
-
-	ct = 0;
-	cl_assert_equal_i(
-		GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
-	cl_assert_equal_i(2, ct);
-
-	cl_assert_equal_i(
-		GIT_EUSER, git_tree_walk(
-			tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
-
-	cl_assert_equal_i(
-		GIT_EUSER, git_tree_walk(
-			tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
-
-	git_tree_free(tree);
-}
diff --git a/tests-clar/odb/loose.c b/tests-clar/odb/loose.c
deleted file mode 100644
index 9539bb2..0000000
--- a/tests-clar/odb/loose.c
+++ /dev/null
@@ -1,89 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-#include "posix.h"
-#include "loose_data.h"
-
-static void write_object_files(object_data *d)
-{
-	int fd;
-
-	if (p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0)
-		cl_assert(errno == EEXIST);
-
-	cl_assert((fd = p_creat(d->file, S_IREAD | S_IWRITE)) >= 0);
-	cl_must_pass(p_write(fd, d->bytes, d->blen));
-
-	p_close(fd);
-}
-
-static void cmp_objects(git_rawobj *o, object_data *d)
-{
-	cl_assert(o->type == git_object_string2type(d->type));
-	cl_assert(o->len == d->dlen);
-
-	if (o->len > 0)
-		cl_assert(memcmp(o->data, d->data, o->len) == 0);
-}
-
-static void test_read_object(object_data *data)
-{
-    git_oid id;
-    git_odb_object *obj;
-	git_odb *odb;
-	git_rawobj tmp;
-
-    write_object_files(data);
-
-    cl_git_pass(git_odb_open(&odb, "test-objects"));
-    cl_git_pass(git_oid_fromstr(&id, data->id));
-    cl_git_pass(git_odb_read(&obj, odb, &id));
-
-	tmp.data = obj->buffer;
-	tmp.len = obj->cached.size;
-	tmp.type = obj->cached.type;
-
-    cmp_objects(&tmp, data);
-
-    git_odb_object_free(obj);
-	git_odb_free(odb);
-}
-
-void test_odb_loose__initialize(void)
-{
-	cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
-}
-
-void test_odb_loose__cleanup(void)
-{
-	cl_fixture_cleanup("test-objects");
-}
-
-void test_odb_loose__exists(void)
-{
-    git_oid id, id2;
-	git_odb *odb;
-
-    write_object_files(&one);
-	cl_git_pass(git_odb_open(&odb, "test-objects"));
-
-    cl_git_pass(git_oid_fromstr(&id, one.id));
-
-    cl_assert(git_odb_exists(odb, &id));
-
-	/* Test for a non-existant object */
-    cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
-    cl_assert(!git_odb_exists(odb, &id2));
-
-	git_odb_free(odb);
-}
-
-void test_odb_loose__simple_reads(void)
-{
-	test_read_object(&commit);
-	test_read_object(&tree);
-	test_read_object(&tag);
-	test_read_object(&zero);
-	test_read_object(&one);
-	test_read_object(&two);
-	test_read_object(&some);
-}
diff --git a/tests-clar/odb/mixed.c b/tests-clar/odb/mixed.c
deleted file mode 100644
index da0ed97..0000000
--- a/tests-clar/odb/mixed.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "clar_libgit2.h"
-#include "odb.h"
-
-static git_odb *_odb;
-
-void test_odb_mixed__initialize(void)
-{
-	cl_git_pass(git_odb_open(&_odb, cl_fixture("duplicate.git/objects")));
-}
-
-void test_odb_mixed__cleanup(void)
-{
-	git_odb_free(_odb);
-	_odb = NULL;
-}
-
-void test_odb_mixed__dup_oid(void) {
-	const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
-	git_oid oid;
-	git_odb_object *obj;
-	cl_git_pass(git_oid_fromstr(&oid, hex));
-	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
-	git_odb_object_free(obj);
-}
-
diff --git a/tests-clar/refs/branches/foreach.c b/tests-clar/refs/branches/foreach.c
deleted file mode 100644
index 433812c..0000000
--- a/tests-clar/refs/branches/foreach.c
+++ /dev/null
@@ -1,173 +0,0 @@
-#include "clar_libgit2.h"
-#include "refs.h"
-
-static git_repository *repo;
-static git_reference *fake_remote;
-
-void test_refs_branches_foreach__initialize(void)
-{
-	git_oid id;
-
-	cl_fixture_sandbox("testrepo.git");
-	cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-
-	cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
-	cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
-}
-
-void test_refs_branches_foreach__cleanup(void)
-{
-	git_reference_free(fake_remote);
-	fake_remote = NULL;
-
-	git_repository_free(repo);
-	repo = NULL;
-
-	cl_fixture_cleanup("testrepo.git");
-
-	cl_git_sandbox_cleanup();
-}
-
-static int count_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
-	int *count;
-
-	GIT_UNUSED(branch_type);
-	GIT_UNUSED(branch_name);
-
-	count = (int *)payload;
-	(*count)++;
-
-	return 0;
-}
-
-static void assert_retrieval(unsigned int flags, unsigned int expected_count)
-{
-	int count = 0;
-
-	cl_git_pass(git_branch_foreach(repo, flags, count_branch_list_cb, &count));
-
-	cl_assert_equal_i(expected_count, count);
-}
-
-void test_refs_branches_foreach__retrieve_all_branches(void)
-{
-	assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14);
-}
-
-void test_refs_branches_foreach__retrieve_remote_branches(void)
-{
-	assert_retrieval(GIT_BRANCH_REMOTE, 2);
-}
-
-void test_refs_branches_foreach__retrieve_local_branches(void)
-{
-	assert_retrieval(GIT_BRANCH_LOCAL, 12);
-}
-
-struct expectations {
-	const char *branch_name;
-	int encounters;
-};
-
-static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
-{
-	int pos = 0;
-
-	for (pos = 0; findings[pos].branch_name; ++pos) {
-		if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
-			cl_assert_equal_i(1, findings[pos].encounters);
-			return;
-		}
-	}
-
-	cl_fail("expected branch not found in list.");
-}
-
-static int contains_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
-	int pos = 0;
-	struct expectations *exp;
-
-	GIT_UNUSED(branch_type);
-
-	exp = (struct expectations *)payload;
-
-	for (pos = 0; exp[pos].branch_name; ++pos) {
-		if (strcmp(branch_name, exp[pos].branch_name) == 0)
-			exp[pos].encounters++;
-	}
-
-	return 0;
-}
-
-/*
- * $ git branch -r
- *  nulltoken/HEAD -> nulltoken/master
- *  nulltoken/master
- */
-void test_refs_branches_foreach__retrieve_remote_symbolic_HEAD_when_present(void)
-{
-	struct expectations exp[] = {
-		{ "nulltoken/HEAD", 0 },
-		{ "nulltoken/master", 0 },
-		{ NULL, 0 }
-	};
-
-	git_reference_free(fake_remote);
-	cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
-
-	assert_retrieval(GIT_BRANCH_REMOTE, 3);
-
-	cl_git_pass(git_branch_foreach(repo, GIT_BRANCH_REMOTE, contains_branch_list_cb, &exp));
-
-	assert_branch_has_been_found(exp, "nulltoken/HEAD");
-	assert_branch_has_been_found(exp, "nulltoken/master");
-}
-
-static int branch_list_interrupt_cb(
-	const char *branch_name, git_branch_t branch_type, void *payload)
-{
-	int *count;
-
-	GIT_UNUSED(branch_type);
-	GIT_UNUSED(branch_name);
-
-	count = (int *)payload;
-	(*count)++;
-
-	return (*count == 5);
-}
-
-void test_refs_branches_foreach__can_cancel(void)
-{
-	int count = 0;
-
-	cl_assert_equal_i(GIT_EUSER,
-		git_branch_foreach(repo, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE,
-			branch_list_interrupt_cb, &count));
-
-	cl_assert_equal_i(5, count);
-}
-
-void test_refs_branches_foreach__mix_of_packed_and_loose(void)
-{
-	struct expectations exp[] = {
-		{ "master", 0 },
-		{ "origin/HEAD", 0 },
-		{ "origin/master", 0 },
-		{ "origin/packed", 0 },
-		{ NULL, 0 }
-	};
-	git_repository *r2;
-
-	r2 = cl_git_sandbox_init("testrepo2");
-
-	cl_git_pass(git_branch_foreach(r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE,
-		contains_branch_list_cb, &exp));
-
-	assert_branch_has_been_found(exp, "master");
-	assert_branch_has_been_found(exp, "origin/HEAD");
-	assert_branch_has_been_found(exp, "origin/master");
-	assert_branch_has_been_found(exp, "origin/packed");
-}
diff --git a/tests-clar/refs/unicode.c b/tests-clar/refs/unicode.c
deleted file mode 100644
index 2ec1032..0000000
--- a/tests-clar/refs/unicode.c
+++ /dev/null
@@ -1,44 +0,0 @@
-#include "clar_libgit2.h"
-
-static git_repository *repo;
-
-void test_refs_unicode__initialize(void)
-{
-	cl_fixture_sandbox("testrepo.git");
-
-	cl_git_pass(git_repository_open(&repo, "testrepo.git"));
-}
-
-void test_refs_unicode__cleanup(void)
-{
-	git_repository_free(repo);
-	repo = NULL;
-
-	cl_fixture_cleanup("testrepo.git");
-}
-
-void test_refs_unicode__create_and_lookup(void)
-{
-	git_reference *ref0, *ref1, *ref2;
-	git_repository *repo2;
-
-	const char *REFNAME = "refs/heads/" "\305" "ngstr" "\366" "m";
-	const char *master = "refs/heads/master";
-
-	/* Create the reference */
-	cl_git_pass(git_reference_lookup(&ref0, repo, master));
-	cl_git_pass(git_reference_create(&ref1, repo, REFNAME, git_reference_target(ref0), 0));
-	cl_assert_equal_s(REFNAME, git_reference_name(ref1));
-
-	/* Lookup the reference in a different instance of the repository */
-	cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
-	cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
-
-	cl_assert(git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)) == 0);
-	cl_assert_equal_s(REFNAME, git_reference_name(ref2));
-
-	git_reference_free(ref0);
-	git_reference_free(ref1);
-	git_reference_free(ref2);
-	git_repository_free(repo2);
-}
diff --git a/tests-clar/resources/duplicate.git/config b/tests-clar/resources/duplicate.git/config
deleted file mode 100644
index 515f483..0000000
--- a/tests-clar/resources/duplicate.git/config
+++ /dev/null
@@ -1,5 +0,0 @@
-[core]
-	repositoryformatversion = 0
-	filemode = true
-	bare = false
-	logallrefupdates = true
diff --git a/tests-clar/resources/duplicate.git/objects/info/packs b/tests-clar/resources/duplicate.git/objects/info/packs
deleted file mode 100644
index 3696a7d..0000000
--- a/tests-clar/resources/duplicate.git/objects/info/packs
+++ /dev/null
@@ -1,2 +0,0 @@
-P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
-
diff --git a/tests-clar/resources/submodules/gitmodules b/tests-clar/resources/submodules/gitmodules
deleted file mode 100644
index 1262f8b..0000000
--- a/tests-clar/resources/submodules/gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "testrepo"]
-	path = testrepo
-	url = 
\ No newline at end of file
diff --git a/tests-clar/stash/stash_helpers.h b/tests-clar/stash/stash_helpers.h
deleted file mode 100644
index bb7fec4..0000000
--- a/tests-clar/stash/stash_helpers.h
+++ /dev/null
@@ -1,8 +0,0 @@
-void setup_stash(
-	git_repository *repo,
-	git_signature *signature);
-
-void commit_staged_files(
-	git_oid *commit_oid,
-	git_index *index,
-	git_signature *signature);
\ No newline at end of file
diff --git a/tests-clar/submodule/lookup.c b/tests-clar/submodule/lookup.c
deleted file mode 100644
index acf8f64..0000000
--- a/tests-clar/submodule/lookup.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "clar_libgit2.h"
-#include "submodule_helpers.h"
-#include "posix.h"
-
-static git_repository *g_repo = NULL;
-
-void test_submodule_lookup__initialize(void)
-{
-	g_repo = cl_git_sandbox_init("submod2");
-
-	cl_fixture_sandbox("submod2_target");
-	p_rename("submod2_target/.gitted", "submod2_target/.git");
-
-	/* must create submod2_target before rewrite so prettify will work */
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-}
-
-void test_submodule_lookup__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-	cl_fixture_cleanup("submod2_target");
-}
-
-void test_submodule_lookup__simple_lookup(void)
-{
-	git_submodule *sm;
-
-	/* lookup existing */
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
-	cl_assert(sm);
-
-	/* lookup pending change in .gitmodules that is not in HEAD */
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
-	cl_assert(sm);
-
-	/* lookup pending change in .gitmodules that is neither in HEAD nor index */
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
-	cl_assert(sm);
-
-	/* lookup git repo subdir that is not added as submodule */
-	cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS);
-
-	/* lookup existing directory that is not a submodule */
-	cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND);
-
-	/* lookup existing file that is not a submodule */
-	cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND);
-
-	/* lookup non-existent item */
-	cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND);
-}
-
-void test_submodule_lookup__accessors(void)
-{
-	git_submodule *sm;
-	const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
-
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
-	cl_assert(git_submodule_owner(sm) == g_repo);
-	cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
-	cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
-	cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
-
-	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
-
-	cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
-	cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
-
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
-	cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
-
-	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
-		"3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
-
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
-	cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
-
-	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
-	cl_assert(git_submodule_head_id(sm) == NULL);
-	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
-
-	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
-	cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
-
-	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
-	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
-		"5e4963595a9774b90524d35a807169049de8ccad") == 0);
-}
-
-typedef struct {
-	int count;
-} sm_lookup_data;
-
-static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
-{
-	sm_lookup_data *data = payload;
-	data->count += 1;
-	cl_assert_equal_s(git_submodule_name(sm), name);
-	return 0;
-}
-
-void test_submodule_lookup__foreach(void)
-{
-	sm_lookup_data data;
-	memset(&data, 0, sizeof(data));
-	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
-	cl_assert_equal_i(8, data.count);
-}
diff --git a/tests-clar/submodule/submodule_helpers.h b/tests-clar/submodule/submodule_helpers.h
deleted file mode 100644
index 6b76a83..0000000
--- a/tests-clar/submodule/submodule_helpers.h
+++ /dev/null
@@ -1,2 +0,0 @@
-extern void rewrite_gitmodules(const char *workdir);
-
diff --git a/tests-clar/threads/basic.c b/tests-clar/threads/basic.c
deleted file mode 100644
index a15c531..0000000
--- a/tests-clar/threads/basic.c
+++ /dev/null
@@ -1,23 +0,0 @@
-#include "clar_libgit2.h"
-
-#include "cache.h"
-
-
-static git_repository *g_repo;
-
-void test_threads_basic__initialize(void)
-{
-	g_repo = cl_git_sandbox_init("testrepo");
-}
-
-void test_threads_basic__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-}
-
-
-void test_threads_basic__cache(void)
-{
-	// run several threads polling the cache at the same time
-	cl_assert(1 == 1);
-}
diff --git a/tests-clar/README.md b/tests/README.md
similarity index 100%
rename from tests-clar/README.md
rename to tests/README.md
diff --git a/tests-clar/attr/attr_expect.h b/tests/attr/attr_expect.h
similarity index 100%
rename from tests-clar/attr/attr_expect.h
rename to tests/attr/attr_expect.h
diff --git a/tests-clar/attr/file.c b/tests/attr/file.c
similarity index 96%
rename from tests-clar/attr/file.c
rename to tests/attr/file.c
index 8866fd9..4eb1d22 100644
--- a/tests-clar/attr/file.c
+++ b/tests/attr/file.c
@@ -49,7 +49,6 @@
 	cl_assert(rule);
 	cl_assert_equal_s("pat0", rule->match.pattern);
 	cl_assert(rule->match.length == strlen("pat0"));
-	cl_assert(rule->match.flags == 0);
 	cl_assert(rule->assigns.length == 1);
 	assign = get_assign(rule,0);
 	cl_assert_equal_s("attr0", assign->name);
@@ -59,16 +58,16 @@
 	rule = get_rule(1);
 	cl_assert_equal_s("pat1", rule->match.pattern);
 	cl_assert(rule->match.length == strlen("pat1"));
-	cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_NEGATIVE);
+	cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0);
 
 	rule = get_rule(2);
 	cl_assert_equal_s("pat2", rule->match.pattern);
 	cl_assert(rule->match.length == strlen("pat2"));
-	cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_DIRECTORY);
+	cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0);
 
 	rule = get_rule(3);
 	cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
-	cl_assert(rule->match.flags == GIT_ATTR_FNMATCH_FULLPATH);
+	cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0);
 
 	rule = get_rule(4);
 	cl_assert_equal_s("pat4.*", rule->match.pattern);
@@ -89,7 +88,6 @@
 	rule = get_rule(8);
 	cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
 	cl_assert(rule->match.length == strlen("pat8 with spaces"));
-	cl_assert(rule->match.flags == 0);
 
 	rule = get_rule(9);
 	cl_assert_equal_s("pat9", rule->match.pattern);
diff --git a/tests-clar/attr/flags.c b/tests/attr/flags.c
similarity index 100%
rename from tests-clar/attr/flags.c
rename to tests/attr/flags.c
diff --git a/tests-clar/attr/ignore.c b/tests/attr/ignore.c
similarity index 100%
rename from tests-clar/attr/ignore.c
rename to tests/attr/ignore.c
diff --git a/tests-clar/attr/lookup.c b/tests/attr/lookup.c
similarity index 100%
rename from tests-clar/attr/lookup.c
rename to tests/attr/lookup.c
diff --git a/tests-clar/attr/repo.c b/tests/attr/repo.c
similarity index 94%
rename from tests-clar/attr/repo.c
rename to tests/attr/repo.c
index ca3e71e..ef2ad5c 100644
--- a/tests-clar/attr/repo.c
+++ b/tests/attr/repo.c
@@ -100,6 +100,22 @@
 	cl_assert_equal_s("yes", values[3]);
 }
 
+void test_attr_repo__get_many_in_place(void)
+{
+	const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
+
+	/* it should be legal to look up values into the same array that has
+	 * the attribute names, overwriting each name as the value is found.
+	 */
+
+	cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
+
+	cl_assert(GIT_ATTR_TRUE(vals[0]));
+	cl_assert(GIT_ATTR_TRUE(vals[1]));
+	cl_assert(GIT_ATTR_UNSPECIFIED(vals[2]));
+	cl_assert_equal_s("yes", vals[3]);
+}
+
 static int count_attrs(
 	const char *name,
 	const char *value,
diff --git a/tests/blame/blame_helpers.c b/tests/blame/blame_helpers.c
new file mode 100644
index 0000000..d64bb5c
--- /dev/null
+++ b/tests/blame/blame_helpers.c
@@ -0,0 +1,64 @@
+#include "blame_helpers.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
+{
+	va_list arglist;
+
+	printf("Hunk %zd (line %d +%d): ", idx,
+			hunk->final_start_line_number, hunk->lines_in_hunk-1);
+
+	va_start(arglist, fmt);
+	vprintf(fmt, arglist);
+	va_end(arglist);
+
+	printf("\n");
+}
+
+void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
+		int start_line, int len, char boundary, const char *commit_id, const char *orig_path)
+{
+	char expected[41] = {0}, actual[41] = {0};
+	const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
+	cl_assert(hunk);
+
+	if (!strncmp(commit_id, "0000", 4)) {
+		strcpy(expected, "0000000000000000000000000000000000000000");
+	} else {
+		git_object *obj;
+		cl_git_pass(git_revparse_single(&obj, repo, commit_id));
+		git_oid_fmt(expected, git_object_id(obj));
+		git_object_free(obj);
+	}
+
+	if (hunk->final_start_line_number != start_line) {
+		hunk_message(idx, hunk, "mismatched start line number: expected %d, got %d",
+				start_line, hunk->final_start_line_number);
+	}
+	cl_assert_equal_i(hunk->final_start_line_number, start_line);
+
+	if (hunk->lines_in_hunk != len) {
+		hunk_message(idx, hunk, "mismatched line count: expected %d, got %d",
+				len, hunk->lines_in_hunk);
+	}
+	cl_assert_equal_i(hunk->lines_in_hunk, len);
+
+	git_oid_fmt(actual, &hunk->final_commit_id);
+	if (strcmp(expected, actual)) {
+		hunk_message(idx, hunk, "has mismatched original id (got %s, expected %s)\n",
+				actual, expected);
+	}
+	cl_assert_equal_s(actual, expected);
+	if (strcmp(hunk->orig_path, orig_path)) {
+		hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n",
+				hunk->orig_path, orig_path);
+	}
+	cl_assert_equal_s(hunk->orig_path, orig_path);
+
+	if (hunk->boundary != boundary) {
+		hunk_message(idx, hunk, "doesn't match boundary flag (got %d, expected %d)\n",
+				hunk->boundary, boundary);
+	}
+	cl_assert_equal_i(boundary, hunk->boundary);
+}
+
+
diff --git a/tests/blame/blame_helpers.h b/tests/blame/blame_helpers.h
new file mode 100644
index 0000000..94321a5
--- /dev/null
+++ b/tests/blame/blame_helpers.h
@@ -0,0 +1,16 @@
+#include "clar_libgit2.h"
+#include "blame.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...);
+
+void check_blame_hunk_index(
+		git_repository *repo,
+		git_blame *blame,
+		int idx,
+		int start_line,
+		int len,
+		char boundary,
+		const char *commit_id,
+		const char *orig_path);
+
+
diff --git a/tests/blame/buffer.c b/tests/blame/buffer.c
new file mode 100644
index 0000000..912ee98
--- /dev/null
+++ b/tests/blame/buffer.c
@@ -0,0 +1,166 @@
+#include "blame_helpers.h"
+
+git_repository *g_repo;
+git_blame *g_fileblame, *g_bufferblame;
+
+void test_blame_buffer__initialize(void)
+{
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+	cl_git_pass(git_blame_file(&g_fileblame, g_repo, "b.txt", NULL));
+	g_bufferblame = NULL;
+}
+
+void test_blame_buffer__cleanup(void)
+{
+	git_blame_free(g_fileblame);
+	git_blame_free(g_bufferblame);
+	git_repository_free(g_repo);
+}
+
+void test_blame_buffer__added_line(void)
+{
+	const git_blame_hunk *hunk;
+
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+abcdefg\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+	cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+	check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt");
+
+	hunk = git_blame_get_hunk_byline(g_bufferblame, 16);
+	cl_assert(hunk);
+	cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
+}
+
+void test_blame_buffer__deleted_line(void)
+{
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+	check_blame_hunk_index(g_repo, g_bufferblame, 2,  6, 3, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 3,  9, 1, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 4, 10, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__add_splits_hunk(void)
+{
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+	check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 2, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 3, 8, 1, 0, "00000000", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 4, 9, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__delete_crosses_hunk_boundary(void)
+{
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+	check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 2, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__replace_line(void)
+{
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+	check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__add_lines_at_end(void)
+{
+	const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+abc\n\
+def\n";
+	cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+
+	cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+	check_blame_hunk_index(g_repo, g_bufferblame, 0,  1, 4, 0, "da237394", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 1,  5, 1, 1, "b99f7ac0", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 2,  6, 5, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+	check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt");
+}
diff --git a/tests/blame/getters.c b/tests/blame/getters.c
new file mode 100644
index 0000000..66eaeec
--- /dev/null
+++ b/tests/blame/getters.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+git_blame *g_blame;
+
+void test_blame_getters__initialize(void)
+{
+	size_t i;
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	git_blame_hunk hunks[] = {
+		{ 3, {{0}},  1, NULL, {{0}}, "a", 0},
+		{ 3, {{0}},  4, NULL, {{0}}, "b", 0},
+		{ 3, {{0}},  7, NULL, {{0}}, "c", 0},
+		{ 3, {{0}}, 10, NULL, {{0}}, "d", 0},
+		{ 3, {{0}}, 13, NULL, {{0}}, "e", 0},
+	};
+
+	g_blame = git_blame__alloc(NULL, opts, "");
+
+	for (i=0; i<5; i++) {
+		git_blame_hunk *h = git__calloc(1, sizeof(git_blame_hunk));
+		h->final_start_line_number = hunks[i].final_start_line_number;
+		h->orig_path = git__strdup(hunks[i].orig_path);
+		h->lines_in_hunk = hunks[i].lines_in_hunk;
+
+		git_vector_insert(&g_blame->hunks, h);
+	}
+}
+
+void test_blame_getters__cleanup(void)
+{
+	git_blame_free(g_blame);
+}
+
+
+void test_blame_getters__byindex(void)
+{
+	const git_blame_hunk *h = git_blame_get_hunk_byindex(g_blame, 2);
+	cl_assert(h);
+	cl_assert_equal_s(h->orig_path, "c");
+
+	h = git_blame_get_hunk_byindex(g_blame, 95);
+	cl_assert_equal_p(h, NULL);
+}
+
+void test_blame_getters__byline(void)
+{
+	const git_blame_hunk *h = git_blame_get_hunk_byline(g_blame, 5);
+	cl_assert(h);
+	cl_assert_equal_s(h->orig_path, "b");
+
+	h = git_blame_get_hunk_byline(g_blame, 95);
+	cl_assert_equal_p(h, NULL);
+}
diff --git a/tests/blame/harder.c b/tests/blame/harder.c
new file mode 100644
index 0000000..7c4dd4f
--- /dev/null
+++ b/tests/blame/harder.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+
+/**
+ * The test repo has a history that looks like this:
+ *
+ * * (A) bc7c5ac
+ * |\
+ * | * (B) aa06ecc
+ * * | (C) 63d671e
+ * |/  
+ * * (D) da23739
+ * * (E) b99f7ac
+ *
+ */
+
+static git_repository *g_repo = NULL;
+
+void test_blame_harder__initialize(void)
+{
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+}
+
+void test_blame_harder__cleanup(void)
+{
+	git_repository_free(g_repo);
+	g_repo = NULL;
+}
+
+
+
+void test_blame_harder__m(void)
+{
+	/* TODO */
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE;
+}
+
+
+void test_blame_harder__c(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	/* Attribute the first hunk in b.txt to (E), since it was cut/pasted from
+	 * a.txt in (D).
+	 */
+	opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+}
+
+void test_blame_harder__cc(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	/* Attribute the second hunk in b.txt to (E), since it was copy/pasted from
+	 * a.txt in (C).
+	 */
+	opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+}
+
+void test_blame_harder__ccc(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+	
+	/* Attribute the third hunk in b.txt to (E).  This hunk was deleted from
+	 * a.txt in (D), but reintroduced in (B).
+	 */
+	opts.flags = GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES;
+}
diff --git a/tests/blame/simple.c b/tests/blame/simple.c
new file mode 100644
index 0000000..79bd56b
--- /dev/null
+++ b/tests/blame/simple.c
@@ -0,0 +1,305 @@
+#include "blame_helpers.h"
+
+static git_repository *g_repo;
+static git_blame *g_blame;
+
+void test_blame_simple__initialize(void)
+{
+	g_repo = NULL;
+	g_blame = NULL;
+}
+
+void test_blame_simple__cleanup(void)
+{
+	git_blame_free(g_blame);
+	git_repository_free(g_repo);
+}
+
+/*
+ * $ git blame -s branch_file.txt
+ *    orig line no                        final line no
+ * commit   V  author       timestamp                 V
+ * c47800c7 1 (Scott Chacon 2010-05-25 11:58:14 -0700 1
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2
+ */
+void test_blame_simple__trivial_testrepo(void)
+{
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo/.gitted")));
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", NULL));
+
+	cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "c47800c7", "branch_file.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf3", "branch_file.txt");
+}
+
+/*
+ * $ git blame -n b.txt
+ *    orig line no                          final line no
+ * commit    V  author     timestamp                  V
+ * da237394  1 (Ben Straub 2013-02-12 15:11:30 -0800  1
+ * da237394  2 (Ben Straub 2013-02-12 15:11:30 -0800  2
+ * da237394  3 (Ben Straub 2013-02-12 15:11:30 -0800  3
+ * da237394  4 (Ben Straub 2013-02-12 15:11:30 -0800  4
+ * ^b99f7ac  1 (Ben Straub 2013-02-12 15:10:12 -0800  5
+ * 63d671eb  6 (Ben Straub 2013-02-12 15:13:04 -0800  6
+ * 63d671eb  7 (Ben Straub 2013-02-12 15:13:04 -0800  7
+ * 63d671eb  8 (Ben Straub 2013-02-12 15:13:04 -0800  8
+ * 63d671eb  9 (Ben Straub 2013-02-12 15:13:04 -0800  9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca  6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca  7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca  8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca  9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__trivial_blamerepo(void)
+{
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", NULL));
+
+	cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  1, 4, 0, "da237394", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1,  5, 1, 1, "b99f7ac0", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 2,  6, 5, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+
+/*
+ * $ git blame -n 359fc2d -- include/git2.h
+ *                     orig line no                                final line no
+ * commit   orig path       V  author              timestamp                  V
+ * d12299fe src/git.h       1 (Vicent Martí        2010-12-03 22:22:10 +0200  1
+ * 359fc2d2 include/git2.h  2 (Edward Thomson      2013-01-08 17:07:25 -0600  2
+ * d12299fe src/git.h       5 (Vicent Martí        2010-12-03 22:22:10 +0200  3
+ * bb742ede include/git2.h  4 (Vicent Martí        2011-09-19 01:54:32 +0300  4
+ * bb742ede include/git2.h  5 (Vicent Martí        2011-09-19 01:54:32 +0300  5
+ * d12299fe src/git.h      24 (Vicent Martí        2010-12-03 22:22:10 +0200  6
+ * d12299fe src/git.h      25 (Vicent Martí        2010-12-03 22:22:10 +0200  7
+ * d12299fe src/git.h      26 (Vicent Martí        2010-12-03 22:22:10 +0200  8
+ * d12299fe src/git.h      27 (Vicent Martí        2010-12-03 22:22:10 +0200  9
+ * d12299fe src/git.h      28 (Vicent Martí        2010-12-03 22:22:10 +0200 10
+ * 96fab093 include/git2.h 11 (Sven Strickroth     2011-10-09 18:37:41 +0200 11
+ * 9d1dcca2 src/git2.h     33 (Vicent Martí        2011-02-07 10:35:58 +0200 12
+ * 44908fe7 src/git2.h     29 (Vicent Martí        2010-12-06 23:03:16 +0200 13
+ * a15c550d include/git2.h 14 (Vicent Martí        2011-11-16 14:09:44 +0100 14
+ * 44908fe7 src/git2.h     30 (Vicent Martí        2010-12-06 23:03:16 +0200 15
+ * d12299fe src/git.h      32 (Vicent Martí        2010-12-03 22:22:10 +0200 16
+ * 44908fe7 src/git2.h     33 (Vicent Martí        2010-12-06 23:03:16 +0200 17
+ * d12299fe src/git.h      34 (Vicent Martí        2010-12-03 22:22:10 +0200 18
+ * 44908fe7 src/git2.h     35 (Vicent Martí        2010-12-06 23:03:16 +0200 19
+ * 638c2ca4 src/git2.h     36 (Vicent Martí        2010-12-18 02:10:25 +0200 20
+ * 44908fe7 src/git2.h     36 (Vicent Martí        2010-12-06 23:03:16 +0200 21
+ * d12299fe src/git.h      37 (Vicent Martí        2010-12-03 22:22:10 +0200 22
+ * 44908fe7 src/git2.h     38 (Vicent Martí        2010-12-06 23:03:16 +0200 23
+ * 44908fe7 src/git2.h     39 (Vicent Martí        2010-12-06 23:03:16 +0200 24
+ * bf787bd8 include/git2.h 25 (Carlos Martín Nieto 2012-04-08 18:56:50 +0200 25
+ * 0984c876 include/git2.h 26 (Scott J. Goldman    2012-11-28 18:27:43 -0800 26
+ * 2f8a8ab2 src/git2.h     41 (Vicent Martí        2011-01-29 01:56:25 +0200 27
+ * 27df4275 include/git2.h 47 (Michael Schubert    2011-06-28 14:13:12 +0200 28
+ * a346992f include/git2.h 28 (Ben Straub          2012-05-10 09:47:14 -0700 29
+ * d12299fe src/git.h      40 (Vicent Martí        2010-12-03 22:22:10 +0200 30
+ * 44908fe7 src/git2.h     41 (Vicent Martí        2010-12-06 23:03:16 +0200 31
+ * 44908fe7 src/git2.h     42 (Vicent Martí        2010-12-06 23:03:16 +0200 32
+ * 44908fe7 src/git2.h     43 (Vicent Martí        2010-12-06 23:03:16 +0200 33
+ * 44908fe7 src/git2.h     44 (Vicent Martí        2010-12-06 23:03:16 +0200 34
+ * 44908fe7 src/git2.h     45 (Vicent Martí        2010-12-06 23:03:16 +0200 35
+ * 65b09b1d include/git2.h 33 (Russell Belfer      2012-02-02 18:03:43 -0800 36
+ * d12299fe src/git.h      46 (Vicent Martí        2010-12-03 22:22:10 +0200 37
+ * 44908fe7 src/git2.h     47 (Vicent Martí        2010-12-06 23:03:16 +0200 38
+ * 5d4cd003 include/git2.h 55 (Carlos Martín Nieto 2011-03-28 17:02:45 +0200 39
+ * 41fb1ca0 include/git2.h 39 (Philip Kelley       2012-10-29 13:41:14 -0400 40
+ * 2dc31040 include/git2.h 56 (Carlos Martín Nieto 2011-06-20 18:58:57 +0200 41
+ * 764df57e include/git2.h 40 (Ben Straub          2012-06-15 13:14:43 -0700 42
+ * 5280f4e6 include/git2.h 41 (Ben Straub          2012-07-31 19:39:06 -0700 43
+ * 613d5eb9 include/git2.h 43 (Philip Kelley       2012-11-28 11:42:37 -0500 44
+ * d12299fe src/git.h      48 (Vicent Martí        2010-12-03 22:22:10 +0200 45
+ * 111ee3fe include/git2.h 41 (Vicent Martí        2012-07-11 14:37:26 +0200 46
+ * f004c4a8 include/git2.h 44 (Russell Belfer      2012-08-21 17:26:39 -0700 47
+ * 111ee3fe include/git2.h 42 (Vicent Martí        2012-07-11 14:37:26 +0200 48
+ * 9c82357b include/git2.h 58 (Carlos Martín Nieto 2011-06-17 18:13:14 +0200 49
+ * d6258deb include/git2.h 61 (Carlos Martín Nieto 2011-06-25 15:10:09 +0200 50
+ * b311e313 include/git2.h 63 (Julien Miotte       2011-07-27 18:31:13 +0200 51
+ * 3412391d include/git2.h 63 (Carlos Martín Nieto 2011-07-07 11:47:31 +0200 52
+ * bfc9ca59 include/git2.h 43 (Russell Belfer      2012-03-28 16:45:36 -0700 53
+ * bf477ed4 include/git2.h 44 (Michael Schubert    2012-02-15 00:33:38 +0100 54
+ * edebceff include/git2.h 46 (nulltoken           2012-05-01 13:57:45 +0200 55
+ * 743a4b3b include/git2.h 48 (nulltoken           2012-06-15 22:24:59 +0200 56
+ * 0a32dca5 include/git2.h 54 (Michael Schubert    2012-08-19 22:26:32 +0200 57
+ * 590fb68b include/git2.h 55 (nulltoken           2012-10-04 13:47:45 +0200 58
+ * bf477ed4 include/git2.h 45 (Michael Schubert    2012-02-15 00:33:38 +0100 59
+ * d12299fe src/git.h      49 (Vicent Martí        2010-12-03 22:22:10 +0200 60
+ */
+void test_blame_simple__trivial_libgit2(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+	git_object *obj;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("../..")));
+
+	/* This test can't work on a shallow clone */
+	if (git_repository_is_shallow(g_repo))
+		return;
+
+	cl_git_pass(git_revparse_single(&obj, g_repo, "359fc2d"));
+	git_oid_cpy(&opts.newest_commit, git_object_id(obj));
+	git_object_free(obj);
+
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "include/git2.h", &opts));
+
+	check_blame_hunk_index(g_repo, g_blame,  0,  1, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame,  1,  2, 1, 0, "359fc2d2", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  2,  3, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame,  3,  4, 2, 0, "bb742ede", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  4,  6, 5, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame,  5, 11, 1, 0, "96fab093", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  6, 12, 1, 0, "9d1dcca2", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  7, 13, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  8, 14, 1, 0, "a15c550d", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame,  9, 15, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 10, 16, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 11, 17, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 12, 18, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 13, 19, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 14, 20, 1, 0, "638c2ca4", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 15, 21, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 16, 22, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 17, 23, 2, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 18, 25, 1, 0, "bf787bd8", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 19, 26, 1, 0, "0984c876", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 20, 27, 1, 0, "2f8a8ab2", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 21, 28, 1, 0, "27df4275", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 22, 29, 1, 0, "a346992f", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 23, 30, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 24, 31, 5, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 25, 36, 1, 0, "65b09b1d", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 26, 37, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 27, 38, 1, 0, "44908fe7", "src/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 28, 39, 1, 0, "5d4cd003", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 29, 40, 1, 0, "41fb1ca0", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 30, 41, 1, 0, "2dc31040", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 31, 42, 1, 0, "764df57e", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 32, 43, 1, 0, "5280f4e6", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 33, 44, 1, 0, "613d5eb9", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 34, 45, 1, 0, "d12299fe", "src/git.h");
+	check_blame_hunk_index(g_repo, g_blame, 35, 46, 1, 0, "111ee3fe", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 36, 47, 1, 0, "f004c4a8", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 37, 48, 1, 0, "111ee3fe", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 38, 49, 1, 0, "9c82357b", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 39, 50, 1, 0, "d6258deb", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 40, 51, 1, 0, "b311e313", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 41, 52, 1, 0, "3412391d", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 42, 53, 1, 0, "bfc9ca59", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 43, 54, 1, 0, "bf477ed4", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 44, 55, 1, 0, "edebceff", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 45, 56, 1, 0, "743a4b3b", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 46, 57, 1, 0, "0a32dca5", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 47, 58, 1, 0, "590fb68b", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 48, 59, 1, 0, "bf477ed4", "include/git2.h");
+	check_blame_hunk_index(g_repo, g_blame, 49, 60, 1, 0, "d12299fe", "src/git.h");
+}
+
+
+/*
+ * $ git blame -n b.txt -L 8
+ *    orig line no                          final line no
+ * commit    V  author     timestamp                  V
+ * 63d671eb  8 (Ben Straub 2013-02-12 15:13:04 -0800  8
+ * 63d671eb  9 (Ben Straub 2013-02-12 15:13:04 -0800  9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca  6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca  7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca  8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca  9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__can_restrict_lines_min(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+	opts.min_line = 8;
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+	cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  8, 3, 0, "63d671eb", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L ,6
+ *    orig line no                          final line no
+ * commit    V  author     timestamp                  V
+ * da237394  1 (Ben Straub 2013-02-12 15:11:30 -0800  1
+ * da237394  2 (Ben Straub 2013-02-12 15:11:30 -0800  2
+ * da237394  3 (Ben Straub 2013-02-12 15:11:30 -0800  3
+ * da237394  4 (Ben Straub 2013-02-12 15:11:30 -0800  4
+ * ^b99f7ac  1 (Ben Straub 2013-02-12 15:10:12 -0800  5
+ * 63d671eb  6 (Ben Straub 2013-02-12 15:13:04 -0800  6
+ */
+void test_blame_simple__can_restrict_lines_max(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+	opts.max_line = 6;
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+	cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  1, 4, 0, "da237394", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1,  5, 1, 1, "b99f7ac0", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 2,  6, 1, 0, "63d671eb", "b.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L 2,7
+ *    orig line no                          final line no
+ * commit   V  author     timestamp                 V
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ * 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
+ */
+void test_blame_simple__can_restrict_lines_both(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+	opts.min_line = 2;
+	opts.max_line = 7;
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+	cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  2, 3, 0, "da237394", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1,  5, 1, 1, "b99f7ac0", "b.txt");
+	check_blame_hunk_index(g_repo, g_blame, 2,  6, 2, 0, "63d671eb", "b.txt");
+}
+
+/*
+ * $ git blame -n branch_file.txt be3563a..HEAD
+ *    orig line no                          final line no
+ * commit   V  author       timestamp                 V
+ * ^be3563a 1 (Scott Chacon 2010-05-25 11:58:27 -0700 1) hi
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2) bye!
+ */
+void test_blame_simple__can_restrict_to_newish_commits(void)
+{
+	git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+	{
+		git_object *obj;
+		cl_git_pass(git_revparse_single(&obj, g_repo, "be3563a"));
+		git_oid_cpy(&opts.oldest_commit, git_object_id(obj));
+		git_object_free(obj);
+	}
+
+	cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", &opts));
+
+	cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+	check_blame_hunk_index(g_repo, g_blame, 0,  1, 1, 1, "be3563a", "branch_file.txt");
+	check_blame_hunk_index(g_repo, g_blame, 1,  2, 1, 0, "a65fedf", "branch_file.txt");
+}
diff --git a/tests-clar/buf/basic.c b/tests/buf/basic.c
similarity index 100%
rename from tests-clar/buf/basic.c
rename to tests/buf/basic.c
diff --git a/tests-clar/buf/splice.c b/tests/buf/splice.c
similarity index 100%
rename from tests-clar/buf/splice.c
rename to tests/buf/splice.c
diff --git a/tests-clar/checkout/binaryunicode.c b/tests/checkout/binaryunicode.c
similarity index 100%
rename from tests-clar/checkout/binaryunicode.c
rename to tests/checkout/binaryunicode.c
diff --git a/tests-clar/checkout/checkout_helpers.c b/tests/checkout/checkout_helpers.c
similarity index 68%
rename from tests-clar/checkout/checkout_helpers.c
rename to tests/checkout/checkout_helpers.c
index 8da024d..06b4e06 100644
--- a/tests-clar/checkout/checkout_helpers.c
+++ b/tests/checkout/checkout_helpers.c
@@ -3,22 +3,6 @@
 #include "refs.h"
 #include "fileops.h"
 
-/* this is essentially the code from git__unescape modified slightly */
-void strip_cr_from_buf(git_buf *buf)
-{
-	char *scan, *pos = buf->ptr, *end = pos + buf->size;
-
-	for (scan = pos; scan < end; pos++, scan++) {
-		if (*scan == '\r')
-			scan++; /* skip '\r' */
-		if (pos != scan)
-			*pos = *scan;
-	}
-
-	*pos = '\0';
-	buf->size = (pos - buf->ptr);
-}
-
 void assert_on_branch(git_repository *repo, const char *branch)
 {
 	git_reference *head;
@@ -50,48 +34,6 @@
 	git_index_free(index);
 }
 
-static void check_file_contents_internal(
-	const char *path,
-	const char *expected_content,
-	bool strip_cr,
-	const char *file,
-	int line,
-	const char *msg)
-{
-	int fd;
-	char data[1024] = {0};
-	git_buf buf = GIT_BUF_INIT;
-	size_t expected_len = expected_content ? strlen(expected_content) : 0;
-
-	fd = p_open(path, O_RDONLY);
-	cl_assert(fd >= 0);
-
-	buf.ptr = data;
-	buf.size = p_read(fd, buf.ptr, sizeof(data));
-
-	cl_git_pass(p_close(fd));
-
-	if (strip_cr)
-		strip_cr_from_buf(&buf);
-
-	clar__assert_equal_i((int)expected_len, (int)buf.size, file, line, "strlen(expected_content) != strlen(actual_content)", 1);
-	clar__assert_equal_s(expected_content, buf.ptr, file, line, msg, 1);
-}
-
-void check_file_contents_at_line(
-	const char *path, const char *expected,
-	const char *file, int line, const char *msg)
-{
-	check_file_contents_internal(path, expected, false, file, line, msg);
-}
-
-void check_file_contents_nocr_at_line(
-	const char *path, const char *expected,
-	const char *file, int line, const char *msg)
-{
-	check_file_contents_internal(path, expected, true, file, line, msg);
-}
-
 int checkout_count_callback(
 	git_checkout_notify_t why,
 	const char *path,
diff --git a/tests/checkout/checkout_helpers.h b/tests/checkout/checkout_helpers.h
new file mode 100644
index 0000000..705ee90
--- /dev/null
+++ b/tests/checkout/checkout_helpers.h
@@ -0,0 +1,29 @@
+#include "buffer.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+
+extern void assert_on_branch(git_repository *repo, const char *branch);
+extern void reset_index_to_treeish(git_object *treeish);
+
+#define check_file_contents(PATH,EXP) \
+	cl_assert_equal_file(EXP,0,PATH)
+
+#define check_file_contents_nocr(PATH,EXP) \
+	cl_assert_equal_file_ignore_cr(EXP,0,PATH)
+
+typedef struct {
+	int n_conflicts;
+	int n_dirty;
+	int n_updates;
+	int n_untracked;
+	int n_ignored;
+	int debug;
+} checkout_counts;
+
+extern int checkout_count_callback(
+	git_checkout_notify_t why,
+	const char *path,
+	const git_diff_file *baseline,
+	const git_diff_file *target,
+	const git_diff_file *workdir,
+	void *payload);
diff --git a/tests/checkout/conflict.c b/tests/checkout/conflict.c
new file mode 100644
index 0000000..66965a8
--- /dev/null
+++ b/tests/checkout/conflict.c
@@ -0,0 +1,1127 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/sys/index.h"
+#include "fileops.h"
+
+static git_repository *g_repo;
+static git_index *g_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define CONFLICTING_ANCESTOR_OID   "d427e0b2e138501a3d15cc376077a3631e15bd46"
+#define CONFLICTING_OURS_OID       "4e886e602529caa9ab11d71f86634bd1b6e0de10"
+#define CONFLICTING_THEIRS_OID     "2bd0a343aeef7a2cf0d158478966a6e587ff3863"
+
+#define AUTOMERGEABLE_ANCESTOR_OID "6212c31dab5e482247d7977e4f0dd3601decf13b"
+#define AUTOMERGEABLE_OURS_OID     "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"
+#define AUTOMERGEABLE_THEIRS_OID   "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe"
+
+#define LINK_ANCESTOR_OID          "1a010b1c0f081b2e8901d55307a15c29ff30af0e"
+#define LINK_OURS_OID              "72ea499e108df5ff0a4a913e7655bbeeb1fb69f2"
+#define LINK_THEIRS_OID            "8bfb012a6d809e499bd8d3e194a3929bc8995b93"
+
+#define LINK_ANCESTOR_TARGET       "file"
+#define LINK_OURS_TARGET           "other-file"
+#define LINK_THEIRS_TARGET         "still-another-file"
+
+#define CONFLICTING_OURS_FILE \
+	"this file is changed in master and branch\n"
+#define CONFLICTING_THEIRS_FILE \
+	"this file is changed in branch and master\n"
+#define CONFLICTING_DIFF3_FILE \
+	"<<<<<<< ours\n" \
+	"this file is changed in master and branch\n" \
+	"=======\n" \
+	"this file is changed in branch and master\n" \
+	">>>>>>> theirs\n"
+
+#define AUTOMERGEABLE_MERGED_FILE \
+	"this file is changed in master\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is changed in branch\n"
+
+struct checkout_index_entry {
+	uint16_t mode;
+	char oid_str[41];
+	int stage;
+	char path[128];
+};
+
+struct checkout_name_entry {
+	char ancestor[64];
+	char ours[64];
+	char theirs[64];
+};
+
+void test_checkout_conflict__initialize(void)
+{
+	g_repo = cl_git_sandbox_init(TEST_REPO_PATH);
+	git_repository_index(&g_index, g_repo);
+
+	cl_git_rewritefile(
+		TEST_REPO_PATH "/.gitattributes",
+		"* text eol=lf\n");
+}
+
+void test_checkout_conflict__cleanup(void)
+{
+	git_index_free(g_index);
+	cl_git_sandbox_cleanup();
+}
+
+static void create_index(struct checkout_index_entry *entries, size_t entries_len)
+{
+	git_buf path = GIT_BUF_INIT;
+	size_t i;
+
+	for (i = 0; i < entries_len; i++) {
+		git_buf_joinpath(&path, TEST_REPO_PATH, entries[i].path);
+
+		if (entries[i].stage == 3 && (i == 0 || strcmp(entries[i-1].path, entries[i].path) != 0 || entries[i-1].stage != 2))
+			p_unlink(git_buf_cstr(&path));
+
+		git_index_remove_bypath(g_index, entries[i].path);
+	}
+
+	for (i = 0; i < entries_len; i++) {
+		git_index_entry entry;
+
+		memset(&entry, 0x0, sizeof(git_index_entry));
+
+		entry.mode = entries[i].mode;
+		entry.flags = entries[i].stage << GIT_IDXENTRY_STAGESHIFT;
+		git_oid_fromstr(&entry.oid, entries[i].oid_str);
+		entry.path = entries[i].path;
+
+		cl_git_pass(git_index_add(g_index, &entry));
+	}
+
+	git_buf_free(&path);
+}
+
+static void create_index_names(struct checkout_name_entry *entries, size_t entries_len)
+{
+	size_t i;
+
+	for (i = 0; i < entries_len; i++) {
+		cl_git_pass(git_index_name_add(g_index,
+			strlen(entries[i].ancestor) == 0 ? NULL : entries[i].ancestor,
+			strlen(entries[i].ours) == 0 ? NULL : entries[i].ours,
+			strlen(entries[i].theirs) == 0 ? NULL : entries[i].theirs));
+	}
+}
+
+static void create_conflicting_index(void)
+{
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting.txt" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+	};
+
+	create_index(checkout_index_entries, 3);
+	git_index_write(g_index);
+}
+
+static void ensure_workdir_contents(const char *path, const char *contents)
+{
+	git_buf fullpath = GIT_BUF_INIT, data_buf = GIT_BUF_INIT;
+
+	cl_git_pass(
+		git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+	cl_git_pass(git_futils_readbuffer(&data_buf, git_buf_cstr(&fullpath)));
+	cl_assert(strcmp(git_buf_cstr(&data_buf), contents) == 0);
+
+	git_buf_free(&fullpath);
+	git_buf_free(&data_buf);
+}
+
+static void ensure_workdir_oid(const char *path, const char *oid_str)
+{
+	git_oid expected, actual;
+
+	cl_git_pass(git_oid_fromstr(&expected, oid_str));
+	cl_git_pass(git_repository_hashfile(&actual, g_repo, path, GIT_OBJ_BLOB, NULL));
+	cl_assert(git_oid_cmp(&expected, &actual) == 0);
+}
+
+static void ensure_workdir_mode(const char *path, int mode)
+{
+#ifndef GIT_WIN32
+	git_buf fullpath = GIT_BUF_INIT;
+	struct stat st;
+
+	cl_git_pass(
+		git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+	cl_git_pass(p_stat(git_buf_cstr(&fullpath), &st));
+	cl_assert_equal_i(mode, st.st_mode);
+
+	git_buf_free(&fullpath);
+#endif
+}
+
+static void ensure_workdir(const char *path, int mode, const char *oid_str)
+{
+	ensure_workdir_mode(path, mode);
+	ensure_workdir_oid(path, oid_str);
+}
+
+static void ensure_workdir_link(const char *path, const char *target)
+{
+#ifdef GIT_WIN32
+	ensure_workdir_contents(path, target);
+#else
+	git_buf fullpath = GIT_BUF_INIT;
+	char actual[1024];
+	struct stat st;
+	int len;
+
+	cl_git_pass(
+		git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+	cl_git_pass(p_lstat(git_buf_cstr(&fullpath), &st));
+	cl_assert(S_ISLNK(st.st_mode));
+
+	cl_assert((len = p_readlink(git_buf_cstr(&fullpath), actual, 1024)) > 0);
+	actual[len] = '\0';
+	cl_assert(strcmp(actual, target) == 0);
+
+	git_buf_free(&fullpath);
+#endif
+}
+
+void test_checkout_conflict__ignored(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED;
+
+	create_conflicting_index();
+	cl_git_pass(p_unlink(TEST_REPO_PATH "/conflicting.txt"));
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	cl_assert(!git_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+}
+
+void test_checkout_conflict__ours(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy |= GIT_CHECKOUT_USE_OURS;
+
+	create_conflicting_index();
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("conflicting.txt", CONFLICTING_OURS_FILE);
+}
+
+void test_checkout_conflict__theirs(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy |= GIT_CHECKOUT_USE_THEIRS;
+
+	create_conflicting_index();
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("conflicting.txt", CONFLICTING_THEIRS_FILE);
+
+}
+
+void test_checkout_conflict__diff3(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	create_conflicting_index();
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__automerge(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+	};
+
+	create_index(checkout_index_entries, 3);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+}
+
+void test_checkout_conflict__directory_file(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+		{ 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 12);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-1~ours", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-2~theirs", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-3~theirs", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-4~ours", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__directory_file_with_custom_labels(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+		{ 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+		{ 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+	opts.our_label = "HEAD";
+	opts.their_label = "branch";
+
+	create_index(checkout_index_entries, 12);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-1~HEAD", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-2~branch", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-3~branch", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("df-4~HEAD", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__link_file(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-1" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "link-1" },
+		{ 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-2" },
+		{ 0120000, LINK_OURS_OID, 2, "link-2" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "link-2" },
+
+		{ 0120000, LINK_ANCESTOR_OID, 1, "link-3" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "link-3" },
+		{ 0120000, LINK_THEIRS_OID, 3, "link-3" },
+
+		{ 0120000, LINK_ANCESTOR_OID, 1, "link-4" },
+		{ 0120000, LINK_OURS_OID, 2, "link-4" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "link-4" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 12);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	/* Typechange conflicts always keep the file in the workdir */
+	ensure_workdir_oid("link-1", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("link-2", CONFLICTING_THEIRS_OID);
+	ensure_workdir_oid("link-3", CONFLICTING_OURS_OID);
+	ensure_workdir_oid("link-4", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__links(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0120000, LINK_ANCESTOR_OID, 1, "link-1" },
+		{ 0120000, LINK_OURS_OID, 2, "link-1" },
+		{ 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+		{ 0120000, LINK_OURS_OID, 2, "link-2" },
+		{ 0120000, LINK_THEIRS_OID, 3, "link-2" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 5);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	/* Conflicts with links always keep the ours side (even with -Xtheirs) */
+	ensure_workdir_link("link-1", LINK_OURS_TARGET);
+	ensure_workdir_link("link-2", LINK_OURS_TARGET);
+}
+
+void test_checkout_conflict__add_add(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 2);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	/* Add/add writes diff3 files */
+	ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__mode_change(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-1" },
+		{ 0100755, CONFLICTING_ANCESTOR_OID, 2, "executable-1" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "executable-1" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-2" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "executable-2" },
+		{ 0100755, CONFLICTING_ANCESTOR_OID, 3, "executable-2" },
+
+		{ 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-3" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 2, "executable-3" },
+		{ 0100755, CONFLICTING_THEIRS_OID, 3, "executable-3" },
+
+		{ 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-4" },
+		{ 0100755, CONFLICTING_OURS_OID, 2, "executable-4" },
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 3, "executable-4" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-5" },
+		{ 0100755, CONFLICTING_OURS_OID, 2, "executable-5" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "executable-5" },
+
+		{ 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-6" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "executable-6" },
+		{ 0100755, CONFLICTING_THEIRS_OID, 3, "executable-6" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 18);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	/* Keep the modified mode */
+	ensure_workdir_oid("executable-1", CONFLICTING_THEIRS_OID);
+	ensure_workdir_mode("executable-1", 0100755);
+
+	ensure_workdir_oid("executable-2", CONFLICTING_OURS_OID);
+	ensure_workdir_mode("executable-2", 0100755);
+
+	ensure_workdir_oid("executable-3", CONFLICTING_THEIRS_OID);
+	ensure_workdir_mode("executable-3", 0100644);
+
+	ensure_workdir_oid("executable-4", CONFLICTING_OURS_OID);
+	ensure_workdir_mode("executable-4", 0100644);
+
+	ensure_workdir_contents("executable-5", CONFLICTING_DIFF3_FILE);
+	ensure_workdir_mode("executable-5", 0100755);
+
+	ensure_workdir_contents("executable-6", CONFLICTING_DIFF3_FILE);
+	ensure_workdir_mode("executable-6", 0100644);
+}
+
+void test_checkout_conflict__renames(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+		{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+		{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+		{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+		{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+	};
+
+	struct checkout_name_entry checkout_name_entries[] = {
+		{
+			"3a-renamed-in-ours-deleted-in-theirs.txt",
+			"3a-newname-in-ours-deleted-in-theirs.txt",
+			""
+		},
+
+		{
+			"3b-renamed-in-theirs-deleted-in-ours.txt",
+			"",
+			"3b-newname-in-theirs-deleted-in-ours.txt"
+		},
+
+		{
+			"4a-renamed-in-ours-added-in-theirs.txt",
+			"4a-newname-in-ours-added-in-theirs.txt",
+			""
+		},
+
+		{
+			"4b-renamed-in-theirs-added-in-ours.txt",
+			"",
+			"4b-newname-in-theirs-added-in-ours.txt"
+		},
+
+		{
+			"5a-renamed-in-ours-added-in-theirs.txt",
+			"5a-newname-in-ours-added-in-theirs.txt",
+			"5a-renamed-in-ours-added-in-theirs.txt"
+		},
+
+		{
+			"5b-renamed-in-theirs-added-in-ours.txt",
+			"5b-renamed-in-theirs-added-in-ours.txt",
+			"5b-newname-in-theirs-added-in-ours.txt"
+		},
+
+		{
+			"6-both-renamed-1-to-2.txt",
+			"6-both-renamed-1-to-2-ours.txt",
+			"6-both-renamed-1-to-2-theirs.txt"
+		},
+
+		{
+			"7-both-renamed-side-1.txt",
+			"7-both-renamed.txt",
+			"7-both-renamed-side-1.txt"
+		},
+
+		{
+			"7-both-renamed-side-2.txt",
+			"7-both-renamed-side-2.txt",
+			"7-both-renamed.txt"
+		}
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 41);
+	create_index_names(checkout_name_entries, 9);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir("0a-no-change.txt",
+		0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+
+	ensure_workdir("0b-duplicated-in-ours.txt",
+		0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+
+	ensure_workdir("0b-rewritten-in-ours.txt",
+		0100644, "4c7e515d6d52d820496858f2f059ece69e99e2e3");
+
+	ensure_workdir("0c-duplicated-in-theirs.txt",
+		0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+
+	ensure_workdir("0c-rewritten-in-theirs.txt",
+		0100644, "4648d658682d1155c2a3db5b0c53305e26884ea5");
+
+	ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+		0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+
+	ensure_workdir("1a-newname-in-ours.txt",
+		0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+
+	ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+		0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+
+	ensure_workdir("1b-newname-in-theirs.txt",
+		0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+
+	ensure_workdir("2-newname-in-both.txt",
+		0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+	ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+		0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+
+	ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+		0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+
+	ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~ours",
+		0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+
+	ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~theirs",
+		0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a");
+
+	ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~ours",
+		0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+
+	ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~theirs",
+		0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db");
+
+	ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~ours",
+		0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+
+	ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~theirs",
+		0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714");
+
+	ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~ours",
+		0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+	ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~theirs",
+		0100644, "63247125386de9ec90a27ad36169307bf8a11a38");
+
+	ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+		0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+	ensure_workdir("6-both-renamed-1-to-2-theirs.txt",
+		0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+	ensure_workdir("7-both-renamed.txt~ours",
+		0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+
+	ensure_workdir("7-both-renamed.txt~theirs",
+		0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+}
+
+void test_checkout_conflict__rename_keep_ours(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+		{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+		{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+		{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+		{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+		{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+	};
+	
+	struct checkout_name_entry checkout_name_entries[] = {
+		{
+			"3a-renamed-in-ours-deleted-in-theirs.txt",
+			"3a-newname-in-ours-deleted-in-theirs.txt",
+			""
+		},
+		
+		{
+			"3b-renamed-in-theirs-deleted-in-ours.txt",
+			"",
+			"3b-newname-in-theirs-deleted-in-ours.txt"
+		},
+		
+		{
+			"4a-renamed-in-ours-added-in-theirs.txt",
+			"4a-newname-in-ours-added-in-theirs.txt",
+			""
+		},
+		
+		{
+			"4b-renamed-in-theirs-added-in-ours.txt",
+			"",
+			"4b-newname-in-theirs-added-in-ours.txt"
+		},
+		
+		{
+			"5a-renamed-in-ours-added-in-theirs.txt",
+			"5a-newname-in-ours-added-in-theirs.txt",
+			"5a-renamed-in-ours-added-in-theirs.txt"
+		},
+		
+		{
+			"5b-renamed-in-theirs-added-in-ours.txt",
+			"5b-renamed-in-theirs-added-in-ours.txt",
+			"5b-newname-in-theirs-added-in-ours.txt"
+		},
+		
+		{
+			"6-both-renamed-1-to-2.txt",
+			"6-both-renamed-1-to-2-ours.txt",
+			"6-both-renamed-1-to-2-theirs.txt"
+		},
+		
+		{
+			"7-both-renamed-side-1.txt",
+			"7-both-renamed.txt",
+			"7-both-renamed-side-1.txt"
+		},
+		
+		{
+			"7-both-renamed-side-2.txt",
+			"7-both-renamed-side-2.txt",
+			"7-both-renamed.txt"
+		}
+	};
+	
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+	
+	create_index(checkout_index_entries, 41);
+	create_index_names(checkout_name_entries, 9);
+	git_index_write(g_index);
+	
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+		
+	ensure_workdir("0a-no-change.txt",
+				   0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+	
+	ensure_workdir("0b-duplicated-in-ours.txt",
+				   0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+	
+	ensure_workdir("0b-rewritten-in-ours.txt",
+				   0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e");
+	
+	ensure_workdir("0c-duplicated-in-theirs.txt",
+				   0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+	
+	ensure_workdir("0c-rewritten-in-theirs.txt",
+				   0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09");
+	
+	ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+				   0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+	
+	ensure_workdir("1a-newname-in-ours.txt",
+				   0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+	
+	ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+				   0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+	
+	ensure_workdir("1b-newname-in-theirs.txt",
+				   0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+	
+	ensure_workdir("2-newname-in-both.txt",
+				   0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+	ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+				   0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+	
+	ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+				   0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+	
+	ensure_workdir("4a-newname-in-ours-added-in-theirs.txt",
+				   0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+	
+	ensure_workdir("4b-newname-in-theirs-added-in-ours.txt",
+				   0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+	
+	ensure_workdir("5a-newname-in-ours-added-in-theirs.txt",
+				   0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+		
+	ensure_workdir("5b-newname-in-theirs-added-in-ours.txt",
+				   0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+	ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+				   0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+	
+	ensure_workdir("7-both-renamed.txt",
+				   0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+}
+
+void test_checkout_conflict__name_mangled_file_exists_in_workdir(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-one-side-one.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-one-side-one.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-one-side-two.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-one-side-two.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-one.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-one.txt" },
+
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-two-side-one.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-two-side-one.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-two-side-two.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-two-side-two.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-two.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-two.txt" },
+
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-three-side-one.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-three-side-one.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-three-side-two.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-three-side-two.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-three.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-three.txt" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+		{ 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+	};
+
+	struct checkout_name_entry checkout_name_entries[] = {
+		{
+			"test-one-side-one.txt",
+			"test-one.txt",
+			"test-one-side-one.txt"
+		},
+		{
+			"test-one-side-two.txt",
+			"test-one-side-two.txt",
+			"test-one.txt"
+		},
+
+		{
+			"test-two-side-one.txt",
+			"test-two.txt",
+			"test-two-side-one.txt"
+		},
+		{
+			"test-two-side-two.txt",
+			"test-two-side-two.txt",
+			"test-two.txt"
+		},
+
+		{
+			"test-three-side-one.txt",
+			"test-three.txt",
+			"test-three-side-one.txt"
+		},
+		{
+			"test-three-side-two.txt",
+			"test-three-side-two.txt",
+			"test-three.txt"
+		}
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	create_index(checkout_index_entries, 24);
+	create_index_names(checkout_name_entries, 6);
+	git_index_write(g_index);
+
+	/* Add some files on disk that conflict with the names that would be chosen
+	 * for the files written for each side. */
+
+	cl_git_rewritefile("merge-resolve/test-one.txt~ours",
+		"Expect index contents to be written to ~ours_0");
+	cl_git_rewritefile("merge-resolve/test-one.txt~theirs",
+		"Expect index contents to be written to ~theirs_0");
+
+	cl_git_rewritefile("merge-resolve/test-two.txt~ours",
+		"Expect index contents to be written to ~ours_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~theirs",
+		"Expect index contents to be written to ~theirs_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~ours_0",
+		"Expect index contents to be written to ~ours_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~theirs_0",
+		"Expect index contents to be written to ~theirs_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~ours_1",
+		"Expect index contents to be written to ~ours_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~theirs_1",
+		"Expect index contents to be written to ~theirs_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~ours_2",
+		"Expect index contents to be written to ~ours_3");
+	cl_git_rewritefile("merge-resolve/test-two.txt~theirs_2",
+		"Expect index contents to be written to ~theirs_3");
+
+	cl_git_rewritefile("merge-resolve/test-three.txt~Ours",
+		"Expect case insensitive filesystems to create ~ours_0");
+	cl_git_rewritefile("merge-resolve/test-three.txt~THEIRS",
+		"Expect case insensitive filesystems to create ~theirs_0");
+
+	cl_git_rewritefile("merge-resolve/directory_file-one~ours",
+		"Index contents written to ~ours_0 in this D/F conflict");
+	cl_git_rewritefile("merge-resolve/directory_file-two~theirs",
+		"Index contents written to ~theirs_0 in this D/F conflict");
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir("test-one.txt~ours_0",
+		0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+	ensure_workdir("test-one.txt~theirs_0",
+		0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+	ensure_workdir("test-two.txt~ours_3",
+		0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+	ensure_workdir("test-two.txt~theirs_3",
+		0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+	/* Name is mangled on case insensitive only */
+#if defined(GIT_WIN32) || defined(__APPLE__)
+	ensure_workdir("test-three.txt~ours_0",
+		0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+	ensure_workdir("test-three.txt~theirs_0",
+		0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#else
+	ensure_workdir("test-three.txt~ours",
+		0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+	ensure_workdir("test-three.txt~theirs",
+		0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#endif
+
+	ensure_workdir("directory_file-one~ours_0", 0100644, CONFLICTING_OURS_OID);
+	ensure_workdir("directory_file-two~theirs_0", 0100644, CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__update_only(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "modify-delete" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "modify-delete" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+		{ 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+	};
+
+	opts.checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY;
+
+	create_index(checkout_index_entries, 3);
+	git_index_write(g_index);
+
+	cl_git_pass(p_mkdir("merge-resolve/directory_file-two", 0777));
+	cl_git_rewritefile("merge-resolve/directory_file-two/file", CONFLICTING_OURS_FILE);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+	ensure_workdir("directory_file-two/file", 0100644, CONFLICTING_OURS_OID);
+
+	cl_assert(!git_path_exists("merge-resolve/modify-delete"));
+	cl_assert(!git_path_exists("merge-resolve/test-one.txt"));
+	cl_assert(!git_path_exists("merge-resolve/test-one-side-one.txt"));
+	cl_assert(!git_path_exists("merge-resolve/test-one-side-two.txt"));
+	cl_assert(!git_path_exists("merge-resolve/test-one.txt~ours"));
+	cl_assert(!git_path_exists("merge-resolve/test-one.txt~theirs"));
+	cl_assert(!git_path_exists("merge-resolve/directory_file-one/file"));
+	cl_assert(!git_path_exists("merge-resolve/directory_file-one~ours"));
+	cl_assert(!git_path_exists("merge-resolve/directory_file-two~theirs"));
+}
+
+void test_checkout_conflict__path_filters(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	char *paths[] = { "conflicting-1.txt", "conflicting-3.txt" };
+	git_strarray patharray = {0};
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+	};
+
+	patharray.count = 2;
+	patharray.strings = paths;
+
+	opts.paths = patharray;
+
+	create_index(checkout_index_entries, 12);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	ensure_workdir_contents("conflicting-1.txt", CONFLICTING_DIFF3_FILE);
+	cl_assert(!git_path_exists("merge-resolve/conflicting-2.txt"));
+	ensure_workdir_contents("conflicting-3.txt", AUTOMERGEABLE_MERGED_FILE);
+	cl_assert(!git_path_exists("merge-resolve/conflicting-4.txt"));
+}
+
+static void collect_progress(
+	const char *path,
+	size_t completed_steps,
+	size_t total_steps,
+	void *payload)
+{
+	git_vector *paths = payload;
+
+	(void)completed_steps;
+	(void)total_steps;
+
+	if (path == NULL)
+		return;
+
+	git_vector_insert(paths, strdup(path));
+}
+
+void test_checkout_conflict__report_progress(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	git_vector paths = GIT_VECTOR_INIT;
+	char *path;
+	size_t i;
+
+	struct checkout_index_entry checkout_index_entries[] = {
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+		{ 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+		{ 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+		{ 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+		{ 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+		{ 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+		{ 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+	};
+
+	opts.progress_cb = collect_progress;
+	opts.progress_payload = &paths;
+
+
+	create_index(checkout_index_entries, 12);
+	git_index_write(g_index);
+
+	cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+	cl_assert_equal_i(4, git_vector_length(&paths));
+	cl_assert_equal_s("conflicting-1.txt", git_vector_get(&paths, 0));
+	cl_assert_equal_s("conflicting-2.txt", git_vector_get(&paths, 1));
+	cl_assert_equal_s("conflicting-3.txt", git_vector_get(&paths, 2));
+	cl_assert_equal_s("conflicting-4.txt", git_vector_get(&paths, 3));
+
+	git_vector_foreach(&paths, i, path)
+		git__free(path);
+
+	git_vector_free(&paths);
+}
diff --git a/tests/checkout/crlf.c b/tests/checkout/crlf.c
new file mode 100644
index 0000000..9a4cbd3
--- /dev/null
+++ b/tests/checkout/crlf.c
@@ -0,0 +1,231 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+#include "../filter/crlf.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "posix.h"
+
+static git_repository *g_repo;
+
+void test_checkout_crlf__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("crlf");
+}
+
+void test_checkout_crlf__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_false(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+	git_checkout_head(g_repo, &opts);
+
+	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
+{
+	git_index *index;
+	const git_index_entry *entry;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+	git_checkout_head(g_repo, &opts);
+
+	git_repository_index(&index, g_repo);
+
+	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+	cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
+
+	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+	cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
+
+	git_index_free(index);
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_true(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	git_checkout_head(g_repo, &opts);
+
+	if (GIT_EOL_NATIVE == GIT_EOL_LF)
+		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+	else
+		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+
+	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__more_lf_autocrlf_true(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	git_checkout_head(g_repo, &opts);
+
+	if (GIT_EOL_NATIVE == GIT_EOL_LF)
+		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW);
+	else
+		check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__more_crlf_autocrlf_true(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	git_checkout_head(g_repo, &opts);
+
+	if (GIT_EOL_NATIVE == GIT_EOL_LF)
+		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW);
+	else
+		check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__all_crlf_autocrlf_true(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	git_checkout_head(g_repo, &opts);
+
+	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
+{
+	git_index *index;
+	const git_index_entry *entry;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	git_checkout_head(g_repo, &opts);
+
+	git_repository_index(&index, g_repo);
+
+	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+
+	if (GIT_EOL_NATIVE == GIT_EOL_LF)
+		cl_assert_equal_sz(strlen(ALL_LF_TEXT_RAW), entry->file_size);
+	else
+		cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
+
+	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+	cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
+
+	git_index_free(index);
+}
+
+void test_checkout_crlf__with_ident(void)
+{
+	git_index *index;
+	git_blob *blob;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+
+	cl_git_mkfile("crlf/.gitattributes",
+		"*.txt text\n*.bin binary\n"
+		"*.crlf text eol=crlf\n"
+		"*.lf text eol=lf\n"
+		"*.ident text ident\n"
+		"*.identcrlf ident text eol=crlf\n"
+		"*.identlf ident text eol=lf\n");
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	/* add files with $Id$ */
+
+	cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
+	cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
+	cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
+	cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "lf.ident"));
+	cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
+	cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
+	cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");
+
+	git_checkout_head(g_repo, &opts);
+
+	/* check that blobs have $Id$ */
+
+	cl_git_pass(git_blob_lookup(&blob, g_repo,
+		& git_index_get_bypath(index, "lf.ident", 0)->oid));
+	cl_assert_equal_s(
+		ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
+	git_blob_free(blob);
+
+	cl_git_pass(git_blob_lookup(&blob, g_repo,
+		& git_index_get_bypath(index, "more2.identcrlf", 0)->oid));
+	cl_assert_equal_s(
+		"\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
+	git_blob_free(blob);
+
+	/* check that filesystem is initially untouched - matching core Git */
+
+	cl_assert_equal_file(
+		ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");
+
+	/* check that forced checkout rewrites correctly */
+
+	p_unlink("crlf/lf.ident");
+	p_unlink("crlf/crlf.ident");
+	p_unlink("crlf/more1.identlf");
+	p_unlink("crlf/more2.identcrlf");
+
+	git_checkout_head(g_repo, &opts);
+
+	if (GIT_EOL_NATIVE == GIT_EOL_LF) {
+		cl_assert_equal_file(
+			ALL_LF_TEXT_RAW
+			"\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\n",
+			0, "crlf/lf.ident");
+		cl_assert_equal_file(
+			ALL_CRLF_TEXT_AS_LF
+			"\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\n\n",
+			0, "crlf/crlf.ident");
+	} else {
+		cl_assert_equal_file(
+			ALL_LF_TEXT_AS_CRLF
+			"\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467$\r\n",
+			0, "crlf/lf.ident");
+		cl_assert_equal_file(
+			ALL_CRLF_TEXT_RAW
+			"\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857$\r\n\r\n",
+			0, "crlf/crlf.ident");
+	}
+
+	cl_assert_equal_file(
+		"$Id: f7830382dac1f1583422be5530fdfbd26289431b$\n"
+		MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");
+
+	cl_assert_equal_file(
+		"\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4$\r\n"
+		MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
+
+	git_index_free(index);
+}
diff --git a/tests-clar/checkout/head.c b/tests/checkout/head.c
similarity index 85%
rename from tests-clar/checkout/head.c
rename to tests/checkout/head.c
index 46646f8..a7a7e90 100644
--- a/tests-clar/checkout/head.c
+++ b/tests/checkout/head.c
@@ -16,11 +16,11 @@
 	cl_git_sandbox_cleanup();
 }
 
-void test_checkout_head__orphaned_head_returns_GIT_EORPHANEDHEAD(void)
+void test_checkout_head__unborn_head_returns_GIT_EUNBORNBRANCH(void)
 {
-	make_head_orphaned(g_repo, NON_EXISTING_HEAD);
+	make_head_unborn(g_repo, NON_EXISTING_HEAD);
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_checkout_head(g_repo, NULL));
+	cl_assert_equal_i(GIT_EUNBORNBRANCH, git_checkout_head(g_repo, NULL));
 }
 
 void test_checkout_head__with_index_only_tree(void)
@@ -54,7 +54,6 @@
 	cl_git_pass(git_checkout_head(g_repo, &opts));
 
 	cl_git_pass(git_repository_index(&index, g_repo));
-	cl_git_pass(git_index_read(index)); /* reload if needed */
 
 	cl_assert(!git_path_isfile("testrepo/newdir/newfile.txt"));
 	cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) == NULL);
diff --git a/tests-clar/checkout/index.c b/tests/checkout/index.c
similarity index 97%
rename from tests-clar/checkout/index.c
rename to tests/checkout/index.c
index 9d8b321..48d6d79 100644
--- a/tests-clar/checkout/index.c
+++ b/tests/checkout/index.c
@@ -224,11 +224,14 @@
 
 void test_checkout_index__options_dir_modes(void)
 {
-#ifndef GIT_WIN32
 	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
 	struct stat st;
 	git_oid oid;
 	git_commit *commit;
+	mode_t um;
+
+	if (!cl_is_chmod_supported())
+		return;
 
 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
 	cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
@@ -240,31 +243,34 @@
 
 	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
 
+	/* umask will influence actual directory creation mode */
+	(void)p_umask(um = p_umask(022));
+
 	cl_git_pass(p_stat("./testrepo/a", &st));
-	cl_assert_equal_i(st.st_mode & 0777, 0701);
+	cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
 
 	/* File-mode test, since we're on the 'dir' branch */
 	cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
-	cl_assert_equal_i(st.st_mode & 0777, 0755);
+	cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
 
 	git_commit_free(commit);
-#endif
 }
 
 void test_checkout_index__options_override_file_modes(void)
 {
-#ifndef GIT_WIN32
 	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
 	struct stat st;
 
+	if (!cl_is_chmod_supported())
+		return;
+
 	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
 	opts.file_mode = 0700;
 
 	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
 
 	cl_git_pass(p_stat("./testrepo/new.txt", &st));
-	cl_assert_equal_i(st.st_mode & 0777, 0700);
-#endif
+	cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
 }
 
 void test_checkout_index__options_open_flags(void)
@@ -587,6 +593,8 @@
 
 	cl_git_pass(git_futils_rmdir_r(
 		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+
+	git_object_free(head);
 }
 
 void test_checkout_index__can_get_repo_from_index(void)
diff --git a/tests-clar/checkout/tree.c b/tests/checkout/tree.c
similarity index 87%
rename from tests-clar/checkout/tree.c
rename to tests/checkout/tree.c
index e4bfbce..66b01bc 100644
--- a/tests-clar/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -677,3 +677,66 @@
 	cl_git_pass(git_futils_rmdir_r(
 		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
 }
+
+void test_checkout_tree__extremely_long_file_name(void)
+{
+	// A utf-8 string with 83 characters, but 249 bytes.
+	const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+	char path[1024];
+
+	g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+	cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
+	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+	sprintf(path, "testrepo/%s.txt", longname);
+	cl_assert(git_path_exists(path));
+
+	git_object_free(g_object);
+	cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+	cl_assert(!git_path_exists(path));
+}
+
+static void create_conflict(void)
+{
+	git_index *index;
+	git_index_entry entry;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	memset(&entry, 0x0, sizeof(git_index_entry));
+	entry.mode = 0100644;
+	entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT;
+	git_oid_fromstr(&entry.oid, "d427e0b2e138501a3d15cc376077a3631e15bd46");
+	entry.path = "conflicts.txt";
+	cl_git_pass(git_index_add(index, &entry));
+
+	entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT;
+	git_oid_fromstr(&entry.oid, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
+	cl_git_pass(git_index_add(index, &entry));
+
+	entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT;
+	git_oid_fromstr(&entry.oid, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
+	cl_git_pass(git_index_add(index, &entry));
+
+	git_index_write(index);
+	git_index_free(index);
+}
+
+void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	git_oid oid;
+	git_object *obj = NULL;
+
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
+
+	create_conflict();
+
+	cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+
+	git_object_free(obj);
+}
diff --git a/tests-clar/checkout/typechange.c b/tests/checkout/typechange.c
similarity index 100%
rename from tests-clar/checkout/typechange.c
rename to tests/checkout/typechange.c
diff --git a/tests-clar/clar.c b/tests/clar.c
similarity index 77%
rename from tests-clar/clar.c
rename to tests/clar.c
index fb10dd3..6c7399a 100644
--- a/tests-clar/clar.c
+++ b/tests/clar.c
@@ -24,28 +24,59 @@
 
 #	define _MAIN_CC __cdecl
 
-#	define stat(path, st) _stat(path, st)
-#	define mkdir(path, mode) _mkdir(path)
-#	define chdir(path) _chdir(path)
-#	define access(path, mode) _access(path, mode)
-#	define strdup(str) _strdup(str)
-#	define strcasecmp(a,b) _stricmp(a,b)
+#	ifndef stat
+#		define stat(path, st) _stat(path, st)
+#	endif
+#	ifndef mkdir
+#		define mkdir(path, mode) _mkdir(path)
+#	endif
+#	ifndef chdir
+#		define chdir(path) _chdir(path)
+#	endif
+#	ifndef access
+#		define access(path, mode) _access(path, mode)
+#	endif
+#	ifndef strdup
+#		define strdup(str) _strdup(str)
+#	endif
+#	ifndef strcasecmp
+#		define strcasecmp(a,b) _stricmp(a,b)
+#	endif
 
 #	ifndef __MINGW32__
 #		pragma comment(lib, "shell32")
-#		define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
-#		define W_OK 02
-#		define S_ISDIR(x) ((x & _S_IFDIR) != 0)
-#		define snprint_eq(buf,sz,fmt,a,b) _snprintf_s(buf,sz,_TRUNCATE,fmt,a,b)
+#		ifndef strncpy
+#			define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
+#		endif
+#		ifndef W_OK
+#			define W_OK 02
+#		endif
+#		ifndef S_ISDIR
+#			define S_ISDIR(x) ((x & _S_IFDIR) != 0)
+#		endif
+#		define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
 #	else
-#		define snprint_eq snprintf
+#		define p_snprintf snprintf
+#	endif
+
+#	ifndef PRIuZ
+#		define PRIuZ "Iu"
+#	endif
+#	ifndef PRIxZ
+#		define PRIxZ "Ix"
 #	endif
 	typedef struct _stat STAT_T;
 #else
 #	include <sys/wait.h> /* waitpid(2) */
 #	include <unistd.h>
 #	define _MAIN_CC
-#	define snprint_eq snprintf
+#	define p_snprintf snprintf
+#	ifndef PRIuZ
+#		define PRIuZ "zu"
+#	endif
+#	ifndef PRIxZ
+#		define PRIxZ "zx"
+#	endif
 	typedef struct stat STAT_T;
 #endif
 
@@ -406,45 +437,66 @@
 	clar__fail(file, line, error_msg, description, should_abort);
 }
 
-void clar__assert_equal_s(
-	const char *s1,
-	const char *s2,
+void clar__assert_equal(
 	const char *file,
 	int line,
 	const char *err,
-	int should_abort)
+	int should_abort,
+	const char *fmt,
+	...)
 {
-	int match = (s1 == NULL || s2 == NULL) ? (s1 == s2) : (strcmp(s1, s2) == 0);
+	va_list args;
+	char buf[4096];
+	int is_equal = 1;
 
-	if (!match) {
-		char buf[4096];
+	va_start(args, fmt);
 
-		if (s1 && s2) {
-			int pos;
-			for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
-				/* find differing byte offset */;
-			snprint_eq(buf, sizeof(buf), "'%s' != '%s' (at byte %d)", s1, s2, pos);
-		} else {
-			snprint_eq(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
+	if (!strcmp("%s", fmt)) {
+		const char *s1 = va_arg(args, const char *);
+		const char *s2 = va_arg(args, const char *);
+		is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);
+
+		if (!is_equal) {
+			if (s1 && s2) {
+				int pos;
+				for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
+					/* find differing byte offset */;
+				p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
+					s1, s2, pos);
+			} else {
+				p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
+			}
 		}
-
-		clar__fail(file, line, err, buf, should_abort);
 	}
-}
-
-void clar__assert_equal_i(
-	int i1,
-	int i2,
-	const char *file,
-	int line,
-	const char *err,
-	int should_abort)
-{
-	if (i1 != i2) {
-		char buf[128];
-		snprint_eq(buf, sizeof(buf), "%d != %d", i1, i2);
-		clar__fail(file, line, err, buf, should_abort);
+	else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
+		size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
+		is_equal = (sz1 == sz2);
+		if (!is_equal) {
+			int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
+			strncat(buf, " != ", sizeof(buf) - offset);
+			p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2);
+		}
 	}
+	else if (!strcmp("%p", fmt)) {
+		void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
+		is_equal = (p1 == p2);
+		if (!is_equal)
+			p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
+	}
+	else {
+		int i1 = va_arg(args, int), i2 = va_arg(args, int);
+		is_equal = (i1 == i2);
+		if (!is_equal) {
+			int offset = p_snprintf(buf, sizeof(buf), fmt, i1);
+			strncat(buf, " != ", sizeof(buf) - offset);
+			p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2);
+		}
+	}
+
+	va_end(args);
+
+	if (!is_equal)
+		clar__fail(file, line, err, buf, should_abort);
 }
 
 void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
diff --git a/tests-clar/clar.h b/tests/clar.h
similarity index 69%
rename from tests-clar/clar.h
rename to tests/clar.h
index d92318b..e1f244e 100644
--- a/tests-clar/clar.h
+++ b/tests/clar.h
@@ -57,15 +57,17 @@
 /**
  * Typed assertion macros
  */
-#define cl_assert_equal_s(s1,s2) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1)
-#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal_s((s1),(s2),__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1)
+#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
+#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
 
-#define cl_assert_equal_i(i1,i2) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2, 1)
-#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal_i((i1),(i2),__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1)
+#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
+#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
 
-#define cl_assert_equal_b(b1,b2) clar__assert_equal_i(!!(b1),!!(b2),__FILE__,__LINE__,#b1 " != " #b2, 1)
+#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
 
-#define cl_assert_equal_p(p1,p2) cl_assert((p1) == (p2))
+#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
+
 
 void clar__fail(
 	const char *file,
@@ -82,7 +84,12 @@
 	const char *description,
 	int should_abort);
 
-void clar__assert_equal_s(const char *,const char *,const char *,int,const char *,int);
-void clar__assert_equal_i(int,int,const char *,int,const char *,int);
+void clar__assert_equal(
+	const char *file,
+	int line,
+	const char *err,
+	int should_abort,
+	const char *fmt,
+	...);
 
 #endif
diff --git a/tests-clar/clar/fixtures.h b/tests/clar/fixtures.h
similarity index 100%
rename from tests-clar/clar/fixtures.h
rename to tests/clar/fixtures.h
diff --git a/tests-clar/clar/fs.h b/tests/clar/fs.h
similarity index 100%
rename from tests-clar/clar/fs.h
rename to tests/clar/fs.h
diff --git a/tests-clar/clar/print.h b/tests/clar/print.h
similarity index 100%
rename from tests-clar/clar/print.h
rename to tests/clar/print.h
diff --git a/tests-clar/clar/sandbox.h b/tests/clar/sandbox.h
similarity index 94%
rename from tests-clar/clar/sandbox.h
rename to tests/clar/sandbox.h
index 1ca6fca..ee75641 100644
--- a/tests-clar/clar/sandbox.h
+++ b/tests/clar/sandbox.h
@@ -43,10 +43,8 @@
 	}
 
 #else
-	DWORD env_len;
-
-	if ((env_len = GetEnvironmentVariable("CLAR_TMP", buffer, length)) > 0 &&
-		env_len < length)
+	DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
+	if (env_len > 0 && env_len < (DWORD)length)
 		return 0;
 
 	if (GetTempPath((DWORD)length, buffer))
diff --git a/tests-clar/clar_libgit2.c b/tests/clar_libgit2.c
similarity index 61%
rename from tests-clar/clar_libgit2.c
rename to tests/clar_libgit2.c
index de0e41b..50762cd 100644
--- a/tests-clar/clar_libgit2.c
+++ b/tests/clar_libgit2.c
@@ -1,6 +1,7 @@
 #include "clar_libgit2.h"
 #include "posix.h"
 #include "path.h"
+#include "git2/sys/repository.h"
 
 void cl_git_report_failure(
 	int error, const char *file, int line, const char *fncall)
@@ -30,24 +31,26 @@
 }
 
 void cl_git_write2file(
-	const char *filename, const char *new_content, int flags, unsigned int mode)
+	const char *path, const char *content, size_t content_len,
+	int flags, unsigned int mode)
 {
-	int fd = p_open(filename, flags, mode);
-	cl_assert(fd >= 0);
-	if (!new_content)
-		new_content = "\n";
-	cl_must_pass(p_write(fd, new_content, strlen(new_content)));
+	int fd;
+	cl_assert(path && content);
+	cl_assert((fd = p_open(path, flags, mode)) >= 0);
+	if (!content_len)
+		content_len = strlen(content);
+	cl_must_pass(p_write(fd, content, content_len));
 	cl_must_pass(p_close(fd));
 }
 
-void cl_git_append2file(const char *filename, const char *new_content)
+void cl_git_append2file(const char *path, const char *content)
 {
-	cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_APPEND, 0644);
+	cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
 }
 
-void cl_git_rewritefile(const char *filename, const char *new_content)
+void cl_git_rewritefile(const char *path, const char *content)
 {
-	cl_git_write2file(filename, new_content, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
 }
 
 #ifdef GIT_WIN32
@@ -56,23 +59,24 @@
 
 char *cl_getenv(const char *name)
 {
-	wchar_t name_utf16[GIT_WIN_PATH];
+	git_win32_path name_utf16;
 	DWORD alloc_len;
 	wchar_t *value_utf16;
 	char *value_utf8;
 
-	git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
+	git_win32_path_from_c(name_utf16, name);
 	alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0);
 	if (alloc_len <= 0)
 		return NULL;
 
-	alloc_len = GIT_WIN_PATH;
 	cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t)));
 
 	GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len);
 
-	cl_assert(value_utf8 = git__malloc(alloc_len));
-	git__utf16_to_8(value_utf8, value_utf16);
+	alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */
+	cl_assert(value_utf8 = git__calloc(alloc_len, 1));
+
+	git__utf16_to_8(value_utf8, alloc_len, value_utf16);
 
 	git__free(value_utf16);
 
@@ -81,13 +85,13 @@
 
 int cl_setenv(const char *name, const char *value)
 {
-	wchar_t name_utf16[GIT_WIN_PATH];
-	wchar_t value_utf16[GIT_WIN_PATH];
+	git_win32_path name_utf16;
+	git_win32_path value_utf16;
 
-	git__utf8_to_16(name_utf16, GIT_WIN_PATH, name);
+	git_win32_path_from_c(name_utf16, name);
 
 	if (value) {
-		git__utf8_to_16(value_utf16, GIT_WIN_PATH, value);
+		git_win32_path_from_c(value_utf16, value);
 		cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16));
 	} else {
 		/* Windows XP returns 0 (failed) when passing NULL for lpValue when
@@ -107,12 +111,12 @@
  * the source is a directory, a child of the source). */
 int cl_rename(const char *source, const char *dest)
 {
-	wchar_t source_utf16[GIT_WIN_PATH];
-	wchar_t dest_utf16[GIT_WIN_PATH];
+	git_win32_path source_utf16;
+	git_win32_path dest_utf16;
 	unsigned retries = 1;
 
-	git__utf8_to_16(source_utf16, GIT_WIN_PATH, source);
-	git__utf8_to_16(dest_utf16, GIT_WIN_PATH, dest);
+	git_win32_path_from_c(source_utf16, source);
+	git_win32_path_from_c(dest_utf16, dest);
 
 	while (!MoveFileW(source_utf16, dest_utf16)) {
 		/* Only retry if the error is ERROR_ACCESS_DENIED;
@@ -187,6 +191,9 @@
 	/* Now open the sandbox repository and make it available for tests */
 	cl_git_pass(git_repository_open(&_cl_repo, sandbox));
 
+	/* Adjust configs after copying to new filesystem */
+	cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
+
 	return _cl_repo;
 }
 
@@ -298,7 +305,7 @@
 	size_t pathlen;
 
 	if (git_path_isdir(path->ptr) == true)
-		return git_path_direach(path, remove_placeholders_recurs, data);
+		return git_path_direach(path, 0, remove_placeholders_recurs, data);
 
 	pathlen = path->size;
 
@@ -336,6 +343,65 @@
 	return error;
 }
 
+#define CL_COMMIT_NAME "Libgit2 Tester"
+#define CL_COMMIT_EMAIL "libgit2-test@github.com"
+#define CL_COMMIT_MSG "Test commit of tree "
+
+void cl_repo_commit_from_index(
+	git_oid *out,
+	git_repository *repo,
+	git_signature *sig,
+	git_time_t time,
+	const char *msg)
+{
+	git_index *index;
+	git_oid commit_id, tree_id;
+	git_object *parent = NULL;
+	git_reference *ref = NULL;
+	git_tree *tree = NULL;
+	char buf[128];
+	int free_sig = (sig == NULL);
+
+	/* it is fine if looking up HEAD fails - we make this the first commit */
+	git_revparse_ext(&parent, &ref, repo, "HEAD");
+
+	/* write the index content as a tree */
+	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_index_write_tree(&tree_id, index));
+	cl_git_pass(git_index_write(index));
+	git_index_free(index);
+
+	cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+
+	if (sig)
+		cl_assert(sig->name && sig->email);
+	else if (!time)
+		cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
+	else
+		cl_git_pass(git_signature_new(
+			&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
+
+	if (!msg) {
+		strcpy(buf, CL_COMMIT_MSG);
+		git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
+			sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
+		msg = buf;
+	}
+
+	cl_git_pass(git_commit_create_v(
+		&commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
+		sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
+
+	if (out)
+		git_oid_cpy(out, &commit_id);
+
+	git_object_free(parent);
+	git_reference_free(ref);
+	if (free_sig)
+		git_signature_free(sig);
+	git_tree_free(tree);
+}
+
 void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
 {
 	git_config *config;
@@ -343,3 +409,75 @@
 	cl_git_pass(git_config_set_bool(config, cfg, value != 0));
 	git_config_free(config);
 }
+
+int cl_repo_get_bool(git_repository *repo, const char *cfg)
+{
+	int val = 0;
+	git_config *config;
+	cl_git_pass(git_repository_config(&config, repo));
+	cl_git_pass(git_config_get_bool(&val, config, cfg));;
+	git_config_free(config);
+	return val;
+}
+
+/* this is essentially the code from git__unescape modified slightly */
+static size_t strip_cr_from_buf(char *start, size_t len)
+{
+	char *scan, *trail, *end = start + len;
+
+	for (scan = trail = start; scan < end; trail++, scan++) {
+		while (*scan == '\r')
+			scan++; /* skip '\r' */
+
+		if (trail != scan)
+			*trail = *scan;
+	}
+
+	*trail = '\0';
+
+	return (trail - start);
+}
+
+void clar__assert_equal_file(
+	const char *expected_data,
+	size_t expected_bytes,
+	int ignore_cr,
+	const char *path,
+	const char *file,
+	int line)
+{
+	char buf[4000];
+	ssize_t bytes, total_bytes = 0;
+	int fd = p_open(path, O_RDONLY | O_BINARY);
+	cl_assert(fd >= 0);
+
+	if (expected_data && !expected_bytes)
+		expected_bytes = strlen(expected_data);
+
+	while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
+		clar__assert(
+			bytes > 0, file, line, "error reading from file", path, 1);
+
+		if (ignore_cr)
+			bytes = strip_cr_from_buf(buf, bytes);
+
+		if (memcmp(expected_data, buf, bytes) != 0) {
+			int pos;
+			for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
+				/* find differing byte offset */;
+			p_snprintf(
+				buf, sizeof(buf), "file content mismatch at byte %d",
+				(int)(total_bytes + pos));
+			clar__fail(file, line, buf, path, 1);
+		}
+
+		expected_data += bytes;
+		total_bytes   += bytes;
+	}
+
+	p_close(fd);
+
+	clar__assert(!bytes, file, line, "error reading from file", path, 1);
+	clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ,
+		(size_t)expected_bytes, (size_t)total_bytes);
+}
diff --git a/tests/clar_libgit2.h b/tests/clar_libgit2.h
new file mode 100644
index 0000000..b9ef562
--- /dev/null
+++ b/tests/clar_libgit2.h
@@ -0,0 +1,119 @@
+#ifndef __CLAR_LIBGIT2__
+#define __CLAR_LIBGIT2__
+
+#include "clar.h"
+#include <git2.h>
+#include "common.h"
+
+/**
+ * Replace for `clar_must_pass` that passes the last library error as the
+ * test failure message.
+ *
+ * Use this wrapper around all `git_` library calls that return error codes!
+ */
+#define cl_git_pass(expr) do { \
+	int _lg2_error; \
+	giterr_clear(); \
+	if ((_lg2_error = (expr)) != 0) \
+		cl_git_report_failure(_lg2_error, __FILE__, __LINE__, "Function call failed: " #expr); \
+	} while (0)
+
+/**
+ * Wrapper for `clar_must_fail` -- this one is
+ * 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)
+
+void cl_git_report_failure(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)
+
+GIT_INLINE(void) clar__assert_in_range(
+	int lo, int val, int hi,
+	const char *file, int line, const char *err, int should_abort)
+{
+	if (lo > val || hi < val) {
+		char buf[128];
+		snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
+		clar__fail(file, line, err, buf, should_abort);
+	}
+}
+
+#define cl_assert_equal_sz(sz1,sz2) do { \
+	size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
+	clar__assert_equal(__FILE__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
+} while (0)
+
+#define cl_assert_in_range(L,V,H) \
+	clar__assert_in_range((L),(V),(H),__FILE__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
+
+#define cl_assert_equal_file(DATA,SIZE,PATH) \
+	clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,(int)__LINE__)
+
+#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
+	clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,(int)__LINE__)
+
+void clar__assert_equal_file(
+	const char *expected_data,
+	size_t expected_size,
+	int ignore_cr,
+	const char *path,
+	const char *file,
+	int line);
+
+/*
+ * Some utility macros for building long strings
+ */
+#define REP4(STR)	 STR STR STR STR
+#define REP15(STR)	 REP4(STR) REP4(STR) REP4(STR) STR STR STR
+#define REP16(STR)	 REP4(REP4(STR))
+#define REP256(STR)  REP16(REP16(STR))
+#define REP1024(STR) REP4(REP256(STR))
+
+/* Write the contents of a buffer to disk */
+void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
+void cl_git_rewritefile(const char *filename, const char *new_content);
+void cl_git_write2file(const char *path, const char *data,
+	size_t datalen, int flags, unsigned int mode);
+
+bool cl_toggle_filemode(const char *filename);
+bool cl_is_chmod_supported(void);
+
+/* Environment wrappers */
+char *cl_getenv(const char *name);
+int cl_setenv(const char *name, const char *value);
+
+/* Reliable rename */
+int cl_rename(const char *source, const char *dest);
+
+/* Git sandbox setup helpers */
+
+git_repository *cl_git_sandbox_init(const char *sandbox);
+void cl_git_sandbox_cleanup(void);
+git_repository *cl_git_sandbox_reopen(void);
+
+/* Local-repo url helpers */
+const char* cl_git_fixture_url(const char *fixturename);
+const char* cl_git_path_url(const char *path);
+
+/* Test repository cleaner */
+int cl_git_remove_placeholders(const char *directory_path, const char *filename);
+
+/* commit creation helpers */
+void cl_repo_commit_from_index(
+	git_oid *out,
+	git_repository *repo,
+	git_signature *sig,
+	git_time_t time,
+	const char *msg);
+
+/* config setting helpers */
+void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
+int cl_repo_get_bool(git_repository *repo, const char *cfg);
+
+#endif
diff --git a/tests-clar/clone/empty.c b/tests/clone/empty.c
similarity index 91%
rename from tests-clar/clone/empty.c
rename to tests/clone/empty.c
index f92fa6c..6d19244 100644
--- a/tests-clar/clone/empty.c
+++ b/tests/clone/empty.c
@@ -10,12 +10,14 @@
 void test_clone_empty__initialize(void)
 {
 	git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
+	git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
 	cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
 
 	g_repo = NULL;
 
 	memset(&g_options, 0, sizeof(git_clone_options));
 	g_options.version = GIT_CLONE_OPTIONS_VERSION;
+	g_options.remote_callbacks = dummy_callbacks;
 }
 
 void test_clone_empty__cleanup(void)
@@ -44,7 +46,7 @@
 	g_options.bare = true;
 	cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
 
-	/* Although the HEAD is orphaned... */
+	/* Although the HEAD is unborn... */
 	cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
 
 	/* ...one can still retrieve the name of the remote tracking reference */
@@ -59,7 +61,7 @@
 
 	cl_assert_equal_s(expected_remote_name, buffer);
 
-	/* ...even when the remote HEAD is orphaned as well */
+	/* ...even when the remote HEAD is unborn as well */
 	cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
 		expected_tracked_branch_name));
 }
diff --git a/tests/clone/nonetwork.c b/tests/clone/nonetwork.c
new file mode 100644
index 0000000..a286e2a
--- /dev/null
+++ b/tests/clone/nonetwork.c
@@ -0,0 +1,179 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "remote.h"
+#include "fileops.h"
+#include "repository.h"
+
+#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
+
+static git_clone_options g_options;
+static git_repository *g_repo;
+static git_reference* g_ref;
+static git_remote* g_remote;
+
+void test_clone_nonetwork__initialize(void)
+{
+	git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
+	git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+	g_repo = NULL;
+
+	memset(&g_options, 0, sizeof(git_clone_options));
+	g_options.version = GIT_CLONE_OPTIONS_VERSION;
+	g_options.checkout_opts = dummy_opts;
+	g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+	g_options.remote_callbacks = dummy_callbacks;
+}
+
+void test_clone_nonetwork__cleanup(void)
+{
+	if (g_repo) {
+		git_repository_free(g_repo);
+		g_repo = NULL;
+	}
+
+	if (g_ref) {
+		git_reference_free(g_ref);
+		g_ref = NULL;
+	}
+
+	if (g_remote) {
+		git_remote_free(g_remote);
+		g_remote = NULL;
+	}
+
+	cl_fixture_cleanup("./foo");
+}
+
+void test_clone_nonetwork__bad_urls(void)
+{
+	/* Clone should clean up the mess if the URL isn't a git repository */
+	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+	cl_assert(!git_path_exists("./foo"));
+	g_options.bare = true;
+	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+	cl_assert(!git_path_exists("./foo"));
+
+	cl_git_fail(git_clone(&g_repo, "git://example.com:asdf", "./foo", &g_options));
+	cl_git_fail(git_clone(&g_repo, "https://example.com:asdf/foo", "./foo", &g_options));
+	cl_git_fail(git_clone(&g_repo, "git://github.com/git://github.com/foo/bar.git.git",
+				"./foo", &g_options));
+	cl_git_fail(git_clone(&g_repo, "arrbee:my/bad:password@github.com:1111/strange:words.git",
+				"./foo", &g_options));
+}
+
+void test_clone_nonetwork__do_not_clean_existing_directory(void)
+{
+	/* Clone should not remove the directory if it already exists, but
+	 * Should clean up entries it creates. */
+	p_mkdir("./foo", GIT_DIR_MODE);
+	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+	cl_assert(git_path_is_empty_dir("./foo"));
+
+	/* Try again with a bare repository. */
+	g_options.bare = true;
+	cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+	cl_assert(git_path_is_empty_dir("./foo"));
+}
+
+void test_clone_nonetwork__local(void)
+{
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_absolute_path(void)
+{
+	const char *local_src;
+	local_src = cl_fixture("testrepo.git");
+	cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_bare(void)
+{
+	g_options.bare = true;
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
+{
+	cl_git_mkfile("./foo", "Bar!");
+	cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
+{
+	p_mkdir("./foo", GIT_DIR_MODE);
+	cl_git_mkfile("./foo/bar", "Baz!");
+	cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__custom_origin_name(void)
+{
+       g_options.remote_name = "my_origin";
+       cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+       cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin"));
+}
+
+void test_clone_nonetwork__defaults(void)
+{
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
+	cl_assert(g_repo);
+	cl_git_pass(git_remote_load(&g_remote, g_repo, "origin"));
+}
+
+void test_clone_nonetwork__cope_with_already_existing_directory(void)
+{
+	p_mkdir("./foo", GIT_DIR_MODE);
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	g_options.checkout_opts.checkout_strategy = 0;
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+	cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&path)));
+
+	git_buf_free(&path);
+}
+
+void test_clone_nonetwork__can_checkout_given_branch(void)
+{
+	g_options.checkout_branch = "test";
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+	cl_assert_equal_i(0, git_repository_head_unborn(g_repo));
+
+	cl_git_pass(git_repository_head(&g_ref, g_repo));
+	cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
+}
+
+void test_clone_nonetwork__can_detached_head(void)
+{
+	git_object *obj;
+	git_repository *cloned;
+	git_reference *cloned_head;
+
+	cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+	cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
+	cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+	cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
+
+	cl_assert(git_repository_head_detached(cloned));
+
+	cl_git_pass(git_repository_head(&cloned_head, cloned));
+	cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head)));
+
+	git_object_free(obj);
+	git_reference_free(cloned_head);
+	git_repository_free(cloned);
+
+	cl_fixture_cleanup("./foo1");
+}
diff --git a/tests-clar/commit/commit.c b/tests/commit/commit.c
similarity index 100%
rename from tests-clar/commit/commit.c
rename to tests/commit/commit.c
diff --git a/tests-clar/commit/parent.c b/tests/commit/parent.c
similarity index 100%
rename from tests-clar/commit/parent.c
rename to tests/commit/parent.c
diff --git a/tests-clar/commit/parse.c b/tests/commit/parse.c
similarity index 74%
rename from tests-clar/commit/parse.c
rename to tests/commit/parse.c
index 415860a..41e1624 100644
--- a/tests-clar/commit/parse.c
+++ b/tests/commit/parse.c
@@ -7,77 +7,77 @@
 static git_repository *g_repo;
 void test_commit_parse__initialize(void)
 {
-   g_repo = cl_git_sandbox_init("testrepo");
+	g_repo = cl_git_sandbox_init("testrepo");
 }
 void test_commit_parse__cleanup(void)
 {
-   cl_git_sandbox_cleanup();
+	cl_git_sandbox_cleanup();
 }
 
 
 // Header parsing
 typedef struct {
-   const char *line;
-   const char *header;
+	const char *line;
+	const char *header;
 } parse_test_case;
 
 static parse_test_case passing_header_cases[] = {
-   { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
-   { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
-   { "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
-   { "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
-   { "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
-   { "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
-   { "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
-   { "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
-   { NULL, NULL }
+	{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+	{ "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+	{ "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
+	{ "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
+	{ "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
+	{ "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
+	{ "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
+	{ "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
+	{ NULL, NULL }
 };
 
 static parse_test_case failing_header_cases[] = {
-   { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
-   { "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
-   { "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
-   { "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
-   { "tree  05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
-   { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
-   { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
-   { "", "tree " },
-   { "", "" },
-   { NULL, NULL }
+	{ "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
+	{ "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+	{ "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
+	{ "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
+	{ "tree  05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+	{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+	{ "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
+	{ "", "tree " },
+	{ "", "" },
+	{ NULL, NULL }
 };
 
 void test_commit_parse__header(void)
 {
-   git_oid oid;
+	git_oid oid;
 
-   parse_test_case *testcase;
-   for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
-   {
-      const char *line = testcase->line;
-      const char *line_end = line + strlen(line);
+	parse_test_case *testcase;
+	for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
+	{
+		const char *line = testcase->line;
+		const char *line_end = line + strlen(line);
 
-      cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
-      cl_assert(line == line_end);
-   }
+		cl_git_pass(git_oid__parse(&oid, &line, line_end, testcase->header));
+		cl_assert(line == line_end);
+	}
 
-   for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
-   {
-      const char *line = testcase->line;
-      const char *line_end = line + strlen(line);
+	for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
+	{
+		const char *line = testcase->line;
+		const char *line_end = line + strlen(line);
 
-      cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
-   }
+		cl_git_fail(git_oid__parse(&oid, &line, line_end, testcase->header));
+	}
 }
 
 
 // Signature parsing
 typedef struct {
-   const char *string;
-   const char *header;
-   const char *name;
-   const char *email;
-   git_time_t time;
-   int offset;
+	const char *string;
+	const char *header;
+	const char *name;
+	const char *email;
+	git_time_t time;
+	int offset;
 } passing_signature_test_case;
 
 passing_signature_test_case passing_signature_cases[] = {
@@ -122,12 +122,12 @@
 	{"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
 	{"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
 
-   {NULL,NULL,NULL,NULL,0,0}
+	{NULL,NULL,NULL,NULL,0,0}
 };
 
 typedef struct {
-   const char *string;
-   const char *header;
+	const char *string;
+	const char *header;
 } failing_signature_test_case;
 
 failing_signature_test_case failing_signature_cases[] = {
@@ -143,31 +143,31 @@
 
 void test_commit_parse__signature(void)
 {
-   passing_signature_test_case *passcase;
-   failing_signature_test_case *failcase;
+	passing_signature_test_case *passcase;
+	failing_signature_test_case *failcase;
 
-   for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
-   {
-      const char *str = passcase->string;
-      size_t len = strlen(passcase->string);
-      struct git_signature person = {0};
+	for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
+	{
+		const char *str = passcase->string;
+		size_t len = strlen(passcase->string);
+		struct git_signature person = {0};
 
-      cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
-      cl_assert_equal_s(passcase->name, person.name);
-      cl_assert_equal_s(passcase->email, person.email);
-      cl_assert_equal_i((int)passcase->time, (int)person.when.time);
-      cl_assert_equal_i(passcase->offset, person.when.offset);
-      git__free(person.name); git__free(person.email);
-   }
+		cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
+		cl_assert_equal_s(passcase->name, person.name);
+		cl_assert_equal_s(passcase->email, person.email);
+		cl_assert_equal_i((int)passcase->time, (int)person.when.time);
+		cl_assert_equal_i(passcase->offset, person.when.offset);
+		git__free(person.name); git__free(person.email);
+	}
 
-   for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
-   {
-      const char *str = failcase->string;
-      size_t len = strlen(failcase->string);
-      git_signature person = {0};
-      cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
-      git__free(person.name); git__free(person.email);
-   }
+	for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
+	{
+		const char *str = failcase->string;
+		size_t len = strlen(failcase->string);
+		git_signature person = {0};
+		cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
+		git__free(person.name); git__free(person.email);
+	}
 }
 
 
@@ -312,17 +312,17 @@
 
 // query the details on a parsed commit
 void test_commit_parse__details0(void) {
-   static const char *commit_ids[] = {
-      "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
-      "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
-      "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
-      "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
-      "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
-      "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
-      "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
-   };
+	static const char *commit_ids[] = {
+		"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+		"9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+		"4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+		"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+		"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+		"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
+	};
 	const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
-   unsigned int i;
+	unsigned int i;
 
 	for (i = 0; i < commit_count; ++i) {
 		git_oid id;
@@ -349,7 +349,6 @@
 		cl_assert_equal_s("Scott Chacon", committer->name);
 		cl_assert_equal_s("schacon@gmail.com", committer->email);
 		cl_assert(message != NULL);
-		cl_assert(strchr(message, '\n') != NULL);
 		cl_assert(commit_time > 0);
 		cl_assert(parents <= 2);
 		for (p = 0;p < parents;p++) {
@@ -382,11 +381,33 @@
 \n\
 This commit has a few LF at the start of the commit message";
 	const char *message =
+"This commit has a few LF at the start of the commit message";
+	const char *raw_message =
 "\n\
 \n\
 This commit has a few LF at the start of the commit message";
+	cl_git_pass(parse_commit(&commit, buffer));
+	cl_assert_equal_s(message, git_commit_message(commit));
+	cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
+	git_commit__free(commit);
+}
+
+void test_commit_parse__only_lf(void)
+{
+	git_commit *commit;
+	const char *buffer =
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+\n\
+\n";
+	const char *message = "";
+	const char *raw_message = "\n\n";
 
 	cl_git_pass(parse_commit(&commit, buffer));
 	cl_assert_equal_s(message, git_commit_message(commit));
+	cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
 	git_commit__free(commit);
 }
diff --git a/tests-clar/commit/signature.c b/tests/commit/signature.c
similarity index 100%
rename from tests-clar/commit/signature.c
rename to tests/commit/signature.c
diff --git a/tests-clar/commit/write.c b/tests/commit/write.c
similarity index 100%
rename from tests-clar/commit/write.c
rename to tests/commit/write.c
diff --git a/tests-clar/config/add.c b/tests/config/add.c
similarity index 100%
rename from tests-clar/config/add.c
rename to tests/config/add.c
diff --git a/tests-clar/config/backend.c b/tests/config/backend.c
similarity index 100%
rename from tests-clar/config/backend.c
rename to tests/config/backend.c
diff --git a/tests-clar/config/config_helpers.c b/tests/config/config_helpers.c
similarity index 100%
rename from tests-clar/config/config_helpers.c
rename to tests/config/config_helpers.c
diff --git a/tests-clar/config/config_helpers.h b/tests/config/config_helpers.h
similarity index 100%
rename from tests-clar/config/config_helpers.h
rename to tests/config/config_helpers.h
diff --git a/tests-clar/config/configlevel.c b/tests/config/configlevel.c
similarity index 100%
rename from tests-clar/config/configlevel.c
rename to tests/config/configlevel.c
diff --git a/tests-clar/config/global.c b/tests/config/global.c
similarity index 68%
rename from tests-clar/config/global.c
rename to tests/config/global.c
index 2ecdf97..d5f95f5 100644
--- a/tests-clar/config/global.c
+++ b/tests/config/global.c
@@ -6,19 +6,22 @@
 {
 	git_buf path = GIT_BUF_INIT;
 
-	cl_must_pass(p_mkdir("home", 0777));
+	cl_assert_equal_i(0, p_mkdir("home", 0777));
 	cl_git_pass(git_path_prettify(&path, "home", NULL));
 	cl_git_pass(git_libgit2_opts(
 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
 
-	cl_must_pass(p_mkdir("xdg", 0777));
-	cl_git_pass(git_path_prettify(&path, "xdg", NULL));
+	cl_assert_equal_i(0, p_mkdir("xdg", 0777));
+	cl_assert_equal_i(0, p_mkdir("xdg/git", 0777));
+	cl_git_pass(git_path_prettify(&path, "xdg/git", NULL));
+	cl_git_pass(git_libgit2_opts(
+		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+	cl_assert_equal_i(0, p_mkdir("etc", 0777));
+	cl_git_pass(git_path_prettify(&path, "etc", NULL));
 	cl_git_pass(git_libgit2_opts(
 		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
 
-	cl_git_pass(git_libgit2_opts(
-		GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL));
-
 	git_buf_free(&path);
 }
 
@@ -26,6 +29,11 @@
 {
 	cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
 	cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
+	cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
+
+	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL);
+	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL);
+	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL);
 }
 
 void test_config_global__open_global(void)
@@ -48,10 +56,7 @@
 	const char *val, *str = "teststring";
 	const char *key = "this.variable";
 
-	p_setenv("XDG_CONFIG_HOME", "xdg", 1);
-
-	cl_must_pass(p_mkdir("xdg/git/", 0777));
-	cl_git_mkfile("xdg/git/config", "");
+	cl_git_mkfile("xdg/git/config", "# XDG config\n[core]\n  test = 1\n");
 
 	cl_git_pass(git_config_open_default(&cfg));
 	cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
diff --git a/tests/config/include.c b/tests/config/include.c
new file mode 100644
index 0000000..5355738
--- /dev/null
+++ b/tests/config/include.c
@@ -0,0 +1,109 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "fileops.h"
+
+void test_config_include__relative(void)
+{
+	git_config *cfg;
+	const char *str;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "huzzah");
+
+	git_config_free(cfg);
+}
+
+void test_config_include__absolute(void)
+{
+	git_config *cfg;
+	const char *str;
+	git_buf buf = GIT_BUF_INIT;
+
+	cl_git_pass(git_buf_printf(&buf, "[include]\npath = %s/config-included", cl_fixture("config")));
+
+	cl_git_mkfile("config-include-absolute", git_buf_cstr(&buf));
+	git_buf_free(&buf);
+	cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "huzzah");
+
+	git_config_free(cfg);
+}
+
+void test_config_include__homedir(void)
+{
+	git_config *cfg;
+	const char *str;
+
+	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, cl_fixture("config")));
+	cl_git_mkfile("config-include-homedir",  "[include]\npath = ~/config-included");
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "huzzah");
+
+	git_config_free(cfg);
+}
+
+void test_config_include__refresh(void)
+{
+	git_config *cfg;
+	const char *str;
+
+	cl_fixture_sandbox("config");
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config-include"));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "huzzah");
+
+	/* Change the included file and see if we refresh */
+	cl_git_mkfile("config/config-included", "[foo \"bar\"]\nbaz = hurrah");
+	cl_git_pass(git_config_refresh(cfg));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "hurrah");
+
+	git_config_free(cfg);
+	cl_fixture_cleanup("config");
+}
+
+/* We need to pretend that the variables were defined where the file was included */
+void test_config_include__ordering(void)
+{
+	git_config *cfg;
+	const char *str;
+
+	cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
+	cl_git_mkfile("including",
+		      "[foo \"bar\"]\nfrotz = hello\n"
+		      "[include]\npath = included\n"
+		      "[foo \"bar\"]\nbaz = huzzah\n");
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.frotz"));
+	cl_assert_equal_s(str, "hiya");
+	cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz"));
+	cl_assert_equal_s(str, "huzzah");
+
+	git_config_free(cfg);
+}
+
+/* We need to pretend that the variables were defined where the file was included */
+void test_config_include__depth(void)
+{
+	git_config *cfg;
+
+	cl_git_mkfile("a", "[include]\npath = b");
+	cl_git_mkfile("b", "[include]\npath = a");
+
+	cl_git_fail(git_config_open_ondisk(&cfg, "a"));
+
+	unlink("a");
+	unlink("b");
+}
diff --git a/tests/config/multivar.c b/tests/config/multivar.c
new file mode 100644
index 0000000..afdb1e5
--- /dev/null
+++ b/tests/config/multivar.c
@@ -0,0 +1,288 @@
+#include "clar_libgit2.h"
+
+static const char *_name = "remote.ab.url";
+
+void test_config_multivar__initialize(void)
+{
+	cl_fixture_sandbox("config");
+}
+
+void test_config_multivar__cleanup(void)
+{
+	cl_fixture_cleanup("config");
+}
+
+static int mv_read_cb(const git_config_entry *entry, void *data)
+{
+	int *n = (int *) data;
+
+	if (!strcmp(entry->name, _name))
+		(*n)++;
+
+	return 0;
+}
+
+void test_config_multivar__foreach(void)
+{
+	git_config *cfg;
+	int n = 0;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
+
+	cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
+	cl_assert(n == 2);
+
+	git_config_free(cfg);
+}
+
+static int cb(const git_config_entry *entry, void *data)
+{
+	int *n = (int *) data;
+
+	GIT_UNUSED(entry);
+
+	(*n)++;
+
+	return 0;
+}
+
+static void check_get_multivar_foreach(
+	git_config *cfg, int expected, int expected_patterned)
+{
+	int n = 0;
+
+	if (expected > 0) {
+		cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+		cl_assert_equal_i(expected, n);
+	} else {
+		cl_assert_equal_i(GIT_ENOTFOUND,
+			git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	}
+
+	n = 0;
+
+	if (expected_patterned > 0) {
+		cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+		cl_assert_equal_i(expected_patterned, n);
+	} else {
+		cl_assert_equal_i(GIT_ENOTFOUND,
+			git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+	}
+}
+
+static void check_get_multivar(git_config *cfg, int expected)
+{
+	git_config_iterator *iter;
+	git_config_entry *entry;
+	int n = 0;
+
+	cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL));
+
+	while (git_config_next(&entry, iter) == 0)
+		n++;
+
+	cl_assert_equal_i(expected, n);
+	git_config_iterator_free(iter);
+
+}
+
+void test_config_multivar__get(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+	check_get_multivar_foreach(cfg, 2, 1);
+
+	/* add another that has the _name entry */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, 1));
+	check_get_multivar_foreach(cfg, 3, 2);
+
+	/* add another that does not have the _name entry */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, 1));
+	check_get_multivar_foreach(cfg, 3, 2);
+
+	/* add another that does not have the _name entry at the end */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, 1));
+	check_get_multivar_foreach(cfg, 3, 2);
+
+	/* drop original file */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, 1));
+	check_get_multivar_foreach(cfg, 1, 1);
+
+	/* drop other file with match */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, 1));
+	check_get_multivar_foreach(cfg, 0, 0);
+
+	/* reload original file (add different place in order) */
+	cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, 1));
+	check_get_multivar_foreach(cfg, 2, 1);
+
+	check_get_multivar(cfg, 2);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__add(void)
+{
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+	cl_git_pass(git_config_set_multivar(cfg, _name, "nonexistant", "git://git.otherplace.org/libgit2"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert_equal_i(n, 3);
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+	cl_assert_equal_i(n, 1);
+
+	git_config_free(cfg);
+
+	/* We know it works in memory, let's see if the file is written correctly */
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert_equal_i(n, 3);
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+	cl_assert_equal_i(n, 1);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__add_new(void)
+{
+	const char *var = "a.brand.new";
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	cl_git_pass(git_config_set_multivar(cfg, var, "", "variable"));
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
+	cl_assert_equal_i(n, 1);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__replace(void)
+{
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 2);
+
+	cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 2);
+
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 2);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__replace_multiple(void)
+{
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+	cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+	cl_assert_equal_i(n, 2);
+
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+	cl_assert_equal_i(n, 2);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__delete(void)
+{
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 2);
+
+	cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 1);
+
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 1);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__delete_multiple(void)
+{
+	git_config *cfg;
+	int n;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+	cl_assert(n == 2);
+
+	cl_git_pass(git_config_delete_multivar(cfg, _name, "git"));
+
+	n = 0;
+	cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	n = 0;
+	cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+	git_config_free(cfg);
+}
+
+void test_config_multivar__delete_notfound(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+	cl_git_fail_with(git_config_delete_multivar(cfg, "remote.ab.noturl", "git"), GIT_ENOTFOUND);
+
+	git_config_free(cfg);
+}
diff --git a/tests-clar/config/new.c b/tests/config/new.c
similarity index 100%
rename from tests-clar/config/new.c
rename to tests/config/new.c
diff --git a/tests-clar/config/read.c b/tests/config/read.c
similarity index 79%
rename from tests-clar/config/read.c
rename to tests/config/read.c
index 9f943d0..abc088d 100644
--- a/tests-clar/config/read.c
+++ b/tests/config/read.c
@@ -164,6 +164,13 @@
 	git_config_free(cfg);
 }
 
+void test_config_read__symbol_headers(void)
+{
+	git_config *cfg;
+	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
+	git_config_free(cfg);
+}
+
 void test_config_read__header_in_last_line(void)
 {
 	git_config *cfg;
@@ -245,6 +252,37 @@
 	git_config_free(cfg);
 }
 
+void test_config_read__iterator(void)
+{
+	git_config *cfg;
+	git_config_iterator *iter;
+	git_config_entry *entry;
+	int count, ret;
+
+	cl_git_pass(git_config_new(&cfg));
+	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+		GIT_CONFIG_LEVEL_SYSTEM, 0));
+	cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+		GIT_CONFIG_LEVEL_GLOBAL, 0));
+
+	count = 0;
+	cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+	while ((ret = git_config_next(&entry, iter)) == 0) {
+		count++;
+	}
+
+	git_config_iterator_free(iter);
+	cl_assert_equal_i(GIT_ITEROVER, ret);
+	cl_assert_equal_i(7, count);
+
+	count = 3;
+	cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+	git_config_iterator_free(iter);
+	git_config_free(cfg);
+}
+
 static int count_cfg_entries(const git_config_entry *entry, void *payload)
 {
 	int *count = payload;
@@ -288,6 +326,38 @@
 	git_config_free(cfg);
 }
 
+static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
+{
+	git_config_iterator *iter;
+	git_config_entry *entry;
+	int count, error;
+
+	cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
+
+	count = 0;
+	while ((error = git_config_next(&entry, iter)) == 0)
+		count++;
+
+	cl_assert_equal_i(GIT_ITEROVER, error);
+	cl_assert_equal_i(expected, count);
+	git_config_iterator_free(iter);
+}
+
+void test_config_read__iterator_glob(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+	check_glob_iter(cfg, "core.*", 3);
+	check_glob_iter(cfg, "remote\\.ab.*", 2);
+	check_glob_iter(cfg, ".*url$", 2);
+	check_glob_iter(cfg, ".*dummy.*", 2);
+	check_glob_iter(cfg, ".*nomatch.*", 0);
+
+	git_config_free(cfg);
+}
+
 void test_config_read__whitespace_not_required_around_assignment(void)
 {
 	git_config *cfg;
@@ -431,10 +501,10 @@
 	git_config_free(cfg);
 }
 
-static void clean_empty_config(void *unused)
+static void clean_test_config(void *unused)
 {
 	GIT_UNUSED(unused);
-	cl_fixture_cleanup("./empty");
+	cl_fixture_cleanup("./testconfig");
 }
 
 void test_config_read__can_load_and_parse_an_empty_config_file(void)
@@ -442,10 +512,58 @@
 	git_config *cfg;
 	int i;
 
-	cl_set_cleanup(&clean_empty_config, NULL);
-	cl_git_mkfile("./empty", "");
-	cl_git_pass(git_config_open_ondisk(&cfg, "./empty"));
+	cl_set_cleanup(&clean_test_config, NULL);
+	cl_git_mkfile("./testconfig", "");
+	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
 	cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
 
 	git_config_free(cfg);
 }
+
+void test_config_read__corrupt_header(void)
+{
+	git_config *cfg;
+
+	cl_set_cleanup(&clean_test_config, NULL);
+	cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\"");
+	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+	git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header2(void)
+{
+	git_config *cfg;
+
+	cl_set_cleanup(&clean_test_config, NULL);
+	cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n    lib = git2\n");
+	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+	git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header3(void)
+{
+	git_config *cfg;
+
+	cl_set_cleanup(&clean_test_config, NULL);
+	cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n    lib = git2\n");
+	cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+	git_config_free(cfg);
+}
+
+void test_config_read__override_variable(void)
+{
+	git_config *cfg;
+	const char *str;
+
+	cl_set_cleanup(&clean_test_config, NULL);
+	cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
+	cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+	cl_git_pass(git_config_get_string(&str, cfg, "some.var"));
+	cl_assert_equal_s(str, "two");
+
+	git_config_free(cfg);
+}
diff --git a/tests-clar/config/refresh.c b/tests/config/refresh.c
similarity index 100%
rename from tests-clar/config/refresh.c
rename to tests/config/refresh.c
diff --git a/tests-clar/config/stress.c b/tests/config/stress.c
similarity index 95%
rename from tests-clar/config/stress.c
rename to tests/config/stress.c
index 8cc64d2..eeca54f 100644
--- a/tests-clar/config/stress.c
+++ b/tests/config/stress.c
@@ -10,12 +10,12 @@
 {
 	git_filebuf file = GIT_FILEBUF_INIT;
 
-	cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0));
+	cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0, 0666));
 
 	git_filebuf_printf(&file, "[color]\n\tui = auto\n");
 	git_filebuf_printf(&file, "[core]\n\teditor = \n");
 
-	cl_git_pass(git_filebuf_commit(&file, 0666));
+	cl_git_pass(git_filebuf_commit(&file));
 }
 
 void test_config_stress__cleanup(void)
diff --git a/tests-clar/config/validkeyname.c b/tests/config/validkeyname.c
similarity index 95%
rename from tests-clar/config/validkeyname.c
rename to tests/config/validkeyname.c
index 03c13d7..3369973 100644
--- a/tests-clar/config/validkeyname.c
+++ b/tests/config/validkeyname.c
@@ -28,7 +28,7 @@
 		GIT_EINVALIDSPEC);
 	cl_git_fail_with(git_config_delete_entry(cfg, name),
 		GIT_EINVALIDSPEC);
-	cl_git_fail_with(git_config_get_multivar(cfg, name, "*", NULL, NULL),
+	cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL),
 		GIT_EINVALIDSPEC);
 	cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
 		GIT_EINVALIDSPEC);
diff --git a/tests-clar/config/write.c b/tests/config/write.c
similarity index 78%
rename from tests-clar/config/write.c
rename to tests/config/write.c
index d70612a..15f750d 100644
--- a/tests-clar/config/write.c
+++ b/tests/config/write.c
@@ -229,6 +229,37 @@
 	git_config_free(cfg);
 }
 
+void test_config_write__add_value_which_needs_quotes(void)
+{
+	git_config *cfg;
+	const char* str1;
+	const char* str2;
+	const char* str3;
+	const char* str4;
+	const char* str5;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+	cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
+	cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
+	cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
+	cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
+	cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
+	git_config_free(cfg);
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+	cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
+	cl_assert_equal_s(" Something", str1);
+	cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
+	cl_assert_equal_s("Something ", str2);
+	cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
+	cl_assert_equal_s("some#thing", str3);
+	cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
+	cl_assert_equal_s("some;thing", str4);
+	cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
+	cl_assert_equal_s(" some\"thing", str5);
+	git_config_free(cfg);
+}
+
 void test_config_write__can_set_a_value_to_NULL(void)
 {
     git_repository *repository;
@@ -242,3 +273,33 @@
 
     cl_git_sandbox_cleanup();
 }
+
+void test_config_write__can_set_an_empty_value(void)
+{
+	git_repository *repository;
+	git_config *config;
+	const char * str;
+
+	repository = cl_git_sandbox_init("testrepo.git");
+	cl_git_pass(git_repository_config(&config, repository));
+
+	cl_git_pass(git_config_set_string(config, "core.somevar", ""));
+	cl_git_pass(git_config_get_string(&str, config, "core.somevar"));
+	cl_assert_equal_s(str, "");
+
+	git_config_free(config);
+	cl_git_sandbox_cleanup();
+}
+
+void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+
+	cl_git_mkfile("config9.lock", "[core]\n");
+
+	cl_git_fail_with(git_config_set_string(cfg, "core.dump", "boom"), GIT_ELOCKED);
+
+	git_config_free(cfg);
+}
diff --git a/tests/core/bitvec.c b/tests/core/bitvec.c
new file mode 100644
index 0000000..48d7b99
--- /dev/null
+++ b/tests/core/bitvec.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "bitvec.h"
+
+#if 0
+static void print_bitvec(git_bitvec *bv)
+{
+	int b;
+
+	if (!bv->length) {
+		for (b = 63; b >= 0; --b)
+			fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0);
+	} else {
+		for (b = bv->length * 8; b >= 0; --b)
+			fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0);
+	}
+	fprintf(stderr, "\n");
+}
+#endif
+
+static void set_some_bits(git_bitvec *bv, size_t length)
+{
+	size_t i;
+
+	for (i = 0; i < length; ++i) {
+		if (i % 3 == 0 || i % 7 == 0)
+			git_bitvec_set(bv, i, true);
+	}
+}
+
+static void check_some_bits(git_bitvec *bv, size_t length)
+{
+	size_t i;
+
+	for (i = 0; i < length; ++i)
+		cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i));
+}
+
+void test_core_bitvec__0(void)
+{
+	git_bitvec bv;
+
+	cl_git_pass(git_bitvec_init(&bv, 32));
+	set_some_bits(&bv, 16);
+	check_some_bits(&bv, 16);
+	git_bitvec_clear(&bv);
+	set_some_bits(&bv, 32);
+	check_some_bits(&bv, 32);
+	git_bitvec_clear(&bv);
+	set_some_bits(&bv, 64);
+	check_some_bits(&bv, 64);
+	git_bitvec_free(&bv);
+
+	cl_git_pass(git_bitvec_init(&bv, 128));
+	set_some_bits(&bv, 32);
+	check_some_bits(&bv, 32);
+	set_some_bits(&bv, 128);
+	check_some_bits(&bv, 128);
+	git_bitvec_free(&bv);
+
+	cl_git_pass(git_bitvec_init(&bv, 4000));
+	set_some_bits(&bv, 4000);
+	check_some_bits(&bv, 4000);
+	git_bitvec_free(&bv);
+}
diff --git a/tests-clar/core/buffer.c b/tests/core/buffer.c
similarity index 90%
rename from tests-clar/core/buffer.c
rename to tests/core/buffer.c
index 3d8221e..11d173d 100644
--- a/tests-clar/core/buffer.c
+++ b/tests/core/buffer.c
@@ -718,6 +718,8 @@
 	size_t data1len = 31;
 	char *data2 = "Internal NUL!!!\000\n\nI see you!\n";
 	size_t data2len = 29;
+	char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n";
+	size_t data3len = 20;
 	git_buf b;
 
 	b.ptr = data0; b.size = b.asize = data0len;
@@ -731,13 +733,18 @@
 	b.ptr = data2; b.size = b.asize = data2len;
 	cl_assert(git_buf_text_is_binary(&b));
 	cl_assert(git_buf_text_contains_nul(&b));
+
+	b.ptr = data3; b.size = b.asize = data3len;
+	cl_assert(!git_buf_text_is_binary(&b));
+	cl_assert(!git_buf_text_contains_nul(&b));
 }
 
 #define SIMILARITY_TEST_DATA_1 \
-	"test data\nright here\ninline\ntada\nneeds more data\nlots of data\n" \
-	"is this enough?\nthere has to be enough data to fill the hash array!\n" \
-	"Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n" \
-	"Let's make sure we've got plenty to go with here.\n   smile   \n"
+	"000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+	"010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+	"020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+	"030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+	"040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
 
 void test_core_buffer__similarity_metric(void)
 {
@@ -761,15 +768,17 @@
 	cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
 	cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 	cl_git_pass(git_buf_sets(&buf,
-		"Test data\nright here\ninline\ntada\nneeds more data\nlots of data\n"
-		"is this enough?\nthere has to be enough data to fill the hash array!\n"
-		"Apparently 191 bytes is the minimum amount of data needed.\nHere goes!\n"
-		 "Let's make sure we've got plenty to go with here.\n   smile   \n"));
+		"000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+		"010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+		"x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+		"030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+		"040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+		));
 	cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 
 	sim = git_hashsig_compare(a, b);
 
-	cl_assert(95 < sim && sim < 100); /* expect >95% similarity */
+	cl_assert_in_range(95, sim, 100); /* expect >95% similarity */
 
 	git_hashsig_free(a);
 	git_hashsig_free(b);
@@ -779,12 +788,13 @@
 	cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
 	cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 	cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1
-		"and if I add some more, it should still be pretty similar, yes?\n"));
+		"050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n"));
 	cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 
 	sim = git_hashsig_compare(a, b);
+	/* 20% lines added ~= 10% lines changed */
 
-	cl_assert(70 < sim && sim < 80); /* expect in the 70-80% similarity range */
+	cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */
 
 	git_hashsig_free(a);
 	git_hashsig_free(b);
@@ -794,15 +804,19 @@
 	cl_git_pass(git_buf_sets(&buf, SIMILARITY_TEST_DATA_1));
 	cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 	cl_git_pass(git_buf_sets(&buf,
-		"test data\nright here\ninline\ntada\nneeds more data\nlots of data\n"
-		"is this enough?\nthere has to be enough data to fill the hash array!\n"
-		"okay, that's half the original\nwhat else can we add?\nmore data\n"
-		 "one more line will complete this\nshort\nlines\ndon't\nmatter\n"));
+		"000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+		"010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+		"020x\n021\n022\n023\n024\n" \
+		"x25\nx26\nx27\nx28\nx29\n" \
+		"x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \
+		"x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n"
+		));
 	cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
 
 	sim = git_hashsig_compare(a, b);
+	/* 50% lines changed */
 
-	cl_assert(40 < sim && sim < 60); /* expect in the 40-60% similarity range */
+	cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */
 
 	git_hashsig_free(a);
 	git_hashsig_free(b);
@@ -891,7 +905,7 @@
 					if (i == j)
 						cl_assert_equal_i(100, sim);
 					else
-						cl_assert(sim < 30); /* expect pretty different */
+						cl_assert_in_range(0, sim, 30); /* pretty different */
 				} else {
 					cl_assert_equal_i(100, sim);
 				}
@@ -905,6 +919,8 @@
 	git_buf_free(&buf);
 }
 
+#include "../filter/crlf.h"
+
 #define check_buf(expected,buf) do { \
 	cl_assert_equal_s(expected, buf.ptr); \
 	cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
@@ -920,16 +936,16 @@
 	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
 	check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
 
-	cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
-	/* no conversion needed if all LFs already */
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(src.ptr, tgt);
 
 	git_buf_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
 
 	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
 	check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
 
-	cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_crlf_to_lf(&tgt, &src));
-	/* no conversion needed if all LFs already */
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(src.ptr, tgt);
 
 	/* CRLF source */
 
@@ -979,10 +995,45 @@
 	check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
 
 	git_buf_sets(&src, "\rcr\r");
-	cl_assert_equal_i(GIT_ENOTFOUND, git_buf_text_lf_to_crlf(&tgt, &src));
+	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+	check_buf(src.ptr, tgt);
 	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
 	check_buf("\rcr\r", tgt);
 
 	git_buf_free(&src);
 	git_buf_free(&tgt);
+
+	/* blob correspondence tests */
+
+	git_buf_sets(&src, ALL_CRLF_TEXT_RAW);
+	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+	check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
+	git_buf_free(&src);
+	git_buf_free(&tgt);
+
+	git_buf_sets(&src, ALL_LF_TEXT_RAW);
+	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+	check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(ALL_LF_TEXT_AS_LF, tgt);
+	git_buf_free(&src);
+	git_buf_free(&tgt);
+
+	git_buf_sets(&src, MORE_CRLF_TEXT_RAW);
+	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+	check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
+	git_buf_free(&src);
+	git_buf_free(&tgt);
+
+	git_buf_sets(&src, MORE_LF_TEXT_RAW);
+	cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src));
+	check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+	cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src));
+	check_buf(MORE_LF_TEXT_AS_LF, tgt);
+	git_buf_free(&src);
+	git_buf_free(&tgt);
 }
diff --git a/tests/core/caps.c b/tests/core/caps.c
new file mode 100644
index 0000000..68a518e
--- /dev/null
+++ b/tests/core/caps.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+
+void test_core_caps__0(void)
+{
+	int major, minor, rev, caps;
+
+	git_libgit2_version(&major, &minor, &rev);
+	cl_assert_equal_i(LIBGIT2_VER_MAJOR, major);
+	cl_assert_equal_i(LIBGIT2_VER_MINOR, minor);
+	cl_assert_equal_i(LIBGIT2_VER_REVISION, rev);
+
+	caps = git_libgit2_capabilities();
+
+#ifdef GIT_THREADS
+	cl_assert((caps & GIT_CAP_THREADS) != 0);
+#else
+	cl_assert((caps & GIT_CAP_THREADS) == 0);
+#endif
+
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+	cl_assert((caps & GIT_CAP_HTTPS) != 0);
+#else
+	cl_assert((caps & GIT_CAP_HTTPS) == 0);
+#endif
+
+#if defined(GIT_SSH)
+	cl_assert((caps & GIT_CAP_SSH) != 0);
+#else
+	cl_assert((caps & GIT_CAP_SSH) == 0);
+#endif
+}
diff --git a/tests-clar/core/copy.c b/tests/core/copy.c
similarity index 100%
rename from tests-clar/core/copy.c
rename to tests/core/copy.c
diff --git a/tests-clar/core/dirent.c b/tests/core/dirent.c
similarity index 78%
rename from tests-clar/core/dirent.c
rename to tests/core/dirent.c
index 5a7859d..f172603 100644
--- a/tests-clar/core/dirent.c
+++ b/tests/core/dirent.c
@@ -88,14 +88,6 @@
 	return GIT_ERROR;
 }
 
-static int dont_call_me(void *state, git_buf *path)
-{
-	GIT_UNUSED(state);
-	GIT_UNUSED(path);
-	return GIT_ERROR;
-}
-
-
 
 static name_data dot_names[] = {
 	{ 0, "./a" },
@@ -115,9 +107,7 @@
 	cl_set_cleanup(&dirent_cleanup__cb, &dot);
 	setup(&dot);
 
-	cl_git_pass(git_path_direach(&dot.path,
-					one_entry,
-					&dot));
+	cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
 
 	check_counts(&dot);
 }
@@ -141,9 +131,7 @@
 	cl_set_cleanup(&dirent_cleanup__cb, &sub);
 	setup(&sub);
 
-	cl_git_pass(git_path_direach(&sub.path,
-					one_entry,
-					&sub));
+	cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
 
 	check_counts(&sub);
 }
@@ -161,9 +149,7 @@
 	cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
 	setup(&sub_slash);
 
-	cl_git_pass(git_path_direach(&sub_slash.path,
-					one_entry,
-					&sub_slash));
+	cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
 
 	check_counts(&sub_slash);
 }
@@ -184,16 +170,12 @@
 	cl_set_cleanup(&dirent_cleanup__cb, &empty);
 	setup(&empty);
 
-	cl_git_pass(git_path_direach(&empty.path,
-					one_entry,
-					&empty));
+	cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
 
 	check_counts(&empty);
 
 	/* make sure callback not called */
-	cl_git_pass(git_path_direach(&empty.path,
-					dont_call_me,
-					&empty));
+	cl_assert(git_path_is_empty_dir(empty.path.ptr));
 }
 
 static name_data odd_names[] = {
@@ -216,9 +198,7 @@
 	cl_set_cleanup(&dirent_cleanup__cb, &odd);
 	setup(&odd);
 
-	cl_git_pass(git_path_direach(&odd.path,
-					one_entry,
-					&odd));
+	cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
 
 	check_counts(&odd);
 }
@@ -231,5 +211,26 @@
 	big_filename[FILENAME_MAX] = 0;
 
 	cl_must_fail(p_creat(big_filename, 0666));
+
 	git__free(big_filename);
 }
+
+void test_core_dirent__empty_dir(void)
+{
+	cl_must_pass(p_mkdir("empty_dir", 0777));
+	cl_assert(git_path_is_empty_dir("empty_dir"));
+
+	cl_git_mkfile("empty_dir/content", "whatever\n");
+	cl_assert(!git_path_is_empty_dir("empty_dir"));
+	cl_assert(!git_path_is_empty_dir("empty_dir/content"));
+
+	cl_must_pass(p_unlink("empty_dir/content"));
+
+	cl_must_pass(p_mkdir("empty_dir/content", 0777));
+	cl_assert(!git_path_is_empty_dir("empty_dir"));
+	cl_assert(git_path_is_empty_dir("empty_dir/content"));
+
+	cl_must_pass(p_rmdir("empty_dir/content"));
+
+	cl_must_pass(p_rmdir("empty_dir"));
+}
diff --git a/tests-clar/core/env.c b/tests/core/env.c
similarity index 100%
rename from tests-clar/core/env.c
rename to tests/core/env.c
diff --git a/tests-clar/core/errors.c b/tests/core/errors.c
similarity index 100%
rename from tests-clar/core/errors.c
rename to tests/core/errors.c
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
new file mode 100644
index 0000000..5a3e751
--- /dev/null
+++ b/tests/core/filebuf.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "filebuf.h"
+
+/* make sure git_filebuf_open doesn't delete an existing lock */
+void test_core_filebuf__0(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	int fd;
+	char test[] = "test", testlock[] = "test.lock";
+
+	fd = p_creat(testlock, 0744); //-V536
+
+	cl_must_pass(fd);
+	cl_must_pass(p_close(fd));
+
+	cl_git_fail(git_filebuf_open(&file, test, 0, 0666));
+	cl_assert(git_path_exists(testlock));
+
+	cl_must_pass(p_unlink(testlock));
+}
+
+
+/* make sure GIT_FILEBUF_APPEND works as expected */
+void test_core_filebuf__1(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char test[] = "test";
+
+	cl_git_mkfile(test, "libgit2 rocks\n");
+
+	cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND, 0666));
+	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+	cl_git_pass(git_filebuf_commit(&file));
+
+	cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test);
+
+	cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_write writes large buffer correctly */
+void test_core_filebuf__2(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char test[] = "test";
+	unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
+
+	memset(buf, 0xfe, sizeof(buf));
+
+	cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+	cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
+	cl_git_pass(git_filebuf_commit(&file));
+
+	cl_assert_equal_file((char *)buf, sizeof(buf), test);
+
+	cl_must_pass(p_unlink(test));
+}
+
+/* make sure git_filebuf_cleanup clears the buffer */
+void test_core_filebuf__4(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char test[] = "test";
+
+	cl_assert(file.buffer == NULL);
+
+	cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+	cl_assert(file.buffer != NULL);
+
+	git_filebuf_cleanup(&file);
+	cl_assert(file.buffer == NULL);
+}
+
+
+/* make sure git_filebuf_commit clears the buffer */
+void test_core_filebuf__5(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char test[] = "test";
+
+	cl_assert(file.buffer == NULL);
+
+	cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+	cl_assert(file.buffer != NULL);
+	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+	cl_assert(file.buffer != NULL);
+
+	cl_git_pass(git_filebuf_commit(&file));
+	cl_assert(file.buffer == NULL);
+
+	cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_commit takes umask into account */
+void test_core_filebuf__umask(void)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	char test[] = "test";
+	struct stat statbuf;
+	mode_t mask, os_mask;
+
+#ifdef GIT_WIN32
+	os_mask = 0600;
+#else
+	os_mask = 0777;
+#endif
+
+	p_umask(mask = p_umask(0));
+
+	cl_assert(file.buffer == NULL);
+
+	cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+	cl_assert(file.buffer != NULL);
+	cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+	cl_assert(file.buffer != NULL);
+
+	cl_git_pass(git_filebuf_commit(&file));
+	cl_assert(file.buffer == NULL);
+
+	cl_must_pass(p_stat("test", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask);
+
+	cl_must_pass(p_unlink(test));
+}
+
diff --git a/tests-clar/core/hex.c b/tests/core/hex.c
similarity index 100%
rename from tests-clar/core/hex.c
rename to tests/core/hex.c
diff --git a/tests/core/iconv.c b/tests/core/iconv.c
new file mode 100644
index 0000000..8aedab2
--- /dev/null
+++ b/tests/core/iconv.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+#ifdef GIT_USE_ICONV
+static git_path_iconv_t ic;
+static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+#endif
+
+void test_core_iconv__initialize(void)
+{
+#ifdef GIT_USE_ICONV
+	cl_git_pass(git_path_iconv_init_precompose(&ic));
+#endif
+}
+
+void test_core_iconv__cleanup(void)
+{
+#ifdef GIT_USE_ICONV
+	git_path_iconv_clear(&ic);
+#endif
+}
+
+void test_core_iconv__unchanged(void)
+{
+#ifdef GIT_USE_ICONV
+	char *data = "Ascii data", *original = data;
+	size_t datalen = strlen(data);
+
+	cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+	GIT_UNUSED(datalen);
+
+	/* There are no high bits set, so this should leave data untouched */
+	cl_assert(data == original);
+#endif
+}
+
+void test_core_iconv__decomposed_to_precomposed(void)
+{
+#ifdef GIT_USE_ICONV
+	char *data = nfd;
+	size_t datalen = strlen(nfd);
+
+	cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+	GIT_UNUSED(datalen);
+
+	/* The decomposed nfd string should be transformed to the nfc form
+	 * (on platforms where iconv is enabled, of course).
+	 */
+	cl_assert_equal_s(nfc, data);
+#endif
+}
+
+void test_core_iconv__precomposed_is_unmodified(void)
+{
+#ifdef GIT_USE_ICONV
+	char *data = nfc;
+	size_t datalen = strlen(nfc);
+
+	cl_git_pass(git_path_iconv(&ic, &data, &datalen));
+	GIT_UNUSED(datalen);
+
+	/* data is already in precomposed form, so even though some bytes have
+	 * the high-bit set, the iconv transform should result in no change.
+	 */
+	cl_assert_equal_s(nfc, data);
+#endif
+}
diff --git a/tests-clar/core/mkdir.c b/tests/core/mkdir.c
similarity index 92%
rename from tests-clar/core/mkdir.c
rename to tests/core/mkdir.c
index 1e50b43..a8c5b10 100644
--- a/tests-clar/core/mkdir.c
+++ b/tests/core/mkdir.c
@@ -111,14 +111,20 @@
 	git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
 }
 
-static void check_mode(mode_t expected, mode_t actual)
+#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)
+
+static void check_mode_at_line(
+	mode_t expected, mode_t actual, const char *file, int line)
 {
-#ifdef GIT_WIN32
-	/* chmod on Win32 doesn't support exec bit, not group/world bits */
-	cl_assert((expected & 0600) == (actual & 0777));
-#else
-	cl_assert(expected == (actual & 0777));
-#endif
+	/* FAT filesystems don't support exec bit, nor group/world bits */
+	if (!cl_is_chmod_supported()) {
+		expected &= 0600;
+		actual &= 0600;
+	}
+
+	clar__assert_equal(
+		file, line, "expected_mode != actual_mode", 1,
+		"%07o", (int)expected, (int)(actual & 0777));
 }
 
 void test_core_mkdir__chmods(void)
diff --git a/tests-clar/core/oid.c b/tests/core/oid.c
similarity index 100%
rename from tests-clar/core/oid.c
rename to tests/core/oid.c
diff --git a/tests-clar/core/oidmap.c b/tests/core/oidmap.c
similarity index 100%
rename from tests-clar/core/oidmap.c
rename to tests/core/oidmap.c
diff --git a/tests-clar/core/opts.c b/tests/core/opts.c
similarity index 100%
rename from tests-clar/core/opts.c
rename to tests/core/opts.c
diff --git a/tests-clar/core/path.c b/tests/core/path.c
similarity index 77%
rename from tests-clar/core/path.c
rename to tests/core/path.c
index 407770b..cf2d5e9 100644
--- a/tests-clar/core/path.c
+++ b/tests/core/path.c
@@ -1,5 +1,5 @@
 #include "clar_libgit2.h"
-#include <fileops.h>
+#include "fileops.h"
 
 static void
 check_dirname(const char *A, const char *B)
@@ -446,16 +446,15 @@
 	cl_git_pass(git_path_apply_relative(&p, "../../../../../.."));
 	cl_assert_equal_s("/this/", p.ptr);
 
-	cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
+	cl_git_pass(git_path_apply_relative(&p, "../"));
 	cl_assert_equal_s("/", p.ptr);
 
-	cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
-	cl_assert_equal_s("/", p.ptr);
+	cl_git_fail(git_path_apply_relative(&p, "../../.."));
 
 
 	cl_git_pass(git_buf_sets(&p, "d:/another/test"));
 
-	cl_git_pass(git_path_apply_relative(&p, "../../../../.."));
+	cl_git_pass(git_path_apply_relative(&p, "../.."));
 	cl_assert_equal_s("d:/", p.ptr);
 
 	cl_git_pass(git_path_apply_relative(&p, "from/here/to/../and/./back/."));
@@ -473,8 +472,112 @@
 	cl_git_pass(git_path_apply_relative(&p, ".."));
 	cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
 
-	cl_git_pass(git_path_apply_relative(&p, "../../../../../"));
+	cl_git_pass(git_path_apply_relative(&p, "../../../"));
 	cl_assert_equal_s("https://", p.ptr);
 
+
+	cl_git_pass(git_buf_sets(&p, "../../this/is/relative"));
+
+	cl_git_pass(git_path_apply_relative(&p, "../../preserves/the/prefix"));
+	cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+	cl_git_pass(git_path_apply_relative(&p, "../../../../that"));
+	cl_assert_equal_s("../../that", p.ptr);
+
+	cl_git_pass(git_path_apply_relative(&p, "../there"));
+	cl_assert_equal_s("../../there", p.ptr);
 	git_buf_free(&p);
 }
+
+static void assert_resolve_relative(
+	git_buf *buf, const char *expected, const char *path)
+{
+	cl_git_pass(git_buf_sets(buf, path));
+	cl_git_pass(git_path_resolve_relative(buf, 0));
+	cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_core_path__15_resolve_relative(void)
+{
+	git_buf buf = GIT_BUF_INIT;
+
+	assert_resolve_relative(&buf, "", "");
+	assert_resolve_relative(&buf, "", ".");
+	assert_resolve_relative(&buf, "", "./");
+	assert_resolve_relative(&buf, "..", "..");
+	assert_resolve_relative(&buf, "../", "../");
+	assert_resolve_relative(&buf, "..", "./..");
+	assert_resolve_relative(&buf, "../", "./../");
+	assert_resolve_relative(&buf, "../", "../.");
+	assert_resolve_relative(&buf, "../", ".././");
+	assert_resolve_relative(&buf, "../..", "../..");
+	assert_resolve_relative(&buf, "../../", "../../");
+
+	assert_resolve_relative(&buf, "/", "/");
+	assert_resolve_relative(&buf, "/", "/.");
+
+	assert_resolve_relative(&buf, "", "a/..");
+	assert_resolve_relative(&buf, "", "a/../");
+	assert_resolve_relative(&buf, "", "a/../.");
+
+	assert_resolve_relative(&buf, "/a", "/a");
+	assert_resolve_relative(&buf, "/a/", "/a/.");
+	assert_resolve_relative(&buf, "/", "/a/../");
+	assert_resolve_relative(&buf, "/", "/a/../.");
+	assert_resolve_relative(&buf, "/", "/a/.././");
+
+	assert_resolve_relative(&buf, "a", "a");
+	assert_resolve_relative(&buf, "a/", "a/");
+	assert_resolve_relative(&buf, "a/", "a/.");
+	assert_resolve_relative(&buf, "a/", "a/./");
+
+	assert_resolve_relative(&buf, "a/b", "a//b");
+	assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+	assert_resolve_relative(&buf, "b/c", "./b/c");
+	assert_resolve_relative(&buf, "a/c", "a/./c");
+	assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+	assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+	assert_resolve_relative(&buf, "/", "////");
+	assert_resolve_relative(&buf, "/a", "///a");
+	assert_resolve_relative(&buf, "/", "///.");
+	assert_resolve_relative(&buf, "/", "///a/..");
+
+	assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+	assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+	cl_git_pass(git_buf_sets(&buf, "/.."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	cl_git_pass(git_buf_sets(&buf, "/./.."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	cl_git_pass(git_buf_sets(&buf, "/.//.."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	cl_git_pass(git_buf_sets(&buf, "/../."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	cl_git_pass(git_buf_sets(&buf, "/../.././../a"));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	cl_git_pass(git_buf_sets(&buf, "////.."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+
+	/* things that start with Windows network paths */
+#ifdef GIT_WIN32
+	assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
+	assert_resolve_relative(&buf, "//a/", "//a/b/..");
+	assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
+
+	cl_git_pass(git_buf_sets(&buf, "//a/b/../.."));
+	cl_git_fail(git_path_resolve_relative(&buf, 0));
+#else
+	assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+	assert_resolve_relative(&buf, "/a/", "//a/b/..");
+	assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
+	assert_resolve_relative(&buf, "/", "//a/b/../..");
+#endif
+
+	git_buf_free(&buf);
+}
diff --git a/tests-clar/core/pool.c b/tests/core/pool.c
similarity index 100%
rename from tests-clar/core/pool.c
rename to tests/core/pool.c
diff --git a/tests/core/posix.c b/tests/core/posix.c
new file mode 100644
index 0000000..1cef937
--- /dev/null
+++ b/tests/core/posix.c
@@ -0,0 +1,99 @@
+#ifndef _WIN32
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#else
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+#  pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_core_posix__initialize(void)
+{
+#ifdef GIT_WIN32
+	/* on win32, the WSA context needs to be initialized
+	 * before any socket calls can be performed */
+	WSADATA wsd;
+
+	cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd));
+	cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2);
+#endif
+}
+
+static bool supports_ipv6(void)
+{
+#ifdef GIT_WIN32
+	/* IPv6 is supported on Vista and newer */
+	return git_has_win32_version(6, 0, 0);
+#else
+	return 1;
+#endif
+}
+
+void test_core_posix__inet_pton(void)
+{
+	struct in_addr addr;
+	struct in6_addr addr6;
+	size_t i;
+	
+	struct in_addr_data {
+		const char *p;
+		const uint8_t n[4];
+	};
+
+	struct in6_addr_data {
+		const char *p;
+		const uint8_t n[16];
+	};
+
+	static struct in_addr_data in_addr_data[] = {
+		{ "0.0.0.0", { 0, 0, 0, 0 } },
+		{ "10.42.101.8", { 10, 42, 101, 8 } },
+		{ "127.0.0.1", { 127, 0, 0, 1 } },
+		{ "140.177.10.12", { 140, 177, 10, 12 } },
+		{ "204.232.175.90", { 204, 232, 175, 90 } },
+		{ "255.255.255.255", { 255, 255, 255, 255 } },
+	};
+
+	static struct in6_addr_data in6_addr_data[] = {
+		{ "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+		{ "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+		{ "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+		{ "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } },
+		{ "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } },
+		{ "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } },
+	};
+
+	/* Test some ipv4 addresses */
+	for (i = 0; i < 6; i++) {
+		cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1);
+		cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0);
+	}
+
+	/* Test some ipv6 addresses */
+	if (supports_ipv6())
+	{
+		for (i = 0; i < 6; i++) {
+			cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1);
+			cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0);
+		}
+	}
+
+	/* Test some invalid strings */
+	cl_assert(p_inet_pton(AF_INET, "", &addr) == 0);
+	cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0);
+	cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0);
+	cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0);
+	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_assert_equal_i(EAFNOSUPPORT, errno);
+}
diff --git a/tests-clar/core/rmdir.c b/tests/core/rmdir.c
similarity index 100%
rename from tests-clar/core/rmdir.c
rename to tests/core/rmdir.c
diff --git a/tests/core/sortedcache.c b/tests/core/sortedcache.c
new file mode 100644
index 0000000..c1869be
--- /dev/null
+++ b/tests/core/sortedcache.c
@@ -0,0 +1,363 @@
+#include "clar_libgit2.h"
+#include "sortedcache.h"
+
+static int name_only_cmp(const void *a, const void *b)
+{
+	return strcmp(a, b);
+}
+
+void test_core_sortedcache__name_only(void)
+{
+	git_sortedcache *sc;
+	void *item;
+	size_t pos;
+
+	cl_git_pass(git_sortedcache_new(
+		&sc, 0, NULL, NULL, name_only_cmp, NULL));
+
+	cl_git_pass(git_sortedcache_wlock(sc));
+	cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa"));
+	cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb"));
+	cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz"));
+	cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm"));
+	cl_git_pass(git_sortedcache_upsert(&item, sc, "iii"));
+	git_sortedcache_wunlock(sc);
+
+	cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+	cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+	cl_assert_equal_s("aaa", item);
+	cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+	cl_assert_equal_s("mmm", item);
+	cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+	cl_assert_equal_s("zzz", item);
+	cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL);
+
+	cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+	cl_assert_equal_s("aaa", item);
+	cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+	cl_assert_equal_s("bbb", item);
+	cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+	cl_assert_equal_s("iii", item);
+	cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+	cl_assert_equal_s("mmm", item);
+	cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+	cl_assert_equal_s("zzz", item);
+	cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+	cl_assert_equal_sz(0, pos);
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii"));
+	cl_assert_equal_sz(2, pos);
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+	cl_assert_equal_sz(4, pos);
+	cl_assert_equal_i(
+		GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
+
+	git_sortedcache_clear(sc, true);
+
+	cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+	cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+	cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+	cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+	git_sortedcache_free(sc);
+}
+
+typedef struct {
+	int value;
+	char smaller_value;
+	char path[GIT_FLEX_ARRAY];
+} sortedcache_test_struct;
+
+static int sortedcache_test_struct_cmp(const void *a_, const void *b_)
+{
+	const sortedcache_test_struct *a = a_, *b = b_;
+	return strcmp(a->path, b->path);
+}
+
+static void sortedcache_test_struct_free(void *payload, void *item_)
+{
+	sortedcache_test_struct *item = item_;
+	int *count = payload;
+	(*count)++;
+	item->smaller_value = 0;
+}
+
+void test_core_sortedcache__in_memory(void)
+{
+	git_sortedcache *sc;
+	sortedcache_test_struct *item;
+	int free_count = 0;
+
+	cl_git_pass(git_sortedcache_new(
+		&sc, offsetof(sortedcache_test_struct, path),
+		sortedcache_test_struct_free, &free_count,
+		sortedcache_test_struct_cmp, NULL));
+
+	cl_git_pass(git_sortedcache_wlock(sc));
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa"));
+	item->value = 10;
+	item->smaller_value = 1;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb"));
+	item->value = 20;
+	item->smaller_value = 2;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz"));
+	item->value = 30;
+	item->smaller_value = 26;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm"));
+	item->value = 40;
+	item->smaller_value = 14;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii"));
+	item->value = 50;
+	item->smaller_value = 9;
+	git_sortedcache_wunlock(sc);
+
+	cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+	cl_git_pass(git_sortedcache_rlock(sc));
+
+	cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+	cl_assert_equal_s("aaa", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+	cl_assert_equal_s("mmm", item->path);
+	cl_assert_equal_i(40, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+	cl_assert_equal_s("zzz", item->path);
+	cl_assert_equal_i(30, item->value);
+	cl_assert(git_sortedcache_lookup(sc, "abc") == NULL);
+
+	/* not on Windows:
+	 * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one
+	 */
+
+	cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+	cl_assert_equal_s("aaa", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+	cl_assert_equal_s("bbb", item->path);
+	cl_assert_equal_i(20, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+	cl_assert_equal_s("iii", item->path);
+	cl_assert_equal_i(50, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+	cl_assert_equal_s("mmm", item->path);
+	cl_assert_equal_i(40, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+	cl_assert_equal_s("zzz", item->path);
+	cl_assert_equal_i(30, item->value);
+	cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+	git_sortedcache_runlock(sc);
+	/* git_sortedcache_runlock(sc); */
+
+	cl_assert_equal_i(0, free_count);
+
+	git_sortedcache_clear(sc, true);
+
+	cl_assert_equal_i(5, free_count);
+
+	cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+	cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+	cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+	cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+	free_count = 0;
+
+	cl_git_pass(git_sortedcache_wlock(sc));
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing"));
+	item->value = 10;
+	item->smaller_value = 3;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again"));
+	item->value = 20;
+	item->smaller_value = 1;
+	cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final"));
+	item->value = 30;
+	item->smaller_value = 2;
+	git_sortedcache_wunlock(sc);
+
+	cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+	cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL);
+	cl_assert_equal_s("testing", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL);
+	cl_assert_equal_s("again", item->path);
+	cl_assert_equal_i(20, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+	cl_assert_equal_s("final", item->path);
+	cl_assert_equal_i(30, item->value);
+	cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL);
+
+	cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+	cl_assert_equal_s("again", item->path);
+	cl_assert_equal_i(20, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+	cl_assert_equal_s("final", item->path);
+	cl_assert_equal_i(30, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+	cl_assert_equal_s("testing", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+	{
+		size_t pos;
+
+		cl_git_pass(git_sortedcache_wlock(sc));
+
+		cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again"));
+		cl_assert_equal_sz(0, pos);
+		cl_git_pass(git_sortedcache_remove(sc, pos));
+		cl_assert_equal_i(
+			GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again"));
+
+		cl_assert_equal_sz(2, git_sortedcache_entrycount(sc));
+
+		cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing"));
+		cl_assert_equal_sz(1, pos);
+		cl_git_pass(git_sortedcache_remove(sc, pos));
+		cl_assert_equal_i(
+			GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing"));
+
+		cl_assert_equal_sz(1, git_sortedcache_entrycount(sc));
+
+		cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+		cl_assert_equal_sz(0, pos);
+		cl_git_pass(git_sortedcache_remove(sc, pos));
+		cl_assert_equal_i(
+			GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final"));
+
+		cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+
+		git_sortedcache_wunlock(sc);
+	}
+
+	git_sortedcache_free(sc);
+
+	cl_assert_equal_i(3, free_count);
+}
+
+static void sortedcache_test_reload(git_sortedcache *sc)
+{
+	int count = 0;
+	git_buf buf = GIT_BUF_INIT;
+	char *scan, *after;
+	sortedcache_test_struct *item;
+
+	cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
+
+	git_sortedcache_clear(sc, false); /* clear once we already have lock */
+
+	for (scan = buf.ptr; *scan; scan = after + 1) {
+		int val = strtol(scan, &after, 0);
+		cl_assert(after > scan);
+		scan = after;
+
+		for (scan = after; git__isspace(*scan); ++scan) /* find start */;
+		for (after = scan; *after && *after != '\n'; ++after) /* find eol */;
+		*after = '\0';
+
+		cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan));
+
+		item->value = val;
+		item->smaller_value = (char)(count++);
+	}
+
+	git_sortedcache_wunlock(sc);
+
+	git_buf_free(&buf);
+}
+
+void test_core_sortedcache__on_disk(void)
+{
+	git_sortedcache *sc;
+	sortedcache_test_struct *item;
+	int free_count = 0;
+	size_t pos;
+
+	cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n");
+
+	cl_git_pass(git_sortedcache_new(
+		&sc, offsetof(sortedcache_test_struct, path),
+		sortedcache_test_struct_free, &free_count,
+		sortedcache_test_struct_cmp, "cacheitems.txt"));
+
+	/* should need to reload the first time */
+
+	sortedcache_test_reload(sc);
+
+	/* test what we loaded */
+
+	cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+	cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+	cl_assert_equal_s("abc", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL);
+	cl_assert_equal_s("cde", item->path);
+	cl_assert_equal_i(30, item->value);
+	cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+
+	cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+	cl_assert_equal_s("abc", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+	cl_assert_equal_s("bcd", item->path);
+	cl_assert_equal_i(20, item->value);
+	cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+	/* should not need to reload this time */
+
+	cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL));
+
+	/* rewrite ondisk file and reload */
+
+	cl_assert_equal_i(0, free_count);
+
+	cl_git_rewritefile(
+		"cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n");
+	sortedcache_test_reload(sc);
+
+	cl_assert_equal_i(3, free_count);
+
+	/* test what we loaded */
+
+	cl_assert_equal_sz(4, git_sortedcache_entrycount(sc));
+
+	cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+	cl_assert_equal_s("abc", item->path);
+	cl_assert_equal_i(100, item->value);
+	cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+	cl_assert_equal_s("final", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert(git_sortedcache_lookup(sc, "cde") == NULL);
+
+	cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+	cl_assert_equal_s("aaa", item->path);
+	cl_assert_equal_i(500, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+	cl_assert_equal_s("final", item->path);
+	cl_assert_equal_i(10, item->value);
+	cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+	cl_assert_equal_s("zzz", item->path);
+	cl_assert_equal_i(200, item->value);
+
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+	cl_assert_equal_sz(0, pos);
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc"));
+	cl_assert_equal_sz(1, pos);
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+	cl_assert_equal_sz(2, pos);
+	cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+	cl_assert_equal_sz(3, pos);
+	cl_assert_equal_i(
+		GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing"));
+	cl_assert_equal_i(
+		GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde"));
+
+	git_sortedcache_free(sc);
+
+	cl_assert_equal_i(7, free_count);
+}
+
diff --git a/tests-clar/core/stat.c b/tests/core/stat.c
similarity index 100%
rename from tests-clar/core/stat.c
rename to tests/core/stat.c
diff --git a/tests-clar/core/string.c b/tests/core/string.c
similarity index 100%
rename from tests-clar/core/string.c
rename to tests/core/string.c
diff --git a/tests-clar/core/strmap.c b/tests/core/strmap.c
similarity index 100%
rename from tests-clar/core/strmap.c
rename to tests/core/strmap.c
diff --git a/tests-clar/core/strtol.c b/tests/core/strtol.c
similarity index 100%
rename from tests-clar/core/strtol.c
rename to tests/core/strtol.c
diff --git a/tests-clar/core/vector.c b/tests/core/vector.c
similarity index 99%
rename from tests-clar/core/vector.c
rename to tests/core/vector.c
index c9e43a1..db52c00 100644
--- a/tests-clar/core/vector.c
+++ b/tests/core/vector.c
@@ -54,7 +54,7 @@
 	cl_git_pass(git_vector_insert(&x, ptrs[1]));
 	cl_assert(x.length == 5);
 
-	git_vector_uniq(&x);
+	git_vector_uniq(&x, NULL);
 	cl_assert(x.length == 2);
 
 	git_vector_free(&x);
diff --git a/tests-clar/date/date.c b/tests/date/date.c
similarity index 100%
rename from tests-clar/date/date.c
rename to tests/date/date.c
diff --git a/tests-clar/diff/blob.c b/tests/diff/blob.c
similarity index 83%
rename from tests-clar/diff/blob.c
rename to tests/diff/blob.c
index 42b9fcd..93f2071 100644
--- a/tests-clar/diff/blob.c
+++ b/tests/diff/blob.c
@@ -26,9 +26,8 @@
 
 	g_repo = cl_git_sandbox_init("attr");
 
-	GIT_INIT_STRUCTURE(&opts, GIT_DIFF_OPTIONS_VERSION);
+	cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
 	opts.context_lines = 1;
-	opts.interhunk_lines = 0;
 
 	memset(&expected, 0, sizeof(expected));
 
@@ -142,7 +141,7 @@
 {
 	git_blob *a, *b, *c;
 	git_oid a_oid, b_oid, c_oid;
-	git_diff_patch *p;
+	git_patch *p;
 	const git_diff_delta *delta;
 	size_t tc, ta, td;
 
@@ -161,11 +160,11 @@
 	/* Doing the equivalent of a `git diff -U1` on these files */
 
 	/* diff on tests/resources/attr/root_test1 */
-	cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
 	cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
@@ -173,22 +172,22 @@
 	cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid));
 	cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
 
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
 
-	cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
 	cl_assert_equal_i(1, (int)tc);
 	cl_assert_equal_i(5, (int)ta);
 	cl_assert_equal_i(0, (int)td);
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	/* diff on tests/resources/attr/root_test2 */
-	cl_git_pass(git_diff_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
 	cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid));
@@ -196,22 +195,22 @@
 	cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
 	cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
 
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(15, git_patch_num_lines_in_hunk(p, 0));
 
-	cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
 	cl_assert_equal_i(3, (int)tc);
 	cl_assert_equal_i(9, (int)ta);
 	cl_assert_equal_i(3, (int)td);
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	/* diff on tests/resources/attr/root_test3 */
-	cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
 	cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid));
@@ -219,19 +218,19 @@
 	cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
 	cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
 
-	cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
 	cl_assert_equal_i(0, (int)tc);
 	cl_assert_equal_i(12, (int)ta);
 	cl_assert_equal_i(1, (int)td);
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	/* one more */
-	cl_git_pass(git_diff_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
 	cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid));
@@ -239,16 +238,16 @@
 	cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
 	cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
 
-	cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0));
-	cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1));
+	cl_assert_equal_i(2, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(5, git_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(9, git_patch_num_lines_in_hunk(p, 1));
 
-	cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
 	cl_assert_equal_i(4, (int)tc);
 	cl_assert_equal_i(6, (int)ta);
 	cl_assert_equal_i(4, (int)td);
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	git_blob_free(a);
 	git_blob_free(b);
@@ -317,16 +316,16 @@
 void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
 {
 	git_blob *e = NULL;
-	git_diff_patch *p;
+	git_patch *p;
 	const git_diff_delta *delta;
-	int line;
-	char origin;
+	const git_diff_line *line;
+	int l, max_l;
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
 	cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
@@ -334,24 +333,24 @@
 	cl_assert(git_oid_iszero(&delta->new_file.oid));
 	cl_assert_equal_sz(0, delta->new_file.size);
 
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
 
-	for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
-		cl_git_pass(git_diff_patch_get_line_in_hunk(
-			&origin, NULL, NULL, NULL, NULL, p, 0, line));
-		cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
+	max_l = git_patch_num_lines_in_hunk(p, 0);
+	for (l = 0; l < max_l; ++l) {
+		cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+		cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
 	}
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	opts.flags |= GIT_DIFF_REVERSE;
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
 	cl_assert(git_oid_iszero(&delta->old_file.oid));
@@ -359,44 +358,44 @@
 	cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
 	cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
 
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
 
-	for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
-		cl_git_pass(git_diff_patch_get_line_in_hunk(
-			&origin, NULL, NULL, NULL, NULL, p, 0, line));
-		cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
+	max_l = git_patch_num_lines_in_hunk(p, 0);
+	for (l = 0; l < max_l; ++l) {
+		cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+		cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
 	}
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	opts.flags ^= GIT_DIFF_REVERSE;
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
 	cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
 
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
 
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
 	cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
 
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 }
 
 static void assert_identical_blobs_comparison(diff_expects *expected)
@@ -437,13 +436,13 @@
 
 void test_diff_blob__can_compare_identical_blobs_with_patch(void)
 {
-	git_diff_patch *p;
+	git_patch *p;
 	const git_diff_delta *delta;
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
 	cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
@@ -451,13 +450,13 @@
 	cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
 	cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
 
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
 	cl_assert(p != NULL);
 
-	delta = git_diff_patch_delta(p);
+	delta = git_patch_get_delta(p);
 	cl_assert(delta != NULL);
 	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
 	cl_assert_equal_sz(0, delta->old_file.size);
@@ -465,14 +464,14 @@
 	cl_assert_equal_sz(0, delta->new_file.size);
 	cl_assert(git_oid_iszero(&delta->new_file.oid));
 
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
+	cl_git_pass(git_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+	git_patch_free(p);
 }
 
 static void assert_binary_blobs_comparison(diff_expects *expected)
@@ -693,7 +692,7 @@
 
 void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
 {
-	git_diff_patch *p;
+	git_patch *p;
 	git_blob *a;
 	git_oid a_oid;
 	const char *a_content = "Hello from the root\n";
@@ -705,58 +704,58 @@
 	cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
 
 	/* diff from blob a to content of b */
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, a, NULL, b_content, strlen(b_content), NULL, &opts));
 
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
+	cl_assert_equal_i(GIT_DELTA_MODIFIED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
 
-	cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+	cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
 	cl_assert_equal_i(1, (int)tc);
 	cl_assert_equal_i(5, (int)ta);
 	cl_assert_equal_i(0, (int)td);
 
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	/* diff from blob a to content of a */
 	opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, a, NULL, a_content, strlen(a_content), NULL, &opts));
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+	git_patch_free(p);
 
 	/* diff from NULL blob to content of a */
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+	git_patch_free(p);
 
 	/* diff from blob a to NULL buffer */
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, a, NULL, NULL, 0, NULL, &opts));
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(GIT_DELTA_DELETED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+	git_patch_free(p);
 
 	/* diff with reverse */
 	opts.flags ^= GIT_DIFF_REVERSE;
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, a, NULL, NULL, 0, NULL, &opts));
 	cl_assert(p != NULL);
-	cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
-	cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
-	cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
-	git_diff_patch_free(p);
+	cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+	cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+	git_patch_free(p);
 
 	git_blob_free(a);
 }
@@ -853,7 +852,7 @@
 		"0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
 	size_t bin_len = 33;
 	const char *changed;
-	git_diff_patch *p;
+	git_patch *p;
 	char *pout;
 
 	/* set up custom diff drivers and 'diff' attribute mappings for them */
@@ -950,9 +949,9 @@
 	cl_assert_equal_i(3, expected.line_adds);
 	cl_assert_equal_i(0, expected.line_dels);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.normal b/zzz.normal\n"
 		"index 45141a7..75b0dbb 100644\n"
@@ -963,21 +962,21 @@
 		"+And more\n"
 		"+Go here\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.binary b/zzz.binary\n"
 		"index 45141a7..75b0dbb 100644\n"
 		"Binary files a/zzz.binary and b/zzz.binary differ\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.alphary b/zzz.alphary\n"
 		"index 45141a7..75b0dbb 100644\n"
@@ -988,11 +987,11 @@
 		"+And more\n"
 		"+Go here\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.numary b/zzz.numary\n"
 		"index 45141a7..75b0dbb 100644\n"
@@ -1003,7 +1002,7 @@
 		"+And more\n"
 		"+Go here\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	/* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"
 	 * 33 bytes
@@ -1011,19 +1010,19 @@
 
 	changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n";
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, bin, "zzz.normal", changed, 37, NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.normal b/zzz.normal\n"
 		"index b435cd5..1604519 100644\n"
 		"Binary files a/zzz.normal and b/zzz.normal differ\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, bin, "zzz.textary", changed, 37, NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.textary b/zzz.textary\n"
 		"index b435cd5..1604519 100644\n"
@@ -1033,11 +1032,11 @@
 		"-0123456789\n"
 		"+replace a line\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.textalphary b/zzz.textalphary\n"
 		"index b435cd5..1604519 100644\n"
@@ -1047,11 +1046,11 @@
 		"-0123456789\n"
 		"+replace a line\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
-	cl_git_pass(git_diff_patch_from_blob_and_buffer(
+	cl_git_pass(git_patch_from_blob_and_buffer(
 		&p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
-	cl_git_pass(git_diff_patch_to_str(&pout, p));
+	cl_git_pass(git_patch_to_str(&pout, p));
 	cl_assert_equal_s(
 		"diff --git a/zzz.textnumary b/zzz.textnumary\n"
 		"index b435cd5..1604519 100644\n"
@@ -1061,7 +1060,7 @@
 		"-0123456789\n"
 		"+replace a line\n", pout);
 	git__free(pout);
-	git_diff_patch_free(p);
+	git_patch_free(p);
 
 	git_blob_free(nonbin);
 	git_blob_free(bin);
diff --git a/tests/diff/diff_helpers.c b/tests/diff/diff_helpers.c
new file mode 100644
index 0000000..33bb561
--- /dev/null
+++ b/tests/diff/diff_helpers.c
@@ -0,0 +1,246 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+git_tree *resolve_commit_oid_to_tree(
+	git_repository *repo,
+	const char *partial_oid)
+{
+	size_t len = strlen(partial_oid);
+	git_oid oid;
+	git_object *obj = NULL;
+	git_tree *tree = NULL;
+
+	if (git_oid_fromstrn(&oid, partial_oid, len) == 0)
+		git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
+	cl_assert(obj);
+	if (git_object_type(obj) == GIT_OBJ_TREE)
+		return (git_tree *)obj;
+	cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT);
+	cl_git_pass(git_commit_tree(&tree, (git_commit *)obj));
+	git_object_free(obj);
+	return tree;
+}
+
+static char diff_pick_suffix(int mode)
+{
+	if (S_ISDIR(mode))
+		return '/';
+	else if (GIT_PERMS_IS_EXEC(mode))
+		return '*';
+	else
+		return ' ';
+}
+
+static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
+{
+	char code = git_diff_status_char(delta->status);
+	char old_suffix = diff_pick_suffix(delta->old_file.mode);
+	char new_suffix = diff_pick_suffix(delta->new_file.mode);
+
+	fprintf(fp, "%c\t%s", code, delta->old_file.path);
+
+	if ((delta->old_file.path != delta->new_file.path &&
+		 strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
+		(delta->old_file.mode != delta->new_file.mode &&
+		 delta->old_file.mode != 0 && delta->new_file.mode != 0))
+		fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
+	else if (old_suffix != ' ')
+		fprintf(fp, "%c", old_suffix);
+
+	fprintf(fp, "\t[%.2f]\n", progress);
+}
+
+int diff_file_cb(
+	const git_diff_delta *delta,
+	float progress,
+	void *payload)
+{
+	diff_expects *e = payload;
+
+	if (e->debug)
+		fprintf_delta(stderr, delta, progress);
+
+	if (e->names)
+		cl_assert_equal_s(e->names[e->files], delta->old_file.path);
+	if (e->statuses)
+		cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
+
+	e->files++;
+
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+		e->files_binary++;
+
+	cl_assert(delta->status <= GIT_DELTA_TYPECHANGE);
+
+	e->file_status[delta->status] += 1;
+
+	return 0;
+}
+
+int diff_print_file_cb(
+	const git_diff_delta *delta,
+	float progress,
+	void *payload)
+{
+	if (!payload) {
+		fprintf_delta(stderr, delta, progress);
+		return 0;
+	}
+
+	if (!((diff_expects *)payload)->debug)
+		fprintf_delta(stderr, delta, progress);
+
+	return diff_file_cb(delta, progress, payload);
+}
+
+int diff_hunk_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	void *payload)
+{
+	diff_expects *e = payload;
+	const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
+
+	GIT_UNUSED(delta);
+
+	/* confirm no NUL bytes in header text */
+	while (scan < scan_end)
+		cl_assert('\0' != *scan++);
+
+	e->hunks++;
+	e->hunk_old_lines += hunk->old_lines;
+	e->hunk_new_lines += hunk->new_lines;
+	return 0;
+}
+
+int diff_line_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
+	void *payload)
+{
+	diff_expects *e = payload;
+
+	GIT_UNUSED(delta);
+	GIT_UNUSED(hunk);
+
+	e->lines++;
+	switch (line->origin) {
+	case GIT_DIFF_LINE_CONTEXT:
+	case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
+		e->line_ctxt++;
+		break;
+	case GIT_DIFF_LINE_ADDITION:
+	case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
+		e->line_adds++;
+		break;
+	case GIT_DIFF_LINE_DELETION:
+	case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
+		e->line_dels++;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+int diff_foreach_via_iterator(
+	git_diff *diff,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_line_cb line_cb,
+	void *data)
+{
+	size_t d, num_d = git_diff_num_deltas(diff);
+
+	for (d = 0; d < num_d; ++d) {
+		git_patch *patch;
+		const git_diff_delta *delta;
+		size_t h, num_h;
+
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
+		cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+		/* call file_cb for this file */
+		if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
+			git_patch_free(patch);
+			goto abort;
+		}
+
+		/* if there are no changes, then the patch will be NULL */
+		if (!patch) {
+			cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
+					  (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+			continue;
+		}
+
+		if (!hunk_cb && !line_cb) {
+			git_patch_free(patch);
+			continue;
+		}
+
+		num_h = git_patch_num_hunks(patch);
+
+		for (h = 0; h < num_h; h++) {
+			const git_diff_hunk *hunk;
+			size_t l, num_l;
+
+			cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+			if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
+				git_patch_free(patch);
+				goto abort;
+			}
+
+			for (l = 0; l < num_l; ++l) {
+				const git_diff_line *line;
+
+				cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+
+				if (line_cb &&
+					line_cb(delta, hunk, line, data) != 0) {
+					git_patch_free(patch);
+					goto abort;
+				}
+			}
+		}
+
+		git_patch_free(patch);
+	}
+
+	return 0;
+
+abort:
+	giterr_clear();
+	return GIT_EUSER;
+}
+
+static int diff_print_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
+	void *payload)
+{
+	FILE *fp = payload;
+
+	GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+	if (line->origin == GIT_DIFF_LINE_CONTEXT ||
+		line->origin == GIT_DIFF_LINE_ADDITION ||
+		line->origin == GIT_DIFF_LINE_DELETION)
+		fputc(line->origin, fp);
+	fwrite(line->content, 1, line->content_len, fp);
+	return 0;
+}
+
+void diff_print(FILE *fp, git_diff *diff)
+{
+	cl_git_pass(git_diff_print(
+		diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, fp ? fp : stderr));
+}
+
+void diff_print_raw(FILE *fp, git_diff *diff)
+{
+	cl_git_pass(git_diff_print(
+		diff, GIT_DIFF_FORMAT_RAW, diff_print_cb, fp ? fp : stderr));
+}
diff --git a/tests-clar/diff/diff_helpers.h b/tests/diff/diff_helpers.h
similarity index 75%
rename from tests-clar/diff/diff_helpers.h
rename to tests/diff/diff_helpers.h
index bb76d00..bf21f4b 100644
--- a/tests-clar/diff/diff_helpers.h
+++ b/tests/diff/diff_helpers.h
@@ -44,25 +44,21 @@
 
 extern int diff_hunk_cb(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	const char *header,
-	size_t header_len,
+	const git_diff_hunk *hunk,
 	void *cb_data);
 
 extern int diff_line_cb(
 	const git_diff_delta *delta,
-	const git_diff_range *range,
-	char line_origin,
-	const char *content,
-	size_t content_len,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
 	void *cb_data);
 
 extern int diff_foreach_via_iterator(
-	git_diff_list *diff,
+	git_diff *diff,
 	git_diff_file_cb file_cb,
 	git_diff_hunk_cb hunk_cb,
-	git_diff_data_cb line_cb,
+	git_diff_line_cb line_cb,
 	void *data);
 
-extern void diff_print(FILE *fp, git_diff_list *diff);
-extern void diff_print_raw(FILE *fp, git_diff_list *diff);
+extern void diff_print(FILE *fp, git_diff *diff);
+extern void diff_print_raw(FILE *fp, git_diff *diff);
diff --git a/tests-clar/diff/diffiter.c b/tests/diff/diffiter.c
similarity index 75%
rename from tests-clar/diff/diffiter.c
rename to tests/diff/diffiter.c
index 932d720..f886e1b 100644
--- a/tests-clar/diff/diffiter.c
+++ b/tests/diff/diffiter.c
@@ -13,47 +13,48 @@
 void test_diff_diffiter__create(void)
 {
 	git_repository *repo = cl_git_sandbox_init("attr");
-	git_diff_list *diff;
+	git_diff *diff;
 	size_t d, num_d;
 
 	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
 
 	num_d = git_diff_num_deltas(diff);
 	for (d = 0; d < num_d; ++d) {
-		const git_diff_delta *delta;
-		cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
+		const git_diff_delta *delta = git_diff_get_delta(diff, d);
+		cl_assert(delta != NULL);
 	}
 
-	git_diff_list_free(diff);
+	cl_assert(!git_diff_get_delta(diff, num_d));
+
+	git_diff_free(diff);
 }
 
-void test_diff_diffiter__iterate_files(void)
+void test_diff_diffiter__iterate_files_1(void)
 {
 	git_repository *repo = cl_git_sandbox_init("attr");
-	git_diff_list *diff;
+	git_diff *diff;
 	size_t d, num_d;
-	int count = 0;
+	diff_expects exp = { 0 };
 
 	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
 
 	num_d = git_diff_num_deltas(diff);
-	cl_assert_equal_i(6, (int)num_d);
 
 	for (d = 0; d < num_d; ++d) {
-		const git_diff_delta *delta;
-		cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
+		const git_diff_delta *delta = git_diff_get_delta(diff, d);
 		cl_assert(delta != NULL);
-		count++;
-	}
-	cl_assert_equal_i(6, count);
 
-	git_diff_list_free(diff);
+		diff_file_cb(delta, (float)d / (float)num_d, &exp);
+	}
+	cl_assert_equal_sz(6, exp.files);
+
+	git_diff_free(diff);
 }
 
 void test_diff_diffiter__iterate_files_2(void)
 {
 	git_repository *repo = cl_git_sandbox_init("status");
-	git_diff_list *diff;
+	git_diff *diff;
 	size_t d, num_d;
 	int count = 0;
 
@@ -63,21 +64,20 @@
 	cl_assert_equal_i(8, (int)num_d);
 
 	for (d = 0; d < num_d; ++d) {
-		const git_diff_delta *delta;
-		cl_git_pass(git_diff_get_patch(NULL, &delta, diff, d));
+		const git_diff_delta *delta = git_diff_get_delta(diff, d);
 		cl_assert(delta != NULL);
 		count++;
 	}
 	cl_assert_equal_i(8, count);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_diffiter__iterate_files_and_hunks(void)
 {
 	git_repository *repo = cl_git_sandbox_init("status");
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	size_t d, num_d;
 	int file_count = 0, hunk_count = 0;
 
@@ -90,47 +90,39 @@
 	num_d = git_diff_num_deltas(diff);
 
 	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
-		const git_diff_delta *delta;
+		git_patch *patch;
 		size_t h, num_h;
 
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-
-		cl_assert(delta);
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
 		cl_assert(patch);
 
 		file_count++;
 
-		num_h = git_diff_patch_num_hunks(patch);
+		num_h = git_patch_num_hunks(patch);
 
 		for (h = 0; h < num_h; h++) {
-			const git_diff_range *range;
-			const char *header;
-			size_t header_len, num_l;
+			const git_diff_hunk *hunk;
 
-			cl_git_pass(git_diff_patch_get_hunk(
-				&range, &header, &header_len, &num_l, patch, h));
-
-			cl_assert(range);
-			cl_assert(header);
+			cl_git_pass(git_patch_get_hunk(&hunk, NULL, patch, h));
+			cl_assert(hunk);
 
 			hunk_count++;
 		}
 
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
 	cl_assert_equal_i(13, file_count);
 	cl_assert_equal_i(8, hunk_count);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_diffiter__max_size_threshold(void)
 {
 	git_repository *repo = cl_git_sandbox_init("status");
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	int file_count = 0, binary_count = 0, hunk_count = 0;
 	size_t d, num_d;
 
@@ -142,27 +134,28 @@
 	num_d = git_diff_num_deltas(diff);
 
 	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
+		git_patch *patch;
 		const git_diff_delta *delta;
 
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-		cl_assert(delta);
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
 		cl_assert(patch);
+		delta = git_patch_get_delta(patch);
+		cl_assert(delta);
 
 		file_count++;
-		hunk_count += (int)git_diff_patch_num_hunks(patch);
+		hunk_count += (int)git_patch_num_hunks(patch);
 
 		assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
 		binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
 
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
 	cl_assert_equal_i(13, file_count);
 	cl_assert_equal_i(0, binary_count);
 	cl_assert_equal_i(8, hunk_count);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* try again with low file size threshold */
 
@@ -177,18 +170,19 @@
 	num_d = git_diff_num_deltas(diff);
 
 	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
+		git_patch *patch;
 		const git_diff_delta *delta;
 
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
+		delta = git_patch_get_delta(patch);
 
 		file_count++;
-		hunk_count += (int)git_diff_patch_num_hunks(patch);
+		hunk_count += (int)git_patch_num_hunks(patch);
 
 		assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
 		binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
 
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
 	cl_assert_equal_i(13, file_count);
@@ -200,7 +194,7 @@
 	cl_assert_equal_i(3, binary_count);
 	cl_assert_equal_i(5, hunk_count);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 
@@ -208,7 +202,7 @@
 {
 	git_repository *repo = cl_git_sandbox_init("status");
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp = {0};
 	size_t d, num_d;
 
@@ -220,57 +214,51 @@
 
 	num_d = git_diff_num_deltas(diff);
 	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
-		const git_diff_delta *delta;
+		git_patch *patch;
 		size_t h, num_h;
 
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-		cl_assert(patch && delta);
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
+		cl_assert(patch);
 		exp.files++;
 
-		num_h = git_diff_patch_num_hunks(patch);
+		num_h = git_patch_num_hunks(patch);
 		for (h = 0; h < num_h; h++) {
-			const git_diff_range *range;
-			const char *header;
-			size_t header_len, l, num_l;
+			const git_diff_hunk *range;
+			size_t l, num_l;
 
-			cl_git_pass(git_diff_patch_get_hunk(
-				&range, &header, &header_len, &num_l, patch, h));
-			cl_assert(range && header);
+			cl_git_pass(git_patch_get_hunk(&range, &num_l, patch, h));
+			cl_assert(range);
 			exp.hunks++;
 
 			for (l = 0; l < num_l; ++l) {
-				char origin;
-				const char *content;
-				size_t content_len;
+				const git_diff_line *line;
 
-				cl_git_pass(git_diff_patch_get_line_in_hunk(
-					&origin, &content, &content_len, NULL, NULL, patch, h, l));
-				cl_assert(content);
+				cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+				cl_assert(line && line->content);
 				exp.lines++;
 			}
 		}
 
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
 	cl_assert_equal_i(13, exp.files);
 	cl_assert_equal_i(8, exp.hunks);
 	cl_assert_equal_i(14, exp.lines);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
-static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp)
+static void iterate_over_patch(git_patch *patch, diff_expects *exp)
 {
-	size_t h, num_h = git_diff_patch_num_hunks(patch), num_l;
+	size_t h, num_h = git_patch_num_hunks(patch), num_l;
 
 	exp->files++;
 	exp->hunks += (int)num_h;
 
 	/* let's iterate in reverse, just because we can! */
 	for (h = 1, num_l = 0; h <= num_h; ++h)
-		num_l += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
+		num_l += git_patch_num_lines_in_hunk(patch, num_h - h);
 
 	exp->lines += (int)num_l;
 }
@@ -281,9 +269,9 @@
 {
 	git_repository *repo = cl_git_sandbox_init("status");
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp = {0};
-	git_diff_patch *patches[PATCH_CACHE];
+	git_patch *patches[PATCH_CACHE];
 	size_t p, d, num_d;
 
 	memset(patches, 0, sizeof(patches));
@@ -308,32 +296,32 @@
 
 	for (d = 0; d < num_d; ++d) {
 		/* take old patch */
-		git_diff_patch *patch = patches[p];
+		git_patch *patch = patches[p];
 		patches[p] = NULL;
 
 		/* cache new patch */
-		cl_git_pass(git_diff_get_patch(&patches[p], NULL, diff, d));
+		cl_git_pass(git_patch_from_diff(&patches[p], diff, d));
 		cl_assert(patches[p] != NULL);
 
 		/* process old patch if non-NULL */
 		if (patch != NULL) {
 			iterate_over_patch(patch, &exp);
-			git_diff_patch_free(patch);
+			git_patch_free(patch);
 		}
 
 		p = rand() % PATCH_CACHE;
 	}
 
 	/* free diff list now - refcounts should keep things safe */
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* process remaining unprocessed patches */
 	for (p = 0; p < PATCH_CACHE; p++) {
-		git_diff_patch *patch = patches[p];
+		git_patch *patch = patches[p];
 
 		if (patch != NULL) {
 			iterate_over_patch(patch, &exp);
-			git_diff_patch_free(patch);
+			git_patch_free(patch);
 		}
 	}
 
@@ -416,7 +404,7 @@
 void test_diff_diffiter__iterate_and_generate_patch_text(void)
 {
 	git_repository *repo = cl_git_sandbox_init("status");
-	git_diff_list *diff;
+	git_diff *diff;
 	size_t d, num_d;
 
 	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
@@ -425,28 +413,28 @@
 	cl_assert_equal_i(8, (int)num_d);
 
 	for (d = 0; d < num_d; ++d) {
-		git_diff_patch *patch;
+		git_patch *patch;
 		char *text;
 
-		cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
 		cl_assert(patch != NULL);
 
-		cl_git_pass(git_diff_patch_to_str(&text, patch));
+		cl_git_pass(git_patch_to_str(&text, patch));
 
 		cl_assert_equal_s(expected_patch_text[d], text);
 
 		git__free(text);
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_diffiter__checks_options_version(void)
 {
 	git_repository *repo = cl_git_sandbox_init("status");
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	const git_error *err;
 
 	opts.version = 0;
diff --git a/tests/diff/drivers.c b/tests/diff/drivers.c
new file mode 100644
index 0000000..fbd1dff
--- /dev/null
+++ b/tests/diff/drivers.c
@@ -0,0 +1,163 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "repository.h"
+#include "diff_driver.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_drivers__initialize(void)
+{
+}
+
+void test_diff_drivers__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+	g_repo = NULL;
+}
+
+void test_diff_drivers__patterns(void)
+{
+	git_config *cfg;
+	const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+	git_tree *one;
+	git_diff *diff;
+	git_patch *patch;
+	char *text;
+	const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n   dreamed--too soon--it had sounded.\r\n \r\n                 -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+	const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
+	const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n   dreamed--too soon--it had sounded.\r\n \r\n                 -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+
+	g_repo = cl_git_sandbox_init("renames");
+
+	one = resolve_commit_oid_to_tree(g_repo, one_sha);
+
+	/* no diff */
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
+	git_diff_free(diff);
+
+	/* default diff */
+
+	cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected0, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	/* attribute diff set to false */
+
+	cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected1, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	/* attribute diff set to unconfigured value (should use default) */
+
+	cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected0, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	/* let's define that driver */
+
+	cl_git_pass(git_repository_config(&cfg, g_repo));
+	cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
+	git_config_free(cfg);
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected1, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	/* let's use a real driver with some regular expressions */
+
+	git_diff_driver_registry_free(g_repo->diff_drivers);
+	g_repo->diff_drivers = NULL;
+
+	cl_git_pass(git_repository_config(&cfg, g_repo));
+	cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
+	cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H"));
+	git_config_free(cfg);
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected2, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	git_tree_free(one);
+}
+
+void test_diff_drivers__long_lines(void)
+{
+	const char *base = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nisi ligula. Ut viverra enim sed lobortis suscipit.\nPhasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissim risus. Suspendisse at nisi quis turpis fringilla rutrum id sit amet nulla.\nNam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\nMauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\nAliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n";
+	git_index *idx;
+	git_diff *diff;
+	git_patch *patch;
+	char *actual;
+	const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n";
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+	cl_git_mkfile("empty_standard_repo/longlines.txt", base);
+	cl_git_pass(git_repository_index(&idx, g_repo));
+	cl_git_pass(git_index_add_bypath(idx, "longlines.txt"));
+	cl_git_pass(git_index_write(idx));
+	git_index_free(idx);
+
+	cl_git_append2file("empty_standard_repo/longlines.txt", "newline\nnewline\n");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+	cl_assert_equal_sz(1, git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&actual, patch));
+
+	/* if chmod not supported, overwrite mode bits since anything is possible */
+	if (!cl_is_chmod_supported()) {
+		size_t actual_len = strlen(actual);
+		if (actual_len > 72 && memcmp(&actual[66], "100644", 6) != 0)
+			memcpy(&actual[66], "100644", 6);
+	}
+
+	cl_assert_equal_s(expected, actual);
+
+	free(actual);
+	git_patch_free(patch);
+	git_diff_free(diff);
+}
+
diff --git a/tests-clar/diff/index.c b/tests/diff/index.c
similarity index 95%
rename from tests-clar/diff/index.c
rename to tests/diff/index.c
index e1c617d..8f45671 100644
--- a/tests-clar/diff/index.c
+++ b/tests/diff/index.c
@@ -21,7 +21,7 @@
 	git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
 	git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
 	cl_assert(a);
@@ -56,7 +56,7 @@
 	cl_assert_equal_i(6, exp.line_adds);
 	cl_assert_equal_i(2, exp.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 	memset(&exp, 0, sizeof(exp));
 
@@ -84,7 +84,7 @@
 	cl_assert_equal_i(11, exp.line_adds);
 	cl_assert_equal_i(2, exp.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	git_tree_free(a);
@@ -114,7 +114,7 @@
 	git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
 	git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
 	cl_assert(a);
@@ -134,7 +134,7 @@
 
 	cl_assert_equal_i(2, exp.files);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	git_tree_free(a);
@@ -146,7 +146,7 @@
 	const char *a_commit = "26a125ee1bf";
 	git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	const git_error *err;
 
 	opts.version = 0;
diff --git a/tests-clar/diff/iterator.c b/tests/diff/iterator.c
similarity index 100%
rename from tests-clar/diff/iterator.c
rename to tests/diff/iterator.c
diff --git a/tests-clar/diff/notify.c b/tests/diff/notify.c
similarity index 95%
rename from tests-clar/diff/notify.c
rename to tests/diff/notify.c
index 433b4a9..cc33cb7 100644
--- a/tests-clar/diff/notify.c
+++ b/tests/diff/notify.c
@@ -13,7 +13,7 @@
 }
 
 static int assert_called_notifications(
-	const git_diff_list *diff_so_far,
+	const git_diff *diff_so_far,
 	const git_diff_delta *delta_to_add,
 	const char *matched_pathspec,
 	void *payload)
@@ -45,7 +45,7 @@
 	int expected_diffed_files_count)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
 	g_repo = cl_git_sandbox_init("status");
@@ -63,7 +63,7 @@
 
 	cl_assert_equal_i(expected_diffed_files_count, exp.files);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_notify__notify_single_pathspec(void)
@@ -155,7 +155,7 @@
 }
 
 static int abort_diff(
-	const git_diff_list *diff_so_far,
+	const git_diff *diff_so_far,
 	const git_diff_delta *delta_to_add,
 	const char *matched_pathspec,
 	void *payload)
@@ -171,7 +171,7 @@
 void test_diff_notify__notify_cb_can_abort_diff(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	char *pathspec = NULL;
 
 	g_repo = cl_git_sandbox_init("status");
@@ -189,7 +189,7 @@
 }
 
 static int filter_all(
-	const git_diff_list *diff_so_far,
+	const git_diff *diff_so_far,
 	const git_diff_delta *delta_to_add,
 	const char *matched_pathspec,
 	void *payload)
@@ -205,7 +205,7 @@
 void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	char *pathspec = NULL;
 	diff_expects exp;
 
@@ -224,5 +224,5 @@
 
 	cl_assert_equal_i(0, exp.files);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
diff --git a/tests/diff/patch.c b/tests/diff/patch.c
new file mode 100644
index 0000000..bd1598b
--- /dev/null
+++ b/tests/diff/patch.c
@@ -0,0 +1,577 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "diff_helpers.h"
+#include "repository.h"
+#include "buf_text.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_patch__initialize(void)
+{
+}
+
+void test_diff_patch__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
+	"deleted file mode 100644\n" \
+	"index e8ee89e..0000000\n" \
+	"--- a/subdir.txt\n" \
+	"+++ /dev/null\n"
+
+#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
+
+static int check_removal_cb(
+	const git_diff_delta *delta,
+	const git_diff_hunk *hunk,
+	const git_diff_line *line,
+	void *payload)
+{
+	GIT_UNUSED(payload);
+
+	switch (line->origin) {
+	case GIT_DIFF_LINE_FILE_HDR:
+		cl_assert_equal_s(EXPECTED_HEADER, line->content);
+		cl_assert(hunk == NULL);
+		goto check_delta;
+
+	case GIT_DIFF_LINE_HUNK_HDR:
+		cl_assert_equal_s(EXPECTED_HUNK, line->content);
+		/* Fall through */
+
+	case GIT_DIFF_LINE_CONTEXT:
+	case GIT_DIFF_LINE_DELETION:
+		goto check_hunk;
+
+	default:
+		/* unexpected code path */
+		return -1;
+	}
+
+check_hunk:
+	cl_assert(hunk != NULL);
+	cl_assert_equal_i(1, hunk->old_start);
+	cl_assert_equal_i(2, hunk->old_lines);
+	cl_assert_equal_i(0, hunk->new_start);
+	cl_assert_equal_i(0, hunk->new_lines);
+
+check_delta:
+	cl_assert_equal_s("subdir.txt", delta->old_file.path);
+	cl_assert_equal_s("subdir.txt", delta->new_file.path);
+	cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+
+	return 0;
+}
+
+void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
+{
+	/*
+	* $ git diff 26a125e..735b6a2
+	* diff --git a/subdir.txt b/subdir.txt
+	* deleted file mode 100644
+	* index e8ee89e..0000000
+	* --- a/subdir.txt
+	* +++ /dev/null
+	* @@ -1,2 +0,0 @@
+	* -Is it a bird?
+	* -Is it a plane?
+	*/
+
+	const char *one_sha = "26a125e";
+	const char *another_sha = "735b6a2";
+	git_tree *one, *another;
+	git_diff *diff;
+
+	g_repo = cl_git_sandbox_init("status");
+
+	one = resolve_commit_oid_to_tree(g_repo, one_sha);
+	another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+	cl_git_pass(git_diff_print(
+		diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, NULL));
+
+	git_diff_free(diff);
+
+	git_tree_free(another);
+	git_tree_free(one);
+}
+
+void test_diff_patch__to_string(void)
+{
+	const char *one_sha = "26a125e";
+	const char *another_sha = "735b6a2";
+	git_tree *one, *another;
+	git_diff *diff;
+	git_patch *patch;
+	char *text;
+	const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
+
+	g_repo = cl_git_sandbox_init("status");
+
+	one = resolve_commit_oid_to_tree(g_repo, one_sha);
+	another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+
+	cl_git_pass(git_patch_to_str(&text, patch));
+
+	cl_assert_equal_s(expected, text);
+
+	cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
+	cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
+	cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
+	cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+	git_tree_free(another);
+	git_tree_free(one);
+}
+
+void test_diff_patch__config_options(void)
+{
+	const char *one_sha = "26a125e"; /* current HEAD */
+	git_tree *one;
+	git_config *cfg;
+	git_diff *diff;
+	git_patch *patch;
+	char *text;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	char *onefile = "staged_changes_modified_file";
+	const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+	const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+	const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+	const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+
+	g_repo = cl_git_sandbox_init("status");
+	cl_git_pass(git_repository_config(&cfg, g_repo));
+	one = resolve_commit_oid_to_tree(g_repo, one_sha);
+	opts.pathspec.count = 1;
+	opts.pathspec.strings = &onefile;
+
+
+	cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
+
+	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected1, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected2, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+
+	cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected3, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+
+	cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
+
+	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&text, patch));
+	cl_assert_equal_s(expected4, text);
+
+	git__free(text);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	git_tree_free(one);
+	git_config_free(cfg);
+}
+
+void test_diff_patch__hunks_have_correct_line_numbers(void)
+{
+	git_config *cfg;
+	git_tree *head;
+	git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff;
+	git_patch *patch;
+	const git_diff_delta *delta;
+	const git_diff_hunk *hunk;
+	const git_diff_line *line;
+	size_t hunklen;
+	git_buf old_content = GIT_BUF_INIT, actual = GIT_BUF_INIT;
+	const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n  -- Rudyard Kipling\n";
+
+	g_repo = cl_git_sandbox_init("renames");
+
+	cl_git_pass(git_config_new(&cfg));
+	git_repository_set_config(g_repo, cfg);
+	git_config_free(cfg);
+
+	git_repository_reinit_filesystem(g_repo, false);
+
+	cl_git_pass(
+		git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
+
+	cl_git_rewritefile("renames/songof7cities.txt", new_content);
+
+	cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+	cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
+
+	/* check hunk 0 */
+
+	cl_git_pass(
+		git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+	cl_assert_equal_i(18, (int)hunklen);
+
+	cl_assert_equal_i(6, (int)hunk->old_start);
+	cl_assert_equal_i(15, (int)hunk->old_lines);
+	cl_assert_equal_i(6, (int)hunk->new_start);
+	cl_assert_equal_i(9, (int)hunk->new_lines);
+
+	cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 0));
+	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
+	cl_assert_equal_i(6, line->old_lineno);
+	cl_assert_equal_i(6, line->new_lineno);
+	cl_assert_equal_i(-1, line->content_offset);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
+	cl_assert_equal_i(9, line->old_lineno);
+	cl_assert_equal_i(-1, line->new_lineno);
+	cl_assert_equal_i(252, line->content_offset);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 12));
+	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("This is some new text;\n", actual.ptr);
+	cl_assert_equal_i(-1, line->old_lineno);
+	cl_assert_equal_i(9, line->new_lineno);
+	cl_assert_equal_i(252, line->content_offset);
+
+	/* check hunk 1 */
+
+	cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 1));
+
+	cl_assert_equal_i(18, (int)hunklen);
+
+	cl_assert_equal_i(31, (int)hunk->old_start);
+	cl_assert_equal_i(15, (int)hunk->old_lines);
+	cl_assert_equal_i(25, (int)hunk->new_start);
+	cl_assert_equal_i(9, (int)hunk->new_lines);
+
+	cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 0));
+	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
+	cl_assert_equal_i(31, line->old_lineno);
+	cl_assert_equal_i(25, line->new_lineno);
+	cl_assert_equal_i(-1, line->content_offset);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 3));
+	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
+	cl_assert_equal_i(34, line->old_lineno);
+	cl_assert_equal_i(-1, line->new_lineno);
+	cl_assert_equal_i(1468, line->content_offset);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 12));
+	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("Another replacement;\n", actual.ptr);
+	cl_assert_equal_i(-1, line->old_lineno);
+	cl_assert_equal_i(28, line->new_lineno);
+	cl_assert_equal_i(1066, line->content_offset);
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	/* Let's check line numbers when there is no newline */
+
+	git_buf_rtrim(&old_content);
+	cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+	cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
+
+	/* check hunk 0 */
+
+	cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+	cl_assert_equal_i(6, (int)hunklen);
+
+	cl_assert_equal_i(46, (int)hunk->old_start);
+	cl_assert_equal_i(4, (int)hunk->old_lines);
+	cl_assert_equal_i(46, (int)hunk->new_start);
+	cl_assert_equal_i(4, (int)hunk->new_lines);
+
+	cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 1));
+	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
+	cl_assert_equal_i(47, line->old_lineno);
+	cl_assert_equal_i(47, line->new_lineno);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 2));
+	cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("\n", actual.ptr);
+	cl_assert_equal_i(48, line->old_lineno);
+	cl_assert_equal_i(48, line->new_lineno);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+	cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("  -- Rudyard Kipling\n", actual.ptr);
+	cl_assert_equal_i(49, line->old_lineno);
+	cl_assert_equal_i(-1, line->new_lineno);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
+	cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("  -- Rudyard Kipling", actual.ptr);
+	cl_assert_equal_i(-1, line->old_lineno);
+	cl_assert_equal_i(49, line->new_lineno);
+
+	cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 5));
+	cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)line->origin);
+	cl_git_pass(git_buf_set(&actual, line->content, line->content_len));
+	cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
+	cl_assert_equal_i(-1, line->old_lineno);
+	cl_assert_equal_i(49, line->new_lineno);
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	git_buf_free(&actual);
+	git_buf_free(&old_content);
+	git_tree_free(head);
+}
+
+static void check_single_patch_stats(
+	git_repository *repo, size_t hunks,
+	size_t adds, size_t dels, size_t ctxt, size_t *sizes,
+	const char *expected)
+{
+	git_diff *diff;
+	git_patch *patch;
+	const git_diff_delta *delta;
+	size_t actual_ctxt, actual_adds, actual_dels;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+	cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+	cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
+
+	cl_git_pass( git_patch_line_stats(
+		&actual_ctxt, &actual_adds, &actual_dels, patch) );
+
+	cl_assert_equal_sz(ctxt, actual_ctxt);
+	cl_assert_equal_sz(adds, actual_adds);
+	cl_assert_equal_sz(dels, actual_dels);
+
+	if (expected != NULL) {
+		char *text;
+		cl_git_pass(git_patch_to_str(&text, patch));
+		cl_assert_equal_s(expected, text);
+		git__free(text);
+
+		cl_assert_equal_sz(
+			strlen(expected), git_patch_size(patch, 1, 1, 1));
+	}
+
+	if (sizes) {
+		if (sizes[0])
+			cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
+		if (sizes[1])
+			cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
+		if (sizes[2])
+			cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
+	}
+
+	/* walk lines in hunk with basic sanity checks */
+	for (; hunks > 0; --hunks) {
+		size_t i, max_i;
+		const git_diff_line *line;
+		int last_new_lineno = -1, last_old_lineno = -1;
+
+		max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
+
+		for (i = 0; i < max_i; ++i) {
+			int expected = 1;
+
+			cl_git_pass(
+				git_patch_get_line_in_hunk(&line, patch, hunks - 1, i));
+
+			if (line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
+				line->origin == GIT_DIFF_LINE_DEL_EOFNL ||
+				line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+				expected = 0;
+
+			if (line->old_lineno >= 0) {
+				if (last_old_lineno >= 0)
+					cl_assert_equal_i(
+						expected, line->old_lineno - last_old_lineno);
+				last_old_lineno = line->old_lineno;
+			}
+
+			if (line->new_lineno >= 0) {
+				if (last_new_lineno >= 0)
+					cl_assert_equal_i(
+						expected, line->new_lineno - last_new_lineno);
+				last_new_lineno = line->new_lineno;
+			}
+		}
+	}
+
+	git_patch_free(patch);
+	git_diff_free(diff);
+}
+
+void test_diff_patch__line_counts_with_eofnl(void)
+{
+	git_config *cfg;
+	git_buf content = GIT_BUF_INIT;
+	const char *end;
+	git_index *index;
+	const char *expected =
+		/* below is pasted output of 'git diff' with fn context removed */
+		"diff --git a/songof7cities.txt b/songof7cities.txt\n"
+		"index 378a7d9..3d0154e 100644\n"
+		"--- a/songof7cities.txt\n"
+		"+++ b/songof7cities.txt\n"
+		"@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
+		" \n"
+		" To the sound of trumpets shall their seed restore my Cities\n"
+		" Wealthy and well-weaponed, that once more may I behold\n"
+		"-All the world go softly when it walks before my Cities,\n"
+		"+#All the world go softly when it walks before my Cities,\n"
+		" And the horses and the chariots fleeing from them as of old!\n"
+		" \n"
+		"   -- Rudyard Kipling\n"
+		"\\ No newline at end of file\n";
+	size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
+
+	g_repo = cl_git_sandbox_init("renames");
+
+	cl_git_pass(git_config_new(&cfg));
+	git_repository_set_config(g_repo, cfg);
+	git_config_free(cfg);
+
+	git_repository_reinit_filesystem(g_repo, false);
+
+	cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
+
+	/* remove first line */
+
+	end = git_buf_cstr(&content) + git_buf_find(&content, '\n') + 1;
+	git_buf_consume(&content, end);
+	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+	check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL);
+
+	/* remove trailing whitespace */
+
+	git_buf_rtrim(&content);
+	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+	check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL);
+
+	/* add trailing whitespace */
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+	cl_git_pass(git_index_write(index));
+	git_index_free(index);
+
+	cl_git_pass(git_buf_putc(&content, '\n'));
+	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+	check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL);
+
+	/* no trailing whitespace as context line */
+
+	{
+		/* walk back a couple lines, make space and insert char */
+		char *scan = content.ptr + content.size;
+		int i;
+
+		for (i = 0; i < 5; ++i) {
+			for (--scan; scan > content.ptr && *scan != '\n'; --scan)
+				/* seek to prev \n */;
+		}
+		cl_assert(scan > content.ptr);
+
+		/* overwrite trailing \n with right-shifted content */
+		memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
+		/* insert '#' char into space we created */
+		scan[1] = '#';
+	}
+	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+	check_single_patch_stats(
+		g_repo, 1, 1, 1, 6, expected_sizes, expected);
+
+	git_buf_free(&content);
+}
diff --git a/tests/diff/pathspec.c b/tests/diff/pathspec.c
new file mode 100644
index 0000000..5761d2d
--- /dev/null
+++ b/tests/diff/pathspec.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_pathspec__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_pathspec__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_diff_pathspec__0(void)
+{
+	const char *a_commit = "26a125ee"; /* the current HEAD */
+	const char *b_commit = "0017bd4a"; /* the start */
+	git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+	git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_strarray paths = { NULL, 1 };
+	char *path;
+	git_pathspec *ps;
+	git_pathspec_match_list *matches;
+
+	cl_assert(a);
+	cl_assert(b);
+
+	path = "*_file";
+	paths.strings = &path;
+	cl_git_pass(git_pathspec_new(&ps, &paths));
+
+	cl_git_pass(git_pathspec_match_tree(&matches, a, GIT_PATHSPEC_DEFAULT, ps));
+	cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+	cl_assert_equal_s("current_file", git_pathspec_match_list_entry(matches,0));
+	cl_assert(git_pathspec_match_list_diff_entry(matches,0) == NULL);
+	git_pathspec_match_list_free(matches);
+
+	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, NULL, a, &opts));
+
+	cl_git_pass(git_pathspec_match_diff(
+		&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+	cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+	cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+	cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+	cl_assert_equal_s("current_file",
+		git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+	cl_assert_equal_i(GIT_DELTA_ADDED,
+		(int)git_pathspec_match_list_diff_entry(matches,0)->status);
+	git_pathspec_match_list_free(matches);
+
+	git_diff_free(diff);
+	diff = NULL;
+
+	cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+	cl_git_pass(git_pathspec_match_diff(
+		&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+	cl_assert_equal_i(3, (int)git_pathspec_match_list_entrycount(matches));
+	cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+	cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+	cl_assert_equal_s("subdir/current_file",
+		git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+	cl_assert_equal_i(GIT_DELTA_DELETED,
+		(int)git_pathspec_match_list_diff_entry(matches,0)->status);
+	git_pathspec_match_list_free(matches);
+
+	git_diff_free(diff);
+	diff = NULL;
+
+	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+	cl_git_pass(git_pathspec_match_diff(
+		&matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+	cl_assert_equal_i(4, (int)git_pathspec_match_list_entrycount(matches));
+	cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+	cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+	cl_assert_equal_s("modified_file",
+		git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+	cl_assert_equal_i(GIT_DELTA_MODIFIED,
+		(int)git_pathspec_match_list_diff_entry(matches,0)->status);
+	git_pathspec_match_list_free(matches);
+
+	git_diff_free(diff);
+	diff = NULL;
+
+	git_tree_free(a);
+	git_tree_free(b);
+	git_pathspec_free(ps);
+}
diff --git a/tests-clar/diff/rename.c b/tests/diff/rename.c
similarity index 83%
rename from tests-clar/diff/rename.c
rename to tests/diff/rename.c
index 2b1873b..42bb65a 100644
--- a/tests-clar/diff/rename.c
+++ b/tests/diff/rename.c
@@ -7,6 +7,8 @@
 void test_diff_rename__initialize(void)
 {
 	g_repo = cl_git_sandbox_init("renames");
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", false);
 }
 
 void test_diff_rename__cleanup(void)
@@ -41,7 +43,7 @@
 	const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
 	const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
 	git_tree *old_tree, *new_tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -86,7 +88,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	cl_git_pass(git_diff_tree_to_tree(
 		&diff, g_repo, old_tree, new_tree, &diffopts));
@@ -107,7 +109,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	git_tree_free(old_tree);
 	git_tree_free(new_tree);
@@ -118,7 +120,7 @@
 	const char *old_sha = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2";
 	const char *new_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
 	git_tree *old_tree, *new_tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	const git_error *err;
@@ -140,7 +142,7 @@
 	err = giterr_last();
 	cl_assert_equal_i(GITERR_INVALID, err->klass);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(old_tree);
 	git_tree_free(new_tree);
 }
@@ -151,7 +153,7 @@
 	const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
 	const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
 	git_tree *old_tree, *new_tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -205,7 +207,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* git diff -M -C \
 	 *          2bc7f351d20b53f1c72c16c4b036e491c478c49a \
@@ -226,7 +228,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* git diff -M -C --find-copies-harder --break-rewrites \
 	 *          2bc7f351d20b53f1c72c16c4b036e491c478c49a \
@@ -236,6 +238,8 @@
 		&diff, g_repo, old_tree, new_tree, &diffopts));
 
 	opts.flags = GIT_DIFF_FIND_ALL;
+	opts.break_rewrite_threshold = 70;
+
 	cl_git_pass(git_diff_find_similar(diff, &opts));
 
 	memset(&exp, 0, sizeof(exp));
@@ -249,7 +253,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* == Changes =====================================================
 	 * songofseven.txt -> untimely.txt    (rename, convert to crlf)
@@ -277,7 +281,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* git diff -M -C \
 	 *          1c068dee5790ef1580cfc4cd670915b48d790084 \
@@ -297,7 +301,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* git diff -M -C --find-copies-harder --break-rewrites \
 	 *          1c068dee5790ef1580cfc4cd670915b48d790084 \
@@ -312,8 +316,8 @@
 
 	/* the default match algorithm is going to find the internal
 	 * whitespace differences in the lines of sixserving.txt to be
-	 * significant enough that this will decide to split it into
-	 * an ADD and a DELETE
+	 * significant enough that this will decide to split it into an ADD
+	 * and a DELETE
 	 */
 
 	memset(&exp, 0, sizeof(exp));
@@ -326,7 +330,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* git diff -M -C --find-copies-harder --break-rewrites \
 	 *          1c068dee5790ef1580cfc4cd670915b48d790084 \
@@ -349,7 +353,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	git_tree_free(old_tree);
 	git_tree_free(new_tree);
@@ -360,7 +364,7 @@
 	const char *tree_sha = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 
@@ -384,7 +388,7 @@
 		GIT_DIFF_FIND_AND_BREAK_REWRITES;
 	cl_git_pass(git_diff_find_similar(diff, &opts));
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 }
@@ -396,7 +400,7 @@
 	git_oid id;
 	git_tree *tree;
 	git_blob *blob;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -447,7 +451,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* rewrite files in the working directory with / without CRLF changes */
 
@@ -473,13 +477,14 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* try a different whitespace option */
 
 	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
 
 	opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE;
+	opts.rename_threshold = 70;
 	cl_git_pass(git_diff_find_similar(diff, &opts));
 
 	memset(&exp, 0, sizeof(exp));
@@ -491,7 +496,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* try a different matching option */
 
@@ -509,14 +514,14 @@
 	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* again with exact match blob */
 
 	cl_git_pass(git_oid_fromstr(&id, blobsha));
 	cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
 	cl_git_pass(git_buf_set(
-		&content, git_blob_rawcontent(blob), git_blob_rawsize(blob)));
+		&content, git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob)));
 	cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
 	git_blob_free(blob);
 
@@ -540,7 +545,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	git_tree_free(tree);
 	git_buf_free(&content);
@@ -552,10 +557,10 @@
 	const char *sha0 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a";
 	const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084";
 	git_tree *old_tree, *new_tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
-	git_diff_patch *patch;
+	git_patch *patch;
 	const git_diff_delta *delta;
 	char *text;
 	const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n  (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n-                -- Rudyard Kipling\n+  -- Rudyard Kipling\n";
@@ -579,25 +584,26 @@
 
 	cl_assert_equal_i(4, (int)git_diff_num_deltas(diff));
 
-	cl_git_pass(git_diff_get_patch(&patch, &delta, diff, 0));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_assert((delta = git_patch_get_delta(patch)) != NULL);
 	cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status);
 
-	cl_git_pass(git_diff_patch_to_str(&text, patch));
+	cl_git_pass(git_patch_to_str(&text, patch));
 	cl_assert_equal_s(expected, text);
 	git__free(text);
 
-	git_diff_patch_free(patch);
+	git_patch_free(patch);
 
-	cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 1));
+	cl_assert((delta = git_diff_get_delta(diff, 1)) != NULL);
 	cl_assert_equal_i(GIT_DELTA_UNMODIFIED, (int)delta->status);
 
-	cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 2));
+	cl_assert((delta = git_diff_get_delta(diff, 2)) != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
 
-	cl_git_pass(git_diff_get_patch(NULL, &delta, diff, 3));
+	cl_assert((delta = git_diff_get_delta(diff, 3)) != NULL);
 	cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(old_tree);
 	git_tree_free(new_tree);
 }
@@ -607,7 +613,7 @@
 	git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -642,7 +648,7 @@
 	cl_assert_equal_i(2, exp.files);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 
@@ -655,7 +661,7 @@
 	git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT, c3 = GIT_BUF_INIT;
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -694,7 +700,7 @@
 	cl_assert_equal_i(3, exp.files);
 	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 
@@ -708,7 +714,7 @@
 	git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -747,7 +753,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 
@@ -760,7 +766,7 @@
 	git_buf c1 = GIT_BUF_INIT, c2 = GIT_BUF_INIT;
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -804,7 +810,7 @@
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_COPIED]);
 	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 
@@ -817,7 +823,7 @@
 	git_buf c1 = GIT_BUF_INIT;
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 	diff_expects exp;
@@ -858,7 +864,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 
@@ -868,6 +874,8 @@
 struct rename_expected
 {
 	size_t len;
+
+	unsigned int *status;
 	const char **sources;
 	const char **targets;
 
@@ -882,7 +890,7 @@
 
 	cl_assert(expected->idx < expected->len);
 
-	cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED);
+	cl_assert_equal_i(delta->status, expected->status[expected->idx]);
 
 	cl_assert(git__strcmp(expected->sources[expected->idx],
 		delta->old_file.path) == 0);
@@ -900,13 +908,14 @@
 	git_index *index;
 	git_tree *tree;
 	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
 	git_buf one = GIT_BUF_INIT, two = GIT_BUF_INIT;
+	unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
 	const char *sources[] = { "Class1.cs", "Class2.cs" };
 	const char *targets[] = { "ClassA.cs", "ClassB.cs" };
-	struct rename_expected expect = { 2, sources, targets };
+	struct rename_expected expect = { 2, status, sources, targets };
 	char *ptr;
 
 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
@@ -953,7 +962,7 @@
 	cl_git_pass(
 		git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 	git_reference_free(head);
@@ -985,12 +994,13 @@
 	git_index *index;
 	git_tree *tree;
 	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+	unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
 	const char *sources[] = { "a.txt", "b.txt" };
 	const char *targets[] = { "c.txt", "d.txt" };
-	struct rename_expected expect = { 2, sources, targets };
+	struct rename_expected expect = { 2, status, sources, targets };
 
 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
 
@@ -1026,18 +1036,121 @@
 		git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
 	cl_assert(expect.idx > 0);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(tree);
 	git_index_free(index);
 	git_reference_free(head);
 	git_reference_free(selfsimilar);
 }
 
+void test_diff_rename__rejected_match_can_match_others_three(void)
+{
+	git_reference *head, *selfsimilar;
+	git_index *index;
+	git_tree *tree;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+	git_diff *diff;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+	/* Both cannot be renames from a.txt */
+	unsigned int status[] = { GIT_DELTA_ADDED, GIT_DELTA_RENAMED };
+	const char *sources[] = { "0001.txt", "a.txt" };
+	const char *targets[] = { "0001.txt", "0002.txt" };
+	struct rename_expected expect = { 2, status, sources, targets };
+
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+	cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+	cl_git_pass(git_reference_symbolic_set_target(
+		&selfsimilar, head, "refs/heads/renames_similar_two"));
+	cl_git_pass(git_checkout_head(g_repo, &opts));
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_pass(p_unlink("renames/a.txt"));
+
+	cl_git_pass(git_index_remove_bypath(index, "a.txt"));
+
+	write_similarity_file_two("renames/0001.txt", 7);
+	write_similarity_file_two("renames/0002.txt", 0);
+
+	cl_git_pass(git_index_add_bypath(index, "0001.txt"));
+	cl_git_pass(git_index_add_bypath(index, "0002.txt"));
+
+	cl_git_pass(git_index_write(index));
+
+	cl_git_pass(
+		git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(
+		git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+	cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+	cl_git_pass(
+		git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+	cl_assert(expect.idx == expect.len);
+
+	git_diff_free(diff);
+	git_tree_free(tree);
+	git_index_free(index);
+	git_reference_free(head);
+	git_reference_free(selfsimilar);
+}
+
+void test_diff_rename__can_rename_from_rewrite(void)
+{
+	git_index *index;
+	git_tree *tree;
+	git_diff *diff;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+	unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+	const char *sources[] = { "ikeepsix.txt", "songof7cities.txt" };
+	const char *targets[] = { "songof7cities.txt", "this-is-a-rename.txt" };
+	struct rename_expected expect = { 2, status, sources, targets };
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_pass(p_rename("renames/songof7cities.txt", "renames/this-is-a-rename.txt"));
+	cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/songof7cities.txt"));
+
+	cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+
+	cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+	cl_git_pass(git_index_add_bypath(index, "this-is-a-rename.txt"));
+
+	cl_git_pass(git_index_write(index));
+
+	cl_git_pass(
+		git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(
+		git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+	findopts.flags |= GIT_DIFF_FIND_AND_BREAK_REWRITES |
+		GIT_DIFF_FIND_REWRITES |
+		GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+	cl_git_pass(
+		git_diff_foreach(diff, test_names_expected, NULL, NULL, &expect));
+
+	cl_assert(expect.idx == expect.len);
+
+	git_diff_free(diff);
+	git_tree_free(tree);
+	git_index_free(index);
+}
+
 void test_diff_rename__case_changes_are_split(void)
 {
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
 
@@ -1070,7 +1183,7 @@
 	cl_assert_equal_i(1, exp.files);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_index_free(index);
 	git_tree_free(tree);
 }
@@ -1079,7 +1192,7 @@
 {
 	git_index *index;
 	git_tree *tree;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
 	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
@@ -1118,7 +1231,56 @@
 	cl_assert_equal_i(1, exp.files);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_index_free(index);
 	git_tree_free(tree);
 }
+
+void test_diff_rename__rewrite_on_single_file(void)
+{
+	git_index *index;
+	git_diff *diff = NULL;
+	diff_expects exp;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+	diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+
+	findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+		GIT_DIFF_FIND_AND_BREAK_REWRITES |
+		GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_rewritefile("renames/ikeepsix.txt",
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+	cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+	memset(&exp, 0, sizeof(exp));
+
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+	cl_assert_equal_i(2, exp.files);
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+	git_diff_free(diff);
+	git_index_free(index);
+}
diff --git a/tests/diff/submodules.c b/tests/diff/submodules.c
new file mode 100644
index 0000000..24545b2
--- /dev/null
+++ b/tests/diff/submodules.c
@@ -0,0 +1,423 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "posix.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_submodules__initialize(void)
+{
+}
+
+void test_diff_submodules__cleanup(void)
+{
+}
+
+static void check_diff_patches_at_line(
+	git_diff *diff, const char **expected, const char *file, int line)
+{
+	const git_diff_delta *delta;
+	git_patch *patch = NULL;
+	size_t d, num_d = git_diff_num_deltas(diff);
+	char *patch_text;
+
+	for (d = 0; d < num_d; ++d, git_patch_free(patch)) {
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
+		cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+		if (delta->status == GIT_DELTA_UNMODIFIED) {
+			cl_assert_at_line(expected[d] == NULL, file, line);
+			continue;
+		}
+
+		if (expected[d] && !strcmp(expected[d], "<SKIP>"))
+			continue;
+		if (expected[d] && !strcmp(expected[d], "<END>")) {
+			cl_git_pass(git_patch_to_str(&patch_text, patch));
+			cl_assert_at_line(!strcmp(expected[d], "<END>"), file, line);
+		}
+
+		cl_git_pass(git_patch_to_str(&patch_text, patch));
+
+		clar__assert_equal(
+			file, line, "expected diff did not match actual diff", 1,
+			"%s", expected[d], patch_text);
+		git__free(patch_text);
+	}
+
+	cl_assert_at_line(expected[d] && !strcmp(expected[d], "<END>"), file, line);
+}
+
+#define check_diff_patches(diff, exp) \
+	check_diff_patches_at_line(diff, exp, __FILE__, __LINE__)
+
+void test_diff_submodules__unmodified_submodule(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	static const char *expected[] = {
+		"<SKIP>", /* .gitmodules */
+		NULL, /* added */
+		NULL, /* ignored */
+		"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+		NULL, /* testrepo.git */
+		NULL, /* unmodified */
+		NULL, /* untracked */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submodules();
+
+	opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+		GIT_DIFF_INCLUDE_UNTRACKED |
+		GIT_DIFF_INCLUDE_UNMODIFIED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected);
+	git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	static const char *expected[] = {
+		"<SKIP>", /* .gitmodules */
+		NULL, /* added */
+		NULL, /* ignored */
+		"diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+		"diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+		NULL, /* unmodified */
+		NULL, /* untracked */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submodules();
+
+	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+	cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+	opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+		GIT_DIFF_INCLUDE_UNTRACKED |
+		GIT_DIFF_INCLUDE_UNMODIFIED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected);
+	git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule_2(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL, *diff2 = NULL;
+	char *smpath = "testrepo";
+	static const char *expected_none[] = { "<END>" };
+	static const char *expected_dirty[] = {
+		"diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submodules();
+
+	cl_git_pass(git_submodule_reload_all(g_repo));
+
+	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
+		GIT_DIFF_SHOW_UNTRACKED_CONTENT |
+		GIT_DIFF_RECURSE_UNTRACKED_DIRS |
+		GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+	opts.pathspec.count = 1;
+	opts.pathspec.strings = &smpath;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_none);
+	git_diff_free(diff);
+
+	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+	cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+
+	{
+		git_tree *head;
+
+		cl_git_pass(git_repository_head_tree(&head, g_repo));
+		cl_git_pass(git_diff_tree_to_index(&diff2, g_repo, head, NULL, &opts));
+		cl_git_pass(git_diff_merge(diff, diff2));
+		git_diff_free(diff2);
+		git_tree_free(head);
+
+		check_diff_patches(diff, expected_dirty);
+	}
+
+	git_diff_free(diff);
+
+	cl_git_pass(git_submodule_reload_all(g_repo));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+	git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_index_to_wd(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	static const char *expected[] = {
+		"<SKIP>", /* .gitmodules */
+		"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+		"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+		"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+		"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submod2();
+
+	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected);
+	git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_head_to_index(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_tree *head;
+	git_diff *diff = NULL;
+	static const char *expected[] = {
+		"<SKIP>", /* .gitmodules */
+		"diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submod2();
+
+	cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+
+	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
+	check_diff_patches(diff, expected);
+	git_diff_free(diff);
+
+	git_tree_free(head);
+}
+
+void test_diff_submodules__invalid_cache(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_submodule *sm;
+	char *smpath = "sm_changed_head";
+	git_repository *smrepo;
+	git_index *smindex;
+	static const char *expected_baseline[] = {
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+		"<END>"
+	};
+	static const char *expected_unchanged[] = { "<END>" };
+	static const char *expected_dirty[] = {
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247-dirty\n",
+		"<END>"
+	};
+	static const char *expected_moved[] = {
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae\n",
+		"<END>"
+	};
+	static const char *expected_moved_dirty[] = {
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae-dirty\n",
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submod2();
+
+	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+	opts.pathspec.count = 1;
+	opts.pathspec.strings = &smpath;
+
+	/* baseline */
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_baseline);
+	git_diff_free(diff);
+
+	/* update index with new HEAD */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+	cl_git_pass(git_submodule_add_to_index(sm, 1));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_unchanged);
+	git_diff_free(diff);
+
+	/* create untracked file in submodule working directory */
+	cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello");
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+	git_diff_free(diff);
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_unchanged);
+	git_diff_free(diff);
+
+	/* modify tracked file in submodule working directory */
+	cl_git_append2file(
+		"submod2/sm_changed_head/file_to_modify", "\nmore stuff\n");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+	git_diff_free(diff);
+
+	cl_git_pass(git_submodule_reload_all(g_repo));
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+	git_diff_free(diff);
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_unchanged);
+	git_diff_free(diff);
+
+	/* add file to index in submodule */
+	cl_git_pass(git_submodule_open(&smrepo, sm));
+	cl_git_pass(git_repository_index(&smindex, smrepo));
+	cl_git_pass(git_index_add_bypath(smindex, "file_to_modify"));
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_dirty);
+	git_diff_free(diff);
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_unchanged);
+	git_diff_free(diff);
+
+	/* commit changed index of submodule */
+	cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_DIRTY);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_moved);
+	git_diff_free(diff);
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_ALL);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_unchanged);
+	git_diff_free(diff);
+
+	git_submodule_set_ignore(sm, GIT_SUBMODULE_IGNORE_NONE);
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_moved_dirty);
+	git_diff_free(diff);
+
+	p_unlink("submod2/sm_changed_head/new_around_here");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_moved);
+	git_diff_free(diff);
+
+	git_index_free(smindex);
+	git_repository_free(smrepo);
+}
+
+void test_diff_submodules__diff_ignore_options(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_config *cfg;
+	static const char *expected_normal[] = {
+		"<SKIP>", /* .gitmodules */
+		"diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+		"diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+		"diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+		"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+		"<END>"
+	};
+	static const char *expected_ignore_all[] = {
+		"<SKIP>", /* .gitmodules */
+		"<END>"
+	};
+	static const char *expected_ignore_dirty[] = {
+		"<SKIP>", /* .gitmodules */
+		"diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+		"diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+		"<END>"
+	};
+
+	g_repo = setup_fixture_submod2();
+
+	opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+	opts.old_prefix = "a"; opts.new_prefix = "b";
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_normal);
+	git_diff_free(diff);
+
+	opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_ignore_all);
+	git_diff_free(diff);
+
+	opts.flags &= ~GIT_DIFF_IGNORE_SUBMODULES;
+	opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_ignore_all);
+	git_diff_free(diff);
+
+	opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_ignore_dirty);
+	git_diff_free(diff);
+
+	opts.ignore_submodules = 0;
+	cl_git_pass(git_repository_config(&cfg, g_repo));
+	cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", false));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_normal);
+	git_diff_free(diff);
+
+	cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", true));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_ignore_all);
+	git_diff_free(diff);
+
+	cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "none"));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_normal);
+	git_diff_free(diff);
+
+	cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "dirty"));
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	check_diff_patches(diff, expected_ignore_dirty);
+	git_diff_free(diff);
+
+	git_config_free(cfg);
+}
diff --git a/tests-clar/diff/tree.c b/tests/diff/tree.c
similarity index 88%
rename from tests-clar/diff/tree.c
rename to tests/diff/tree.c
index f05c786..582174b 100644
--- a/tests-clar/diff/tree.c
+++ b/tests/diff/tree.c
@@ -3,15 +3,13 @@
 
 static git_repository *g_repo = NULL;
 static git_diff_options opts;
-static git_diff_list *diff;
+static git_diff *diff;
 static git_tree *a, *b;
 static diff_expects expect;
 
 void test_diff_tree__initialize(void)
 {
-	GIT_INIT_STRUCTURE(&opts, GIT_DIFF_OPTIONS_VERSION);
-	/* The default context lines is set by _INIT which we can't use here */
-	opts.context_lines = 3;
+	cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
 
 	memset(&expect, 0, sizeof(expect));
 
@@ -22,7 +20,7 @@
 
 void test_diff_tree__cleanup(void)
 {
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(a);
 	git_tree_free(b);
 
@@ -65,7 +63,7 @@
 	cl_assert_equal_i(24 + 1 + 5 + 5, expect.line_adds);
 	cl_assert_equal_i(7 + 1, expect.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	memset(&expect, 0, sizeof(expect));
@@ -90,6 +88,10 @@
 	git_tree_free(c);
 }
 
+#define DIFF_OPTS(FLAGS, CTXT) \
+	{GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_DEFAULT, \
+	{NULL,0}, NULL, NULL, (CTXT), 1}
+
 void test_diff_tree__options(void)
 {
 	/* grabbed a couple of commit oids from the history of the attr repo */
@@ -102,16 +104,16 @@
 	int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
 	git_diff_options test_options[] = {
 		/* a vs b tests */
-		{ 1, GIT_DIFF_NORMAL, 1, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_REVERSE, 2, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_FORCE_TEXT, 2, 1, NULL, NULL, {0} },
+		DIFF_OPTS(GIT_DIFF_NORMAL, 1),
+		DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+		DIFF_OPTS(GIT_DIFF_REVERSE, 2),
+		DIFF_OPTS(GIT_DIFF_FORCE_TEXT, 2),
 		/* c vs d tests */
-		{ 1, GIT_DIFF_NORMAL, 3, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_IGNORE_WHITESPACE, 3, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_IGNORE_WHITESPACE_EOL, 3, 1, NULL, NULL, {0} },
-		{ 1, GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1, 1, NULL, NULL, {0} },
+		DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+		DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE, 3),
+		DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3),
+		DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_EOL, 3),
+		DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1),
 	};
 
 	/* to generate these values:
@@ -168,7 +170,7 @@
 		cl_assert_equal_i(actual.line_adds, expected->line_adds);
 		cl_assert_equal_i(actual.line_dels, expected->line_dels);
 
-		git_diff_list_free(diff);
+		git_diff_free(diff);
 		diff = NULL;
 	}
 
@@ -214,7 +216,7 @@
 	const char *b_commit = "370fe9ec22";
 	const char *c_commit = "f5b0af1fb4f5c";
 	git_tree *c;
-	git_diff_list *diff1 = NULL, *diff2 = NULL;
+	git_diff *diff1 = NULL, *diff2 = NULL;
 
 	g_repo = cl_git_sandbox_init("attr");
 
@@ -230,7 +232,7 @@
 
 	cl_git_pass(git_diff_merge(diff1, diff2));
 
-	git_diff_list_free(diff2);
+	git_diff_free(diff2);
 
 	cl_git_pass(git_diff_foreach(
 		diff1, diff_file_cb, diff_hunk_cb, diff_line_cb, &expect));
@@ -247,19 +249,17 @@
 	cl_assert_equal_i(36, expect.line_adds);
 	cl_assert_equal_i(22, expect.line_dels);
 
-	git_diff_list_free(diff1);
+	git_diff_free(diff1);
 }
 
 void test_diff_tree__larger_hunks(void)
 {
 	const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
 	const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
-	size_t d, num_d, h, num_h, l, num_l, header_len, line_len;
-	const git_diff_delta *delta;
-	git_diff_patch *patch;
-	const git_diff_range *range;
-	const char *header, *line;
-	char origin;
+	size_t d, num_d, h, num_h, l, num_l;
+	git_patch *patch;
+	const git_diff_hunk *hunk;
+	const git_diff_line *line;
 
 	g_repo = cl_git_sandbox_init("diff");
 
@@ -273,31 +273,27 @@
 
 	num_d = git_diff_num_deltas(diff);
 	for (d = 0; d < num_d; ++d) {
-		cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
-		cl_assert(patch && delta);
+		cl_git_pass(git_patch_from_diff(&patch, diff, d));
+		cl_assert(patch);
 
-		num_h = git_diff_patch_num_hunks(patch);
+		num_h = git_patch_num_hunks(patch);
 		for (h = 0; h < num_h; h++) {
-			cl_git_pass(git_diff_patch_get_hunk(
-				&range, &header, &header_len, &num_l, patch, h));
+			cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
 
 			for (l = 0; l < num_l; ++l) {
-				cl_git_pass(git_diff_patch_get_line_in_hunk(
-					&origin, &line, &line_len, NULL, NULL, patch, h, l));
+				cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
 				cl_assert(line);
 			}
 
-			cl_git_fail(git_diff_patch_get_line_in_hunk(
-				&origin, &line, &line_len, NULL, NULL, patch, h, num_l));
+			cl_git_fail(git_patch_get_line_in_hunk(&line, patch, h, num_l));
 		}
 
-		cl_git_fail(git_diff_patch_get_hunk(
-			&range, &header, &header_len, &num_l, patch, num_h));
+		cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
 
-		git_diff_patch_free(patch);
+		git_patch_free(patch);
 	}
 
-	cl_git_fail(git_diff_get_patch(&patch, &delta, diff, num_d));
+	cl_git_fail(git_patch_from_diff(&patch, diff, num_d));
 
 	cl_assert_equal_i(2, (int)num_d);
 }
@@ -487,7 +483,7 @@
 	cl_assert_equal_i(7, expect.line_adds);
 	cl_assert_equal_i(15, expect.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	set_config_int(g_repo, "diff.context", 1);
@@ -507,7 +503,7 @@
 	cl_assert_equal_i(7, expect.line_adds);
 	cl_assert_equal_i(15, expect.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	set_config_int(g_repo, "diff.context", 0);
diff --git a/tests-clar/diff/workdir.c b/tests/diff/workdir.c
similarity index 77%
rename from tests-clar/diff/workdir.c
rename to tests/diff/workdir.c
index 6a2504d..7cc0322 100644
--- a/tests-clar/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -16,7 +16,7 @@
 void test_diff_workdir__to_index(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	int use_iterator;
 
@@ -60,7 +60,61 @@
 		cl_assert_equal_i(5, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
+}
+
+void test_diff_workdir__to_index_with_assume_unchanged(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_index *idx = NULL;
+	diff_expects exp;
+	const git_index_entry *iep;
+	git_index_entry ie;
+
+	g_repo = cl_git_sandbox_init("status");
+
+	/* do initial diff */
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+	cl_assert_equal_i(8, exp.files);
+	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+	git_diff_free(diff);
+
+	/* mark a couple of entries with ASSUME_UNCHANGED */
+
+	cl_git_pass(git_repository_index(&idx, g_repo));
+
+	cl_assert((iep = git_index_get_bypath(idx, "modified_file", 0)) != NULL);
+	memcpy(&ie, iep, sizeof(ie));
+	ie.flags |= GIT_IDXENTRY_VALID;
+	cl_git_pass(git_index_add(idx, &ie));
+
+	cl_assert((iep = git_index_get_bypath(idx, "file_deleted", 0)) != NULL);
+	memcpy(&ie, iep, sizeof(ie));
+	ie.flags |= GIT_IDXENTRY_VALID;
+	cl_git_pass(git_index_add(idx, &ie));
+
+	cl_git_pass(git_index_write(idx));
+	git_index_free(idx);
+
+	/* redo diff and see that entries are skipped */
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+	cl_assert_equal_i(6, exp.files);
+	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+	git_diff_free(diff);
+
 }
 
 void test_diff_workdir__to_tree(void)
@@ -70,8 +124,8 @@
 	const char *b_commit = "0017bd4ab1ec3"; /* the start */
 	git_tree *a, *b;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
-	git_diff_list *diff2 = NULL;
+	git_diff *diff = NULL;
+	git_diff *diff2 = NULL;
 	diff_expects exp;
 	int use_iterator;
 
@@ -119,7 +173,7 @@
 	 * do more apples-to-apples test comparison below.
 	 */
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 	memset(&exp, 0, sizeof(exp));
 
@@ -130,7 +184,7 @@
 	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
 	cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
 	cl_git_pass(git_diff_merge(diff, diff2));
-	git_diff_list_free(diff2);
+	git_diff_free(diff2);
 
 	for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
 		memset(&exp, 0, sizeof(exp));
@@ -157,7 +211,7 @@
 		cl_assert_equal_i(5, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 	memset(&exp, 0, sizeof(exp));
 
@@ -167,7 +221,7 @@
 	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
 	cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
 	cl_git_pass(git_diff_merge(diff, diff2));
-	git_diff_list_free(diff2);
+	git_diff_free(diff2);
 
 	for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
 		memset(&exp, 0, sizeof(exp));
@@ -194,7 +248,39 @@
 		cl_assert_equal_i(4, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
+
+	/* Let's try that once more with a reversed diff */
+
+	opts.flags |= GIT_DIFF_REVERSE;
+
+	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+	cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+	cl_git_pass(git_diff_merge(diff, diff2));
+	git_diff_free(diff2);
+
+	memset(&exp, 0, sizeof(exp));
+
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+	cl_assert_equal_i(16, exp.files);
+	cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+	cl_assert_equal_i(12, exp.hunks);
+
+	cl_assert_equal_i(19, exp.lines);
+	cl_assert_equal_i(3, exp.line_ctxt);
+	cl_assert_equal_i(12, exp.line_dels);
+	cl_assert_equal_i(4, exp.line_adds);
+
+	git_diff_free(diff);
+
+	/* all done now */
 
 	git_tree_free(a);
 	git_tree_free(b);
@@ -203,7 +289,7 @@
 void test_diff_workdir__to_index_with_pathspec(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	char *pathspec = NULL;
 	int use_iterator;
@@ -235,7 +321,7 @@
 		cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	pathspec = "modified_file";
 
@@ -258,7 +344,7 @@
 		cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	pathspec = "subdir";
 
@@ -281,7 +367,7 @@
 		cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	pathspec = "*_deleted";
 
@@ -304,12 +390,12 @@
 		cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_workdir__filemode_changes(void)
 {
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	int use_iterator;
 
@@ -339,7 +425,7 @@
 		cl_assert_equal_i(0, exp.hunks);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* chmod file and test again */
 
@@ -362,14 +448,14 @@
 		cl_assert_equal_i(0, exp.hunks);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	cl_assert(cl_toggle_filemode("issue_592/a.txt"));
 }
 
 void test_diff_workdir__filemode_changes_with_filemode_false(void)
 {
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
 	if (!cl_is_chmod_supported())
@@ -391,7 +477,7 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(0, exp.hunks);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* chmod file and test again */
 
@@ -407,7 +493,7 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
 	cl_assert_equal_i(0, exp.hunks);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	cl_assert(cl_toggle_filemode("issue_592/a.txt"));
 }
@@ -415,7 +501,7 @@
 void test_diff_workdir__head_index_and_workdir_all_differ(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff_i2t = NULL, *diff_w2i = NULL;
+	git_diff *diff_i2t = NULL, *diff_w2i = NULL;
 	diff_expects exp;
 	char *pathspec = "staged_changes_modified_file";
 	git_tree *tree;
@@ -504,8 +590,8 @@
 		cl_assert_equal_i(0, exp.line_dels);
 	}
 
-	git_diff_list_free(diff_i2t);
-	git_diff_list_free(diff_w2i);
+	git_diff_free(diff_i2t);
+	git_diff_free(diff_w2i);
 
 	git_tree_free(tree);
 }
@@ -513,7 +599,7 @@
 void test_diff_workdir__eof_newline_changes(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	char *pathspec = "current_file";
 	int use_iterator;
@@ -546,7 +632,7 @@
 		cl_assert_equal_i(0, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	cl_git_append2file("status/current_file", "\n");
 
@@ -573,7 +659,7 @@
 		cl_assert_equal_i(0, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	cl_git_rewritefile("status/current_file", "current_file");
 
@@ -600,7 +686,7 @@
 		cl_assert_equal_i(2, exp.line_dels);
 	}
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 /* PREPARATION OF TEST DATA
@@ -673,7 +759,7 @@
 	const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
 	git_tree *a, *b;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	size_t i, d, num_d, h, num_h, l, num_l, header_len, line_len;
+	size_t i, d, num_d, h, num_h, l, num_l;
 
 	g_repo = cl_git_sandbox_init("diff");
 
@@ -684,11 +770,10 @@
 	opts.interhunk_lines = 0;
 
 	for (i = 0; i <= 2; ++i) {
-		git_diff_list *diff = NULL;
-		git_diff_patch *patch;
-		const git_diff_range *range;
-		const char *header, *line;
-		char origin;
+		git_diff *diff = NULL;
+		git_patch *patch;
+		const git_diff_hunk *hunk;
+		const git_diff_line *line;
 
 		/* okay, this is a bit silly, but oh well */
 		switch (i) {
@@ -707,33 +792,31 @@
 		cl_assert_equal_i(2, (int)num_d);
 
 		for (d = 0; d < num_d; ++d) {
-			cl_git_pass(git_diff_get_patch(&patch, NULL, diff, d));
+			cl_git_pass(git_patch_from_diff(&patch, diff, d));
 			cl_assert(patch);
 
-			num_h = git_diff_patch_num_hunks(patch);
+			num_h = git_patch_num_hunks(patch);
 			for (h = 0; h < num_h; h++) {
-				cl_git_pass(git_diff_patch_get_hunk(
-					&range, &header, &header_len, &num_l, patch, h));
+				cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
 
 				for (l = 0; l < num_l; ++l) {
-					cl_git_pass(git_diff_patch_get_line_in_hunk(
-						&origin, &line, &line_len, NULL, NULL, patch, h, l));
+					cl_git_pass(
+						git_patch_get_line_in_hunk(&line, patch, h, l));
 					cl_assert(line);
 				}
 
 				/* confirm fail after the last item */
-				cl_git_fail(git_diff_patch_get_line_in_hunk(
-					&origin, &line, &line_len, NULL, NULL, patch, h, num_l));
+				cl_git_fail(
+					git_patch_get_line_in_hunk(&line, patch, h, num_l));
 			}
 
 			/* confirm fail after the last item */
-			cl_git_fail(git_diff_patch_get_hunk(
-				&range, &header, &header_len, &num_l, patch, num_h));
+			cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
 
-			git_diff_patch_free(patch);
+			git_patch_free(patch);
 		}
 
-		git_diff_list_free(diff);
+		git_diff_free(diff);
 	}
 
 	git_tree_free(a);
@@ -758,26 +841,18 @@
 	const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
 	git_tree *a;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
-	g_repo = cl_git_sandbox_init("submod2");
-
-	cl_fixture_sandbox("submod2_target");
-	p_rename("submod2_target/.gitted", "submod2_target/.git");
-
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-	p_rename("submod2/not/.gitted", "submod2/not/.git");
-
-	cl_fixture_cleanup("submod2_target");
+	g_repo = setup_fixture_submod2();
 
 	a = resolve_commit_oid_to_tree(g_repo, a_commit);
 
 	opts.flags =
 		GIT_DIFF_INCLUDE_UNTRACKED |
+		GIT_DIFF_INCLUDE_IGNORED |
 		GIT_DIFF_RECURSE_UNTRACKED_DIRS |
-		GIT_DIFF_INCLUDE_UNTRACKED_CONTENT;
+		GIT_DIFF_SHOW_UNTRACKED_CONTENT;
 
 	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
 
@@ -806,7 +881,7 @@
 	 * only significant difference is that those Added items will show up
 	 * as Untracked items in the pure libgit2 diff.
 	 *
-	 * Then add in the two extra untracked items "not" and "not-submodule"
+	 * Then add in the two extra ignored items "not" and "not-submodule"
 	 * to get the 12 files reported here.
 	 */
 
@@ -815,8 +890,8 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
-	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
-	cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
+	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_IGNORED]);
+	cl_assert_equal_i(8, exp.file_status[GIT_DELTA_UNTRACKED]);
 
 	/* the following numbers match "git diff 873585" exactly */
 
@@ -827,14 +902,14 @@
 	cl_assert_equal_i(30, exp.line_adds);
 	cl_assert_equal_i(1, exp.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(a);
 }
 
 void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	git_tree *tree;
 
 	g_repo = cl_git_sandbox_init("testrepo.git");
@@ -852,7 +927,7 @@
 
 void test_diff_workdir__to_null_tree(void)
 {
-	git_diff_list *diff;
+	git_diff *diff;
 	diff_expects exp;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
 
@@ -870,12 +945,12 @@
 
 	cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_workdir__checks_options_version(void)
 {
-	git_diff_list *diff;
+	git_diff *diff;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
 	const git_error *err;
 
@@ -895,11 +970,11 @@
 
 void test_diff_workdir__can_diff_empty_file(void)
 {
-	git_diff_list *diff;
+	git_diff *diff;
 	git_tree *tree;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
 	struct stat st;
-	git_diff_patch *patch;
+	git_patch *patch;
 
 	g_repo = cl_git_sandbox_init("attr_index");
 
@@ -909,7 +984,7 @@
 
 	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
 	cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* empty contents of file */
 
@@ -920,9 +995,9 @@
 	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
 	cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
 	/* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
+	cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+	git_patch_free(patch);
+	git_diff_free(diff);
 
 	/* remove a file altogether */
 
@@ -931,9 +1006,9 @@
 
 	cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
 	cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
-	cl_git_pass(git_diff_get_patch(&patch, NULL, diff, 1));
-	git_diff_patch_free(patch);
-	git_diff_list_free(diff);
+	cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+	git_patch_free(patch);
+	git_diff_free(diff);
 
 	git_tree_free(tree);
 }
@@ -941,7 +1016,7 @@
 void test_diff_workdir__to_index_issue_1397(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 
 	g_repo = cl_git_sandbox_init("issue_1397");
@@ -961,7 +1036,7 @@
 	cl_assert_equal_i(0, exp.hunks);
 	cl_assert_equal_i(0, exp.lines);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	cl_git_rewritefile("issue_1397/crlf_file.txt",
@@ -983,7 +1058,7 @@
 	cl_assert_equal_i(1, exp.line_adds);
 	cl_assert_equal_i(1, exp.line_dels);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 void test_diff_workdir__to_tree_issue_1397(void)
@@ -991,8 +1066,8 @@
 	const char *a_commit = "7f483a738"; /* the current HEAD */
 	git_tree *a;
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
-	git_diff_list *diff2 = NULL;
+	git_diff *diff = NULL;
+	git_diff *diff2 = NULL;
 	diff_expects exp;
 
 	g_repo = cl_git_sandbox_init("issue_1397");
@@ -1014,13 +1089,13 @@
 	cl_assert_equal_i(0, exp.hunks);
 	cl_assert_equal_i(0, exp.lines);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	diff = NULL;
 
 	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
 	cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
 	cl_git_pass(git_diff_merge(diff, diff2));
-	git_diff_list_free(diff2);
+	git_diff_free(diff2);
 
 	memset(&exp, 0, sizeof(exp));
 	cl_git_pass(git_diff_foreach(
@@ -1030,14 +1105,14 @@
 	cl_assert_equal_i(0, exp.hunks);
 	cl_assert_equal_i(0, exp.lines);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 	git_tree_free(a);
 }
 
 void test_diff_workdir__untracked_directory_scenarios(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 	diff_expects exp;
 	char *pathspec = NULL;
 	static const char *files0[] = {
@@ -1087,7 +1162,7 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* empty directory */
 
@@ -1107,7 +1182,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* empty directory in empty directory */
 
@@ -1127,7 +1202,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* directory with only ignored files */
 
@@ -1151,7 +1226,7 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* directory with ignored directory (contents irrelevant) */
 
@@ -1174,11 +1249,11 @@
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* quick version avoids directory scan */
 
-	opts.flags = opts.flags | GIT_DIFF_FAST_UNTRACKED_DIRS;
+	opts.flags = opts.flags | GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
 
 	memset(&exp, 0, sizeof(exp));
 	exp.names = files1;
@@ -1194,11 +1269,11 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* directory with nested non-ignored content */
 
-	opts.flags = opts.flags & ~GIT_DIFF_FAST_UNTRACKED_DIRS;
+	opts.flags = opts.flags & ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
 
 	cl_git_mkfile("status/subdir/directory/more/notignored",
 		"not ignored deep under untracked\n");
@@ -1217,7 +1292,7 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 
 	/* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
 
@@ -1238,14 +1313,14 @@
 	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
 	cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
 }
 
 
 void test_diff_workdir__untracked_directory_comes_last(void)
 {
 	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
-	git_diff_list *diff = NULL;
+	git_diff *diff = NULL;
 
 	g_repo = cl_git_sandbox_init("renames");
 
@@ -1263,5 +1338,153 @@
 
 	cl_assert(diff != NULL);
 
-	git_diff_list_free(diff);
+	git_diff_free(diff);
+}
+
+void test_diff_workdir__untracked_with_bom(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	const git_diff_delta *delta;
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+	cl_git_write2file("empty_standard_repo/bom.txt",
+		"\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
+
+	opts.flags =
+		GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+	cl_assert_equal_i(1, git_diff_num_deltas(diff));
+	cl_assert((delta = git_diff_get_delta(diff, 0)) != NULL);
+	cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
+
+	/* not known at this point
+	 * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+	 */
+
+	git_diff_free(diff);
+}
+
+void test_diff_workdir__patience_diff(void)
+{
+	git_index *index;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_patch *patch = NULL;
+	char *as_str = NULL;
+	const char *expected_normal = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n-how to create\n-a patience diff\n I did not know\n how to create\n+a patience diff\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+	const char *expected_patience = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n+I did not know\n how to create\n a patience diff\n-I did not know\n-how to create\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_mkfile(
+		"empty_standard_repo/test.txt",
+		"When I wrote this\nI did not know\nhow to create\na patience diff\nI did not know\nhow to create\nanother problem\nI did not know\nhow to create\na minimal diff\n");
+	cl_git_pass(git_index_add_bypath(index, "test.txt"));
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "Base");
+	git_index_free(index);
+
+	cl_git_rewritefile(
+		"empty_standard_repo/test.txt",
+		"When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	cl_assert_equal_i(1, git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&as_str, patch));
+
+	cl_assert_equal_s(expected_normal, as_str);
+	git__free(as_str);
+	git_patch_free(patch);
+	git_diff_free(diff);
+
+	opts.flags |= GIT_DIFF_PATIENCE;
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+	cl_assert_equal_i(1, git_diff_num_deltas(diff));
+	cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+	cl_git_pass(git_patch_to_str(&as_str, patch));
+
+	cl_assert_equal_s(expected_patience, as_str);
+	git__free(as_str);
+	git_patch_free(patch);
+	git_diff_free(diff);
+}
+
+void test_diff_workdir__with_stale_index(void)
+{
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	git_diff *diff = NULL;
+	git_index *idx = NULL;
+	diff_expects exp;
+
+	g_repo = cl_git_sandbox_init("status");
+	cl_git_pass(git_repository_index(&idx, g_repo));
+
+	/* make the in-memory index invalid */
+	{
+		git_repository *r2;
+		git_index *idx2;
+		cl_git_pass(git_repository_open(&r2, "status"));
+		cl_git_pass(git_repository_index(&idx2, r2));
+		cl_git_pass(git_index_add_bypath(idx2, "new_file"));
+		cl_git_pass(git_index_add_bypath(idx2, "subdir/new_file"));
+		cl_git_pass(git_index_remove_bypath(idx2, "staged_new_file"));
+		cl_git_pass(git_index_remove_bypath(idx2, "staged_changes_file_deleted"));
+		cl_git_pass(git_index_write(idx2));
+		git_index_free(idx2);
+		git_repository_free(r2);
+	}
+
+	opts.context_lines = 3;
+	opts.interhunk_lines = 1;
+	opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED;
+
+	/* first try with index pointer which should prevent reload */
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
+
+	memset(&exp, 0, sizeof(exp));
+
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+	cl_assert_equal_i(17, exp.files);
+	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+	cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+	git_diff_free(diff);
+
+	/* now let's try without the index pointer which should trigger reload */
+
+	/* two files that were UNTRACKED should have become UNMODIFIED */
+	/* one file that was UNMODIFIED should now have become UNTRACKED */
+	/* one file that was DELETED should now be gone completely */
+
+	cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+	memset(&exp, 0, sizeof(exp));
+
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+	git_diff_free(diff);
+
+	cl_assert_equal_i(16, exp.files);
+	cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+	cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+	cl_assert_equal_i(6, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+	git_index_free(idx);
 }
diff --git a/tests-clar/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h
similarity index 100%
rename from tests-clar/fetchhead/fetchhead_data.h
rename to tests/fetchhead/fetchhead_data.h
diff --git a/tests-clar/fetchhead/nonetwork.c b/tests/fetchhead/nonetwork.c
similarity index 100%
rename from tests-clar/fetchhead/nonetwork.c
rename to tests/fetchhead/nonetwork.c
diff --git a/tests/filter/blob.c b/tests/filter/blob.c
new file mode 100644
index 0000000..9600a97
--- /dev/null
+++ b/tests/filter/blob.c
@@ -0,0 +1,84 @@
+#include "clar_libgit2.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_blob__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("crlf");
+	cl_git_mkfile("crlf/.gitattributes",
+		"*.txt text\n*.bin binary\n"
+		"*.crlf text eol=crlf\n"
+		"*.lf text eol=lf\n"
+		"*.ident text ident\n"
+		"*.identcrlf ident text eol=crlf\n"
+		"*.identlf ident text eol=lf\n");
+}
+
+void test_filter_blob__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_filter_blob__all_crlf(void)
+{
+	git_blob *blob;
+	git_buf buf = { 0 };
+
+	cl_git_pass(git_revparse_single(
+		(git_object **)&blob, g_repo, "a9a2e891")); /* all-crlf */
+
+	cl_assert_equal_s(ALL_CRLF_TEXT_RAW, git_blob_rawcontent(blob));
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "file.bin", 1));
+
+	cl_assert_equal_s(ALL_CRLF_TEXT_RAW, buf.ptr);
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "file.crlf", 1));
+
+	/* in this case, raw content has crlf in it already */
+	cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "file.lf", 1));
+
+	cl_assert_equal_s(ALL_CRLF_TEXT_AS_LF, buf.ptr);
+
+	git_buf_free(&buf);
+	git_blob_free(blob);
+}
+
+void test_filter_blob__ident(void)
+{
+	git_oid id;
+	git_blob *blob;
+	git_buf buf = { 0 };
+
+	cl_git_mkfile("crlf/test.ident", "Some text\n$Id$\nGoes there\n");
+	cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "test.ident"));
+	cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+	cl_assert_equal_s(
+		"Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+	git_blob_free(blob);
+
+	cl_git_mkfile("crlf/test.ident", "Some text\n$Id: Any old just you want$\nGoes there\n");
+	cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "test.ident"));
+	cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+	cl_assert_equal_s(
+		"Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.bin", 1));
+	cl_assert_equal_s(
+		"Some text\n$Id$\nGoes there\n", buf.ptr);
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.identcrlf", 1));
+	cl_assert_equal_s(
+		"Some text\r\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845$\r\nGoes there\r\n", buf.ptr);
+
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "filter.identlf", 1));
+	cl_assert_equal_s(
+		"Some text\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845$\nGoes there\n", buf.ptr);
+
+	git_buf_free(&buf);
+	git_blob_free(blob);
+
+}
diff --git a/tests/filter/crlf.c b/tests/filter/crlf.c
new file mode 100644
index 0000000..c9fb9cd
--- /dev/null
+++ b/tests/filter/crlf.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_crlf__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("crlf");
+
+	cl_git_mkfile("crlf/.gitattributes",
+		"*.txt text\n*.bin binary\n*.crlf text eol=crlf\n*.lf text eol=lf\n");
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", true);
+}
+
+void test_filter_crlf__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_filter_crlf__to_worktree(void)
+{
+	git_filter_list *fl;
+	git_filter *crlf;
+	git_buf in = { 0 }, out = { 0 };
+
+	cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+
+	crlf = git_filter_lookup(GIT_FILTER_CRLF);
+	cl_assert(crlf != NULL);
+
+	cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+	in.ptr = "Some text\nRight here\n";
+	in.size = strlen(in.ptr);
+
+	cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+#ifdef GIT_WIN32
+	cl_assert_equal_s("Some text\r\nRight here\r\n", out.ptr);
+#else
+	cl_assert_equal_s("Some text\nRight here\n", out.ptr);
+#endif
+
+	git_filter_list_free(fl);
+	git_buf_free(&out);
+}
+
+void test_filter_crlf__to_odb(void)
+{
+	git_filter_list *fl;
+	git_filter *crlf;
+	git_buf in = { 0 }, out = { 0 };
+
+	cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+
+	crlf = git_filter_lookup(GIT_FILTER_CRLF);
+	cl_assert(crlf != NULL);
+
+	cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+	in.ptr = "Some text\r\nRight here\r\n";
+	in.size = strlen(in.ptr);
+
+	cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+	cl_assert_equal_s("Some text\nRight here\n", out.ptr);
+
+	git_filter_list_free(fl);
+	git_buf_free(&out);
+}
diff --git a/tests/filter/crlf.h b/tests/filter/crlf.h
new file mode 100644
index 0000000..9cb98ad
--- /dev/null
+++ b/tests/filter/crlf.h
@@ -0,0 +1,25 @@
+#ifndef INCLUDE_filter_crlf_h__
+#define INCLUDE_filter_crlf_h__
+
+/*
+ * file content for files in the resources/crlf repository
+ */
+
+#define UTF8_BOM "\xEF\xBB\xBF"
+
+#define ALL_CRLF_TEXT_RAW		"crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
+#define ALL_LF_TEXT_RAW			"lf\nlf\nlf\nlf\nlf\n"
+#define MORE_CRLF_TEXT_RAW		"crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_RAW		"lf\nlf\ncrlf\r\nlf\nlf\n"
+
+#define ALL_CRLF_TEXT_AS_CRLF	ALL_CRLF_TEXT_RAW
+#define ALL_LF_TEXT_AS_CRLF		"lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
+#define MORE_CRLF_TEXT_AS_CRLF	"crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_AS_CRLF	"lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
+
+#define ALL_CRLF_TEXT_AS_LF		"crlf\ncrlf\ncrlf\ncrlf\n"
+#define ALL_LF_TEXT_AS_LF		ALL_LF_TEXT_RAW
+#define MORE_CRLF_TEXT_AS_LF	"crlf\ncrlf\nlf\ncrlf\ncrlf\n"
+#define MORE_LF_TEXT_AS_LF		"lf\nlf\ncrlf\nlf\nlf\n"
+
+#endif
diff --git a/tests/filter/custom.c b/tests/filter/custom.c
new file mode 100644
index 0000000..a81885c
--- /dev/null
+++ b/tests/filter/custom.c
@@ -0,0 +1,337 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+#include "buf_text.h"
+#include "git2/sys/filter.h"
+#include "git2/sys/repository.h"
+
+/* going TO_WORKDIR, filters are executed low to high
+ * going TO_ODB, filters are executed high to low
+ */
+#define BITFLIP_FILTER_PRIORITY -1
+#define REVERSE_FILTER_PRIORITY -2
+
+#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
+
+#ifdef GIT_WIN32
+# define NEWLINE "\r\n"
+#else
+# define NEWLINE "\n"
+#endif
+
+static char workdir_data[] =
+	"some simple" NEWLINE
+	"data" NEWLINE
+	"that will be" NEWLINE
+	"trivially" NEWLINE
+	"scrambled." NEWLINE;
+
+/* Represents the data above scrambled (bits flipped) after \r\n -> \n
+ * conversion, then bytewise reversed
+ */
+static unsigned char bitflipped_and_reversed_data[] =
+	{ 0xf5, 0xd1, 0x9b, 0x9a, 0x93, 0x9d, 0x92, 0x9e, 0x8d, 0x9c, 0x8c,
+	  0xf5, 0x86, 0x93, 0x93, 0x9e, 0x96, 0x89, 0x96, 0x8d, 0x8b, 0xf5,
+	  0x9a, 0x9d, 0xdf, 0x93, 0x93, 0x96, 0x88, 0xdf, 0x8b, 0x9e, 0x97,
+	  0x8b, 0xf5, 0x9e, 0x8b, 0x9e, 0x9b, 0xf5, 0x9a, 0x93, 0x8f, 0x92,
+	  0x96, 0x8c, 0xdf, 0x9a, 0x92, 0x90, 0x8c };
+
+#define BITFLIPPED_AND_REVERSED_DATA_LEN 51
+
+static git_repository *g_repo = NULL;
+
+static void register_custom_filters(void);
+
+void test_filter_custom__initialize(void)
+{
+	register_custom_filters();
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+	cl_git_mkfile(
+		"empty_standard_repo/.gitattributes",
+		"hero* bitflip reverse\n"
+		"herofile text\n"
+		"heroflip -reverse binary\n"
+		"*.bin binary\n");
+}
+
+void test_filter_custom__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+	g_repo = NULL;
+}
+
+static int bitflip_filter_apply(
+	git_filter     *self,
+	void          **payload,
+	git_buf        *to,
+	const git_buf  *from,
+	const git_filter_source *source)
+{
+	const unsigned char *src = (const unsigned char *)from->ptr;
+	unsigned char *dst;
+	size_t i;
+
+	GIT_UNUSED(self); GIT_UNUSED(payload);
+
+	/* verify that attribute path match worked as expected */
+	cl_assert_equal_i(
+		0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+	if (!from->size)
+		return 0;
+
+	cl_git_pass(git_buf_grow(to, from->size));
+
+	dst = (unsigned char *)to->ptr;
+
+	for (i = 0; i < from->size; i++)
+		dst[i] = VERY_SECURE_ENCRYPTION(src[i]);
+
+	to->size = from->size;
+
+	return 0;
+}
+
+static void bitflip_filter_free(git_filter *f)
+{
+	git__free(f);
+}
+
+static git_filter *create_bitflip_filter(void)
+{
+	git_filter *filter = git__calloc(1, sizeof(git_filter));
+	cl_assert(filter);
+
+	filter->version = GIT_FILTER_VERSION;
+	filter->attributes = "+bitflip";
+	filter->shutdown = bitflip_filter_free;
+	filter->apply = bitflip_filter_apply;
+
+	return filter;
+}
+
+
+static int reverse_filter_apply(
+	git_filter     *self,
+	void          **payload,
+	git_buf        *to,
+	const git_buf  *from,
+	const git_filter_source *source)
+{
+	const unsigned char *src = (const unsigned char *)from->ptr;
+	const unsigned char *end = src + from->size;
+	unsigned char *dst;
+
+	GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source);
+
+	/* verify that attribute path match worked as expected */
+	cl_assert_equal_i(
+		0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+	if (!from->size)
+		return 0;
+
+	cl_git_pass(git_buf_grow(to, from->size));
+
+	dst = (unsigned char *)to->ptr + from->size - 1;
+
+	while (src < end)
+		*dst-- = *src++;
+
+	to->size = from->size;
+
+	return 0;
+}
+
+static void reverse_filter_free(git_filter *f)
+{
+	git__free(f);
+}
+
+static git_filter *create_reverse_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->shutdown = reverse_filter_free;
+	filter->apply = reverse_filter_apply;
+
+	return filter;
+}
+
+static void register_custom_filters(void)
+{
+	static int filters_registered = 0;
+
+	if (!filters_registered) {
+		cl_git_pass(git_filter_register(
+			"bitflip", create_bitflip_filter(), BITFLIP_FILTER_PRIORITY));
+
+		cl_git_pass(git_filter_register(
+			"reverse", create_reverse_filter("+reverse"),
+			REVERSE_FILTER_PRIORITY));
+
+		/* re-register reverse filter with standard filter=xyz priority */
+		cl_git_pass(git_filter_register(
+			"pre-reverse",
+			create_reverse_filter("+prereverse"),
+			GIT_FILTER_DRIVER_PRIORITY));
+
+		filters_registered = 1;
+	}
+}
+
+
+void test_filter_custom__to_odb(void)
+{
+	git_filter_list *fl;
+	git_buf out = { 0 };
+	git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data));
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB));
+
+	cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+	cl_assert_equal_i(BITFLIPPED_AND_REVERSED_DATA_LEN, out.size);
+
+	cl_assert_equal_i(
+		0, memcmp(bitflipped_and_reversed_data, out.ptr, out.size));
+
+	git_filter_list_free(fl);
+	git_buf_free(&out);
+}
+
+void test_filter_custom__to_workdir(void)
+{
+	git_filter_list *fl;
+	git_buf out = { 0 };
+	git_buf in = GIT_BUF_INIT_CONST(
+		bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN);
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+
+	cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in));
+
+	cl_assert_equal_i(strlen(workdir_data), out.size);
+
+	cl_assert_equal_i(
+		0, memcmp(workdir_data, out.ptr, out.size));
+
+	git_filter_list_free(fl);
+	git_buf_free(&out);
+}
+
+void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
+{
+	git_filter_list *fl;
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE));
+	/* expect: bitflip, reverse, crlf */
+	cl_assert_equal_sz(3, git_filter_list_length(fl));
+	git_filter_list_free(fl);
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE));
+	/* expect: bitflip, reverse - possibly crlf depending on global config */
+	{
+		size_t flen = git_filter_list_length(fl);
+		cl_assert(flen == 2 || flen == 3);
+	}
+	git_filter_list_free(fl);
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE));
+	/* expect: bitflip, reverse */
+	cl_assert_equal_sz(2, git_filter_list_length(fl));
+	git_filter_list_free(fl);
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE));
+	/* expect: bitflip (because of -reverse) */
+	cl_assert_equal_sz(1, git_filter_list_length(fl));
+	git_filter_list_free(fl);
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE));
+	/* expect: none */
+	cl_assert_equal_sz(0, git_filter_list_length(fl));
+	git_filter_list_free(fl);
+}
+
+void test_filter_custom__order_dependency(void)
+{
+	git_index *index;
+	git_blob *blob;
+	git_buf buf = { 0 };
+
+	/* so if ident and reverse are used together, an interesting thing
+	 * happens - a reversed "$Id$" string is no longer going to trigger
+	 * ident correctly.  When checking out, the filters should be applied
+	 * in order CLRF, then ident, then reverse, so ident expansion should
+	 * work correctly.  On check in, the content should be reversed, then
+	 * ident, then CRLF filtered.  Let's make sure that works...
+	 */
+
+	cl_git_mkfile(
+		"empty_standard_repo/.gitattributes",
+		"hero.*.rev-ident text ident prereverse eol=lf\n");
+
+	cl_git_mkfile(
+		"empty_standard_repo/hero.1.rev-ident",
+		"This is a test\n$Id$\nHave fun!\n");
+
+	cl_git_mkfile(
+		"empty_standard_repo/hero.2.rev-ident",
+		"Another test\n$dI$\nCrazy!\n");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(git_index_add_bypath(index, "hero.1.rev-ident"));
+	cl_git_pass(git_index_add_bypath(index, "hero.2.rev-ident"));
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Filter chains\n");
+	git_index_free(index);
+
+	cl_git_pass(git_blob_lookup(&blob, g_repo,
+		& git_index_get_bypath(index, "hero.1.rev-ident", 0)->oid));
+	cl_assert_equal_s(
+		"\n!nuf evaH\n$dI$\ntset a si sihT", git_blob_rawcontent(blob));
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.1.rev-ident", 0));
+	/* no expansion because id was reversed at checkin and now at ident
+	 * time, reverse is not applied yet */
+	cl_assert_equal_s(
+		"This is a test\n$Id$\nHave fun!\n", buf.ptr);
+	git_blob_free(blob);
+
+	cl_git_pass(git_blob_lookup(&blob, g_repo,
+		& git_index_get_bypath(index, "hero.2.rev-ident", 0)->oid));
+	cl_assert_equal_s(
+		"\n!yzarC\n$Id$\ntset rehtonA", git_blob_rawcontent(blob));
+	cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.2.rev-ident", 0));
+	/* expansion because reverse was applied at checkin and at ident time,
+	 * reverse is not applied yet */
+	cl_assert_equal_s(
+		"Another test\n$59001fe193103b1016b27027c0c827d036fd0ac8 :dI$\nCrazy!\n", buf.ptr);
+	cl_assert_equal_i(0, git_oid_strcmp(
+		git_blob_id(blob), "8ca0df630d728c0c72072b6101b301391ef10095"));
+	git_blob_free(blob);
+
+	git_buf_free(&buf);
+}
+
+void test_filter_custom__filter_registry_failure_cases(void)
+{
+	git_filter fake = { GIT_FILTER_VERSION, 0 };
+
+	cl_assert_equal_i(GIT_EEXISTS, git_filter_register("bitflip", &fake, 0));
+
+	cl_git_fail(git_filter_unregister(GIT_FILTER_CRLF));
+	cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT));
+	cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter"));
+}
diff --git a/tests/filter/ident.c b/tests/filter/ident.c
new file mode 100644
index 0000000..2c8e6ab
--- /dev/null
+++ b/tests/filter/ident.c
@@ -0,0 +1,131 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_ident__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("crlf");
+}
+
+void test_filter_ident__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+static void add_blob_and_filter(
+	const char *data,
+	git_filter_list *fl,
+	const char *expected)
+{
+	git_oid id;
+	git_blob *blob;
+	git_buf out = { 0 };
+
+	cl_git_mkfile("crlf/identtest", data);
+	cl_git_pass(git_blob_create_fromworkdir(&id, g_repo, "identtest"));
+	cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+
+	cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+
+	cl_assert_equal_s(expected, out.ptr);
+
+	git_blob_free(blob);
+	git_buf_free(&out);
+}
+
+void test_filter_ident__to_worktree(void)
+{
+	git_filter_list *fl;
+	git_filter *ident;
+
+	cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE));
+
+	ident = git_filter_lookup(GIT_FILTER_IDENT);
+	cl_assert(ident != NULL);
+
+	cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+	add_blob_and_filter(
+		"Hello\n$Id$\nFun stuff\n", fl,
+		"Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30$\nFun stuff\n");
+	add_blob_and_filter(
+		"Hello\n$Id: Junky$\nFun stuff\n", fl,
+		"Hello\n$Id: 45cd107a7102911cb2a7df08404674327fa050b9$\nFun stuff\n");
+	add_blob_and_filter(
+		"$Id$\nAt the start\n", fl,
+		"$Id: b13415c767abc196fb95bd17070e8c1113e32160$\nAt the start\n");
+	add_blob_and_filter(
+		"At the end\n$Id$", fl,
+		"At the end\n$Id: 1344925c6bc65b34c5a7b50f86bf688e48e9a272$");
+	add_blob_and_filter(
+		"$Id$", fl,
+		"$Id: b3f5ebfb5843bc43ceecff6d4f26bb37c615beb1$");
+	add_blob_and_filter(
+		"$Id: Some sort of junk goes here$", fl,
+		"$Id: ab2dd3853c7c9a4bff55aca2bea077a73c32ac06$");
+
+	add_blob_and_filter("$Id: ", fl, "$Id: ");
+	add_blob_and_filter("$Id", fl, "$Id");
+	add_blob_and_filter("$I", fl, "$I");
+	add_blob_and_filter("Id$", fl, "Id$");
+
+	git_filter_list_free(fl);
+}
+
+void test_filter_ident__to_odb(void)
+{
+	git_filter_list *fl;
+	git_filter *ident;
+
+	cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB));
+
+	ident = git_filter_lookup(GIT_FILTER_IDENT);
+	cl_assert(ident != NULL);
+
+	cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+	add_blob_and_filter(
+		"Hello\n$Id$\nFun stuff\n",
+		fl, "Hello\n$Id$\nFun stuff\n");
+	add_blob_and_filter(
+		"Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30$\nFun stuff\n",
+		fl, "Hello\n$Id$\nFun stuff\n");
+	add_blob_and_filter(
+		"Hello\n$Id: Any junk you may have left here$\nFun stuff\n",
+		fl, "Hello\n$Id$\nFun stuff\n");
+	add_blob_and_filter(
+		"Hello\n$Id:$\nFun stuff\n",
+		fl, "Hello\n$Id$\nFun stuff\n");
+	add_blob_and_filter(
+		"Hello\n$Id:x$\nFun stuff\n",
+		fl, "Hello\n$Id$\nFun stuff\n");
+
+	add_blob_and_filter(
+		"$Id$\nAt the start\n", fl, "$Id$\nAt the start\n");
+	add_blob_and_filter(
+		"$Id: lots of random text that should be removed from here$\nAt the start\n", fl, "$Id$\nAt the start\n");
+	add_blob_and_filter(
+		"$Id: lots of random text that should not be removed without a terminator\nAt the start\n", fl, "$Id: lots of random text that should not be removed without a terminator\nAt the start\n");
+
+	add_blob_and_filter(
+		"At the end\n$Id$", fl, "At the end\n$Id$");
+	add_blob_and_filter(
+		"At the end\n$Id:$", fl, "At the end\n$Id$");
+	add_blob_and_filter(
+		"At the end\n$Id:asdfasdf$", fl, "At the end\n$Id$");
+	add_blob_and_filter(
+		"At the end\n$Id", fl, "At the end\n$Id");
+	add_blob_and_filter(
+		"At the end\n$IddI", fl, "At the end\n$IddI");
+
+	add_blob_and_filter("$Id$", fl, "$Id$");
+	add_blob_and_filter("$Id: any$", fl, "$Id$");
+	add_blob_and_filter("$Id: any long stuff goes here you see$", fl, "$Id$");
+	add_blob_and_filter("$Id: ", fl, "$Id: ");
+	add_blob_and_filter("$Id", fl, "$Id");
+	add_blob_and_filter("$I", fl, "$I");
+	add_blob_and_filter("Id$", fl, "Id$");
+
+	git_filter_list_free(fl);
+}
diff --git a/tests-clar/generate.py b/tests/generate.py
similarity index 100%
rename from tests-clar/generate.py
rename to tests/generate.py
diff --git a/tests-clar/index/addall.c b/tests/index/addall.c
similarity index 81%
rename from tests-clar/index/addall.c
rename to tests/index/addall.c
index fca6e77..44c5127 100644
--- a/tests-clar/index/addall.c
+++ b/tests/index/addall.c
@@ -1,6 +1,7 @@
 #include "clar_libgit2.h"
 #include "../status/status_helpers.h"
 #include "posix.h"
+#include "fileops.h"
 
 git_repository *g_repo = NULL;
 
@@ -67,10 +68,11 @@
 	return 0;
 }
 
-static void check_status(
+static void check_status_at_line(
 	git_repository *repo,
 	size_t index_adds, size_t index_dels, size_t index_mods,
-	size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores)
+	size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores,
+	const char *file, int line)
 {
 	index_status_counts vals;
 
@@ -78,15 +80,25 @@
 
 	cl_git_pass(git_status_foreach(repo, index_status_cb, &vals));
 
-	cl_assert_equal_sz(index_adds, vals.index_adds);
-	cl_assert_equal_sz(index_dels, vals.index_dels);
-	cl_assert_equal_sz(index_mods, vals.index_mods);
-	cl_assert_equal_sz(wt_adds, vals.wt_adds);
-	cl_assert_equal_sz(wt_dels, vals.wt_dels);
-	cl_assert_equal_sz(wt_mods, vals.wt_mods);
-	cl_assert_equal_sz(ignores, vals.ignores);
+	clar__assert_equal(
+		file,line,"wrong index adds", 1, "%"PRIuZ, index_adds, vals.index_adds);
+	clar__assert_equal(
+		file,line,"wrong index dels", 1, "%"PRIuZ, index_dels, vals.index_dels);
+	clar__assert_equal(
+		file,line,"wrong index mods", 1, "%"PRIuZ, index_mods, vals.index_mods);
+	clar__assert_equal(
+		file,line,"wrong workdir adds", 1, "%"PRIuZ, wt_adds, vals.wt_adds);
+	clar__assert_equal(
+		file,line,"wrong workdir dels", 1, "%"PRIuZ, wt_dels, vals.wt_dels);
+	clar__assert_equal(
+		file,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods);
+	clar__assert_equal(
+		file,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores);
 }
 
+#define check_status(R,IA,ID,IM,WA,WD,WM,IG) \
+	check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,__FILE__,__LINE__)
+
 static void check_stat_data(git_index *index, const char *path, bool match)
 {
 	const git_index_entry *entry;
@@ -108,8 +120,11 @@
 		cl_assert(st.st_size == entry->file_size);
 		cl_assert(st.st_uid  == entry->uid);
 		cl_assert(st.st_gid  == entry->gid);
-		cl_assert_equal_b(st.st_mode & ~0777, entry->mode & ~0777);
-		cl_assert_equal_b(st.st_mode &  0111, entry->mode &  0111);
+		cl_assert_equal_i_fmt(
+			GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
+		if (cl_is_chmod_supported())
+			cl_assert_equal_b(
+				GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
 	} else {
 		/* most things will still match */
 		cl_assert(st.st_size != entry->file_size);
@@ -117,37 +132,6 @@
 	}
 }
 
-static void commit_index_to_head(
-	git_repository *repo,
-	const char *commit_message)
-{
-	git_index *index;
-	git_oid tree_id, commit_id;
-	git_tree *tree;
-	git_signature *sig;
-	git_commit *parent = NULL;
-
-	git_revparse_single((git_object **)&parent, repo, "HEAD");
-	/* it is okay if looking up the HEAD fails */
-
-	cl_git_pass(git_repository_index(&index, repo));
-	cl_git_pass(git_index_write_tree(&tree_id, index));
-	cl_git_pass(git_index_write(index)); /* not needed, but might as well */
-	git_index_free(index);
-
-	cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
-
-	cl_git_pass(git_signature_now(&sig, "Testy McTester", "tt@tester.test"));
-
-	cl_git_pass(git_commit_create_v(
-		&commit_id, repo, "HEAD", sig, sig,
-		NULL, commit_message, tree, parent ? 1 : 0, parent));
-
-	git_commit_free(parent);
-	git_tree_free(tree);
-	git_signature_free(sig);
-}
-
 void test_index_addall__repo_lifecycle(void)
 {
 	int error;
@@ -194,7 +178,7 @@
 	check_stat_data(index, "addall/file.zzz", true);
 	check_status(g_repo, 2, 0, 0, 3, 0, 0, 1);
 
-	commit_index_to_head(g_repo, "first commit");
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit");
 	check_status(g_repo, 0, 0, 0, 3, 0, 0, 1);
 
 	/* attempt to add an ignored file - does nothing */
@@ -241,7 +225,7 @@
 	cl_git_pass(git_index_add_bypath(index, "file.zzz"));
 	check_status(g_repo, 1, 0, 1, 3, 0, 0, 0);
 
-	commit_index_to_head(g_repo, "second commit");
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit");
 	check_status(g_repo, 0, 0, 0, 3, 0, 0, 0);
 
 	cl_must_pass(p_unlink("addall/file.zzz"));
diff --git a/tests-clar/index/conflicts.c b/tests/index/conflicts.c
similarity index 100%
rename from tests-clar/index/conflicts.c
rename to tests/index/conflicts.c
diff --git a/tests-clar/index/filemodes.c b/tests/index/filemodes.c
similarity index 82%
rename from tests-clar/index/filemodes.c
rename to tests/index/filemodes.c
index e56a9c0..0139326 100644
--- a/tests-clar/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -44,30 +44,36 @@
 
 	cl_git_pass(p_rename(path.ptr, backup));
 	cl_git_write2file(
-		path.ptr, content.ptr, O_WRONLY|O_CREAT|O_TRUNC, create_mode);
+		path.ptr, content.ptr, content.size,
+		O_WRONLY|O_CREAT|O_TRUNC, create_mode);
 
 	git_buf_free(&path);
 	git_buf_free(&content);
 }
 
-static void add_and_check_mode(
-	git_index *index, const char *filename, unsigned int expect_mode)
+#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__LINE__)
+
+static void add_and_check_mode_(
+	git_index *index, const char *filename, unsigned int expect_mode,
+	const char *file, int line)
 {
 	size_t pos;
 	const git_index_entry *entry;
 
 	cl_git_pass(git_index_add_bypath(index, filename));
 
-	cl_assert(!git_index_find(&pos, index, filename));
+	clar__assert(!git_index_find(&pos, index, filename),
+		file, line, "Cannot find index entry", NULL, 1);
 
 	entry = git_index_get_byindex(index, pos);
-	cl_assert(entry->mode == expect_mode);
+
+	clar__assert_equal(file, line, "Expected mode does not match index",
+		1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
 }
 
 void test_index_filemodes__untrusted(void)
 {
 	git_index *index;
-	bool can_filemode = cl_is_chmod_supported();
 
 	cl_repo_set_bool(g_repo, "core.filemode", false);
 
@@ -91,19 +97,14 @@
 	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
 
 	/*  5 - add new 0644 -> expect 0644 */
-	cl_git_write2file("filemodes/new_off", "blah",
+	cl_git_write2file("filemodes/new_off", "blah", 0,
 		O_WRONLY | O_CREAT | O_TRUNC, 0644);
 	add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
 
-	/* this test won't give predictable results on a platform
-	 * that doesn't support filemodes correctly, so skip it.
-	 */
-	if (can_filemode) {
-		/* 6 - add 0755 -> expect 0755 */
-		cl_git_write2file("filemodes/new_on", "blah",
-			O_WRONLY | O_CREAT | O_TRUNC, 0755);
-		add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
-	}
+	/* 6 - add new 0755 -> expect 0644 if core.filemode == false */
+	cl_git_write2file("filemodes/new_on", "blah", 0,
+		O_WRONLY | O_CREAT | O_TRUNC, 0755);
+	add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
 
 	git_index_free(index);
 }
@@ -140,12 +141,12 @@
 	add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
 
 	/*  5 - add new 0644 -> expect 0644 */
-	cl_git_write2file("filemodes/new_off", "blah",
+	cl_git_write2file("filemodes/new_off", "blah", 0,
 		O_WRONLY | O_CREAT | O_TRUNC, 0644);
 	add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
 
 	/* 6 - add 0755 -> expect 0755 */
-	cl_git_write2file("filemodes/new_on", "blah",
+	cl_git_write2file("filemodes/new_on", "blah", 0,
 		O_WRONLY | O_CREAT | O_TRUNC, 0755);
 	add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
 
diff --git a/tests-clar/index/inmemory.c b/tests/index/inmemory.c
similarity index 100%
rename from tests-clar/index/inmemory.c
rename to tests/index/inmemory.c
diff --git a/tests-clar/index/names.c b/tests/index/names.c
similarity index 60%
rename from tests-clar/index/names.c
rename to tests/index/names.c
index 95a560e..9007b1b 100644
--- a/tests-clar/index/names.c
+++ b/tests/index/names.c
@@ -63,7 +63,7 @@
 	git_index_clear(repo_index);
 	cl_assert(git_index_name_entrycount(repo_index) == 0);
 
-	cl_git_pass(git_index_read(repo_index));
+	cl_git_pass(git_index_read(repo_index, true));
 	cl_assert(git_index_name_entrycount(repo_index) == 3);
 
 	conflict_name = git_index_name_get_byindex(repo_index, 0);
@@ -80,5 +80,69 @@
 	cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
 	cl_assert(conflict_name->ours == NULL);
 	cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
 
+void test_index_names__cleaned_on_reset_hard(void)
+{
+	git_object *target;
+
+	retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+	test_index_names__add();
+	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD));
+	cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+	git_object_free(target);
+}
+
+void test_index_names__cleaned_on_reset_mixed(void)
+{
+	git_object *target;
+
+	retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876");
+
+	test_index_names__add();
+	cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED));
+	cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+	git_object_free(target);
+}
+
+void test_index_names__cleaned_on_checkout_tree(void)
+{
+	git_oid oid;
+	git_object *obj;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+	test_index_names__add();
+	git_reference_name_to_id(&oid, repo, "refs/heads/master");
+	git_object_lookup(&obj, repo, &oid, GIT_OBJ_ANY);
+	git_checkout_tree(repo, obj, &opts);
+	cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+
+	git_object_free(obj);
+}
+
+void test_index_names__cleaned_on_checkout_head(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+	test_index_names__add();
+	git_checkout_head(repo, &opts);
+	cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+}
+
+void test_index_names__retained_on_checkout_index(void)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+	test_index_names__add();
+	git_checkout_index(repo, repo_index, &opts);
+	cl_assert(git_index_name_entrycount(repo_index) > 0);
 }
diff --git a/tests-clar/index/read_tree.c b/tests/index/read_tree.c
similarity index 100%
rename from tests-clar/index/read_tree.c
rename to tests/index/read_tree.c
diff --git a/tests-clar/index/rename.c b/tests/index/rename.c
similarity index 100%
rename from tests-clar/index/rename.c
rename to tests/index/rename.c
diff --git a/tests-clar/index/reuc.c b/tests/index/reuc.c
similarity index 99%
rename from tests-clar/index/reuc.c
rename to tests/index/reuc.c
index 69ed4a9..a18d560 100644
--- a/tests-clar/index/reuc.c
+++ b/tests/index/reuc.c
@@ -276,8 +276,6 @@
 		0100644, &their_oid));
 
 	cl_git_pass(git_index_write(repo_index));
-
-	cl_git_pass(git_index_read(repo_index));
 	cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
 
 	/* ensure sort order was round-tripped correct */
diff --git a/tests-clar/index/stage.c b/tests/index/stage.c
similarity index 100%
rename from tests-clar/index/stage.c
rename to tests/index/stage.c
diff --git a/tests-clar/index/tests.c b/tests/index/tests.c
similarity index 76%
rename from tests-clar/index/tests.c
rename to tests/index/tests.c
index 1bc5e6a..5a05bd8 100644
--- a/tests-clar/index/tests.c
+++ b/tests/index/tests.c
@@ -6,9 +6,10 @@
 #define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
 #define TEST_INDEX2_PATH cl_fixture("gitgit.index")
 #define TEST_INDEXBIG_PATH cl_fixture("big.index")
+#define TEST_INDEXBAD_PATH cl_fixture("bad.index")
 
 
-// Suite data
+/* Suite data */
 struct test_entry {
    size_t index;
    char path[128];
@@ -24,7 +25,7 @@
    {48, "src/revobject.h", 1448, 0x4C3F7FE2}
 };
 
-// Helpers
+/* Helpers */
 static void copy_file(const char *src, const char *dst)
 {
 	git_buf source_buf = GIT_BUF_INIT;
@@ -32,7 +33,7 @@
 
 	cl_git_pass(git_futils_readbuffer(&source_buf, src));
 
-	dst_fd = git_futils_creat_withpath(dst, 0777, 0666); //-V536
+	dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
 	if (dst_fd < 0)
 		goto cleanup;
 
@@ -66,7 +67,7 @@
 }
 
 
-// Fixture setup and teardown
+/* Fixture setup and teardown */
 void test_index_tests__initialize(void)
 {
 }
@@ -99,11 +100,11 @@
    entries = (git_index_entry **)index->entries.contents;
 
    for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
-      git_index_entry *e = entries[test_entries[i].index];
+		git_index_entry *e = entries[test_entries[i].index];
 
-      cl_assert_equal_s(e->path, test_entries[i].path);
-      cl_assert(e->mtime.seconds == test_entries[i].mtime);
-      cl_assert(e->file_size == test_entries[i].file_size);
+		cl_assert_equal_s(e->path, test_entries[i].path);
+		cl_assert(e->mtime.seconds == test_entries[i].mtime);
+		cl_assert(e->file_size == test_entries[i].file_size);
    }
 
    git_index_free(index);
@@ -131,10 +132,10 @@
    cl_git_pass(git_index_open(&index, TEST_INDEX_PATH));
 
    for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
-	  size_t idx;
+		size_t idx;
 
-	  cl_assert(!git_index_find(&idx, index, test_entries[i].path));
-	  cl_assert(idx == test_entries[i].index);
+		cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+		cl_assert(idx == test_entries[i].index);
    }
 
    git_index_free(index);
@@ -148,7 +149,7 @@
    cl_git_pass(git_index_open(&index, "fake-index"));
 
    for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
-      cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+		cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
    }
 
    git_index_free(index);
@@ -173,7 +174,8 @@
 
 void test_index_tests__sort0(void)
 {
-   // sort the entires in an index
+	/* sort the entires in an index */
+
    /*
    * TODO: This no longer applies:
    * index sorting in Git uses some specific changes to the way
@@ -187,7 +189,7 @@
 
 void test_index_tests__sort1(void)
 {
-   // sort the entires in an empty index
+   /* sort the entires in an empty index */
    git_index *index;
 
    cl_git_pass(git_index_open(&index, "fake-index"));
@@ -223,9 +225,9 @@
 
 	/* Create a new file in the working directory */
 	cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
-	cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0));
+	cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
 	cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
-	cl_git_pass(git_filebuf_commit(&file, 0666));
+	cl_git_pass(git_filebuf_commit(&file));
 
 	/* Store the expected hash of the file/blob
 	 * This has been generated by executing the following
@@ -349,14 +351,14 @@
 	cl_git_pass(git_index_add_bypath(index, "hello"));
 	cl_git_pass(git_index_write(index));
 
-	cl_git_pass(git_index_read(index)); /* reload */
+	cl_git_pass(git_index_read(index, true)); /* reload */
 	cl_assert(git_index_entrycount(index) == 1);
 	cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
 
 	cl_git_pass(git_index_remove(index, "hello", 0));
 	cl_git_pass(git_index_write(index));
 
-	cl_git_pass(git_index_read(index)); /* reload */
+	cl_git_pass(git_index_read(index, true)); /* reload */
 	cl_assert(git_index_entrycount(index) == 0);
 	cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
 
@@ -388,7 +390,7 @@
 	cl_git_pass(git_index_add_bypath(index, "b.txt"));
 	cl_git_pass(git_index_write(index));
 
-	cl_git_pass(git_index_read(index)); /* reload */
+	cl_git_pass(git_index_read(index, true)); /* reload */
 	cl_assert_equal_i(4, (int)git_index_entrycount(index));
 	cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
 	cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
@@ -397,7 +399,7 @@
 	cl_git_pass(git_index_remove(index, "a/1.txt", 0));
 	cl_git_pass(git_index_write(index));
 
-	cl_git_pass(git_index_read(index)); /* reload */
+	cl_git_pass(git_index_read(index, true)); /* reload */
 	cl_assert_equal_i(3, (int)git_index_entrycount(index));
 	cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
 	cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
@@ -406,7 +408,7 @@
 	cl_git_pass(git_index_remove_directory(index, "a", 0));
 	cl_git_pass(git_index_write(index));
 
-	cl_git_pass(git_index_read(index)); /* reload */
+	cl_git_pass(git_index_read(index, true)); /* reload */
 	cl_assert_equal_i(1, (int)git_index_entrycount(index));
 	cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
 	cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
@@ -459,3 +461,85 @@
 	git_repository_free(repo);
 }
 
+void test_index_tests__elocked(void)
+{
+	git_repository *repo;
+	git_index *index;
+	git_filebuf file = GIT_FILEBUF_INIT;
+	const git_error *err;
+	int error;
+
+	cl_set_cleanup(&cleanup_myrepo, NULL);
+
+	cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+	cl_git_pass(git_repository_index(&index, repo));
+
+	/* Lock the index file so we fail to lock it */
+	cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+	error = git_index_write(index);
+	cl_assert_equal_i(GIT_ELOCKED, error);
+
+	err = giterr_last();
+	cl_assert_equal_i(err->klass, GITERR_INDEX);
+
+	git_filebuf_cleanup(&file);
+	git_index_free(index);
+	git_repository_free(repo);
+}
+
+void test_index_tests__reload_from_disk(void)
+{
+	git_repository *repo;
+	git_index *read_index;
+	git_index *write_index;
+
+	cl_set_cleanup(&cleanup_myrepo, NULL);
+
+	cl_git_pass(git_futils_mkdir("./myrepo", NULL, 0777, GIT_MKDIR_PATH));
+	cl_git_mkfile("./myrepo/a.txt", "a\n");
+	cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+	cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+	cl_git_pass(git_repository_index(&write_index, repo));
+	cl_assert_equal_i(false, write_index->on_disk);
+
+	cl_git_pass(git_index_open(&read_index, write_index->index_file_path));
+	cl_assert_equal_i(false, read_index->on_disk);
+
+	/* Stage two new files agaisnt the write_index */
+	cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+	cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+	cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+	/* Persist the index changes to disk */
+	cl_git_pass(git_index_write(write_index));
+	cl_assert_equal_i(true, write_index->on_disk);
+
+	/* Sync the changes back into the read_index */
+	cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+	cl_git_pass(git_index_read(read_index, true));
+	cl_assert_equal_i(true, read_index->on_disk);
+
+	cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+	/* Remove the index file from the filesystem */
+	cl_git_pass(p_unlink(write_index->index_file_path));
+
+	/* Sync the changes back into the read_index */
+	cl_git_pass(git_index_read(read_index, true));
+	cl_assert_equal_i(false, read_index->on_disk);
+	cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+	git_index_free(read_index);
+	git_index_free(write_index);
+	git_repository_free(repo);
+}
+
+void test_index_tests__corrupted_extension(void)
+{
+	git_index *index;
+
+	cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR);
+}
diff --git a/tests-clar/main.c b/tests/main.c
similarity index 100%
rename from tests-clar/main.c
rename to tests/main.c
diff --git a/tests-clar/merge/merge_helpers.c b/tests/merge/merge_helpers.c
similarity index 89%
rename from tests-clar/merge/merge_helpers.c
rename to tests/merge/merge_helpers.c
index e409278..43619be 100644
--- a/tests-clar/merge/merge_helpers.c
+++ b/tests/merge/merge_helpers.c
@@ -52,6 +52,29 @@
 	return 0;
 }
 
+int merge_branches(git_merge_result **result, git_repository *repo, const char *ours_branch, const char *theirs_branch, git_merge_opts *opts)
+{
+	git_reference *head_ref, *theirs_ref;
+	git_merge_head *theirs_head;
+	git_checkout_opts head_checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+
+	head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+	cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1));
+	cl_git_pass(git_checkout_head(repo, &head_checkout_opts));
+
+	cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
+	cl_git_pass(git_merge_head_from_ref(&theirs_head, repo, theirs_ref));
+
+	cl_git_pass(git_merge(result, repo, (const git_merge_head **)&theirs_head, 1, opts));
+
+	git_reference_free(head_ref);
+	git_reference_free(theirs_ref);
+	git_merge_head_free(theirs_head);
+
+	return 0;
+}
+
 void merge__dump_index_entries(git_vector *index_entries)
 {
 	size_t i;
@@ -291,7 +314,7 @@
 	git_buf wd = GIT_BUF_INIT;
 
 	git_buf_puts(&wd, repo->workdir);
-	git_path_direach(&wd, dircount, &actual_len);
+	git_path_direach(&wd, 0, dircount, &actual_len);
 
 	if (actual_len != expected_len)
 		return 0;
diff --git a/tests-clar/merge/merge_helpers.h b/tests/merge/merge_helpers.h
similarity index 91%
rename from tests-clar/merge/merge_helpers.h
rename to tests/merge/merge_helpers.h
index cb718e0..ae32744 100644
--- a/tests-clar/merge/merge_helpers.h
+++ b/tests/merge/merge_helpers.h
@@ -44,6 +44,9 @@
 	const char *ours_name, const char *theirs_name,
 	git_merge_tree_opts *opts);
 
+int merge_branches(git_merge_result **result, git_repository *repo,
+	const char *ours_branch, const char *theirs_branch, git_merge_opts *opts);
+
 int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len);
 
 int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len);
diff --git a/tests-clar/merge/trees/automerge.c b/tests/merge/trees/automerge.c
similarity index 99%
rename from tests-clar/merge/trees/automerge.c
rename to tests/merge/trees/automerge.c
index 04a7bef..746ce50 100644
--- a/tests-clar/merge/trees/automerge.c
+++ b/tests/merge/trees/automerge.c
@@ -122,7 +122,7 @@
 	cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
 
 	cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB));
-	cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, entry->file_size) == 0);
+	cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0);
 
 	git_index_free(index);
 	git_blob_free(blob);
diff --git a/tests-clar/merge/trees/modeconflict.c b/tests/merge/trees/modeconflict.c
similarity index 100%
rename from tests-clar/merge/trees/modeconflict.c
rename to tests/merge/trees/modeconflict.c
diff --git a/tests-clar/merge/trees/renames.c b/tests/merge/trees/renames.c
similarity index 100%
rename from tests-clar/merge/trees/renames.c
rename to tests/merge/trees/renames.c
diff --git a/tests-clar/merge/trees/treediff.c b/tests/merge/trees/treediff.c
similarity index 100%
rename from tests-clar/merge/trees/treediff.c
rename to tests/merge/trees/treediff.c
diff --git a/tests-clar/merge/trees/trivial.c b/tests/merge/trees/trivial.c
similarity index 100%
rename from tests-clar/merge/trees/trivial.c
rename to tests/merge/trees/trivial.c
diff --git a/tests/merge/workdir/fastforward.c b/tests/merge/workdir/fastforward.c
new file mode 100644
index 0000000..861f383
--- /dev/null
+++ b/tests/merge/workdir/fastforward.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_FASTFORWARD_BRANCH	"ff_branch"
+#define THEIRS_FASTFORWARD_OID		"fd89f8cffb663ac89095a0f9764902e93ceaca6a"
+
+#define THEIRS_NOFASTFORWARD_BRANCH	"branch"
+#define THEIRS_NOFASTFORWARD_OID	"7cb63eed597130ba4abb87b3e544b85021905520"
+
+
+// Fixture setup and teardown
+void test_merge_workdir_fastforward__initialize(void)
+{
+	repo = cl_git_sandbox_init(TEST_REPO_PATH);
+	git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_fastforward__cleanup(void)
+{
+	git_index_free(repo_index);
+	cl_git_sandbox_cleanup();
+}
+
+static git_merge_result *merge_fastforward_branch(int flags)
+{
+	git_reference *their_ref;
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	opts.merge_flags = flags;
+
+	cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_FASTFORWARD_BRANCH));
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	git_merge_head_free(their_heads[0]);
+	git_reference_free(their_ref);
+
+	return result;
+}
+
+void test_merge_workdir_fastforward__fastforward(void)
+{
+	git_merge_result *result;
+	git_oid expected, ff_oid;
+
+	cl_git_pass(git_oid_fromstr(&expected, THEIRS_FASTFORWARD_OID));
+
+	cl_assert(result = merge_fastforward_branch(0));
+	cl_assert(git_merge_result_is_fastforward(result));
+	cl_git_pass(git_merge_result_fastforward_oid(&ff_oid, result));
+	cl_assert(git_oid_cmp(&ff_oid, &expected) == 0);
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__fastforward_only(void)
+{
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+	git_reference *their_ref;
+	git_merge_head *their_head;
+	int error;
+
+	opts.merge_flags = GIT_MERGE_FASTFORWARD_ONLY;
+
+	cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_NOFASTFORWARD_BRANCH));
+	cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref));
+
+	cl_git_fail((error = git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts)));
+	cl_assert(error == GIT_ENONFASTFORWARD);
+
+	git_merge_head_free(their_head);
+	git_reference_free(their_ref);
+}
+
+void test_merge_workdir_fastforward__no_fastforward(void)
+{
+	git_merge_result *result;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+		{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
+		{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+		{ 0100644, "bd9cb4cd0a770cb9adcb5fce212142ef40ea1c35", 0, "changed-in-master.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+		{ 0100644, "364bbe4ce80c7bd31e6307dce77d46e3e1759fb3", 0, "new-in-ff.txt" },
+		{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+		{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+	};
+
+	cl_assert(result = merge_fastforward_branch(GIT_MERGE_NO_FASTFORWARD));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__uptodate(void)
+{
+	git_reference *their_ref;
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+
+	cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_HEAD_FILE));
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
+
+	cl_assert(git_merge_result_is_uptodate(result));
+
+	git_merge_head_free(their_heads[0]);
+	git_reference_free(their_ref);
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_fastforward__uptodate_merging_prev_commit(void)
+{
+	git_oid their_oid;
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+
+	cl_git_pass(git_oid_fromstr(&their_oid, "c607fc30883e335def28cd686b51f6cfa02b06ec"));
+	cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oid));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL));
+
+	cl_assert(git_merge_result_is_uptodate(result));
+
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
+
diff --git a/tests/merge/workdir/renames.c b/tests/merge/workdir/renames.c
new file mode 100644
index 0000000..d383979
--- /dev/null
+++ b/tests/merge/workdir/renames.c
@@ -0,0 +1,156 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "buffer.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "fileops.h"
+#include "refs.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define BRANCH_RENAME_OURS					"rename_conflict_ours"
+#define BRANCH_RENAME_THEIRS				"rename_conflict_theirs"
+
+// Fixture setup and teardown
+void test_merge_workdir_renames__initialize(void)
+{
+	repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_workdir_renames__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_merge_workdir_renames__renames(void)
+{
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+		{ 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+		{ 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+		{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+		{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+		{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+		{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+		{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+		{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+		{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+		{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+	};
+
+	opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+	opts.merge_tree_opts.rename_threshold = 50;
+
+	cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+	cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_renames__ours(void)
+{
+	git_index *index;
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+		{ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+		{ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+		{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+		{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+		{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+		{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt" },
+		{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-renamed-in-theirs-added-in-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed-side-2.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" },
+	};
+
+	opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+	opts.merge_tree_opts.rename_threshold = 50;
+	opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+	cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_index_write(index));
+	cl_assert(merge_test_workdir(repo, merge_index_entries, 20));
+
+	git_merge_result_free(result);
+	git_index_free(index);
+}
+
+void test_merge_workdir_renames__similar(void)
+{
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	/*
+	 * Note: this differs slightly from the core git merge result - there, 4a is
+	 * tracked as a rename/delete instead of a rename/add and the theirs side
+	 * is not placed in workdir in any form.
+	 */
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+		{ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+		{ 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+		{ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+		{ 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+		{ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+		{ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+		{ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+		{ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+		{ 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+		{ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+		{ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+		{ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+		{ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+		{ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+		{ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+		{ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+		{ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+		{ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+		{ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+		{ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+		{ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+		{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+	};
+
+	opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES;
+	opts.merge_tree_opts.rename_threshold = 50;
+
+	cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts));
+	cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+
+	git_merge_result_free(result);
+}
+
diff --git a/tests-clar/merge/workdir/setup.c b/tests/merge/workdir/setup.c
similarity index 91%
rename from tests-clar/merge/workdir/setup.c
rename to tests/merge/workdir/setup.c
index 1c84032..870d55e 100644
--- a/tests-clar/merge/workdir/setup.c
+++ b/tests/merge/workdir/setup.c
@@ -71,7 +71,7 @@
 	git_buf_free(&file_path_buf);
 }
 
-/* git merge --no-ff octo1 */
+/* git merge octo1 */
 void test_merge_workdir_setup__one_branch(void)
 {
 	git_oid our_oid;
@@ -97,7 +97,33 @@
 	git_merge_head_free(their_heads[0]);
 }
 
-/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */
+/* git merge --no-ff octo1 */
+void test_merge_workdir_setup__no_fastforward(void)
+{
+	git_oid our_oid;
+	git_reference *octo1_ref;
+	git_merge_head *our_head, *their_heads[1];
+
+	cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+	cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+	cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+	cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, GIT_MERGE_NO_FASTFORWARD));
+
+	cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+	cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+	cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+	cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+	git_reference_free(octo1_ref);
+
+	git_merge_head_free(our_head);
+	git_merge_head_free(their_heads[0]);
+}
+
+/* git merge  16f825815cfd20a07a75c71554e82d8eede0b061 */
 void test_merge_workdir_setup__one_oid(void)
 {
 	git_oid our_oid;
@@ -964,3 +990,68 @@
 
 	cl_assert(cb_data.i == cb_data.len);
 }
+
+void test_merge_workdir_setup__retained_after_success(void)
+{
+	git_oid our_oid;
+	git_reference *octo1_ref;
+	git_merge_head *our_head, *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
+
+	cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+	cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+	cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
+
+	cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+	cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+	cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+	cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+	git_reference_free(octo1_ref);
+
+	git_merge_head_free(our_head);
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_setup__removed_after_failure(void)
+{
+	git_oid our_oid;
+	git_reference *octo1_ref;
+	git_merge_head *our_head, *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD;
+
+	cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD));
+	cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid));
+
+	cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref));
+
+	cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
+		"Conflicting file!\n\nMerge will fail!\n");
+
+	cl_git_fail(git_merge(
+		&result, repo, (const git_merge_head **)&their_heads[0], 1, &opts));
+
+	cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE));
+	cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE));
+	cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MODE_FILE));
+	cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_MSG_FILE));
+
+	git_reference_free(octo1_ref);
+
+	git_merge_head_free(our_head);
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
diff --git a/tests/merge/workdir/simple.c b/tests/merge/workdir/simple.c
new file mode 100644
index 0000000..4a3b86e
--- /dev/null
+++ b/tests/merge/workdir/simple.c
@@ -0,0 +1,491 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "buffer.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_SIMPLE_BRANCH		"branch"
+#define THEIRS_SIMPLE_OID			"7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define THEIRS_UNRELATED_BRANCH		"unrelated"
+#define THEIRS_UNRELATED_OID		"55b4e4687e7a0d9ca367016ed930f385d4022e6f"
+#define THEIRS_UNRELATED_PARENT		"d6cf6c7741b3316826af1314042550c97ded1d50"
+
+#define OURS_DIRECTORY_FILE			"df_side1"
+#define THEIRS_DIRECTORY_FILE		"fc90237dc4891fa6c69827fc465632225e391618"
+
+
+/* Non-conflicting files, index entries are common to every merge operation */
+#define ADDED_IN_MASTER_INDEX_ENTRY	\
+	{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, \
+	  "added-in-master.txt" }
+#define AUTOMERGEABLE_INDEX_ENTRY \
+	{ 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, \
+	  "automergeable.txt" }
+#define CHANGED_IN_BRANCH_INDEX_ENTRY \
+	{ 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, \
+	  "changed-in-branch.txt" }
+#define CHANGED_IN_MASTER_INDEX_ENTRY \
+	{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, \
+	  "changed-in-master.txt" }
+#define UNCHANGED_INDEX_ENTRY \
+	{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, \
+	  "unchanged.txt" }
+
+/* Unrelated files */
+#define UNRELATED_NEW1 \
+	{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, \
+	  "new-in-unrelated1.txt" }
+#define UNRELATED_NEW2 \
+	{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, \
+	  "new-in-unrelated2.txt" }
+
+/* Expected REUC entries */
+#define AUTOMERGEABLE_REUC_ENTRY \
+	{ "automergeable.txt", 0100644, 0100644, 0100644, \
+	  "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+	  "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+	  "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
+#define CONFLICTING_REUC_ENTRY \
+	{ "conflicting.txt", 0100644, 0100644, 0100644, \
+	  "d427e0b2e138501a3d15cc376077a3631e15bd46", \
+	  "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
+	  "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
+#define REMOVED_IN_BRANCH_REUC_ENTRY \
+	{ "removed-in-branch.txt", 0100644, 0100644, 0, \
+	  "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+	  "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+	  "" }
+#define REMOVED_IN_MASTER_REUC_ENTRY \
+	{ "removed-in-master.txt", 0100644, 0, 0100644, \
+	  "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+	  "", \
+	  "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
+
+#define AUTOMERGEABLE_MERGED_FILE \
+	"this file is changed in master\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is automergeable\n" \
+	"this file is changed in branch\n"
+
+#define AUTOMERGEABLE_MERGED_FILE_CRLF \
+	"this file is changed in master\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is automergeable\r\n" \
+	"this file is changed in branch\r\n"
+
+#define CONFLICTING_DIFF3_FILE \
+	"<<<<<<< HEAD\n" \
+	"this file is changed in master and branch\n" \
+	"=======\n" \
+	"this file is changed in branch and master\n" \
+	">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
+
+// Fixture setup and teardown
+void test_merge_workdir_simple__initialize(void)
+{
+	repo = cl_git_sandbox_init(TEST_REPO_PATH);
+	git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_simple__cleanup(void)
+{
+	git_index_free(repo_index);
+	cl_git_sandbox_cleanup();
+}
+
+static git_merge_result *merge_simple_branch(int automerge_flags, int checkout_strategy)
+{
+	git_oid their_oids[1];
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_SIMPLE_OID));
+	cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+	opts.merge_tree_opts.automerge_flags = automerge_flags;
+	opts.checkout_opts.checkout_strategy = checkout_strategy;
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	git_merge_head_free(their_heads[0]);
+
+	return result;
+}
+
+static void set_core_autocrlf_to(git_repository *repo, bool value)
+{
+	git_config *cfg;
+
+	cl_git_pass(git_repository_config(&cfg, repo));
+	cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
+
+	git_config_free(cfg);
+}
+
+void test_merge_workdir_simple__automerge(void)
+{
+	git_index *index;
+	const git_index_entry *entry;
+	git_merge_result *result;
+	git_buf automergeable_buf = GIT_BUF_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+
+		{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+		{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY
+	};
+
+
+	set_core_autocrlf_to(repo, false);
+
+	cl_assert(result = merge_simple_branch(0, 0));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+		TEST_REPO_PATH "/automergeable.txt"));
+	cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE) == 0);
+	git_buf_free(&automergeable_buf);
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+	git_merge_result_free(result);
+
+	git_repository_index(&index, repo);
+
+	cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+	cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+	git_index_free(index);
+}
+
+void test_merge_workdir_simple__automerge_crlf(void)
+{
+#ifdef GIT_WIN32
+	git_index *index;
+	const git_index_entry *entry;
+
+	git_merge_result *result;
+	git_buf automergeable_buf = GIT_BUF_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+
+		{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+		{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY
+	};
+
+	set_core_autocrlf_to(repo, true);
+
+	cl_assert(result = merge_simple_branch(0, 0));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+		TEST_REPO_PATH "/automergeable.txt"));
+	cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE_CRLF) == 0);
+	git_buf_free(&automergeable_buf);
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+	git_merge_result_free(result);
+
+	git_repository_index(&index, repo);
+
+	cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+	cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE_CRLF));
+
+	git_index_free(index);
+#endif /* GIT_WIN32 */
+}
+
+void test_merge_workdir_simple__diff3(void)
+{
+	git_merge_result *result;
+	git_buf conflicting_buf = GIT_BUF_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+
+		{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+		{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY
+	};
+
+	cl_assert(result = merge_simple_branch(0, 0));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+		TEST_REPO_PATH "/conflicting.txt"));
+	cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0);
+	git_buf_free(&conflicting_buf);
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__checkout_ours(void)
+{
+	git_merge_result *result;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+
+		{ 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+		{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY
+	};
+
+	cl_assert(result = merge_simple_branch(0, GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+	cl_assert(git_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__favor_ours(void)
+{
+	git_merge_result *result;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		CONFLICTING_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY,
+	};
+
+	cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_OURS, 0));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__favor_theirs(void)
+{
+	git_merge_result *result;
+
+	struct merge_index_entry merge_index_entries[] = {
+		ADDED_IN_MASTER_INDEX_ENTRY,
+		AUTOMERGEABLE_INDEX_ENTRY,
+		CHANGED_IN_BRANCH_INDEX_ENTRY,
+		CHANGED_IN_MASTER_INDEX_ENTRY,
+		{ 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
+		UNCHANGED_INDEX_ENTRY,
+	};
+
+	struct merge_reuc_entry merge_reuc_entries[] = {
+		AUTOMERGEABLE_REUC_ENTRY,
+		CONFLICTING_REUC_ENTRY,
+		REMOVED_IN_BRANCH_REUC_ENTRY,
+		REMOVED_IN_MASTER_REUC_ENTRY,
+	};
+
+	cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_THEIRS, 0));
+	cl_assert(!git_merge_result_is_fastforward(result));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+	cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__directory_file(void)
+{
+	git_reference *head;
+	git_oid their_oids[1], head_commit_id;
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+	git_commit *head_commit;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+		{ 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+		{ 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+		{ 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+		{ 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+		{ 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+		{ 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+		{ 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+		{ 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+		{ 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+		{ 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+		{ 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+		{ 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+		{ 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+		{ 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+		{ 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+		{ 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+		{ 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+		{ 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+		{ 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+	};
+
+	cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1));
+	cl_git_pass(git_reference_name_to_id(&head_commit_id, repo, GIT_HEAD_FILE));
+	cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_id));
+	cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD));
+
+	cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_DIRECTORY_FILE));
+	cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+	opts.merge_tree_opts.automerge_flags = 0;
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 20));
+
+	git_reference_free(head);
+	git_commit_free(head_commit);
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__unrelated(void)
+{
+	git_oid their_oids[1];
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+		{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
+		{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+		{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+		{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+		{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+		{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+		{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+	};
+
+	cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_PARENT));
+	cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+	opts.merge_tree_opts.automerge_flags = 0;
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 9));
+
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
+
+void test_merge_workdir_simple__unrelated_with_conflicts(void)
+{
+	git_oid their_oids[1];
+	git_merge_head *their_heads[1];
+	git_merge_result *result;
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+
+	struct merge_index_entry merge_index_entries[] = {
+		{ 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+		{ 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+		{ 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+		{ 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+		{ 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+		{ 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+		{ 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+		{ 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+		{ 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+		{ 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+		{ 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+	};
+
+	cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_OID));
+	cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0]));
+
+	opts.merge_tree_opts.automerge_flags = 0;
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	cl_assert(merge_test_index(repo_index, merge_index_entries, 11));
+
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+}
+
diff --git a/tests/merge/workdir/trivial.c b/tests/merge/workdir/trivial.c
new file mode 100644
index 0000000..9f95662
--- /dev/null
+++ b/tests/merge/workdir/trivial.c
@@ -0,0 +1,341 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "fileops.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+
+// Fixture setup and teardown
+void test_merge_workdir_trivial__initialize(void)
+{
+	repo = cl_git_sandbox_init(TEST_REPO_PATH);
+	git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_trivial__cleanup(void)
+{
+	git_index_free(repo_index);
+	cl_git_sandbox_cleanup();
+}
+
+
+static int merge_trivial(const char *ours, const char *theirs, bool automerge)
+{
+	git_buf branch_buf = GIT_BUF_INIT;
+	git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+	git_reference *our_ref, *their_ref;
+	git_merge_head *their_heads[1];
+	git_merge_opts opts = GIT_MERGE_OPTS_INIT;
+	git_merge_result *result;
+
+	checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+	opts.merge_tree_opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE;
+
+	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
+	cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1));
+
+	cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+	git_buf_clear(&branch_buf);
+	git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs);
+	cl_git_pass(git_reference_lookup(&their_ref, repo, branch_buf.ptr));
+	cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref));
+
+	cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts));
+
+	git_buf_free(&branch_buf);
+	git_reference_free(our_ref);
+	git_reference_free(their_ref);
+	git_merge_head_free(their_heads[0]);
+	git_merge_result_free(result);
+
+	return 0;
+}
+
+static size_t merge_trivial_conflict_entrycount(void)
+{
+	const git_index_entry *entry;
+	size_t count = 0;
+	size_t i;
+
+	for (i = 0; i < git_index_entrycount(repo_index); i++) {
+		cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+		if (git_index_entry_stage(entry) > 0)
+			count++;
+	}
+
+	return count;
+}
+
+/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
+void test_merge_workdir_trivial__2alt(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0));
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
+void test_merge_workdir_trivial__3alt(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0));
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__4(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 2));
+	cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 3));
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_1(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0));
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_2(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0));
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__6(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 1);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 1));
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__6_automerge(void)
+{
+	const git_index_entry *entry;
+	const git_index_reuc_entry *reuc;
+
+	cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 1));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+	cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-both.txt"));
+
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_workdir_trivial__8(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 3));
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_workdir_trivial__8_automerge(void)
+{
+	const git_index_entry *entry;
+	const git_index_reuc_entry *reuc;
+
+	cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 1));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
+
+	cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+	cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-8.txt"));
+
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_workdir_trivial__7(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_workdir_trivial__7_automerge(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__10(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 2));
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__10_automerge(void)
+{
+	const git_index_entry *entry;
+	const git_index_reuc_entry *reuc;
+
+	cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 1));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
+
+	cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+	cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-10-branch.txt"));
+
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__9(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__9_automerge(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 1));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 2);
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
+}
+
+/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+void test_merge_workdir_trivial__13(void)
+{
+	const git_index_entry *entry;
+	git_oid expected_oid;
+
+	cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0));
+	cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b"));
+	cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
+
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+void test_merge_workdir_trivial__14(void)
+{
+	const git_index_entry *entry;
+	git_oid expected_oid;
+
+	cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch", 0));
+
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0));
+	cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9"));
+	cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0);
+
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+	cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__11(void)
+{
+	const git_index_entry *entry;
+
+	cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch", 0));
+
+	cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL);
+	cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+	cl_assert(merge_trivial_conflict_entrycount() == 3);
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 1));
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 2));
+	cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 3));
+}
diff --git a/tests-clar/network/cred.c b/tests/network/cred.c
similarity index 100%
rename from tests-clar/network/cred.c
rename to tests/network/cred.c
diff --git a/tests-clar/network/fetchlocal.c b/tests/network/fetchlocal.c
similarity index 80%
rename from tests-clar/network/fetchlocal.c
rename to tests/network/fetchlocal.c
index 09335b3..28c7115 100644
--- a/tests-clar/network/fetchlocal.c
+++ b/tests/network/fetchlocal.c
@@ -25,13 +25,18 @@
 	git_strarray refnames = {0};
 
 	const char *url = cl_git_fixture_url("testrepo.git");
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+	callbacks.transfer_progress = transfer_cb;
+	callbacks.payload = &callcount;
 
 	cl_set_cleanup(&cleanup_local_repo, "foo");
 	cl_git_pass(git_repository_init(&repo, "foo", true));
 
 	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+	git_remote_set_callbacks(origin, &callbacks);
 	cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
-	cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
+	cl_git_pass(git_remote_download(origin));
 	cl_git_pass(git_remote_update_tips(origin));
 
 	cl_git_pass(git_reference_list(&refnames, repo));
@@ -56,6 +61,10 @@
 	int callcount = 0;
 	git_strarray refnames = {0};
 	const char *url;
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+	callbacks.transfer_progress = transfer_cb;
+	callbacks.payload = &callcount;
 
 	cl_set_cleanup(&cleanup_sandbox, NULL);
 	cl_git_pass(git_reference_list(&refnames, repo));
@@ -63,8 +72,9 @@
 
 	url = cl_git_fixture_url("testrepo.git");
 	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+	git_remote_set_callbacks(origin, &callbacks);
 	cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
-	cl_git_pass(git_remote_download(origin, transfer_cb, &callcount));
+	cl_git_pass(git_remote_download(origin));
 	cl_git_pass(git_remote_update_tips(origin));
 
 	git_strarray_free(&refnames);
diff --git a/tests-clar/network/refspecs.c b/tests/network/refspecs.c
similarity index 100%
rename from tests-clar/network/refspecs.c
rename to tests/network/refspecs.c
diff --git a/tests-clar/network/remote/createthenload.c b/tests/network/remote/createthenload.c
similarity index 100%
rename from tests-clar/network/remote/createthenload.c
rename to tests/network/remote/createthenload.c
diff --git a/tests-clar/network/remote/isvalidname.c b/tests/network/remote/isvalidname.c
similarity index 100%
rename from tests-clar/network/remote/isvalidname.c
rename to tests/network/remote/isvalidname.c
diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c
new file mode 100644
index 0000000..3091429
--- /dev/null
+++ b/tests/network/remote/local.c
@@ -0,0 +1,234 @@
+#include "clar_libgit2.h"
+#include "buffer.h"
+#include "path.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_buf file_path_buf = GIT_BUF_INIT;
+static git_remote *remote;
+
+void test_network_remote_local__initialize(void)
+{
+	cl_git_pass(git_repository_init(&repo, "remotelocal/", 0));
+	cl_assert(repo != NULL);
+}
+
+void test_network_remote_local__cleanup(void)
+{
+	git_buf_free(&file_path_buf);
+
+	git_remote_free(remote);
+	remote = NULL;
+
+	git_repository_free(repo);
+	repo = NULL;
+
+	cl_fixture_cleanup("remotelocal");
+}
+
+static void connect_to_local_repository(const char *local_repository)
+{
+	git_buf_sets(&file_path_buf, cl_git_path_url(local_repository));
+
+	cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf)));
+	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+}
+
+void test_network_remote_local__connected(void)
+{
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	cl_assert(git_remote_connected(remote));
+
+	git_remote_disconnect(remote);
+	cl_assert(!git_remote_connected(remote));
+}
+
+void test_network_remote_local__retrieve_advertised_references(void)
+{
+	const git_remote_head **refs;
+	size_t refs_len;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+
+	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+	cl_assert_equal_i(refs_len, 28);
+}
+
+void test_network_remote_local__retrieve_advertised_references_after_disconnect(void)
+{
+	const git_remote_head **refs;
+	size_t refs_len;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	git_remote_disconnect(remote);
+
+	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+	cl_assert_equal_i(refs_len, 28);
+}
+
+void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
+{
+	const git_remote_head **refs;
+	size_t refs_len;
+
+	cl_fixture_sandbox("testrepo.git");
+	cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
+
+	connect_to_local_repository("spaced testrepo.git");
+
+	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+	cl_assert_equal_i(refs_len, 28);
+
+	git_remote_free(remote);	/* Disconnect from the "spaced repo" before the cleanup */
+	remote = NULL;
+
+	cl_fixture_cleanup("spaced testrepo.git");
+}
+
+void test_network_remote_local__nested_tags_are_completely_peeled(void)
+{
+	const git_remote_head **refs;
+	size_t refs_len, i;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+
+	cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+	for (i = 0; i < refs_len; i++) {
+		if (!strcmp(refs[i]->name, "refs/tags/test^{}"))
+			cl_git_pass(git_oid_streq(&refs[i]->oid, "e90810b8df3e80c413d903f631643c716887138d"));
+	}
+}
+
+void test_network_remote_local__shorthand_fetch_refspec0(void)
+{
+	const char *refspec = "master:remotes/sloppy/master";
+	const char *refspec2 = "master:boh/sloppy/master";
+
+	git_reference *ref;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	cl_git_pass(git_remote_add_fetch(remote, refspec));
+	cl_git_pass(git_remote_add_fetch(remote, refspec2));
+
+	cl_git_pass(git_remote_download(remote));
+	cl_git_pass(git_remote_update_tips(remote));
+
+	cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
+	git_reference_free(ref);
+
+	cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
+	git_reference_free(ref);
+}
+
+void test_network_remote_local__shorthand_fetch_refspec1(void)
+{
+	const char *refspec = "master";
+	const char *refspec2 = "hard_tag";
+
+	git_reference *ref;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	git_remote_clear_refspecs(remote);
+	cl_git_pass(git_remote_add_fetch(remote, refspec));
+	cl_git_pass(git_remote_add_fetch(remote, refspec2));
+
+	cl_git_pass(git_remote_download(remote));
+	cl_git_pass(git_remote_update_tips(remote));
+
+	cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
+
+	cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+}
+
+void test_network_remote_local__tagopt(void)
+{
+	git_reference *ref;
+
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
+
+	cl_git_pass(git_remote_download(remote));
+	cl_git_pass(git_remote_update_tips(remote));
+
+
+	cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master"));
+
+	cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+	git_reference_free(ref);
+}
+
+void test_network_remote_local__push_to_bare_remote(void)
+{
+	/* Should be able to push to a bare remote */
+	git_remote *localremote;
+	git_push *push;
+
+	/* Get some commits */
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	cl_git_pass(git_remote_add_fetch(remote, "master:master"));
+	cl_git_pass(git_remote_download(remote));
+	cl_git_pass(git_remote_update_tips(remote));
+	git_remote_disconnect(remote);
+
+	/* Set up an empty bare repo to push into */
+	{
+		git_repository *localbarerepo;
+		cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1));
+		git_repository_free(localbarerepo);
+	}
+
+	/* Connect to the bare repo */
+	cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localbare.git"));
+	cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH));
+
+	/* Try to push */
+	cl_git_pass(git_push_new(&push, localremote));
+	cl_git_pass(git_push_add_refspec(push, "refs/heads/master:"));
+	cl_git_pass(git_push_finish(push));
+	cl_assert(git_push_unpack_ok(push));
+
+	/* Clean up */
+	git_push_free(push);
+	git_remote_free(localremote);
+	cl_fixture_cleanup("localbare.git");
+}
+
+void test_network_remote_local__push_to_non_bare_remote(void)
+{
+	/* Shouldn't be able to push to a non-bare remote */
+	git_remote *localremote;
+	git_push *push;
+
+	/* Get some commits */
+	connect_to_local_repository(cl_fixture("testrepo.git"));
+	cl_git_pass(git_remote_add_fetch(remote, "master:master"));
+	cl_git_pass(git_remote_download(remote));
+	cl_git_pass(git_remote_update_tips(remote));
+	git_remote_disconnect(remote);
+
+	/* Set up an empty non-bare repo to push into */
+	{
+		git_repository *remoterepo = NULL;
+		cl_git_pass(git_repository_init(&remoterepo, "localnonbare", 0));
+		git_repository_free(remoterepo);
+	}
+
+	/* Connect to the bare repo */
+	cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localnonbare"));
+	cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH));
+
+	/* Try to push */
+	cl_git_pass(git_push_new(&push, localremote));
+	cl_git_pass(git_push_add_refspec(push, "refs/heads/master:"));
+	cl_git_fail_with(git_push_finish(push), GIT_EBAREREPO);
+	cl_assert_equal_i(0, git_push_unpack_ok(push));
+
+	/* Clean up */
+	git_push_free(push);
+	git_remote_free(localremote);
+	cl_fixture_cleanup("localbare.git");
+}
diff --git a/tests-clar/network/remote/remotes.c b/tests/network/remote/remotes.c
similarity index 84%
rename from tests-clar/network/remote/remotes.c
rename to tests/network/remote/remotes.c
index 3c4fa96..954ded8 100644
--- a/tests-clar/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -150,8 +150,10 @@
 void test_network_remote_remotes__save(void)
 {
 	git_strarray array;
-	const char *fetch_refspec = "refs/heads/*:refs/remotes/upstream/*";
-	const char *push_refspec = "refs/heads/*:refs/heads/*";
+	const char *fetch_refspec1 = "refs/heads/ns1/*:refs/remotes/upstream/ns1/*";
+	const char *fetch_refspec2 = "refs/heads/ns2/*:refs/remotes/upstream/ns2/*";
+	const char *push_refspec1 = "refs/heads/ns1/*:refs/heads/ns1/*";
+	const char *push_refspec2 = "refs/heads/ns2/*:refs/heads/ns2/*";
 
 	git_remote_free(_remote);
 	_remote = NULL;
@@ -160,8 +162,10 @@
 	cl_git_pass(git_remote_create(&_remote, _repo, "upstream", "git://github.com/libgit2/libgit2"));
 	git_remote_clear_refspecs(_remote);
 
-	cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec));
-	cl_git_pass(git_remote_add_push(_remote, push_refspec));
+	cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec1));
+	cl_git_pass(git_remote_add_fetch(_remote, fetch_refspec2));
+	cl_git_pass(git_remote_add_push(_remote, push_refspec1));
+	cl_git_pass(git_remote_add_push(_remote, push_refspec2));
 	cl_git_pass(git_remote_set_pushurl(_remote, "git://github.com/libgit2/libgit2_push"));
 	cl_git_pass(git_remote_save(_remote));
 	git_remote_free(_remote);
@@ -171,16 +175,19 @@
 	cl_git_pass(git_remote_load(&_remote, _repo, "upstream"));
 
 	cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
-	cl_assert_equal_i(1, (int)array.count);
-	cl_assert_equal_s(fetch_refspec, array.strings[0]);
+	cl_assert_equal_i(2, (int)array.count);
+	cl_assert_equal_s(fetch_refspec1, array.strings[0]);
+	cl_assert_equal_s(fetch_refspec2, array.strings[1]);
 	git_strarray_free(&array);
 
 	cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
-	cl_assert_equal_i(1, (int)array.count);
-	cl_assert_equal_s(push_refspec, array.strings[0]);
+	cl_assert_equal_i(2, (int)array.count);
+	cl_assert_equal_s(push_refspec1, array.strings[0]);
+	cl_assert_equal_s(push_refspec2, array.strings[1]);
+	git_strarray_free(&array);
+
 	cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
 	cl_assert_equal_s(git_remote_pushurl(_remote), "git://github.com/libgit2/libgit2_push");
-	git_strarray_free(&array);
 
 	/* remove the pushurl again and see if we can save that too */
 	cl_git_pass(git_remote_set_pushurl(_remote, NULL));
@@ -243,13 +250,19 @@
 	git_config *cfg;
 
 	cl_git_pass(git_remote_list(&list, _repo));
-	cl_assert(list.count == 4);
+	cl_assert(list.count == 5);
 	git_strarray_free(&list);
 
 	cl_git_pass(git_repository_config(&cfg, _repo));
+
+	/* Create a new remote */
 	cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+
+	/* Update a remote (previously without any url/pushurl entry) */
+	cl_git_pass(git_config_set_string(cfg, "remote.no-remote-url.pushurl", "http://example.com"));
+
 	cl_git_pass(git_remote_list(&list, _repo));
-	cl_assert(list.count == 5);
+	cl_assert(list.count == 7);
 	git_strarray_free(&list);
 
 	git_config_free(cfg);
@@ -361,13 +374,43 @@
 	git_config_free(cfg);
 }
 
-void test_network_remote_remotes__cannot_load_with_an_empty_url(void)
+void test_network_remote_remotes__can_load_with_an_empty_url(void)
 {
 	git_remote *remote = NULL;
 
-	cl_git_fail(git_remote_load(&remote, _repo, "empty-remote-url"));
+	cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-url"));
+
+	cl_assert(remote->url == NULL);
+	cl_assert(remote->pushurl == NULL);
+
+	cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+	cl_assert(giterr_last() != NULL);
 	cl_assert(giterr_last()->klass == GITERR_INVALID);
-	cl_assert_equal_p(remote, NULL);
+
+	git_remote_free(remote);
+}
+
+void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void)
+{
+	git_remote *remote = NULL;
+
+	cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-pushurl"));
+
+	cl_assert(remote->url == NULL);
+	cl_assert(remote->pushurl == NULL);
+
+	cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH));
+
+	git_remote_free(remote);
+}
+
+void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl(void)
+{
+	git_remote *remote = NULL;
+
+	cl_git_fail_with(
+		git_remote_load(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND);
 }
 
 void test_network_remote_remotes__check_structure_version(void)
@@ -407,7 +450,6 @@
 	assert_cannot_create_remote("test", GIT_EEXISTS);
 }
 
-
 void test_network_remote_remotes__cannot_create_a_remote_which_name_is_invalid(void)
 {
 	assert_cannot_create_remote("/", GIT_EINVALIDSPEC);
@@ -416,6 +458,17 @@
 	assert_cannot_create_remote("a.lock", GIT_EINVALIDSPEC);
 }
 
+void test_network_remote_remote__git_remote_create_with_fetchspec(void)
+{
+	git_remote *remote;
+	git_strarray array;
+
+	cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", "+refs/*:refs/*"));
+	git_remote_get_fetch_refspecs(&array, remote);
+	cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+	git_remote_free(remote);
+}
+
 static const char *fetch_refspecs[] = {
 	"+refs/heads/*:refs/remotes/origin/*",
 	"refs/tags/*:refs/tags/*",
diff --git a/tests-clar/network/remote/rename.c b/tests/network/remote/rename.c
similarity index 100%
rename from tests-clar/network/remote/rename.c
rename to tests/network/remote/rename.c
diff --git a/tests/network/urlparse.c b/tests/network/urlparse.c
new file mode 100644
index 0000000..2a9c2f6
--- /dev/null
+++ b/tests/network/urlparse.c
@@ -0,0 +1,193 @@
+#include "clar_libgit2.h"
+#include "netops.h"
+
+static char *host, *port, *path, *user, *pass;
+static gitno_connection_data conndata;
+
+void test_network_urlparse__initialize(void)
+{
+	host = port = path = user = pass = NULL;
+	memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_network_urlparse__cleanup(void)
+{
+#define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; }
+	FREE_AND_NULL(host);
+	FREE_AND_NULL(port);
+	FREE_AND_NULL(path);
+	FREE_AND_NULL(user);
+	FREE_AND_NULL(pass);
+
+	gitno_connection_data_free_ptrs(&conndata);
+}
+
+void test_network_urlparse__trivial(void)
+{
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"http://example.com/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "8080");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_p(user, NULL);
+	cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__encoded_password(void)
+{
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
+	cl_assert_equal_s(host, "hostname.com");
+	cl_assert_equal_s(port, "1234");
+	cl_assert_equal_s(path, "/");
+	cl_assert_equal_s(user, "user");
+	cl_assert_equal_s(pass, "pass/is@bad");
+}
+
+void test_network_urlparse__user(void)
+{
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://user@example.com/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "8080");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_s(user, "user");
+	cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_pass(void)
+{
+	/* user:pass@hostname.tld/resource */
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://user:pass@example.com/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "8080");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_s(user, "user");
+	cl_assert_equal_s(pass, "pass");
+}
+
+void test_network_urlparse__port(void)
+{
+	/* hostname.tld:port/resource */
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://example.com:9191/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "9191");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_p(user, NULL);
+	cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_port(void)
+{
+	/* user@hostname.tld:port/resource */
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://user@example.com:9191/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "9191");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_s(user, "user");
+	cl_assert_equal_p(pass, NULL);
+}
+
+void test_network_urlparse__user_pass_port(void)
+{
+	/* user:pass@hostname.tld:port/resource */
+	cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
+				"https://user:pass@example.com:9191/resource", "8080"));
+	cl_assert_equal_s(host, "example.com");
+	cl_assert_equal_s(port, "9191");
+	cl_assert_equal_s(path, "/resource");
+	cl_assert_equal_s(user, "user");
+	cl_assert_equal_s(pass, "pass");
+}
+
+void test_network_urlparse__connection_data_http(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"http://example.com/foo/bar/baz", "bar/baz"));
+	cl_assert_equal_s(conndata.host, "example.com");
+	cl_assert_equal_s(conndata.port, "80");
+	cl_assert_equal_s(conndata.path, "/foo/");
+	cl_assert_equal_p(conndata.user, NULL);
+	cl_assert_equal_p(conndata.pass, NULL);
+	cl_assert_equal_i(conndata.use_ssl, false);
+}
+
+void test_network_urlparse__connection_data_ssl(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"https://example.com/foo/bar/baz", "bar/baz"));
+	cl_assert_equal_s(conndata.host, "example.com");
+	cl_assert_equal_s(conndata.port, "443");
+	cl_assert_equal_s(conndata.path, "/foo/");
+	cl_assert_equal_p(conndata.user, NULL);
+	cl_assert_equal_p(conndata.pass, NULL);
+	cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+void test_network_urlparse__encoded_username_password(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz"));
+	cl_assert_equal_s(conndata.host, "example.com");
+	cl_assert_equal_s(conndata.port, "443");
+	cl_assert_equal_s(conndata.path, "/foo/");
+	cl_assert_equal_s(conndata.user, "user/name");
+	cl_assert_equal_s(conndata.pass, "pass@word%zyx%v");
+	cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+void test_network_urlparse__connection_data_cross_host_redirect(void)
+{
+	conndata.host = git__strdup("bar.com");
+	cl_git_fail_with(gitno_connection_data_from_url(&conndata,
+				"https://foo.com/bar/baz", NULL),
+			-1);
+}
+
+void test_network_urlparse__connection_data_http_downgrade(void)
+{
+	conndata.use_ssl = true;
+	cl_git_fail_with(gitno_connection_data_from_url(&conndata,
+				"http://foo.com/bar/baz", NULL),
+			-1);
+}
+
+void test_network_urlparse__connection_data_relative_redirect(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"http://foo.com/bar/baz/biff", NULL));
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"/zap/baz/biff?bam", NULL));
+	cl_assert_equal_s(conndata.host, "foo.com");
+	cl_assert_equal_s(conndata.port, "80");
+	cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+	cl_assert_equal_p(conndata.user, NULL);
+	cl_assert_equal_p(conndata.pass, NULL);
+	cl_assert_equal_i(conndata.use_ssl, false);
+}
+
+void test_network_urlparse__connection_data_relative_redirect_ssl(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"https://foo.com/bar/baz/biff", NULL));
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"/zap/baz/biff?bam", NULL));
+	cl_assert_equal_s(conndata.host, "foo.com");
+	cl_assert_equal_s(conndata.port, "443");
+	cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+	cl_assert_equal_p(conndata.user, NULL);
+	cl_assert_equal_p(conndata.pass, NULL);
+	cl_assert_equal_i(conndata.use_ssl, true);
+}
+
+/* Run this under valgrind */
+void test_network_urlparse__connection_data_cleanup(void)
+{
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"http://foo.com/bar/baz/biff", "baz/biff"));
+	cl_git_pass(gitno_connection_data_from_url(&conndata,
+				"https://foo.com/bar/baz/biff", "baz/biff"));
+}
diff --git a/tests-clar/notes/notes.c b/tests/notes/notes.c
similarity index 100%
rename from tests-clar/notes/notes.c
rename to tests/notes/notes.c
diff --git a/tests-clar/notes/notesref.c b/tests/notes/notesref.c
similarity index 100%
rename from tests-clar/notes/notesref.c
rename to tests/notes/notesref.c
diff --git a/tests/object/blob/filter.c b/tests/object/blob/filter.c
new file mode 100644
index 0000000..0b2d6bf
--- /dev/null
+++ b/tests/object/blob/filter.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "buf_text.h"
+
+static git_repository *g_repo = NULL;
+
+#define CRLF_NUM_TEST_OBJECTS	9
+
+static const char *g_crlf_raw[CRLF_NUM_TEST_OBJECTS] = {
+	"",
+	"foo\nbar\n",
+	"foo\rbar\r",
+	"foo\r\nbar\r\n",
+	"foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+	"123\n\000\001\002\003\004abc\255\254\253\r\n",
+	"\xEF\xBB\xBFThis is UTF-8\n",
+	"\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n",
+	"\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
+};
+
+static git_off_t g_crlf_raw_len[CRLF_NUM_TEST_OBJECTS] = {
+	-1, -1, -1, -1, -1, 17, -1, -1, 12
+};
+
+static git_oid g_crlf_oids[CRLF_NUM_TEST_OBJECTS];
+
+static git_buf g_crlf_filtered[CRLF_NUM_TEST_OBJECTS] = {
+	{ "", 0, 0 },
+	{ "foo\nbar\n", 0, 8 },
+	{ "foo\rbar\r", 0, 8 },
+	{ "foo\nbar\n", 0, 8 },
+	{ "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+	{ "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
+	{ "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
+	{ "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n", 0, 29 },
+	{ "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
+};
+
+static git_buf_text_stats g_crlf_filtered_stats[CRLF_NUM_TEST_OBJECTS] = {
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 2, 0, 6, 0 },
+	{ 0, 0, 2, 0, 0, 6, 0 },
+	{ 0, 0, 2, 2, 2, 6, 0 },
+	{ 0, 0, 4, 4, 1, 31, 0 },
+	{ 0, 1, 1, 2, 1, 9, 5 },
+	{ GIT_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
+	{ GIT_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
+	{ GIT_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
+};
+
+void test_object_blob_filter__initialize(void)
+{
+	int i;
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+	for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+		if (g_crlf_raw_len[i] < 0)
+			g_crlf_raw_len[i] = strlen(g_crlf_raw[i]);
+
+		cl_git_pass(git_blob_create_frombuffer(
+			&g_crlf_oids[i], g_repo, g_crlf_raw[i], (size_t)g_crlf_raw_len[i]));
+	}
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+	int i;
+	git_blob *blob;
+
+	for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+		size_t raw_len = (size_t)g_crlf_raw_len[i];
+
+		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+		cl_assert_equal_sz(raw_len, (size_t)git_blob_rawsize(blob));
+		cl_assert_equal_i(
+			0, memcmp(g_crlf_raw[i], git_blob_rawcontent(blob), raw_len));
+
+		git_blob_free(blob);
+	}
+}
+
+void test_object_blob_filter__stats(void)
+{
+	int i;
+	git_blob *blob;
+	git_buf buf = GIT_BUF_INIT;
+	git_buf_text_stats stats;
+
+	for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+		cl_git_pass(git_blob__getbuf(&buf, blob));
+		git_buf_text_gather_stats(&stats, &buf, false);
+		cl_assert_equal_i(
+			0, memcmp(&g_crlf_filtered_stats[i], &stats, sizeof(stats)));
+		git_blob_free(blob);
+	}
+
+	git_buf_free(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+	git_filter_list *fl = NULL;
+	git_config *cfg;
+	int i;
+	git_blob *blob;
+	git_buf out = GIT_BUF_INIT;
+
+	cl_git_pass(git_repository_config(&cfg, g_repo));
+	cl_assert(cfg);
+
+	git_attr_cache_flush(g_repo);
+	cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+	cl_git_pass(git_filter_list_load(
+		&fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB));
+	cl_assert(fl != NULL);
+
+	for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+		cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+		cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+
+		cl_assert_equal_sz(g_crlf_filtered[i].size, out.size);
+
+		cl_assert_equal_i(
+			0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size));
+
+		git_blob_free(blob);
+	}
+
+	git_filter_list_free(fl);
+	git_buf_free(&out);
+	git_config_free(cfg);
+}
diff --git a/tests-clar/object/blob/fromchunks.c b/tests/object/blob/fromchunks.c
similarity index 64%
rename from tests-clar/object/blob/fromchunks.c
rename to tests/object/blob/fromchunks.c
index dc57d4f..03ed4ef 100644
--- a/tests-clar/object/blob/fromchunks.c
+++ b/tests/object/blob/fromchunks.c
@@ -41,14 +41,46 @@
 
 	cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
 
-	cl_git_fail(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
+	cl_git_fail_with(
+		git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY),
+		GIT_ENOTFOUND);
 
 	cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
 
 	cl_git_pass(git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY));
+	cl_assert(git_oid_cmp(&expected_oid, git_object_id(blob)) == 0);
+
 	git_object_free(blob);
 }
 
+void test_object_blob_fromchunks__doesnot_overwrite_an_already_existing_object(void)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_buf content = GIT_BUF_INIT;
+	git_oid expected_oid, oid;
+	int howmany = 7;
+	
+	cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9"));
+
+	cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+	/* Let's replace the content of the blob file storage with something else... */
+	cl_git_pass(git_buf_joinpath(&path, git_repository_path(repo), "objects/32/1cbdf08803c744082332332838df6bd160f8f9"));
+	cl_git_pass(p_unlink(git_buf_cstr(&path)));
+	cl_git_mkfile(git_buf_cstr(&path), "boom");
+
+	/* ...request a creation of the same blob... */
+	howmany = 7;
+	cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany));
+
+	/* ...and ensure the content of the faked blob file hasn't been altered */
+	cl_git_pass(git_futils_readbuffer(&content, git_buf_cstr(&path)));
+	cl_assert(!git__strcmp("boom", git_buf_cstr(&content)));
+
+	git_buf_free(&path);
+	git_buf_free(&content);
+}
+
 #define GITATTR "* text=auto\n" \
 	"*.txt text\n" \
 	"*.data binary\n"
diff --git a/tests-clar/object/blob/write.c b/tests/object/blob/write.c
similarity index 100%
rename from tests-clar/object/blob/write.c
rename to tests/object/blob/write.c
diff --git a/tests-clar/object/cache.c b/tests/object/cache.c
similarity index 100%
rename from tests-clar/object/cache.c
rename to tests/object/cache.c
diff --git a/tests-clar/object/commit/commitstagedfile.c b/tests/object/commit/commitstagedfile.c
similarity index 100%
rename from tests-clar/object/commit/commitstagedfile.c
rename to tests/object/commit/commitstagedfile.c
diff --git a/tests-clar/object/lookup.c b/tests/object/lookup.c
similarity index 100%
rename from tests-clar/object/lookup.c
rename to tests/object/lookup.c
diff --git a/tests/object/lookupbypath.c b/tests/object/lookupbypath.c
new file mode 100644
index 0000000..31aac76
--- /dev/null
+++ b/tests/object/lookupbypath.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_tree *g_root_tree;
+static git_commit *g_head_commit;
+static git_object *g_expectedobject,
+						*g_actualobject;
+
+void test_object_lookupbypath__initialize(void)
+{
+	git_reference *head;
+	git_tree_entry *tree_entry;
+
+	cl_git_pass(git_repository_open(&g_repo, cl_fixture("attr/.gitted")));
+
+	cl_git_pass(git_repository_head(&head, g_repo));
+	cl_git_pass(git_reference_peel((git_object**)&g_head_commit, head, GIT_OBJ_COMMIT));
+	cl_git_pass(git_commit_tree(&g_root_tree, g_head_commit));
+	cl_git_pass(git_tree_entry_bypath(&tree_entry, g_root_tree, "subdir/subdir_test2.txt"));
+	cl_git_pass(git_object_lookup(&g_expectedobject, g_repo, git_tree_entry_id(tree_entry),
+				GIT_OBJ_ANY));
+
+	git_tree_entry_free(tree_entry);
+	git_reference_free(head);
+
+	g_actualobject = NULL;
+}
+void test_object_lookupbypath__cleanup(void)
+{
+	git_object_free(g_actualobject);
+	git_object_free(g_expectedobject);
+	git_tree_free(g_root_tree);
+	git_commit_free(g_head_commit);
+	g_expectedobject = NULL;
+	git_repository_free(g_repo);
+	g_repo = NULL;
+}
+
+void test_object_lookupbypath__errors(void)
+{
+	cl_assert_equal_i(GIT_EINVALIDSPEC,
+			git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+				"subdir/subdir_test2.txt", GIT_OBJ_TREE)); // It's not a tree
+	cl_assert_equal_i(GIT_ENOTFOUND,
+			git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+				"file/doesnt/exist", GIT_OBJ_ANY));
+}
+
+void test_object_lookupbypath__from_root_tree(void)
+{
+	cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+				"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
+	cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+				git_object_id(g_actualobject)));
+}
+
+void test_object_lookupbypath__from_head_commit(void)
+{
+	cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
+				"subdir/subdir_test2.txt", GIT_OBJ_BLOB));
+	cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+				git_object_id(g_actualobject)));
+}
+
+void test_object_lookupbypath__from_subdir_tree(void)
+{
+	git_tree_entry *entry = NULL;
+	git_tree *tree = NULL;
+
+	cl_git_pass(git_tree_entry_bypath(&entry, g_root_tree, "subdir"));
+	cl_git_pass(git_tree_lookup(&tree, g_repo, git_tree_entry_id(entry)));
+
+	cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
+				"subdir_test2.txt", GIT_OBJ_BLOB));
+	cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject),
+				git_object_id(g_actualobject)));
+
+	git_tree_entry_free(entry);
+	git_tree_free(tree);
+}
+
diff --git a/tests-clar/object/message.c b/tests/object/message.c
similarity index 100%
rename from tests-clar/object/message.c
rename to tests/object/message.c
diff --git a/tests-clar/object/peel.c b/tests/object/peel.c
similarity index 100%
rename from tests-clar/object/peel.c
rename to tests/object/peel.c
diff --git a/tests-clar/object/raw/chars.c b/tests/object/raw/chars.c
similarity index 100%
rename from tests-clar/object/raw/chars.c
rename to tests/object/raw/chars.c
diff --git a/tests-clar/object/raw/compare.c b/tests/object/raw/compare.c
similarity index 100%
rename from tests-clar/object/raw/compare.c
rename to tests/object/raw/compare.c
diff --git a/tests-clar/object/raw/convert.c b/tests/object/raw/convert.c
similarity index 100%
rename from tests-clar/object/raw/convert.c
rename to tests/object/raw/convert.c
diff --git a/tests-clar/object/raw/data.h b/tests/object/raw/data.h
similarity index 100%
rename from tests-clar/object/raw/data.h
rename to tests/object/raw/data.h
diff --git a/tests-clar/object/raw/fromstr.c b/tests/object/raw/fromstr.c
similarity index 100%
rename from tests-clar/object/raw/fromstr.c
rename to tests/object/raw/fromstr.c
diff --git a/tests-clar/object/raw/hash.c b/tests/object/raw/hash.c
similarity index 100%
rename from tests-clar/object/raw/hash.c
rename to tests/object/raw/hash.c
diff --git a/tests-clar/object/raw/short.c b/tests/object/raw/short.c
similarity index 100%
rename from tests-clar/object/raw/short.c
rename to tests/object/raw/short.c
diff --git a/tests-clar/object/raw/size.c b/tests/object/raw/size.c
similarity index 100%
rename from tests-clar/object/raw/size.c
rename to tests/object/raw/size.c
diff --git a/tests-clar/object/raw/type2string.c b/tests/object/raw/type2string.c
similarity index 100%
rename from tests-clar/object/raw/type2string.c
rename to tests/object/raw/type2string.c
diff --git a/tests-clar/object/raw/write.c b/tests/object/raw/write.c
similarity index 98%
rename from tests-clar/object/raw/write.c
rename to tests/object/raw/write.c
index 9709c03..273f08f 100644
--- a/tests-clar/object/raw/write.c
+++ b/tests/object/raw/write.c
@@ -31,9 +31,9 @@
    int error;
 
    cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
-   stream->write(stream, raw->data, raw->len);
-   error = stream->finalize_write(oid, stream);
-   stream->free(stream);
+   git_odb_stream_write(stream, raw->data, raw->len);
+   error = git_odb_stream_finalize_write(oid, stream);
+   git_odb_stream_free(stream);
    cl_git_pass(error);
 }
 
diff --git a/tests-clar/object/tag/list.c b/tests/object/tag/list.c
similarity index 100%
rename from tests-clar/object/tag/list.c
rename to tests/object/tag/list.c
diff --git a/tests-clar/object/tag/peel.c b/tests/object/tag/peel.c
similarity index 100%
rename from tests-clar/object/tag/peel.c
rename to tests/object/tag/peel.c
diff --git a/tests-clar/object/tag/read.c b/tests/object/tag/read.c
similarity index 100%
rename from tests-clar/object/tag/read.c
rename to tests/object/tag/read.c
diff --git a/tests-clar/object/tag/write.c b/tests/object/tag/write.c
similarity index 100%
rename from tests-clar/object/tag/write.c
rename to tests/object/tag/write.c
diff --git a/tests-clar/object/tree/attributes.c b/tests/object/tree/attributes.c
similarity index 95%
rename from tests-clar/object/tree/attributes.c
rename to tests/object/tree/attributes.c
index cc93b45..85216cd 100644
--- a/tests-clar/object/tree/attributes.c
+++ b/tests/object/tree/attributes.c
@@ -107,7 +107,8 @@
 	cl_git_pass(git_tree_lookup(&tree, repo, &id));
 
 	entry = git_tree_entry_byname(tree, "ListaTeste.xml");
-	cl_assert_equal_i(entry->attr, GIT_FILEMODE_BLOB);
+	cl_assert_equal_i(git_tree_entry_filemode(entry), GIT_FILEMODE_BLOB);
+	cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
 
 	git_tree_free(tree);
 	cl_git_sandbox_cleanup();
diff --git a/tests-clar/object/tree/duplicateentries.c b/tests/object/tree/duplicateentries.c
similarity index 100%
rename from tests-clar/object/tree/duplicateentries.c
rename to tests/object/tree/duplicateentries.c
diff --git a/tests-clar/object/tree/frompath.c b/tests/object/tree/frompath.c
similarity index 100%
rename from tests-clar/object/tree/frompath.c
rename to tests/object/tree/frompath.c
diff --git a/tests-clar/object/tree/read.c b/tests/object/tree/read.c
similarity index 100%
rename from tests-clar/object/tree/read.c
rename to tests/object/tree/read.c
diff --git a/tests/object/tree/walk.c b/tests/object/tree/walk.c
new file mode 100644
index 0000000..1207e86
--- /dev/null
+++ b/tests/object/tree/walk.c
@@ -0,0 +1,177 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static git_repository *g_repo;
+
+void test_object_tree_walk__initialize(void)
+{
+   g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_walk__cleanup(void)
+{
+   cl_git_sandbox_cleanup();
+}
+
+static int treewalk_count_cb(
+	const char *root, const git_tree_entry *entry, void *payload)
+{
+	int *count = payload;
+
+	GIT_UNUSED(root);
+	GIT_UNUSED(entry);
+
+	(*count) += 1;
+
+	return 0;
+}
+
+void test_object_tree_walk__0(void)
+{
+	git_oid id;
+	git_tree *tree;
+	int ct;
+
+	git_oid_fromstr(&id, tree_oid);
+
+	cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+	ct = 0;
+	cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
+	cl_assert_equal_i(3, ct);
+
+	ct = 0;
+	cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
+	cl_assert_equal_i(3, ct);
+
+	git_tree_free(tree);
+}
+
+
+static int treewalk_stop_cb(
+	const char *root, const git_tree_entry *entry, void *payload)
+{
+	int *count = payload;
+
+	GIT_UNUSED(root);
+	GIT_UNUSED(entry);
+
+	(*count) += 1;
+
+	return (*count == 2) ? -1 : 0;
+}
+
+static int treewalk_stop_immediately_cb(
+	const char *root, const git_tree_entry *entry, void *payload)
+{
+	GIT_UNUSED(root);
+	GIT_UNUSED(entry);
+	GIT_UNUSED(payload);
+	return -100;
+}
+
+void test_object_tree_walk__1(void)
+{
+	git_oid id;
+	git_tree *tree;
+	int ct;
+
+	git_oid_fromstr(&id, tree_oid);
+
+	cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+	ct = 0;
+	cl_assert_equal_i(
+		GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
+	cl_assert_equal_i(2, ct);
+
+	ct = 0;
+	cl_assert_equal_i(
+		GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
+	cl_assert_equal_i(2, ct);
+
+	cl_assert_equal_i(
+		GIT_EUSER, git_tree_walk(
+			tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
+
+	cl_assert_equal_i(
+		GIT_EUSER, git_tree_walk(
+			tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
+
+	git_tree_free(tree);
+}
+
+
+struct treewalk_skip_data {
+	int files;
+	int dirs;
+	const char *skip;
+	const char *stop;
+};
+
+static int treewalk_skip_de_cb(
+	const char *root, const git_tree_entry *entry, void *payload)
+{
+	struct treewalk_skip_data *data = payload;
+	const char *name = git_tree_entry_name(entry);
+
+	GIT_UNUSED(root);
+
+	if (git_tree_entry_type(entry) == GIT_OBJ_TREE)
+		data->dirs++;
+	else
+		data->files++;
+
+	if (data->skip && !strcmp(name, data->skip))
+		return 1;
+	else if (data->stop && !strcmp(name, data->stop))
+		return -1;
+	else
+		return 0;
+}
+
+void test_object_tree_walk__2(void)
+{
+	git_oid id;
+	git_tree *tree;
+	struct treewalk_skip_data data;
+
+	/* look up a deep tree */
+	git_oid_fromstr(&id, "ae90f12eea699729ed24555e40b9fd669da12a12");
+	cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+	memset(&data, 0, sizeof(data));
+	data.skip = "de";
+
+	cl_assert_equal_i(0, git_tree_walk(
+		tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+	cl_assert_equal_i(5, data.files);
+	cl_assert_equal_i(3, data.dirs);
+
+	memset(&data, 0, sizeof(data));
+	data.stop = "3.txt";
+
+	cl_assert_equal_i(GIT_EUSER, git_tree_walk(
+		tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+	cl_assert_equal_i(3, data.files);
+	cl_assert_equal_i(2, data.dirs);
+
+	memset(&data, 0, sizeof(data));
+	data.skip = "new.txt";
+
+	cl_assert_equal_i(0, git_tree_walk(
+		tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+	cl_assert_equal_i(7, data.files);
+	cl_assert_equal_i(4, data.dirs);
+
+	memset(&data, 0, sizeof(data));
+	data.stop = "new.txt";
+
+	cl_assert_equal_i(GIT_EUSER, git_tree_walk(
+		tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+	cl_assert_equal_i(7, data.files);
+	cl_assert_equal_i(4, data.dirs);
+
+	git_tree_free(tree);
+}
diff --git a/tests-clar/object/tree/write.c b/tests/object/tree/write.c
similarity index 100%
rename from tests-clar/object/tree/write.c
rename to tests/object/tree/write.c
diff --git a/tests-clar/odb/alternates.c b/tests/odb/alternates.c
similarity index 94%
rename from tests-clar/odb/alternates.c
rename to tests/odb/alternates.c
index 4e876c2..c75f6fe 100644
--- a/tests-clar/odb/alternates.c
+++ b/tests/odb/alternates.c
@@ -32,9 +32,9 @@
 	cl_git_pass(git_futils_mkdir(filepath.ptr, NULL, 0755, GIT_MKDIR_PATH));
 	cl_git_pass(git_buf_joinpath(&filepath, filepath.ptr , "alternates"));
 
-	cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0));
+	cl_git_pass(git_filebuf_open(&file, git_buf_cstr(&filepath), 0, 0666));
 	git_filebuf_printf(&file, "%s\n", git_buf_cstr(&destpath));
-	cl_git_pass(git_filebuf_commit(&file, 0644));
+	cl_git_pass(git_filebuf_commit(&file));
 
 	git_repository_free(repo);
 }
diff --git a/tests/odb/backend/nonrefreshing.c b/tests/odb/backend/nonrefreshing.c
new file mode 100644
index 0000000..b435294
--- /dev/null
+++ b/tests/odb/backend/nonrefreshing.c
@@ -0,0 +1,274 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+#include "repository.h"
+
+typedef struct fake_backend {
+	git_odb_backend parent;
+
+	git_error_code error_code;
+
+	int exists_calls;
+	int read_calls;
+	int read_header_calls;
+	int read_prefix_calls;
+} fake_backend;
+
+static git_repository *_repo;
+static fake_backend *_fake;
+static git_oid _oid;
+
+static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+	fake_backend *fake;
+
+	GIT_UNUSED(oid);
+
+	fake = (fake_backend *)backend;
+
+	fake->exists_calls++;
+
+	return (fake->error_code == GIT_OK);
+}
+
+static int fake_backend__read(
+	void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
+{
+	fake_backend *fake;
+
+	GIT_UNUSED(buffer_p);
+	GIT_UNUSED(len_p);
+	GIT_UNUSED(type_p);
+	GIT_UNUSED(oid);
+
+	fake = (fake_backend *)backend;
+
+	fake->read_calls++;
+
+	*len_p = 0;
+	*buffer_p = NULL;
+	*type_p = GIT_OBJ_BLOB;
+
+	return fake->error_code;
+}
+
+static int fake_backend__read_header(
+	size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *oid)
+{
+	fake_backend *fake;
+
+	GIT_UNUSED(len_p);
+	GIT_UNUSED(type_p);
+	GIT_UNUSED(oid);
+
+	fake = (fake_backend *)backend;
+
+	fake->read_header_calls++;
+
+	*len_p = 0;
+	*type_p = GIT_OBJ_BLOB;
+
+	return fake->error_code;
+}
+
+static int fake_backend__read_prefix(
+	git_oid *out_oid, void **buffer_p, size_t *len_p, git_otype *type_p,
+	git_odb_backend *backend, const git_oid *short_oid,	size_t len)
+{
+	fake_backend *fake;
+
+	GIT_UNUSED(out_oid);
+	GIT_UNUSED(buffer_p);
+	GIT_UNUSED(len_p);
+	GIT_UNUSED(type_p);
+	GIT_UNUSED(short_oid);
+	GIT_UNUSED(len);
+
+	fake = (fake_backend *)backend;
+
+	fake->read_prefix_calls++;
+
+	*len_p = 0;
+	*buffer_p = NULL;
+	*type_p = GIT_OBJ_BLOB;
+
+	return fake->error_code;
+}
+
+static void fake_backend__free(git_odb_backend *_backend)
+{
+	fake_backend *backend;
+
+	backend = (fake_backend *)_backend;
+
+	git__free(backend);
+}
+
+static int build_fake_backend(
+	git_odb_backend **out,
+	git_error_code error_code)
+{
+	fake_backend *backend;
+
+	backend = git__calloc(1, sizeof(fake_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+	backend->parent.refresh = NULL;
+	backend->error_code = error_code;
+
+	backend->parent.read = fake_backend__read;
+	backend->parent.read_prefix = fake_backend__read_prefix;
+	backend->parent.read_header = fake_backend__read_header;
+	backend->parent.exists = fake_backend__exists;
+	backend->parent.free = &fake_backend__free;
+
+	*out = (git_odb_backend *)backend;
+
+	return 0;
+}
+
+static void setup_repository_and_backend(git_error_code error_code)
+{
+	git_odb *odb = NULL;
+	git_odb_backend *backend = NULL;
+
+	_repo = cl_git_sandbox_init("testrepo.git");
+
+	cl_git_pass(build_fake_backend(&backend, error_code));
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_git_pass(git_odb_add_backend(odb, backend, 10));
+
+	_fake = (fake_backend *)backend;
+
+	cl_git_pass(git_oid_fromstr(&_oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+}
+
+void test_odb_backend_nonrefreshing__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void)
+{
+	git_odb *odb;
+
+	setup_repository_and_backend(GIT_ENOTFOUND);
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_assert_equal_b(false, git_odb_exists(odb, &_oid));
+
+	cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void)
+{
+	git_object *obj;
+
+	setup_repository_and_backend(GIT_ENOTFOUND);
+
+	cl_git_fail_with(
+		git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY),
+		GIT_ENOTFOUND);
+
+	cl_assert_equal_i(1, _fake->read_calls);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void)
+{
+	git_object *obj;
+
+	setup_repository_and_backend(GIT_ENOTFOUND);
+
+	cl_git_fail_with(
+		git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY),
+		GIT_ENOTFOUND);
+
+	cl_assert_equal_i(1, _fake->read_prefix_calls);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void)
+{
+	git_odb *odb;
+	size_t len;
+	git_otype type;
+
+	setup_repository_and_backend(GIT_ENOTFOUND);
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+	cl_git_fail_with(
+		git_odb_read_header(&len, &type, odb, &_oid),
+		GIT_ENOTFOUND);
+
+	cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void)
+{
+	git_odb *odb;
+
+	setup_repository_and_backend(GIT_OK);
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+	cl_assert_equal_b(true, git_odb_exists(odb, &_oid));
+
+	cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void)
+{
+	git_object *obj;
+
+	setup_repository_and_backend(GIT_OK);
+
+	cl_git_pass(git_object_lookup(&obj, _repo, &_oid, GIT_OBJ_ANY));
+
+	cl_assert_equal_i(1, _fake->read_calls);
+
+	git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void)
+{
+	git_object *obj;
+
+	setup_repository_and_backend(GIT_OK);
+
+	cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_oid, 7, GIT_OBJ_ANY));
+
+	cl_assert_equal_i(1, _fake->read_prefix_calls);
+
+	git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void)
+{
+	git_odb *odb;
+	size_t len;
+	git_otype type;
+
+	setup_repository_and_backend(GIT_OK);
+
+	cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+	cl_git_pass(git_odb_read_header(&len, &type, odb, &_oid));
+
+	cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full_oid(void)
+{
+	git_object *obj;
+
+	setup_repository_and_backend(GIT_ENOTFOUND);
+
+	cl_git_fail_with(
+		git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+		GIT_ENOTFOUND);
+
+	cl_assert_equal_i(1, _fake->read_calls);
+}
diff --git a/tests-clar/odb/foreach.c b/tests/odb/foreach.c
similarity index 95%
rename from tests-clar/odb/foreach.c
rename to tests/odb/foreach.c
index f643d96..ebb8866 100644
--- a/tests-clar/odb/foreach.c
+++ b/tests/odb/foreach.c
@@ -27,7 +27,7 @@
 }
 
 /*
- * $ git --git-dir tests-clar/resources/testrepo.git count-objects --verbose
+ * $ git --git-dir tests/resources/testrepo.git count-objects --verbose
  * count: 47
  * size: 4
  * in-pack: 1640
diff --git a/tests/odb/loose.c b/tests/odb/loose.c
new file mode 100644
index 0000000..a85f143
--- /dev/null
+++ b/tests/odb/loose.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "git2/odb_backend.h"
+#include "posix.h"
+#include "loose_data.h"
+
+#ifdef __ANDROID_API__
+# define S_IREAD        S_IRUSR
+# define S_IWRITE       S_IWUSR
+#endif
+
+static void write_object_files(object_data *d)
+{
+	int fd;
+
+	if (p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0)
+		cl_assert(errno == EEXIST);
+
+	cl_assert((fd = p_creat(d->file, S_IREAD | S_IWRITE)) >= 0);
+	cl_must_pass(p_write(fd, d->bytes, d->blen));
+
+	p_close(fd);
+}
+
+static void cmp_objects(git_rawobj *o, object_data *d)
+{
+	cl_assert(o->type == git_object_string2type(d->type));
+	cl_assert(o->len == d->dlen);
+
+	if (o->len > 0)
+		cl_assert(memcmp(o->data, d->data, o->len) == 0);
+}
+
+static void test_read_object(object_data *data)
+{
+    git_oid id;
+    git_odb_object *obj;
+	git_odb *odb;
+	git_rawobj tmp;
+
+    write_object_files(data);
+
+    cl_git_pass(git_odb_open(&odb, "test-objects"));
+    cl_git_pass(git_oid_fromstr(&id, data->id));
+    cl_git_pass(git_odb_read(&obj, odb, &id));
+
+	tmp.data = obj->buffer;
+	tmp.len = obj->cached.size;
+	tmp.type = obj->cached.type;
+
+    cmp_objects(&tmp, data);
+
+    git_odb_object_free(obj);
+	git_odb_free(odb);
+}
+
+void test_odb_loose__initialize(void)
+{
+	cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
+}
+
+void test_odb_loose__cleanup(void)
+{
+	cl_fixture_cleanup("test-objects");
+}
+
+void test_odb_loose__exists(void)
+{
+    git_oid id, id2;
+	git_odb *odb;
+
+    write_object_files(&one);
+	cl_git_pass(git_odb_open(&odb, "test-objects"));
+
+    cl_git_pass(git_oid_fromstr(&id, one.id));
+
+    cl_assert(git_odb_exists(odb, &id));
+
+	/* Test for a non-existant object */
+    cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa"));
+    cl_assert(!git_odb_exists(odb, &id2));
+
+	git_odb_free(odb);
+}
+
+void test_odb_loose__simple_reads(void)
+{
+	test_read_object(&commit);
+	test_read_object(&tree);
+	test_read_object(&tag);
+	test_read_object(&zero);
+	test_read_object(&one);
+	test_read_object(&two);
+	test_read_object(&some);
+}
+
+void test_write_object_permission(
+	mode_t dir_mode, mode_t file_mode,
+	mode_t expected_dir_mode, mode_t expected_file_mode)
+{
+	git_odb *odb;
+	git_odb_backend *backend;
+	git_oid oid;
+	struct stat statbuf;
+	mode_t mask, os_mask;
+
+	/* Windows does not return group/user bits from stat,
+	* files are never executable.
+	*/
+#ifdef GIT_WIN32
+	os_mask = 0600;
+#else
+	os_mask = 0777;
+#endif
+
+	mask = p_umask(0);
+	p_umask(mask);
+
+	cl_git_pass(git_odb_new(&odb));
+	cl_git_pass(git_odb_backend_loose(&backend, "test-objects", -1, 0, dir_mode, file_mode));
+	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));
+
+	cl_git_pass(p_stat("test-objects/67", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_dir_mode & ~mask) & os_mask);
+
+	cl_git_pass(p_stat("test-objects/67/b808feb36201507a77f85e6d898f0a2836e4a5", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_file_mode & ~mask) & os_mask);
+
+	git_odb_free(odb);
+}
+
+void test_odb_loose__permissions_standard(void)
+{
+	test_write_object_permission(0, 0, GIT_OBJECT_DIR_MODE, GIT_OBJECT_FILE_MODE);
+}
+
+void test_odb_loose_permissions_readonly(void)
+{
+	test_write_object_permission(0777, 0444, 0777, 0444);
+}
+
+void test_odb_loose__permissions_readwrite(void)
+{
+	test_write_object_permission(0777, 0666, 0777, 0666);
+}
diff --git a/tests-clar/odb/loose_data.h b/tests/odb/loose_data.h
similarity index 100%
rename from tests-clar/odb/loose_data.h
rename to tests/odb/loose_data.h
diff --git a/tests/odb/mixed.c b/tests/odb/mixed.c
new file mode 100644
index 0000000..51970ce
--- /dev/null
+++ b/tests/odb/mixed.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+
+static git_odb *_odb;
+
+void test_odb_mixed__initialize(void)
+{
+	cl_git_pass(git_odb_open(&_odb, cl_fixture("duplicate.git/objects")));
+}
+
+void test_odb_mixed__cleanup(void)
+{
+	git_odb_free(_odb);
+	_odb = NULL;
+}
+
+void test_odb_mixed__dup_oid(void) {
+	const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
+	const char short_hex[] = "ce01362";
+	git_oid oid;
+	git_odb_object *obj;
+
+	cl_git_pass(git_oid_fromstr(&oid, hex));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ));
+	git_odb_object_free(obj);
+	cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1));
+	git_odb_object_free(obj);
+}
+
+/* some known sha collisions of file content:
+ *   'aabqhq' and 'aaazvc' with prefix 'dea509d0' (+ '9' and + 'b')
+ *   'aaeufo' and 'aaaohs' with prefix '81b5bff5' (+ 'f' and + 'b')
+ *   'aafewy' and 'aaepta' with prefix '739e3c4c'
+ *   'aahsyn' and 'aadrjg' with prefix '0ddeaded' (+ '9' and + 'e')
+ */
+
+void test_odb_mixed__dup_oid_prefix_0(void) {
+	char hex[10];
+	git_oid oid;
+	git_odb_object *obj;
+
+	/* ambiguous in the same pack file */
+
+	strncpy(hex, "dea509d0", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_assert_equal_i(
+		GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+	strncpy(hex, "dea509d09", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+
+	strncpy(hex, "dea509d0b", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+
+	/* ambiguous in different pack files */
+
+	strncpy(hex, "81b5bff5", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_assert_equal_i(
+		GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+	strncpy(hex, "81b5bff5b", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+
+	strncpy(hex, "81b5bff5f", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+
+	/* ambiguous in pack file and loose */
+
+	strncpy(hex, "0ddeaded", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_assert_equal_i(
+		GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+
+	strncpy(hex, "0ddeaded9", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+
+	strncpy(hex, "0ddeadede", sizeof(hex));
+	cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex)));
+	cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+	git_odb_object_free(obj);
+}
diff --git a/tests-clar/odb/pack_data.h b/tests/odb/pack_data.h
similarity index 100%
rename from tests-clar/odb/pack_data.h
rename to tests/odb/pack_data.h
diff --git a/tests-clar/odb/pack_data_one.h b/tests/odb/pack_data_one.h
similarity index 100%
rename from tests-clar/odb/pack_data_one.h
rename to tests/odb/pack_data_one.h
diff --git a/tests-clar/odb/packed.c b/tests/odb/packed.c
similarity index 100%
rename from tests-clar/odb/packed.c
rename to tests/odb/packed.c
diff --git a/tests-clar/odb/packed_one.c b/tests/odb/packed_one.c
similarity index 100%
rename from tests-clar/odb/packed_one.c
rename to tests/odb/packed_one.c
diff --git a/tests-clar/odb/sorting.c b/tests/odb/sorting.c
similarity index 100%
rename from tests-clar/odb/sorting.c
rename to tests/odb/sorting.c
diff --git a/tests/odb/streamwrite.c b/tests/odb/streamwrite.c
new file mode 100644
index 0000000..591a200
--- /dev/null
+++ b/tests/odb/streamwrite.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+static git_repository *repo;
+static git_odb *odb;
+static git_odb_stream *stream;
+
+void test_odb_streamwrite__initialize(void)
+{
+	repo = cl_git_sandbox_init("testrepo.git");
+	cl_git_pass(git_repository_odb(&odb, repo));
+
+	cl_git_pass(git_odb_open_wstream(&stream, odb, 14, GIT_OBJ_BLOB));
+	cl_assert_equal_sz(14, stream->declared_size);
+}
+
+void test_odb_streamwrite__cleanup(void)
+{
+	git_odb_stream_free(stream);
+	git_odb_free(odb);
+	cl_git_sandbox_cleanup();
+}
+
+void test_odb_streamwrite__can_accept_chunks(void)
+{
+	git_oid oid;
+
+	cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+	cl_assert_equal_sz(8, stream->received_bytes);
+
+	cl_git_pass(git_odb_stream_write(stream, "deadbeef", 6));
+	cl_assert_equal_sz(8 + 6, stream->received_bytes);
+
+	cl_git_pass(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_missing_bytes(void)
+{
+	git_oid oid;
+
+	cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+	cl_assert_equal_sz(8, stream->received_bytes);
+
+	cl_git_pass(git_odb_stream_write(stream, "deadbeef", 4));
+	cl_assert_equal_sz(8 + 4, stream->received_bytes);
+
+	cl_git_fail(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_additional_bytes(void)
+{
+	cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+	cl_assert_equal_sz(8, stream->received_bytes);
+
+	cl_git_fail(git_odb_stream_write(stream, "deadbeef", 7));
+}
diff --git a/tests-clar/online/clone.c b/tests/online/clone.c
similarity index 62%
rename from tests-clar/online/clone.c
rename to tests/online/clone.c
index bc4285a..efc76d9 100644
--- a/tests-clar/online/clone.c
+++ b/tests/online/clone.c
@@ -11,6 +11,7 @@
 #define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git"
 #define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git"
 #define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git"
+#define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git"
 
 static git_repository *g_repo;
 static git_clone_options g_options;
@@ -18,6 +19,7 @@
 void test_online_clone__initialize(void)
 {
 	git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT;
+	git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
 
 	g_repo = NULL;
 
@@ -25,6 +27,7 @@
 	g_options.version = GIT_CLONE_OPTIONS_VERSION;
 	g_options.checkout_opts = dummy_opts;
 	g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+	g_options.remote_callbacks = dummy_callbacks;
 }
 
 void test_online_clone__cleanup(void)
@@ -69,7 +72,7 @@
 	cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./foo", &g_options));
 
 	cl_assert_equal_i(true, git_repository_is_empty(g_repo));
-	cl_assert_equal_i(true, git_repository_head_orphan(g_repo));
+	cl_assert_equal_i(true, git_repository_head_unborn(g_repo));
 
 	cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
 	cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
@@ -103,8 +106,8 @@
 	g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
 	g_options.checkout_opts.progress_cb = &checkout_progress;
 	g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
-	g_options.fetch_progress_cb = &fetch_progress;
-	g_options.fetch_progress_payload = &fetch_progress_cb_was_called;
+	g_options.remote_callbacks.transfer_progress = &fetch_progress;
+	g_options.remote_callbacks.payload = &fetch_progress_cb_was_called;
 
 	cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
 
@@ -122,6 +125,45 @@
 	git_buf_free(&path);
 }
 
+void test_online_clone__clone_into(void)
+{
+	git_buf path = GIT_BUF_INIT;
+	git_remote *remote;
+	git_reference *head;
+	git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+	bool checkout_progress_cb_was_called = false,
+		  fetch_progress_cb_was_called = false;
+
+	checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
+	checkout_opts.progress_cb = &checkout_progress;
+	checkout_opts.progress_payload = &checkout_progress_cb_was_called;
+
+	cl_git_pass(git_repository_init(&g_repo, "./foo", false));
+	cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL));
+
+	callbacks.transfer_progress = &fetch_progress;
+	callbacks.payload = &fetch_progress_cb_was_called;
+	git_remote_set_callbacks(remote, &callbacks);
+
+	cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL));
+
+	cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+	cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path)));
+
+	cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+	cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head));
+	cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+	cl_assert_equal_i(true, checkout_progress_cb_was_called);
+	cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+	git_remote_free(remote);
+	git_reference_free(head);
+	git_buf_free(&path);
+}
+
 static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
 {
 	int *callcount = (int*)payload;
@@ -132,17 +174,50 @@
 
 void test_online_clone__custom_remote_callbacks(void)
 {
-	git_remote_callbacks remote_callbacks = GIT_REMOTE_CALLBACKS_INIT;
 	int callcount = 0;
 
-	g_options.remote_callbacks = &remote_callbacks;
-	remote_callbacks.update_tips = update_tips;
-	remote_callbacks.payload = &callcount;
+	g_options.remote_callbacks.update_tips = update_tips;
+	g_options.remote_callbacks.payload = &callcount;
 
 	cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
 	cl_assert(callcount > 0);
 }
 
+static int cred_failure_cb(
+	git_cred **cred,
+	const char *url,
+	const char *username_from_url,
+	unsigned int allowed_types,
+	void *data)
+{
+	GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url);
+	GIT_UNUSED(allowed_types); GIT_UNUSED(data);
+	return -1;
+}
+
+void test_online_clone__cred_callback_failure_is_euser(void)
+{
+	const char *remote_url = cl_getenv("GITTEST_REMOTE_URL");
+	const char *remote_user = cl_getenv("GITTEST_REMOTE_USER");
+	const char *remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
+	int error;
+
+	if (!remote_url) {
+		printf("GITTEST_REMOTE_URL unset; skipping clone test\n");
+		return;
+	}
+
+	if (!remote_user && !remote_default) {
+		printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_DEFAULT unset; skipping clone test\n");
+		return;
+	}
+
+	g_options.remote_callbacks.credentials = cred_failure_cb;
+
+	cl_git_fail(error = git_clone(&g_repo, remote_url, "./foo", &g_options));
+	cl_assert_equal_i(error, GIT_EUSER);
+}
+
 void test_online_clone__credentials(void)
 {
 	/* Remote URL environment variable must be set.  User and password are optional.  */
@@ -154,8 +229,8 @@
 
 	if (!remote_url) return;
 
-	g_options.cred_acquire_cb = git_cred_userpass;
-	g_options.cred_acquire_payload = &user_pass;
+	g_options.remote_callbacks.credentials = git_cred_userpass;
+	g_options.remote_callbacks.payload = &user_pass;
 
 	cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options));
 	git_repository_free(g_repo); g_repo = NULL;
@@ -168,8 +243,8 @@
 		"libgit2", "libgit2"
 	};
 
-	g_options.cred_acquire_cb = git_cred_userpass;
-	g_options.cred_acquire_payload = &user_pass;
+	g_options.remote_callbacks.credentials = git_cred_userpass;
+	g_options.remote_callbacks.payload = &user_pass;
 
 	cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
 	git_repository_free(g_repo); g_repo = NULL;
@@ -188,6 +263,11 @@
 	cl_fixture_cleanup("./foo");
 }
 
+void test_online_clone__assembla_style(void)
+{
+	cl_git_pass(git_clone(&g_repo, ASSEMBLA_REPO_URL, "./foo", NULL));
+}
+
 static int cancel_at_half(const git_transfer_progress *stats, void *payload)
 {
 	GIT_UNUSED(payload);
@@ -199,6 +279,13 @@
 
 void test_online_clone__can_cancel(void)
 {
-	g_options.fetch_progress_cb = cancel_at_half;
+	g_options.remote_callbacks.transfer_progress = cancel_at_half;
+
 	cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER);
 }
+
+
+
+
+
+
diff --git a/tests-clar/online/fetch.c b/tests/online/fetch.c
similarity index 78%
rename from tests-clar/online/fetch.c
rename to tests/online/fetch.c
index bfa1eb9..5153a7a 100644
--- a/tests-clar/online/fetch.c
+++ b/tests/online/fetch.c
@@ -38,14 +38,16 @@
 	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
 	size_t bytes_received = 0;
 
+	callbacks.transfer_progress = progress;
 	callbacks.update_tips = update_tips;
+	callbacks.payload = &bytes_received;
 	counter = 0;
 
 	cl_git_pass(git_remote_create(&remote, _repo, "test", url));
 	git_remote_set_callbacks(remote, &callbacks);
 	git_remote_set_autotag(remote, flag);
 	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-	cl_git_pass(git_remote_download(remote, progress, &bytes_received));
+	cl_git_pass(git_remote_download(remote));
 	cl_git_pass(git_remote_update_tips(remote));
 	git_remote_disconnect(remote);
 	cl_assert_equal_i(counter, n);
@@ -64,6 +66,11 @@
 	do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
 }
 
+void test_online_fetch__default_https(void)
+{
+	do_fetch("https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
 void test_online_fetch__no_tags_git(void)
 {
 	do_fetch("git://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
@@ -88,6 +95,7 @@
 	git_repository *_repository;
 	bool invoked = false;
 	git_remote *remote;
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
 	git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
 	opts.bare = true;
 
@@ -102,7 +110,10 @@
 
 	cl_assert_equal_i(false, invoked);
 
-	cl_git_pass(git_remote_download(remote, &transferProgressCallback, &invoked));
+	callbacks.transfer_progress = &transferProgressCallback;
+	callbacks.payload = &invoked;
+	git_remote_set_callbacks(remote, &callbacks);
+	cl_git_pass(git_remote_download(remote));
 
 	cl_assert_equal_i(false, invoked);
 
@@ -126,38 +137,35 @@
 {
 	git_remote *remote;
 	size_t bytes_received = 0;
+	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
 
 	cl_git_pass(git_remote_create(&remote, _repo, "test",
 				"http://github.com/libgit2/TestGitRepository.git"));
+
+	callbacks.transfer_progress = cancel_at_half;
+	callbacks.payload = &bytes_received;
+	git_remote_set_callbacks(remote, &callbacks);
+
 	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-	cl_git_fail_with(git_remote_download(remote, cancel_at_half, &bytes_received), GIT_EUSER);
+	cl_git_fail_with(git_remote_download(remote), GIT_EUSER);
 	git_remote_disconnect(remote);
 	git_remote_free(remote);
 }
 
-int ls_cb(git_remote_head *rhead, void *payload)
-{
-	int *nr = payload;
-	GIT_UNUSED(rhead);
-
-	(*nr)++;
-
-	return 0;
-}
-
 void test_online_fetch__ls_disconnected(void)
 {
+	const git_remote_head **refs;
+	size_t refs_len_before, refs_len_after;
 	git_remote *remote;
-	int nr_before = 0, nr_after = 0;
 
 	cl_git_pass(git_remote_create(&remote, _repo, "test",
 				"http://github.com/libgit2/TestGitRepository.git"));
 	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-	cl_git_pass(git_remote_ls(remote, ls_cb, &nr_before));
+	cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote));
 	git_remote_disconnect(remote);
-	cl_git_pass(git_remote_ls(remote, ls_cb, &nr_after));
+	cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote));
 
-	cl_assert_equal_i(nr_before, nr_after);
+	cl_assert_equal_i(refs_len_before, refs_len_after);
 
 	git_remote_free(remote);
 }
diff --git a/tests-clar/online/fetchhead.c b/tests/online/fetchhead.c
similarity index 92%
rename from tests-clar/online/fetchhead.c
rename to tests/online/fetchhead.c
index 58717ee..57b183f 100644
--- a/tests-clar/online/fetchhead.c
+++ b/tests/online/fetchhead.c
@@ -12,10 +12,12 @@
 
 void test_online_fetchhead__initialize(void)
 {
+	git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT;
 	g_repo = NULL;
 
 	memset(&g_options, 0, sizeof(git_clone_options));
 	g_options.version = GIT_CLONE_OPTIONS_VERSION;
+	g_options.remote_callbacks = dummy_callbacks;
 }
 
 void test_online_fetchhead__cleanup(void)
@@ -48,7 +50,7 @@
 	}
 
 	cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH));
-	cl_git_pass(git_remote_download(remote, NULL, NULL));
+	cl_git_pass(git_remote_download(remote));
 	cl_git_pass(git_remote_update_tips(remote));
 	git_remote_disconnect(remote);
 	git_remote_free(remote);
diff --git a/tests-clar/online/push.c b/tests/online/push.c
similarity index 71%
rename from tests-clar/online/push.c
rename to tests/online/push.c
index 5dc7974..be505c3 100644
--- a/tests-clar/online/push.c
+++ b/tests/online/push.c
@@ -10,11 +10,19 @@
 static git_repository *_repo;
 
 static char *_remote_url;
+
+static char *_remote_ssh_key;
+static char *_remote_ssh_pubkey;
+static char *_remote_ssh_passphrase;
+
 static char *_remote_user;
 static char *_remote_pass;
 
+static char *_remote_default;
+
+static int cred_acquire_cb(git_cred **,	const char *, const char *, unsigned int, void *);
+
 static git_remote *_remote;
-static bool _cred_acquire_called;
 static record_callbacks_data _record_cbs_data = {{ 0 }};
 static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
 
@@ -29,28 +37,54 @@
 static git_oid _tag_tree;
 static git_oid _tag_blob;
 static git_oid _tag_lightweight;
+static git_oid _tag_tag;
 
 static int cred_acquire_cb(
-		git_cred **cred,
-		const char *url,
-		const char *user_from_url,
-		unsigned int allowed_types,
-		void *payload)
+	git_cred **cred,
+	const char *url,
+	const char *user_from_url,
+	unsigned int allowed_types,
+	void *payload)
 {
 	GIT_UNUSED(url);
 	GIT_UNUSED(user_from_url);
+	GIT_UNUSED(payload);
 
-	*((bool*)payload) = true;
+	if (GIT_CREDTYPE_DEFAULT & allowed_types) {
+		if (!_remote_default) {
+			printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
+			return -1;
+		}
 
-	if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
-		git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass) < 0)
-		return -1;
+		return git_cred_default_new(cred);
+	}
 
-	return 0;
+	if (GIT_CREDTYPE_SSH_KEY & allowed_types) {
+		if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) {
+			printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n");
+			return -1;
+		}
+
+		return git_cred_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase);
+	}
+
+	if (GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) {
+		if (!_remote_user || !_remote_pass) {
+			printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_PASS must be set\n");
+			return -1;
+		}
+
+		return git_cred_userpass_plaintext_new(cred, _remote_user, _remote_pass);
+	}
+
+	return -1;
 }
 
+/* the results of a push status.  when used for expected values, msg may be NULL
+ * to indicate that it should not be matched. */
 typedef struct {
 	const char *ref;
+	int success;
 	const char *msg;
 } push_status;
 
@@ -65,6 +99,7 @@
 
 	cl_assert(s = git__malloc(sizeof(*s)));
 	s->ref = ref;
+	s->success = (msg == NULL);
 	s->msg = msg;
 
 	git_vector_insert(statuses, s);
@@ -86,9 +121,8 @@
 	else
 		git_vector_foreach(&actual, i, iter)
 			if (strcmp(expected[i].ref, iter->ref) ||
-				(expected[i].msg && !iter->msg) ||
-				(!expected[i].msg && iter->msg) ||
-				(expected[i].msg && iter->msg && strcmp(expected[i].msg, iter->msg))) {
+				(expected[i].success != iter->success) ||
+				(expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) {
 				failed = true;
 				break;
 			}
@@ -101,13 +135,17 @@
 		for(i = 0; i < expected_len; i++) {
 			git_buf_printf(&msg, "%s: %s\n",
 				expected[i].ref,
-				expected[i].msg ? expected[i].msg : "<NULL>");
+				expected[i].success ? "success" : "failed");
 		}
 
 		git_buf_puts(&msg, "\nACTUAL:\n");
 
-		git_vector_foreach(&actual, i, iter)
-			git_buf_printf(&msg, "%s: %s\n", iter->ref, iter->msg);
+		git_vector_foreach(&actual, i, iter) {
+			if (iter->success)
+				git_buf_printf(&msg, "%s: success\n", iter->ref);
+			else
+				git_buf_printf(&msg, "%s: failed with message: %s", iter->ref, iter->msg);
+		}
 
 		cl_fail(git_buf_cstr(&msg));
 
@@ -130,24 +168,11 @@
  */
 static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
 {
-	git_vector actual_refs = GIT_VECTOR_INIT;
+	const git_remote_head **actual_refs;
+	size_t actual_refs_len;
 
-	git_remote_ls(remote, record_ref_cb, &actual_refs);
-	verify_remote_refs(&actual_refs, expected_refs, expected_refs_len);
-
-	git_vector_free(&actual_refs);
-}
-
-static int tracking_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
-{
-	git_vector *tracking = (git_vector *)payload;
-
-	if (branch_type == GIT_BRANCH_REMOTE)
-		git_vector_insert(tracking, git__strdup(branch_name));
-	else
-		GIT_UNUSED(branch_name);
-
-	return 0;
+	git_remote_ls(&actual_refs, &actual_refs_len, remote);
+	verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len);
 }
 
 /**
@@ -164,14 +189,24 @@
 	size_t i, j;
 	git_buf msg = GIT_BUF_INIT;
 	git_buf ref_name = GIT_BUF_INIT;
-	git_buf canonical_ref_name = GIT_BUF_INIT;
 	git_vector actual_refs = GIT_VECTOR_INIT;
+	git_branch_iterator *iter;
 	char *actual_ref;
 	git_oid oid;
-	int failed = 0;
+	int failed = 0, error;
+	git_branch_t branch_type;
+	git_reference *ref;
 
 	/* Get current remote branches */
-	cl_git_pass(git_branch_foreach(remote->repo, GIT_BRANCH_REMOTE, tracking_branch_list_cb, &actual_refs));
+	cl_git_pass(git_branch_iterator_new(&iter, remote->repo, GIT_BRANCH_REMOTE));
+
+	while ((error = git_branch_next(&ref, &branch_type, iter)) == 0) {
+		cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
+
+		cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
+	}
+
+	cl_assert_equal_i(error, GIT_ITEROVER);
 
 	/* Loop through expected refs, make sure they exist */
 	for (i = 0; i < expected_refs_len; i++) {
@@ -187,11 +222,7 @@
 
 		/* Find matching remote branch */
 		git_vector_foreach(&actual_refs, j, actual_ref) {
-
-			/* Construct canonical ref name from the actual_ref name */
-			git_buf_clear(&canonical_ref_name);
-			cl_git_pass(git_buf_printf(&canonical_ref_name, "refs/remotes/%s", actual_ref));
-			if (!strcmp(git_buf_cstr(&ref_name), git_buf_cstr(&canonical_ref_name)))
+			if (!strcmp(git_buf_cstr(&ref_name), actual_ref))
 				break;
 		}
 
@@ -202,7 +233,7 @@
 		}
 
 		/* Make sure tracking branch is at expected commit ID */
-		cl_git_pass(git_reference_name_to_id(&oid, remote->repo, git_buf_cstr(&canonical_ref_name)));
+		cl_git_pass(git_reference_name_to_id(&oid, remote->repo, actual_ref));
 
 		if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
 			git_buf_puts(&msg, "Tracking branch commit does not match expected ID.");
@@ -231,7 +262,6 @@
 
 	git_vector_free(&actual_refs);
 	git_buf_free(&msg);
-	git_buf_free(&canonical_ref_name);
 	git_buf_free(&ref_name);
 	return;
 }
@@ -239,9 +269,9 @@
 void test_online_push__initialize(void)
 {
 	git_vector delete_specs = GIT_VECTOR_INIT;
-	size_t i;
+	const git_remote_head **heads;
+	size_t i, heads_len;
 	char *curr_del_spec;
-	_cred_acquire_called = false;
 
 	_repo = cl_git_sandbox_init("push_src");
 
@@ -272,17 +302,21 @@
 	git_oid_fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e");
 	git_oid_fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498");
 	git_oid_fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce");
+	git_oid_fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5");
 
 	/* Remote URL environment variable must be set.  User and password are optional.  */
 	_remote_url = cl_getenv("GITTEST_REMOTE_URL");
 	_remote_user = cl_getenv("GITTEST_REMOTE_USER");
 	_remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
+	_remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+	_remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+	_remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+	_remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
 	_remote = NULL;
 
 	if (_remote_url) {
 		cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
 
-		git_remote_set_cred_acquire_cb(_remote, cred_acquire_cb, &_cred_acquire_called);
 		record_callbacks_data_clear(&_record_cbs_data);
 		git_remote_set_callbacks(_remote, &_record_cbs);
 
@@ -294,7 +328,8 @@
 		 * order to delete the remote branch pointed to by HEAD (usually master).
 		 * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
 		 */
-		cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs));
+		cl_git_pass(git_remote_ls(&heads, &heads_len, _remote));
+		cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len));
 		if (delete_specs.length) {
 			git_push *push;
 
@@ -314,7 +349,7 @@
 
 		/* Now that we've deleted everything, fetch from the remote */
 		cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH));
-		cl_git_pass(git_remote_download(_remote, NULL, NULL));
+		cl_git_pass(git_remote_download(_remote));
 		cl_git_pass(git_remote_update_tips(_remote));
 		git_remote_disconnect(_remote);
 	} else
@@ -336,6 +371,22 @@
 	cl_git_sandbox_cleanup();
 }
 
+static int push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload)
+{
+	int *was_called = (int *) payload;
+	GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total);
+	*was_called = 1;
+	return 0;
+}
+
+static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload)
+{
+	int *was_called = (int *) payload;
+	GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
+	*was_called = 1;
+	return 0;
+}
+
 /**
  * Calls push and relists refs on remote to verify success.
  *
@@ -344,15 +395,17 @@
  * @param expected_refs expected remote refs after push
  * @param expected_refs_len length of expected_refs
  * @param expected_ret expected return value from git_push_finish()
+ * @param check_progress_cb Check that the push progress callbacks are called
  */
 static void do_push(const char *refspecs[], size_t refspecs_len,
 	push_status expected_statuses[], size_t expected_statuses_len,
-	expected_ref expected_refs[], size_t expected_refs_len, int expected_ret)
+	expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb)
 {
 	git_push *push;
 	git_push_options opts = GIT_PUSH_OPTIONS_INIT;
 	size_t i;
 	int ret;
+	int pack_progress_called = 0, transfer_progress_called = 0;
 
 	if (_remote) {
 		/* Auto-detect the number of threads to use */
@@ -363,6 +416,9 @@
 		cl_git_pass(git_push_new(&push, _remote));
 		cl_git_pass(git_push_set_options(push, &opts));
 
+		if (check_progress_cb)
+			cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called));
+
 		for (i = 0; i < refspecs_len; i++)
 			cl_git_pass(git_push_add_refspec(push, refspecs[i]));
 
@@ -375,6 +431,11 @@
 			cl_assert_equal_i(1, git_push_unpack_ok(push));
 		}
 
+		if (check_progress_cb) {
+			cl_assert_equal_i(1, pack_progress_called);
+			cl_assert_equal_i(1, transfer_progress_called);
+		}
+
 		do_verify_push_status(push, expected_statuses, expected_statuses_len);
 
 		cl_assert_equal_i(expected_ret, ret);
@@ -393,57 +454,57 @@
 /* Call push_finish() without ever calling git_push_add_refspec() */
 void test_online_push__noop(void)
 {
-	do_push(NULL, 0, NULL, 0, NULL, 0, 0);
+	do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0);
 }
 
 void test_online_push__b1(void)
 {
 	const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
-	push_status exp_stats[] = { { "refs/heads/b1", NULL } };
+	push_status exp_stats[] = { { "refs/heads/b1", 1 } };
 	expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__b2(void)
 {
 	const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
-	push_status exp_stats[] = { { "refs/heads/b2", NULL } };
+	push_status exp_stats[] = { { "refs/heads/b2", 1 } };
 	expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__b3(void)
 {
 	const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
-	push_status exp_stats[] = { { "refs/heads/b3", NULL } };
+	push_status exp_stats[] = { { "refs/heads/b3", 1 } };
 	expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__b4(void)
 {
 	const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
-	push_status exp_stats[] = { { "refs/heads/b4", NULL } };
+	push_status exp_stats[] = { { "refs/heads/b4", 1 } };
 	expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__b5(void)
 {
 	const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
-	push_status exp_stats[] = { { "refs/heads/b5", NULL } };
+	push_status exp_stats[] = { { "refs/heads/b5", 1 } };
 	expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__multi(void)
@@ -456,11 +517,11 @@
 		"refs/heads/b5:refs/heads/b5"
 	};
 	push_status exp_stats[] = {
-		{ "refs/heads/b1", NULL },
-		{ "refs/heads/b2", NULL },
-		{ "refs/heads/b3", NULL },
-		{ "refs/heads/b4", NULL },
-		{ "refs/heads/b5", NULL }
+		{ "refs/heads/b1", 1 },
+		{ "refs/heads/b2", 1 },
+		{ "refs/heads/b3", 1 },
+		{ "refs/heads/b4", 1 },
+		{ "refs/heads/b5", 1 }
 	};
 	expected_ref exp_refs[] = {
 		{ "refs/heads/b1", &_oid_b1 },
@@ -471,17 +532,17 @@
 	};
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__implicit_tgt(void)
 {
 	const char *specs1[] = { "refs/heads/b1:" };
-	push_status exp_stats1[] = { { "refs/heads/b1", NULL } };
+	push_status exp_stats1[] = { { "refs/heads/b1", 1 } };
 	expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
 
 	const char *specs2[] = { "refs/heads/b2:" };
-	push_status exp_stats2[] = { { "refs/heads/b2", NULL } };
+	push_status exp_stats2[] = { { "refs/heads/b2", 1 } };
 	expected_ref exp_refs2[] = {
 	{ "refs/heads/b1", &_oid_b1 },
 	{ "refs/heads/b2", &_oid_b2 }
@@ -489,10 +550,10 @@
 
 	do_push(specs1, ARRAY_SIZE(specs1),
 		exp_stats1, ARRAY_SIZE(exp_stats1),
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
 	do_push(specs2, ARRAY_SIZE(specs2),
 		exp_stats2, ARRAY_SIZE(exp_stats2),
-		exp_refs2, ARRAY_SIZE(exp_refs2), 0);
+		exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0);
 }
 
 void test_online_push__fast_fwd(void)
@@ -500,11 +561,11 @@
 	/* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
 
 	const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
-	push_status exp_stats_init[] = { { "refs/heads/b1", NULL } };
+	push_status exp_stats_init[] = { { "refs/heads/b1", 1 } };
 	expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
 
 	const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
-	push_status exp_stats_ff[] = { { "refs/heads/b1", NULL } };
+	push_status exp_stats_ff[] = { { "refs/heads/b1", 1 } };
 	expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
 
 	/* Do a force push to reset b1 in target back to _oid_b1 */
@@ -514,85 +575,95 @@
 
 	do_push(specs_init, ARRAY_SIZE(specs_init),
 		exp_stats_init, ARRAY_SIZE(exp_stats_init),
-		exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
+		exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1);
 
 	do_push(specs_ff, ARRAY_SIZE(specs_ff),
 		exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
-		exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
+		exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
 
 	do_push(specs_reset, ARRAY_SIZE(specs_reset),
 		exp_stats_init, ARRAY_SIZE(exp_stats_init),
-		exp_refs_init, ARRAY_SIZE(exp_refs_init), 0);
+		exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0);
 
 	do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
 		exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
-		exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0);
+		exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0);
 }
 
 void test_online_push__tag_commit(void)
 {
 	const char *specs[] = { "refs/tags/tag-commit:refs/tags/tag-commit" };
-	push_status exp_stats[] = { { "refs/tags/tag-commit", NULL } };
+	push_status exp_stats[] = { { "refs/tags/tag-commit", 1 } };
 	expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__tag_tree(void)
 {
 	const char *specs[] = { "refs/tags/tag-tree:refs/tags/tag-tree" };
-	push_status exp_stats[] = { { "refs/tags/tag-tree", NULL } };
+	push_status exp_stats[] = { { "refs/tags/tag-tree", 1 } };
 	expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__tag_blob(void)
 {
 	const char *specs[] = { "refs/tags/tag-blob:refs/tags/tag-blob" };
-	push_status exp_stats[] = { { "refs/tags/tag-blob", NULL } };
+	push_status exp_stats[] = { { "refs/tags/tag-blob", 1 } };
 	expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 }
 
 void test_online_push__tag_lightweight(void)
 {
 	const char *specs[] = { "refs/tags/tag-lightweight:refs/tags/tag-lightweight" };
-	push_status exp_stats[] = { { "refs/tags/tag-lightweight", NULL } };
+	push_status exp_stats[] = { { "refs/tags/tag-lightweight", 1 } };
 	expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
+}
+
+void test_online_push__tag_to_tag(void)
+{
+	const char *specs[] = { "refs/tags/tag-tag:refs/tags/tag-tag" };
+	push_status exp_stats[] = { { "refs/tags/tag-tag", 1 } };
+	expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
+	do_push(specs, ARRAY_SIZE(specs),
+		exp_stats, ARRAY_SIZE(exp_stats),
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 0);
 }
 
 void test_online_push__force(void)
 {
 	const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
-	push_status exp_stats1[] = { { "refs/heads/tgt", NULL } };
+	push_status exp_stats1[] = { { "refs/heads/tgt", 1 } };
 	expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
 
 	const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
 
 	const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
-	push_status exp_stats2_force[] = { { "refs/heads/tgt", NULL } };
+	push_status exp_stats2_force[] = { { "refs/heads/tgt", 1 } };
 	expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
 
 	do_push(specs1, ARRAY_SIZE(specs1),
 		exp_stats1, ARRAY_SIZE(exp_stats1),
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
 
 	do_push(specs2, ARRAY_SIZE(specs2),
 		NULL, 0,
-		exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD);
+		exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0);
 
 	/* Non-fast-forward update with force should pass. */
 	do_push(specs2_force, ARRAY_SIZE(specs2_force),
 		exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
-		exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0);
+		exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1);
 }
 
 void test_online_push__delete(void)
@@ -602,8 +673,8 @@
 		"refs/heads/b1:refs/heads/tgt2"
 	};
 	push_status exp_stats1[] = {
-		{ "refs/heads/tgt1", NULL },
-		{ "refs/heads/tgt2", NULL }
+		{ "refs/heads/tgt1", 1 },
+		{ "refs/heads/tgt2", 1 }
 	};
 	expected_ref exp_refs1[] = {
 		{ "refs/heads/tgt1", &_oid_b1 },
@@ -613,17 +684,17 @@
 	const char *specs_del_fake[] = { ":refs/heads/fake" };
 	/* Force has no effect for delete. */
 	const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
-	push_status exp_stats_fake[] = { { "refs/heads/fake", NULL } };
+	push_status exp_stats_fake[] = { { "refs/heads/fake", 1 } };
 
 	const char *specs_delete[] = { ":refs/heads/tgt1" };
-	push_status exp_stats_delete[] = { { "refs/heads/tgt1", NULL } };
+	push_status exp_stats_delete[] = { { "refs/heads/tgt1", 1 } };
 	expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
 	/* Force has no effect for delete. */
 	const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
 
 	do_push(specs1, ARRAY_SIZE(specs1),
 		exp_stats1, ARRAY_SIZE(exp_stats1),
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1);
 
 	/* When deleting a non-existent branch, the git client sends zero for both
 	 * the old and new commit id.  This should succeed on the server with the
@@ -633,23 +704,23 @@
 	 */
 	do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
 		exp_stats_fake, 1,
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
 	do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
 		exp_stats_fake, 1,
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
 
 	/* Delete one of the pushed branches. */
 	do_push(specs_delete, ARRAY_SIZE(specs_delete),
 		exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
-		exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
+		exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
 
 	/* Re-push branches and retry delete with force. */
 	do_push(specs1, ARRAY_SIZE(specs1),
 		exp_stats1, ARRAY_SIZE(exp_stats1),
-		exp_refs1, ARRAY_SIZE(exp_refs1), 0);
+		exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0);
 	do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
 		exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
-		exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0);
+		exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0);
 }
 
 void test_online_push__bad_refspecs(void)
@@ -675,17 +746,19 @@
 	/* TODO: Expressions in refspecs doesn't actually work yet */
 	const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" };
 
+	/* expect not NULL to indicate failure (core git replies "funny refname",
+	 * other servers may be less pithy. */
 	const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" };
-	push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", "funny refname" } };
+	push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", 0 } };
 
 	/* TODO: Find a more precise way of checking errors than a exit code of -1. */
 	do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
 		NULL, 0,
-		NULL, 0, -1);
+		NULL, 0, -1, 0);
 
 	do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr),
 		exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr),
-		NULL, 0, 0);
+		NULL, 0, 0, 1);
 }
 
 void test_online_push__notes(void)
@@ -693,7 +766,7 @@
 	git_oid note_oid, *target_oid, expected_oid;
 	git_signature *signature;
 	const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
-	push_status exp_stats[] = { { "refs/notes/commits", NULL } };
+	push_status exp_stats[] = { { "refs/notes/commits", 1 } };
 	expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
 	git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb");
 
@@ -705,7 +778,7 @@
 
 	do_push(specs, ARRAY_SIZE(specs),
 		exp_stats, ARRAY_SIZE(exp_stats),
-		exp_refs, ARRAY_SIZE(exp_refs), 0);
+		exp_refs, ARRAY_SIZE(exp_refs), 0, 1);
 
 	git_signature_free(signature);
 }
diff --git a/tests-clar/online/push_util.c b/tests/online/push_util.c
similarity index 70%
rename from tests-clar/online/push_util.c
rename to tests/online/push_util.c
index 2e45784..038c144 100644
--- a/tests-clar/online/push_util.c
+++ b/tests/online/push_util.c
@@ -44,20 +44,23 @@
 	return 0;
 }
 
-int delete_ref_cb(git_remote_head *head, void *payload)
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len)
 {
-	git_vector *delete_specs = (git_vector *)payload;
 	git_buf del_spec = GIT_BUF_INIT;
+	size_t i;
 
-	/* Ignore malformed ref names (which also saves us from tag^{} */
-	if (!git_reference_is_valid_name(head->name))
-		return 0;
+	for (i = 0; i < heads_len; i++) {
+		const git_remote_head *head = heads[i];
+		/* Ignore malformed ref names (which also saves us from tag^{} */
+		if (!git_reference_is_valid_name(head->name))
+			return 0;
 
-	/* Create a refspec that deletes a branch in the remote */
-	if (strcmp(head->name, "refs/heads/master")) {
-		cl_git_pass(git_buf_putc(&del_spec, ':'));
-		cl_git_pass(git_buf_puts(&del_spec, head->name));
-		cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec)));
+		/* Create a refspec that deletes a branch in the remote */
+		if (strcmp(head->name, "refs/heads/master")) {
+			cl_git_pass(git_buf_putc(&del_spec, ':'));
+			cl_git_pass(git_buf_puts(&del_spec, head->name));
+			cl_git_pass(git_vector_insert(out, git_buf_detach(&del_spec)));
+		}
 	}
 
 	return 0;
@@ -69,26 +72,28 @@
 	return git_vector_insert(refs, head);
 }
 
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len)
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len)
 {
 	size_t i, j = 0;
 	git_buf msg = GIT_BUF_INIT;
-	git_remote_head *actual;
+	const git_remote_head *actual;
 	char *oid_str;
 	bool master_present = false;
 
 	/* We don't care whether "master" is present on the other end or not */
-	git_vector_foreach(actual_refs, i, actual) {
+	for (i = 0; i < actual_refs_len; i++) {
+		actual = actual_refs[i];
 		if (!strcmp(actual->name, "refs/heads/master")) {
 			master_present = true;
 			break;
 		}
 	}
 
-	if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length)
+	if (expected_refs_len + (master_present ? 1 : 0) != actual_refs_len)
 		goto failed;
 
-	git_vector_foreach(actual_refs, i, actual) {
+	for (i = 0; i < actual_refs_len; i++) {
+		actual = actual_refs[i];
 		if (master_present && !strcmp(actual->name, "refs/heads/master"))
 			continue;
 
@@ -111,7 +116,8 @@
 	}
 
 	git_buf_puts(&msg, "\nACTUAL:\n");
-	git_vector_foreach(actual_refs, i, actual) {
+	for (i = 0; i < actual_refs_len; i++) {
+		actual = actual_refs[i];
 		if (master_present && !strcmp(actual->name, "refs/heads/master"))
 			continue;
 
diff --git a/tests-clar/online/push_util.h b/tests/online/push_util.h
similarity index 69%
rename from tests-clar/online/push_util.h
rename to tests/online/push_util.h
index 759122a..a7207c4 100644
--- a/tests-clar/online/push_util.h
+++ b/tests/online/push_util.h
@@ -12,7 +12,7 @@
  * @param data pointer to a record_callbacks_data instance
  */
 #define RECORD_CALLBACKS_INIT(data) \
-	{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, record_update_tips_cb, data }
+	{ GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, record_update_tips_cb, data }
 
 typedef struct {
 	char *name;
@@ -41,12 +41,13 @@
 int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
 
 /**
- * Callback for git_remote_list that adds refspecs to delete each ref
+ * Create a set of refspecs that deletes each of the inputs
  *
- * @param head a ref on the remote
- * @param payload a git_push instance
+ * @param out the vector in which to store the refspecs
+ * @param heads the remote heads
+ * @param heads_len the size of the array
  */
-int delete_ref_cb(git_remote_head *head, void *payload);
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len);
 
 /**
  * Callback for git_remote_list that adds refspecs to vector
@@ -60,10 +61,11 @@
  * Verifies that refs on remote stored by record_ref_cb match the expected
  * names, oids, and order.
  *
- * @param actual_refs actual refs stored by record_ref_cb()
+ * @param actual_refs actual refs in the remote
+ * @param actual_refs_len length of actual_refs
  * @param expected_refs expected remote refs
  * @param expected_refs_len length of expected_refs
  */
-void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len);
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len);
 
 #endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c
new file mode 100644
index 0000000..07963a9
--- /dev/null
+++ b/tests/pack/indexer.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "fileops.h"
+#include "hash.h"
+#include "iterator.h"
+#include "vector.h"
+#include "posix.h"
+
+
+/*
+ * This is a packfile with three objects. The second is a delta which
+ * depends on the third, which is also a delta.
+ */
+unsigned char out_of_order_pack[] = {
+  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
+  0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+  0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+  0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+  0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x75, 0x01,
+  0xd7, 0x71, 0x36, 0x66, 0xf4, 0xde, 0x82, 0x27, 0x76, 0xc7, 0x62, 0x2c,
+  0x10, 0xf1, 0xb0, 0x7d, 0xe2, 0x80, 0xdc, 0x78, 0x9c, 0x63, 0x62, 0x62,
+  0x62, 0xb7, 0x03, 0x00, 0x00, 0x69, 0x00, 0x4c, 0xde, 0x7d, 0xaa, 0xe4,
+  0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92,
+  0x6f, 0xae, 0x66, 0x75
+};
+unsigned int out_of_order_pack_len = 112;
+
+/*
+ * Packfile with two objects. The second is a delta against an object
+ * which is not in the packfile
+ */
+unsigned char thin_pack[] = {
+  0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+  0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+  0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+  0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+  0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x42, 0x52,
+  0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97,
+  0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04
+};
+unsigned int thin_pack_len = 78;
+
+unsigned char base_obj[] = { 07, 076 };
+unsigned int base_obj_len = 2;
+
+void test_pack_indexer__out_of_order(void)
+{
+	git_indexer *idx;
+	git_transfer_progress stats;
+
+	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+	cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats));
+	cl_git_pass(git_indexer_commit(idx, &stats));
+
+	cl_assert_equal_i(stats.total_objects, 3);
+	cl_assert_equal_i(stats.received_objects, 3);
+	cl_assert_equal_i(stats.indexed_objects, 3);
+
+	git_indexer_free(idx);
+}
+
+void test_pack_indexer__fix_thin(void)
+{
+	git_indexer *idx;
+	git_transfer_progress stats;
+	git_repository *repo;
+	git_odb *odb;
+	git_oid id, should_id;
+
+	cl_git_pass(git_repository_init(&repo, "thin.git", true));
+	cl_git_pass(git_repository_odb(&odb, repo));
+
+	/* Store the missing base into your ODB so the indexer can fix the pack */
+	cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJ_BLOB));
+	git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18");
+	cl_assert(!git_oid_cmp(&id, &should_id));
+
+	cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL));
+	cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
+	cl_git_pass(git_indexer_commit(idx, &stats));
+
+	cl_assert_equal_i(stats.total_objects, 2);
+	cl_assert_equal_i(stats.received_objects, 2);
+	cl_assert_equal_i(stats.indexed_objects, 2);
+	cl_assert_equal_i(stats.local_objects, 1);
+
+	git_oid_fromstr(&should_id, "11f0f69b334728fdd8bc86b80499f22f29d85b15");
+	cl_assert(!git_oid_cmp(git_indexer_hash(idx), &should_id));
+
+	git_indexer_free(idx);
+	git_odb_free(odb);
+	git_repository_free(repo);
+
+	/*
+	 * The pack's name/hash only tells us what objects there are,
+	 * so we need to go through the packfile again in order to
+	 * figure out whether we calculated the trailer correctly.
+	 */
+	{
+		unsigned char buffer[128];
+		int fd;
+		ssize_t read;
+		struct stat st;
+		const char *name = "pack-11f0f69b334728fdd8bc86b80499f22f29d85b15.pack";
+
+		fd = p_open(name, O_RDONLY);
+		cl_assert(fd != -1);
+
+		cl_git_pass(p_stat(name, &st));
+
+		cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+		read = p_read(fd, buffer, sizeof(buffer));
+		cl_assert(read != -1);
+		p_close(fd);
+
+		cl_git_pass(git_indexer_append(idx, buffer, read, &stats));
+		cl_git_pass(git_indexer_commit(idx, &stats));
+
+		cl_assert_equal_i(stats.total_objects, 3);
+		cl_assert_equal_i(stats.received_objects, 3);
+		cl_assert_equal_i(stats.indexed_objects, 3);
+		cl_assert_equal_i(stats.local_objects, 0);
+
+		git_indexer_free(idx);
+	}
+}
diff --git a/tests-clar/pack/packbuilder.c b/tests/pack/packbuilder.c
similarity index 63%
rename from tests-clar/pack/packbuilder.c
rename to tests/pack/packbuilder.c
index 764fba2..1ae2322 100644
--- a/tests-clar/pack/packbuilder.c
+++ b/tests/pack/packbuilder.c
@@ -1,5 +1,6 @@
 #include "clar_libgit2.h"
 #include "fileops.h"
+#include "pack.h"
 #include "hash.h"
 #include "iterator.h"
 #include "vector.h"
@@ -8,7 +9,7 @@
 static git_repository *_repo;
 static git_revwalk *_revwalker;
 static git_packbuilder *_packbuilder;
-static git_indexer_stream *_indexer;
+static git_indexer *_indexer;
 static git_vector _commits;
 static int _commits_is_initialized;
 
@@ -40,7 +41,7 @@
 	git_revwalk_free(_revwalker);
 	_revwalker = NULL;
 
-	git_indexer_stream_free(_indexer);
+	git_indexer_free(_indexer);
 	_indexer = NULL;
 
 	cl_git_sandbox_cleanup();
@@ -79,7 +80,7 @@
 {
 	git_transfer_progress *stats = (git_transfer_progress *)payload;
 
-	return git_indexer_stream_add(_indexer, ptr, len, stats);
+	return git_indexer_append(_indexer, ptr, len, stats);
 }
 
 void test_pack_packbuilder__create_pack(void)
@@ -92,11 +93,11 @@
 
 	seed_packbuilder();
 
-	cl_git_pass(git_indexer_stream_new(&_indexer, ".", NULL, NULL));
+	cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL, NULL));
 	cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
-	cl_git_pass(git_indexer_stream_finalize(_indexer, &stats));
+	cl_git_pass(git_indexer_commit(_indexer, &stats));
 
-	git_oid_fmt(hex, git_indexer_stream_hash(_indexer));
+	git_oid_fmt(hex, git_indexer_hash(_indexer));
 	git_buf_printf(&path, "pack-%s.pack", hex);
 
 	/*
@@ -105,7 +106,7 @@
 	 * we create exactly the same pack as git.git does when *not*
 	 * reusing existing deltas (as libgit2).
 	 *
-	 * $ cd tests-clar/resources/testrepo.git
+	 * $ cd tests/resources/testrepo.git
 	 * $ git rev-list --objects HEAD | \
 	 * 	git pack-objects -q --no-reuse-delta --threads=1 pack
 	 * $ sha1sum git-80e61eb315239ef3c53033e37fee43b744d57122.pack
@@ -128,21 +129,76 @@
 	cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
 }
 
+void test_pack_packbuilder__get_hash(void)
+{
+	char hex[41]; hex[40] = '\0';
+
+	seed_packbuilder();
+
+	git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL);
+	git_oid_fmt(hex, git_packbuilder_hash(_packbuilder));
+
+	cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122");
+}
+
+static void test_write_pack_permission(mode_t given, mode_t expected)
+{
+	struct stat statbuf;
+	mode_t mask, os_mask;
+
+	seed_packbuilder();
+
+	git_packbuilder_write(_packbuilder, ".", given, NULL, NULL);
+
+	/* Windows does not return group/user bits from stat,
+	* files are never executable.
+	*/
+#ifdef GIT_WIN32
+	os_mask = 0600;
+#else
+	os_mask = 0777;
+#endif
+
+	mask = p_umask(0);
+	p_umask(mask);
+
+	cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+	cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf));
+	cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+}
+
+void test_pack_packbuilder__permissions_standard(void)
+{
+	test_write_pack_permission(0, GIT_PACK_FILE_MODE);
+}
+
+void test_pack_packbuilder__permissions_readonly(void)
+{
+	test_write_pack_permission(0444, 0444);
+}
+
+void test_pack_packbuilder__permissions_readwrite(void)
+{
+	test_write_pack_permission(0666, 0666);
+}
+
 static git_transfer_progress stats;
 static int foreach_cb(void *buf, size_t len, void *payload)
 {
-	git_indexer_stream *idx = (git_indexer_stream *) payload;
-	cl_git_pass(git_indexer_stream_add(idx, buf, len, &stats));
+	git_indexer *idx = (git_indexer *) payload;
+	cl_git_pass(git_indexer_append(idx, buf, len, &stats));
 	return 0;
 }
 
 void test_pack_packbuilder__foreach(void)
 {
-	git_indexer_stream *idx;
+	git_indexer *idx;
 
 	seed_packbuilder();
-	cl_git_pass(git_indexer_stream_new(&idx, ".", NULL, NULL));
+	cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
 	cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
-	cl_git_pass(git_indexer_stream_finalize(idx, &stats));
-	git_indexer_stream_free(idx);
+	cl_git_pass(git_indexer_commit(idx, &stats));
+	git_indexer_free(idx);
 }
diff --git a/tests-clar/refs/branches/create.c b/tests/refs/branches/create.c
similarity index 100%
rename from tests-clar/refs/branches/create.c
rename to tests/refs/branches/create.c
diff --git a/tests-clar/refs/branches/delete.c b/tests/refs/branches/delete.c
similarity index 97%
rename from tests-clar/refs/branches/delete.c
rename to tests/refs/branches/delete.c
index 7af5a3e..de90cb7 100644
--- a/tests-clar/refs/branches/delete.c
+++ b/tests/refs/branches/delete.c
@@ -57,11 +57,11 @@
 	git_reference_free(branch);
 }
 
-void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_orphaned(void)
+void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void)
 {
 	git_reference *branch;
 
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
 	cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
 	cl_git_pass(git_branch_delete(branch));
diff --git a/tests-clar/refs/branches/ishead.c b/tests/refs/branches/ishead.c
similarity index 95%
rename from tests-clar/refs/branches/ishead.c
rename to tests/refs/branches/ishead.c
index dfcf1b5..b1ad09c 100644
--- a/tests-clar/refs/branches/ishead.c
+++ b/tests/refs/branches/ishead.c
@@ -26,13 +26,13 @@
 	cl_assert_equal_i(true, git_branch_is_head(branch));
 }
 
-void test_refs_branches_ishead__can_properly_handle_orphaned_HEAD(void)
+void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
 {
 	git_repository_free(repo);
 
 	repo = cl_git_sandbox_init("testrepo.git");
 
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
 	cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
 
diff --git a/tests/refs/branches/iterator.c b/tests/refs/branches/iterator.c
new file mode 100644
index 0000000..904c6a1
--- /dev/null
+++ b/tests/refs/branches/iterator.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_iterator__initialize(void)
+{
+	git_oid id;
+
+	cl_fixture_sandbox("testrepo.git");
+	cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+	cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+	cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0));
+}
+
+void test_refs_branches_iterator__cleanup(void)
+{
+	git_reference_free(fake_remote);
+	fake_remote = NULL;
+
+	git_repository_free(repo);
+	repo = NULL;
+
+	cl_fixture_cleanup("testrepo.git");
+
+	cl_git_sandbox_cleanup();
+}
+
+static void assert_retrieval(unsigned int flags, unsigned int expected_count)
+{
+	git_branch_iterator *iter;
+	git_reference *ref;
+	int count = 0, error;
+	git_branch_t type;
+
+	cl_git_pass(git_branch_iterator_new(&iter, repo, flags));
+	while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+		count++;
+		git_reference_free(ref);
+	}
+
+	git_branch_iterator_free(iter);
+	cl_assert_equal_i(error, GIT_ITEROVER);
+	cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_branches_iterator__retrieve_all_branches(void)
+{
+	assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14);
+}
+
+void test_refs_branches_iterator__retrieve_remote_branches(void)
+{
+	assert_retrieval(GIT_BRANCH_REMOTE, 2);
+}
+
+void test_refs_branches_iterator__retrieve_local_branches(void)
+{
+	assert_retrieval(GIT_BRANCH_LOCAL, 12);
+}
+
+struct expectations {
+	const char *branch_name;
+	int encounters;
+};
+
+static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
+{
+	int pos = 0;
+
+	for (pos = 0; findings[pos].branch_name; ++pos) {
+		if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
+			cl_assert_equal_i(1, findings[pos].encounters);
+			return;
+		}
+	}
+
+	cl_fail("expected branch not found in list.");
+}
+
+static void contains_branches(struct expectations exp[], git_branch_iterator *iter)
+{
+	git_reference *ref;
+	git_branch_t type;
+	int error, pos = 0;
+
+	while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+		for (pos = 0; exp[pos].branch_name; ++pos) {
+			if (strcmp(git_reference_shorthand(ref), exp[pos].branch_name) == 0)
+				exp[pos].encounters++;
+		}
+
+		git_reference_free(ref);
+	}
+
+	cl_assert_equal_i(error, GIT_ITEROVER);
+}
+
+/*
+ * $ git branch -r
+ *  nulltoken/HEAD -> nulltoken/master
+ *  nulltoken/master
+ */
+void test_refs_branches_iterator__retrieve_remote_symbolic_HEAD_when_present(void)
+{
+	git_branch_iterator *iter;
+	struct expectations exp[] = {
+		{ "nulltoken/HEAD", 0 },
+		{ "nulltoken/master", 0 },
+		{ NULL, 0 }
+	};
+
+	git_reference_free(fake_remote);
+	cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0));
+
+	assert_retrieval(GIT_BRANCH_REMOTE, 3);
+
+	cl_git_pass(git_branch_iterator_new(&iter, repo, GIT_BRANCH_REMOTE));
+	contains_branches(exp, iter);
+	git_branch_iterator_free(iter);
+
+	assert_branch_has_been_found(exp, "nulltoken/HEAD");
+	assert_branch_has_been_found(exp, "nulltoken/master");
+}
+
+void test_refs_branches_iterator__mix_of_packed_and_loose(void)
+{
+	git_branch_iterator *iter;
+	struct expectations exp[] = {
+		{ "master", 0 },
+		{ "origin/HEAD", 0 },
+		{ "origin/master", 0 },
+		{ "origin/packed", 0 },
+		{ NULL, 0 }
+	};
+	git_repository *r2;
+
+	r2 = cl_git_sandbox_init("testrepo2");
+
+	cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE));
+	contains_branches(exp, iter);
+
+	git_branch_iterator_free(iter);
+
+	assert_branch_has_been_found(exp, "master");
+	assert_branch_has_been_found(exp, "origin/HEAD");
+	assert_branch_has_been_found(exp, "origin/master");
+	assert_branch_has_been_found(exp, "origin/packed");
+}
diff --git a/tests-clar/refs/branches/lookup.c b/tests/refs/branches/lookup.c
similarity index 100%
rename from tests-clar/refs/branches/lookup.c
rename to tests/refs/branches/lookup.c
diff --git a/tests-clar/refs/branches/move.c b/tests/refs/branches/move.c
similarity index 100%
rename from tests-clar/refs/branches/move.c
rename to tests/refs/branches/move.c
diff --git a/tests-clar/refs/branches/name.c b/tests/refs/branches/name.c
similarity index 100%
rename from tests-clar/refs/branches/name.c
rename to tests/refs/branches/name.c
diff --git a/tests-clar/refs/branches/remote.c b/tests/refs/branches/remote.c
similarity index 100%
rename from tests-clar/refs/branches/remote.c
rename to tests/refs/branches/remote.c
diff --git a/tests-clar/refs/branches/upstream.c b/tests/refs/branches/upstream.c
similarity index 100%
rename from tests-clar/refs/branches/upstream.c
rename to tests/refs/branches/upstream.c
diff --git a/tests-clar/refs/branches/upstreamname.c b/tests/refs/branches/upstreamname.c
similarity index 100%
rename from tests-clar/refs/branches/upstreamname.c
rename to tests/refs/branches/upstreamname.c
diff --git a/tests-clar/refs/crashes.c b/tests/refs/crashes.c
similarity index 100%
rename from tests-clar/refs/crashes.c
rename to tests/refs/crashes.c
diff --git a/tests-clar/refs/create.c b/tests/refs/create.c
similarity index 100%
rename from tests-clar/refs/create.c
rename to tests/refs/create.c
diff --git a/tests-clar/refs/delete.c b/tests/refs/delete.c
similarity index 100%
rename from tests-clar/refs/delete.c
rename to tests/refs/delete.c
diff --git a/tests-clar/refs/foreachglob.c b/tests/refs/foreachglob.c
similarity index 100%
rename from tests-clar/refs/foreachglob.c
rename to tests/refs/foreachglob.c
diff --git a/tests-clar/refs/isvalidname.c b/tests/refs/isvalidname.c
similarity index 100%
rename from tests-clar/refs/isvalidname.c
rename to tests/refs/isvalidname.c
diff --git a/tests-clar/refs/iterator.c b/tests/refs/iterator.c
similarity index 100%
rename from tests-clar/refs/iterator.c
rename to tests/refs/iterator.c
diff --git a/tests-clar/refs/list.c b/tests/refs/list.c
similarity index 92%
rename from tests-clar/refs/list.c
rename to tests/refs/list.c
index c9c2af4..de5c0fd 100644
--- a/tests-clar/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, 13);
+	cl_assert_equal_i((int)ref_list.count, 14);
 
 	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, 13);
+	cl_assert_equal_i((int)ref_list.count, 14);
 
 	git_strarray_free(&ref_list);
 }
diff --git a/tests-clar/refs/listall.c b/tests/refs/listall.c
similarity index 100%
rename from tests-clar/refs/listall.c
rename to tests/refs/listall.c
diff --git a/tests-clar/refs/lookup.c b/tests/refs/lookup.c
similarity index 80%
rename from tests-clar/refs/lookup.c
rename to tests/refs/lookup.c
index 0dbebc5..2e31cf0 100644
--- a/tests-clar/refs/lookup.c
+++ b/tests/refs/lookup.c
@@ -46,3 +46,15 @@
 	cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
 	cl_assert(git_oid_cmp(&tag, &expected) == 0);
 }
+
+void test_refs_lookup__namespace(void)
+{
+	int error;
+	git_reference *ref;
+
+	error = git_reference_lookup(&ref, g_repo, "refs/heads");
+	cl_assert_equal_i(error, GIT_ENOTFOUND);
+
+	error = git_reference_lookup(&ref, g_repo, "refs/heads/");
+	cl_assert_equal_i(error, GIT_EINVALIDSPEC);
+}
diff --git a/tests-clar/refs/normalize.c b/tests/refs/normalize.c
similarity index 100%
rename from tests-clar/refs/normalize.c
rename to tests/refs/normalize.c
diff --git a/tests-clar/refs/overwrite.c b/tests/refs/overwrite.c
similarity index 100%
rename from tests-clar/refs/overwrite.c
rename to tests/refs/overwrite.c
diff --git a/tests-clar/refs/pack.c b/tests/refs/pack.c
similarity index 73%
rename from tests-clar/refs/pack.c
rename to tests/refs/pack.c
index d8d5cc6..849a052 100644
--- a/tests-clar/refs/pack.c
+++ b/tests/refs/pack.c
@@ -32,7 +32,7 @@
 
 void test_refs_pack__empty(void)
 {
-   // create a packfile for an empty folder
+	/* create a packfile for an empty folder */
 	git_buf temp_path = GIT_BUF_INIT;
 
 	cl_git_pass(git_buf_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
@@ -44,7 +44,7 @@
 
 void test_refs_pack__loose(void)
 {
-   // create a packfile from all the loose rn a repo
+	/* create a packfile from all the loose refs in a repo */
 	git_reference *reference;
 	git_buf temp_path = GIT_BUF_INIT;
 
@@ -77,3 +77,29 @@
 	git_reference_free(reference);
 	git_buf_free(&temp_path);
 }
+
+void test_refs_pack__symbolic(void)
+{
+	/* create a packfile from loose refs skipping symbolic refs */
+	int i;
+	git_oid head;
+	git_reference *ref;
+	char name[128];
+
+	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+	/* make a bunch of references */
+
+	for (i = 0; i < 100; ++i) {
+		snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
+		cl_git_pass(git_reference_symbolic_create(
+			&ref, g_repo, name, "refs/heads/master", 0));
+		git_reference_free(ref);
+
+		snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
+		cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+		git_reference_free(ref);
+	}
+
+	packall();
+}
diff --git a/tests-clar/refs/peel.c b/tests/refs/peel.c
similarity index 100%
rename from tests-clar/refs/peel.c
rename to tests/refs/peel.c
diff --git a/tests-clar/refs/read.c b/tests/refs/read.c
similarity index 94%
rename from tests-clar/refs/read.c
rename to tests/refs/read.c
index afb6be0..35cf17e 100644
--- a/tests-clar/refs/read.c
+++ b/tests/refs/read.c
@@ -255,6 +255,22 @@
 	assert_is_branch("refs/tags/e90810b", false);
 }
 
+static void assert_is_tag(const char *name, bool expected_tagness)
+{
+	git_reference *reference;
+	cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+	cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference));
+	git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_tag(void)
+{
+	assert_is_tag("refs/tags/e90810b", true);
+	assert_is_tag("refs/tags/test", true);
+	assert_is_tag("refs/heads/packed", false);
+	assert_is_tag("refs/remotes/test/master", false);
+}
+
 void test_refs_read__invalid_name_returns_EINVALIDSPEC(void)
 {
 	git_reference *reference;
diff --git a/tests-clar/refs/ref_helpers.c b/tests/refs/ref_helpers.c
similarity index 100%
rename from tests-clar/refs/ref_helpers.c
rename to tests/refs/ref_helpers.c
diff --git a/tests-clar/refs/ref_helpers.h b/tests/refs/ref_helpers.h
similarity index 100%
rename from tests-clar/refs/ref_helpers.h
rename to tests/refs/ref_helpers.h
diff --git a/tests-clar/refs/reflog/drop.c b/tests/refs/reflog/drop.c
similarity index 91%
rename from tests-clar/refs/reflog/drop.c
rename to tests/refs/reflog/drop.c
index 21cc847..916bd99 100644
--- a/tests-clar/refs/reflog/drop.c
+++ b/tests/refs/reflog/drop.c
@@ -8,15 +8,10 @@
 
 void test_refs_reflog_drop__initialize(void)
 {
-	git_reference *ref;
-
 	g_repo = cl_git_sandbox_init("testrepo.git");
-	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
 
-	git_reflog_read(&g_reflog, ref);
+	git_reflog_read(&g_reflog, g_repo, "HEAD");
 	entrycount = git_reflog_entrycount(g_reflog);
-
-	git_reference_free(ref);
 }
 
 void test_refs_reflog_drop__cleanup(void)
@@ -106,19 +101,15 @@
 
 void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
 {
-	git_reference *ref;
-
 	cl_assert(entrycount > 2);
 
-	cl_git_pass(git_reference_lookup(&ref, g_repo, g_reflog->ref_name));
 	cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
 	cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
 	cl_git_pass(git_reflog_write(g_reflog));
 
 	git_reflog_free(g_reflog);
 
-	git_reflog_read(&g_reflog, ref);
-	git_reference_free(ref);
+	git_reflog_read(&g_reflog, g_repo, "HEAD");
 
 	cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
 }
diff --git a/tests-clar/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
similarity index 74%
rename from tests-clar/refs/reflog/reflog.c
rename to tests/refs/reflog/reflog.c
index 095cabf..bcd2242 100644
--- a/tests-clar/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -13,7 +13,7 @@
 
 
 // helpers
-static void assert_signature(git_signature *expected, git_signature *actual)
+static void assert_signature(const git_signature *expected, const git_signature *actual)
 {
 	cl_assert(actual);
 	cl_assert_equal_s(expected->name, actual->name);
@@ -34,30 +34,13 @@
    cl_git_sandbox_cleanup();
 }
 
-void test_refs_reflog_reflog__append_then_read(void)
+static void assert_appends(const git_signature *committer, const git_oid *oid)
 {
-   // write a reflog for a given reference and ensure it can be read back
 	git_repository *repo2;
-	git_reference *ref, *lookedup_ref;
-	git_oid oid;
-	git_signature *committer;
+	git_reference *lookedup_ref;
 	git_reflog *reflog;
 	const git_reflog_entry *entry;
 
-	/* Create a new branch pointing at the HEAD */
-	git_oid_fromstr(&oid, current_master_tip);
-	cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
-
-	cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
-
-	cl_git_pass(git_reflog_read(&reflog, ref));
-
-	cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
-	cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
-	cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
-	cl_git_pass(git_reflog_write(reflog));
-	git_reflog_free(reflog);
-
 	/* Reopen a new instance of the repository */
 	cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
 
@@ -65,29 +48,78 @@
 	cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
 
 	/* Read and parse the reflog for this branch */
-	cl_git_pass(git_reflog_read(&reflog, lookedup_ref));
+	cl_git_pass(git_reflog_read(&reflog, repo2, new_ref));
 	cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog));
 
 	entry = git_reflog_entry_byindex(reflog, 1);
 	assert_signature(committer, entry->committer);
 	cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0);
-	cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
+	cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
 	cl_assert(entry->msg == NULL);
 
 	entry = git_reflog_entry_byindex(reflog, 0);
 	assert_signature(committer, entry->committer);
-	cl_assert(git_oid_cmp(&oid, &entry->oid_old) == 0);
-	cl_assert(git_oid_cmp(&oid, &entry->oid_cur) == 0);
+	cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+	cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
 	cl_assert_equal_s(commit_msg, entry->msg);
 
-	git_signature_free(committer);
 	git_reflog_free(reflog);
 	git_repository_free(repo2);
 
-	git_reference_free(ref);
 	git_reference_free(lookedup_ref);
 }
 
+void test_refs_reflog_reflog__append_then_read(void)
+{
+	/* write a reflog for a given reference and ensure it can be read back */
+	git_reference *ref;
+	git_oid oid;
+	git_signature *committer;
+	git_reflog *reflog;
+
+	/* Create a new branch pointing at the HEAD */
+	git_oid_fromstr(&oid, current_master_tip);
+	cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+	git_reference_free(ref);
+
+	cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+	cl_git_pass(git_reflog_read(&reflog, g_repo, new_ref));
+
+	cl_git_fail(git_reflog_append(reflog, &oid, committer, "no inner\nnewline"));
+	cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
+	cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
+	cl_git_pass(git_reflog_write(reflog));
+	git_reflog_free(reflog);
+
+	assert_appends(committer, &oid);
+
+	git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__append_to_then_read(void)
+{
+	/* write a reflog for a given reference and ensure it can be read back */
+	git_reference *ref;
+	git_oid oid;
+	git_signature *committer;
+
+	/* Create a new branch pointing at the HEAD */
+	git_oid_fromstr(&oid, current_master_tip);
+	cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0));
+	git_reference_free(ref);
+
+	cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+	cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline"));
+	cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL));
+	cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n"));
+
+	assert_appends(committer, &oid);
+
+	git_signature_free(committer);
+}
+
 void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
 {
 	git_reference *master, *new_master;
@@ -133,21 +165,18 @@
 
 void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
 {
-	git_reference *subtrees;
 	git_reflog *reflog;
+	const char *refname = "refs/heads/subtrees";
 	git_buf subtrees_log_path = GIT_BUF_INIT;
 
-	cl_git_pass(git_reference_lookup(&subtrees, g_repo, "refs/heads/subtrees"));
-
-	git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, git_reference_name(subtrees));
+	git_buf_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname);
 	cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&subtrees_log_path)));
 
-	cl_git_pass(git_reflog_read(&reflog, subtrees));
+	cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
 
 	cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
 
 	git_reflog_free(reflog);
-	git_reference_free(subtrees);
 	git_buf_free(&subtrees_log_path);
 }
 
@@ -158,7 +187,7 @@
 	git_reflog *reflog;
 
 	cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
-	cl_git_pass(git_reflog_read(&reflog, master));
+	cl_git_pass(git_reflog_read(&reflog, g_repo, "refs/heads/master"));
 
 	cl_git_pass(git_reflog_write(reflog));
 
@@ -175,12 +204,6 @@
 
 void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
 {
-	git_reference *master;
-
-	cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
-
 	cl_assert_equal_i(GIT_EINVALIDSPEC,
-		git_reflog_rename(master, "refs/heads/Inv@{id"));
-
-	git_reference_free(master);
+			  git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
 }
diff --git a/tests-clar/refs/rename.c b/tests/refs/rename.c
similarity index 100%
rename from tests-clar/refs/rename.c
rename to tests/refs/rename.c
diff --git a/tests-clar/refs/revparse.c b/tests/refs/revparse.c
similarity index 92%
rename from tests-clar/refs/revparse.c
rename to tests/refs/revparse.c
index 69d9274..37d3981 100644
--- a/tests-clar/refs/revparse.c
+++ b/tests/refs/revparse.c
@@ -544,6 +544,37 @@
 		GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
 }
 
+/*
+ * $ echo "aabqhq" | git hash-object -t blob --stdin
+ * dea509d0b3cb8ee0650f6ca210bc83f4678851ba
+ * 
+ * $ echo "aaazvc" | git hash-object -t blob --stdin
+ * dea509d097ce692e167dfc6a48a7a280cc5e877e
+ */
+void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void)
+{
+	git_repository *repo;
+	git_index *index;
+	git_object *obj;
+
+	repo = cl_git_sandbox_init("testrepo");
+
+	cl_git_mkfile("testrepo/one.txt", "aabqhq\n");
+	cl_git_mkfile("testrepo/two.txt", "aaazvc\n");
+	
+	cl_git_pass(git_repository_index(&index, repo));
+	cl_git_pass(git_index_add_bypath(index, "one.txt"));
+	cl_git_pass(git_index_add_bypath(index, "two.txt"));
+	
+	cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS);
+
+	cl_git_pass(git_revparse_single(&obj, repo, "dea509d09"));
+
+	git_object_free(obj);
+	git_index_free(index);
+	cl_git_sandbox_cleanup();
+}
+
 void test_refs_revparse__issue_994(void)
 {
 	git_repository *repo;
@@ -738,4 +769,45 @@
 		"master",
 		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
 		"refs/heads/master");
+
+	test_object_and_ref(
+		"HEAD",
+		"a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+		"refs/heads/master");
+
+    test_object_and_ref(
+		"tags/test",
+		"b25fa35b38051e4ae45d4222e795f9df2e43f1d1",
+        "refs/tags/test");
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void)
+{
+    test_object_and_ref(
+        "HEAD~3",
+        "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+        NULL);
+
+    test_object_and_ref(
+        "HEAD~0",
+        "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+        NULL);
+
+    test_object_and_ref(
+        "HEAD^0",
+        "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+        NULL);
+
+    test_object_and_ref(
+		"@{-1}@{0}",
+		"a4a7dce85cf63874e984719f4fdd239f5145052f",
+		NULL);
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void)
+{
+    test_object_and_ref(
+		"tags/test:readme.txt",
+		"0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+        NULL);
 }
diff --git a/tests-clar/refs/setter.c b/tests/refs/setter.c
similarity index 100%
rename from tests-clar/refs/setter.c
rename to tests/refs/setter.c
diff --git a/tests-clar/refs/shorthand.c b/tests/refs/shorthand.c
similarity index 100%
rename from tests-clar/refs/shorthand.c
rename to tests/refs/shorthand.c
diff --git a/tests/refs/unicode.c b/tests/refs/unicode.c
new file mode 100644
index 0000000..b560128
--- /dev/null
+++ b/tests/refs/unicode.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+	repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_unicode__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+	repo = NULL;
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+	git_reference *ref0, *ref1, *ref2;
+	git_repository *repo2;
+
+	const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m";
+	const char *master = "refs/heads/master";
+
+	/* Create the reference */
+	cl_git_pass(git_reference_lookup(&ref0, repo, master));
+	cl_git_pass(git_reference_create(
+		&ref1, repo, REFNAME, git_reference_target(ref0), 0));
+	cl_assert_equal_s(REFNAME, git_reference_name(ref1));
+	git_reference_free(ref0);
+
+	/* Lookup the reference in a different instance of the repository */
+	cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+	cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+	cl_assert_equal_i(
+		0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+	cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+	git_reference_free(ref2);
+
+#if GIT_USE_ICONV
+	/* Lookup reference by decomposed unicode name */
+
+#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
+
+	cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
+	cl_assert_equal_i(
+		0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2)));
+	cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+	git_reference_free(ref2);
+#endif
+
+	/* Cleanup */
+
+	git_reference_free(ref1);
+	git_repository_free(repo2);
+}
diff --git a/tests-clar/refs/update.c b/tests/refs/update.c
similarity index 100%
rename from tests-clar/refs/update.c
rename to tests/refs/update.c
diff --git a/tests-clar/repo/config.c b/tests/repo/config.c
similarity index 96%
rename from tests-clar/repo/config.c
rename to tests/repo/config.c
index b8971bb..e77acc8 100644
--- a/tests-clar/repo/config.c
+++ b/tests/repo/config.c
@@ -2,7 +2,7 @@
 #include "fileops.h"
 #include <ctype.h>
 
-git_buf path = GIT_BUF_INIT;
+static git_buf path = GIT_BUF_INIT;
 
 void test_repo_config__initialize(void)
 {
@@ -46,6 +46,8 @@
 	git_config_free(global);
 	git_config_free(config);
 	git_repository_free(repo);
+
+	git_futils_dirs_global_shutdown();
 }
 
 void test_repo_config__open_missing_global_with_separators(void)
@@ -73,6 +75,8 @@
 	git_config_free(global);
 	git_config_free(config);
 	git_repository_free(repo);
+
+	git_futils_dirs_global_shutdown();
 }
 
 #include "repository.h"
@@ -101,6 +105,8 @@
 	cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
 	git_repository_free(repo);
 
+	git_futils_dirs_global_shutdown();
+
 	/* with just system */
 
 	cl_must_pass(p_mkdir("alternate/1", 0777));
@@ -197,4 +203,6 @@
 
 	cl_assert(!git_path_exists("empty_standard_repo/.git/config"));
 	cl_assert(!git_path_exists("alternate/3/.gitconfig"));
+
+	git_futils_dirs_global_shutdown();
 }
diff --git a/tests-clar/repo/discover.c b/tests/repo/discover.c
similarity index 100%
rename from tests-clar/repo/discover.c
rename to tests/repo/discover.c
diff --git a/tests-clar/repo/getters.c b/tests/repo/getters.c
similarity index 100%
rename from tests-clar/repo/getters.c
rename to tests/repo/getters.c
diff --git a/tests-clar/repo/hashfile.c b/tests/repo/hashfile.c
similarity index 100%
rename from tests-clar/repo/hashfile.c
rename to tests/repo/hashfile.c
diff --git a/tests-clar/repo/head.c b/tests/repo/head.c
similarity index 85%
rename from tests-clar/repo/head.c
rename to tests/repo/head.c
index a9f5cfc..5a55984 100644
--- a/tests-clar/repo/head.c
+++ b/tests/repo/head.c
@@ -32,20 +32,20 @@
 	cl_assert_equal_i(false, git_repository_head_detached(repo));
 }
 
-void test_repo_head__head_orphan(void)
+void test_repo_head__unborn_head(void)
 {
 	git_reference *ref;
 
 	cl_git_pass(git_repository_head_detached(repo));
 
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
-	cl_assert(git_repository_head_orphan(repo) == 1);
+	cl_assert(git_repository_head_unborn(repo) == 1);
 
 
 	/* take the repo back to it's original state */
 	cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1));
-	cl_assert(git_repository_head_orphan(repo) == 0);
+	cl_assert(git_repository_head_unborn(repo) == 0);
 
 	git_reference_free(ref);
 }
@@ -58,7 +58,7 @@
 
 	cl_assert_equal_i(false, git_repository_head_detached(repo));
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head(&head, repo));
+	cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
 }
 
 void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void)
@@ -163,20 +163,20 @@
 	git_reference_free(head);
 }
 
-void test_repo_head__detaching_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void)
+void test_repo_head__detaching_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
 {
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_detach_head(repo));
+	cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo));
 }
 
-void test_repo_head__retrieving_an_orphaned_head_returns_GIT_EORPHANEDHEAD(void)
+void test_repo_head__retrieving_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
 {
 	git_reference *head;
 
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head(&head, repo));
+	cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
 }
 
 void test_repo_head__retrieving_a_missing_head_returns_GIT_ENOTFOUND(void)
@@ -188,9 +188,9 @@
 	cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head(&head, repo));
 }
 
-void test_repo_head__can_tell_if_an_orphaned_head_is_detached(void)
+void test_repo_head__can_tell_if_an_unborn_head_is_detached(void)
 {
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
 	cl_assert_equal_i(false, git_repository_head_detached(repo));
 }
diff --git a/tests-clar/repo/headtree.c b/tests/repo/headtree.c
similarity index 80%
rename from tests-clar/repo/headtree.c
rename to tests/repo/headtree.c
index 0e7fe93..e899ac3 100644
--- a/tests-clar/repo/headtree.c
+++ b/tests/repo/headtree.c
@@ -36,13 +36,13 @@
 	cl_assert(git_oid_streq(git_tree_id(tree), "az"));
 }
 
-void test_repo_headtree__when_head_is_orphaned_returns_EORPHANEDHEAD(void)
+void test_repo_headtree__when_head_is_unborn_returns_EUNBORNBRANCH(void)
 {
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
-	cl_assert_equal_i(true, git_repository_head_orphan(repo));
+	cl_assert_equal_i(true, git_repository_head_unborn(repo));
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD, git_repository_head_tree(&tree, repo));
+	cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head_tree(&tree, repo));
 }
 
 void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void)
diff --git a/tests-clar/repo/init.c b/tests/repo/init.c
similarity index 73%
rename from tests-clar/repo/init.c
rename to tests/repo/init.c
index 8cf7379..aea383c 100644
--- a/tests-clar/repo/init.c
+++ b/tests/repo/init.c
@@ -10,10 +10,17 @@
 };
 
 static git_repository *_repo = NULL;
+static mode_t g_umask = 0;
 
 void test_repo_init__initialize(void)
 {
 	_repo = NULL;
+
+	/* load umask if not already loaded */
+	if (!g_umask) {
+		g_umask = p_umask(022);
+		(void)p_umask(g_umask);
+	}
 }
 
 static void cleanup_repository(void *path)
@@ -172,41 +179,32 @@
 	git_buf_free(&path);
 }
 
-static void assert_config_entry_on_init_bytype(const char *config_key, int expected_value, bool is_bare)
+static void assert_config_entry_on_init_bytype(
+	const char *config_key, int expected_value, bool is_bare)
 {
 	git_config *config;
-	int current_value;
-	git_buf repo_path = GIT_BUF_INIT;
+	int error, current_value;
+	const char *repo_path = is_bare ?
+		"config_entry/test.bare.git" : "config_entry/test.non.bare.git";
 
 	cl_set_cleanup(&cleanup_repository, "config_entry");
 
-	cl_git_pass(git_buf_puts(&repo_path, "config_entry/test."));
+	cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
 
-	if (!is_bare)
-		cl_git_pass(git_buf_puts(&repo_path, "non."));
-
-	cl_git_pass(git_buf_puts(&repo_path, "bare.git"));
-
-	cl_git_pass(git_repository_init(&_repo, git_buf_cstr(&repo_path), is_bare));
-
-	git_buf_free(&repo_path);
-
-	git_repository_config(&config, _repo);
+	cl_git_pass(git_repository_config(&config, _repo));
+	error = git_config_get_bool(&current_value, config, config_key);
+	git_config_free(config);
 
 	if (expected_value >= 0) {
-		cl_git_pass(git_config_get_bool(&current_value, config, config_key));
-
+		cl_assert_equal_i(0, error);
 		cl_assert_equal_i(expected_value, current_value);
 	} else {
-		int error = git_config_get_bool(&current_value, config, config_key);
-
 		cl_assert_equal_i(expected_value, error);
 	}
-
-	git_config_free(config);
 }
 
-static void assert_config_entry_on_init(const char *config_key, int expected_value)
+static void assert_config_entry_on_init(
+	const char *config_key, int expected_value)
 {
 	assert_config_entry_on_init_bytype(config_key, expected_value, true);
 	git_repository_free(_repo);
@@ -216,21 +214,36 @@
 
 void test_repo_init__detect_filemode(void)
 {
-#ifdef GIT_WIN32
-	assert_config_entry_on_init("core.filemode", false);
-#else
-	assert_config_entry_on_init("core.filemode", true);
-#endif
+	assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
 }
 
-#define CASE_INSENSITIVE_FILESYSTEM (defined GIT_WIN32 || defined __APPLE__)
-
 void test_repo_init__detect_ignorecase(void)
 {
-#if CASE_INSENSITIVE_FILESYSTEM
-	assert_config_entry_on_init("core.ignorecase", true);
+	struct stat st;
+	bool found_without_match;
+
+	cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+	found_without_match = (p_stat("Testcaps", &st) == 0);
+	cl_must_pass(p_unlink("testCAPS"));
+
+	assert_config_entry_on_init(
+		"core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
+}
+
+void test_repo_init__detect_precompose_unicode_required(void)
+{
+#ifdef GIT_USE_ICONV
+	char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn";
+	struct stat st;
+	bool found_with_nfd;
+
+	cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+	found_with_nfd = (p_stat(decomposed, &st) == 0);
+	cl_must_pass(p_unlink(composed));
+
+	assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
 #else
-	assert_config_entry_on_init("core.ignorecase", GIT_ENOTFOUND);
+	assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
 #endif
 }
 
@@ -263,14 +276,7 @@
 
 void test_repo_init__reinit_overwrites_filemode(void)
 {
-	git_config *config;
-	int expected, current_value;
-
-#ifdef GIT_WIN32
-	expected = false;
-#else
-	expected = true;
-#endif
+	int expected = cl_is_chmod_supported(), current_value;
 
 	/* Init a new repo */
 	cl_set_cleanup(&cleanup_repository, "overwrite.git");
@@ -284,13 +290,10 @@
 
 	/* Reinit the repository */
 	cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
-	git_repository_config(&config, _repo);
 
 	/* Ensure the "core.filemode" config value has been reset */
-	cl_git_pass(git_config_get_bool(&current_value, config, "core.filemode"));
+	current_value = cl_repo_get_bool(_repo, "core.filemode");
 	cl_assert_equal_i(expected, current_value);
-
-	git_config_free(config);
 }
 
 void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
@@ -345,7 +348,10 @@
 
 	cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
 	cl_assert(S_ISDIR(st.st_mode));
-	cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+	if (cl_is_chmod_supported())
+		cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+	else
+		cl_assert((S_ISGID & st.st_mode) == 0);
 
 	cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
 	cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
@@ -361,6 +367,8 @@
 	cl_fixture_cleanup("root");
 }
 
+#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
+
 static void assert_hooks_match(
 	const char *template_dir,
 	const char *repo_dir,
@@ -379,12 +387,18 @@
 
 	cl_assert(expected_st.st_size == st.st_size);
 
-	if (!core_filemode) {
-		expected_st.st_mode = expected_st.st_mode & ~0111;
-		st.st_mode = st.st_mode & ~0111;
-	}
+	if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
+		mode_t expected_mode =
+			GIT_MODE_TYPE(expected_st.st_mode) |
+			(GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);
 
-	cl_assert_equal_i((int)expected_st.st_mode, (int)st.st_mode);
+		if (!core_filemode) {
+			CLEAR_FOR_CORE_FILEMODE(expected_mode);
+			CLEAR_FOR_CORE_FILEMODE(st.st_mode);
+		}
+
+		cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
+	}
 
 	git_buf_free(&expected);
 	git_buf_free(&actual);
@@ -402,24 +416,19 @@
 	git_buf_free(&full);
 
 	if (!core_filemode) {
-		expect_mode = expect_mode & ~0111;
-		st.st_mode = st.st_mode & ~0111;
+		CLEAR_FOR_CORE_FILEMODE(expect_mode);
+		CLEAR_FOR_CORE_FILEMODE(st.st_mode);
 		expect_setgid = false;
 	}
 
-	if (S_ISGID != 0) {
-		if (expect_setgid)
-			cl_assert((st.st_mode & S_ISGID) != 0);
-		else
-			cl_assert((st.st_mode & S_ISGID) == 0);
-	}
+	if (S_ISGID != 0)
+		cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);
 
-	if ((expect_mode & 0111) != 0)
-		cl_assert((st.st_mode & 0111) != 0);
-	else
-		cl_assert((st.st_mode & 0111) == 0);
+	cl_assert_equal_b(
+		GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));
 
-	cl_assert((expect_mode & 0170000) == (st.st_mode & 0170000));
+	cl_assert_equal_i_fmt(
+		GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
 }
 
 void test_repo_init__extended_with_template(void)
@@ -427,6 +436,7 @@
 	git_buf expected = GIT_BUF_INIT;
 	git_buf actual = GIT_BUF_INIT;
 	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+	int filemode;
 
 	cl_set_cleanup(&cleanup_repository, "templated.git");
 
@@ -450,13 +460,15 @@
 	git_buf_free(&expected);
 	git_buf_free(&actual);
 
-	assert_hooks_match(
-		cl_fixture("template"), git_repository_path(_repo),
-		"hooks/update.sample", true);
+	filemode = cl_repo_get_bool(_repo, "core.filemode");
 
 	assert_hooks_match(
 		cl_fixture("template"), git_repository_path(_repo),
-		"hooks/link.sample", true);
+		"hooks/update.sample", filemode);
+
+	assert_hooks_match(
+		cl_fixture("template"), git_repository_path(_repo),
+		"hooks/link.sample", filemode);
 }
 
 void test_repo_init__extended_with_template_and_shared_mode(void)
@@ -464,7 +476,6 @@
 	git_buf expected = GIT_BUF_INIT;
 	git_buf actual = GIT_BUF_INIT;
 	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
-	git_config *config;
 	int filemode = true;
 	const char *repo_path = NULL;
 
@@ -480,9 +491,7 @@
 	cl_assert(!git_repository_is_bare(_repo));
 	cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/"));
 
-	cl_git_pass(git_repository_config(&config, _repo));
-	cl_git_pass(git_config_get_bool(&filemode, config, "core.filemode"));
-	git_config_free(config);
+	filemode = cl_repo_get_bool(_repo, "core.filemode");
 
 	cl_git_pass(git_futils_readbuffer(
 		&expected, cl_fixture("template/description")));
@@ -530,3 +539,68 @@
 
 	git_repository_free(reinit);
 }
+
+void test_repo_init__init_with_initial_commit(void)
+{
+	git_index *index;
+
+	cl_set_cleanup(&cleanup_repository, "committed");
+
+	/* Initialize the repository */
+	cl_git_pass(git_repository_init(&_repo, "committed", 0));
+
+	/* Init will be automatically created when requested for a new repo */
+	cl_git_pass(git_repository_index(&index, _repo));
+
+	/* Create a file so we can commit it
+	 *
+	 * If you are writing code outside the test suite, you can create this
+	 * file any way that you like, such as:
+	 *      FILE *fp = fopen("committed/file.txt", "w");
+	 *      fputs("some stuff\n", fp);
+	 *      fclose(fp);
+	 * We like to use the help functions because they do error detection
+	 * in a way that's easily compatible with our test suite.
+	 */
+	cl_git_mkfile("committed/file.txt", "some stuff\n");
+
+	/* Add file to the index */
+	cl_git_pass(git_index_add_bypath(index, "file.txt"));
+	cl_git_pass(git_index_write(index));
+
+	/* Intentionally not using cl_repo_commit_from_index here so this code
+	 * can be used as an example of how an initial commit is typically
+	 * made to a repository...
+	 */
+
+	/* Make sure we're ready to use git_signature_default :-) */
+	{
+		git_config *cfg, *local;
+		cl_git_pass(git_repository_config(&cfg, _repo));
+		cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
+		cl_git_pass(git_config_set_string(local, "user.name", "Test User"));
+		cl_git_pass(git_config_set_string(local, "user.email", "t@example.com"));
+		git_config_free(local);
+		git_config_free(cfg);
+	}
+
+	/* Create a commit with the new contents of the index */
+	{
+		git_signature *sig;
+		git_oid tree_id, commit_id;
+		git_tree *tree;
+
+		cl_git_pass(git_signature_default(&sig, _repo));
+		cl_git_pass(git_index_write_tree(&tree_id, index));
+		cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+
+		cl_git_pass(git_commit_create_v(
+			&commit_id, _repo, "HEAD", sig, sig,
+			NULL, "First", tree, 0));
+
+		git_tree_free(tree);
+		git_signature_free(sig);
+	}
+
+	git_index_free(index);
+}
diff --git a/tests-clar/repo/iterator.c b/tests/repo/iterator.c
similarity index 95%
rename from tests-clar/repo/iterator.c
rename to tests/repo/iterator.c
index 11a7d2a..56b5185 100644
--- a/tests-clar/repo/iterator.c
+++ b/tests/repo/iterator.c
@@ -906,6 +906,7 @@
 	static const char *expect_base[] = {
 		"heads/br2",
 		"heads/dir",
+		"heads/long-file-name",
 		"heads/master",
 		"heads/packed-test",
 		"heads/subtrees",
@@ -922,6 +923,40 @@
 
 	cl_git_pass(git_iterator_for_filesystem(
 		&i, "testrepo/.git/refs", 0, NULL, NULL));
-	expect_iterator_items(i, 11, expect_base, 11, expect_base);
+	expect_iterator_items(i, 12, expect_base, 12, expect_base);
+	git_iterator_free(i);
+}
+
+void test_repo_iterator__fs_preserves_error(void)
+{
+	git_iterator *i;
+	const git_index_entry *e;
+
+	if (!cl_is_chmod_supported())
+		return;
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+	cl_must_pass(p_mkdir("empty_standard_repo/r", 0777));
+	cl_git_mkfile("empty_standard_repo/r/a", "hello");
+	cl_must_pass(p_mkdir("empty_standard_repo/r/b", 0777));
+	cl_git_mkfile("empty_standard_repo/r/b/problem", "not me");
+	cl_must_pass(p_chmod("empty_standard_repo/r/b", 0000));
+	cl_must_pass(p_mkdir("empty_standard_repo/r/c", 0777));
+	cl_git_mkfile("empty_standard_repo/r/d", "final");
+
+	cl_git_pass(git_iterator_for_filesystem(
+		&i, "empty_standard_repo/r", 0, NULL, NULL));
+
+	cl_git_pass(git_iterator_advance(&e, i)); /* a */
+	cl_git_fail(git_iterator_advance(&e, i)); /* b */
+	cl_assert(giterr_last());
+	cl_assert(giterr_last()->message != NULL);
+	/* skip 'c/' empty directory */
+	cl_git_pass(git_iterator_advance(&e, i)); /* d */
+	cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
+
+	cl_must_pass(p_chmod("empty_standard_repo/r/b", 0777));
+
 	git_iterator_free(i);
 }
diff --git a/tests-clar/repo/message.c b/tests/repo/message.c
similarity index 100%
rename from tests-clar/repo/message.c
rename to tests/repo/message.c
diff --git a/tests-clar/repo/open.c b/tests/repo/open.c
similarity index 81%
rename from tests-clar/repo/open.c
rename to tests/repo/open.c
index 8408585..7cfe041 100644
--- a/tests-clar/repo/open.c
+++ b/tests/repo/open.c
@@ -69,14 +69,23 @@
 	cl_fixture_cleanup("attr");
 }
 
+static void make_gitlink_dir(const char *dir, const char *linktext)
+{
+	git_buf path = GIT_BUF_INIT;
+
+	cl_git_pass(git_futils_mkdir(dir, NULL, 0777, GIT_MKDIR_VERIFY_DIR));
+	cl_git_pass(git_buf_joinpath(&path, dir, ".git"));
+	cl_git_rewritefile(path.ptr, linktext);
+	git_buf_free(&path);
+}
+
 void test_repo_open__gitlinked(void)
 {
 	/* need to have both repo dir and workdir set up correctly */
 	git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
 	git_repository *repo2;
 
-	cl_must_pass(p_mkdir("alternate", 0777));
-	cl_git_mkfile("alternate/.git", "gitdir: ../empty_standard_repo/.git");
+	make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
 
 	cl_git_pass(git_repository_open(&repo2, "alternate"));
 
@@ -193,12 +202,11 @@
 
 	cl_git_sandbox_init("attr");
 
-	cl_git_pass(p_mkdir("alternate", 0777));
 	cl_git_pass(p_mkdir("invalid", 0777));
 	cl_git_pass(git_futils_mkdir_r("invalid2/.git", NULL, 0777));
 
 	for (scan = bad_links; *scan != NULL; scan++) {
-		cl_git_rewritefile("alternate/.git", *scan);
+		make_gitlink_dir("alternate", *scan);
 		cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
 	}
 
@@ -314,4 +322,55 @@
 	git_config_free(config);
 	git_repository_free(repo);
 	cl_fixture_cleanup("empty_standard_repo");
+
+	git_futils_dirs_global_shutdown();
+}
+
+void test_repo_open__force_bare(void)
+{
+	/* need to have both repo dir and workdir set up correctly */
+	git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+	git_repository *barerepo;
+
+	make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
+
+	cl_assert(!git_repository_is_bare(repo));
+
+	cl_git_pass(git_repository_open(&barerepo, "alternate"));
+	cl_assert(!git_repository_is_bare(barerepo));
+	git_repository_free(barerepo);
+
+	cl_git_pass(git_repository_open_bare(
+		&barerepo, "empty_standard_repo/.git"));
+	cl_assert(git_repository_is_bare(barerepo));
+	git_repository_free(barerepo);
+
+	cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git"));
+
+	cl_git_pass(git_repository_open_ext(
+		&barerepo, "alternate/.git", GIT_REPOSITORY_OPEN_BARE, NULL));
+	cl_assert(git_repository_is_bare(barerepo));
+	git_repository_free(barerepo);
+
+	cl_git_pass(p_mkdir("empty_standard_repo/subdir", 0777));
+	cl_git_mkfile("empty_standard_repo/subdir/something.txt", "something");
+
+	cl_git_fail(git_repository_open_bare(
+		&barerepo, "empty_standard_repo/subdir"));
+
+	cl_git_pass(git_repository_open_ext(
+		&barerepo, "empty_standard_repo/subdir", GIT_REPOSITORY_OPEN_BARE, NULL));
+	cl_assert(git_repository_is_bare(barerepo));
+	git_repository_free(barerepo);
+
+	cl_git_pass(p_mkdir("alternate/subdir", 0777));
+	cl_git_pass(p_mkdir("alternate/subdir/sub2", 0777));
+	cl_git_mkfile("alternate/subdir/sub2/something.txt", "something");
+
+	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));
+	cl_assert(git_repository_is_bare(barerepo));
+	git_repository_free(barerepo);
 }
diff --git a/tests/repo/pathspec.c b/tests/repo/pathspec.c
new file mode 100644
index 0000000..334066b
--- /dev/null
+++ b/tests/repo/pathspec.c
@@ -0,0 +1,385 @@
+#include "clar_libgit2.h"
+#include "git2/pathspec.h"
+
+static git_repository *g_repo;
+
+void test_repo_pathspec__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("status");
+}
+
+void test_repo_pathspec__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+	g_repo = NULL;
+}
+
+static char *str0[] = { "*_file", "new_file", "garbage" };
+static char *str1[] = { "*_FILE", "NEW_FILE", "GARBAGE" };
+static char *str2[] = { "staged_*" };
+static char *str3[] = { "!subdir", "*_file", "new_file" };
+static char *str4[] = { "*" };
+static char *str5[] = { "S*" };
+
+void test_repo_pathspec__workdir0(void)
+{
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "*_file", "new_file", "garbage" } */
+	s.strings = str0; s.count = ARRAY_SIZE(str0);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+	cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+	cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 0));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_FIND_FAILURES | GIT_PATHSPEC_FAILURES_ONLY, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir1(void)
+{
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+	s.strings = str1; s.count = ARRAY_SIZE(str1);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_IGNORE_CASE, ps));
+	cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_USE_CASE, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir2(void)
+{
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "staged_*" } */
+	s.strings = str2; s.count = ARRAY_SIZE(str2);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+	cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir3(void)
+{
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "!subdir", "*_file", "new_file" } */
+	s.strings = str3; s.count = ARRAY_SIZE(str3);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+	cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+
+	cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+	cl_assert_equal_s("new_file", git_pathspec_match_list_entry(m, 2));
+	cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 3));
+	cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+	cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 5));
+	cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 6));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir4(void)
+{
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "*" } */
+	s.strings = str4; s.count = ARRAY_SIZE(str4);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+	cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_s("这", git_pathspec_match_list_entry(m, 12));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+}
+
+
+void test_repo_pathspec__index0(void)
+{
+	git_index *idx;
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	cl_git_pass(git_repository_index(&idx, g_repo));
+
+	/* { "*_file", "new_file", "garbage" } */
+	s.strings = str0; s.count = ARRAY_SIZE(str0);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_index(&m, idx, 0, ps));
+	cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+	cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+	cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 3));
+	cl_assert_equal_s("staged_new_file_deleted_file", git_pathspec_match_list_entry(m, 4));
+	cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 5));
+	cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+	cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 7));
+	cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 8));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 9));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_index(&m, idx,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+	cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+	cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+	git_index_free(idx);
+}
+
+void test_repo_pathspec__index1(void)
+{
+	/* Currently the USE_CASE and IGNORE_CASE flags don't work on the
+	 * index because the index sort order for the index iterator is
+	 * set by the index itself.  I think the correct fix is for the
+	 * index not to embed a global sort order but to support traversal
+	 * in either case sensitive or insensitive order in a stateless
+	 * manner.
+	 *
+	 * Anyhow, as it is, there is no point in doing this test.
+	 */
+#if 0
+	git_index *idx;
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	cl_git_pass(git_repository_index(&idx, g_repo));
+
+	/* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+	s.strings = str1; s.count = ARRAY_SIZE(str1);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_pathspec_match_index(&m, idx,
+		GIT_PATHSPEC_USE_CASE, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_index(&m, idx,
+		GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_index(&m, idx,
+		GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_pathspec_free(ps);
+	git_index_free(idx);
+#endif
+}
+
+void test_repo_pathspec__tree0(void)
+{
+	git_object *tree;
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "*_file", "new_file", "garbage" } */
+	s.strings = str0; s.count = ARRAY_SIZE(str0);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+	cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(4, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+	cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+	cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 4));
+	cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+	cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+	cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+	git_pathspec_match_list_free(m);
+
+	git_object_free(tree);
+
+	cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+		GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+	cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+	cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+	cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 4));
+	cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 5));
+	cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 6));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+	cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+	cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+	cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+	cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+	git_pathspec_match_list_free(m);
+
+	git_object_free(tree);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__tree5(void)
+{
+	git_object *tree;
+	git_strarray s;
+	git_pathspec *ps;
+	git_pathspec_match_list *m;
+
+	/* { "S*" } */
+	s.strings = str5; s.count = ARRAY_SIZE(str5);
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+	cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+		GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+		GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_object_free(tree);
+
+	cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+		GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+	cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+	cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+	cl_assert_equal_s("subdir.txt", git_pathspec_match_list_entry(m, 5));
+	cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+	cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+	git_pathspec_match_list_free(m);
+
+	git_object_free(tree);
+
+	git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__in_memory(void)
+{
+	static char *strings[] = { "one", "two*", "!three*", "*four" };
+	git_strarray s = { strings, ARRAY_SIZE(strings) };
+	git_pathspec *ps;
+
+	cl_git_pass(git_pathspec_new(&ps, &s));
+
+	cl_assert(git_pathspec_matches_path(ps, 0, "one"));
+	cl_assert(!git_pathspec_matches_path(ps, 0, "ONE"));
+	cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_IGNORE_CASE, "ONE"));
+	cl_assert(git_pathspec_matches_path(ps, 0, "two"));
+	cl_assert(git_pathspec_matches_path(ps, 0, "two.txt"));
+	cl_assert(!git_pathspec_matches_path(ps, 0, "three.txt"));
+	cl_assert(git_pathspec_matches_path(ps, 0, "anything.four"));
+	cl_assert(!git_pathspec_matches_path(ps, 0, "three.four"));
+	cl_assert(!git_pathspec_matches_path(ps, 0, "nomatch"));
+	cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two"));
+	cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two*"));
+	cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "anyfour"));
+	cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "*four"));
+
+	git_pathspec_free(ps);
+}
diff --git a/tests-clar/repo/repo_helpers.c b/tests/repo/repo_helpers.c
similarity index 87%
rename from tests-clar/repo/repo_helpers.c
rename to tests/repo/repo_helpers.c
index 74902e4..3d477ff 100644
--- a/tests-clar/repo/repo_helpers.c
+++ b/tests/repo/repo_helpers.c
@@ -3,7 +3,7 @@
 #include "repo_helpers.h"
 #include "posix.h"
 
-void make_head_orphaned(git_repository* repo, const char *target)
+void make_head_unborn(git_repository* repo, const char *target)
 {
 	git_reference *head;
 
diff --git a/tests-clar/repo/repo_helpers.h b/tests/repo/repo_helpers.h
similarity index 62%
rename from tests-clar/repo/repo_helpers.h
rename to tests/repo/repo_helpers.h
index 09b5cac..6783d57 100644
--- a/tests-clar/repo/repo_helpers.h
+++ b/tests/repo/repo_helpers.h
@@ -2,5 +2,5 @@
 
 #define NON_EXISTING_HEAD "refs/heads/hide/and/seek"
 
-extern void make_head_orphaned(git_repository* repo, const char *target);
+extern void make_head_unborn(git_repository* repo, const char *target);
 extern void delete_head(git_repository* repo);
diff --git a/tests-clar/repo/setters.c b/tests/repo/setters.c
similarity index 100%
rename from tests-clar/repo/setters.c
rename to tests/repo/setters.c
diff --git a/tests-clar/repo/shallow.c b/tests/repo/shallow.c
similarity index 100%
rename from tests-clar/repo/shallow.c
rename to tests/repo/shallow.c
diff --git a/tests-clar/repo/state.c b/tests/repo/state.c
similarity index 100%
rename from tests-clar/repo/state.c
rename to tests/repo/state.c
diff --git a/tests-clar/reset/default.c b/tests/reset/default.c
similarity index 100%
rename from tests-clar/reset/default.c
rename to tests/reset/default.c
diff --git a/tests-clar/reset/hard.c b/tests/reset/hard.c
similarity index 100%
rename from tests-clar/reset/hard.c
rename to tests/reset/hard.c
diff --git a/tests-clar/reset/mixed.c b/tests/reset/mixed.c
similarity index 100%
rename from tests-clar/reset/mixed.c
rename to tests/reset/mixed.c
diff --git a/tests-clar/reset/reset_helpers.c b/tests/reset/reset_helpers.c
similarity index 100%
rename from tests-clar/reset/reset_helpers.c
rename to tests/reset/reset_helpers.c
diff --git a/tests-clar/reset/reset_helpers.h b/tests/reset/reset_helpers.h
similarity index 100%
rename from tests-clar/reset/reset_helpers.h
rename to tests/reset/reset_helpers.h
diff --git a/tests-clar/reset/soft.c b/tests/reset/soft.c
similarity index 94%
rename from tests-clar/reset/soft.c
rename to tests/reset/soft.c
index 884697c..bd6fcc2 100644
--- a/tests-clar/reset/soft.c
+++ b/tests/reset/soft.c
@@ -95,19 +95,19 @@
 	cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT));
 }
 
-void test_reset_soft__resetting_against_an_orphaned_head_repo_makes_the_head_no_longer_orphaned(void)
+void test_reset_soft__resetting_against_an_unborn_head_repo_makes_the_head_no_longer_unborn(void)
 {
 	git_reference *head;
 
 	retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO);
 
-	make_head_orphaned(repo, NON_EXISTING_HEAD);
+	make_head_unborn(repo, NON_EXISTING_HEAD);
 
-	cl_assert_equal_i(true, git_repository_head_orphan(repo));
+	cl_assert_equal_i(true, git_repository_head_unborn(repo));
 
 	cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT));
 
-	cl_assert_equal_i(false, git_repository_head_orphan(repo));
+	cl_assert_equal_i(false, git_repository_head_unborn(repo));
 
 	cl_git_pass(git_reference_lookup(&head, repo, NON_EXISTING_HEAD));
 	cl_assert_equal_i(0, git_oid_streq(git_reference_target(head), KNOWN_COMMIT_IN_BARE_REPO));
diff --git a/tests-clar/resources/.gitattributes b/tests/resources/.gitattributes
similarity index 100%
rename from tests-clar/resources/.gitattributes
rename to tests/resources/.gitattributes
diff --git a/tests-clar/resources/.gitignore b/tests/resources/.gitignore
similarity index 100%
rename from tests-clar/resources/.gitignore
rename to tests/resources/.gitignore
diff --git a/tests-clar/resources/attr/.gitted/HEAD b/tests/resources/attr/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/attr/.gitted/HEAD
rename to tests/resources/attr/.gitted/HEAD
diff --git a/tests-clar/resources/attr/.gitted/config b/tests/resources/attr/.gitted/config
similarity index 100%
rename from tests-clar/resources/attr/.gitted/config
rename to tests/resources/attr/.gitted/config
diff --git a/tests-clar/resources/attr/.gitted/description b/tests/resources/attr/.gitted/description
similarity index 100%
rename from tests-clar/resources/attr/.gitted/description
rename to tests/resources/attr/.gitted/description
diff --git a/tests-clar/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
similarity index 100%
rename from tests-clar/resources/attr/.gitted/index
rename to tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes
similarity index 100%
rename from tests-clar/resources/attr/.gitted/info/attributes
rename to tests/resources/attr/.gitted/info/attributes
diff --git a/tests-clar/resources/attr/.gitted/info/exclude b/tests/resources/attr/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/attr/.gitted/info/exclude
rename to tests/resources/attr/.gitted/info/exclude
diff --git a/tests-clar/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/attr/.gitted/logs/HEAD
rename to tests/resources/attr/.gitted/logs/HEAD
diff --git a/tests-clar/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/attr/.gitted/logs/refs/heads/master
rename to tests/resources/attr/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
rename to tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512 b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
rename to tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
rename to tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
rename to tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
rename to tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
rename to tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
diff --git a/tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
rename to tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
rename to tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
rename to tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
rename to tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
rename to tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
rename to tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
rename to tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
rename to tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
rename to tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
rename to tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
rename to tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
rename to tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
rename to tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
rename to tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
diff --git a/tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
rename to tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
rename to tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911 b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
rename to tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
diff --git a/tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
rename to tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
rename to tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
rename to tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
rename to tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
rename to tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
rename to tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
rename to tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
rename to tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
rename to tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
rename to tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04 b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
rename to tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
rename to tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
rename to tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
rename to tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
rename to tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
diff --git a/tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
rename to tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
rename to tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
rename to tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
rename to tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
rename to tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
rename to tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
rename to tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
rename to tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
rename to tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
rename to tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
rename to tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
rename to tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
diff --git a/tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
similarity index 100%
rename from tests-clar/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
rename to tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
diff --git a/tests-clar/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/attr/.gitted/refs/heads/master
rename to tests/resources/attr/.gitted/refs/heads/master
diff --git a/tests-clar/resources/attr/attr0 b/tests/resources/attr/attr0
similarity index 100%
rename from tests-clar/resources/attr/attr0
rename to tests/resources/attr/attr0
diff --git a/tests-clar/resources/attr/attr1 b/tests/resources/attr/attr1
similarity index 100%
rename from tests-clar/resources/attr/attr1
rename to tests/resources/attr/attr1
diff --git a/tests-clar/resources/attr/attr2 b/tests/resources/attr/attr2
similarity index 100%
rename from tests-clar/resources/attr/attr2
rename to tests/resources/attr/attr2
diff --git a/tests-clar/resources/attr/attr3 b/tests/resources/attr/attr3
similarity index 100%
rename from tests-clar/resources/attr/attr3
rename to tests/resources/attr/attr3
diff --git a/tests-clar/resources/attr/binfile b/tests/resources/attr/binfile
similarity index 100%
rename from tests-clar/resources/attr/binfile
rename to tests/resources/attr/binfile
diff --git a/tests-clar/resources/attr/dir/file b/tests/resources/attr/dir/file
similarity index 100%
rename from tests-clar/resources/attr/dir/file
rename to tests/resources/attr/dir/file
diff --git a/tests-clar/resources/attr/file b/tests/resources/attr/file
similarity index 100%
rename from tests-clar/resources/attr/file
rename to tests/resources/attr/file
diff --git a/tests-clar/resources/attr/gitattributes b/tests/resources/attr/gitattributes
similarity index 100%
rename from tests-clar/resources/attr/gitattributes
rename to tests/resources/attr/gitattributes
diff --git a/tests-clar/resources/attr/gitignore b/tests/resources/attr/gitignore
similarity index 100%
rename from tests-clar/resources/attr/gitignore
rename to tests/resources/attr/gitignore
diff --git a/tests-clar/resources/attr/ign b/tests/resources/attr/ign
similarity index 100%
rename from tests-clar/resources/attr/ign
rename to tests/resources/attr/ign
diff --git a/tests-clar/resources/attr/macro_bad b/tests/resources/attr/macro_bad
similarity index 100%
rename from tests-clar/resources/attr/macro_bad
rename to tests/resources/attr/macro_bad
diff --git a/tests-clar/resources/attr/macro_test b/tests/resources/attr/macro_test
similarity index 100%
rename from tests-clar/resources/attr/macro_test
rename to tests/resources/attr/macro_test
diff --git a/tests-clar/resources/attr/root_test1 b/tests/resources/attr/root_test1
similarity index 100%
rename from tests-clar/resources/attr/root_test1
rename to tests/resources/attr/root_test1
diff --git a/tests-clar/resources/attr/root_test2 b/tests/resources/attr/root_test2
similarity index 100%
rename from tests-clar/resources/attr/root_test2
rename to tests/resources/attr/root_test2
diff --git a/tests-clar/resources/attr/root_test3 b/tests/resources/attr/root_test3
similarity index 100%
rename from tests-clar/resources/attr/root_test3
rename to tests/resources/attr/root_test3
diff --git a/tests-clar/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
similarity index 100%
rename from tests-clar/resources/attr/root_test4.txt
rename to tests/resources/attr/root_test4.txt
diff --git a/tests-clar/resources/attr/sub/.gitattributes b/tests/resources/attr/sub/.gitattributes
similarity index 100%
rename from tests-clar/resources/attr/sub/.gitattributes
rename to tests/resources/attr/sub/.gitattributes
diff --git a/tests-clar/resources/attr/sub/abc b/tests/resources/attr/sub/abc
similarity index 100%
rename from tests-clar/resources/attr/sub/abc
rename to tests/resources/attr/sub/abc
diff --git a/tests-clar/resources/attr/sub/dir/file b/tests/resources/attr/sub/dir/file
similarity index 100%
rename from tests-clar/resources/attr/sub/dir/file
rename to tests/resources/attr/sub/dir/file
diff --git a/tests-clar/resources/attr/sub/file b/tests/resources/attr/sub/file
similarity index 100%
rename from tests-clar/resources/attr/sub/file
rename to tests/resources/attr/sub/file
diff --git a/tests-clar/resources/attr/sub/ign/file b/tests/resources/attr/sub/ign/file
similarity index 100%
rename from tests-clar/resources/attr/sub/ign/file
rename to tests/resources/attr/sub/ign/file
diff --git a/tests-clar/resources/attr/sub/ign/sub/file b/tests/resources/attr/sub/ign/sub/file
similarity index 100%
rename from tests-clar/resources/attr/sub/ign/sub/file
rename to tests/resources/attr/sub/ign/sub/file
diff --git a/tests-clar/resources/attr/sub/sub/.gitattributes b/tests/resources/attr/sub/sub/.gitattributes
similarity index 100%
rename from tests-clar/resources/attr/sub/sub/.gitattributes
rename to tests/resources/attr/sub/sub/.gitattributes
diff --git a/tests-clar/resources/attr/sub/sub/dir b/tests/resources/attr/sub/sub/dir
similarity index 100%
rename from tests-clar/resources/attr/sub/sub/dir
rename to tests/resources/attr/sub/sub/dir
diff --git a/tests-clar/resources/attr/sub/sub/file b/tests/resources/attr/sub/sub/file
similarity index 100%
rename from tests-clar/resources/attr/sub/sub/file
rename to tests/resources/attr/sub/sub/file
diff --git a/tests-clar/resources/attr/sub/sub/subsub.txt b/tests/resources/attr/sub/sub/subsub.txt
similarity index 100%
rename from tests-clar/resources/attr/sub/sub/subsub.txt
rename to tests/resources/attr/sub/sub/subsub.txt
diff --git a/tests-clar/resources/attr/sub/subdir_test1 b/tests/resources/attr/sub/subdir_test1
similarity index 100%
rename from tests-clar/resources/attr/sub/subdir_test1
rename to tests/resources/attr/sub/subdir_test1
diff --git a/tests-clar/resources/attr/sub/subdir_test2.txt b/tests/resources/attr/sub/subdir_test2.txt
similarity index 100%
rename from tests-clar/resources/attr/sub/subdir_test2.txt
rename to tests/resources/attr/sub/subdir_test2.txt
diff --git a/tests-clar/resources/attr_index/.gitted/HEAD b/tests/resources/attr_index/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/HEAD
rename to tests/resources/attr_index/.gitted/HEAD
diff --git a/tests-clar/resources/attr_index/.gitted/config b/tests/resources/attr_index/.gitted/config
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/config
rename to tests/resources/attr_index/.gitted/config
diff --git a/tests-clar/resources/attr_index/.gitted/description b/tests/resources/attr_index/.gitted/description
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/description
rename to tests/resources/attr_index/.gitted/description
diff --git a/tests-clar/resources/attr_index/.gitted/index b/tests/resources/attr_index/.gitted/index
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/index
rename to tests/resources/attr_index/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/info/exclude b/tests/resources/attr_index/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/info/exclude
rename to tests/resources/attr_index/.gitted/info/exclude
diff --git a/tests-clar/resources/attr_index/.gitted/info/refs b/tests/resources/attr_index/.gitted/info/refs
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/info/refs
rename to tests/resources/attr_index/.gitted/info/refs
diff --git a/tests-clar/resources/attr_index/.gitted/logs/HEAD b/tests/resources/attr_index/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/logs/HEAD
rename to tests/resources/attr_index/.gitted/logs/HEAD
diff --git a/tests-clar/resources/attr_index/.gitted/logs/refs/heads/master b/tests/resources/attr_index/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/logs/refs/heads/master
rename to tests/resources/attr_index/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
rename to tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
diff --git a/tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
rename to tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
rename to tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
rename to tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/info/packs b/tests/resources/attr_index/.gitted/objects/info/packs
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/info/packs
rename to tests/resources/attr_index/.gitted/objects/info/packs
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
rename to tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
rename to tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
Binary files differ
diff --git a/tests-clar/resources/attr_index/.gitted/packed-refs b/tests/resources/attr_index/.gitted/packed-refs
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/packed-refs
rename to tests/resources/attr_index/.gitted/packed-refs
diff --git a/tests-clar/resources/attr_index/.gitted/refs/heads/master b/tests/resources/attr_index/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/attr_index/.gitted/refs/heads/master
rename to tests/resources/attr_index/.gitted/refs/heads/master
diff --git a/tests-clar/resources/attr_index/README.md b/tests/resources/attr_index/README.md
similarity index 100%
rename from tests-clar/resources/attr_index/README.md
rename to tests/resources/attr_index/README.md
diff --git a/tests-clar/resources/attr_index/README.txt b/tests/resources/attr_index/README.txt
similarity index 100%
rename from tests-clar/resources/attr_index/README.txt
rename to tests/resources/attr_index/README.txt
diff --git a/tests-clar/resources/attr_index/gitattributes b/tests/resources/attr_index/gitattributes
similarity index 100%
rename from tests-clar/resources/attr_index/gitattributes
rename to tests/resources/attr_index/gitattributes
diff --git a/tests-clar/resources/attr_index/sub/sub/.gitattributes b/tests/resources/attr_index/sub/sub/.gitattributes
similarity index 100%
rename from tests-clar/resources/attr_index/sub/sub/.gitattributes
rename to tests/resources/attr_index/sub/sub/.gitattributes
diff --git a/tests-clar/resources/attr_index/sub/sub/README.md b/tests/resources/attr_index/sub/sub/README.md
similarity index 100%
rename from tests-clar/resources/attr_index/sub/sub/README.md
rename to tests/resources/attr_index/sub/sub/README.md
diff --git a/tests-clar/resources/attr_index/sub/sub/README.txt b/tests/resources/attr_index/sub/sub/README.txt
similarity index 100%
rename from tests-clar/resources/attr_index/sub/sub/README.txt
rename to tests/resources/attr_index/sub/sub/README.txt
diff --git a/tests/resources/bad.index b/tests/resources/bad.index
new file mode 100644
index 0000000..5374654
--- /dev/null
+++ b/tests/resources/bad.index
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/HEAD b/tests/resources/bad_tag.git/HEAD
similarity index 100%
rename from tests-clar/resources/bad_tag.git/HEAD
rename to tests/resources/bad_tag.git/HEAD
diff --git a/tests-clar/resources/bad_tag.git/config b/tests/resources/bad_tag.git/config
similarity index 100%
rename from tests-clar/resources/bad_tag.git/config
rename to tests/resources/bad_tag.git/config
diff --git a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
similarity index 100%
rename from tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
rename to tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
similarity index 100%
rename from tests-clar/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
rename to tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
Binary files differ
diff --git a/tests-clar/resources/bad_tag.git/packed-refs b/tests/resources/bad_tag.git/packed-refs
similarity index 100%
rename from tests-clar/resources/bad_tag.git/packed-refs
rename to tests/resources/bad_tag.git/packed-refs
diff --git a/tests-clar/resources/bad_tag.git/refs/dummy-marker.txt b/tests/resources/bad_tag.git/refs/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/bad_tag.git/refs/dummy-marker.txt
rename to tests/resources/bad_tag.git/refs/dummy-marker.txt
diff --git a/tests-clar/resources/big.index b/tests/resources/big.index
similarity index 100%
rename from tests-clar/resources/big.index
rename to tests/resources/big.index
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/HEAD b/tests/resources/binaryunicode/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/HEAD
rename to tests/resources/binaryunicode/.gitted/HEAD
diff --git a/tests-clar/resources/binaryunicode/.gitted/config b/tests/resources/binaryunicode/.gitted/config
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/config
rename to tests/resources/binaryunicode/.gitted/config
diff --git a/tests-clar/resources/binaryunicode/.gitted/description b/tests/resources/binaryunicode/.gitted/description
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/description
rename to tests/resources/binaryunicode/.gitted/description
diff --git a/tests-clar/resources/binaryunicode/.gitted/index b/tests/resources/binaryunicode/.gitted/index
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/index
rename to tests/resources/binaryunicode/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/info/exclude b/tests/resources/binaryunicode/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/info/exclude
rename to tests/resources/binaryunicode/.gitted/info/exclude
diff --git a/tests-clar/resources/binaryunicode/.gitted/info/refs b/tests/resources/binaryunicode/.gitted/info/refs
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/info/refs
rename to tests/resources/binaryunicode/.gitted/info/refs
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/info/packs b/tests/resources/binaryunicode/.gitted/objects/info/packs
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/objects/info/packs
rename to tests/resources/binaryunicode/.gitted/objects/info/packs
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
rename to tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
rename to tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
Binary files differ
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch1 b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/refs/heads/branch1
rename to tests/resources/binaryunicode/.gitted/refs/heads/branch1
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/branch2 b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/refs/heads/branch2
rename to tests/resources/binaryunicode/.gitted/refs/heads/branch2
diff --git a/tests-clar/resources/binaryunicode/.gitted/refs/heads/master b/tests/resources/binaryunicode/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/binaryunicode/.gitted/refs/heads/master
rename to tests/resources/binaryunicode/.gitted/refs/heads/master
diff --git a/tests-clar/resources/binaryunicode/file.txt b/tests/resources/binaryunicode/file.txt
similarity index 100%
rename from tests-clar/resources/binaryunicode/file.txt
rename to tests/resources/binaryunicode/file.txt
diff --git a/tests-clar/resources/bad_tag.git/HEAD b/tests/resources/blametest.git/HEAD
similarity index 100%
copy from tests-clar/resources/bad_tag.git/HEAD
copy to tests/resources/blametest.git/HEAD
diff --git a/tests-clar/resources/twowaymerge.git/config b/tests/resources/blametest.git/config
similarity index 100%
copy from tests-clar/resources/twowaymerge.git/config
copy to tests/resources/blametest.git/config
diff --git a/tests-clar/resources/duplicate.git/description b/tests/resources/blametest.git/description
similarity index 100%
copy from tests-clar/resources/duplicate.git/description
copy to tests/resources/blametest.git/description
diff --git a/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448 b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
new file mode 100644
index 0000000..90331ce
--- /dev/null
+++ b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
new file mode 100644
index 0000000..7189027
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
new file mode 100644
index 0000000..e664306
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
new file mode 100644
index 0000000..7da4cf5
--- /dev/null
+++ b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
new file mode 100644
index 0000000..5f41f29
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36 b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
new file mode 100644
index 0000000..c6c285e
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
Binary files differ
diff --git a/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
new file mode 100644
index 0000000..9d8f605
--- /dev/null
+++ b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
Binary files differ
diff --git a/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
new file mode 100644
index 0000000..d716e3b
--- /dev/null
+++ b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
new file mode 100644
index 0000000..12407f6
--- /dev/null
+++ b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
Binary files differ
diff --git a/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9 b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
new file mode 100644
index 0000000..bc13bad
--- /dev/null
+++ b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2 b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
new file mode 100644
index 0000000..caf5cc1
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
new file mode 100644
index 0000000..d4f9cca
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5 b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
new file mode 100644
index 0000000..a428fd6
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
Binary files differ
diff --git a/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136 b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
new file mode 100644
index 0000000..4e6ad15
--- /dev/null
+++ b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
Binary files differ
diff --git a/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3 b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
new file mode 100644
index 0000000..2048e81
--- /dev/null
+++ b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640 b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
new file mode 100644
index 0000000..926c4bb
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
@@ -0,0 +1 @@
+x+)JMU03c040031QHÔ+©(a˜Ñyíihyâª>3ö<í^¹G¥nÕ@$H­É\;ÍMêo㶜úѬ‹£Ƥ
\ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
new file mode 100644
index 0000000..e9e1383
--- /dev/null
+++ b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
@@ -0,0 +1,4 @@
+x•K
+Â0@]÷³ëJ™|'"¢ÞÀ$“ÔvÑVbzžÀíƒïÉ:ÏS­ðÐj)Ñif£Ñ‘äDNSÆdOÌbs§Ñ[ìÞ±–¥Ab(
+¦Y;“ƒfç¬(‚˜„ƒ‰®‹[×
+·²À³Õ¸%8§Ïõ5µqK'Yç(ã‘zF8b@ìvº·µòŸÕÝKý£ÿc–?S
\ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243 b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
new file mode 100644
index 0000000..7e5586c
--- /dev/null
+++ b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
Binary files differ
diff --git a/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1 b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
new file mode 100644
index 0000000..d021ccf
--- /dev/null
+++ b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
Binary files differ
diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master
new file mode 100644
index 0000000..b763025
--- /dev/null
+++ b/tests/resources/blametest.git/refs/heads/master
@@ -0,0 +1 @@
+bc7c5ac2bafe828a68e9d1d460343718d6fbe136
diff --git a/tests-clar/resources/config/.gitconfig b/tests/resources/config/.gitconfig
similarity index 100%
rename from tests-clar/resources/config/.gitconfig
rename to tests/resources/config/.gitconfig
diff --git a/tests/resources/config/config-include b/tests/resources/config/config-include
new file mode 100644
index 0000000..6b5e79d
--- /dev/null
+++ b/tests/resources/config/config-include
@@ -0,0 +1,2 @@
+[include]
+	path = config-included
diff --git a/tests/resources/config/config-included b/tests/resources/config/config-included
new file mode 100644
index 0000000..089ca08
--- /dev/null
+++ b/tests/resources/config/config-included
@@ -0,0 +1,2 @@
+[foo "bar"]
+     baz = huzzah
diff --git a/tests-clar/resources/config/config0 b/tests/resources/config/config0
similarity index 100%
rename from tests-clar/resources/config/config0
rename to tests/resources/config/config0
diff --git a/tests-clar/resources/config/config1 b/tests/resources/config/config1
similarity index 100%
rename from tests-clar/resources/config/config1
rename to tests/resources/config/config1
diff --git a/tests-clar/resources/config/config10 b/tests/resources/config/config10
similarity index 100%
rename from tests-clar/resources/config/config10
rename to tests/resources/config/config10
diff --git a/tests-clar/resources/config/config11 b/tests/resources/config/config11
similarity index 83%
rename from tests-clar/resources/config/config11
rename to tests/resources/config/config11
index 7331862..1d8a744 100644
--- a/tests-clar/resources/config/config11
+++ b/tests/resources/config/config11
@@ -1,4 +1,4 @@
-[remote "fancy"]
+[remote "ab"]
     url = git://github.com/libgit2/libgit2
     url = git://git.example.com/libgit2
 
diff --git a/tests-clar/resources/config/config12 b/tests/resources/config/config12
similarity index 100%
rename from tests-clar/resources/config/config12
rename to tests/resources/config/config12
diff --git a/tests-clar/resources/config/config13 b/tests/resources/config/config13
similarity index 100%
rename from tests-clar/resources/config/config13
rename to tests/resources/config/config13
diff --git a/tests-clar/resources/config/config14 b/tests/resources/config/config14
similarity index 100%
rename from tests-clar/resources/config/config14
rename to tests/resources/config/config14
diff --git a/tests-clar/resources/config/config15 b/tests/resources/config/config15
similarity index 100%
rename from tests-clar/resources/config/config15
rename to tests/resources/config/config15
diff --git a/tests-clar/resources/config/config16 b/tests/resources/config/config16
similarity index 100%
rename from tests-clar/resources/config/config16
rename to tests/resources/config/config16
diff --git a/tests-clar/resources/config/config17 b/tests/resources/config/config17
similarity index 100%
rename from tests-clar/resources/config/config17
rename to tests/resources/config/config17
diff --git a/tests-clar/resources/config/config18 b/tests/resources/config/config18
similarity index 100%
rename from tests-clar/resources/config/config18
rename to tests/resources/config/config18
diff --git a/tests-clar/resources/config/config19 b/tests/resources/config/config19
similarity index 100%
rename from tests-clar/resources/config/config19
rename to tests/resources/config/config19
diff --git a/tests-clar/resources/config/config2 b/tests/resources/config/config2
similarity index 100%
rename from tests-clar/resources/config/config2
rename to tests/resources/config/config2
diff --git a/tests/resources/config/config20 b/tests/resources/config/config20
new file mode 100644
index 0000000..8f0f12c
--- /dev/null
+++ b/tests/resources/config/config20
@@ -0,0 +1,11 @@
+[valid "[subsection]"]
+    something = a
+; we don't allow anything after closing "
+[sec "[subsec]/child"]
+    parent = grand
+[sec2 "[subsec2]/child2"]
+    type = dvcs
+[sec3 "escape\"quote"]
+    vcs = git
+[sec4 "escaping\\slash"]
+    lib = git2
diff --git a/tests-clar/resources/config/config3 b/tests/resources/config/config3
similarity index 100%
rename from tests-clar/resources/config/config3
rename to tests/resources/config/config3
diff --git a/tests-clar/resources/config/config4 b/tests/resources/config/config4
similarity index 100%
rename from tests-clar/resources/config/config4
rename to tests/resources/config/config4
diff --git a/tests-clar/resources/config/config5 b/tests/resources/config/config5
similarity index 100%
rename from tests-clar/resources/config/config5
rename to tests/resources/config/config5
diff --git a/tests-clar/resources/config/config6 b/tests/resources/config/config6
similarity index 100%
rename from tests-clar/resources/config/config6
rename to tests/resources/config/config6
diff --git a/tests-clar/resources/config/config7 b/tests/resources/config/config7
similarity index 100%
rename from tests-clar/resources/config/config7
rename to tests/resources/config/config7
diff --git a/tests-clar/resources/config/config8 b/tests/resources/config/config8
similarity index 100%
rename from tests-clar/resources/config/config8
rename to tests/resources/config/config8
diff --git a/tests-clar/resources/config/config9 b/tests/resources/config/config9
similarity index 100%
rename from tests-clar/resources/config/config9
rename to tests/resources/config/config9
diff --git a/tests-clar/resources/crlf/.gitted/HEAD b/tests/resources/crlf/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/HEAD
rename to tests/resources/crlf/.gitted/HEAD
diff --git a/tests-clar/resources/crlf/.gitted/config b/tests/resources/crlf/.gitted/config
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/config
rename to tests/resources/crlf/.gitted/config
diff --git a/tests-clar/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
rename to tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
rename to tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
rename to tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
rename to tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
rename to tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb
diff --git a/tests-clar/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6 b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
rename to tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
rename to tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
rename to tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
rename to tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
diff --git a/tests-clar/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
rename to tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
rename to tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
rename to tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
rename to tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
Binary files differ
diff --git a/tests-clar/resources/crlf/.gitted/refs/heads/master b/tests/resources/crlf/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/refs/heads/master
rename to tests/resources/crlf/.gitted/refs/heads/master
diff --git a/tests-clar/resources/crlf/.gitted/refs/heads/utf8 b/tests/resources/crlf/.gitted/refs/heads/utf8
similarity index 100%
rename from tests-clar/resources/crlf/.gitted/refs/heads/utf8
rename to tests/resources/crlf/.gitted/refs/heads/utf8
diff --git a/tests-clar/resources/deprecated-mode.git/HEAD b/tests/resources/deprecated-mode.git/HEAD
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/HEAD
rename to tests/resources/deprecated-mode.git/HEAD
diff --git a/tests-clar/resources/deprecated-mode.git/config b/tests/resources/deprecated-mode.git/config
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/config
rename to tests/resources/deprecated-mode.git/config
diff --git a/tests-clar/resources/deprecated-mode.git/description b/tests/resources/deprecated-mode.git/description
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/description
rename to tests/resources/deprecated-mode.git/description
diff --git a/tests-clar/resources/deprecated-mode.git/index b/tests/resources/deprecated-mode.git/index
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/index
rename to tests/resources/deprecated-mode.git/index
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/info/exclude b/tests/resources/deprecated-mode.git/info/exclude
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/info/exclude
rename to tests/resources/deprecated-mode.git/info/exclude
diff --git a/tests-clar/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
rename to tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
rename to tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
rename to tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021 b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
rename to tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
Binary files differ
diff --git a/tests-clar/resources/deprecated-mode.git/refs/heads/master b/tests/resources/deprecated-mode.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/deprecated-mode.git/refs/heads/master
rename to tests/resources/deprecated-mode.git/refs/heads/master
diff --git a/tests-clar/resources/diff/.gitted/HEAD b/tests/resources/diff/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/diff/.gitted/HEAD
rename to tests/resources/diff/.gitted/HEAD
diff --git a/tests-clar/resources/diff/.gitted/config b/tests/resources/diff/.gitted/config
similarity index 100%
rename from tests-clar/resources/diff/.gitted/config
rename to tests/resources/diff/.gitted/config
diff --git a/tests-clar/resources/diff/.gitted/description b/tests/resources/diff/.gitted/description
similarity index 100%
rename from tests-clar/resources/diff/.gitted/description
rename to tests/resources/diff/.gitted/description
diff --git a/tests-clar/resources/diff/.gitted/index b/tests/resources/diff/.gitted/index
similarity index 100%
rename from tests-clar/resources/diff/.gitted/index
rename to tests/resources/diff/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/info/exclude b/tests/resources/diff/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/diff/.gitted/info/exclude
rename to tests/resources/diff/.gitted/info/exclude
diff --git a/tests-clar/resources/diff/.gitted/logs/HEAD b/tests/resources/diff/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/diff/.gitted/logs/HEAD
rename to tests/resources/diff/.gitted/logs/HEAD
diff --git a/tests-clar/resources/diff/.gitted/logs/refs/heads/master b/tests/resources/diff/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/diff/.gitted/logs/refs/heads/master
rename to tests/resources/diff/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7 b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
rename to tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847 b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
rename to tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989 b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
rename to tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10 b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
rename to tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693 b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
rename to tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
rename to tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
diff --git a/tests-clar/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455 b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
rename to tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
Binary files differ
diff --git a/tests-clar/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69 b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
similarity index 100%
rename from tests-clar/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
rename to tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
diff --git a/tests-clar/resources/diff/.gitted/refs/heads/master b/tests/resources/diff/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/diff/.gitted/refs/heads/master
rename to tests/resources/diff/.gitted/refs/heads/master
diff --git a/tests-clar/resources/diff/another.txt b/tests/resources/diff/another.txt
similarity index 100%
rename from tests-clar/resources/diff/another.txt
rename to tests/resources/diff/another.txt
diff --git a/tests-clar/resources/diff/readme.txt b/tests/resources/diff/readme.txt
similarity index 100%
rename from tests-clar/resources/diff/readme.txt
rename to tests/resources/diff/readme.txt
diff --git a/tests-clar/resources/duplicate.git/COMMIT_EDITMSG b/tests/resources/duplicate.git/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/duplicate.git/COMMIT_EDITMSG
rename to tests/resources/duplicate.git/COMMIT_EDITMSG
diff --git a/tests-clar/resources/duplicate.git/HEAD b/tests/resources/duplicate.git/HEAD
similarity index 100%
rename from tests-clar/resources/duplicate.git/HEAD
rename to tests/resources/duplicate.git/HEAD
diff --git a/tests-clar/resources/short_tag.git/config b/tests/resources/duplicate.git/config
similarity index 100%
copy from tests-clar/resources/short_tag.git/config
copy to tests/resources/duplicate.git/config
diff --git a/tests-clar/resources/duplicate.git/description b/tests/resources/duplicate.git/description
similarity index 100%
rename from tests-clar/resources/duplicate.git/description
rename to tests/resources/duplicate.git/description
diff --git a/tests-clar/resources/duplicate.git/index b/tests/resources/duplicate.git/index
similarity index 100%
rename from tests-clar/resources/duplicate.git/index
rename to tests/resources/duplicate.git/index
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/info/exclude b/tests/resources/duplicate.git/info/exclude
similarity index 100%
rename from tests-clar/resources/duplicate.git/info/exclude
rename to tests/resources/duplicate.git/info/exclude
diff --git a/tests-clar/resources/duplicate.git/info/refs b/tests/resources/duplicate.git/info/refs
similarity index 100%
rename from tests-clar/resources/duplicate.git/info/refs
rename to tests/resources/duplicate.git/info/refs
diff --git a/tests-clar/resources/duplicate.git/logs/HEAD b/tests/resources/duplicate.git/logs/HEAD
similarity index 100%
rename from tests-clar/resources/duplicate.git/logs/HEAD
rename to tests/resources/duplicate.git/logs/HEAD
diff --git a/tests-clar/resources/duplicate.git/logs/refs/heads/master b/tests/resources/duplicate.git/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/duplicate.git/logs/refs/heads/master
rename to tests/resources/duplicate.git/logs/refs/heads/master
diff --git a/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
new file mode 100644
index 0000000..47c2a63
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
similarity index 100%
rename from tests-clar/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
rename to tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/info/packs b/tests/resources/duplicate.git/objects/info/packs
new file mode 100644
index 0000000..d0fdf90
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/info/packs
@@ -0,0 +1,3 @@
+P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+P pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
+
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
new file mode 100644
index 0000000..acbed82
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
new file mode 100644
index 0000000..652b0c9
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
new file mode 100644
index 0000000..fff6855
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
new file mode 100644
index 0000000..e3e5f0e
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
similarity index 100%
rename from tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
rename to tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
similarity index 100%
rename from tests-clar/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
rename to tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
new file mode 100644
index 0000000..9f78f6e
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
new file mode 100644
index 0000000..d1dd3b6
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
Binary files differ
diff --git a/tests-clar/resources/duplicate.git/packed-refs b/tests/resources/duplicate.git/packed-refs
similarity index 100%
rename from tests-clar/resources/duplicate.git/packed-refs
rename to tests/resources/duplicate.git/packed-refs
diff --git a/tests/resources/duplicate.git/refs/heads/dummy-marker.txt b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
new file mode 100644
index 0000000..421376d
--- /dev/null
+++ b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
@@ -0,0 +1 @@
+dummy
diff --git a/tests-clar/resources/empty_bare.git/HEAD b/tests/resources/empty_bare.git/HEAD
similarity index 100%
rename from tests-clar/resources/empty_bare.git/HEAD
rename to tests/resources/empty_bare.git/HEAD
diff --git a/tests-clar/resources/empty_bare.git/config b/tests/resources/empty_bare.git/config
similarity index 100%
rename from tests-clar/resources/empty_bare.git/config
rename to tests/resources/empty_bare.git/config
diff --git a/tests-clar/resources/empty_bare.git/description b/tests/resources/empty_bare.git/description
similarity index 100%
rename from tests-clar/resources/empty_bare.git/description
rename to tests/resources/empty_bare.git/description
diff --git a/tests-clar/resources/empty_bare.git/info/exclude b/tests/resources/empty_bare.git/info/exclude
similarity index 100%
rename from tests-clar/resources/empty_bare.git/info/exclude
rename to tests/resources/empty_bare.git/info/exclude
diff --git a/tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_bare.git/objects/info/dummy-marker.txt
rename to tests/resources/empty_bare.git/objects/info/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_bare.git/objects/pack/dummy-marker.txt
rename to tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_bare.git/refs/heads/dummy-marker.txt
rename to tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
diff --git a/tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_bare.git/refs/tags/dummy-marker.txt
rename to tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/HEAD b/tests/resources/empty_standard_repo/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/HEAD
rename to tests/resources/empty_standard_repo/.gitted/HEAD
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/config b/tests/resources/empty_standard_repo/.gitted/config
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/config
rename to tests/resources/empty_standard_repo/.gitted/config
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/description b/tests/resources/empty_standard_repo/.gitted/description
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/description
rename to tests/resources/empty_standard_repo/.gitted/description
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/info/exclude b/tests/resources/empty_standard_repo/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/info/exclude
rename to tests/resources/empty_standard_repo/.gitted/info/exclude
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
rename to tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
rename to tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
rename to tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
diff --git a/tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
similarity index 100%
rename from tests-clar/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
rename to tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
diff --git a/tests-clar/resources/filemodes/.gitted/HEAD b/tests/resources/filemodes/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/HEAD
rename to tests/resources/filemodes/.gitted/HEAD
diff --git a/tests-clar/resources/filemodes/.gitted/config b/tests/resources/filemodes/.gitted/config
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/config
rename to tests/resources/filemodes/.gitted/config
diff --git a/tests-clar/resources/filemodes/.gitted/description b/tests/resources/filemodes/.gitted/description
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/description
rename to tests/resources/filemodes/.gitted/description
diff --git a/tests-clar/resources/filemodes/.gitted/index b/tests/resources/filemodes/.gitted/index
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/index
rename to tests/resources/filemodes/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/info/exclude b/tests/resources/filemodes/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/info/exclude
rename to tests/resources/filemodes/.gitted/info/exclude
diff --git a/tests-clar/resources/filemodes/.gitted/logs/HEAD b/tests/resources/filemodes/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/logs/HEAD
rename to tests/resources/filemodes/.gitted/logs/HEAD
diff --git a/tests-clar/resources/filemodes/.gitted/logs/refs/heads/master b/tests/resources/filemodes/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/logs/refs/heads/master
rename to tests/resources/filemodes/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
rename to tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1 b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
rename to tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182 b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
rename to tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
Binary files differ
diff --git a/tests-clar/resources/filemodes/.gitted/refs/heads/master b/tests/resources/filemodes/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/filemodes/.gitted/refs/heads/master
rename to tests/resources/filemodes/.gitted/refs/heads/master
diff --git a/tests-clar/resources/filemodes/exec_off b/tests/resources/filemodes/exec_off
similarity index 100%
rename from tests-clar/resources/filemodes/exec_off
rename to tests/resources/filemodes/exec_off
diff --git a/tests-clar/resources/filemodes/exec_off2on_staged b/tests/resources/filemodes/exec_off2on_staged
similarity index 100%
rename from tests-clar/resources/filemodes/exec_off2on_staged
rename to tests/resources/filemodes/exec_off2on_staged
diff --git a/tests-clar/resources/filemodes/exec_off2on_workdir b/tests/resources/filemodes/exec_off2on_workdir
similarity index 100%
rename from tests-clar/resources/filemodes/exec_off2on_workdir
rename to tests/resources/filemodes/exec_off2on_workdir
diff --git a/tests-clar/resources/filemodes/exec_off_untracked b/tests/resources/filemodes/exec_off_untracked
similarity index 100%
rename from tests-clar/resources/filemodes/exec_off_untracked
rename to tests/resources/filemodes/exec_off_untracked
diff --git a/tests-clar/resources/filemodes/exec_on b/tests/resources/filemodes/exec_on
similarity index 100%
rename from tests-clar/resources/filemodes/exec_on
rename to tests/resources/filemodes/exec_on
diff --git a/tests-clar/resources/filemodes/exec_on2off_staged b/tests/resources/filemodes/exec_on2off_staged
similarity index 100%
rename from tests-clar/resources/filemodes/exec_on2off_staged
rename to tests/resources/filemodes/exec_on2off_staged
diff --git a/tests-clar/resources/filemodes/exec_on2off_workdir b/tests/resources/filemodes/exec_on2off_workdir
similarity index 100%
rename from tests-clar/resources/filemodes/exec_on2off_workdir
rename to tests/resources/filemodes/exec_on2off_workdir
diff --git a/tests-clar/resources/filemodes/exec_on_untracked b/tests/resources/filemodes/exec_on_untracked
similarity index 100%
rename from tests-clar/resources/filemodes/exec_on_untracked
rename to tests/resources/filemodes/exec_on_untracked
diff --git a/tests-clar/resources/gitgit.index b/tests/resources/gitgit.index
similarity index 100%
rename from tests-clar/resources/gitgit.index
rename to tests/resources/gitgit.index
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/HEAD b/tests/resources/icase/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/icase/.gitted/HEAD
rename to tests/resources/icase/.gitted/HEAD
diff --git a/tests-clar/resources/icase/.gitted/config b/tests/resources/icase/.gitted/config
similarity index 100%
rename from tests-clar/resources/icase/.gitted/config
rename to tests/resources/icase/.gitted/config
diff --git a/tests-clar/resources/icase/.gitted/description b/tests/resources/icase/.gitted/description
similarity index 100%
rename from tests-clar/resources/icase/.gitted/description
rename to tests/resources/icase/.gitted/description
diff --git a/tests-clar/resources/icase/.gitted/index b/tests/resources/icase/.gitted/index
similarity index 100%
rename from tests-clar/resources/icase/.gitted/index
rename to tests/resources/icase/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/info/exclude b/tests/resources/icase/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/icase/.gitted/info/exclude
rename to tests/resources/icase/.gitted/info/exclude
diff --git a/tests-clar/resources/icase/.gitted/logs/HEAD b/tests/resources/icase/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/icase/.gitted/logs/HEAD
rename to tests/resources/icase/.gitted/logs/HEAD
diff --git a/tests-clar/resources/icase/.gitted/logs/refs/heads/master b/tests/resources/icase/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/icase/.gitted/logs/refs/heads/master
rename to tests/resources/icase/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
similarity index 100%
rename from tests-clar/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
rename to tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
similarity index 100%
rename from tests-clar/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
rename to tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
similarity index 100%
rename from tests-clar/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
rename to tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
similarity index 100%
rename from tests-clar/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
rename to tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
similarity index 100%
rename from tests-clar/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
rename to tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
Binary files differ
diff --git a/tests-clar/resources/icase/.gitted/refs/heads/master b/tests/resources/icase/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/icase/.gitted/refs/heads/master
rename to tests/resources/icase/.gitted/refs/heads/master
diff --git a/tests-clar/resources/icase/B b/tests/resources/icase/B
similarity index 100%
rename from tests-clar/resources/icase/B
rename to tests/resources/icase/B
diff --git a/tests-clar/resources/icase/D b/tests/resources/icase/D
similarity index 100%
rename from tests-clar/resources/icase/D
rename to tests/resources/icase/D
diff --git a/tests-clar/resources/icase/F b/tests/resources/icase/F
similarity index 100%
rename from tests-clar/resources/icase/F
rename to tests/resources/icase/F
diff --git a/tests-clar/resources/icase/H b/tests/resources/icase/H
similarity index 100%
rename from tests-clar/resources/icase/H
rename to tests/resources/icase/H
diff --git a/tests-clar/resources/icase/J b/tests/resources/icase/J
similarity index 100%
rename from tests-clar/resources/icase/J
rename to tests/resources/icase/J
diff --git a/tests-clar/resources/icase/L/1 b/tests/resources/icase/L/1
similarity index 100%
rename from tests-clar/resources/icase/L/1
rename to tests/resources/icase/L/1
diff --git a/tests-clar/resources/icase/L/B b/tests/resources/icase/L/B
similarity index 100%
rename from tests-clar/resources/icase/L/B
rename to tests/resources/icase/L/B
diff --git a/tests-clar/resources/icase/L/D b/tests/resources/icase/L/D
similarity index 100%
rename from tests-clar/resources/icase/L/D
rename to tests/resources/icase/L/D
diff --git a/tests-clar/resources/icase/L/a b/tests/resources/icase/L/a
similarity index 100%
rename from tests-clar/resources/icase/L/a
rename to tests/resources/icase/L/a
diff --git a/tests-clar/resources/icase/L/c b/tests/resources/icase/L/c
similarity index 100%
rename from tests-clar/resources/icase/L/c
rename to tests/resources/icase/L/c
diff --git a/tests-clar/resources/icase/a b/tests/resources/icase/a
similarity index 100%
rename from tests-clar/resources/icase/a
rename to tests/resources/icase/a
diff --git a/tests-clar/resources/icase/c b/tests/resources/icase/c
similarity index 100%
rename from tests-clar/resources/icase/c
rename to tests/resources/icase/c
diff --git a/tests-clar/resources/icase/e b/tests/resources/icase/e
similarity index 100%
rename from tests-clar/resources/icase/e
rename to tests/resources/icase/e
diff --git a/tests-clar/resources/icase/g b/tests/resources/icase/g
similarity index 100%
rename from tests-clar/resources/icase/g
rename to tests/resources/icase/g
diff --git a/tests-clar/resources/icase/i b/tests/resources/icase/i
similarity index 100%
rename from tests-clar/resources/icase/i
rename to tests/resources/icase/i
diff --git a/tests-clar/resources/icase/k/1 b/tests/resources/icase/k/1
similarity index 100%
rename from tests-clar/resources/icase/k/1
rename to tests/resources/icase/k/1
diff --git a/tests-clar/resources/icase/k/B b/tests/resources/icase/k/B
similarity index 100%
rename from tests-clar/resources/icase/k/B
rename to tests/resources/icase/k/B
diff --git a/tests-clar/resources/icase/k/D b/tests/resources/icase/k/D
similarity index 100%
rename from tests-clar/resources/icase/k/D
rename to tests/resources/icase/k/D
diff --git a/tests-clar/resources/icase/k/a b/tests/resources/icase/k/a
similarity index 100%
rename from tests-clar/resources/icase/k/a
rename to tests/resources/icase/k/a
diff --git a/tests-clar/resources/icase/k/c b/tests/resources/icase/k/c
similarity index 100%
rename from tests-clar/resources/icase/k/c
rename to tests/resources/icase/k/c
diff --git a/tests-clar/resources/issue_1397/.gitted/HEAD b/tests/resources/issue_1397/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/HEAD
rename to tests/resources/issue_1397/.gitted/HEAD
diff --git a/tests-clar/resources/issue_1397/.gitted/config b/tests/resources/issue_1397/.gitted/config
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/config
rename to tests/resources/issue_1397/.gitted/config
diff --git a/tests-clar/resources/issue_1397/.gitted/index b/tests/resources/issue_1397/.gitted/index
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/index
rename to tests/resources/issue_1397/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5 b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
rename to tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318 b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
rename to tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283 b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
rename to tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
rename to tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
rename to tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741 b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
rename to tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
Binary files differ
diff --git a/tests-clar/resources/issue_1397/.gitted/refs/heads/master b/tests/resources/issue_1397/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/issue_1397/.gitted/refs/heads/master
rename to tests/resources/issue_1397/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_1397/crlf_file.txt b/tests/resources/issue_1397/crlf_file.txt
similarity index 100%
rename from tests-clar/resources/issue_1397/crlf_file.txt
rename to tests/resources/issue_1397/crlf_file.txt
diff --git a/tests-clar/resources/issue_1397/some_other_crlf_file.txt b/tests/resources/issue_1397/some_other_crlf_file.txt
similarity index 100%
rename from tests-clar/resources/issue_1397/some_other_crlf_file.txt
rename to tests/resources/issue_1397/some_other_crlf_file.txt
diff --git a/tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/COMMIT_EDITMSG
rename to tests/resources/issue_592/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/issue_592/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/HEAD
rename to tests/resources/issue_592/.gitted/HEAD
diff --git a/tests-clar/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/config
rename to tests/resources/issue_592/.gitted/config
diff --git a/tests-clar/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/index
rename to tests/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/info/exclude
rename to tests/resources/issue_592/.gitted/info/exclude
diff --git a/tests-clar/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/logs/HEAD
rename to tests/resources/issue_592/.gitted/logs/HEAD
diff --git a/tests-clar/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/logs/refs/heads/master
rename to tests/resources/issue_592/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
rename to tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
rename to tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
rename to tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
rename to tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
rename to tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
rename to tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
rename to tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/issue_592/.gitted/refs/heads/master
rename to tests/resources/issue_592/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt
similarity index 100%
rename from tests-clar/resources/issue_592/a.txt
rename to tests/resources/issue_592/a.txt
diff --git a/tests-clar/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt
similarity index 100%
rename from tests-clar/resources/issue_592/c/a.txt
rename to tests/resources/issue_592/c/a.txt
diff --git a/tests-clar/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt
similarity index 100%
rename from tests-clar/resources/issue_592/l.txt
rename to tests/resources/issue_592/l.txt
diff --git a/tests-clar/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt
similarity index 100%
rename from tests-clar/resources/issue_592/t/a.txt
rename to tests/resources/issue_592/t/a.txt
diff --git a/tests-clar/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt
similarity index 100%
rename from tests-clar/resources/issue_592/t/b.txt
rename to tests/resources/issue_592/t/b.txt
diff --git a/tests-clar/resources/issue_592b/.gitted/HEAD b/tests/resources/issue_592b/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/HEAD
rename to tests/resources/issue_592b/.gitted/HEAD
diff --git a/tests-clar/resources/issue_592b/.gitted/config b/tests/resources/issue_592b/.gitted/config
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/config
rename to tests/resources/issue_592b/.gitted/config
diff --git a/tests-clar/resources/issue_592b/.gitted/description b/tests/resources/issue_592b/.gitted/description
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/description
rename to tests/resources/issue_592b/.gitted/description
diff --git a/tests-clar/resources/issue_592b/.gitted/index b/tests/resources/issue_592b/.gitted/index
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/index
rename to tests/resources/issue_592b/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/info/exclude b/tests/resources/issue_592b/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/info/exclude
rename to tests/resources/issue_592b/.gitted/info/exclude
diff --git a/tests-clar/resources/issue_592b/.gitted/logs/HEAD b/tests/resources/issue_592b/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/logs/HEAD
rename to tests/resources/issue_592b/.gitted/logs/HEAD
diff --git a/tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/logs/refs/heads/master
rename to tests/resources/issue_592b/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
rename to tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
rename to tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
rename to tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
rename to tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
rename to tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
rename to tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
rename to tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
Binary files differ
diff --git a/tests-clar/resources/issue_592b/.gitted/refs/heads/master b/tests/resources/issue_592b/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/issue_592b/.gitted/refs/heads/master
rename to tests/resources/issue_592b/.gitted/refs/heads/master
diff --git a/tests-clar/resources/issue_592b/gitignore b/tests/resources/issue_592b/gitignore
similarity index 100%
rename from tests-clar/resources/issue_592b/gitignore
rename to tests/resources/issue_592b/gitignore
diff --git a/tests-clar/resources/issue_592b/ignored/contained/ignored3.txt b/tests/resources/issue_592b/ignored/contained/ignored3.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/ignored/contained/ignored3.txt
rename to tests/resources/issue_592b/ignored/contained/ignored3.txt
diff --git a/tests-clar/resources/issue_592b/ignored/contained/tracked3.txt b/tests/resources/issue_592b/ignored/contained/tracked3.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/ignored/contained/tracked3.txt
rename to tests/resources/issue_592b/ignored/contained/tracked3.txt
diff --git a/tests-clar/resources/issue_592b/ignored/ignored2.txt b/tests/resources/issue_592b/ignored/ignored2.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/ignored/ignored2.txt
rename to tests/resources/issue_592b/ignored/ignored2.txt
diff --git a/tests-clar/resources/issue_592b/ignored/tracked2.txt b/tests/resources/issue_592b/ignored/tracked2.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/ignored/tracked2.txt
rename to tests/resources/issue_592b/ignored/tracked2.txt
diff --git a/tests-clar/resources/issue_592b/ignored1.txt b/tests/resources/issue_592b/ignored1.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/ignored1.txt
rename to tests/resources/issue_592b/ignored1.txt
diff --git a/tests-clar/resources/issue_592b/tracked1.txt b/tests/resources/issue_592b/tracked1.txt
similarity index 100%
rename from tests-clar/resources/issue_592b/tracked1.txt
rename to tests/resources/issue_592b/tracked1.txt
diff --git a/tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/COMMIT_EDITMSG
rename to tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/merge-resolve/.gitted/HEAD b/tests/resources/merge-resolve/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/HEAD
rename to tests/resources/merge-resolve/.gitted/HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/ORIG_HEAD
rename to tests/resources/merge-resolve/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/config b/tests/resources/merge-resolve/.gitted/config
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/config
rename to tests/resources/merge-resolve/.gitted/config
diff --git a/tests-clar/resources/merge-resolve/.gitted/description b/tests/resources/merge-resolve/.gitted/description
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/description
rename to tests/resources/merge-resolve/.gitted/description
diff --git a/tests-clar/resources/merge-resolve/.gitted/index b/tests/resources/merge-resolve/.gitted/index
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/index
rename to tests/resources/merge-resolve/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/HEAD b/tests/resources/merge-resolve/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/HEAD
rename to tests/resources/merge-resolve/.gitted/logs/HEAD
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/master
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo1
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo2
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo3
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo4
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo5
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/octo6
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames1
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/renames2
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
rename to tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
rename to tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
rename to tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
rename to tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
rename to tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
rename to tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
rename to tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
rename to tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
rename to tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
rename to tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
rename to tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
rename to tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
rename to tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
rename to tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
rename to tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
rename to tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
rename to tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
rename to tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
rename to tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
rename to tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
rename to tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
rename to tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
rename to tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
rename to tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
rename to tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
rename to tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
rename to tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
rename to tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
rename to tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
rename to tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
rename to tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
rename to tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
rename to tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
rename to tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
rename to tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
rename to tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
new file mode 100644
index 0000000..6039df0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
rename to tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
rename to tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
rename to tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
rename to tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
rename to tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
rename to tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
rename to tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
rename to tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
rename to tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
rename to tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
rename to tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
rename to tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
rename to tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
rename to tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
rename to tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
rename to tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
rename to tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
rename to tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
rename to tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
rename to tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
rename to tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
rename to tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
rename to tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
rename to tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
rename to tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
rename to tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
rename to tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
rename to tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
rename to tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
rename to tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
rename to tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
rename to tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
rename to tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
rename to tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
rename to tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
rename to tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
rename to tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
rename to tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
rename to tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
rename to tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
rename to tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
rename to tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
rename to tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
rename to tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
rename to tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
rename to tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
rename to tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
rename to tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
rename to tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
rename to tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
rename to tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
rename to tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
rename to tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
rename to tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
rename to tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
rename to tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
rename to tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
rename to tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
rename to tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
rename to tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
rename to tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
rename to tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
rename to tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
rename to tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
rename to tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
rename to tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
rename to tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
rename to tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
rename to tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
rename to tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
rename to tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
rename to tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
rename to tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
rename to tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
rename to tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
rename to tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
rename to tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
rename to tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
rename to tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
rename to tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
rename to tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
rename to tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
rename to tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
rename to tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
rename to tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
rename to tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
rename to tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
rename to tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
rename to tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
rename to tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
rename to tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
rename to tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
rename to tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
rename to tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
rename to tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
rename to tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
rename to tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
rename to tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
rename to tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
rename to tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
rename to tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
rename to tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
rename to tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
rename to tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
rename to tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2 b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
new file mode 100644
index 0000000..4886e49
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
rename to tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
rename to tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
rename to tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
rename to tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
rename to tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
rename to tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
rename to tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
rename to tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
rename to tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
rename to tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
rename to tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
rename to tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
rename to tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
rename to tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
rename to tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
rename to tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
rename to tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
rename to tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
rename to tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
rename to tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
rename to tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
rename to tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93 b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
new file mode 100644
index 0000000..a90ee08
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
rename to tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
rename to tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
rename to tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
rename to tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
rename to tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
rename to tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
rename to tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
rename to tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
rename to tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
rename to tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
rename to tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
rename to tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
rename to tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
rename to tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
rename to tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
rename to tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
rename to tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
rename to tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
rename to tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
rename to tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
rename to tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
rename to tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
rename to tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
rename to tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
rename to tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
rename to tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
rename to tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
rename to tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
rename to tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
rename to tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
rename to tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
rename to tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
rename to tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
rename to tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
rename to tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
rename to tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
rename to tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
rename to tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
rename to tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
rename to tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
rename to tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
rename to tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
rename to tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
rename to tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
rename to tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
rename to tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
rename to tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
rename to tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
rename to tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
rename to tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
rename to tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
rename to tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
rename to tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
rename to tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
rename to tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
rename to tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
rename to tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
rename to tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
rename to tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
rename to tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
rename to tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
rename to tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
rename to tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
rename to tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
rename to tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
rename to tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
rename to tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
rename to tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
rename to tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
rename to tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
rename to tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
rename to tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
rename to tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
rename to tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
rename to tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
rename to tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
rename to tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
rename to tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
rename to tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
rename to tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
rename to tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
rename to tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
rename to tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
rename to tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
rename to tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
rename to tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
rename to tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
rename to tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
rename to tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
rename to tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
rename to tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
rename to tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
rename to tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
rename to tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
rename to tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
rename to tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
rename to tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
rename to tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
rename to tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
rename to tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
rename to tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
rename to tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
rename to tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
rename to tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
rename to tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
rename to tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
rename to tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
rename to tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
rename to tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
rename to tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
rename to tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
rename to tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
rename to tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
rename to tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
rename to tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
rename to tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
rename to tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
rename to tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
rename to tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
rename to tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
rename to tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
rename to tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
rename to tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
rename to tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
rename to tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
rename to tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
rename to tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
rename to tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
rename to tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
rename to tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
rename to tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
rename to tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
rename to tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
rename to tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
rename to tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
rename to tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
rename to tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
rename to tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
rename to tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
rename to tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
rename to tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
rename to tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
rename to tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
rename to tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
Binary files differ
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/branch b/tests/resources/merge-resolve/.gitted/refs/heads/branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_ancestor
rename to tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side1
rename to tests/resources/merge-resolve/.gitted/refs/heads/df_side1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/df_side2
rename to tests/resources/merge-resolve/.gitted/refs/heads/df_side2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/ff_branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/master b/tests/resources/merge-resolve/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/master
rename to tests/resources/merge-resolve/.gitted/refs/heads/master
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo1
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo2
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo3
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo3
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo4
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo4
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo5
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo5
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/octo6
rename to tests/resources/merge-resolve/.gitted/refs/heads/octo6
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
rename to tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
rename to tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
rename to tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/renames1
rename to tests/resources/merge-resolve/.gitted/refs/heads/renames1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/renames2
rename to tests/resources/merge-resolve/.gitted/refs/heads/renames2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
rename to tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
diff --git a/tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
similarity index 100%
rename from tests-clar/resources/merge-resolve/.gitted/refs/heads/unrelated
rename to tests/resources/merge-resolve/.gitted/refs/heads/unrelated
diff --git a/tests-clar/resources/merge-resolve/added-in-master.txt b/tests/resources/merge-resolve/added-in-master.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/added-in-master.txt
rename to tests/resources/merge-resolve/added-in-master.txt
diff --git a/tests-clar/resources/merge-resolve/automergeable.txt b/tests/resources/merge-resolve/automergeable.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/automergeable.txt
rename to tests/resources/merge-resolve/automergeable.txt
diff --git a/tests-clar/resources/merge-resolve/changed-in-branch.txt b/tests/resources/merge-resolve/changed-in-branch.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/changed-in-branch.txt
rename to tests/resources/merge-resolve/changed-in-branch.txt
diff --git a/tests-clar/resources/merge-resolve/changed-in-master.txt b/tests/resources/merge-resolve/changed-in-master.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/changed-in-master.txt
rename to tests/resources/merge-resolve/changed-in-master.txt
diff --git a/tests-clar/resources/merge-resolve/conflicting.txt b/tests/resources/merge-resolve/conflicting.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/conflicting.txt
rename to tests/resources/merge-resolve/conflicting.txt
diff --git a/tests-clar/resources/merge-resolve/removed-in-branch.txt b/tests/resources/merge-resolve/removed-in-branch.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/removed-in-branch.txt
rename to tests/resources/merge-resolve/removed-in-branch.txt
diff --git a/tests-clar/resources/merge-resolve/unchanged.txt b/tests/resources/merge-resolve/unchanged.txt
similarity index 100%
rename from tests-clar/resources/merge-resolve/unchanged.txt
rename to tests/resources/merge-resolve/unchanged.txt
diff --git a/tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/COMMIT_EDITMSG
rename to tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/mergedrepo/.gitted/HEAD b/tests/resources/mergedrepo/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/HEAD
rename to tests/resources/mergedrepo/.gitted/HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/MERGE_HEAD
rename to tests/resources/mergedrepo/.gitted/MERGE_HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MODE b/tests/resources/mergedrepo/.gitted/MERGE_MODE
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/MERGE_MODE
rename to tests/resources/mergedrepo/.gitted/MERGE_MODE
diff --git a/tests-clar/resources/mergedrepo/.gitted/MERGE_MSG b/tests/resources/mergedrepo/.gitted/MERGE_MSG
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/MERGE_MSG
rename to tests/resources/mergedrepo/.gitted/MERGE_MSG
diff --git a/tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/ORIG_HEAD
rename to tests/resources/mergedrepo/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/config b/tests/resources/mergedrepo/.gitted/config
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/config
rename to tests/resources/mergedrepo/.gitted/config
diff --git a/tests-clar/resources/mergedrepo/.gitted/description b/tests/resources/mergedrepo/.gitted/description
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/description
rename to tests/resources/mergedrepo/.gitted/description
diff --git a/tests-clar/resources/mergedrepo/.gitted/index b/tests/resources/mergedrepo/.gitted/index
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/index
rename to tests/resources/mergedrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/info/exclude b/tests/resources/mergedrepo/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/info/exclude
rename to tests/resources/mergedrepo/.gitted/info/exclude
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/HEAD b/tests/resources/mergedrepo/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/logs/HEAD
rename to tests/resources/mergedrepo/.gitted/logs/HEAD
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/branch
rename to tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
diff --git a/tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/logs/refs/heads/master
rename to tests/resources/mergedrepo/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
rename to tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
rename to tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
rename to tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
rename to tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
rename to tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
rename to tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
rename to tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
rename to tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
rename to tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
rename to tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
rename to tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
rename to tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
rename to tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
rename to tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
rename to tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
rename to tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
rename to tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
rename to tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
rename to tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
rename to tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
rename to tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
rename to tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
rename to tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
rename to tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
rename to tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
rename to tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
rename to tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
rename to tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
rename to tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
rename to tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
Binary files differ
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/branch b/tests/resources/mergedrepo/.gitted/refs/heads/branch
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/refs/heads/branch
rename to tests/resources/mergedrepo/.gitted/refs/heads/branch
diff --git a/tests-clar/resources/mergedrepo/.gitted/refs/heads/master b/tests/resources/mergedrepo/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/mergedrepo/.gitted/refs/heads/master
rename to tests/resources/mergedrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/mergedrepo/conflicts-one.txt b/tests/resources/mergedrepo/conflicts-one.txt
similarity index 100%
rename from tests-clar/resources/mergedrepo/conflicts-one.txt
rename to tests/resources/mergedrepo/conflicts-one.txt
diff --git a/tests-clar/resources/mergedrepo/conflicts-two.txt b/tests/resources/mergedrepo/conflicts-two.txt
similarity index 100%
rename from tests-clar/resources/mergedrepo/conflicts-two.txt
rename to tests/resources/mergedrepo/conflicts-two.txt
diff --git a/tests-clar/resources/mergedrepo/one.txt b/tests/resources/mergedrepo/one.txt
similarity index 100%
rename from tests-clar/resources/mergedrepo/one.txt
rename to tests/resources/mergedrepo/one.txt
diff --git a/tests-clar/resources/mergedrepo/two.txt b/tests/resources/mergedrepo/two.txt
similarity index 100%
rename from tests-clar/resources/mergedrepo/two.txt
rename to tests/resources/mergedrepo/two.txt
diff --git a/tests-clar/resources/partial-testrepo/.gitted/HEAD b/tests/resources/partial-testrepo/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/HEAD
rename to tests/resources/partial-testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/partial-testrepo/.gitted/config b/tests/resources/partial-testrepo/.gitted/config
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/config
rename to tests/resources/partial-testrepo/.gitted/config
diff --git a/tests-clar/resources/partial-testrepo/.gitted/index b/tests/resources/partial-testrepo/.gitted/index
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/index
rename to tests/resources/partial-testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
rename to tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
rename to tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
rename to tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
rename to tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
rename to tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
rename to tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
rename to tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/partial-testrepo/.gitted/objects/pack/.gitkeep b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
rename to tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
diff --git a/tests-clar/resources/partial-testrepo/.gitted/refs/heads/dir b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
similarity index 100%
rename from tests-clar/resources/partial-testrepo/.gitted/refs/heads/dir
rename to tests/resources/partial-testrepo/.gitted/refs/heads/dir
diff --git a/tests-clar/resources/peeled.git/HEAD b/tests/resources/peeled.git/HEAD
similarity index 100%
rename from tests-clar/resources/peeled.git/HEAD
rename to tests/resources/peeled.git/HEAD
diff --git a/tests-clar/resources/peeled.git/config b/tests/resources/peeled.git/config
similarity index 100%
rename from tests-clar/resources/peeled.git/config
rename to tests/resources/peeled.git/config
diff --git a/tests-clar/resources/peeled.git/objects/info/packs b/tests/resources/peeled.git/objects/info/packs
similarity index 100%
rename from tests-clar/resources/peeled.git/objects/info/packs
rename to tests/resources/peeled.git/objects/info/packs
diff --git a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
similarity index 100%
rename from tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
rename to tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
Binary files differ
diff --git a/tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
similarity index 100%
rename from tests-clar/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
rename to tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
Binary files differ
diff --git a/tests-clar/resources/peeled.git/packed-refs b/tests/resources/peeled.git/packed-refs
similarity index 100%
rename from tests-clar/resources/peeled.git/packed-refs
rename to tests/resources/peeled.git/packed-refs
diff --git a/tests-clar/resources/peeled.git/refs/heads/master b/tests/resources/peeled.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/peeled.git/refs/heads/master
rename to tests/resources/peeled.git/refs/heads/master
diff --git a/tests-clar/resources/push.sh b/tests/resources/push.sh
similarity index 100%
rename from tests-clar/resources/push.sh
rename to tests/resources/push.sh
diff --git a/tests-clar/resources/push_src/.gitted/COMMIT_EDITMSG b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/COMMIT_EDITMSG
rename to tests/resources/push_src/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/push_src/.gitted/HEAD b/tests/resources/push_src/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/HEAD
rename to tests/resources/push_src/.gitted/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/ORIG_HEAD b/tests/resources/push_src/.gitted/ORIG_HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/ORIG_HEAD
rename to tests/resources/push_src/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/push_src/.gitted/config b/tests/resources/push_src/.gitted/config
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/config
rename to tests/resources/push_src/.gitted/config
diff --git a/tests-clar/resources/push_src/.gitted/description b/tests/resources/push_src/.gitted/description
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/description
rename to tests/resources/push_src/.gitted/description
diff --git a/tests-clar/resources/push_src/.gitted/index b/tests/resources/push_src/.gitted/index
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/index
rename to tests/resources/push_src/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/info/exclude b/tests/resources/push_src/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/info/exclude
rename to tests/resources/push_src/.gitted/info/exclude
diff --git a/tests-clar/resources/push_src/.gitted/logs/HEAD b/tests/resources/push_src/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/HEAD
rename to tests/resources/push_src/.gitted/logs/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b1 b/tests/resources/push_src/.gitted/logs/refs/heads/b1
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/b1
rename to tests/resources/push_src/.gitted/logs/refs/heads/b1
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b2 b/tests/resources/push_src/.gitted/logs/refs/heads/b2
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/b2
rename to tests/resources/push_src/.gitted/logs/refs/heads/b2
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b3 b/tests/resources/push_src/.gitted/logs/refs/heads/b3
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/b3
rename to tests/resources/push_src/.gitted/logs/refs/heads/b3
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b4 b/tests/resources/push_src/.gitted/logs/refs/heads/b4
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/b4
rename to tests/resources/push_src/.gitted/logs/refs/heads/b4
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/b5 b/tests/resources/push_src/.gitted/logs/refs/heads/b5
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/b5
rename to tests/resources/push_src/.gitted/logs/refs/heads/b5
diff --git a/tests-clar/resources/push_src/.gitted/logs/refs/heads/master b/tests/resources/push_src/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/logs/refs/heads/master
rename to tests/resources/push_src/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/HEAD b/tests/resources/push_src/.gitted/modules/submodule/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/HEAD
rename to tests/resources/push_src/.gitted/modules/submodule/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/config b/tests/resources/push_src/.gitted/modules/submodule/config
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/config
rename to tests/resources/push_src/.gitted/modules/submodule/config
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/description b/tests/resources/push_src/.gitted/modules/submodule/description
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/description
rename to tests/resources/push_src/.gitted/modules/submodule/description
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/index b/tests/resources/push_src/.gitted/modules/submodule/index
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/index
rename to tests/resources/push_src/.gitted/modules/submodule/index
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/info/exclude b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/info/exclude
rename to tests/resources/push_src/.gitted/modules/submodule/info/exclude
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/logs/HEAD
rename to tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
rename to tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
rename to tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
rename to tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
rename to tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
rename to tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
rename to tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
rename to tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
rename to tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
rename to tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
rename to tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
rename to tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
rename to tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
rename to tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
rename to tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
rename to tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
rename to tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
rename to tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
rename to tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
rename to tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
rename to tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
rename to tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
rename to tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
rename to tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
rename to tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
rename to tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
rename to tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
rename to tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
rename to tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
rename to tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
rename to tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
rename to tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/packed-refs b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/packed-refs
rename to tests/resources/push_src/.gitted/modules/submodule/packed-refs
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/refs/heads/master
rename to tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
diff --git a/tests-clar/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
rename to tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
rename to tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
diff --git a/tests-clar/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
rename to tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840 b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
rename to tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60 b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
rename to tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
new file mode 100644
index 0000000..0bc57f2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915 b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
rename to tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472 b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
rename to tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
rename to tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85 b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
rename to tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
rename to tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
rename to tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests-clar/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247 b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
rename to tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498 b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
rename to tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
rename to tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279 b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
rename to tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
rename to tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4 b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
rename to tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591 b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
rename to tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
new file mode 100644
index 0000000..b7b81d5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928 b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
rename to tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
rename to tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2 b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
rename to tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
diff --git a/tests-clar/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
rename to tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
diff --git a/tests-clar/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925 b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
rename to tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
Binary files differ
diff --git a/tests-clar/resources/push_src/.gitted/objects/pack/dummy b/tests/resources/push_src/.gitted/objects/pack/dummy
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/objects/pack/dummy
rename to tests/resources/push_src/.gitted/objects/pack/dummy
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b1 b/tests/resources/push_src/.gitted/refs/heads/b1
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b1
rename to tests/resources/push_src/.gitted/refs/heads/b1
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b2 b/tests/resources/push_src/.gitted/refs/heads/b2
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b2
rename to tests/resources/push_src/.gitted/refs/heads/b2
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b3 b/tests/resources/push_src/.gitted/refs/heads/b3
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b3
rename to tests/resources/push_src/.gitted/refs/heads/b3
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b4 b/tests/resources/push_src/.gitted/refs/heads/b4
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b4
rename to tests/resources/push_src/.gitted/refs/heads/b4
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b5 b/tests/resources/push_src/.gitted/refs/heads/b5
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b5
rename to tests/resources/push_src/.gitted/refs/heads/b5
diff --git a/tests-clar/resources/push_src/.gitted/refs/heads/b6 b/tests/resources/push_src/.gitted/refs/heads/b6
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/heads/b6
rename to tests/resources/push_src/.gitted/refs/heads/b6
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-blob b/tests/resources/push_src/.gitted/refs/tags/tag-blob
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/tags/tag-blob
rename to tests/resources/push_src/.gitted/refs/tags/tag-blob
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-commit b/tests/resources/push_src/.gitted/refs/tags/tag-commit
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/tags/tag-commit
rename to tests/resources/push_src/.gitted/refs/tags/tag-commit
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit-two b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
new file mode 100644
index 0000000..abb3650
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
@@ -0,0 +1 @@
+36f79b2846017d3761e0a02d0bccd573e0f90c57
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-lightweight b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/tags/tag-lightweight
rename to tests/resources/push_src/.gitted/refs/tags/tag-lightweight
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tag b/tests/resources/push_src/.gitted/refs/tags/tag-tag
new file mode 100644
index 0000000..d6cd748
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tag
@@ -0,0 +1 @@
+eea4f2705eeec2db3813f2430829afce99cd00b5
diff --git a/tests-clar/resources/push_src/.gitted/refs/tags/tag-tree b/tests/resources/push_src/.gitted/refs/tags/tag-tree
similarity index 100%
rename from tests-clar/resources/push_src/.gitted/refs/tags/tag-tree
rename to tests/resources/push_src/.gitted/refs/tags/tag-tree
diff --git a/tests-clar/resources/push_src/a.txt b/tests/resources/push_src/a.txt
similarity index 100%
rename from tests-clar/resources/push_src/a.txt
rename to tests/resources/push_src/a.txt
diff --git a/tests-clar/resources/push_src/fold/b.txt b/tests/resources/push_src/fold/b.txt
similarity index 100%
rename from tests-clar/resources/push_src/fold/b.txt
rename to tests/resources/push_src/fold/b.txt
diff --git a/tests-clar/resources/push_src/foldb.txt b/tests/resources/push_src/foldb.txt
similarity index 100%
rename from tests-clar/resources/push_src/foldb.txt
rename to tests/resources/push_src/foldb.txt
diff --git a/tests-clar/resources/push_src/gitmodules b/tests/resources/push_src/gitmodules
similarity index 100%
rename from tests-clar/resources/push_src/gitmodules
rename to tests/resources/push_src/gitmodules
diff --git a/tests-clar/resources/push_src/submodule/.gitted b/tests/resources/push_src/submodule/.gitted
similarity index 100%
rename from tests-clar/resources/push_src/submodule/.gitted
rename to tests/resources/push_src/submodule/.gitted
diff --git a/tests-clar/resources/push_src/submodule/README b/tests/resources/push_src/submodule/README
similarity index 100%
rename from tests-clar/resources/push_src/submodule/README
rename to tests/resources/push_src/submodule/README
diff --git a/tests-clar/resources/push_src/submodule/branch_file.txt b/tests/resources/push_src/submodule/branch_file.txt
similarity index 100%
rename from tests-clar/resources/push_src/submodule/branch_file.txt
rename to tests/resources/push_src/submodule/branch_file.txt
diff --git a/tests-clar/resources/push_src/submodule/new.txt b/tests/resources/push_src/submodule/new.txt
similarity index 100%
rename from tests-clar/resources/push_src/submodule/new.txt
rename to tests/resources/push_src/submodule/new.txt
diff --git a/tests-clar/resources/renames/.gitted/HEAD b/tests/resources/renames/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/renames/.gitted/HEAD
rename to tests/resources/renames/.gitted/HEAD
diff --git a/tests-clar/resources/renames/.gitted/config b/tests/resources/renames/.gitted/config
similarity index 100%
rename from tests-clar/resources/renames/.gitted/config
rename to tests/resources/renames/.gitted/config
diff --git a/tests-clar/resources/renames/.gitted/description b/tests/resources/renames/.gitted/description
similarity index 100%
rename from tests-clar/resources/renames/.gitted/description
rename to tests/resources/renames/.gitted/description
diff --git a/tests-clar/resources/renames/.gitted/index b/tests/resources/renames/.gitted/index
similarity index 100%
rename from tests-clar/resources/renames/.gitted/index
rename to tests/resources/renames/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/info/exclude b/tests/resources/renames/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/renames/.gitted/info/exclude
rename to tests/resources/renames/.gitted/info/exclude
diff --git a/tests-clar/resources/renames/.gitted/logs/HEAD b/tests/resources/renames/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/renames/.gitted/logs/HEAD
rename to tests/resources/renames/.gitted/logs/HEAD
diff --git a/tests-clar/resources/renames/.gitted/logs/refs/heads/master b/tests/resources/renames/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/renames/.gitted/logs/refs/heads/master
rename to tests/resources/renames/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7 b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
rename to tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
rename to tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
rename to tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
diff --git a/tests-clar/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084 b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
rename to tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
rename to tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2 b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
rename to tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
rename to tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
rename to tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2 b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
rename to tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512 b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
rename to tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
rename to tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
rename to tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953 b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
rename to tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
rename to tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
diff --git a/tests-clar/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109 b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
rename to tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745 b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
rename to tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
rename to tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
rename to tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935 b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
rename to tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113 b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
rename to tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
rename to tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323 b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
rename to tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
rename to tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
rename to tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5 b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
similarity index 100%
rename from tests-clar/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
rename to tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
Binary files differ
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/master b/tests/resources/renames/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/renames/.gitted/refs/heads/master
rename to tests/resources/renames/.gitted/refs/heads/master
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar b/tests/resources/renames/.gitted/refs/heads/renames_similar
similarity index 100%
rename from tests-clar/resources/renames/.gitted/refs/heads/renames_similar
rename to tests/resources/renames/.gitted/refs/heads/renames_similar
diff --git a/tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
similarity index 100%
rename from tests-clar/resources/renames/.gitted/refs/heads/renames_similar_two
rename to tests/resources/renames/.gitted/refs/heads/renames_similar_two
diff --git a/tests-clar/resources/renames/ikeepsix.txt b/tests/resources/renames/ikeepsix.txt
similarity index 100%
rename from tests-clar/resources/renames/ikeepsix.txt
rename to tests/resources/renames/ikeepsix.txt
diff --git a/tests-clar/resources/renames/sixserving.txt b/tests/resources/renames/sixserving.txt
similarity index 100%
rename from tests-clar/resources/renames/sixserving.txt
rename to tests/resources/renames/sixserving.txt
diff --git a/tests-clar/resources/renames/songof7cities.txt b/tests/resources/renames/songof7cities.txt
similarity index 100%
rename from tests-clar/resources/renames/songof7cities.txt
rename to tests/resources/renames/songof7cities.txt
diff --git a/tests-clar/resources/renames/untimely.txt b/tests/resources/renames/untimely.txt
similarity index 100%
rename from tests-clar/resources/renames/untimely.txt
rename to tests/resources/renames/untimely.txt
diff --git a/tests-clar/resources/shallow.git/HEAD b/tests/resources/shallow.git/HEAD
similarity index 100%
rename from tests-clar/resources/shallow.git/HEAD
rename to tests/resources/shallow.git/HEAD
diff --git a/tests-clar/resources/shallow.git/config b/tests/resources/shallow.git/config
similarity index 100%
rename from tests-clar/resources/shallow.git/config
rename to tests/resources/shallow.git/config
diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
similarity index 100%
rename from tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
rename to tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
Binary files differ
diff --git a/tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
similarity index 100%
rename from tests-clar/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
rename to tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
Binary files differ
diff --git a/tests-clar/resources/shallow.git/packed-refs b/tests/resources/shallow.git/packed-refs
similarity index 100%
rename from tests-clar/resources/shallow.git/packed-refs
rename to tests/resources/shallow.git/packed-refs
diff --git a/tests-clar/resources/shallow.git/refs/.gitkeep b/tests/resources/shallow.git/refs/.gitkeep
similarity index 100%
rename from tests-clar/resources/shallow.git/refs/.gitkeep
rename to tests/resources/shallow.git/refs/.gitkeep
diff --git a/tests-clar/resources/shallow.git/shallow b/tests/resources/shallow.git/shallow
similarity index 100%
rename from tests-clar/resources/shallow.git/shallow
rename to tests/resources/shallow.git/shallow
diff --git a/tests-clar/resources/short_tag.git/HEAD b/tests/resources/short_tag.git/HEAD
similarity index 100%
rename from tests-clar/resources/short_tag.git/HEAD
rename to tests/resources/short_tag.git/HEAD
diff --git a/tests-clar/resources/short_tag.git/config b/tests/resources/short_tag.git/config
similarity index 100%
rename from tests-clar/resources/short_tag.git/config
rename to tests/resources/short_tag.git/config
diff --git a/tests-clar/resources/short_tag.git/index b/tests/resources/short_tag.git/index
similarity index 100%
rename from tests-clar/resources/short_tag.git/index
rename to tests/resources/short_tag.git/index
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
similarity index 100%
rename from tests-clar/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
rename to tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
similarity index 100%
rename from tests-clar/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
rename to tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729 b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
similarity index 100%
rename from tests-clar/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
rename to tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/short_tag.git/packed-refs b/tests/resources/short_tag.git/packed-refs
similarity index 100%
rename from tests-clar/resources/short_tag.git/packed-refs
rename to tests/resources/short_tag.git/packed-refs
diff --git a/tests-clar/resources/short_tag.git/refs/heads/master b/tests/resources/short_tag.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/short_tag.git/refs/heads/master
rename to tests/resources/short_tag.git/refs/heads/master
diff --git a/tests-clar/resources/status/.gitted/COMMIT_EDITMSG b/tests/resources/status/.gitted/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/status/.gitted/COMMIT_EDITMSG
rename to tests/resources/status/.gitted/COMMIT_EDITMSG
diff --git a/tests-clar/resources/status/.gitted/HEAD b/tests/resources/status/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/status/.gitted/HEAD
rename to tests/resources/status/.gitted/HEAD
diff --git a/tests-clar/resources/status/.gitted/ORIG_HEAD b/tests/resources/status/.gitted/ORIG_HEAD
similarity index 100%
rename from tests-clar/resources/status/.gitted/ORIG_HEAD
rename to tests/resources/status/.gitted/ORIG_HEAD
diff --git a/tests-clar/resources/status/.gitted/config b/tests/resources/status/.gitted/config
similarity index 100%
rename from tests-clar/resources/status/.gitted/config
rename to tests/resources/status/.gitted/config
diff --git a/tests-clar/resources/status/.gitted/description b/tests/resources/status/.gitted/description
similarity index 100%
rename from tests-clar/resources/status/.gitted/description
rename to tests/resources/status/.gitted/description
diff --git a/tests-clar/resources/status/.gitted/index b/tests/resources/status/.gitted/index
similarity index 100%
rename from tests-clar/resources/status/.gitted/index
rename to tests/resources/status/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/info/exclude b/tests/resources/status/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/status/.gitted/info/exclude
rename to tests/resources/status/.gitted/info/exclude
diff --git a/tests-clar/resources/status/.gitted/logs/HEAD b/tests/resources/status/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/status/.gitted/logs/HEAD
rename to tests/resources/status/.gitted/logs/HEAD
diff --git a/tests-clar/resources/status/.gitted/logs/refs/heads/master b/tests/resources/status/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/status/.gitted/logs/refs/heads/master
rename to tests/resources/status/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
rename to tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
diff --git a/tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
rename to tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
rename to tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
rename to tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
rename to tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
rename to tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
rename to tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
rename to tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
rename to tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
rename to tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
rename to tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
rename to tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
rename to tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
rename to tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
rename to tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
rename to tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
rename to tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
rename to tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
rename to tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
rename to tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
rename to tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
rename to tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
rename to tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
rename to tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
rename to tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
similarity index 100%
rename from tests-clar/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
rename to tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/refs/heads/master b/tests/resources/status/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/status/.gitted/refs/heads/master
rename to tests/resources/status/.gitted/refs/heads/master
diff --git a/tests-clar/resources/status/current_file b/tests/resources/status/current_file
similarity index 100%
rename from tests-clar/resources/status/current_file
rename to tests/resources/status/current_file
diff --git a/tests-clar/resources/status/ignored_file b/tests/resources/status/ignored_file
similarity index 100%
rename from tests-clar/resources/status/ignored_file
rename to tests/resources/status/ignored_file
diff --git a/tests-clar/resources/status/modified_file b/tests/resources/status/modified_file
similarity index 100%
rename from tests-clar/resources/status/modified_file
rename to tests/resources/status/modified_file
diff --git a/tests-clar/resources/status/new_file b/tests/resources/status/new_file
similarity index 100%
rename from tests-clar/resources/status/new_file
rename to tests/resources/status/new_file
diff --git a/tests-clar/resources/status/staged_changes b/tests/resources/status/staged_changes
similarity index 100%
rename from tests-clar/resources/status/staged_changes
rename to tests/resources/status/staged_changes
diff --git a/tests-clar/resources/status/staged_changes_modified_file b/tests/resources/status/staged_changes_modified_file
similarity index 100%
rename from tests-clar/resources/status/staged_changes_modified_file
rename to tests/resources/status/staged_changes_modified_file
diff --git a/tests-clar/resources/status/staged_delete_modified_file b/tests/resources/status/staged_delete_modified_file
similarity index 100%
rename from tests-clar/resources/status/staged_delete_modified_file
rename to tests/resources/status/staged_delete_modified_file
diff --git a/tests-clar/resources/status/staged_new_file b/tests/resources/status/staged_new_file
similarity index 100%
rename from tests-clar/resources/status/staged_new_file
rename to tests/resources/status/staged_new_file
diff --git a/tests-clar/resources/status/staged_new_file_modified_file b/tests/resources/status/staged_new_file_modified_file
similarity index 100%
rename from tests-clar/resources/status/staged_new_file_modified_file
rename to tests/resources/status/staged_new_file_modified_file
diff --git a/tests-clar/resources/status/subdir.txt b/tests/resources/status/subdir.txt
similarity index 100%
rename from tests-clar/resources/status/subdir.txt
rename to tests/resources/status/subdir.txt
diff --git a/tests-clar/resources/status/subdir/current_file b/tests/resources/status/subdir/current_file
similarity index 100%
rename from tests-clar/resources/status/subdir/current_file
rename to tests/resources/status/subdir/current_file
diff --git a/tests-clar/resources/status/subdir/modified_file b/tests/resources/status/subdir/modified_file
similarity index 100%
rename from tests-clar/resources/status/subdir/modified_file
rename to tests/resources/status/subdir/modified_file
diff --git a/tests-clar/resources/status/subdir/new_file b/tests/resources/status/subdir/new_file
similarity index 100%
rename from tests-clar/resources/status/subdir/new_file
rename to tests/resources/status/subdir/new_file
diff --git "a/tests-clar/resources/status/\350\277\231" "b/tests/resources/status/\350\277\231"
similarity index 100%
rename from "tests-clar/resources/status/\350\277\231"
rename to "tests/resources/status/\350\277\231"
diff --git a/tests-clar/resources/submod2/.gitted/HEAD b/tests/resources/submod2/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/HEAD
rename to tests/resources/submod2/.gitted/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/config b/tests/resources/submod2/.gitted/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/config
rename to tests/resources/submod2/.gitted/config
diff --git a/tests-clar/resources/submod2/.gitted/description b/tests/resources/submod2/.gitted/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/description
rename to tests/resources/submod2/.gitted/description
diff --git a/tests-clar/resources/submod2/.gitted/index b/tests/resources/submod2/.gitted/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/index
rename to tests/resources/submod2/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/info/exclude b/tests/resources/submod2/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/info/exclude
rename to tests/resources/submod2/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/logs/HEAD b/tests/resources/submod2/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/logs/HEAD
rename to tests/resources/submod2/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/logs/refs/heads/master b/tests/resources/submod2/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/config
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/description
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/index
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/config
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/description
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/index
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/config
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/description
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/index
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/config
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/description
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/index
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/config
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/config
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/description
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/description
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/index
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/index
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
diff --git a/tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
rename to tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
rename to tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
rename to tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
diff --git a/tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
rename to tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
rename to tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
rename to tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
rename to tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
rename to tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
rename to tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
diff --git a/tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
rename to tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
rename to tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
rename to tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
rename to tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
rename to tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
rename to tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
rename to tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
rename to tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
rename to tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
rename to tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
rename to tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
Binary files differ
diff --git a/tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
rename to tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
diff --git a/tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
rename to tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
diff --git a/tests-clar/resources/submod2/.gitted/refs/heads/master b/tests/resources/submod2/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/.gitted/refs/heads/master
rename to tests/resources/submod2/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2/README.txt b/tests/resources/submod2/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/README.txt
rename to tests/resources/submod2/README.txt
diff --git a/tests-clar/resources/submod2/gitmodules b/tests/resources/submod2/gitmodules
similarity index 100%
rename from tests-clar/resources/submod2/gitmodules
rename to tests/resources/submod2/gitmodules
diff --git a/tests-clar/resources/submod2/just_a_dir/contents b/tests/resources/submod2/just_a_dir/contents
similarity index 100%
rename from tests-clar/resources/submod2/just_a_dir/contents
rename to tests/resources/submod2/just_a_dir/contents
diff --git a/tests-clar/resources/submod2/just_a_file b/tests/resources/submod2/just_a_file
similarity index 100%
rename from tests-clar/resources/submod2/just_a_file
rename to tests/resources/submod2/just_a_file
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/HEAD b/tests/resources/submod2/not-submodule/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/HEAD
rename to tests/resources/submod2/not-submodule/.gitted/HEAD
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/config b/tests/resources/submod2/not-submodule/.gitted/config
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/config
rename to tests/resources/submod2/not-submodule/.gitted/config
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/description b/tests/resources/submod2/not-submodule/.gitted/description
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/description
rename to tests/resources/submod2/not-submodule/.gitted/description
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/index b/tests/resources/submod2/not-submodule/.gitted/index
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/index
rename to tests/resources/submod2/not-submodule/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/info/exclude b/tests/resources/submod2/not-submodule/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/info/exclude
rename to tests/resources/submod2/not-submodule/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/logs/HEAD b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/logs/HEAD
rename to tests/resources/submod2/not-submodule/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/logs/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
rename to tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
rename to tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
rename to tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
rename to tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
Binary files differ
diff --git a/tests-clar/resources/submod2/not-submodule/.gitted/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/.gitted/refs/heads/master
rename to tests/resources/submod2/not-submodule/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2/not-submodule/README.txt b/tests/resources/submod2/not-submodule/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/not-submodule/README.txt
rename to tests/resources/submod2/not-submodule/README.txt
diff --git a/tests-clar/resources/submod2/not/.gitted/notempty b/tests/resources/submod2/not/.gitted/notempty
similarity index 100%
rename from tests-clar/resources/submod2/not/.gitted/notempty
rename to tests/resources/submod2/not/.gitted/notempty
diff --git a/tests-clar/resources/submod2/not/README.txt b/tests/resources/submod2/not/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/not/README.txt
rename to tests/resources/submod2/not/README.txt
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/.gitted b/tests/resources/submod2/sm_added_and_uncommited/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_added_and_uncommited/.gitted
rename to tests/resources/submod2/sm_added_and_uncommited/.gitted
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/README.txt b/tests/resources/submod2/sm_added_and_uncommited/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_added_and_uncommited/README.txt
rename to tests/resources/submod2/sm_added_and_uncommited/README.txt
diff --git a/tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_added_and_uncommited/file_to_modify
rename to tests/resources/submod2/sm_added_and_uncommited/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_file/.gitted b/tests/resources/submod2/sm_changed_file/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_file/.gitted
rename to tests/resources/submod2/sm_changed_file/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_file/README.txt b/tests/resources/submod2/sm_changed_file/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_file/README.txt
rename to tests/resources/submod2/sm_changed_file/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_file/file_to_modify b/tests/resources/submod2/sm_changed_file/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_file/file_to_modify
rename to tests/resources/submod2/sm_changed_file/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_head/.gitted b/tests/resources/submod2/sm_changed_head/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_head/.gitted
rename to tests/resources/submod2/sm_changed_head/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_head/README.txt b/tests/resources/submod2/sm_changed_head/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_head/README.txt
rename to tests/resources/submod2/sm_changed_head/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_head/file_to_modify b/tests/resources/submod2/sm_changed_head/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_head/file_to_modify
rename to tests/resources/submod2/sm_changed_head/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_index/.gitted b/tests/resources/submod2/sm_changed_index/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_index/.gitted
rename to tests/resources/submod2/sm_changed_index/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_index/README.txt b/tests/resources/submod2/sm_changed_index/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_index/README.txt
rename to tests/resources/submod2/sm_changed_index/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_index/file_to_modify b/tests/resources/submod2/sm_changed_index/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_index/file_to_modify
rename to tests/resources/submod2/sm_changed_index/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/.gitted b/tests/resources/submod2/sm_changed_untracked_file/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_untracked_file/.gitted
rename to tests/resources/submod2/sm_changed_untracked_file/.gitted
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/README.txt b/tests/resources/submod2/sm_changed_untracked_file/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_untracked_file/README.txt
rename to tests/resources/submod2/sm_changed_untracked_file/README.txt
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_untracked_file/file_to_modify
rename to tests/resources/submod2/sm_changed_untracked_file/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
similarity index 100%
rename from tests-clar/resources/submod2/sm_changed_untracked_file/i_am_untracked
rename to tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
diff --git a/tests-clar/resources/submod2/sm_missing_commits/.gitted b/tests/resources/submod2/sm_missing_commits/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_missing_commits/.gitted
rename to tests/resources/submod2/sm_missing_commits/.gitted
diff --git a/tests-clar/resources/submod2/sm_missing_commits/README.txt b/tests/resources/submod2/sm_missing_commits/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_missing_commits/README.txt
rename to tests/resources/submod2/sm_missing_commits/README.txt
diff --git a/tests-clar/resources/submod2/sm_missing_commits/file_to_modify b/tests/resources/submod2/sm_missing_commits/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_missing_commits/file_to_modify
rename to tests/resources/submod2/sm_missing_commits/file_to_modify
diff --git a/tests-clar/resources/submod2/sm_unchanged/.gitted b/tests/resources/submod2/sm_unchanged/.gitted
similarity index 100%
rename from tests-clar/resources/submod2/sm_unchanged/.gitted
rename to tests/resources/submod2/sm_unchanged/.gitted
diff --git a/tests-clar/resources/submod2/sm_unchanged/README.txt b/tests/resources/submod2/sm_unchanged/README.txt
similarity index 100%
rename from tests-clar/resources/submod2/sm_unchanged/README.txt
rename to tests/resources/submod2/sm_unchanged/README.txt
diff --git a/tests-clar/resources/submod2/sm_unchanged/file_to_modify b/tests/resources/submod2/sm_unchanged/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2/sm_unchanged/file_to_modify
rename to tests/resources/submod2/sm_unchanged/file_to_modify
diff --git a/tests-clar/resources/submod2_target/.gitted/HEAD b/tests/resources/submod2_target/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/HEAD
rename to tests/resources/submod2_target/.gitted/HEAD
diff --git a/tests-clar/resources/submod2_target/.gitted/config b/tests/resources/submod2_target/.gitted/config
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/config
rename to tests/resources/submod2_target/.gitted/config
diff --git a/tests-clar/resources/submod2_target/.gitted/description b/tests/resources/submod2_target/.gitted/description
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/description
rename to tests/resources/submod2_target/.gitted/description
diff --git a/tests-clar/resources/submod2_target/.gitted/index b/tests/resources/submod2_target/.gitted/index
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/index
rename to tests/resources/submod2_target/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/info/exclude b/tests/resources/submod2_target/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/info/exclude
rename to tests/resources/submod2_target/.gitted/info/exclude
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/HEAD b/tests/resources/submod2_target/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/logs/HEAD
rename to tests/resources/submod2_target/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/logs/refs/heads/master
rename to tests/resources/submod2_target/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/submod2_target/.gitted/refs/heads/master b/tests/resources/submod2_target/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submod2_target/.gitted/refs/heads/master
rename to tests/resources/submod2_target/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submod2_target/README.txt b/tests/resources/submod2_target/README.txt
similarity index 100%
rename from tests-clar/resources/submod2_target/README.txt
rename to tests/resources/submod2_target/README.txt
diff --git a/tests-clar/resources/submod2_target/file_to_modify b/tests/resources/submod2_target/file_to_modify
similarity index 100%
rename from tests-clar/resources/submod2_target/file_to_modify
rename to tests/resources/submod2_target/file_to_modify
diff --git a/tests-clar/resources/submodules/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/HEAD
rename to tests/resources/submodules/.gitted/HEAD
diff --git a/tests-clar/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/config
rename to tests/resources/submodules/.gitted/config
diff --git a/tests-clar/resources/submodules/.gitted/description b/tests/resources/submodules/.gitted/description
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/description
rename to tests/resources/submodules/.gitted/description
diff --git a/tests-clar/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/index
rename to tests/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/info/exclude
rename to tests/resources/submodules/.gitted/info/exclude
diff --git a/tests-clar/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/info/refs
rename to tests/resources/submodules/.gitted/info/refs
diff --git a/tests-clar/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/logs/HEAD
rename to tests/resources/submodules/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/logs/refs/heads/master
rename to tests/resources/submodules/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
rename to tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
diff --git a/tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
rename to tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
rename to tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
rename to tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
rename to tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
rename to tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/info/packs
rename to tests/resources/submodules/.gitted/objects/info/packs
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
rename to tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
rename to tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/packed-refs
rename to tests/resources/submodules/.gitted/packed-refs
diff --git a/tests-clar/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submodules/.gitted/refs/heads/master
rename to tests/resources/submodules/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submodules/added b/tests/resources/submodules/added
similarity index 100%
rename from tests-clar/resources/submodules/added
rename to tests/resources/submodules/added
diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules
new file mode 100644
index 0000000..2798b69
--- /dev/null
+++ b/tests/resources/submodules/gitmodules
@@ -0,0 +1,6 @@
+[submodule "testrepo"]
+	path = testrepo
+	url = 
+[submodule ""]
+	path = testrepo
+	url = 
\ No newline at end of file
diff --git a/tests-clar/resources/submodules/ignored b/tests/resources/submodules/ignored
similarity index 100%
rename from tests-clar/resources/submodules/ignored
rename to tests/resources/submodules/ignored
diff --git a/tests-clar/resources/submodules/modified b/tests/resources/submodules/modified
similarity index 100%
rename from tests-clar/resources/submodules/modified
rename to tests/resources/submodules/modified
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/HEAD
rename to tests/resources/submodules/testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/config
rename to tests/resources/submodules/testrepo/.gitted/config
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/description
rename to tests/resources/submodules/testrepo/.gitted/description
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/index
rename to tests/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/info/exclude
rename to tests/resources/submodules/testrepo/.gitted/info/exclude
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/logs/HEAD
rename to tests/resources/submodules/testrepo/.gitted/logs/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/logs/refs/heads/master
rename to tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
rename to tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
rename to tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
rename to tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
rename to tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
rename to tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
rename to tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
rename to tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
rename to tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
rename to tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
rename to tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
rename to tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
rename to tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
rename to tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
rename to tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
rename to tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
rename to tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
rename to tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/packed-refs
rename to tests/resources/submodules/testrepo/.gitted/packed-refs
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/refs/heads/master
rename to tests/resources/submodules/testrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
rename to tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/README
rename to tests/resources/submodules/testrepo/README
diff --git a/tests-clar/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/branch_file.txt
rename to tests/resources/submodules/testrepo/branch_file.txt
diff --git a/tests-clar/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt
similarity index 100%
rename from tests-clar/resources/submodules/testrepo/new.txt
rename to tests/resources/submodules/testrepo/new.txt
diff --git a/tests-clar/resources/submodules/unmodified b/tests/resources/submodules/unmodified
similarity index 100%
rename from tests-clar/resources/submodules/unmodified
rename to tests/resources/submodules/unmodified
diff --git a/tests-clar/resources/submodules/untracked b/tests/resources/submodules/untracked
similarity index 100%
rename from tests-clar/resources/submodules/untracked
rename to tests/resources/submodules/untracked
diff --git a/tests-clar/resources/template/branches/.gitignore b/tests/resources/template/branches/.gitignore
similarity index 100%
rename from tests-clar/resources/template/branches/.gitignore
rename to tests/resources/template/branches/.gitignore
diff --git a/tests-clar/resources/template/description b/tests/resources/template/description
similarity index 100%
rename from tests-clar/resources/template/description
rename to tests/resources/template/description
diff --git a/tests-clar/resources/template/hooks/link.sample b/tests/resources/template/hooks/link.sample
similarity index 100%
rename from tests-clar/resources/template/hooks/link.sample
rename to tests/resources/template/hooks/link.sample
diff --git a/tests-clar/resources/template/hooks/update.sample b/tests/resources/template/hooks/update.sample
similarity index 100%
rename from tests-clar/resources/template/hooks/update.sample
rename to tests/resources/template/hooks/update.sample
diff --git a/tests-clar/resources/template/info/exclude b/tests/resources/template/info/exclude
similarity index 100%
rename from tests-clar/resources/template/info/exclude
rename to tests/resources/template/info/exclude
diff --git a/tests-clar/resources/testrepo.git/FETCH_HEAD b/tests/resources/testrepo.git/FETCH_HEAD
similarity index 100%
rename from tests-clar/resources/testrepo.git/FETCH_HEAD
rename to tests/resources/testrepo.git/FETCH_HEAD
diff --git a/tests-clar/resources/testrepo.git/HEAD b/tests/resources/testrepo.git/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo.git/HEAD
rename to tests/resources/testrepo.git/HEAD
diff --git a/tests-clar/resources/testrepo.git/HEAD_TRACKER b/tests/resources/testrepo.git/HEAD_TRACKER
similarity index 100%
rename from tests-clar/resources/testrepo.git/HEAD_TRACKER
rename to tests/resources/testrepo.git/HEAD_TRACKER
diff --git a/tests-clar/resources/testrepo.git/config b/tests/resources/testrepo.git/config
similarity index 90%
rename from tests-clar/resources/testrepo.git/config
rename to tests/resources/testrepo.git/config
index 904a4e3..dfab4ee 100644
--- a/tests-clar/resources/testrepo.git/config
+++ b/tests/resources/testrepo.git/config
@@ -10,7 +10,11 @@
 	url = git://github.com/libgit2/libgit2
 [remote "empty-remote-url"]
 	url = 
-
+	pushurl =
+[remote "empty-remote-pushurl"]
+	pushurl =
+[remote "no-remote-url"]
+	fetch =
 [remote "test_with_pushurl"]
 	url = git://github.com/libgit2/fetchlibgit2
 	pushurl = git://github.com/libgit2/pushlibgit2
diff --git a/tests-clar/resources/testrepo.git/index b/tests/resources/testrepo.git/index
similarity index 100%
rename from tests-clar/resources/testrepo.git/index
rename to tests/resources/testrepo.git/index
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/logs/HEAD b/tests/resources/testrepo.git/logs/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/HEAD
rename to tests/resources/testrepo.git/logs/HEAD
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/br2 b/tests/resources/testrepo.git/logs/refs/heads/br2
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/refs/heads/br2
rename to tests/resources/testrepo.git/logs/refs/heads/br2
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/master b/tests/resources/testrepo.git/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/refs/heads/master
rename to tests/resources/testrepo.git/logs/refs/heads/master
diff --git a/tests-clar/resources/testrepo.git/logs/refs/heads/not-good b/tests/resources/testrepo.git/logs/refs/heads/not-good
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/refs/heads/not-good
rename to tests/resources/testrepo.git/logs/refs/heads/not-good
diff --git a/tests-clar/resources/testrepo.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/refs/remotes/origin/HEAD
rename to tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo.git/logs/refs/remotes/test/master b/tests/resources/testrepo.git/logs/refs/remotes/test/master
similarity index 100%
rename from tests-clar/resources/testrepo.git/logs/refs/remotes/test/master
rename to tests/resources/testrepo.git/logs/refs/remotes/test/master
diff --git a/tests-clar/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
rename to tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
rename to tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
rename to tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
rename to tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
rename to tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
rename to tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
rename to tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
rename to tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
rename to tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679 b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
rename to tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
rename to tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
rename to tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
rename to tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
rename to tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
rename to tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
rename to tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
rename to tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
rename to tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
diff --git a/tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
rename to tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
rename to tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
rename to tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
rename to tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
rename to tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
rename to tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
rename to tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
rename to tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
rename to tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
rename to tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
rename to tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
rename to tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
rename to tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
rename to tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
rename to tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
rename to tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
similarity index 100%
rename from tests-clar/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
rename to tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo.git/packed-refs b/tests/resources/testrepo.git/packed-refs
similarity index 100%
rename from tests-clar/resources/testrepo.git/packed-refs
rename to tests/resources/testrepo.git/packed-refs
diff --git a/tests-clar/resources/testrepo.git/refs/heads/br2 b/tests/resources/testrepo.git/refs/heads/br2
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/br2
rename to tests/resources/testrepo.git/refs/heads/br2
diff --git a/tests-clar/resources/testrepo.git/refs/heads/cannot-fetch b/tests/resources/testrepo.git/refs/heads/cannot-fetch
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/cannot-fetch
rename to tests/resources/testrepo.git/refs/heads/cannot-fetch
diff --git a/tests-clar/resources/testrepo.git/refs/heads/chomped b/tests/resources/testrepo.git/refs/heads/chomped
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/chomped
rename to tests/resources/testrepo.git/refs/heads/chomped
diff --git a/tests-clar/resources/testrepo.git/refs/heads/haacked b/tests/resources/testrepo.git/refs/heads/haacked
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/haacked
rename to tests/resources/testrepo.git/refs/heads/haacked
diff --git a/tests-clar/resources/testrepo.git/refs/heads/master b/tests/resources/testrepo.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/master
rename to tests/resources/testrepo.git/refs/heads/master
diff --git a/tests-clar/resources/testrepo.git/refs/heads/not-good b/tests/resources/testrepo.git/refs/heads/not-good
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/not-good
rename to tests/resources/testrepo.git/refs/heads/not-good
diff --git a/tests-clar/resources/testrepo.git/refs/heads/packed-test b/tests/resources/testrepo.git/refs/heads/packed-test
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/packed-test
rename to tests/resources/testrepo.git/refs/heads/packed-test
diff --git a/tests-clar/resources/testrepo.git/refs/heads/subtrees b/tests/resources/testrepo.git/refs/heads/subtrees
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/subtrees
rename to tests/resources/testrepo.git/refs/heads/subtrees
diff --git a/tests-clar/resources/testrepo.git/refs/heads/test b/tests/resources/testrepo.git/refs/heads/test
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/test
rename to tests/resources/testrepo.git/refs/heads/test
diff --git a/tests-clar/resources/testrepo.git/refs/heads/track-local b/tests/resources/testrepo.git/refs/heads/track-local
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/track-local
rename to tests/resources/testrepo.git/refs/heads/track-local
diff --git a/tests-clar/resources/testrepo.git/refs/heads/trailing b/tests/resources/testrepo.git/refs/heads/trailing
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/heads/trailing
rename to tests/resources/testrepo.git/refs/heads/trailing
diff --git a/tests-clar/resources/testrepo.git/refs/notes/fanout b/tests/resources/testrepo.git/refs/notes/fanout
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/notes/fanout
rename to tests/resources/testrepo.git/refs/notes/fanout
diff --git a/tests-clar/resources/testrepo.git/refs/remotes/test/master b/tests/resources/testrepo.git/refs/remotes/test/master
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/remotes/test/master
rename to tests/resources/testrepo.git/refs/remotes/test/master
diff --git a/tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/annotated_tag_to_blob
rename to tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
diff --git a/tests-clar/resources/testrepo.git/refs/tags/e90810b b/tests/resources/testrepo.git/refs/tags/e90810b
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/e90810b
rename to tests/resources/testrepo.git/refs/tags/e90810b
diff --git a/tests-clar/resources/testrepo.git/refs/tags/hard_tag b/tests/resources/testrepo.git/refs/tags/hard_tag
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/hard_tag
rename to tests/resources/testrepo.git/refs/tags/hard_tag
diff --git a/tests-clar/resources/testrepo.git/refs/tags/point_to_blob b/tests/resources/testrepo.git/refs/tags/point_to_blob
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/point_to_blob
rename to tests/resources/testrepo.git/refs/tags/point_to_blob
diff --git a/tests-clar/resources/testrepo.git/refs/tags/taggerless b/tests/resources/testrepo.git/refs/tags/taggerless
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/taggerless
rename to tests/resources/testrepo.git/refs/tags/taggerless
diff --git a/tests-clar/resources/testrepo.git/refs/tags/test b/tests/resources/testrepo.git/refs/tags/test
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/test
rename to tests/resources/testrepo.git/refs/tags/test
diff --git a/tests-clar/resources/testrepo.git/refs/tags/wrapped_tag b/tests/resources/testrepo.git/refs/tags/wrapped_tag
similarity index 100%
rename from tests-clar/resources/testrepo.git/refs/tags/wrapped_tag
rename to tests/resources/testrepo.git/refs/tags/wrapped_tag
diff --git a/tests-clar/resources/testrepo/.gitted/HEAD b/tests/resources/testrepo/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/HEAD
rename to tests/resources/testrepo/.gitted/HEAD
diff --git a/tests-clar/resources/testrepo/.gitted/HEAD_TRACKER b/tests/resources/testrepo/.gitted/HEAD_TRACKER
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/HEAD_TRACKER
rename to tests/resources/testrepo/.gitted/HEAD_TRACKER
diff --git a/tests-clar/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/config
rename to tests/resources/testrepo/.gitted/config
diff --git a/tests-clar/resources/testrepo/.gitted/index b/tests/resources/testrepo/.gitted/index
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/index
rename to tests/resources/testrepo/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
rename to tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
rename to tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
rename to tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
rename to tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
rename to tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
rename to tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
rename to tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6 b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
rename to tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
rename to tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
rename to tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
rename to tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
new file mode 100644
index 0000000..ee7c781
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
new file mode 100644
index 0000000..197685b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
rename to tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
new file mode 100644
index 0000000..db778aa
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
rename to tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80 b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
rename to tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
rename to tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
diff --git a/tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
rename to tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
rename to tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
rename to tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
rename to tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
rename to tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
rename to tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
rename to tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
rename to tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
rename to tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
rename to tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
rename to tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
rename to tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
rename to tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
rename to tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
rename to tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
rename to tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
rename to tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
rename to tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo/.gitted/packed-refs b/tests/resources/testrepo/.gitted/packed-refs
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/packed-refs
rename to tests/resources/testrepo/.gitted/packed-refs
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/br2 b/tests/resources/testrepo/.gitted/refs/heads/br2
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/br2
rename to tests/resources/testrepo/.gitted/refs/heads/br2
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/dir b/tests/resources/testrepo/.gitted/refs/heads/dir
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/dir
rename to tests/resources/testrepo/.gitted/refs/heads/dir
diff --git a/tests/resources/testrepo/.gitted/refs/heads/long-file-name b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
new file mode 100644
index 0000000..1f942a7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
@@ -0,0 +1 @@
+6b377958d8c6a4906e8573b53672a1a23a4e8ce6
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/master b/tests/resources/testrepo/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/master
rename to tests/resources/testrepo/.gitted/refs/heads/master
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/packed-test b/tests/resources/testrepo/.gitted/refs/heads/packed-test
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/packed-test
rename to tests/resources/testrepo/.gitted/refs/heads/packed-test
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/subtrees b/tests/resources/testrepo/.gitted/refs/heads/subtrees
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/subtrees
rename to tests/resources/testrepo/.gitted/refs/heads/subtrees
diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/test b/tests/resources/testrepo/.gitted/refs/heads/test
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/heads/test
rename to tests/resources/testrepo/.gitted/refs/heads/test
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/e90810b b/tests/resources/testrepo/.gitted/refs/tags/e90810b
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/tags/e90810b
rename to tests/resources/testrepo/.gitted/refs/tags/e90810b
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/tags/foo/bar
rename to tests/resources/testrepo/.gitted/refs/tags/foo/bar
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/foo/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/tags/foo/foo/bar
rename to tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/tags/point_to_blob
rename to tests/resources/testrepo/.gitted/refs/tags/point_to_blob
diff --git a/tests-clar/resources/testrepo/.gitted/refs/tags/test b/tests/resources/testrepo/.gitted/refs/tags/test
similarity index 100%
rename from tests-clar/resources/testrepo/.gitted/refs/tags/test
rename to tests/resources/testrepo/.gitted/refs/tags/test
diff --git a/tests-clar/resources/testrepo2/.gitted/HEAD b/tests/resources/testrepo2/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/HEAD
rename to tests/resources/testrepo2/.gitted/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/config b/tests/resources/testrepo2/.gitted/config
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/config
rename to tests/resources/testrepo2/.gitted/config
diff --git a/tests-clar/resources/testrepo2/.gitted/description b/tests/resources/testrepo2/.gitted/description
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/description
rename to tests/resources/testrepo2/.gitted/description
diff --git a/tests-clar/resources/testrepo2/.gitted/index b/tests/resources/testrepo2/.gitted/index
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/index
rename to tests/resources/testrepo2/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/info/exclude b/tests/resources/testrepo2/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/info/exclude
rename to tests/resources/testrepo2/.gitted/info/exclude
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/HEAD b/tests/resources/testrepo2/.gitted/logs/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/logs/HEAD
rename to tests/resources/testrepo2/.gitted/logs/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/logs/refs/heads/master
rename to tests/resources/testrepo2/.gitted/logs/refs/heads/master
diff --git a/tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
rename to tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
rename to tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
rename to tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
rename to tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
rename to tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
rename to tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
rename to tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
rename to tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
rename to tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
rename to tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
rename to tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
rename to tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
rename to tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
rename to tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
rename to tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
rename to tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
rename to tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
rename to tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
rename to tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
rename to tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
rename to tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
rename to tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
rename to tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
rename to tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
rename to tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
rename to tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
rename to tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests-clar/resources/testrepo2/.gitted/packed-refs b/tests/resources/testrepo2/.gitted/packed-refs
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/packed-refs
rename to tests/resources/testrepo2/.gitted/packed-refs
diff --git a/tests-clar/resources/testrepo2/.gitted/refs/heads/master b/tests/resources/testrepo2/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/refs/heads/master
rename to tests/resources/testrepo2/.gitted/refs/heads/master
diff --git a/tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
rename to tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/testrepo2/README b/tests/resources/testrepo2/README
similarity index 100%
rename from tests-clar/resources/testrepo2/README
rename to tests/resources/testrepo2/README
diff --git a/tests-clar/resources/testrepo2/new.txt b/tests/resources/testrepo2/new.txt
similarity index 100%
rename from tests-clar/resources/testrepo2/new.txt
rename to tests/resources/testrepo2/new.txt
diff --git a/tests-clar/resources/testrepo2/subdir/README b/tests/resources/testrepo2/subdir/README
similarity index 100%
rename from tests-clar/resources/testrepo2/subdir/README
rename to tests/resources/testrepo2/subdir/README
diff --git a/tests-clar/resources/testrepo2/subdir/new.txt b/tests/resources/testrepo2/subdir/new.txt
similarity index 100%
rename from tests-clar/resources/testrepo2/subdir/new.txt
rename to tests/resources/testrepo2/subdir/new.txt
diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/README b/tests/resources/testrepo2/subdir/subdir2/README
similarity index 100%
rename from tests-clar/resources/testrepo2/subdir/subdir2/README
rename to tests/resources/testrepo2/subdir/subdir2/README
diff --git a/tests-clar/resources/testrepo2/subdir/subdir2/new.txt b/tests/resources/testrepo2/subdir/subdir2/new.txt
similarity index 100%
rename from tests-clar/resources/testrepo2/subdir/subdir2/new.txt
rename to tests/resources/testrepo2/subdir/subdir2/new.txt
diff --git a/tests-clar/resources/twowaymerge.git/HEAD b/tests/resources/twowaymerge.git/HEAD
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/HEAD
rename to tests/resources/twowaymerge.git/HEAD
diff --git a/tests-clar/resources/twowaymerge.git/config b/tests/resources/twowaymerge.git/config
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/config
rename to tests/resources/twowaymerge.git/config
diff --git a/tests-clar/resources/twowaymerge.git/description b/tests/resources/twowaymerge.git/description
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/description
rename to tests/resources/twowaymerge.git/description
diff --git a/tests-clar/resources/twowaymerge.git/info/exclude b/tests/resources/twowaymerge.git/info/exclude
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/info/exclude
rename to tests/resources/twowaymerge.git/info/exclude
diff --git a/tests-clar/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29 b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
rename to tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67 b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
rename to tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5 b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
rename to tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
rename to tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006 b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
rename to tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83 b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
rename to tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4 b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
rename to tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432 b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
rename to tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8 b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
rename to tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4 b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
rename to tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
rename to tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118 b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
rename to tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
rename to tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11 b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
rename to tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912 b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
rename to tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247 b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
rename to tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
diff --git a/tests-clar/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545 b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
rename to tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650 b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
rename to tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30 b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
rename to tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28 b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
rename to tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706 b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
rename to tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19 b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
rename to tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417 b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
rename to tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
diff --git a/tests-clar/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
rename to tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
diff --git a/tests-clar/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4 b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
rename to tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
rename to tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6 b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
rename to tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6 b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
rename to tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494 b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
rename to tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
rename to tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
diff --git a/tests-clar/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694 b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
rename to tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25 b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
rename to tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
Binary files differ
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/first-branch b/tests/resources/twowaymerge.git/refs/heads/first-branch
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/refs/heads/first-branch
rename to tests/resources/twowaymerge.git/refs/heads/first-branch
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/master b/tests/resources/twowaymerge.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/refs/heads/master
rename to tests/resources/twowaymerge.git/refs/heads/master
diff --git a/tests-clar/resources/twowaymerge.git/refs/heads/second-branch b/tests/resources/twowaymerge.git/refs/heads/second-branch
similarity index 100%
rename from tests-clar/resources/twowaymerge.git/refs/heads/second-branch
rename to tests/resources/twowaymerge.git/refs/heads/second-branch
diff --git a/tests-clar/resources/typechanges/.gitted/HEAD b/tests/resources/typechanges/.gitted/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/HEAD
rename to tests/resources/typechanges/.gitted/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/config b/tests/resources/typechanges/.gitted/config
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/config
rename to tests/resources/typechanges/.gitted/config
diff --git a/tests-clar/resources/typechanges/.gitted/description b/tests/resources/typechanges/.gitted/description
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/description
rename to tests/resources/typechanges/.gitted/description
diff --git a/tests-clar/resources/typechanges/.gitted/index b/tests/resources/typechanges/.gitted/index
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/index
rename to tests/resources/typechanges/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/info/exclude b/tests/resources/typechanges/.gitted/info/exclude
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/info/exclude
rename to tests/resources/typechanges/.gitted/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/HEAD b/tests/resources/typechanges/.gitted/modules/b/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/HEAD
rename to tests/resources/typechanges/.gitted/modules/b/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/config b/tests/resources/typechanges/.gitted/modules/b/config
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/config
rename to tests/resources/typechanges/.gitted/modules/b/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/description b/tests/resources/typechanges/.gitted/modules/b/description
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/description
rename to tests/resources/typechanges/.gitted/modules/b/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/index b/tests/resources/typechanges/.gitted/modules/b/index
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/index
rename to tests/resources/typechanges/.gitted/modules/b/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/info/exclude b/tests/resources/typechanges/.gitted/modules/b/info/exclude
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/info/exclude
rename to tests/resources/typechanges/.gitted/modules/b/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/packed-refs b/tests/resources/typechanges/.gitted/modules/b/packed-refs
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/packed-refs
rename to tests/resources/typechanges/.gitted/modules/b/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/refs/heads/master b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/refs/heads/master
rename to tests/resources/typechanges/.gitted/modules/b/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
rename to tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/HEAD b/tests/resources/typechanges/.gitted/modules/d/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/HEAD
rename to tests/resources/typechanges/.gitted/modules/d/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/config b/tests/resources/typechanges/.gitted/modules/d/config
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/config
rename to tests/resources/typechanges/.gitted/modules/d/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/description b/tests/resources/typechanges/.gitted/modules/d/description
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/description
rename to tests/resources/typechanges/.gitted/modules/d/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/index b/tests/resources/typechanges/.gitted/modules/d/index
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/index
rename to tests/resources/typechanges/.gitted/modules/d/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/info/exclude b/tests/resources/typechanges/.gitted/modules/d/info/exclude
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/info/exclude
rename to tests/resources/typechanges/.gitted/modules/d/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/packed-refs b/tests/resources/typechanges/.gitted/modules/d/packed-refs
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/packed-refs
rename to tests/resources/typechanges/.gitted/modules/d/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/refs/heads/master b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/refs/heads/master
rename to tests/resources/typechanges/.gitted/modules/d/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
rename to tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/HEAD b/tests/resources/typechanges/.gitted/modules/e/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/HEAD
rename to tests/resources/typechanges/.gitted/modules/e/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/config b/tests/resources/typechanges/.gitted/modules/e/config
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/config
rename to tests/resources/typechanges/.gitted/modules/e/config
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/description b/tests/resources/typechanges/.gitted/modules/e/description
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/description
rename to tests/resources/typechanges/.gitted/modules/e/description
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/index b/tests/resources/typechanges/.gitted/modules/e/index
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/index
rename to tests/resources/typechanges/.gitted/modules/e/index
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/info/exclude b/tests/resources/typechanges/.gitted/modules/e/info/exclude
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/info/exclude
rename to tests/resources/typechanges/.gitted/modules/e/info/exclude
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
rename to tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
rename to tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
rename to tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
rename to tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
rename to tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
rename to tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
rename to tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
rename to tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
rename to tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
rename to tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
rename to tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
rename to tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/packed-refs b/tests/resources/typechanges/.gitted/modules/e/packed-refs
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/packed-refs
rename to tests/resources/typechanges/.gitted/modules/e/packed-refs
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/refs/heads/master b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/refs/heads/master
rename to tests/resources/typechanges/.gitted/modules/e/refs/heads/master
diff --git a/tests-clar/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
rename to tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8 b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
rename to tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
rename to tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586 b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
rename to tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97 b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
rename to tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475 b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
rename to tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02 b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
rename to tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95 b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
rename to tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592 b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
rename to tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5 b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
rename to tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
rename to tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
rename to tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0 b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
rename to tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0 b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
rename to tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4 b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
rename to tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729 b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
rename to tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2 b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
rename to tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
rename to tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
rename to tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
rename to tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3 b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
rename to tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
rename to tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
rename to tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7 b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
rename to tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
rename to tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29 b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
rename to tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447 b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
rename to tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
rename to tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707 b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
rename to tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
rename to tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095 b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
rename to tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
rename to tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
rename to tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
rename to tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
rename to tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
rename to tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
rename to tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
Binary files differ
diff --git a/tests-clar/resources/typechanges/.gitted/refs/heads/master b/tests/resources/typechanges/.gitted/refs/heads/master
similarity index 100%
rename from tests-clar/resources/typechanges/.gitted/refs/heads/master
rename to tests/resources/typechanges/.gitted/refs/heads/master
diff --git a/tests-clar/resources/typechanges/README.md b/tests/resources/typechanges/README.md
similarity index 100%
rename from tests-clar/resources/typechanges/README.md
rename to tests/resources/typechanges/README.md
diff --git a/tests-clar/resources/typechanges/gitmodules b/tests/resources/typechanges/gitmodules
similarity index 100%
rename from tests-clar/resources/typechanges/gitmodules
rename to tests/resources/typechanges/gitmodules
diff --git a/tests-clar/resources/unsymlinked.git/HEAD b/tests/resources/unsymlinked.git/HEAD
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/HEAD
rename to tests/resources/unsymlinked.git/HEAD
diff --git a/tests-clar/resources/unsymlinked.git/config b/tests/resources/unsymlinked.git/config
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/config
rename to tests/resources/unsymlinked.git/config
diff --git a/tests-clar/resources/unsymlinked.git/description b/tests/resources/unsymlinked.git/description
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/description
rename to tests/resources/unsymlinked.git/description
diff --git a/tests-clar/resources/unsymlinked.git/info/exclude b/tests/resources/unsymlinked.git/info/exclude
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/info/exclude
rename to tests/resources/unsymlinked.git/info/exclude
diff --git a/tests-clar/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
rename to tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
rename to tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
rename to tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
rename to tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
rename to tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
rename to tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
rename to tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
rename to tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
rename to tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
rename to tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
diff --git a/tests-clar/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
rename to tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
rename to tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
rename to tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
rename to tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
Binary files differ
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/exe-file b/tests/resources/unsymlinked.git/refs/heads/exe-file
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/refs/heads/exe-file
rename to tests/resources/unsymlinked.git/refs/heads/exe-file
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/master b/tests/resources/unsymlinked.git/refs/heads/master
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/refs/heads/master
rename to tests/resources/unsymlinked.git/refs/heads/master
diff --git a/tests-clar/resources/unsymlinked.git/refs/heads/reg-file b/tests/resources/unsymlinked.git/refs/heads/reg-file
similarity index 100%
rename from tests-clar/resources/unsymlinked.git/refs/heads/reg-file
rename to tests/resources/unsymlinked.git/refs/heads/reg-file
diff --git a/tests-clar/revwalk/basic.c b/tests/revwalk/basic.c
similarity index 80%
rename from tests-clar/revwalk/basic.c
rename to tests/revwalk/basic.c
index e827762..6d55aed 100644
--- a/tests-clar/revwalk/basic.c
+++ b/tests/revwalk/basic.c
@@ -98,27 +98,46 @@
 	return test_walk_only(walk, possible_results, results_count);
 }
 
-static git_repository *_repo;
-static git_revwalk *_walk;
+static git_repository *_repo = NULL;
+static git_revwalk *_walk = NULL;
+static const char *_fixture = NULL;
 
 void test_revwalk_basic__initialize(void)
 {
-	cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
-	cl_git_pass(git_revwalk_new(&_walk, _repo));
 }
 
 void test_revwalk_basic__cleanup(void)
 {
 	git_revwalk_free(_walk);
-	_walk = NULL;
-	git_repository_free(_repo);
+
+	if (_fixture)
+		cl_git_sandbox_cleanup();
+	else
+		git_repository_free(_repo);
+
+	_fixture = NULL;
 	_repo = NULL;
+	_walk = NULL;
+}
+
+static void revwalk_basic_setup_walk(const char *fixture)
+{
+	if (fixture) {
+		_fixture = fixture;
+		_repo = cl_git_sandbox_init(fixture);
+	} else {
+		cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+	}
+
+	cl_git_pass(git_revwalk_new(&_walk, _repo));
 }
 
 void test_revwalk_basic__sorting_modes(void)
 {
 	git_oid id;
 
+	revwalk_basic_setup_walk(NULL);
+
 	git_oid_fromstr(&id, commit_head);
 
 	cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME, commit_sorting_time, 1));
@@ -132,6 +151,8 @@
 	int i = 0;
 	git_oid oid;
 
+	revwalk_basic_setup_walk(NULL);
+
 	cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
 
 	while (git_revwalk_next(&oid, _walk) == 0) {
@@ -142,11 +163,30 @@
 	cl_assert(i == 14);
 }
 
+void test_revwalk_basic__glob_heads_with_invalid(void)
+{
+	int i;
+	git_oid oid;
+
+	revwalk_basic_setup_walk("testrepo");
+
+	cl_git_mkfile("testrepo/.git/refs/heads/garbage", "not-a-ref");
+	cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+	for (i = 0; !git_revwalk_next(&oid, _walk); ++i)
+		/* walking */;
+
+	/* git log --branches --oneline | wc -l => 16 */
+	cl_assert_equal_i(17, i);
+}
+
 void test_revwalk_basic__push_head(void)
 {
 	int i = 0;
 	git_oid oid;
 
+	revwalk_basic_setup_walk(NULL);
+
 	cl_git_pass(git_revwalk_push_head(_walk));
 
 	while (git_revwalk_next(&oid, _walk) == 0) {
@@ -162,6 +202,8 @@
 	int i = 0;
 	git_oid oid;
 
+	revwalk_basic_setup_walk(NULL);
+
 	cl_git_pass(git_revwalk_push_head(_walk));
 	cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
 
@@ -178,6 +220,8 @@
 	int i = 0;
 	git_oid oid;
 
+	revwalk_basic_setup_walk(NULL);
+
 	cl_git_pass(git_revwalk_push_head(_walk));
 	cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed"));
 
@@ -193,12 +237,16 @@
 {
 	git_oid oid;
 
+	revwalk_basic_setup_walk(NULL);
+
 	cl_git_pass(git_oid_fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91"));
 	cl_git_fail(git_revwalk_push(_walk, &oid));
 }
 
 void test_revwalk_basic__push_range(void)
 {
+	revwalk_basic_setup_walk(NULL);
+
 	git_revwalk_reset(_walk);
 	git_revwalk_sorting(_walk, 0);
 	cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e"));
diff --git a/tests-clar/revwalk/mergebase.c b/tests/revwalk/mergebase.c
similarity index 95%
rename from tests-clar/revwalk/mergebase.c
rename to tests/revwalk/mergebase.c
index e2617ab..2d01647 100644
--- a/tests-clar/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -123,6 +123,18 @@
 	cl_assert_equal_sz(4, behind);
 }
 
+void test_revwalk_mergebase__prefer_youngest_merge_base(void)
+{
+	git_oid result, one, two, expected;
+
+	cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f "));
+	cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+	cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+	cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+	cl_assert(git_oid_cmp(&result, &expected) == 0);
+}
+
 void test_revwalk_mergebase__no_off_by_one_missing(void)
 {
 	git_oid result, one, two;
@@ -160,9 +172,9 @@
 	va_end(ap);
 
 	if (expected_sha == NULL)
-		cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_many(&oid, _repo, oids, count));
+		cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_many(&oid, _repo, count, oids));
 	else {
-		cl_git_pass(git_merge_base_many(&oid, _repo, oids, count));
+		cl_git_pass(git_merge_base_many(&oid, _repo, count, oids));
 		cl_git_pass(git_oid_fromstr(&expected, expected_sha));
 
 		cl_assert(git_oid_cmp(&expected, &oid) == 0);
diff --git a/tests-clar/revwalk/signatureparsing.c b/tests/revwalk/signatureparsing.c
similarity index 100%
rename from tests-clar/revwalk/signatureparsing.c
rename to tests/revwalk/signatureparsing.c
diff --git a/tests/revwalk/simplify.c b/tests/revwalk/simplify.c
new file mode 100644
index 0000000..81c19d3
--- /dev/null
+++ b/tests/revwalk/simplify.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+
+void test_revwalk_simplify__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+/*
+	*   a4a7dce [0] Merge branch 'master' into br2
+	|\
+	| * 9fd738e [1] a fourth commit
+	| * 4a202b3 [2] a third commit
+	* | c47800c [3] branch commit one
+	|/
+	* 5b5b025 [5] another commit
+	* 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *expected_str[] = {
+	"a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+	"c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+	"8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+	"5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+void test_revwalk_simplify__first_parent(void)
+{
+	git_repository *repo;
+	git_revwalk *walk;
+	git_oid id, expected[4];
+	int i, error;
+
+	for (i = 0; i < 4; i++) {
+		git_oid_fromstr(&expected[i], expected_str[i]);
+	}
+
+	repo = cl_git_sandbox_init("testrepo.git");
+	cl_git_pass(git_revwalk_new(&walk, repo));
+
+	git_oid_fromstr(&id, commit_head);
+	cl_git_pass(git_revwalk_push(walk, &id));
+	git_revwalk_simplify_first_parent(walk);
+
+	i = 0;
+	while ((error = git_revwalk_next(&id, walk)) == 0) {
+		git_oid_cmp(&id, &expected[i]);
+		i++;
+	}
+
+	cl_assert_equal_i(i, 4);
+	cl_assert_equal_i(error, GIT_ITEROVER);
+
+	git_revwalk_free(walk);
+}
diff --git a/tests-clar/stash/drop.c b/tests/stash/drop.c
similarity index 88%
rename from tests-clar/stash/drop.c
rename to tests/stash/drop.c
index 60b3c72..63ff037 100644
--- a/tests-clar/stash/drop.c
+++ b/tests/stash/drop.c
@@ -36,25 +36,27 @@
 	cl_git_mkfile("stash/zero.txt", "content\n");
 	cl_git_pass(git_repository_index(&index, repo));
 	cl_git_pass(git_index_add_bypath(index, "zero.txt"));
-	commit_staged_files(&oid, index, signature);
+	cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
 	cl_assert(git_path_exists("stash/zero.txt"));
+	git_index_free(index);
 
 	cl_git_mkfile("stash/one.txt", "content\n");
-	cl_git_pass(git_stash_save(&oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
+	cl_git_pass(git_stash_save(
+		&oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
 	cl_assert(!git_path_exists("stash/one.txt"));
 	cl_assert(git_path_exists("stash/zero.txt"));
 
 	cl_git_mkfile("stash/two.txt", "content\n");
-	cl_git_pass(git_stash_save(&oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
+	cl_git_pass(git_stash_save(
+		&oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
 	cl_assert(!git_path_exists("stash/two.txt"));
 	cl_assert(git_path_exists("stash/zero.txt"));
 
 	cl_git_mkfile("stash/three.txt", "content\n");
-	cl_git_pass(git_stash_save(&oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
+	cl_git_pass(git_stash_save(
+		&oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
 	cl_assert(!git_path_exists("stash/three.txt"));
 	cl_assert(git_path_exists("stash/zero.txt"));
-
-	git_index_free(index);
 }
 
 void test_stash_drop__cannot_drop_a_non_existing_stashed_state(void)
@@ -100,7 +102,7 @@
 
 	cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
 
-	cl_git_pass(git_reflog_read(&reflog, stash));
+	cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
 	entry = git_reflog_entry_byindex(reflog, 1);
 
 	git_oid_cpy(&oid, git_reflog_entry_id_old(entry));
@@ -110,7 +112,7 @@
 
 	cl_git_pass(git_stash_drop(repo, 1));
 
-	cl_git_pass(git_reflog_read(&reflog, stash));
+	cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
 	entry = git_reflog_entry_byindex(reflog, 0);
 
 	cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_id_old(entry)));
@@ -160,7 +162,7 @@
 	retrieve_top_stash_id(&oid);
 
 	cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}"));
-	cl_assert_equal_i(false, git_oid_cmp(&oid, git_object_id(next_top_stash)) == 0);
+	cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)) != 0);
 
 	cl_git_pass(git_stash_drop(repo, 0));
 
diff --git a/tests-clar/stash/foreach.c b/tests/stash/foreach.c
similarity index 100%
rename from tests-clar/stash/foreach.c
rename to tests/stash/foreach.c
diff --git a/tests-clar/stash/save.c b/tests/stash/save.c
similarity index 86%
rename from tests-clar/stash/save.c
rename to tests/stash/save.c
index eae116a..3d92b26 100644
--- a/tests-clar/stash/save.c
+++ b/tests/stash/save.c
@@ -113,33 +113,15 @@
 	cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
 }
 
-static void assert_status(
-	const char *path,
-	int status_flags)
-{
-	unsigned int status;
-	int error;
-
-	error = git_status_file(&status, repo, path);
-
-	if (status_flags < 0) {
-		cl_assert_equal_i(status_flags, error);
-		return;
-	}
-
-	cl_assert_equal_i(0, error);
-	cl_assert_equal_i((unsigned int)status_flags, status);
-}
-
 void test_stash_save__can_keep_index(void)
 {
 	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
 
-	assert_status("what", GIT_STATUS_INDEX_MODIFIED);
-	assert_status("how", GIT_STATUS_INDEX_MODIFIED);
-	assert_status("who", GIT_STATUS_CURRENT);
-	assert_status("when", GIT_STATUS_WT_NEW);
-	assert_status("just.ignore", GIT_STATUS_IGNORED);
+	assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "who", GIT_STATUS_CURRENT);
+	assert_status(repo, "when", GIT_STATUS_WT_NEW);
+	assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
 }
 
 static void assert_commit_message_contains(const char *revision, const char *fragment)
@@ -194,7 +176,7 @@
 
 	cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1));
 
-	cl_assert_equal_i(GIT_EORPHANEDHEAD,
+	cl_assert_equal_i(GIT_EUNBORNBRANCH,
 		git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
 
 	git_reference_free(head);
@@ -241,7 +223,7 @@
 void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
 {
 	git_index *index;
-	git_oid commit_oid, stash_tip_oid;
+	git_oid stash_tip_oid;
 
 	cl_git_pass(git_repository_index(&index, repo));
 
@@ -251,8 +233,7 @@
 	 */
 	cl_git_pass(git_index_add_bypath(index, "what"));
 	cl_git_pass(git_index_add_bypath(index, "who"));
-	cl_git_pass(git_index_write(index));
-	commit_staged_files(&commit_oid, index, signature);
+	cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
 	git_index_free(index);
 
 	cl_assert_equal_i(GIT_ENOTFOUND,
@@ -309,25 +290,25 @@
 	 * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b    when
 	*/
 
-	assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
-	assert_status("how", GIT_STATUS_INDEX_MODIFIED);
-	assert_status("who", GIT_STATUS_WT_MODIFIED);
-	assert_status("when", GIT_STATUS_WT_NEW);
-	assert_status("just.ignore", GIT_STATUS_IGNORED);
+	assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+	assert_status(repo, "when", GIT_STATUS_WT_NEW);
+	assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
 
 	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
-	assert_status("what", GIT_STATUS_CURRENT);
-	assert_status("how", GIT_STATUS_CURRENT);
-	assert_status("who", GIT_STATUS_CURRENT);
-	assert_status("when", GIT_STATUS_WT_NEW);
-	assert_status("just.ignore", GIT_STATUS_IGNORED);
+	assert_status(repo, "what", GIT_STATUS_CURRENT);
+	assert_status(repo, "how", GIT_STATUS_CURRENT);
+	assert_status(repo, "who", GIT_STATUS_CURRENT);
+	assert_status(repo, "when", GIT_STATUS_WT_NEW);
+	assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
 
 	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
-	assert_status("what", GIT_STATUS_CURRENT);
-	assert_status("how", GIT_STATUS_CURRENT);
-	assert_status("who", GIT_STATUS_CURRENT);
-	assert_status("when", GIT_ENOTFOUND);
-	assert_status("just.ignore", GIT_STATUS_IGNORED);
+	assert_status(repo, "what", GIT_STATUS_CURRENT);
+	assert_status(repo, "how", GIT_STATUS_CURRENT);
+	assert_status(repo, "who", GIT_STATUS_CURRENT);
+	assert_status(repo, "when", GIT_ENOTFOUND);
+	assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
 
 
 	assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449");	/* see you later */
@@ -361,11 +342,11 @@
 {
 	cl_git_pass(p_unlink("stash/when"));
 
-	assert_status("what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
-	assert_status("how", GIT_STATUS_INDEX_MODIFIED);
-	assert_status("who", GIT_STATUS_WT_MODIFIED);
-	assert_status("when", GIT_ENOTFOUND);
-	assert_status("just.ignore", GIT_STATUS_IGNORED);
+	assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+	assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+	assert_status(repo, "when", GIT_ENOTFOUND);
+	assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
 
 	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
 
diff --git a/tests-clar/stash/stash_helpers.c b/tests/stash/stash_helpers.c
similarity index 72%
rename from tests-clar/stash/stash_helpers.c
rename to tests/stash/stash_helpers.c
index f462a13..8b7d685 100644
--- a/tests-clar/stash/stash_helpers.c
+++ b/tests/stash/stash_helpers.c
@@ -2,38 +2,8 @@
 #include "fileops.h"
 #include "stash_helpers.h"
 
-void commit_staged_files(
-	git_oid *commit_oid,
-	git_index *index,
-	git_signature *signature)
-{
-	git_tree *tree;
-	git_oid tree_oid;
-	git_repository *repo;
-
-	repo = git_index_owner(index);
-
-	cl_git_pass(git_index_write_tree(&tree_oid, index));
-
-	cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
-
-	cl_git_pass(git_commit_create_v(
-		commit_oid,
-		repo,
-		"HEAD",
-		signature,
-		signature,
-		NULL,
-		"Initial commit",
-		tree,
-		0));
-
-	git_tree_free(tree);
-}
-
 void setup_stash(git_repository *repo, git_signature *signature)
 {
-	git_oid commit_oid;
 	git_index *index;
 
 	cl_git_pass(git_repository_index(&index, repo));
@@ -50,9 +20,8 @@
 	cl_git_pass(git_index_add_bypath(index, "how"));
 	cl_git_pass(git_index_add_bypath(index, "who"));
 	cl_git_pass(git_index_add_bypath(index, ".gitignore"));
-	cl_git_pass(git_index_write(index));
 
-	commit_staged_files(&commit_oid, index, signature);
+	cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
 
 	cl_git_rewritefile("stash/what", "goodbye\n");			/* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
 	cl_git_rewritefile("stash/how", "not so small and\n");	/* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
@@ -66,3 +35,22 @@
 
 	git_index_free(index);
 }
+
+void assert_status(
+	git_repository *repo,
+	const char *path,
+	int status_flags)
+{
+	unsigned int status;
+	int error;
+
+	error = git_status_file(&status, repo, path);
+
+	if (status_flags < 0) {
+		cl_assert_equal_i(status_flags, error);
+		return;
+	}
+
+	cl_assert_equal_i(0, error);
+	cl_assert_equal_i((unsigned int)status_flags, status);
+}
diff --git a/tests/stash/stash_helpers.h b/tests/stash/stash_helpers.h
new file mode 100644
index 0000000..66d758f
--- /dev/null
+++ b/tests/stash/stash_helpers.h
@@ -0,0 +1,8 @@
+void setup_stash(
+	git_repository *repo,
+	git_signature *signature);
+
+void assert_status(
+	git_repository *repo,
+	const char *path,
+	int status_flags);
diff --git a/tests/stash/submodules.c b/tests/stash/submodules.c
new file mode 100644
index 0000000..137c440
--- /dev/null
+++ b/tests/stash/submodules.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "stash_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+static git_submodule *sm;
+
+void test_stash_submodules__initialize(void)
+{
+	cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+	repo = setup_fixture_submodules();
+
+	cl_git_pass(git_submodule_lookup(&sm, repo, "testrepo"));
+}
+
+void test_stash_submodules__cleanup(void)
+{
+	git_signature_free(signature);
+	signature = NULL;
+}
+
+void test_stash_submodules__does_not_stash_modified_submodules(void)
+{
+	static git_index *smindex;
+	static git_repository *smrepo;
+
+	assert_status(repo, "modified", GIT_STATUS_WT_MODIFIED);
+
+	/* modify file in submodule */
+	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+	assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+	/* add file to index in submodule */
+	cl_git_pass(git_submodule_open(&smrepo, sm));
+	cl_git_pass(git_repository_index(&smindex, smrepo));
+	cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+	/* commit changed index of submodule */
+	cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+	assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+	assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+	assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+	git_index_free(smindex);
+	git_repository_free(smrepo);
+}
+
+void test_stash_submodules__stash_is_empty_with_modified_submodules(void)
+{
+	static git_index *smindex;
+	static git_repository *smrepo;
+
+	cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+	assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+	/* modify file in submodule */
+	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+	assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+	/* add file to index in submodule */
+	cl_git_pass(git_submodule_open(&smrepo, sm));
+	cl_git_pass(git_repository_index(&smindex, smrepo));
+	cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+	/* commit changed index of submodule */
+	cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+	assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+	cl_git_fail_with(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT), GIT_ENOTFOUND);
+
+	git_index_free(smindex);
+	git_repository_free(smrepo);
+}
diff --git a/tests-clar/status/ignore.c b/tests/status/ignore.c
similarity index 80%
rename from tests-clar/status/ignore.c
rename to tests/status/ignore.c
index 4f6879c..acdc8fb 100644
--- a/tests-clar/status/ignore.c
+++ b/tests/status/ignore.c
@@ -459,3 +459,124 @@
 	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c"));
 	cl_assert(!ignored);
 }
+
+void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
+{
+	status_entry_single st;
+	char *test_cases[] = {
+		"!file",
+		"#blah",
+		"[blah]",
+		"[attr]",
+		"[attr]blah",
+		NULL
+	};
+	int i;
+
+	for (i = 0; *(test_cases + i) != NULL; i++) {
+		git_buf file = GIT_BUF_INIT;
+		char *file_name = *(test_cases + i);
+		git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+		cl_git_pass(git_buf_joinpath(&file, "empty_standard_repo", file_name));
+		cl_git_mkfile(git_buf_cstr(&file), "Please don't ignore me!");
+
+		memset(&st, 0, sizeof(st));
+		cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+		cl_assert(st.count == 1);
+		cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+		cl_git_pass(git_status_file(&st.status, repo, file_name));
+		cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+		cl_git_sandbox_cleanup();
+		git_buf_free(&file);
+	}
+}
+
+void test_status_ignore__issue_1766_negated_ignores(void)
+{
+	int ignored = 0;
+	unsigned int status;
+
+	g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+	cl_git_pass(git_futils_mkdir_r(
+		"empty_standard_repo/a", NULL, 0775));
+	cl_git_mkfile(
+		"empty_standard_repo/a/.gitignore", "*\n!.gitignore\n");
+	cl_git_mkfile(
+		"empty_standard_repo/a/ignoreme", "I should be ignored\n");
+
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
+	cl_assert(!ignored);
+
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
+	cl_assert(ignored);
+
+	cl_git_pass(git_futils_mkdir_r(
+		"empty_standard_repo/b", NULL, 0775));
+	cl_git_mkfile(
+		"empty_standard_repo/b/.gitignore", "*\n!.gitignore\n");
+	cl_git_mkfile(
+		"empty_standard_repo/b/ignoreme", "I should be ignored\n");
+
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/.gitignore"));
+	cl_assert(!ignored);
+
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/ignoreme"));
+	cl_assert(ignored);
+
+	/* shouldn't have changed results from first couple either */
+
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore"));
+	cl_assert(!ignored);
+	cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme"));
+	cl_assert(ignored);
+
+	/* status should find the two ignore files and nothing else */
+
+	cl_git_pass(git_status_file(&status, g_repo, "a/.gitignore"));
+	cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+	cl_git_pass(git_status_file(&status, g_repo, "a/ignoreme"));
+	cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+	cl_git_pass(git_status_file(&status, g_repo, "b/.gitignore"));
+	cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+	cl_git_pass(git_status_file(&status, g_repo, "b/ignoreme"));
+	cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+	{
+		git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+		status_entry_counts counts;
+		static const char *paths[] = {
+			"a/.gitignore",
+			"a/ignoreme",
+			"b/.gitignore",
+			"b/ignoreme",
+		};
+		static const unsigned int statuses[] = {
+			GIT_STATUS_WT_NEW,
+			GIT_STATUS_IGNORED,
+			GIT_STATUS_WT_NEW,
+			GIT_STATUS_IGNORED,
+		};
+
+		memset(&counts, 0x0, sizeof(status_entry_counts));
+		counts.expected_entry_count = 4;
+		counts.expected_paths = paths;
+		counts.expected_statuses = statuses;
+
+		opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+		cl_git_pass(git_status_foreach_ext(
+			g_repo, &opts, cb_status__normal, &counts));
+
+		cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+		cl_assert_equal_i(0, counts.wrong_status_flags_count);
+		cl_assert_equal_i(0, counts.wrong_sorted_path);
+	}
+}
+
diff --git a/tests-clar/status/renames.c b/tests/status/renames.c
similarity index 67%
rename from tests-clar/status/renames.c
rename to tests/status/renames.c
index 80ff260..16fd026 100644
--- a/tests-clar/status/renames.c
+++ b/tests/status/renames.c
@@ -11,6 +11,8 @@
 void test_status_renames__initialize(void)
 {
 	g_repo = cl_git_sandbox_init("renames");
+
+	cl_repo_set_bool(g_repo, "core.autocrlf", false);
 }
 
 void test_status_renames__cleanup(void)
@@ -67,14 +69,14 @@
 		actual = git_status_byindex(status_list, i);
 		expected = &expected_list[i];
 
-		cl_assert_equal_i((int)expected->status, (int)actual->status);
-
 		oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
 			actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
 
 		newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
 			actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
 
+		cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
+
 		if (oldname)
 			cl_assert(git__strcmp(oldname, expected->oldname) == 0);
 		else
@@ -153,6 +155,65 @@
 	git_index_free(index);
 }
 
+void test_status_renames__head2index_no_rename_from_rewrite(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+		{ GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+	rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+	rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+	cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+	cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+	cl_git_pass(git_index_write(index));
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 2);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
+void test_status_renames__head2index_rename_from_rewrite(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+		{ GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+	rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+	rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+	cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+	cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+	cl_git_pass(git_index_write(index));
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 2);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
 void test_status_renames__index2workdir_one(void)
 {
 	git_status_list *statuslist;
@@ -197,6 +258,32 @@
 	git_status_list_free(statuslist);
 }
 
+void test_status_renames__index2workdir_rename_from_rewrite(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+		{ GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+	rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+	rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 2);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
 void test_status_renames__both_one(void)
 {
 	git_index *index;
@@ -274,6 +361,91 @@
 	git_index_free(index);
 }
 
+
+void test_status_renames__both_rename_from_rewrite(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+		  "songof7cities.txt", "ikeepsix.txt" },
+		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+		  "ikeepsix.txt", "sixserving.txt" },
+		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+		  "sixserving.txt", "songof7cities.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+	rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+	rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
+	rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
+
+	cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+	cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+	cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+	cl_git_pass(git_index_write(index));
+
+	rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
+	rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
+	rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+	rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 3);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
+void test_status_renames__rewrites_only_for_renames(void)
+{
+	git_index *index;
+	git_status_list *statuslist;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_entry expected[] = {
+		{ GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+	};
+
+	opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+	opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	cl_git_rewritefile("renames/ikeepsix.txt",
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n" \
+		"This is enough content for the file to be rewritten.\n");
+
+	cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+	test_status(statuslist, expected, 1);
+	git_status_list_free(statuslist);
+
+	git_index_free(index);
+}
+
 void test_status_renames__both_casechange_one(void)
 {
 	git_index *index;
@@ -335,14 +507,14 @@
 		  "untimely.txt", "untimeliest.txt" }
 	};
 	struct status_entry expected_case[] = {
-		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
-		  GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
-		  "ikeepsix.txt", "ikeepsix.txt" },
-		{ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
-		  "sixserving.txt", "SixServing.txt" },
 		{ GIT_STATUS_INDEX_RENAMED |
 		  GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
 		  "songof7cities.txt", "SONGOF7.txt" },
+		{ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
+		  "sixserving.txt", "SixServing.txt" },
+		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+		  GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+		  "ikeepsix.txt", "ikeepsix.txt" },
 		{ GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
 		  "untimely.txt", "untimeliest.txt" }
 	};
diff --git a/tests-clar/status/single.c b/tests/status/single.c
similarity index 100%
rename from tests-clar/status/single.c
rename to tests/status/single.c
diff --git a/tests-clar/status/status_data.h b/tests/status/status_data.h
similarity index 68%
rename from tests-clar/status/status_data.h
rename to tests/status/status_data.h
index a41bde7..8ad4235 100644
--- a/tests-clar/status/status_data.h
+++ b/tests/status/status_data.h
@@ -1,5 +1,9 @@
 #include "status_helpers.h"
 
+// A utf-8 string with 83 characters, but 249 bytes.
+static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+
+
 /* entries for a plain copy of tests/resources/status */
 
 static const char *entry_paths0[] = {
@@ -250,3 +254,73 @@
 };
 
 static const int entry_count4 = 23;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the HEAD and the index (changes to be committed)
+ */
+
+static const char *entry_paths5[] = {
+	"staged_changes",
+	"staged_changes_file_deleted",
+	"staged_changes_modified_file",
+	"staged_delete_file_deleted",
+	"staged_delete_modified_file",
+	"staged_new_file",
+	"staged_new_file_deleted_file",
+	"staged_new_file_modified_file",
+};
+
+static const unsigned int entry_statuses5[] = {
+	GIT_STATUS_INDEX_MODIFIED,
+	GIT_STATUS_INDEX_MODIFIED,
+	GIT_STATUS_INDEX_MODIFIED,
+	GIT_STATUS_INDEX_DELETED,
+	GIT_STATUS_INDEX_DELETED,
+	GIT_STATUS_INDEX_NEW,
+	GIT_STATUS_INDEX_NEW,
+	GIT_STATUS_INDEX_NEW,
+};
+
+static const int entry_count5 = 8;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the workdir and the index (changes not staged, untracked files)
+ */
+
+static const char *entry_paths6[] = {
+	"file_deleted",
+	"ignored_file",
+	"modified_file",
+	"new_file",
+	"staged_changes_file_deleted",
+	"staged_changes_modified_file",
+	"staged_delete_modified_file",
+	"staged_new_file_deleted_file",
+	"staged_new_file_modified_file",
+	"subdir/deleted_file",
+	"subdir/modified_file",
+	"subdir/new_file",
+	"\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses6[] = {
+	GIT_STATUS_WT_DELETED,
+	GIT_STATUS_IGNORED,
+	GIT_STATUS_WT_MODIFIED,
+	GIT_STATUS_WT_NEW,
+	GIT_STATUS_WT_DELETED,
+	GIT_STATUS_WT_MODIFIED,
+	GIT_STATUS_WT_NEW,
+	GIT_STATUS_WT_DELETED,
+	GIT_STATUS_WT_MODIFIED,
+	GIT_STATUS_WT_DELETED,
+	GIT_STATUS_WT_MODIFIED,
+	GIT_STATUS_WT_NEW,
+	GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count6 = 13;
diff --git a/tests-clar/status/status_helpers.c b/tests/status/status_helpers.c
similarity index 100%
rename from tests-clar/status/status_helpers.c
rename to tests/status/status_helpers.c
diff --git a/tests-clar/status/status_helpers.h b/tests/status/status_helpers.h
similarity index 100%
rename from tests-clar/status/status_helpers.h
rename to tests/status/status_helpers.h
diff --git a/tests-clar/status/submodules.c b/tests/status/submodules.c
similarity index 94%
rename from tests-clar/status/submodules.c
rename to tests/status/submodules.c
index af87077..ef2888f 100644
--- a/tests-clar/status/submodules.c
+++ b/tests/status/submodules.c
@@ -9,25 +9,18 @@
 
 void test_status_submodules__initialize(void)
 {
-	g_repo = cl_git_sandbox_init("submodules");
-
-	cl_fixture_sandbox("testrepo.git");
-
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-
-	p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
 }
 
 void test_status_submodules__cleanup(void)
 {
-	cl_git_sandbox_cleanup();
-	cl_fixture_cleanup("testrepo.git");
 }
 
 void test_status_submodules__api(void)
 {
 	git_submodule *sm;
 
+	g_repo = setup_fixture_submodules();
+
 	cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
 
 	cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
@@ -42,6 +35,8 @@
 {
 	int counts = 0;
 
+	g_repo = setup_fixture_submodules();
+
 	cl_assert(git_path_isdir("submodules/.git"));
 	cl_assert(git_path_isdir("submodules/testrepo/.git"));
 	cl_assert(git_path_isfile("submodules/.gitmodules"));
@@ -86,6 +81,8 @@
 {
 	status_entry_counts counts;
 
+	g_repo = setup_fixture_submodules();
+
 	cl_assert(git_path_isdir("submodules/.git"));
 	cl_assert(git_path_isdir("submodules/testrepo/.git"));
 	cl_assert(git_path_isfile("submodules/.gitmodules"));
@@ -104,6 +101,7 @@
 void test_status_submodules__single_file(void)
 {
 	unsigned int status = 0;
+	g_repo = setup_fixture_submodules();
 	cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
 	cl_assert(!status);
 }
@@ -134,6 +132,8 @@
 		GIT_STATUS_WT_NEW
 	};
 
+	g_repo = setup_fixture_submodules();
+
 	cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
 	cl_git_pass(git_submodule_open(&smrepo, sm));
 
@@ -192,6 +192,8 @@
 		GIT_STATUS_WT_NEW
 	};
 
+	g_repo = setup_fixture_submodules();
+
 	cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
 	cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
 
diff --git a/tests-clar/status/worktree.c b/tests/status/worktree.c
similarity index 90%
rename from tests-clar/status/worktree.c
rename to tests/status/worktree.c
index 920671e..34be6d3 100644
--- a/tests-clar/status/worktree.c
+++ b/tests/status/worktree.c
@@ -40,6 +40,48 @@
 	cl_assert_equal_i(0, counts.wrong_sorted_path);
 }
 
+void assert_show(const int entry_counts, const char *entry_paths[],
+				 const unsigned int entry_statuses[], git_status_show_t show)
+{
+	status_entry_counts counts;
+	git_repository *repo = cl_git_sandbox_init("status");
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+	memset(&counts, 0x0, sizeof(status_entry_counts));
+	counts.expected_entry_count = entry_counts;
+	counts.expected_paths = entry_paths;
+	counts.expected_statuses = entry_statuses;
+
+	opts.flags = GIT_STATUS_OPT_DEFAULTS;
+	opts.show = show;
+
+	cl_git_pass(
+		git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+	);
+
+	cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+	cl_assert_equal_i(0, counts.wrong_status_flags_count);
+	cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__show_index_and_workdir(void)
+{
+	assert_show(entry_count0, entry_paths0, entry_statuses0,
+		GIT_STATUS_SHOW_INDEX_AND_WORKDIR);
+}
+
+void test_status_worktree__show_index_only(void)
+{
+	assert_show(entry_count5, entry_paths5, entry_statuses5,
+		GIT_STATUS_SHOW_INDEX_ONLY);
+}
+
+void test_status_worktree__show_workdir_only(void)
+{
+	assert_show(entry_count6, entry_paths6, entry_statuses6,
+		GIT_STATUS_SHOW_WORKDIR_ONLY);
+}
+
 /* this test is equivalent to t18-status.c:statuscb1 */
 void test_status_worktree__empty_repository(void)
 {
@@ -77,7 +119,7 @@
 
 	/* first purge the contents of the worktree */
 	cl_git_pass(git_buf_sets(&workdir, git_repository_workdir(repo)));
-	cl_git_pass(git_path_direach(&workdir, remove_file_cb, NULL));
+	cl_git_pass(git_path_direach(&workdir, 0, remove_file_cb, NULL));
 	git_buf_free(&workdir);
 
 	/* now get status */
@@ -428,16 +470,15 @@
 	cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
 
 	cl_git_pass(git_repository_index(&index, repo));
-
 	cl_git_pass(git_index_remove(index, "modified_file", 0));
-	cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
-		&our_entry, &their_entry));
+	cl_git_pass(git_index_conflict_add(
+		index, &ancestor_entry, &our_entry, &their_entry));
+	cl_git_pass(git_index_write(index));
+	git_index_free(index);
 
 	cl_git_pass(git_status_file(&status, repo, "modified_file"));
 
 	cl_assert_equal_i(GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW, status);
-
-	git_index_free(index);
 }
 
 static const char *filemode_paths[] = {
@@ -590,35 +631,12 @@
 
 static void stage_and_commit(git_repository *repo, const char *path)
 {
-	git_oid tree_oid, commit_oid;
-	git_tree *tree;
-	git_signature *signature;
 	git_index *index;
 
 	cl_git_pass(git_repository_index(&index, repo));
 	cl_git_pass(git_index_add_bypath(index, path));
-	cl_git_pass(git_index_write(index));
-
-	cl_git_pass(git_index_write_tree(&tree_oid, index));
+	cl_repo_commit_from_index(NULL, repo, NULL, 1323847743, "Initial commit\n");
 	git_index_free(index);
-
-	cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
-
-	cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
-
-	cl_git_pass(git_commit_create_v(
-		&commit_oid,
-		repo,
-		"HEAD",
-		signature,
-		signature,
-		NULL,
-		"Initial commit\n\0",
-		tree,
-		0));
-
-	git_tree_free(tree);
-	git_signature_free(signature);
 }
 
 static void assert_ignore_case(
@@ -823,3 +841,35 @@
 	cl_assert_equal_i(0, counts.wrong_status_flags_count);
 	cl_assert_equal_i(0, counts.wrong_sorted_path);
 }
+
+void test_status_worktree__long_filenames(void)
+{
+	char path[260*4+1];
+	const char *expected_paths[] = {path};
+	const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+	git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	status_entry_counts counts = {0};
+
+	// Create directory with amazingly long filename
+	sprintf(path, "empty_standard_repo/%s", longname);
+	cl_git_pass(git_futils_mkdir_r(path, NULL, 0777));
+	sprintf(path, "empty_standard_repo/%s/foo", longname);
+	cl_git_mkfile(path, "dummy");
+
+	sprintf(path, "%s/foo", longname);
+	counts.expected_entry_count = 1;
+	counts.expected_paths = expected_paths;
+	counts.expected_statuses = expected_statuses;
+
+	opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+	opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+	cl_git_pass(
+		git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+	cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+	cl_assert_equal_i(0, counts.wrong_status_flags_count);
+	cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
diff --git a/tests-clar/status/worktree_init.c b/tests/status/worktree_init.c
similarity index 100%
rename from tests-clar/status/worktree_init.c
rename to tests/status/worktree_init.c
diff --git a/tests/stress/diff.c b/tests/stress/diff.c
new file mode 100644
index 0000000..3d20926
--- /dev/null
+++ b/tests/stress/diff.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "../diff/diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_stress_diff__initialize(void)
+{
+}
+
+void test_stress_diff__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+#define ANOTHER_POEM \
+"OH, glorious are the guarded heights\nWhere guardian souls abide—\nSelf-exiled from our gross delights—\nAbove, beyond, outside:\nAn ampler arc their spirit swings—\nCommands a juster view—\nWe have their word for all these things,\nNo doubt their words are true.\n\nYet we, the bond slaves of our day,\nWhom dirt and danger press—\nCo-heirs of insolence, delay,\nAnd leagued unfaithfulness—\nSuch is our need must seek indeed\nAnd, having found, engage\nThe men who merely do the work\nFor which they draw the wage.\n\nFrom forge and farm and mine and bench,\nDeck, altar, outpost lone—\nMill, school, battalion, counter, trench,\nRail, senate, sheepfold, throne—\nCreation's cry goes up on high\nFrom age to cheated age:\n\"Send us the men who do the work\n\"For which they draw the wage!\"\n"
+
+static void test_with_many(int expected_new)
+{
+	git_index *index;
+	git_tree *tree, *new_tree;
+	git_diff *diff = NULL;
+	diff_expects exp;
+	git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+	git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+	cl_git_pass(
+		git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
+	cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+	cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
+	cl_git_pass(git_index_write(index));
+
+	cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, NULL, NULL, &exp));
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(expected_new + 2, exp.files);
+
+	opts.flags = GIT_DIFF_FIND_ALL;
+	cl_git_pass(git_diff_find_similar(diff, &opts));
+
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, NULL, NULL, &exp));
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+	cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(expected_new + 1, exp.files);
+
+	git_diff_free(diff);
+
+	cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "yoyoyo");
+	cl_git_pass(git_revparse_single(
+		(git_object **)&new_tree, g_repo, "HEAD^{tree}"));
+
+	cl_git_pass(git_diff_tree_to_tree(
+		&diff, g_repo, tree, new_tree, &diffopts));
+
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, NULL, NULL, &exp));
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+	cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(expected_new + 2, exp.files);
+
+	opts.flags = GIT_DIFF_FIND_ALL;
+	cl_git_pass(git_diff_find_similar(diff, &opts));
+
+	memset(&exp, 0, sizeof(exp));
+	cl_git_pass(git_diff_foreach(
+		diff, diff_file_cb, NULL, NULL, &exp));
+	cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+	cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+	cl_assert_equal_i(expected_new + 1, exp.files);
+
+	git_diff_free(diff);
+
+	git_tree_free(new_tree);
+	git_tree_free(tree);
+	git_index_free(index);
+}
+
+void test_stress_diff__rename_big_files(void)
+{
+	git_index *index;
+	char tmp[64];
+	int i, j;
+	git_buf b = GIT_BUF_INIT;
+
+	g_repo = cl_git_sandbox_init("renames");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	for (i = 0; i < 100; i += 1) {
+		snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+		for (j = i * 256; j > 0; --j)
+			git_buf_printf(&b, "more content %d\n", i);
+		cl_git_mkfile(tmp, b.ptr);
+	}
+
+	for (i = 0; i < 100; i += 1) {
+		snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+		cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+	}
+
+	git_buf_free(&b);
+	git_index_free(index);
+
+	test_with_many(100);
+}
+
+void test_stress_diff__rename_many_files(void)
+{
+	git_index *index;
+	char tmp[64];
+	int i;
+	git_buf b = GIT_BUF_INIT;
+
+	g_repo = cl_git_sandbox_init("renames");
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	git_buf_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0);
+
+	for (i = 0; i < 2500; i += 1) {
+		snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+		snprintf(b.ptr, 9, "%08d", i);
+		b.ptr[8] = '\n';
+		cl_git_mkfile(tmp, b.ptr);
+	}
+	git_buf_free(&b);
+
+	for (i = 0; i < 2500; i += 1) {
+		snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+		cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+	}
+
+	git_index_free(index);
+
+	test_with_many(2500);
+}
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
new file mode 100644
index 0000000..5f320e7
--- /dev/null
+++ b/tests/submodule/lookup.c
@@ -0,0 +1,172 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "posix.h"
+#include "git2/sys/repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_lookup__initialize(void)
+{
+	g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_lookup__simple_lookup(void)
+{
+	git_submodule *sm;
+
+	/* lookup existing */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is not in HEAD */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is neither in HEAD nor index */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+	cl_assert(sm);
+
+	/* lookup git repo subdir that is not added as submodule */
+	cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS);
+
+	/* lookup existing directory that is not a submodule */
+	cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND);
+
+	/* lookup existing file that is not a submodule */
+	cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND);
+
+	/* lookup non-existent item */
+	cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND);
+}
+
+void test_submodule_lookup__accessors(void)
+{
+	git_submodule *sm;
+	const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_assert(git_submodule_owner(sm) == g_repo);
+	cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+	cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+	cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+	cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+	cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+	cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
+
+	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+		"3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
+
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+	cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
+
+	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+	cl_assert(git_submodule_head_id(sm) == NULL);
+	cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+	cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
+
+	cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+	cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+		"5e4963595a9774b90524d35a807169049de8ccad") == 0);
+}
+
+typedef struct {
+	int count;
+} sm_lookup_data;
+
+static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
+{
+	sm_lookup_data *data = payload;
+	data->count += 1;
+	cl_assert_equal_s(git_submodule_name(sm), name);
+	return 0;
+}
+
+void test_submodule_lookup__foreach(void)
+{
+	sm_lookup_data data;
+	memset(&data, 0, sizeof(data));
+	cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+	cl_assert_equal_i(8, data.count);
+}
+
+void test_submodule_lookup__lookup_even_with_unborn_head(void)
+{
+	git_reference *head;
+	git_submodule *sm;
+
+	/* put us on an unborn branch */
+	cl_git_pass(git_reference_symbolic_create(
+		&head, g_repo, "HEAD", "refs/heads/garbage", 1));
+	git_reference_free(head);
+
+	/* lookup existing */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is not in HEAD */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is neither in HEAD nor index */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+	cl_assert(sm);
+
+	/* lookup git repo subdir that is not added as submodule */
+	cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
+
+	/* lookup existing directory that is not a submodule */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+
+	/* lookup existing file that is not a submodule */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
+
+	/* lookup non-existent item */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
+}
+
+void test_submodule_lookup__lookup_even_with_missing_index(void)
+{
+	git_index *idx;
+	git_submodule *sm;
+
+	/* give the repo an empty index */
+	cl_git_pass(git_index_new(&idx));
+	git_repository_set_index(g_repo, idx);
+	git_index_free(idx);
+
+	/* lookup existing */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is not in HEAD */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+	cl_assert(sm);
+
+	/* lookup pending change in .gitmodules that is neither in HEAD nor index */
+	cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+	cl_assert(sm);
+
+	/* lookup git repo subdir that is not added as submodule */
+	cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule"));
+
+	/* lookup existing directory that is not a submodule */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir"));
+
+	/* lookup existing file that is not a submodule */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file"));
+
+	/* lookup non-existent item */
+	cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file"));
+}
diff --git a/tests-clar/submodule/modify.c b/tests/submodule/modify.c
similarity index 91%
rename from tests-clar/submodule/modify.c
rename to tests/submodule/modify.c
index 94eb373..e326287 100644
--- a/tests-clar/submodule/modify.c
+++ b/tests/submodule/modify.c
@@ -11,20 +11,7 @@
 
 void test_submodule_modify__initialize(void)
 {
-	g_repo = cl_git_sandbox_init("submod2");
-
-	cl_fixture_sandbox("submod2_target");
-	p_rename("submod2_target/.gitted", "submod2_target/.git");
-
-	/* must create submod2_target before rewrite so prettify will work */
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-}
-
-void test_submodule_modify__cleanup(void)
-{
-	cl_git_sandbox_cleanup();
-	cl_fixture_cleanup("submod2_target");
+	g_repo = setup_fixture_submod2();
 }
 
 void test_submodule_modify__add(void)
@@ -204,10 +191,10 @@
 	cl_git_pass(git_submodule_set_url(sm1, old_url));
 	cl_assert_equal_i(
 		(int)GIT_SUBMODULE_IGNORE_UNTRACKED,
-		(int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT));
+		(int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET));
 	cl_assert_equal_i(
 		(int)GIT_SUBMODULE_UPDATE_REBASE,
-		(int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT));
+		(int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET));
 	cl_assert_equal_i(
 		1, git_submodule_set_fetch_recurse_submodules(sm1, old_fetchrecurse));
 
@@ -228,10 +215,10 @@
 	cl_git_pass(git_submodule_save(sm1));
 
 	/* attempt to "revert" values */
-	git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_DEFAULT);
-	git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_DEFAULT);
+	git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET);
+	git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET);
 
-	/* but ignore and update should NOT revert because the DEFAULT
+	/* but ignore and update should NOT revert because the RESET
 	 * should now be the newly saved value...
 	 */
 	cl_assert_equal_i(
diff --git a/tests-clar/submodule/status.c b/tests/submodule/status.c
similarity index 92%
rename from tests-clar/submodule/status.c
rename to tests/submodule/status.c
index 68110bd..f5111c8 100644
--- a/tests-clar/submodule/status.c
+++ b/tests/submodule/status.c
@@ -9,21 +9,11 @@
 
 void test_submodule_status__initialize(void)
 {
-	g_repo = cl_git_sandbox_init("submod2");
-
-	cl_fixture_sandbox("submod2_target");
-	p_rename("submod2_target/.gitted", "submod2_target/.git");
-
-	/* must create submod2_target before rewrite so prettify will work */
-	rewrite_gitmodules(git_repository_workdir(g_repo));
-	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
-	p_rename("submod2/not/.gitted", "submod2/not/.git");
+	g_repo = setup_fixture_submod2();
 }
 
 void test_submodule_status__cleanup(void)
 {
-	cl_git_sandbox_cleanup();
-	cl_fixture_cleanup("submod2_target");
 }
 
 void test_submodule_status__unchanged(void)
@@ -326,6 +316,7 @@
 typedef struct {
 	size_t counter;
 	const char **paths;
+	int *statuses;
 } submodule_expectations;
 
 static int confirm_submodule_status(
@@ -336,6 +327,7 @@
 	while (git__suffixcmp(exp->paths[exp->counter], "/") == 0)
 		exp->counter++;
 
+	cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags);
 	cl_assert_equal_s(exp->paths[exp->counter++], path);
 
 	GIT_UNUSED(status_flags);
@@ -365,7 +357,24 @@
 		"sm_unchanged",
 		NULL
 	};
-	submodule_expectations exp = { 0, expected };
+	static int expected_flags[] = {
+		GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */
+		0,					    /* "just_a_dir/" will be skipped */
+		GIT_STATUS_CURRENT,     /* "just_a_dir/contents" */
+		GIT_STATUS_CURRENT,	    /* "just_a_file" */
+		GIT_STATUS_IGNORED,	    /* "not" (contains .git) */
+		GIT_STATUS_IGNORED,     /* "not-submodule" (contains .git) */
+		GIT_STATUS_CURRENT,     /* "README.txt */
+		GIT_STATUS_INDEX_NEW,   /* "sm_added_and_uncommited" */
+		GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */
+		GIT_STATUS_WT_MODIFIED, /* "sm_changed_head" */
+		GIT_STATUS_WT_MODIFIED, /* "sm_changed_index" */
+		GIT_STATUS_WT_MODIFIED, /* "sm_changed_untracked_file" */
+		GIT_STATUS_WT_MODIFIED, /* "sm_missing_commits" */
+		GIT_STATUS_CURRENT,     /* "sm_unchanged" */
+		0
+	};
+	submodule_expectations exp = { 0, expected, expected_flags };
 	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
 
 	cl_git_pass(git_iterator_for_workdir(&iter, g_repo,
@@ -378,7 +387,9 @@
 
 	opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
 		GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
-		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+		GIT_STATUS_OPT_INCLUDE_IGNORED |
+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+		GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
 
 	cl_git_pass(git_status_foreach_ext(
 		g_repo, &opts, confirm_submodule_status, &exp));
diff --git a/tests-clar/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
similarity index 65%
rename from tests-clar/submodule/submodule_helpers.c
rename to tests/submodule/submodule_helpers.c
index 0c3e79f..d575067 100644
--- a/tests-clar/submodule/submodule_helpers.c
+++ b/tests/submodule/submodule_helpers.c
@@ -4,6 +4,7 @@
 #include "util.h"
 #include "posix.h"
 #include "submodule_helpers.h"
+#include "git2/sys/repository.h"
 
 /* rewrite gitmodules -> .gitmodules
  * rewrite the empty or relative urls inside each module
@@ -82,3 +83,45 @@
 	git_buf_free(&out_f);
 	git_buf_free(&path);
 }
+
+static void cleanup_fixture_submodules(void *payload)
+{
+	cl_git_sandbox_cleanup(); /* either "submodules" or "submod2" */
+
+	if (payload)
+		cl_fixture_cleanup(payload);
+}
+
+git_repository *setup_fixture_submodules(void)
+{
+	git_repository *repo = cl_git_sandbox_init("submodules");
+
+	cl_fixture_sandbox("testrepo.git");
+
+	rewrite_gitmodules(git_repository_workdir(repo));
+	p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
+
+	cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+	cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+	return repo;
+}
+
+git_repository *setup_fixture_submod2(void)
+{
+	git_repository *repo = cl_git_sandbox_init("submod2");
+
+	cl_fixture_sandbox("submod2_target");
+	p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+	rewrite_gitmodules(git_repository_workdir(repo));
+	p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
+	p_rename("submod2/not/.gitted", "submod2/not/.git");
+
+	cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
+
+	cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+	return repo;
+}
diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h
new file mode 100644
index 0000000..610c407
--- /dev/null
+++ b/tests/submodule/submodule_helpers.h
@@ -0,0 +1,5 @@
+extern void rewrite_gitmodules(const char *workdir);
+
+/* these will automatically set a cleanup callback */
+extern git_repository *setup_fixture_submodules(void);
+extern git_repository *setup_fixture_submod2(void);
diff --git a/tests/threads/basic.c b/tests/threads/basic.c
new file mode 100644
index 0000000..a329ee7
--- /dev/null
+++ b/tests/threads/basic.c
@@ -0,0 +1,36 @@
+#include "clar_libgit2.h"
+
+#include "cache.h"
+
+
+static git_repository *g_repo;
+
+void test_threads_basic__initialize(void)
+{
+	g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_threads_basic__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+}
+
+
+void test_threads_basic__cache(void)
+{
+	// run several threads polling the cache at the same time
+	cl_assert(1 == 1);
+}
+
+void test_threads_basic__multiple_init(void)
+{
+	git_repository *nested_repo;
+
+	git_threads_init();
+	cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+	git_repository_free(nested_repo);
+
+	git_threads_shutdown();
+	cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+	git_repository_free(nested_repo);
+}
diff --git a/tests/threads/refdb.c b/tests/threads/refdb.c
new file mode 100644
index 0000000..3c651e3
--- /dev/null
+++ b/tests/threads/refdb.c
@@ -0,0 +1,213 @@
+#include "clar_libgit2.h"
+#include "git2/refdb.h"
+#include "refdb.h"
+
+static git_repository *g_repo;
+static int g_expected = 0;
+
+void test_threads_refdb__initialize(void)
+{
+	g_repo = NULL;
+}
+
+void test_threads_refdb__cleanup(void)
+{
+	cl_git_sandbox_cleanup();
+	g_repo = NULL;
+}
+
+#define REPEAT 20
+#define THREADS 20
+
+static void *iterate_refs(void *arg)
+{
+	git_reference_iterator *i;
+	git_reference *ref;
+	int count = 0;
+
+	cl_git_pass(git_reference_iterator_new(&i, g_repo));
+
+	for (count = 0; !git_reference_next(&ref, i); ++count) {
+		cl_assert(ref != NULL);
+		git_reference_free(ref);
+	}
+
+	if (g_expected > 0)
+		cl_assert_equal_i(g_expected, count);
+
+	git_reference_iterator_free(i);
+
+	return arg;
+}
+
+void test_threads_refdb__iterator(void)
+{
+	int r, t;
+	git_thread th[THREADS];
+	int id[THREADS];
+	git_oid head;
+	git_reference *ref;
+	char name[128];
+	git_refdb *refdb;
+
+	g_repo = cl_git_sandbox_init("testrepo2");
+
+	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+	/* make a bunch of references */
+
+	for (r = 0; r < 200; ++r) {
+		snprintf(name, sizeof(name), "refs/heads/direct-%03d", r);
+		cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+		git_reference_free(ref);
+	}
+
+	cl_git_pass(git_repository_refdb(&refdb, g_repo));
+	cl_git_pass(git_refdb_compress(refdb));
+	git_refdb_free(refdb);
+
+	g_expected = 206;
+
+	for (r = 0; r < REPEAT; ++r) {
+		g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
+
+		for (t = 0; t < THREADS; ++t) {
+			id[t] = t;
+#ifdef GIT_THREADS
+			cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+#else
+			th[t] = t;
+			iterate_refs(&id[t]);
+#endif
+		}
+
+#ifdef GIT_THREADS
+		for (t = 0; t < THREADS; ++t) {
+			cl_git_pass(git_thread_join(th[t], NULL));
+		}
+#endif
+
+		memset(th, 0, sizeof(th));
+	}
+}
+
+static void *create_refs(void *arg)
+{
+	int *id = arg, i;
+	git_oid head;
+	char name[128];
+	git_reference *ref[10];
+
+	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+	for (i = 0; i < 10; ++i) {
+		snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i);
+		cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0));
+
+		if (i == 5) {
+			git_refdb *refdb;
+			cl_git_pass(git_repository_refdb(&refdb, g_repo));
+			cl_git_pass(git_refdb_compress(refdb));
+			git_refdb_free(refdb);
+		}
+	}
+
+	for (i = 0; i < 10; ++i)
+		git_reference_free(ref[i]);
+
+	return arg;
+}
+
+static void *delete_refs(void *arg)
+{
+	int *id = arg, i;
+	git_reference *ref;
+	char name[128];
+
+	for (i = 0; i < 10; ++i) {
+		snprintf(
+			name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i);
+
+		if (!git_reference_lookup(&ref, g_repo, name)) {
+			cl_git_pass(git_reference_delete(ref));
+			git_reference_free(ref);
+		}
+
+		if (i == 5) {
+			git_refdb *refdb;
+			cl_git_pass(git_repository_refdb(&refdb, g_repo));
+			cl_git_pass(git_refdb_compress(refdb));
+			git_refdb_free(refdb);
+		}
+	}
+
+	return arg;
+}
+
+void test_threads_refdb__edit_while_iterate(void)
+{
+	int r, t;
+	int id[THREADS];
+	git_oid head;
+	git_reference *ref;
+	char name[128];
+	git_refdb *refdb;
+
+#ifdef GIT_THREADS
+	git_thread th[THREADS];
+#endif
+
+	g_repo = cl_git_sandbox_init("testrepo2");
+
+	cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+	/* make a bunch of references */
+
+	for (r = 0; r < 50; ++r) {
+		snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
+		cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0));
+		git_reference_free(ref);
+	}
+
+	cl_git_pass(git_repository_refdb(&refdb, g_repo));
+	cl_git_pass(git_refdb_compress(refdb));
+	git_refdb_free(refdb);
+
+	g_expected = -1;
+
+	g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
+
+	for (t = 0; t < THREADS; ++t) {
+		void *(*fn)(void *arg);
+
+		switch (t & 0x3) {
+		case 0:  fn = create_refs;  break;
+		case 1:  fn = delete_refs;  break;
+		default: fn = iterate_refs; break;
+		}
+
+		id[t] = t;
+#ifdef GIT_THREADS
+		cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t]));
+#else
+		fn(&id[t]);
+#endif
+	}
+
+#ifdef GIT_THREADS
+	for (t = 0; t < THREADS; ++t) {
+		cl_git_pass(git_thread_join(th[t], NULL));
+	}
+
+	memset(th, 0, sizeof(th));
+
+	for (t = 0; t < THREADS; ++t) {
+		id[t] = t;
+		cl_git_pass(git_thread_create(&th[t], NULL, iterate_refs, &id[t]));
+	}
+
+	for (t = 0; t < THREADS; ++t) {
+		cl_git_pass(git_thread_join(th[t], NULL));
+	}
+#endif
+}
diff --git a/tests-clar/trace/trace.c b/tests/trace/trace.c
similarity index 100%
rename from tests-clar/trace/trace.c
rename to tests/trace/trace.c
diff --git a/tests-clar/valgrind-supp-mac.txt b/tests/valgrind-supp-mac.txt
similarity index 87%
rename from tests-clar/valgrind-supp-mac.txt
rename to tests/valgrind-supp-mac.txt
index fcc7ede..99833d0 100644
--- a/tests-clar/valgrind-supp-mac.txt
+++ b/tests/valgrind-supp-mac.txt
@@ -154,3 +154,31 @@
 	fun:printf
 	fun:clar_print_init
 }
+{
+	molo-1
+	Memcheck:Leak
+	fun:malloc_zone_malloc
+	...
+	fun:_objc_init
+}
+{
+	molo-2
+	Memcheck:Leak
+	fun:malloc_zone_calloc
+	...
+	fun:_objc_init
+}
+{
+	molo-3
+	Memcheck:Leak
+	fun:malloc
+	...
+	fun:_objc_init
+}
+{
+	molo-4
+	Memcheck:Leak
+	fun:malloc
+	...
+	fun:dyld_register_image_state_change_handler
+}