Merge pull request #332 from ezwiebel/rebase-wrapper

Rebase wrapper
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..8eb5872
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/libgit2"]
+	path = vendor/libgit2
+	url = https://github.com/libgit2/libgit2
diff --git a/.travis.yml b/.travis.yml
index f796389..d9130bc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,13 +5,17 @@
 install: ./script/install-libgit2.sh
 
 go:
-  - 1.1
-  - 1.2
-  - 1.3
-  - 1.4
   - 1.5
+  - 1.6
+  - 1.7
   - tip
 
 matrix:
   allow_failures:
     - go: tip
+
+branches:
+  only:
+  - master
+  - /v\d+/
+  - next
diff --git a/Makefile b/Makefile
index 9c42283..39fc558 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,11 @@
 default: test
 
-test:
+build-libgit2:
+	./script/build-libgit2-static.sh
+
+test: build-libgit2
 	go run script/check-MakeGitError-thread-lock.go
 	go test ./...
 
-install:
+install: build-libgit2
 	go install ./...
diff --git a/README.md b/README.md
index 315032f..4ecfa34 100644
--- a/README.md
+++ b/README.md
@@ -2,57 +2,59 @@
 ======
 [![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go) [![Build Status](https://travis-ci.org/libgit2/git2go.svg?branch=master)](https://travis-ci.org/libgit2/git2go)
 
+Go bindings for [libgit2](http://libgit2.github.com/).
 
-Go bindings for [libgit2](http://libgit2.github.com/). The `master` branch follows the latest libgit2 release. The versioned branches indicate which libgit2 version they work against.
+### Which branch to use
+
+The numbered branches work against the version of libgit2 as specified by their number. You can import them in your project via gopkg.in, e.g. if you have libgit2 v0.25 installed you'd import with
+
+    import "gopkg.in/libgit2/git2go.v25"
+
+which will ensure there are no sudden changes to the API.
+
+The `master` branch follows the tip of libgit2 itself (with some lag) and as such has no guarantees on its own API nor does it have expectations the stability of libgit2's. Thus this only supports statically linking against libgit2.
 
 Installing
 ----------
 
-This project wraps the functionality provided by libgit2. If you're using a stable version, install it to your system via your system's package manager and then install git2go as usual.
+This project wraps the functionality provided by libgit2. It thus needs it in order to perform the work.
 
-Otherwise (`next` which tracks an unstable version), we need to build libgit2 as well. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively.
+This project wraps the functionality provided by libgit2. If you're using a versioned branch, install it to your system via your system's package manager and then install git2go.
 
-### Stable version
 
-git2go has `master` which tracks the latest release of libgit2, and versioned branches which indicate which version of libgit2 they work against. Install the development package on your system via your favourite package manager or from source and you can use a service like gopkg.in to use the appropriate version. For the libgit2 v0.22 case, you can use
+### Versioned branch, dynamic linking
 
-    import "gopkg.in/libgit2/git2go.v22"
+When linking dynamically against a released version of libgit2, install it via your system's package manager. CGo will take care of finding its pkg-config file and set up the linking. Import via gopkg.in, e.g. to work against libgit2 v0.25
 
-to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library. You can use
+    import "gopkg.in/libgit2/git2go.v25"
 
-    import "github.com/libgit2/git2go"
+### Master branch, or static linking
 
-to use the version which works against the latest release.
-
-### From `next`
-
-The `next` branch follows libgit2's master branch, which means there is no stable API or ABI to link against. git2go can statically link against a vendored version of libgit2.
+If using `master` or building a branch statically, we need to build libgit2 first. In order to build it, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL (outside of Windows or macOS) and LibSSH2 installed if you want libgit2 to support HTTPS and SSH respectively. Note that even if libgit2 is included in the resulting binary, its dependencies will not be.
 
 Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` directory. From there, we need to build the C code and put it into the resulting go binary.
 
-    git checkout next
     git submodule update --init # get libgit2
     make install
 
-will compile libgit2. Run `go install` so that it's statically linked to the git2go package.
+will compile libgit2, link it into git2go and install it.
 
 Parallelism and network operations
 ----------------------------------
 
-libgit2 uses OpenSSL and LibSSH2 for performing encrypted network connections. For now, git2go asks libgit2 to set locking for OpenSSL. This makes HTTPS connections thread-safe, but it is fragile and will likely stop doing it soon. This may also make SSH connections thread-safe if your copy of libssh2 is linked against OpenSSL. Check libgit2's `THREADSAFE.md` for more information.
+libgit2 may use OpenSSL and LibSSH2 for performing encrypted network connections. For now, git2go asks libgit2 to set locking for OpenSSL. This makes HTTPS connections thread-safe, but it is fragile and will likely stop doing it soon. This may also make SSH connections thread-safe if your copy of libssh2 is linked against OpenSSL. Check libgit2's `THREADSAFE.md` for more information.
 
 Running the tests
 -----------------
 
-For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
+For the stable version, `go test` will work as usual. For the `next` branch, similarly to installing, running the tests requires building a local libgit2 library, so the Makefile provides a wrapper that makes sure it's built
 
     make test
 
-Alternatively, if you want to pass arguments to `go test`, you can use the script that sets it all up
+Alternatively, you can build the library manually first and then run the tests
 
-    ./script/with-static.sh go test -v
-
-which will run the specified arguments with the correct environment variables.
+    ./script/build-libgit2-static.sh
+    go test -v
 
 License
 -------
diff --git a/blame_test.go b/blame_test.go
index a2a4d38..ec96af7 100644
--- a/blame_test.go
+++ b/blame_test.go
@@ -6,6 +6,7 @@
 )
 
 func TestBlame(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/blob.go b/blob.go
index 1a86e60..73a4a19 100644
--- a/blob.go
+++ b/blob.go
@@ -4,15 +4,13 @@
 #include <git2.h>
 #include <string.h>
 
-extern int _go_git_blob_create_fromchunks(git_oid *id,
-	git_repository *repo,
-	const char *hintpath,
-	void *payload);
-
+int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len);
+void _go_git_writestream_free(git_writestream *stream);
 */
 import "C"
 import (
 	"io"
+	"reflect"
 	"runtime"
 	"unsafe"
 )
@@ -37,15 +35,24 @@
 	defer runtime.UnlockOSThread()
 
 	var id C.git_oid
-	var ptr unsafe.Pointer
+	var size C.size_t
 
+	// Go 1.6 added some increased checking of passing pointer to
+	// C, but its check depends on its expectations of waht we
+	// pass to the C function, so unless we take the address of
+	// its contents at the call site itself, it can fail when
+	// 'data' is a slice of a slice.
+	//
+	// When we're given an empty slice, create a dummy one where 0
+	// isn't out of bounds.
 	if len(data) > 0 {
-		ptr = unsafe.Pointer(&data[0])
+		size = C.size_t(len(data))
 	} else {
-		ptr = unsafe.Pointer(nil)
+		data = []byte{0}
+		size = C.size_t(0)
 	}
 
-	ecode := C.git_blob_create_frombuffer(&id, repo.ptr, ptr, C.size_t(len(data)))
+	ecode := C.git_blob_create_frombuffer(&id, repo.ptr, unsafe.Pointer(&data[0]), size)
 	if ecode < 0 {
 		return nil, MakeGitError(ecode)
 	}
@@ -78,27 +85,71 @@
 	return len(goBuf)
 }
 
-func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunkCallback) (*Oid, error) {
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
+func (repo *Repository) CreateFromStream(hintPath string) (*BlobWriteStream, error) {
 	var chintPath *C.char = nil
+	var stream *C.git_writestream
+
 	if len(hintPath) > 0 {
 		chintPath = C.CString(hintPath)
 		defer C.free(unsafe.Pointer(chintPath))
 	}
-	oid := C.git_oid{}
 
-	payload := &BlobCallbackData{Callback: callback}
-	handle := pointerHandles.Track(payload)
-	defer pointerHandles.Untrack(handle)
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
 
-	ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle)
-	if payload.Error != nil {
-		return nil, payload.Error
-	}
+	ecode := C.git_blob_create_fromstream(&stream, repo.ptr, chintPath)
 	if ecode < 0 {
 		return nil, MakeGitError(ecode)
 	}
+
+	return newBlobWriteStreamFromC(stream), nil
+}
+
+type BlobWriteStream struct {
+	ptr *C.git_writestream
+}
+
+func newBlobWriteStreamFromC(ptr *C.git_writestream) *BlobWriteStream {
+	stream := &BlobWriteStream{
+		ptr: ptr,
+	}
+
+	runtime.SetFinalizer(stream, (*BlobWriteStream).Free)
+	return stream
+}
+
+// Implement io.Writer
+func (stream *BlobWriteStream) Write(p []byte) (int, error) {
+	header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
+	ptr := (*C.char)(unsafe.Pointer(header.Data))
+	size := C.size_t(header.Len)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C._go_git_writestream_write(stream.ptr, ptr, size)
+	if ecode < 0 {
+		return 0, MakeGitError(ecode)
+	}
+
+	return len(p), nil
+}
+
+func (stream *BlobWriteStream) Free() {
+	runtime.SetFinalizer(stream, nil)
+	C._go_git_writestream_free(stream.ptr)
+}
+
+func (stream *BlobWriteStream) Commit() (*Oid, error) {
+	oid := C.git_oid{}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_blob_create_fromstream_commit(&oid, stream.ptr)
+	if ecode < 0 {
+		return nil, MakeGitError(ecode)
+	}
+
 	return newOidFromC(&oid), nil
 }
diff --git a/blob_test.go b/blob_test.go
index 2b5ec4f..815ab3d 100644
--- a/blob_test.go
+++ b/blob_test.go
@@ -1,10 +1,23 @@
 package git
 
 import (
+	"bytes"
 	"testing"
 )
 
+type bufWrapper struct {
+	buf     [64]byte
+	pointer []byte
+}
+
+func doublePointerBytes() []byte {
+	o := &bufWrapper{}
+	o.pointer = o.buf[0:10]
+	return o.pointer[0:1]
+}
+
 func TestCreateBlobFromBuffer(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -14,4 +27,16 @@
 	if id.String() != "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391" {
 		t.Fatal("Empty buffer did not deliver empty blob id")
 	}
+
+	for _, data := range []([]byte){[]byte("hello there"), doublePointerBytes()} {
+		id, err = repo.CreateBlobFromBuffer(data)
+		checkFatal(t, err)
+
+		blob, err := repo.LookupBlob(id)
+		checkFatal(t, err)
+		if !bytes.Equal(blob.Contents(), data) {
+			t.Fatal("Loaded bytes don't match original bytes:",
+				blob.Contents(), "!=", data)
+		}
+	}
 }
diff --git a/branch.go b/branch.go
index df72dba..d381c23 100644
--- a/branch.go
+++ b/branch.go
@@ -13,6 +13,7 @@
 type BranchType uint
 
 const (
+	BranchAll    BranchType = C.GIT_BRANCH_ALL
 	BranchLocal  BranchType = C.GIT_BRANCH_LOCAL
 	BranchRemote BranchType = C.GIT_BRANCH_REMOTE
 )
@@ -72,6 +73,10 @@
 		}
 	}
 
+	if err != nil && IsErrorCode(err, ErrIterOver) {
+		return nil
+	}
+
 	return err
 }
 
diff --git a/branch_test.go b/branch_test.go
index a0834a8..01a2e28 100644
--- a/branch_test.go
+++ b/branch_test.go
@@ -5,6 +5,7 @@
 )
 
 func TestBranchIterator(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -27,6 +28,7 @@
 }
 
 func TestBranchIteratorEach(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/checkout.go b/checkout.go
index a2e312b..f5822c9 100644
--- a/checkout.go
+++ b/checkout.go
@@ -2,6 +2,8 @@
 
 /*
 #include <git2.h>
+
+extern void _go_git_populate_checkout_cb(git_checkout_options *opts);
 */
 import "C"
 import (
@@ -10,9 +12,18 @@
 	"unsafe"
 )
 
+type CheckoutNotifyType uint
 type CheckoutStrategy uint
 
 const (
+	CheckoutNotifyNone      CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_NONE
+	CheckoutNotifyConflict  CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_CONFLICT
+	CheckoutNotifyDirty     CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_DIRTY
+	CheckoutNotifyUpdated   CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UPDATED
+	CheckoutNotifyUntracked CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_UNTRACKED
+	CheckoutNotifyIgnored   CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_IGNORED
+	CheckoutNotifyAll       CheckoutNotifyType = C.GIT_CHECKOUT_NOTIFY_ALL
+
 	CheckoutNone                      CheckoutStrategy = C.GIT_CHECKOUT_NONE                         // Dry run, no actual updates
 	CheckoutSafe                      CheckoutStrategy = C.GIT_CHECKOUT_SAFE                         // Allow safe updates that cannot overwrite uncommitted data
 	CheckoutForce                     CheckoutStrategy = C.GIT_CHECKOUT_FORCE                        // Allow all updates to force working directory to look like index
@@ -37,15 +48,21 @@
 	CheckoutUpdateSubmodulesIfChanged CheckoutStrategy = C.GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED // Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED)
 )
 
+type CheckoutNotifyCallback func(why CheckoutNotifyType, path string, baseline, target, workdir DiffFile) ErrorCode
+type CheckoutProgressCallback func(path string, completed, total uint) ErrorCode
+
 type CheckoutOpts struct {
-	Strategy        CheckoutStrategy // Default will be a dry run
-	DisableFilters  bool             // Don't apply filters like CRLF conversion
-	DirMode         os.FileMode      // Default is 0755
-	FileMode        os.FileMode      // Default is 0644 or 0755 as dictated by blob
-	FileOpenFlags   int              // Default is O_CREAT | O_TRUNC | O_WRONLY
-	TargetDirectory string           // Alternative checkout path to workdir
-	Paths           []string
-	Baseline        *Tree
+	Strategy         CheckoutStrategy   // Default will be a dry run
+	DisableFilters   bool               // Don't apply filters like CRLF conversion
+	DirMode          os.FileMode        // Default is 0755
+	FileMode         os.FileMode        // Default is 0644 or 0755 as dictated by blob
+	FileOpenFlags    int                // Default is O_CREAT | O_TRUNC | O_WRONLY
+	NotifyFlags      CheckoutNotifyType // Default will be none
+	NotifyCallback   CheckoutNotifyCallback
+	ProgressCallback CheckoutProgressCallback
+	TargetDirectory  string // Alternative checkout path to workdir
+	Paths            []string
+	Baseline         *Tree
 }
 
 func checkoutOptionsFromC(c *C.git_checkout_options) CheckoutOpts {
@@ -55,6 +72,13 @@
 	opts.DirMode = os.FileMode(c.dir_mode)
 	opts.FileMode = os.FileMode(c.file_mode)
 	opts.FileOpenFlags = int(c.file_open_flags)
+	opts.NotifyFlags = CheckoutNotifyType(c.notify_flags)
+	if c.notify_payload != nil {
+		opts.NotifyCallback = pointerHandles.Get(c.notify_payload).(*CheckoutOpts).NotifyCallback
+	}
+	if c.progress_payload != nil {
+		opts.ProgressCallback = pointerHandles.Get(c.progress_payload).(*CheckoutOpts).ProgressCallback
+	}
 	if c.target_directory != nil {
 		opts.TargetDirectory = C.GoString(c.target_directory)
 	}
@@ -70,6 +94,38 @@
 	return &c
 }
 
+//export checkoutNotifyCallback
+func checkoutNotifyCallback(why C.git_checkout_notify_t, cpath *C.char, cbaseline, ctarget, cworkdir, data unsafe.Pointer) int {
+	if data == nil {
+		return 0
+	}
+	path := C.GoString(cpath)
+	var baseline, target, workdir DiffFile
+	if cbaseline != nil {
+		baseline = diffFileFromC((*C.git_diff_file)(cbaseline))
+	}
+	if ctarget != nil {
+		target = diffFileFromC((*C.git_diff_file)(ctarget))
+	}
+	if cworkdir != nil {
+		workdir = diffFileFromC((*C.git_diff_file)(cworkdir))
+	}
+	opts := pointerHandles.Get(data).(*CheckoutOpts)
+	if opts.NotifyCallback == nil {
+		return 0
+	}
+	return int(opts.NotifyCallback(CheckoutNotifyType(why), path, baseline, target, workdir))
+}
+
+//export checkoutProgressCallback
+func checkoutProgressCallback(path *C.char, completed_steps, total_steps C.size_t, data unsafe.Pointer) int {
+	opts := pointerHandles.Get(data).(*CheckoutOpts)
+	if opts.ProgressCallback == nil {
+		return 0
+	}
+	return int(opts.ProgressCallback(C.GoString(path), uint(completed_steps), uint(total_steps)))
+}
+
 // Convert the CheckoutOpts struct to the corresponding
 // C-struct. Returns a pointer to ptr, or nil if opts is nil, in order
 // to help with what to pass.
@@ -83,6 +139,17 @@
 	ptr.disable_filters = cbool(opts.DisableFilters)
 	ptr.dir_mode = C.uint(opts.DirMode.Perm())
 	ptr.file_mode = C.uint(opts.FileMode.Perm())
+	ptr.notify_flags = C.uint(opts.NotifyFlags)
+	if opts.NotifyCallback != nil || opts.ProgressCallback != nil {
+		C._go_git_populate_checkout_cb(ptr)
+	}
+	payload := pointerHandles.Track(opts)
+	if opts.NotifyCallback != nil {
+		ptr.notify_payload = payload
+	}
+	if opts.ProgressCallback != nil {
+		ptr.progress_payload = payload
+	}
 	if opts.TargetDirectory != "" {
 		ptr.target_directory = C.CString(opts.TargetDirectory)
 	}
@@ -106,6 +173,9 @@
 	if ptr.paths.count > 0 {
 		freeStrarray(&ptr.paths)
 	}
+	if ptr.notify_payload != nil {
+		pointerHandles.Untrack(ptr.notify_payload)
+	}
 }
 
 // Updates files in the index and the working tree to match the content of
diff --git a/cherrypick_test.go b/cherrypick_test.go
index a3246bd..bfa5ca8 100644
--- a/cherrypick_test.go
+++ b/cherrypick_test.go
@@ -33,6 +33,7 @@
 }
 
 func TestCherrypick(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/clone.go b/clone.go
index e80d14d..1ff5124 100644
--- a/clone.go
+++ b/clone.go
@@ -41,7 +41,6 @@
 
 	var ptr *C.git_repository
 	ret := C.git_clone(&ptr, curl, cpath, copts)
-	freeCheckoutOpts(&copts.checkout_opts)
 
 	if ret < 0 {
 		return nil, MakeGitError(ret)
diff --git a/clone_test.go b/clone_test.go
index a6bbf94..24c3a09 100644
--- a/clone_test.go
+++ b/clone_test.go
@@ -10,6 +10,7 @@
 )
 
 func TestClone(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -35,6 +36,7 @@
 }
 
 func TestCloneWithCallback(t *testing.T) {
+	t.Parallel()
 	testPayload := 0
 
 	repo := createTestRepo(t)
diff --git a/commit.go b/commit.go
index 6830da3..07b7c37 100644
--- a/commit.go
+++ b/commit.go
@@ -22,6 +22,10 @@
 	return C.GoString(C.git_commit_message(c.cast_ptr))
 }
 
+func (c Commit) RawMessage() string {
+	return C.GoString(C.git_commit_message_raw(c.cast_ptr))
+}
+
 func (c Commit) Summary() string {
 	return C.GoString(C.git_commit_summary(c.cast_ptr))
 }
diff --git a/config_test.go b/config_test.go
index fea8d8a..f31e73e 100644
--- a/config_test.go
+++ b/config_test.go
@@ -88,6 +88,7 @@
 }
 
 func TestConfigLookups(t *testing.T) {
+	t.Parallel()
 	var (
 		err error
 		c   *Config
diff --git a/credentials.go b/credentials.go
index bb0ec41..4e42b6e 100644
--- a/credentials.go
+++ b/credentials.go
@@ -44,13 +44,15 @@
 	return int(ret), cred
 }
 
-func NewCredSshKey(username string, publickey string, privatekey string, passphrase string) (int, Cred) {
+// NewCredSshKey creates new ssh credentials reading the public and private keys
+// from the file system.
+func NewCredSshKey(username string, publicKeyPath string, privateKeyPath string, passphrase string) (int, Cred) {
 	cred := Cred{}
 	cusername := C.CString(username)
 	defer C.free(unsafe.Pointer(cusername))
-	cpublickey := C.CString(publickey)
+	cpublickey := C.CString(publicKeyPath)
 	defer C.free(unsafe.Pointer(cpublickey))
-	cprivatekey := C.CString(privatekey)
+	cprivatekey := C.CString(privateKeyPath)
 	defer C.free(unsafe.Pointer(cprivatekey))
 	cpassphrase := C.CString(passphrase)
 	defer C.free(unsafe.Pointer(cpassphrase))
@@ -58,6 +60,22 @@
 	return int(ret), cred
 }
 
+// NewCredSshKeyFromMemory creates new ssh credentials using the publicKey and privateKey
+// arguments as the values for the public and private keys.
+func NewCredSshKeyFromMemory(username string, publicKey string, privateKey string, passphrase string) (int, Cred) {
+	cred := Cred{}
+	cusername := C.CString(username)
+	defer C.free(unsafe.Pointer(cusername))
+	cpublickey := C.CString(publicKey)
+	defer C.free(unsafe.Pointer(cpublickey))
+	cprivatekey := C.CString(privateKey)
+	defer C.free(unsafe.Pointer(cprivatekey))
+	cpassphrase := C.CString(passphrase)
+	defer C.free(unsafe.Pointer(cpassphrase))
+	ret := C.git_cred_ssh_key_memory_new(&cred.ptr, cusername, cpublickey, cprivatekey, cpassphrase)
+	return int(ret), cred
+}
+
 func NewCredSshKeyFromAgent(username string) (int, Cred) {
 	cred := Cred{}
 	cusername := C.CString(username)
diff --git a/describe_test.go b/describe_test.go
index 25af107..3181f23 100644
--- a/describe_test.go
+++ b/describe_test.go
@@ -8,6 +8,7 @@
 )
 
 func TestDescribeCommit(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -92,14 +93,16 @@
 func checkDescribeNoRefsFound(t *testing.T, err error) {
 	// The failure happens at wherever we were called, not here
 	_, file, line, ok := runtime.Caller(1)
+	expectedString := "no reference found, cannot describe anything"
 	if !ok {
 		t.Fatalf("Unable to get caller")
 	}
-	if err == nil || !strings.Contains(err.Error(), "No reference found, cannot describe anything") {
+	if err == nil || !strings.Contains(err.Error(), expectedString) {
 		t.Fatalf(
-			"%s:%v: was expecting error 'No reference found, cannot describe anything', got %v",
+			"%s:%v: was expecting error %v, got %v",
 			path.Base(file),
 			line,
+			expectedString,
 			err,
 		)
 	}
diff --git a/diff.go b/diff.go
index 9a7ac78..e8d5007 100644
--- a/diff.go
+++ b/diff.go
@@ -20,6 +20,7 @@
 	DiffFlagBinary    DiffFlag = C.GIT_DIFF_FLAG_BINARY
 	DiffFlagNotBinary DiffFlag = C.GIT_DIFF_FLAG_NOT_BINARY
 	DiffFlagValidOid  DiffFlag = C.GIT_DIFF_FLAG_VALID_ID
+	DiffFlagExists    DiffFlag = C.GIT_DIFF_FLAG_EXISTS
 )
 
 type Delta int
@@ -34,6 +35,8 @@
 	DeltaIgnored    Delta = C.GIT_DELTA_IGNORED
 	DeltaUntracked  Delta = C.GIT_DELTA_UNTRACKED
 	DeltaTypeChange Delta = C.GIT_DELTA_TYPECHANGE
+	DeltaUnreadable Delta = C.GIT_DELTA_UNREADABLE
+	DeltaConflicted Delta = C.GIT_DELTA_CONFLICTED
 )
 
 type DiffLineType int
@@ -217,6 +220,33 @@
 	return int(C.git_diff_stats_files_changed(stats.ptr))
 }
 
+type DiffStatsFormat int
+
+const (
+	DiffStatsNone           DiffStatsFormat = C.GIT_DIFF_STATS_NONE
+	DiffStatsFull           DiffStatsFormat = C.GIT_DIFF_STATS_FULL
+	DiffStatsShort          DiffStatsFormat = C.GIT_DIFF_STATS_SHORT
+	DiffStatsNumber         DiffStatsFormat = C.GIT_DIFF_STATS_NUMBER
+	DiffStatsIncludeSummary DiffStatsFormat = C.GIT_DIFF_STATS_INCLUDE_SUMMARY
+)
+
+func (stats *DiffStats) String(format DiffStatsFormat,
+	width uint) (string, error) {
+	buf := C.git_buf{}
+	defer C.git_buf_free(&buf)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_diff_stats_to_buf(&buf,
+		stats.ptr, C.git_diff_stats_format_t(format), C.size_t(width))
+	if ret < 0 {
+		return "", MakeGitError(ret)
+	}
+
+	return C.GoString(buf.ptr), nil
+}
+
 func (diff *Diff) Stats() (*DiffStats, error) {
 	stats := new(DiffStats)
 
@@ -372,6 +402,7 @@
 	DiffIgnoreFilemode         DiffOptionsFlag = C.GIT_DIFF_IGNORE_FILEMODE
 	DiffIgnoreSubmodules       DiffOptionsFlag = C.GIT_DIFF_IGNORE_SUBMODULES
 	DiffIgnoreCase             DiffOptionsFlag = C.GIT_DIFF_IGNORE_CASE
+	DiffIncludeCaseChange      DiffOptionsFlag = C.GIT_DIFF_INCLUDE_CASECHANGE
 
 	DiffDisablePathspecMatch    DiffOptionsFlag = C.GIT_DIFF_DISABLE_PATHSPEC_MATCH
 	DiffSkipBinaryCheck         DiffOptionsFlag = C.GIT_DIFF_SKIP_BINARY_CHECK
diff --git a/diff_test.go b/diff_test.go
index 850ed8e..6fbad51 100644
--- a/diff_test.go
+++ b/diff_test.go
@@ -7,6 +7,7 @@
 )
 
 func TestFindSimilar(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -61,7 +62,7 @@
 }
 
 func TestDiffTreeToTree(t *testing.T) {
-
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -189,6 +190,7 @@
 }
 
 func TestDiffBlobs(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/features.go b/features.go
new file mode 100644
index 0000000..f6474a0
--- /dev/null
+++ b/features.go
@@ -0,0 +1,30 @@
+package git
+
+/*
+#include <git2.h>
+*/
+import "C"
+
+type Feature int
+
+const (
+	// libgit2 was built with threading support
+	FeatureThreads Feature = C.GIT_FEATURE_THREADS
+
+	// libgit2 was built with HTTPS support built-in
+	FeatureHttps Feature = C.GIT_FEATURE_HTTPS
+
+	// libgit2 was build with SSH support built-in
+	FeatureSsh Feature = C.GIT_FEATURE_SSH
+
+	// libgit2 was built with nanosecond support for files
+	FeatureNSec Feature = C.GIT_FEATURE_NSEC
+)
+
+// Features returns a bit-flag of Feature values indicating which features the
+// loaded libgit2 library has.
+func Features() Feature {
+	features := C.git_libgit2_features()
+
+	return Feature(features)
+}
diff --git a/git.go b/git.go
index e272389..c032b0a 100644
--- a/git.go
+++ b/git.go
@@ -1,12 +1,15 @@
 package git
 
 /*
+#cgo CFLAGS: -I${SRCDIR}/vendor/libgit2/include
+#cgo LDFLAGS: -L${SRCDIR}/vendor/libgit2/build/ -lgit2
+#cgo windows LDFLAGS: -lwinhttp
+#cgo !windows pkg-config: --static ${SRCDIR}/vendor/libgit2/build/libgit2.pc
 #include <git2.h>
 #include <git2/sys/openssl.h>
-#cgo pkg-config: libgit2
 
-#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 24
-# error "Invalid libgit2 version; this git2go supports libgit2 v0.24"
+#if LIBGIT2_VER_MAJOR != 0 || LIBGIT2_VER_MINOR != 25
+# error "Invalid libgit2 version; this git2go supports libgit2 v0.25"
 #endif
 
 */
@@ -126,6 +129,15 @@
 
 	C.git_libgit2_init()
 
+	// Due to the multithreaded nature of Go and its interaction with
+	// calling C functions, we cannot work with a library that was not built
+	// with multi-threading support. The most likely outcome is a segfault
+	// or panic at an incomprehensible time, so let's make it easy by
+	// panicking right here.
+	if Features()&FeatureThreads == 0 {
+		panic("libgit2 was not built with threading support")
+	}
+
 	// This is not something we should be doing, as we may be
 	// stomping all over someone else's setup. The user should do
 	// this themselves or use some binding/wrapper which does it
diff --git a/git_test.go b/git_test.go
index 58caf71..807dcc2 100644
--- a/git_test.go
+++ b/git_test.go
@@ -58,6 +58,8 @@
 	checkFatal(t, err)
 	err = idx.AddByPath("README")
 	checkFatal(t, err)
+	err = idx.Write()
+	checkFatal(t, err)
 	treeId, err := idx.WriteTree()
 	checkFatal(t, err)
 
@@ -91,6 +93,8 @@
 	checkFatal(t, err)
 	err = idx.AddByPath("README")
 	checkFatal(t, err)
+	err = idx.Write()
+	checkFatal(t, err)
 	treeId, err := idx.WriteTree()
 	checkFatal(t, err)
 
@@ -109,6 +113,7 @@
 }
 
 func TestOidZero(t *testing.T) {
+	t.Parallel()
 	var zeroId Oid
 
 	if !zeroId.IsZero() {
@@ -117,6 +122,7 @@
 }
 
 func TestEmptyOid(t *testing.T) {
+	t.Parallel()
 	_, err := NewOid("")
 	if err == nil || !IsErrorCode(err, ErrGeneric) {
 		t.Fatal("Should have returned invalid error")
diff --git a/index.go b/index.go
index ae94864..2afdcdf 100644
--- a/index.go
+++ b/index.go
@@ -278,6 +278,22 @@
 	return nil
 }
 
+// RemoveDirectory removes all entries from the index under a given directory.
+func (v *Index) RemoveDirectory(dir string, stage int) error {
+	cstr := C.CString(dir)
+	defer C.free(unsafe.Pointer(cstr))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_index_remove_directory(v.ptr, cstr, C.int(stage))
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+
+	return nil
+}
+
 func (v *Index) WriteTreeTo(repo *Repository) (*Oid, error) {
 	oid := new(Oid)
 
diff --git a/index_test.go b/index_test.go
index 5f6b375..f47dace 100644
--- a/index_test.go
+++ b/index_test.go
@@ -8,6 +8,7 @@
 )
 
 func TestCreateRepoAndStage(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -24,6 +25,7 @@
 }
 
 func TestIndexReadTree(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -53,6 +55,7 @@
 }
 
 func TestIndexWriteTreeTo(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -72,6 +75,7 @@
 }
 
 func TestIndexAddAndWriteTreeTo(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -105,7 +109,48 @@
 	}
 }
 
+func TestIndexRemoveDirectory(t *testing.T) {
+	repo := createTestRepo(t)
+	defer cleanupTestRepo(t, repo)
+
+	odb, err := repo.Odb()
+	checkFatal(t, err)
+
+	blobID, err := odb.Write([]byte("fou\n"), ObjectBlob)
+	checkFatal(t, err)
+
+	idx, err := NewIndex()
+	checkFatal(t, err)
+
+	entryCount := idx.EntryCount()
+	if entryCount != 0 {
+		t.Fatal("Index should count 0 entry")
+	}
+
+	entry := IndexEntry{
+		Path: "path/to/LISEZ_MOI",
+		Id:   blobID,
+		Mode: FilemodeBlob,
+	}
+
+	err = idx.Add(&entry)
+	checkFatal(t, err)
+
+	entryCount = idx.EntryCount()
+	if entryCount != 1 {
+		t.Fatal("Index should count 1 entry")
+	}
+
+	err = idx.RemoveDirectory("path", 0)
+
+	entryCount = idx.EntryCount()
+	if entryCount != 0 {
+		t.Fatal("Index should count 0 entry")
+	}
+}
+
 func TestIndexAddAllNoCallback(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -127,6 +172,7 @@
 }
 
 func TestIndexAddAllCallback(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -155,6 +201,7 @@
 }
 
 func TestIndexOpen(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/merge_test.go b/merge_test.go
index 8059727..f2c84bc 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -6,6 +6,7 @@
 )
 
 func TestMergeWithSelf(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -24,6 +25,7 @@
 }
 
 func TestMergeAnalysisWithSelf(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -46,6 +48,7 @@
 }
 
 func TestMergeSameFile(t *testing.T) {
+	t.Parallel()
 	file := MergeFileInput{
 		Path:     "test",
 		Mode:     33188,
@@ -68,6 +71,7 @@
 
 }
 func TestMergeTreesWithoutAncestor(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -125,6 +129,7 @@
 }
 
 func TestMergeBase(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/note_test.go b/note_test.go
index 27e04be..9f64eb8 100644
--- a/note_test.go
+++ b/note_test.go
@@ -8,6 +8,7 @@
 )
 
 func TestCreateNote(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -27,6 +28,7 @@
 }
 
 func TestNoteIterator(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -63,6 +65,7 @@
 }
 
 func TestRemoveNote(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -86,6 +89,7 @@
 }
 
 func TestDefaultNoteRef(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/object_test.go b/object_test.go
index 2ae2a6a..cb262de 100644
--- a/object_test.go
+++ b/object_test.go
@@ -5,6 +5,7 @@
 )
 
 func TestObjectPoymorphism(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -88,6 +89,7 @@
 }
 
 func TestObjectOwner(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -104,6 +106,7 @@
 }
 
 func TestObjectPeel(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/odb.go b/odb.go
index 9c6baa3..a728cd3 100644
--- a/odb.go
+++ b/odb.go
@@ -36,8 +36,8 @@
 	return odb, nil
 }
 
-func NewOdbBackendFromC(ptr *C.git_odb_backend) (backend *OdbBackend) {
-	backend = &OdbBackend{ptr}
+func NewOdbBackendFromC(ptr unsafe.Pointer) (backend *OdbBackend) {
+	backend = &OdbBackend{(*C.git_odb_backend)(ptr)}
 	return backend
 }
 
@@ -226,6 +226,10 @@
 	return uint64(C.git_odb_object_size(object.ptr))
 }
 
+func (object *OdbObject) Type() ObjectType {
+	return ObjectType(C.git_odb_object_type(object.ptr))
+}
+
 func (object *OdbObject) Data() (data []byte) {
 	var c_blob unsafe.Pointer = C.git_odb_object_data(object.ptr)
 	var blob []byte
diff --git a/odb_test.go b/odb_test.go
index dfd2ad0..3d22fc9 100644
--- a/odb_test.go
+++ b/odb_test.go
@@ -7,6 +7,7 @@
 )
 
 func TestOdbReadHeader(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -35,6 +36,7 @@
 }
 
 func TestOdbStream(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -64,7 +66,7 @@
 }
 
 func TestOdbHash(t *testing.T) {
-
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -92,6 +94,7 @@
 }
 
 func TestOdbForeach(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/patch_test.go b/patch_test.go
index 2d52fb4..291c705 100644
--- a/patch_test.go
+++ b/patch_test.go
@@ -6,6 +6,7 @@
 )
 
 func TestPatch(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/push_test.go b/push_test.go
index 8f6e806..f372882 100644
--- a/push_test.go
+++ b/push_test.go
@@ -5,6 +5,7 @@
 )
 
 func TestRemotePush(t *testing.T) {
+	t.Parallel()
 	repo := createBareTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/refdb.go b/refdb.go
index 0d1e241..4e3ebf9 100644
--- a/refdb.go
+++ b/refdb.go
@@ -9,6 +9,7 @@
 import "C"
 import (
 	"runtime"
+	"unsafe"
 )
 
 type Refdb struct {
@@ -34,8 +35,8 @@
 	return refdb, nil
 }
 
-func NewRefdbBackendFromC(ptr *C.git_refdb_backend) (backend *RefdbBackend) {
-	backend = &RefdbBackend{ptr}
+func NewRefdbBackendFromC(ptr unsafe.Pointer) (backend *RefdbBackend) {
+	backend = &RefdbBackend{(*C.git_refdb_backend)(ptr)}
 	return backend
 }
 
diff --git a/reference_test.go b/reference_test.go
index 761daf8..b6721e1 100644
--- a/reference_test.go
+++ b/reference_test.go
@@ -8,6 +8,7 @@
 )
 
 func TestRefModification(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -53,6 +54,7 @@
 }
 
 func TestReferenceIterator(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -131,6 +133,7 @@
 }
 
 func TestReferenceOwner(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -150,6 +153,7 @@
 }
 
 func TestUtil(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -177,6 +181,7 @@
 }
 
 func TestIsNote(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -210,6 +215,7 @@
 }
 
 func TestReferenceIsValidName(t *testing.T) {
+	t.Parallel()
 	if !ReferenceIsValidName("HEAD") {
 		t.Errorf("HEAD should be a valid reference name")
 	}
diff --git a/remote.go b/remote.go
index 8a57280..d3f437d 100644
--- a/remote.go
+++ b/remote.go
@@ -117,6 +117,30 @@
 	Headers []string
 }
 
+type ProxyType uint
+
+const (
+	// Do not attempt to connect through a proxy
+	//
+	// If built against lbicurl, it itself may attempt to connect
+	// to a proxy if the environment variables specify it.
+	ProxyTypeNone ProxyType = C.GIT_PROXY_NONE
+
+	// Try to auto-detect the proxy from the git configuration.
+	ProxyTypeAuto ProxyType = C.GIT_PROXY_AUTO
+
+	// Connect via the URL given in the options
+	ProxyTypeSpecified ProxyType = C.GIT_PROXY_SPECIFIED
+)
+
+type ProxyOptions struct {
+	// The type of proxy to use (or none)
+	Type ProxyType
+
+	// The proxy's URL
+	Url string
+}
+
 type Remote struct {
 	ptr       *C.git_remote
 	callbacks RemoteCallbacks
@@ -215,7 +239,7 @@
 func credentialsCallback(_cred **C.git_cred, _url *C.char, _username_from_url *C.char, allowed_types uint, data unsafe.Pointer) int {
 	callbacks, _ := pointerHandles.Get(data).(*RemoteCallbacks)
 	if callbacks.CredentialsCallback == nil {
-		return 0
+		return C.GIT_PASSTHROUGH
 	}
 	url := C.GoString(_url)
 	username_from_url := C.GoString(_username_from_url)
@@ -320,6 +344,20 @@
 	return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)))
 }
 
+func populateProxyOptions(ptr *C.git_proxy_options, opts *ProxyOptions) {
+	C.git_proxy_init_options(ptr, C.GIT_PROXY_OPTIONS_VERSION)
+	if opts == nil {
+		return
+	}
+
+	ptr._type = C.git_proxy_t(opts.Type)
+	ptr.url = C.CString(opts.Url)
+}
+
+func freeProxyOptions(ptr *C.git_proxy_options) {
+	C.free(unsafe.Pointer(ptr.url))
+}
+
 func RemoteIsValidName(name string) bool {
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
@@ -454,6 +492,26 @@
 	return C.GoString(C.git_remote_pushurl(o.ptr))
 }
 
+func (c *RemoteCollection) Rename(remote, newname string) ([]string, error) {
+	cproblems := C.git_strarray{}
+	defer freeStrarray(&cproblems)
+	cnewname := C.CString(newname)
+	defer C.free(unsafe.Pointer(cnewname))
+	cremote := C.CString(remote)
+	defer C.free(unsafe.Pointer(cremote))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_remote_rename(&cproblems, c.repo.ptr, cremote, cnewname)
+	if ret < 0 {
+		return []string{}, MakeGitError(ret)
+	}
+
+	problems := makeStringsFromCStrings(cproblems.strings, int(cproblems.count))
+	return problems, nil
+}
+
 func (c *RemoteCollection) SetUrl(remote, url string) error {
 	curl := C.CString(url)
 	defer C.free(unsafe.Pointer(curl))
@@ -654,12 +712,12 @@
 	return nil
 }
 
-func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, headers []string) error {
-	return o.Connect(ConnectDirectionFetch, callbacks, headers)
+func (o *Remote) ConnectFetch(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error {
+	return o.Connect(ConnectDirectionFetch, callbacks, proxyOpts, headers)
 }
 
-func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, headers []string) error {
-	return o.Connect(ConnectDirectionPush, callbacks, headers)
+func (o *Remote) ConnectPush(callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error {
+	return o.Connect(ConnectDirectionPush, callbacks, proxyOpts, headers)
 }
 
 // Connect opens a connection to a remote.
@@ -669,24 +727,36 @@
 // starts up a specific binary which can only do the one or the other.
 //
 // 'headers' are extra HTTP headers to use in this connection.
-func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, headers []string) error {
+func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks, proxyOpts *ProxyOptions, headers []string) error {
 	var ccallbacks C.git_remote_callbacks
 	populateRemoteCallbacks(&ccallbacks, callbacks)
 
+	var cproxy C.git_proxy_options
+	populateProxyOptions(&cproxy, proxyOpts)
+	defer freeProxyOptions(&cproxy)
+	
 	cheaders := C.git_strarray{}
 	cheaders.count = C.size_t(len(headers))
 	cheaders.strings = makeCStringsFromStrings(headers)
 	defer freeStrarray(&cheaders)
 
+
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cheaders); ret != 0 {
+	if ret := C.git_remote_connect(o.ptr, C.git_direction(direction), &ccallbacks, &cproxy, &cheaders); ret != 0 {
 		return MakeGitError(ret)
 	}
 	return nil
 }
 
+func (o *Remote) Disconnect() {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	C.git_remote_disconnect(o.ptr)
+}
+
 func (o *Remote) Ls(filterRefs ...string) ([]RemoteHead, error) {
 
 	var refs **C.git_remote_head
diff --git a/remote_test.go b/remote_test.go
index 978b803..8b20fd2 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -6,6 +6,7 @@
 )
 
 func TestListRemotes(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -33,6 +34,7 @@
 }
 
 func TestCertificateCheck(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -52,24 +54,26 @@
 }
 
 func TestRemoteConnect(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
 	remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
 	checkFatal(t, err)
 
-	err = remote.ConnectFetch(nil, nil)
+	err = remote.ConnectFetch(nil, nil, nil)
 	checkFatal(t, err)
 }
 
 func TestRemoteLs(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
 	remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
 	checkFatal(t, err)
 
-	err = remote.ConnectFetch(nil, nil)
+	err = remote.ConnectFetch(nil, nil, nil)
 	checkFatal(t, err)
 
 	heads, err := remote.Ls()
@@ -81,13 +85,14 @@
 }
 
 func TestRemoteLsFiltering(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
 	remote, err := repo.Remotes.Create("origin", "https://github.com/libgit2/TestGitRepository")
 	checkFatal(t, err)
 
-	err = remote.ConnectFetch(nil, nil)
+	err = remote.ConnectFetch(nil, nil, nil)
 	checkFatal(t, err)
 
 	heads, err := remote.Ls("master")
@@ -107,6 +112,7 @@
 }
 
 func TestRemotePruneRefs(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -129,6 +135,7 @@
 }
 
 func TestRemotePrune(t *testing.T) {
+	t.Parallel()
 	remoteRepo := createTestRepo(t)
 	defer cleanupTestRepo(t, remoteRepo)
 
@@ -166,7 +173,7 @@
 	rr, err := repo.Remotes.Lookup("origin")
 	checkFatal(t, err)
 
-	err = rr.ConnectFetch(nil, nil)
+	err = rr.ConnectFetch(nil, nil, nil)
 	checkFatal(t, err)
 
 	err = rr.Prune(nil)
diff --git a/repository.go b/repository.go
index efc506e..0612b5f 100644
--- a/repository.go
+++ b/repository.go
@@ -30,6 +30,9 @@
 	// Tags represents the collection of tags and can be used to create,
 	// list, iterate and remove tags in this repository.
 	Tags TagsCollection
+	// Stashes represents the collection of stashes and can be used to
+	// save, apply and iterate over stash states in this repository.
+	Stashes StashCollection
 }
 
 func newRepositoryFromC(ptr *C.git_repository) *Repository {
@@ -40,6 +43,7 @@
 	repo.References.repo = repo
 	repo.Notes.repo = repo
 	repo.Tags.repo = repo
+	repo.Stashes.repo = repo
 
 	runtime.SetFinalizer(repo, (*Repository).Free)
 
@@ -264,6 +268,40 @@
 	return ret != 0, nil
 }
 
+func (v *Repository) IsHeadUnborn() (bool, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_repository_head_unborn(v.ptr)
+	if ret < 0 {
+		return false, MakeGitError(ret)
+	}
+	return ret != 0, nil
+}
+
+func (v *Repository) IsEmpty() (bool, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_repository_is_empty(v.ptr)
+	if ret < 0 {
+		return false, MakeGitError(ret)
+	}
+
+	return ret != 0, nil
+}
+
+func (v *Repository) IsShallow() (bool, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_repository_is_shallow(v.ptr)
+	if ret < 0 {
+		return false, MakeGitError(ret)
+	}
+	return ret != 0, nil
+}
+
 func (v *Repository) Walk() (*RevWalk, error) {
 
 	var walkPtr *C.git_revwalk
diff --git a/reset_test.go b/reset_test.go
index ec578bd..45777e4 100644
--- a/reset_test.go
+++ b/reset_test.go
@@ -6,6 +6,7 @@
 )
 
 func TestResetToCommit(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	seedTestRepo(t, repo)
 	// create commit to reset to
diff --git a/revparse_test.go b/revparse_test.go
index 75e9ffd..2835434 100644
--- a/revparse_test.go
+++ b/revparse_test.go
@@ -5,6 +5,7 @@
 )
 
 func TestRevparse(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -17,6 +18,7 @@
 }
 
 func TestRevparseSingle(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -29,6 +31,7 @@
 }
 
 func TestRevparseExt(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/script/with-static.sh b/script/with-static.sh
deleted file mode 100755
index 3f60e31..0000000
--- a/script/with-static.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/sh
-
-set -ex
-
-export BUILD="$PWD/vendor/libgit2/build"
-export PCFILE="$BUILD/libgit2.pc"
-
-FLAGS=$(pkg-config --static --libs $PCFILE) || exit 1
-export CGO_LDFLAGS="$BUILD/libgit2.a -L$BUILD ${FLAGS}"
-export CGO_CFLAGS="-I$PWD/vendor/libgit2/include"
-
-$@
diff --git a/stash.go b/stash.go
new file mode 100644
index 0000000..809732e
--- /dev/null
+++ b/stash.go
@@ -0,0 +1,338 @@
+package git
+
+/*
+#include <git2.h>
+
+extern void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts);
+extern int _go_git_stash_foreach(git_repository *repo, void *payload);
+*/
+import "C"
+import (
+	"runtime"
+	"unsafe"
+)
+
+// StashFlag are flags that affect the stash save operation.
+type StashFlag int
+
+const (
+	// StashDefault represents no option, default.
+	StashDefault StashFlag = C.GIT_STASH_DEFAULT
+
+	// StashKeepIndex leaves all changes already added to the
+	// index intact in the working directory.
+	StashKeepIndex StashFlag = C.GIT_STASH_KEEP_INDEX
+
+	// StashIncludeUntracked means all untracked files are also
+	// stashed and then cleaned up from the working directory.
+	StashIncludeUntracked StashFlag = C.GIT_STASH_INCLUDE_UNTRACKED
+
+	// StashIncludeIgnored means all ignored files are also
+	// stashed and then cleaned up from the working directory.
+	StashIncludeIgnored StashFlag = C.GIT_STASH_INCLUDE_IGNORED
+)
+
+// StashCollection represents the possible operations that can be
+// performed on the collection of stashes for a repository.
+type StashCollection struct {
+	repo *Repository
+}
+
+// Save saves the local modifications to a new stash.
+//
+// Stasher is the identity of the person performing the stashing.
+// Message is the optional description along with the stashed state.
+// Flags control the stashing process and are given as bitwise OR.
+func (c *StashCollection) Save(
+	stasher *Signature, message string, flags StashFlag) (*Oid, error) {
+
+	oid := new(Oid)
+
+	stasherC, err := stasher.toC()
+	if err != nil {
+		return nil, err
+	}
+	defer C.git_signature_free(stasherC)
+
+	messageC := C.CString(message)
+	defer C.free(unsafe.Pointer(messageC))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_stash_save(
+		oid.toC(), c.repo.ptr,
+		stasherC, messageC, C.uint32_t(flags))
+
+	if ret < 0 {
+		return nil, MakeGitError(ret)
+	}
+	return oid, nil
+}
+
+// StashApplyFlag are flags that affect the stash apply operation.
+type StashApplyFlag int
+
+const (
+	// StashApplyDefault is the default.
+	StashApplyDefault StashApplyFlag = C.GIT_STASH_APPLY_DEFAULT
+
+	// StashApplyReinstateIndex will try to reinstate not only the
+	// working tree's changes, but also the index's changes.
+	StashApplyReinstateIndex StashApplyFlag = C.GIT_STASH_APPLY_REINSTATE_INDEX
+)
+
+// StashApplyProgress are flags describing the progress of the apply operation.
+type StashApplyProgress int
+
+const (
+	// StashApplyProgressNone means loading the stashed data from the object store.
+	StashApplyProgressNone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_NONE
+
+	// StashApplyProgressLoadingStash means the stored index is being analyzed.
+	StashApplyProgressLoadingStash StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_LOADING_STASH
+
+	// StashApplyProgressAnalyzeIndex means the stored index is being analyzed.
+	StashApplyProgressAnalyzeIndex StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX
+
+	// StashApplyProgressAnalyzeModified means the modified files are being analyzed.
+	StashApplyProgressAnalyzeModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED
+
+	// StashApplyProgressAnalyzeUntracked means the untracked and ignored files are being analyzed.
+	StashApplyProgressAnalyzeUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED
+
+	// StashApplyProgressCheckoutUntracked means the untracked files are being written to disk.
+	StashApplyProgressCheckoutUntracked StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED
+
+	// StashApplyProgressCheckoutModified means the modified files are being written to disk.
+	StashApplyProgressCheckoutModified StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED
+
+	// StashApplyProgressDone means the stash was applied successfully.
+	StashApplyProgressDone StashApplyProgress = C.GIT_STASH_APPLY_PROGRESS_DONE
+)
+
+// StashApplyProgressCallback is the apply operation notification callback.
+type StashApplyProgressCallback func(progress StashApplyProgress) error
+
+type stashApplyProgressData struct {
+	Callback StashApplyProgressCallback
+	Error    error
+}
+
+//export stashApplyProgressCb
+func stashApplyProgressCb(progress C.git_stash_apply_progress_t, handle unsafe.Pointer) int {
+	payload := pointerHandles.Get(handle)
+	data, ok := payload.(*stashApplyProgressData)
+	if !ok {
+		panic("could not retrieve data for handle")
+	}
+
+	if data != nil {
+		err := data.Callback(StashApplyProgress(progress))
+		if err != nil {
+			data.Error = err
+			return C.GIT_EUSER
+		}
+	}
+	return 0
+}
+
+// StashApplyOptions represents options to control the apply operation.
+type StashApplyOptions struct {
+	Flags            StashApplyFlag
+	CheckoutOptions  CheckoutOpts               // options to use when writing files to the working directory
+	ProgressCallback StashApplyProgressCallback // optional callback to notify the consumer of application progress
+}
+
+// DefaultStashApplyOptions initializes the structure with default values.
+func DefaultStashApplyOptions() (StashApplyOptions, error) {
+	optsC := C.git_stash_apply_options{}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_stash_apply_init_options(&optsC, C.GIT_STASH_APPLY_OPTIONS_VERSION)
+	if ecode < 0 {
+		return StashApplyOptions{}, MakeGitError(ecode)
+	}
+	return StashApplyOptions{
+		Flags:           StashApplyFlag(optsC.flags),
+		CheckoutOptions: checkoutOptionsFromC(&optsC.checkout_options),
+	}, nil
+}
+
+func (opts *StashApplyOptions) toC() (
+	optsC *C.git_stash_apply_options, progressData *stashApplyProgressData) {
+
+	if opts != nil {
+		progressData = &stashApplyProgressData{
+			Callback: opts.ProgressCallback,
+		}
+
+		optsC = &C.git_stash_apply_options{
+			version: C.GIT_STASH_APPLY_OPTIONS_VERSION,
+			flags:   C.git_stash_apply_flags(opts.Flags),
+		}
+		populateCheckoutOpts(&optsC.checkout_options, &opts.CheckoutOptions)
+		if opts.ProgressCallback != nil {
+			C._go_git_setup_stash_apply_progress_callbacks(optsC)
+			optsC.progress_payload = pointerHandles.Track(progressData)
+		}
+	}
+	return
+}
+
+// should be called after every call to toC() as deferred.
+func untrackStashApplyOptionsCallback(optsC *C.git_stash_apply_options) {
+	if optsC != nil && optsC.progress_payload != nil {
+		pointerHandles.Untrack(optsC.progress_payload)
+	}
+}
+
+func freeStashApplyOptions(optsC *C.git_stash_apply_options) {
+	if optsC != nil {
+		freeCheckoutOpts(&optsC.checkout_options)
+	}
+}
+
+// Apply applies a single stashed state from the stash list.
+//
+// If local changes in the working directory conflict with changes in the
+// stash then ErrConflict will be returned.  In this case, the index
+// will always remain unmodified and all files in the working directory will
+// remain unmodified.  However, if you are restoring untracked files or
+// ignored files and there is a conflict when applying the modified files,
+// then those files will remain in the working directory.
+//
+// If passing the StashApplyReinstateIndex flag and there would be conflicts
+// when reinstating the index, the function will return ErrConflict
+// and both the working directory and index will be left unmodified.
+//
+// Note that a minimum checkout strategy of 'CheckoutSafe' is implied.
+//
+// 'index' is the position within the stash list. 0 points to the most
+// recent stashed state.
+//
+// Returns error code ErrNotFound if there's no stashed state for the given
+// index, error code ErrConflict if local changes in the working directory
+// conflict with changes in the stash, the user returned error from the
+// StashApplyProgressCallback, if any, or other error code.
+//
+// Error codes can be interogated with IsErrorCode(err, ErrNotFound).
+func (c *StashCollection) Apply(index int, opts StashApplyOptions) error {
+	optsC, progressData := opts.toC()
+	defer untrackStashApplyOptionsCallback(optsC)
+	defer freeStashApplyOptions(optsC)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_stash_apply(c.repo.ptr, C.size_t(index), optsC)
+	if ret == C.GIT_EUSER {
+		return progressData.Error
+	}
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
+// StashCallback is called per entry when interating over all
+// the stashed states.
+//
+// 'index' is the position of the current stash in the stash list,
+// 'message' is the message used when creating the stash and 'id'
+// is the commit id of the stash.
+type StashCallback func(index int, message string, id *Oid) error
+
+type stashCallbackData struct {
+	Callback StashCallback
+	Error    error
+}
+
+//export stashForeachCb
+func stashForeachCb(index C.size_t, message *C.char, id *C.git_oid, handle unsafe.Pointer) int {
+	payload := pointerHandles.Get(handle)
+	data, ok := payload.(*stashCallbackData)
+	if !ok {
+		panic("could not retrieve data for handle")
+	}
+
+	err := data.Callback(int(index), C.GoString(message), newOidFromC(id))
+	if err != nil {
+		data.Error = err
+		return C.GIT_EUSER
+	}
+	return 0
+}
+
+// Foreach loops over all the stashed states and calls the callback
+// for each one.
+//
+// If callback returns an error, this will stop looping.
+func (c *StashCollection) Foreach(callback StashCallback) error {
+	data := stashCallbackData{
+		Callback: callback,
+	}
+
+	handle := pointerHandles.Track(&data)
+	defer pointerHandles.Untrack(handle)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C._go_git_stash_foreach(c.repo.ptr, handle)
+	if ret == C.GIT_EUSER {
+		return data.Error
+	}
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
+// Drop removes a single stashed state from the stash list.
+//
+// 'index' is the position within the stash list. 0 points
+// to the most recent stashed state.
+//
+// Returns error code ErrNotFound if there's no stashed
+// state for the given index.
+func (c *StashCollection) Drop(index int) error {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_stash_drop(c.repo.ptr, C.size_t(index))
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
+// Pop applies a single stashed state from the stash list
+// and removes it from the list if successful.
+//
+// 'index' is the position within the stash list. 0 points
+// to the most recent stashed state.
+//
+// 'opts' controls how stashes are applied.
+//
+// Returns error code ErrNotFound if there's no stashed
+// state for the given index.
+func (c *StashCollection) Pop(index int, opts StashApplyOptions) error {
+	optsC, progressData := opts.toC()
+	defer untrackStashApplyOptionsCallback(optsC)
+	defer freeStashApplyOptions(optsC)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_stash_pop(c.repo.ptr, C.size_t(index), optsC)
+	if ret == C.GIT_EUSER {
+		return progressData.Error
+	}
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
diff --git a/stash_test.go b/stash_test.go
new file mode 100644
index 0000000..180a16b
--- /dev/null
+++ b/stash_test.go
@@ -0,0 +1,198 @@
+package git
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path"
+	"reflect"
+	"runtime"
+	"testing"
+	"time"
+)
+
+func TestStash(t *testing.T) {
+	repo := createTestRepo(t)
+	defer cleanupTestRepo(t, repo)
+
+	prepareStashRepo(t, repo)
+
+	sig := &Signature{
+		Name:  "Rand Om Hacker",
+		Email: "random@hacker.com",
+		When:  time.Now(),
+	}
+
+	stash1, err := repo.Stashes.Save(sig, "First stash", StashDefault)
+	checkFatal(t, err)
+
+	_, err = repo.LookupCommit(stash1)
+	checkFatal(t, err)
+
+	b, err := ioutil.ReadFile(pathInRepo(repo, "README"))
+	checkFatal(t, err)
+	if string(b) == "Update README goes to stash\n" {
+		t.Errorf("README still contains the uncommitted changes")
+	}
+
+	if !fileExistsInRepo(repo, "untracked.txt") {
+		t.Errorf("untracked.txt doesn't exist in the repo; should be untracked")
+	}
+
+	// Apply: default
+
+	opts, err := DefaultStashApplyOptions()
+	checkFatal(t, err)
+
+	err = repo.Stashes.Apply(0, opts)
+	checkFatal(t, err)
+
+	b, err = ioutil.ReadFile(pathInRepo(repo, "README"))
+	checkFatal(t, err)
+	if string(b) != "Update README goes to stash\n" {
+		t.Errorf("README changes aren't here")
+	}
+
+	// Apply: no stash for the given index
+
+	err = repo.Stashes.Apply(1, opts)
+	if !IsErrorCode(err, ErrNotFound) {
+		t.Errorf("expecting GIT_ENOTFOUND error code %d, got %v", ErrNotFound, err)
+	}
+
+	// Apply: callback stopped
+
+	opts.ProgressCallback = func(progress StashApplyProgress) error {
+		if progress == StashApplyProgressCheckoutModified {
+			return fmt.Errorf("Stop")
+		}
+		return nil
+	}
+
+	err = repo.Stashes.Apply(0, opts)
+	if err.Error() != "Stop" {
+		t.Errorf("expecting error 'Stop', got %v", err)
+	}
+
+	// Create second stash with ignored files
+
+	os.MkdirAll(pathInRepo(repo, "tmp"), os.ModeDir|os.ModePerm)
+	err = ioutil.WriteFile(pathInRepo(repo, "tmp/ignored.txt"), []byte("Ignore me\n"), 0644)
+	checkFatal(t, err)
+
+	stash2, err := repo.Stashes.Save(sig, "Second stash", StashIncludeIgnored)
+	checkFatal(t, err)
+
+	if fileExistsInRepo(repo, "tmp/ignored.txt") {
+		t.Errorf("tmp/ignored.txt should not exist anymore in the work dir")
+	}
+
+	// Stash foreach
+
+	expected := []stash{
+		{0, "On master: Second stash", stash2.String()},
+		{1, "On master: First stash", stash1.String()},
+	}
+	checkStashes(t, repo, expected)
+
+	// Stash pop
+
+	opts, _ = DefaultStashApplyOptions()
+	err = repo.Stashes.Pop(1, opts)
+	checkFatal(t, err)
+
+	b, err = ioutil.ReadFile(pathInRepo(repo, "README"))
+	checkFatal(t, err)
+	if string(b) != "Update README goes to stash\n" {
+		t.Errorf("README changes aren't here")
+	}
+
+	expected = []stash{
+		{0, "On master: Second stash", stash2.String()},
+	}
+	checkStashes(t, repo, expected)
+
+	// Stash drop
+
+	err = repo.Stashes.Drop(0)
+	checkFatal(t, err)
+
+	expected = []stash{}
+	checkStashes(t, repo, expected)
+}
+
+type stash struct {
+	index int
+	msg   string
+	id    string
+}
+
+func checkStashes(t *testing.T, repo *Repository, expected []stash) {
+	var actual []stash
+
+	repo.Stashes.Foreach(func(index int, msg string, id *Oid) error {
+		stash := stash{index, msg, id.String()}
+		if len(expected) > len(actual) {
+			if s := expected[len(actual)]; s.id == "" {
+				stash.id = "" //  don't check id
+			}
+		}
+		actual = append(actual, stash)
+		return nil
+	})
+
+	if len(expected) > 0 && !reflect.DeepEqual(expected, actual) {
+		// The failure happens at wherever we were called, not here
+		_, file, line, ok := runtime.Caller(1)
+		if !ok {
+			t.Fatalf("Unable to get caller")
+		}
+		t.Errorf("%v:%v: expecting %#v\ngot %#v", path.Base(file), line, expected, actual)
+	}
+}
+
+func prepareStashRepo(t *testing.T, repo *Repository) {
+	seedTestRepo(t, repo)
+
+	err := ioutil.WriteFile(pathInRepo(repo, ".gitignore"), []byte("tmp\n"), 0644)
+	checkFatal(t, err)
+
+	sig := &Signature{
+		Name:  "Rand Om Hacker",
+		Email: "random@hacker.com",
+		When:  time.Now(),
+	}
+
+	idx, err := repo.Index()
+	checkFatal(t, err)
+	err = idx.AddByPath(".gitignore")
+	checkFatal(t, err)
+	treeID, err := idx.WriteTree()
+	checkFatal(t, err)
+	err = idx.Write()
+	checkFatal(t, err)
+
+	currentBranch, err := repo.Head()
+	checkFatal(t, err)
+	currentTip, err := repo.LookupCommit(currentBranch.Target())
+	checkFatal(t, err)
+
+	message := "Add .gitignore\n"
+	tree, err := repo.LookupTree(treeID)
+	checkFatal(t, err)
+	_, err = repo.CreateCommit("HEAD", sig, sig, message, tree, currentTip)
+	checkFatal(t, err)
+
+	err = ioutil.WriteFile(pathInRepo(repo, "README"), []byte("Update README goes to stash\n"), 0644)
+	checkFatal(t, err)
+
+	err = ioutil.WriteFile(pathInRepo(repo, "untracked.txt"), []byte("Hello, World\n"), 0644)
+	checkFatal(t, err)
+}
+
+func fileExistsInRepo(repo *Repository, name string) bool {
+	if _, err := os.Stat(pathInRepo(repo, name)); err != nil {
+		return false
+	}
+	return true
+}
diff --git a/status.go b/status.go
index 068a474..e68e6e9 100644
--- a/status.go
+++ b/status.go
@@ -25,6 +25,7 @@
 	StatusWtTypeChange    Status = C.GIT_STATUS_WT_TYPECHANGE
 	StatusWtRenamed       Status = C.GIT_STATUS_WT_RENAMED
 	StatusIgnored         Status = C.GIT_STATUS_IGNORED
+	StatusConflicted      Status = C.GIT_STATUS_CONFLICTED
 )
 
 type StatusEntry struct {
diff --git a/status_test.go b/status_test.go
index 5b97b00..17ed94f 100644
--- a/status_test.go
+++ b/status_test.go
@@ -7,6 +7,7 @@
 )
 
 func TestStatusFile(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -27,6 +28,7 @@
 }
 
 func TestStatusList(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/submodule_test.go b/submodule_test.go
index 43c890a..fa2e98c 100644
--- a/submodule_test.go
+++ b/submodule_test.go
@@ -5,6 +5,7 @@
 )
 
 func TestSubmoduleForeach(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/tag_test.go b/tag_test.go
index 2fdfe00..3404923 100644
--- a/tag_test.go
+++ b/tag_test.go
@@ -7,6 +7,7 @@
 )
 
 func TestCreateTag(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -26,6 +27,7 @@
 }
 
 func TestCreateTagLightweight(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -50,6 +52,7 @@
 }
 
 func TestListTags(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -79,6 +82,7 @@
 }
 
 func TestListTagsWithMatch(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -116,6 +120,7 @@
 }
 
 func TestTagForeach(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/tree_test.go b/tree_test.go
index fae395a..f5b6822 100644
--- a/tree_test.go
+++ b/tree_test.go
@@ -3,6 +3,7 @@
 import "testing"
 
 func TestTreeEntryById(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
@@ -22,6 +23,7 @@
 }
 
 func TestTreeBuilderInsert(t *testing.T) {
+	t.Parallel()
 	repo := createTestRepo(t)
 	defer cleanupTestRepo(t, repo)
 
diff --git a/vendor/libgit2 b/vendor/libgit2
new file mode 160000
index 0000000..df4dfaa
--- /dev/null
+++ b/vendor/libgit2
@@ -0,0 +1 @@
+Subproject commit df4dfaadcf709646ebab2e57e3589952cf1ac809
diff --git a/walk.go b/walk.go
index 60e618d..ab1de61 100644
--- a/walk.go
+++ b/walk.go
@@ -173,10 +173,6 @@
 			return nil
 		}
 		if err != nil {
-			if err.(GitError).Code == ErrIterOver {
-				err = nil
-			}
-
 			return err
 		}
 
diff --git a/wrapper.c b/wrapper.c
index a0688c0..11c2f32 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -10,6 +10,12 @@
 	opts->remote_cb = (git_remote_create_cb)remoteCreateCallback;
 }
 
+void _go_git_populate_checkout_cb(git_checkout_options *opts)
+{
+	opts->notify_cb = (git_checkout_notify_cb)checkoutNotifyCallback;
+	opts->progress_cb = (git_checkout_progress_cb)checkoutProgressCallback;
+}
+
 int _go_git_visit_submodule(git_repository *repo, void *fct)
 {
 	  return git_submodule_foreach(repo, (gogit_submodule_cbk)&SubmoduleVisitor, fct);
@@ -108,19 +114,6 @@
 	callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback;
 }
 
-int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload)
-{
-    return blobChunkCb(buffer, maxLen, payload);
-}
-
-int _go_git_blob_create_fromchunks(git_oid *id,
-	git_repository *repo,
-	const char *hintpath,
-	void *payload)
-{
-    return git_blob_create_fromchunks(id, repo, hintpath, _go_blob_chunk_cb, payload);
-}
-
 int _go_git_index_add_all(git_index *index, const git_strarray *pathspec, unsigned int flags, void *callback) {
 	git_index_matched_path_cb cb = callback ? (git_index_matched_path_cb) &indexMatchedPathCallback : NULL;
 	return git_index_add_all(index, pathspec, flags, cb, callback);
@@ -164,4 +157,27 @@
 	return git_merge_file(out, &ancestor, &ours, &theirs, copts);
 }
 
+void _go_git_setup_stash_apply_progress_callbacks(git_stash_apply_options *opts) {
+	opts->progress_cb = (git_stash_apply_progress_cb)stashApplyProgressCb;
+}
+
+int _go_git_stash_foreach(git_repository *repo, void *payload) {
+    return git_stash_foreach(repo, (git_stash_cb)&stashForeachCb, payload);
+}
+
+int _go_git_writestream_write(git_writestream *stream, const char *buffer, size_t len)
+{
+	return stream->write(stream, buffer, len);
+}
+
+int _go_git_writestream_close(git_writestream *stream)
+{
+	return stream->close(stream);
+}
+
+void _go_git_writestream_free(git_writestream *stream)
+{
+	stream->free(stream);
+}
+
 /* EOF */