Merge pull request #173 from AaronO/add/graph-methods

Add bindings for git_graph_* methods
diff --git a/Makefile b/Makefile
index 4ecc8a4..3040857 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,7 @@
 	./script/build-libgit2-static.sh
 
 test: build-libgit2
+	go run script/check-MakeGitError-thread-lock.go
 	./script/with-static.sh go test ./...
 
 install: build-libgit2
diff --git a/README.md b/README.md
index d10c46e..faeb5f5 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,26 @@
 git2go
 ======
-[![GoDoc](https://godoc.org/github.com/libgit2/git2go?status.svg)](http://godoc.org/github.com/libgit2/git2go)
+[![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/). The master branch follows the latest libgit2 release.
+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.
 
 Installing
 ----------
 
-This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, you need `cmake`, `pkg-config` and a C compiler. You will also need the development packages for OpenSSL and LibSSH2 if you want to use HTTPS and SSH respectively.
+This project needs libgit2, which is written in C so we need to build that as well. In order to build libgit2, 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.
+
+### Stable version
+
+git2go has versioned branches which indicate which version of libgit2 they work against. Install the development package it 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
+
+    import "gopkg.in/libgit2/git2go.v22"
+
+to use a version of git2go which will work against libgit2 v0.22 and dynamically link to the library.
+
+### From master
+
+The master 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.
 
 Run `go get -d github.com/libgit2/git2go` to download the code and go to your `$GOPATH/src/github.com/libgit2/git2go` dir. From there, we need to build the C code and put it into the resulting go binary.
 
@@ -17,10 +29,15 @@
 
 will compile libgit2 and run `go install` such that it's statically linked to the git2go package.
 
+Paralellism 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.
+
 Running the tests
 -----------------
 
-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 master branch, similarly to installing, running the tests requires linking against the local libgit2 library, so the Makefile provides a wrapper
 
     make test
 
diff --git a/blame.go b/blame.go
new file mode 100644
index 0000000..c24c934
--- /dev/null
+++ b/blame.go
@@ -0,0 +1,157 @@
+package git
+
+/*
+#include <git2.h>
+*/
+import "C"
+import (
+	"runtime"
+	"unsafe"
+)
+
+type BlameOptions struct {
+	Flags              BlameOptionsFlag
+	MinMatchCharacters uint16
+	NewestCommit       *Oid
+	OldestCommit       *Oid
+	MinLine            uint32
+	MaxLine            uint32
+}
+
+func DefaultBlameOptions() (BlameOptions, error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	opts := C.git_blame_options{}
+	ecode := C.git_blame_init_options(&opts, C.GIT_BLAME_OPTIONS_VERSION)
+	if ecode < 0 {
+		return BlameOptions{}, MakeGitError(ecode)
+	}
+
+	return BlameOptions{
+		Flags:              BlameOptionsFlag(opts.flags),
+		MinMatchCharacters: uint16(opts.min_match_characters),
+		NewestCommit:       newOidFromC(&opts.newest_commit),
+		OldestCommit:       newOidFromC(&opts.oldest_commit),
+		MinLine:            uint32(opts.min_line),
+		MaxLine:            uint32(opts.max_line),
+	}, nil
+}
+
+type BlameOptionsFlag uint32
+
+const (
+	BlameNormal                      BlameOptionsFlag = C.GIT_BLAME_NORMAL
+	BlameTrackCopiesSameFile         BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
+	BlameTrackCopiesSameCommitMoves  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
+	BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
+	BlameTrackCopiesAnyCommitCopies  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
+	BlameFirstParent                 BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
+)
+
+func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
+	var blamePtr *C.git_blame
+
+	var copts *C.git_blame_options
+	if opts != nil {
+		copts = &C.git_blame_options{
+			version:              C.GIT_BLAME_OPTIONS_VERSION,
+			flags:                C.uint32_t(opts.Flags),
+			min_match_characters: C.uint16_t(opts.MinMatchCharacters),
+			min_line:             C.uint32_t(opts.MinLine),
+			max_line:             C.uint32_t(opts.MaxLine),
+		}
+		if opts.NewestCommit != nil {
+			copts.newest_commit = *opts.NewestCommit.toC()
+		}
+		if opts.OldestCommit != nil {
+			copts.oldest_commit = *opts.OldestCommit.toC()
+		}
+	}
+
+	cpath := C.CString(path)
+	defer C.free(unsafe.Pointer(cpath))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts)
+	if ecode < 0 {
+		return nil, MakeGitError(ecode)
+	}
+
+	return newBlameFromC(blamePtr), nil
+}
+
+type Blame struct {
+	ptr *C.git_blame
+}
+
+func (blame *Blame) HunkCount() int {
+	return int(C.git_blame_get_hunk_count(blame.ptr))
+}
+
+func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
+	ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
+	if ptr == nil {
+		return BlameHunk{}, ErrInvalid
+	}
+	return blameHunkFromC(ptr), nil
+}
+
+func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) {
+	ptr := C.git_blame_get_hunk_byline(blame.ptr, C.uint32_t(lineno))
+	if ptr == nil {
+		return BlameHunk{}, ErrInvalid
+	}
+	return blameHunkFromC(ptr), nil
+}
+
+func newBlameFromC(ptr *C.git_blame) *Blame {
+	if ptr == nil {
+		return nil
+	}
+
+	blame := &Blame{
+		ptr: ptr,
+	}
+
+	runtime.SetFinalizer(blame, (*Blame).Free)
+	return blame
+}
+
+func (blame *Blame) Free() error {
+	if blame.ptr == nil {
+		return ErrInvalid
+	}
+	runtime.SetFinalizer(blame, nil)
+	C.git_blame_free(blame.ptr)
+	blame.ptr = nil
+	return nil
+}
+
+type BlameHunk struct {
+	LinesInHunk          uint16
+	FinalCommitId        *Oid
+	FinalStartLineNumber uint16
+	FinalSignature       *Signature
+	OrigCommitId         *Oid
+	OrigPath             string
+	OrigStartLineNumber  uint16
+	OrigSignature        *Signature
+	Boundary             bool
+}
+
+func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
+	return BlameHunk{
+		LinesInHunk:          uint16(hunk.lines_in_hunk),
+		FinalCommitId:        newOidFromC(&hunk.final_commit_id),
+		FinalStartLineNumber: uint16(hunk.final_start_line_number),
+		FinalSignature:       newSignatureFromC(hunk.final_signature),
+		OrigCommitId:         newOidFromC(&hunk.orig_commit_id),
+		OrigPath:             C.GoString(hunk.orig_path),
+		OrigStartLineNumber:  uint16(hunk.orig_start_line_number),
+		OrigSignature:        newSignatureFromC(hunk.orig_signature),
+		Boundary:             hunk.boundary == 1,
+	}
+}
diff --git a/blame_test.go b/blame_test.go
new file mode 100644
index 0000000..1785042
--- /dev/null
+++ b/blame_test.go
@@ -0,0 +1,74 @@
+package git
+
+import (
+	"os"
+	"reflect"
+	"testing"
+)
+
+func TestBlame(t *testing.T) {
+	repo := createTestRepo(t)
+	defer repo.Free()
+	defer os.RemoveAll(repo.Workdir())
+
+	commitId1, _ := seedTestRepo(t, repo)
+	commitId2, _ := updateReadme(t, repo, "foo\nbar\nbaz\n")
+
+	opts := BlameOptions{
+		NewestCommit: commitId2,
+		OldestCommit: nil,
+		MinLine:      1,
+		MaxLine:      3,
+	}
+	blame, err := repo.BlameFile("README", &opts)
+	checkFatal(t, err)
+	defer blame.Free()
+	if blame.HunkCount() != 2 {
+		t.Errorf("got hunk count %d, want 2", blame.HunkCount())
+	}
+
+	wantHunk1 := BlameHunk{
+		LinesInHunk:          1,
+		FinalCommitId:        commitId1,
+		FinalStartLineNumber: 1,
+		OrigCommitId:         commitId1,
+		OrigPath:             "README",
+		OrigStartLineNumber:  1,
+		Boundary:             true,
+	}
+	wantHunk2 := BlameHunk{
+		LinesInHunk:          2,
+		FinalCommitId:        commitId2,
+		FinalStartLineNumber: 2,
+		OrigCommitId:         commitId2,
+		OrigPath:             "README",
+		OrigStartLineNumber:  2,
+		Boundary:             false,
+	}
+
+	hunk1, err := blame.HunkByIndex(0)
+	checkFatal(t, err)
+	checkHunk(t, "index 0", hunk1, wantHunk1)
+
+	hunk2, err := blame.HunkByIndex(1)
+	checkFatal(t, err)
+	checkHunk(t, "index 1", hunk2, wantHunk2)
+
+	hunkLine1, err := blame.HunkByLine(1)
+	checkFatal(t, err)
+	checkHunk(t, "line 1", hunkLine1, wantHunk1)
+
+	hunkLine2, err := blame.HunkByLine(3)
+	checkFatal(t, err)
+	checkHunk(t, "line 2", hunkLine2, wantHunk2)
+}
+
+func checkHunk(t *testing.T, label string, hunk, want BlameHunk) {
+	hunk.FinalSignature = nil
+	want.FinalSignature = nil
+	hunk.OrigSignature = nil
+	want.OrigSignature = nil
+	if !reflect.DeepEqual(hunk, want) {
+		t.Fatalf("%s: got hunk %+v, want %+v", label, hunk, want)
+	}
+}
diff --git a/blob.go b/blob.go
index 58258fd..5a33bd8 100644
--- a/blob.go
+++ b/blob.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 #include <string.h>
 
 extern int _go_git_blob_create_fromchunks(git_oid *id,
diff --git a/branch.go b/branch.go
index 95c3807..bb231c3 100644
--- a/branch.go
+++ b/branch.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 
diff --git a/checkout.go b/checkout.go
index 9c7188e..06d010c 100644
--- a/checkout.go
+++ b/checkout.go
@@ -7,6 +7,7 @@
 import (
 	"os"
 	"runtime"
+	"unsafe"
 )
 
 type CheckoutStrategy uint
@@ -31,11 +32,12 @@
 )
 
 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
+	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
 }
 
 func (opts *CheckoutOpts) toC() *C.git_checkout_options {
@@ -60,17 +62,29 @@
 	ptr.disable_filters = cbool(opts.DisableFilters)
 	ptr.dir_mode = C.uint(opts.DirMode.Perm())
 	ptr.file_mode = C.uint(opts.FileMode.Perm())
-
+	if opts.TargetDirectory != "" {
+		ptr.target_directory = C.CString(opts.TargetDirectory)
+	}
 	return ptr
 }
 
+func freeCheckoutOpts(ptr *C.git_checkout_options) {
+	if ptr == nil {
+		return
+	}
+	C.free(unsafe.Pointer(ptr.target_directory))
+}
+
 // Updates files in the index and the working tree to match the content of
 // the commit pointed at by HEAD. opts may be nil.
 func (v *Repository) CheckoutHead(opts *CheckoutOpts) error {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	ret := C.git_checkout_head(v.ptr, opts.toC())
+	cOpts := opts.toC()
+	defer freeCheckoutOpts(cOpts)
+
+	ret := C.git_checkout_head(v.ptr, cOpts)
 	if ret < 0 {
 		return MakeGitError(ret)
 	}
@@ -90,7 +104,10 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	ret := C.git_checkout_index(v.ptr, iptr, opts.toC())
+	cOpts := opts.toC()
+	defer freeCheckoutOpts(cOpts)
+
+	ret := C.git_checkout_index(v.ptr, iptr, cOpts)
 	if ret < 0 {
 		return MakeGitError(ret)
 	}
@@ -102,7 +119,10 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	ret := C.git_checkout_tree(v.ptr, tree.ptr, opts.toC())
+	cOpts := opts.toC()
+	defer freeCheckoutOpts(cOpts)
+
+	ret := C.git_checkout_tree(v.ptr, tree.ptr, cOpts)
 	if ret < 0 {
 		return MakeGitError(ret)
 	}
diff --git a/clone.go b/clone.go
index 958e65d..b796b6e 100644
--- a/clone.go
+++ b/clone.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 */
 import "C"
@@ -31,6 +30,7 @@
 
 	var copts C.git_clone_options
 	populateCloneOptions(&copts, options)
+	defer freeCheckoutOpts(&copts.checkout_opts)
 
 	if len(options.CheckoutBranch) != 0 {
 		copts.checkout_branch = C.CString(options.CheckoutBranch)
diff --git a/commit.go b/commit.go
index ed026a4..559a1bd 100644
--- a/commit.go
+++ b/commit.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
 */
diff --git a/config.go b/config.go
index 5bcb0f8..9d25e35 100644
--- a/config.go
+++ b/config.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 import (
@@ -36,14 +35,14 @@
 )
 
 type ConfigEntry struct {
-	Name string
+	Name  string
 	Value string
 	Level ConfigLevel
 }
 
 func newConfigEntryFromC(centry *C.git_config_entry) *ConfigEntry {
 	return &ConfigEntry{
-		Name: C.GoString(centry.name),
+		Name:  C.GoString(centry.name),
 		Value: C.GoString(centry.value),
 		Level: ConfigLevel(centry.level),
 	}
@@ -75,7 +74,6 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-
 	ret := C.git_config_add_file_ondisk(c.ptr, cpath, C.git_config_level_t(level), cbool(force))
 	if ret < 0 {
 		return MakeGitError(ret)
@@ -131,7 +129,6 @@
 	return C.GoString(ptr), nil
 }
 
-
 func (c *Config) LookupBool(name string) (bool, error) {
 	var out C.int
 	cname := C.CString(name)
@@ -235,6 +232,9 @@
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_config_set_int32(c.ptr, cname, C.int32_t(value))
 	if ret < 0 {
 		return MakeGitError(ret)
@@ -350,6 +350,9 @@
 func (iter *ConfigIterator) Next() (*ConfigEntry, error) {
 	var centry *C.git_config_entry
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_config_next(&centry, iter.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -362,3 +365,48 @@
 	runtime.SetFinalizer(iter, nil)
 	C.free(unsafe.Pointer(iter.ptr))
 }
+
+func ConfigFindGlobal() (string, error) {
+	var buf C.git_buf
+	defer C.git_buf_free(&buf)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_config_find_global(&buf)
+	if ret < 0 {
+		return "", MakeGitError(ret)
+	}
+
+	return C.GoString(buf.ptr), nil
+}
+
+func ConfigFindSystem() (string, error) {
+	var buf C.git_buf
+	defer C.git_buf_free(&buf)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_config_find_system(&buf)
+	if ret < 0 {
+		return "", MakeGitError(ret)
+	}
+
+	return C.GoString(buf.ptr), nil
+}
+
+func ConfigFindXDG() (string, error) {
+	var buf C.git_buf
+	defer C.git_buf_free(&buf)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_config_find_xdg(&buf)
+	if ret < 0 {
+		return "", MakeGitError(ret)
+	}
+
+	return C.GoString(buf.ptr), nil
+}
diff --git a/credentials.go b/credentials.go
index b04bf98..bb0ec41 100644
--- a/credentials.go
+++ b/credentials.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 import "unsafe"
diff --git a/diff.go b/diff.go
index f762a56..63fa867 100644
--- a/diff.go
+++ b/diff.go
@@ -164,6 +164,72 @@
 	return nil
 }
 
+func (diff *Diff) FindSimilar(opts *DiffFindOptions) error {
+
+	var copts *C.git_diff_find_options
+	if opts != nil {
+		copts = &C.git_diff_find_options{
+			version:                       C.GIT_DIFF_FIND_OPTIONS_VERSION,
+			flags:                         C.uint32_t(opts.Flags),
+			rename_threshold:              C.uint16_t(opts.RenameThreshold),
+			copy_threshold:                C.uint16_t(opts.CopyThreshold),
+			rename_from_rewrite_threshold: C.uint16_t(opts.RenameFromRewriteThreshold),
+			break_rewrite_threshold:       C.uint16_t(opts.BreakRewriteThreshold),
+			rename_limit:                  C.size_t(opts.RenameLimit),
+		}
+	}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_diff_find_similar(diff.ptr, copts)
+	if ecode < 0 {
+		return MakeGitError(ecode)
+	}
+
+	return nil
+}
+
+type DiffStats struct {
+	ptr *C.git_diff_stats
+}
+
+func (stats *DiffStats) Free() error {
+	if stats.ptr == nil {
+		return ErrInvalid
+	}
+	runtime.SetFinalizer(stats, nil)
+	C.git_diff_stats_free(stats.ptr)
+	stats.ptr = nil
+	return nil
+}
+
+func (stats *DiffStats) Insertions() int {
+	return int(C.git_diff_stats_insertions(stats.ptr))
+}
+
+func (stats *DiffStats) Deletions() int {
+	return int(C.git_diff_stats_deletions(stats.ptr))
+}
+
+func (stats *DiffStats) FilesChanged() int {
+	return int(C.git_diff_stats_files_changed(stats.ptr))
+}
+
+func (diff *Diff) Stats() (*DiffStats, error) {
+	stats := new(DiffStats)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if ecode := C.git_diff_get_stats(&stats.ptr, diff.ptr); ecode < 0 {
+		return nil, MakeGitError(ecode)
+	}
+	runtime.SetFinalizer(stats, (*DiffStats).Free)
+
+	return stats, nil
+}
+
 type diffForEachData struct {
 	FileCallback DiffForEachFileCallback
 	HunkCallback DiffForEachHunkCallback
@@ -264,6 +330,9 @@
 	}
 	var patchPtr *C.git_patch
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ecode := C.git_patch_from_diff(&patchPtr, diff.ptr, C.size_t(deltaIndex))
 	if ecode < 0 {
 		return nil, MakeGitError(ecode)
@@ -313,8 +382,8 @@
 	Pathspec         []string
 	NotifyCallback   DiffNotifyCallback
 
-	ContextLines   uint16
-	InterhunkLines uint16
+	ContextLines   uint32
+	InterhunkLines uint32
 	IdAbbrev       uint16
 
 	MaxSize int
@@ -325,6 +394,10 @@
 
 func DefaultDiffOptions() (DiffOptions, error) {
 	opts := C.git_diff_options{}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ecode := C.git_diff_init_options(&opts, C.GIT_DIFF_OPTIONS_VERSION)
 	if ecode < 0 {
 		return DiffOptions{}, MakeGitError(ecode)
@@ -334,10 +407,64 @@
 		Flags:            DiffOptionsFlag(opts.flags),
 		IgnoreSubmodules: SubmoduleIgnore(opts.ignore_submodules),
 		Pathspec:         makeStringsFromCStrings(opts.pathspec.strings, int(opts.pathspec.count)),
-		ContextLines:     uint16(opts.context_lines),
-		InterhunkLines:   uint16(opts.interhunk_lines),
+		ContextLines:     uint32(opts.context_lines),
+		InterhunkLines:   uint32(opts.interhunk_lines),
 		IdAbbrev:         uint16(opts.id_abbrev),
 		MaxSize:          int(opts.max_size),
+		OldPrefix:        "a",
+		NewPrefix:        "b",
+	}, nil
+}
+
+type DiffFindOptionsFlag int
+
+const (
+	DiffFindByConfig                    DiffFindOptionsFlag = C.GIT_DIFF_FIND_BY_CONFIG
+	DiffFindRenames                     DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES
+	DiffFindRenamesFromRewrites         DiffFindOptionsFlag = C.GIT_DIFF_FIND_RENAMES_FROM_REWRITES
+	DiffFindCopies                      DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES
+	DiffFindCopiesFromUnmodified        DiffFindOptionsFlag = C.GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED
+	DiffFindRewrites                    DiffFindOptionsFlag = C.GIT_DIFF_FIND_REWRITES
+	DiffFindBreakRewrites               DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES
+	DiffFindAndBreakRewrites            DiffFindOptionsFlag = C.GIT_DIFF_FIND_AND_BREAK_REWRITES
+	DiffFindForUntracked                DiffFindOptionsFlag = C.GIT_DIFF_FIND_FOR_UNTRACKED
+	DiffFindAll                         DiffFindOptionsFlag = C.GIT_DIFF_FIND_ALL
+	DiffFindIgnoreLeadingWhitespace     DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE
+	DiffFindIgnoreWhitespace            DiffFindOptionsFlag = C.GIT_DIFF_FIND_IGNORE_WHITESPACE
+	DiffFindDontIgnoreWhitespace        DiffFindOptionsFlag = C.GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE
+	DiffFindExactMatchOnly              DiffFindOptionsFlag = C.GIT_DIFF_FIND_EXACT_MATCH_ONLY
+	DiffFindBreakRewritesForRenamesOnly DiffFindOptionsFlag = C.GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY
+	DiffFindRemoveUnmodified            DiffFindOptionsFlag = C.GIT_DIFF_FIND_REMOVE_UNMODIFIED
+)
+
+//TODO implement git_diff_similarity_metric
+type DiffFindOptions struct {
+	Flags                      DiffFindOptionsFlag
+	RenameThreshold            uint16
+	CopyThreshold              uint16
+	RenameFromRewriteThreshold uint16
+	BreakRewriteThreshold      uint16
+	RenameLimit                uint
+}
+
+func DefaultDiffFindOptions() (DiffFindOptions, error) {
+	opts := C.git_diff_find_options{}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_diff_find_init_options(&opts, C.GIT_DIFF_FIND_OPTIONS_VERSION)
+	if ecode < 0 {
+		return DiffFindOptions{}, MakeGitError(ecode)
+	}
+
+	return DiffFindOptions{
+		Flags:                      DiffFindOptionsFlag(opts.flags),
+		RenameThreshold:            uint16(opts.rename_threshold),
+		CopyThreshold:              uint16(opts.copy_threshold),
+		RenameFromRewriteThreshold: uint16(opts.rename_from_rewrite_threshold),
+		BreakRewriteThreshold:      uint16(opts.break_rewrite_threshold),
+		RenameLimit:                uint(opts.rename_limit),
 	}, nil
 }
 
@@ -374,6 +501,47 @@
 	return 0
 }
 
+func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *diffNotifyData) {
+	cpathspec := C.git_strarray{}
+	if opts != nil {
+		notifyData = &diffNotifyData{
+			Callback: opts.NotifyCallback,
+		}
+		if opts.Pathspec != nil {
+			cpathspec.count = C.size_t(len(opts.Pathspec))
+			cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
+		}
+
+		copts = &C.git_diff_options{
+			version:           C.GIT_DIFF_OPTIONS_VERSION,
+			flags:             C.uint32_t(opts.Flags),
+			ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
+			pathspec:          cpathspec,
+			context_lines:     C.uint32_t(opts.ContextLines),
+			interhunk_lines:   C.uint32_t(opts.InterhunkLines),
+			id_abbrev:         C.uint16_t(opts.IdAbbrev),
+			max_size:          C.git_off_t(opts.MaxSize),
+			old_prefix:        C.CString(opts.OldPrefix),
+			new_prefix:        C.CString(opts.NewPrefix),
+		}
+
+		if opts.NotifyCallback != nil {
+			C._go_git_setup_diff_notify_callbacks(copts)
+			copts.notify_payload = unsafe.Pointer(notifyData)
+		}
+	}
+	return
+}
+
+func freeDiffOptions(copts *C.git_diff_options) {
+	if copts != nil {
+		cpathspec := copts.pathspec
+		freeStrarray(&cpathspec)
+		C.free(unsafe.Pointer(copts.old_prefix))
+		C.free(unsafe.Pointer(copts.new_prefix))
+	}
+}
+
 func (v *Repository) DiffTreeToTree(oldTree, newTree *Tree, opts *DiffOptions) (*Diff, error) {
 	var diffPtr *C.git_diff
 	var oldPtr, newPtr *C.git_tree
@@ -386,35 +554,11 @@
 		newPtr = newTree.cast_ptr
 	}
 
-	cpathspec := C.git_strarray{}
-	var copts *C.git_diff_options
-	var notifyData *diffNotifyData
-	if opts != nil {
-		notifyData = &diffNotifyData{
-			Callback: opts.NotifyCallback,
-		}
-		if opts.Pathspec != nil {
-			cpathspec.count = C.size_t(len(opts.Pathspec))
-			cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
-			defer freeStrarray(&cpathspec)
-		}
+	copts, notifyData := diffOptionsToC(opts)
+	defer freeDiffOptions(copts)
 
-		copts = &C.git_diff_options{
-			version:           C.GIT_DIFF_OPTIONS_VERSION,
-			flags:             C.uint32_t(opts.Flags),
-			ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
-			pathspec:          cpathspec,
-			context_lines:     C.uint16_t(opts.ContextLines),
-			interhunk_lines:   C.uint16_t(opts.InterhunkLines),
-			id_abbrev:         C.uint16_t(opts.IdAbbrev),
-			max_size:          C.git_off_t(opts.MaxSize),
-		}
-
-		if opts.NotifyCallback != nil {
-			C._go_git_setup_diff_notify_callbacks(copts)
-			copts.notify_payload = unsafe.Pointer(notifyData)
-		}
-	}
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
 
 	ecode := C.git_diff_tree_to_tree(&diffPtr, v.ptr, oldPtr, newPtr, copts)
 	if ecode < 0 {
@@ -435,35 +579,11 @@
 		oldPtr = oldTree.cast_ptr
 	}
 
-	cpathspec := C.git_strarray{}
-	var copts *C.git_diff_options
-	var notifyData *diffNotifyData
-	if opts != nil {
-		notifyData = &diffNotifyData{
-			Callback: opts.NotifyCallback,
-		}
-		if opts.Pathspec != nil {
-			cpathspec.count = C.size_t(len(opts.Pathspec))
-			cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
-			defer freeStrarray(&cpathspec)
-		}
+	copts, notifyData := diffOptionsToC(opts)
+	defer freeDiffOptions(copts)
 
-		copts = &C.git_diff_options{
-			version:           C.GIT_DIFF_OPTIONS_VERSION,
-			flags:             C.uint32_t(opts.Flags),
-			ignore_submodules: C.git_submodule_ignore_t(opts.IgnoreSubmodules),
-			pathspec:          cpathspec,
-			context_lines:     C.uint16_t(opts.ContextLines),
-			interhunk_lines:   C.uint16_t(opts.InterhunkLines),
-			id_abbrev:         C.uint16_t(opts.IdAbbrev),
-			max_size:          C.git_off_t(opts.MaxSize),
-		}
-
-		if opts.NotifyCallback != nil {
-			C._go_git_setup_diff_notify_callbacks(copts)
-			copts.notify_payload = unsafe.Pointer(notifyData)
-		}
-	}
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
 
 	ecode := C.git_diff_tree_to_workdir(&diffPtr, v.ptr, oldPtr, copts)
 	if ecode < 0 {
@@ -474,5 +594,4 @@
 		return notifyData.Diff, nil
 	}
 	return newDiffFromC(diffPtr), nil
-
 }
diff --git a/diff_test.go b/diff_test.go
index b688294..fc6fed9 100644
--- a/diff_test.go
+++ b/diff_test.go
@@ -3,23 +3,72 @@
 import (
 	"errors"
 	"os"
+	"strings"
 	"testing"
 )
 
-func TestDiffTreeToTree(t *testing.T) {
+func TestFindSimilar(t *testing.T) {
 	repo := createTestRepo(t)
 	defer repo.Free()
 	defer os.RemoveAll(repo.Workdir())
 
-	_, originalTreeId := seedTestRepo(t, repo)
-	originalTree, err := repo.LookupTree(originalTreeId)
+	originalTree, newTree := createTestTrees(t, repo)
 
+	diffOpt, _ := DefaultDiffOptions()
+
+	diff, err := repo.DiffTreeToTree(originalTree, newTree, &diffOpt)
+	checkFatal(t, err)
+	if diff == nil {
+		t.Fatal("no diff returned")
+	}
+
+	findOpts, err := DefaultDiffFindOptions()
+	checkFatal(t, err)
+	findOpts.Flags = DiffFindBreakRewrites
+
+	err = diff.FindSimilar(&findOpts)
 	checkFatal(t, err)
 
-	_, newTreeId := updateReadme(t, repo, "file changed\n")
+	numDiffs := 0
+	numAdded := 0
+	numDeleted := 0
 
-	newTree, err := repo.LookupTree(newTreeId)
-	checkFatal(t, err)
+	err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
+		numDiffs++
+
+		switch file.Status {
+		case DeltaAdded:
+			numAdded++
+		case DeltaDeleted:
+			numDeleted++
+		}
+
+		return func(hunk DiffHunk) (DiffForEachLineCallback, error) {
+			return func(line DiffLine) error {
+				return nil
+			}, nil
+		}, nil
+	}, DiffDetailLines)
+
+	if numDiffs != 2 {
+		t.Fatal("Incorrect number of files in diff")
+	}
+	if numAdded != 1 {
+		t.Fatal("Incorrect number of new files in diff")
+	}
+	if numDeleted != 1 {
+		t.Fatal("Incorrect number of deleted files in diff")
+	}
+
+}
+
+func TestDiffTreeToTree(t *testing.T) {
+
+	repo := createTestRepo(t)
+	defer repo.Free()
+	defer os.RemoveAll(repo.Workdir())
+
+	originalTree, newTree := createTestTrees(t, repo)
 
 	callbackInvoked := false
 	opts := DiffOptions{
@@ -27,6 +76,8 @@
 			callbackInvoked = true
 			return nil
 		},
+		OldPrefix: "x1/",
+		NewPrefix: "y1/",
 	}
 
 	diff, err := repo.DiffTreeToTree(originalTree, newTree, &opts)
@@ -42,7 +93,19 @@
 	files := make([]string, 0)
 	hunks := make([]DiffHunk, 0)
 	lines := make([]DiffLine, 0)
+	patches := make([]string, 0)
 	err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
+		patch, err := diff.Patch(len(patches))
+		if err != nil {
+			return nil, err
+		}
+		defer patch.Free()
+		patchStr, err := patch.String()
+		if err != nil {
+			return nil, err
+		}
+		patches = append(patches, patchStr)
+
 		files = append(files, file.OldFile.Path)
 		return func(hunk DiffHunk) (DiffForEachLineCallback, error) {
 			hunks = append(hunks, hunk)
@@ -83,6 +146,24 @@
 		t.Fatal("Incorrect lines in diff")
 	}
 
+	if want1, want2 := "x1/README", "y1/README"; !strings.Contains(patches[0], want1) || !strings.Contains(patches[0], want2) {
+		t.Errorf("Diff patch doesn't contain %q or %q\n\n%s", want1, want2, patches[0])
+
+	}
+
+	stats, err := diff.Stats()
+	checkFatal(t, err)
+
+	if stats.Insertions() != 1 {
+		t.Fatal("Incorrect number of insertions in diff")
+	}
+	if stats.Deletions() != 1 {
+		t.Fatal("Incorrect number of deletions in diff")
+	}
+	if stats.FilesChanged() != 1 {
+		t.Fatal("Incorrect number of changed files in diff")
+	}
+
 	errTest := errors.New("test error")
 
 	err = diff.ForEach(func(file DiffDelta, progress float64) (DiffForEachHunkCallback, error) {
@@ -94,3 +175,18 @@
 	}
 
 }
+
+func createTestTrees(t *testing.T, repo *Repository) (originalTree *Tree, newTree *Tree) {
+	var err error
+	_, originalTreeId := seedTestRepo(t, repo)
+	originalTree, err = repo.LookupTree(originalTreeId)
+
+	checkFatal(t, err)
+
+	_, newTreeId := updateReadme(t, repo, "file changed\n")
+
+	newTree, err = repo.LookupTree(newTreeId)
+	checkFatal(t, err)
+
+	return originalTree, newTree
+}
diff --git a/git.go b/git.go
index adfa3b0..8e78710 100644
--- a/git.go
+++ b/git.go
@@ -2,7 +2,7 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
+#include <git2/sys/openssl.h>
 */
 import "C"
 import (
@@ -93,7 +93,14 @@
 )
 
 func init() {
-	C.git_threads_init()
+	C.git_libgit2_init()
+
+	// 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
+	// in such a way that they can be sure they're the only ones
+	// setting it up.
+	C.git_openssl_set_locking()
 }
 
 // Oid represents the id for a Git object.
diff --git a/index.go b/index.go
index 230e159..6b90758 100644
--- a/index.go
+++ b/index.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern int _go_git_index_add_all(git_index*, const git_strarray*, unsigned int, void*);
 extern int _go_git_index_update_all(git_index*, const git_strarray*, void*);
diff --git a/merge.go b/merge.go
index 93ac71b..5b68a8b 100644
--- a/merge.go
+++ b/merge.go
@@ -2,11 +2,10 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
-extern git_merge_head** _go_git_make_merge_head_array(size_t len);
-extern void _go_git_merge_head_array_set(git_merge_head** array, git_merge_head* ptr, size_t n);
-extern git_merge_head* _go_git_merge_head_array_get(git_merge_head** array, size_t n);
+extern git_annotated_commit** _go_git_make_merge_head_array(size_t len);
+extern void _go_git_annotated_commit_array_set(git_annotated_commit** array, git_annotated_commit* ptr, size_t n);
+extern git_annotated_commit* _go_git_annotated_commit_array_get(git_annotated_commit** array, size_t n);
 
 */
 import "C"
@@ -15,23 +14,23 @@
 	"unsafe"
 )
 
-type MergeHead struct {
-	ptr *C.git_merge_head
+type AnnotatedCommit struct {
+	ptr *C.git_annotated_commit
 }
 
-func newMergeHeadFromC(c *C.git_merge_head) *MergeHead {
-	mh := &MergeHead{ptr: c}
-	runtime.SetFinalizer(mh, (*MergeHead).Free)
+func newAnnotatedCommitFromC(c *C.git_annotated_commit) *AnnotatedCommit {
+	mh := &AnnotatedCommit{ptr: c}
+	runtime.SetFinalizer(mh, (*AnnotatedCommit).Free)
 	return mh
 }
 
-func (mh *MergeHead) Free() {
+func (mh *AnnotatedCommit) Free() {
 	runtime.SetFinalizer(mh, nil)
-	C.git_merge_head_free(mh.ptr)
+	C.git_annotated_commit_free(mh.ptr)
 }
 
-func (r *Repository) MergeHeadFromFetchHead(branchName string, remoteURL string, oid *Oid) (*MergeHead, error) {
-	mh := &MergeHead{}
+func (r *Repository) AnnotatedCommitFromFetchHead(branchName string, remoteURL string, oid *Oid) (*AnnotatedCommit, error) {
+	mh := &AnnotatedCommit{}
 
 	cbranchName := C.CString(branchName)
 	defer C.free(unsafe.Pointer(cbranchName))
@@ -39,33 +38,42 @@
 	cremoteURL := C.CString(remoteURL)
 	defer C.free(unsafe.Pointer(cremoteURL))
 
-	ret := C.git_merge_head_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC())
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_annotated_commit_from_fetchhead(&mh.ptr, r.ptr, cbranchName, cremoteURL, oid.toC())
 	if ret < 0 {
 		return nil, MakeGitError(ret)
 	}
-	runtime.SetFinalizer(mh, (*MergeHead).Free)
+	runtime.SetFinalizer(mh, (*AnnotatedCommit).Free)
 	return mh, nil
 }
 
-func (r *Repository) MergeHeadFromId(oid *Oid) (*MergeHead, error) {
-	mh := &MergeHead{}
+func (r *Repository) LookupAnnotatedCommit(oid *Oid) (*AnnotatedCommit, error) {
+	mh := &AnnotatedCommit{}
 
-	ret := C.git_merge_head_from_id(&mh.ptr, r.ptr, oid.toC())
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_annotated_commit_lookup(&mh.ptr, r.ptr, oid.toC())
 	if ret < 0 {
 		return nil, MakeGitError(ret)
 	}
-	runtime.SetFinalizer(mh, (*MergeHead).Free)
+	runtime.SetFinalizer(mh, (*AnnotatedCommit).Free)
 	return mh, nil
 }
 
-func (r *Repository) MergeHeadFromRef(ref *Reference) (*MergeHead, error) {
-	mh := &MergeHead{}
+func (r *Repository) AnnotatedCommitFromRef(ref *Reference) (*AnnotatedCommit, error) {
+	mh := &AnnotatedCommit{}
 
-	ret := C.git_merge_head_from_ref(&mh.ptr, r.ptr, ref.ptr)
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_annotated_commit_from_ref(&mh.ptr, r.ptr, ref.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
 	}
-	runtime.SetFinalizer(mh, (*MergeHead).Free)
+	runtime.SetFinalizer(mh, (*AnnotatedCommit).Free)
 	return mh, nil
 }
 
@@ -98,6 +106,10 @@
 
 func DefaultMergeOptions() (MergeOptions, error) {
 	opts := C.git_merge_options{}
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ecode := C.git_merge_init_options(&opts, C.GIT_MERGE_OPTIONS_VERSION)
 	if ecode < 0 {
 		return MergeOptions{}, MakeGitError(ecode)
@@ -127,19 +139,20 @@
 	MergeFileFavorUnion  MergeFileFavor = C.GIT_MERGE_FILE_FAVOR_UNION
 )
 
-func (r *Repository) Merge(theirHeads []*MergeHead, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error {
+func (r *Repository) Merge(theirHeads []*AnnotatedCommit, mergeOptions *MergeOptions, checkoutOptions *CheckoutOpts) error {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
 	cMergeOpts := mergeOptions.toC()
 	cCheckoutOpts := checkoutOptions.toC()
+	defer freeCheckoutOpts(cCheckoutOpts)
 
-	gmerge_head_array := make([]*C.git_merge_head, len(theirHeads))
+	gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
 	for i := 0; i < len(theirHeads); i++ {
 		gmerge_head_array[i] = theirHeads[i].ptr
 	}
 	ptr := unsafe.Pointer(&gmerge_head_array[0])
-	err := C.git_merge(r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts)
+	err := C.git_merge(r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)), cMergeOpts, cCheckoutOpts)
 	if err < 0 {
 		return MakeGitError(err)
 	}
@@ -164,18 +177,18 @@
 	MergePreferenceFastForwardOnly MergePreference = C.GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY
 )
 
-func (r *Repository) MergeAnalysis(theirHeads []*MergeHead) (MergeAnalysis, MergePreference, error) {
+func (r *Repository) MergeAnalysis(theirHeads []*AnnotatedCommit) (MergeAnalysis, MergePreference, error) {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	gmerge_head_array := make([]*C.git_merge_head, len(theirHeads))
+	gmerge_head_array := make([]*C.git_annotated_commit, len(theirHeads))
 	for i := 0; i < len(theirHeads); i++ {
 		gmerge_head_array[i] = theirHeads[i].ptr
 	}
 	ptr := unsafe.Pointer(&gmerge_head_array[0])
 	var analysis C.git_merge_analysis_t
 	var preference C.git_merge_preference_t
-	err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_merge_head)(ptr), C.size_t(len(theirHeads)))
+	err := C.git_merge_analysis(&analysis, &preference, r.ptr, (**C.git_annotated_commit)(ptr), C.size_t(len(theirHeads)))
 	if err < 0 {
 		return MergeAnalysisNone, MergePreferenceNone, MakeGitError(err)
 	}
diff --git a/merge_test.go b/merge_test.go
index 7e884c0..1eba806 100644
--- a/merge_test.go
+++ b/merge_test.go
@@ -13,10 +13,10 @@
 	master, err := repo.LookupReference("refs/heads/master")
 	checkFatal(t, err)
 
-	mergeHead, err := repo.MergeHeadFromRef(master)
+	mergeHead, err := repo.AnnotatedCommitFromRef(master)
 	checkFatal(t, err)
 
-	mergeHeads := make([]*MergeHead, 1)
+	mergeHeads := make([]*AnnotatedCommit, 1)
 	mergeHeads[0] = mergeHead
 	err = repo.Merge(mergeHeads, nil, nil)
 	checkFatal(t, err)
@@ -30,10 +30,10 @@
 	master, err := repo.LookupReference("refs/heads/master")
 	checkFatal(t, err)
 
-	mergeHead, err := repo.MergeHeadFromRef(master)
+	mergeHead, err := repo.AnnotatedCommitFromRef(master)
 	checkFatal(t, err)
 
-	mergeHeads := make([]*MergeHead, 1)
+	mergeHeads := make([]*AnnotatedCommit, 1)
 	mergeHeads[0] = mergeHead
 	a, _, err := repo.MergeAnalysis(mergeHeads)
 	checkFatal(t, err)
diff --git a/note.go b/note.go
new file mode 100644
index 0000000..3cdd340
--- /dev/null
+++ b/note.go
@@ -0,0 +1,99 @@
+package git
+
+/*
+#include <git2.h>
+*/
+import "C"
+
+import (
+	"runtime"
+	"unsafe"
+)
+
+// Note
+type Note struct {
+	ptr *C.git_note
+}
+
+// Free frees a git_note object
+func (n *Note) Free() error {
+	if n.ptr == nil {
+		return ErrInvalid
+	}
+	runtime.SetFinalizer(n, nil)
+	C.git_note_free(n.ptr)
+	n.ptr = nil
+	return nil
+}
+
+// Author returns the signature of the note author
+func (n *Note) Author() *Signature {
+	ptr := C.git_note_author(n.ptr)
+	return newSignatureFromC(ptr)
+}
+
+// Id returns the note object's id
+func (n *Note) Id() *Oid {
+	ptr := C.git_note_id(n.ptr)
+	return newOidFromC(ptr)
+}
+
+// Committer returns the signature of the note committer
+func (n *Note) Committer() *Signature {
+	ptr := C.git_note_committer(n.ptr)
+	return newSignatureFromC(ptr)
+}
+
+// Message returns the note message
+func (n *Note) Message() string {
+	return C.GoString(C.git_note_message(n.ptr))
+}
+
+// NoteIterator
+type NoteIterator struct {
+	ptr *C.git_note_iterator
+}
+
+// NewNoteIterator creates a new iterator for notes
+func (repo *Repository) NewNoteIterator(ref string) (*NoteIterator, error) {
+	var cref *C.char
+	if ref == "" {
+		cref = nil
+	} else {
+		cref = C.CString(ref)
+		defer C.free(unsafe.Pointer(cref))
+	}
+
+	var ptr *C.git_note_iterator
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if ret := C.git_note_iterator_new(&ptr, repo.ptr, cref); ret < 0 {
+		return nil, MakeGitError(ret)
+	}
+
+	iter := &NoteIterator{ptr: ptr}
+	runtime.SetFinalizer(iter, (*NoteIterator).Free)
+	return iter, nil
+}
+
+// Free frees the note interator
+func (v *NoteIterator) Free() {
+	runtime.SetFinalizer(v, nil)
+	C.git_note_iterator_free(v.ptr)
+}
+
+// Next returns the current item (note id & annotated id) and advances the
+// iterator internally to the next item
+func (it *NoteIterator) Next() (noteId, annotatedId *Oid, err error) {
+	noteId, annotatedId = new(Oid), new(Oid)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if ret := C.git_note_next(noteId.toC(), annotatedId.toC(), it.ptr); ret < 0 {
+		err = MakeGitError(ret)
+	}
+	return
+}
diff --git a/note_test.go b/note_test.go
new file mode 100644
index 0000000..f5e9c01
--- /dev/null
+++ b/note_test.go
@@ -0,0 +1,113 @@
+package git
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"testing"
+	"time"
+)
+
+func TestCreateNote(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+
+	commitId, _ := seedTestRepo(t, repo)
+
+	commit, err := repo.LookupCommit(commitId)
+	checkFatal(t, err)
+
+	note, noteId := createTestNote(t, repo, commit)
+
+	compareStrings(t, "I am a note\n", note.Message())
+	compareStrings(t, noteId.String(), note.Id().String())
+	compareStrings(t, "alice", note.Author().Name)
+	compareStrings(t, "alice@example.com", note.Author().Email)
+	compareStrings(t, "alice", note.Committer().Name)
+	compareStrings(t, "alice@example.com", note.Committer().Email)
+}
+
+func TestNoteIterator(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+	seedTestRepo(t, repo)
+
+	notes := make([]*Note, 5)
+	for i := range notes {
+		commitId, _ := updateReadme(t, repo, fmt.Sprintf("README v%d\n", i+1))
+		commit, err := repo.LookupCommit(commitId)
+		checkFatal(t, err)
+
+		note, _ := createTestNote(t, repo, commit)
+		notes[i] = note
+	}
+
+	iter, err := repo.NewNoteIterator("")
+	checkFatal(t, err)
+	for {
+		noteId, commitId, err := iter.Next()
+		if err != nil {
+			if !IsErrorCode(err, ErrIterOver) {
+				checkFatal(t, err)
+			}
+			break
+		}
+
+		note, err := repo.ReadNote("", commitId)
+		checkFatal(t, err)
+
+		if !reflect.DeepEqual(note.Id(), noteId) {
+			t.Errorf("expected note oid '%v', actual '%v'", note.Id(), noteId)
+		}
+	}
+}
+
+func TestRemoveNote(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+
+	commitId, _ := seedTestRepo(t, repo)
+
+	commit, err := repo.LookupCommit(commitId)
+	checkFatal(t, err)
+
+	note, _ := createTestNote(t, repo, commit)
+
+	_, err = repo.ReadNote("", commit.Id())
+	checkFatal(t, err)
+
+	err = repo.RemoveNote("", note.Author(), note.Committer(), commitId)
+	checkFatal(t, err)
+
+	_, err = repo.ReadNote("", commit.Id())
+	if err == nil {
+		t.Fatal("note remove failed")
+	}
+}
+
+func TestDefaultNoteRef(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+
+	ref, err := repo.DefaultNoteRef()
+	checkFatal(t, err)
+
+	compareStrings(t, "refs/notes/commits", ref)
+}
+
+func createTestNote(t *testing.T, repo *Repository, commit *Commit) (*Note, *Oid) {
+	loc, err := time.LoadLocation("Europe/Berlin")
+	sig := &Signature{
+		Name:  "alice",
+		Email: "alice@example.com",
+		When:  time.Date(2015, 01, 05, 13, 0, 0, 0, loc),
+	}
+
+	noteId, err := repo.CreateNote("", sig, sig, commit.Id(), "I am a note\n", false)
+	checkFatal(t, err)
+
+	note, err := repo.ReadNote("", commit.Id())
+	checkFatal(t, err)
+
+	return note, noteId
+}
diff --git a/object.go b/object.go
index a3bf652..7428e0f 100644
--- a/object.go
+++ b/object.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 import "runtime"
diff --git a/odb.go b/odb.go
index 7076e20..19bb71c 100644
--- a/odb.go
+++ b/odb.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern int _go_git_odb_foreach(git_odb *db, void *payload);
 extern void _go_git_odb_backend_free(git_odb_backend *backend);
@@ -25,6 +24,9 @@
 func NewOdb() (odb *Odb, err error) {
 	odb = new(Odb)
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_new(&odb.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -40,6 +42,10 @@
 }
 
 func (v *Odb) AddBackend(backend *OdbBackend, priority int) (err error) {
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_add_backend(v.ptr, backend.ptr, C.int(priority))
 	if ret < 0 {
 		backend.Free()
@@ -110,6 +116,9 @@
 		err: nil,
 	}
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&data))
 	if ret == C.GIT_EUSER {
 		return data.err
@@ -140,6 +149,10 @@
 // contents of the object.
 func (v *Odb) NewReadStream(id *Oid) (*OdbReadStream, error) {
 	stream := new(OdbReadStream)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_open_rstream(&stream.ptr, v.ptr, id.toC())
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -154,6 +167,10 @@
 // known in advance
 func (v *Odb) NewWriteStream(size int, otype ObjectType) (*OdbWriteStream, error) {
 	stream := new(OdbWriteStream)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_open_wstream(&stream.ptr, v.ptr, C.size_t(size), C.git_otype(otype))
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -207,6 +224,10 @@
 	header := (*reflect.SliceHeader)(unsafe.Pointer(&data))
 	ptr := (*C.char)(unsafe.Pointer(header.Data))
 	size := C.size_t(header.Cap)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_stream_read(stream.ptr, ptr, size)
 	if ret < 0 {
 		return 0, MakeGitError(ret)
@@ -239,6 +260,9 @@
 	ptr := (*C.char)(unsafe.Pointer(header.Data))
 	size := C.size_t(header.Len)
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_stream_write(stream.ptr, ptr, size)
 	if ret < 0 {
 		return 0, MakeGitError(ret)
@@ -250,6 +274,9 @@
 // Close signals that all the data has been written and stores the
 // resulting object id in the stream's Id field.
 func (stream *OdbWriteStream) Close() error {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_odb_stream_finalize_write(stream.Id.toC(), stream.ptr)
 	if ret < 0 {
 		return MakeGitError(ret)
diff --git a/packbuilder.go b/packbuilder.go
index 666f5c4..7c94926 100644
--- a/packbuilder.go
+++ b/packbuilder.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 #include <git2/pack.h>
 #include <stdlib.h>
 
@@ -132,6 +131,9 @@
 		err: nil,
 	}
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	err := C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&data))
 	if err == C.GIT_EUSER {
 		return data.err
diff --git a/patch.go b/patch.go
index 0665501..45e14ac 100644
--- a/patch.go
+++ b/patch.go
@@ -6,6 +6,7 @@
 import "C"
 import (
 	"runtime"
+	"unsafe"
 )
 
 type Patch struct {
@@ -40,9 +41,47 @@
 		return "", ErrInvalid
 	}
 	var buf C.git_buf
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ecode := C.git_patch_to_buf(&buf, patch.ptr)
 	if ecode < 0 {
 		return "", MakeGitError(ecode)
 	}
 	return C.GoString(buf.ptr), nil
 }
+
+func toPointer(data []byte) (ptr unsafe.Pointer) {
+	if len(data) > 0 {
+		ptr = unsafe.Pointer(&data[0])
+	} else {
+		ptr = unsafe.Pointer(nil)
+	}
+	return
+}
+
+func (v *Repository) PatchFromBuffers(oldPath, newPath string, oldBuf, newBuf []byte, opts *DiffOptions) (*Patch, error) {
+	var patchPtr *C.git_patch
+
+	oldPtr := toPointer(oldBuf)
+	newPtr := (*C.char)(toPointer(newBuf))
+
+	cOldPath := C.CString(oldPath)
+	defer C.free(unsafe.Pointer(cOldPath))
+
+	cNewPath := C.CString(newPath)
+	defer C.free(unsafe.Pointer(cNewPath))
+
+	copts, _ := diffOptionsToC(opts)
+	defer freeDiffOptions(copts)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ecode := C.git_patch_from_buffers(&patchPtr, oldPtr, C.size_t(len(oldBuf)), cOldPath, newPtr, C.size_t(len(newBuf)), cNewPath, copts)
+	if ecode < 0 {
+		return nil, MakeGitError(ecode)
+	}
+	return newPatchFromC(patchPtr), nil
+}
diff --git a/patch_test.go b/patch_test.go
index 569eac2..a061142 100644
--- a/patch_test.go
+++ b/patch_test.go
@@ -20,7 +20,11 @@
 	newTree, err := repo.LookupTree(newTreeId)
 	checkFatal(t, err)
 
-	diff, err := repo.DiffTreeToTree(originalTree, newTree, nil)
+	opts := &DiffOptions{
+		OldPrefix: "a",
+		NewPrefix: "b",
+	}
+	diff, err := repo.DiffTreeToTree(originalTree, newTree, opts)
 	checkFatal(t, err)
 
 	patch, err := diff.Patch(0)
diff --git a/push.go b/push.go
deleted file mode 100644
index 5fb7f07..0000000
--- a/push.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package git
-
-/*
-#include <git2.h>
-#include <git2/errors.h>
-
-int _go_git_push_status_foreach(git_push *push, void *data);
-int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data);
-
-*/
-import "C"
-import (
-	"runtime"
-	"unsafe"
-)
-
-type Push struct {
-	ptr *C.git_push
-
-	packbuilderProgress *PackbuilderProgressCallback
-	transferProgress    *PushTransferProgressCallback
-}
-
-func newPushFromC(cpush *C.git_push) *Push {
-	p := &Push{ptr: cpush}
-	runtime.SetFinalizer(p, (*Push).Free)
-	return p
-}
-
-func (p *Push) Free() {
-	runtime.SetFinalizer(p, nil)
-	C.git_push_free(p.ptr)
-}
-
-func (remote *Remote) NewPush() (*Push, error) {
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	var cpush *C.git_push
-	ret := C.git_push_new(&cpush, remote.ptr)
-	if ret < 0 {
-		return nil, MakeGitError(ret)
-	}
-	return newPushFromC(cpush), nil
-}
-
-func (p *Push) Finish() error {
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	ret := C.git_push_finish(p.ptr)
-	if ret < 0 {
-		return MakeGitError(ret)
-	}
-	return nil
-}
-
-func (p *Push) UnpackOk() bool {
-
-	ret := C.git_push_unpack_ok(p.ptr)
-	if ret == 0 {
-		return false
-	}
-	return true
-
-}
-
-func (p *Push) UpdateTips(sig *Signature, msg string) error {
-
-	var csig *C.git_signature = nil
-	if sig != nil {
-		csig = sig.toC()
-		defer C.free(unsafe.Pointer(csig))
-	}
-
-	var cmsg *C.char
-	if msg == "" {
-		cmsg = nil
-	} else {
-		cmsg = C.CString(msg)
-		defer C.free(unsafe.Pointer(cmsg))
-	}
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	ret := C.git_push_update_tips(p.ptr, csig, cmsg)
-	if ret < 0 {
-		return MakeGitError(ret)
-	}
-	return nil
-}
-
-func (p *Push) AddRefspec(refspec string) error {
-
-	crefspec := C.CString(refspec)
-	defer C.free(unsafe.Pointer(crefspec))
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	ret := C.git_push_add_refspec(p.ptr, crefspec)
-	if ret < 0 {
-		return MakeGitError(ret)
-	}
-	return nil
-}
-
-type PushOptions struct {
-	Version       uint
-	PbParallelism uint
-}
-
-func (p *Push) SetOptions(opts PushOptions) error {
-	copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)}
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	ret := C.git_push_set_options(p.ptr, &copts)
-	if ret < 0 {
-		return MakeGitError(ret)
-	}
-	return nil
-}
-
-type StatusForeachFunc func(ref string, msg string) int
-
-//export statusForeach
-func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int {
-	ref := C.GoString(_ref)
-	msg := C.GoString(_msg)
-
-	cb := (*StatusForeachFunc)(_data)
-
-	return C.int((*cb)(ref, msg))
-}
-
-func (p *Push) StatusForeach(callback StatusForeachFunc) error {
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback))
-	if ret < 0 {
-		return MakeGitError(ret)
-	}
-	return nil
-
-}
-
-type PushCallbacks struct {
-	PackbuilderProgress *PackbuilderProgressCallback
-	TransferProgress    *PushTransferProgressCallback
-}
-
-type PackbuilderProgressCallback func(stage int, current uint, total uint) int
-type PushTransferProgressCallback func(current uint, total uint, bytes uint) int
-
-//export packbuilderProgress
-func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int {
-	return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total)))
-}
-
-//export pushTransferProgress
-func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int {
-	return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes)))
-}
-
-func (p *Push) SetCallbacks(callbacks PushCallbacks) {
-
-	runtime.LockOSThread()
-	defer runtime.UnlockOSThread()
-
-	// save callbacks so they don't get GC'd
-	p.packbuilderProgress = callbacks.PackbuilderProgress
-	p.transferProgress = callbacks.TransferProgress
-
-	C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress))
-}
diff --git a/push_test.go b/push_test.go
index 65f4dd2..cd708c6 100644
--- a/push_test.go
+++ b/push_test.go
@@ -3,55 +3,25 @@
 import (
 	"os"
 	"testing"
-	"time"
 )
 
-func Test_Push_ToRemote(t *testing.T) {
+func TestRemotePush(t *testing.T) {
 	repo := createBareTestRepo(t)
 	defer os.RemoveAll(repo.Path())
-	repo2 := createTestRepo(t)
-	defer os.RemoveAll(repo2.Workdir())
+	localRepo := createTestRepo(t)
+	defer os.RemoveAll(localRepo.Workdir())
 
-	remote, err := repo2.CreateRemote("test_push", repo.Path())
+	remote, err := localRepo.CreateRemote("test_push", repo.Path())
 	checkFatal(t, err)
 
-	index, err := repo2.Index()
+	seedTestRepo(t, localRepo)
+
+	err = remote.Push([]string{"refs/heads/master"}, nil, nil, "")
 	checkFatal(t, err)
 
-	index.AddByPath("README")
-
-	err = index.Write()
+	_, err = localRepo.LookupReference("refs/remotes/test_push/master")
 	checkFatal(t, err)
 
-	newTreeId, err := index.WriteTree()
+	_, err = repo.LookupReference("refs/heads/master")
 	checkFatal(t, err)
-
-	tree, err := repo2.LookupTree(newTreeId)
-	checkFatal(t, err)
-
-	sig := &Signature{Name: "Rand Om Hacker", Email: "random@hacker.com", When: time.Now()}
-	// this should cause master branch to be created if it does not already exist
-	_, err = repo2.CreateCommit("HEAD", sig, sig, "message", tree)
-	checkFatal(t, err)
-
-	push, err := remote.NewPush()
-	checkFatal(t, err)
-
-	err = push.AddRefspec("refs/heads/master")
-	checkFatal(t, err)
-
-	err = push.Finish()
-	checkFatal(t, err)
-
-	err = push.StatusForeach(func(ref string, msg string) int {
-		return 0
-	})
-	checkFatal(t, err)
-
-	if !push.UnpackOk() {
-		t.Fatalf("unable to unpack")
-	}
-
-	defer remote.Free()
-	defer repo.Free()
 }
diff --git a/refdb.go b/refdb.go
index 46fbb63..0d1e241 100644
--- a/refdb.go
+++ b/refdb.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 #include <git2/sys/refdb_backend.h>
 
 extern void _go_git_refdb_backend_free(git_refdb_backend *backend);
@@ -23,6 +22,9 @@
 func (v *Repository) NewRefdb() (refdb *Refdb, err error) {
 	refdb = new(Refdb)
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_refdb_new(&refdb.ptr, v.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -38,6 +40,9 @@
 }
 
 func (v *Refdb) SetBackend(backend *RefdbBackend) (err error) {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_refdb_set_backend(v.ptr, backend.ptr)
 	if ret < 0 {
 		backend.Free()
diff --git a/reference.go b/reference.go
index ce9d722..46436a6 100644
--- a/reference.go
+++ b/reference.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 import (
@@ -294,6 +293,10 @@
 // returned error is git.ErrIterOver
 func (v *ReferenceIterator) Next() (*Reference, error) {
 	var ptr *C.git_reference
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_reference_next(&ptr, v.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
diff --git a/remote.go b/remote.go
index a2288fa..2684c20 100644
--- a/remote.go
+++ b/remote.go
@@ -53,6 +53,9 @@
 type TransferProgressCallback func(stats TransferProgress) ErrorCode
 type UpdateTipsCallback func(refname string, a *Oid, b *Oid) ErrorCode
 type CertificateCheckCallback func(cert *Certificate, valid bool, hostname string) ErrorCode
+type PackbuilderProgressCallback func(stage int32, current, total uint32) ErrorCode
+type PushTransferProgressCallback func(current, total uint32, bytes uint) ErrorCode
+type PushUpdateReferenceCallback func(refname, status string) ErrorCode
 
 type RemoteCallbacks struct {
 	SidebandProgressCallback TransportMessageCallback
@@ -61,6 +64,9 @@
 	TransferProgressCallback
 	UpdateTipsCallback
 	CertificateCheckCallback
+	PackProgressCallback PackbuilderProgressCallback
+	PushTransferProgressCallback
+	PushUpdateReferenceCallback
 }
 
 type Remote struct {
@@ -101,6 +107,10 @@
 	HashSHA1 [20]byte
 }
 
+type PushOptions struct {
+	PbParallelism uint
+}
+
 type RemoteHead struct {
 	Id   *Oid
 	Name string
@@ -216,6 +226,38 @@
 	return int(callbacks.CertificateCheckCallback(&cert, valid, host))
 }
 
+//export packProgressCallback
+func packProgressCallback(stage C.int, current, total C.uint, data unsafe.Pointer) int {
+	callbacks := (*RemoteCallbacks)(data)
+
+	if callbacks.PackProgressCallback == nil {
+		return 0
+	}
+
+	return int(callbacks.PackProgressCallback(int32(stage), uint32(current), uint32(total)))
+}
+
+//export pushTransferProgressCallback
+func pushTransferProgressCallback(current, total C.uint, bytes C.size_t, data unsafe.Pointer) int {
+	callbacks := (*RemoteCallbacks)(data)
+	if callbacks.PushTransferProgressCallback == nil {
+		return 0
+	}
+
+	return int(callbacks.PushTransferProgressCallback(uint32(current), uint32(total), uint(bytes)))
+}
+
+//export pushUpdateReferenceCallback
+func pushUpdateReferenceCallback(refname, status *C.char, data unsafe.Pointer) int {
+	callbacks := (*RemoteCallbacks)(data)
+
+	if callbacks.PushUpdateReferenceCallback == nil {
+		return 0
+	}
+
+	return int(callbacks.PushUpdateReferenceCallback(C.GoString(refname), C.GoString(status)))
+}
+
 func RemoteIsValidName(name string) bool {
 	cname := C.CString(name)
 	defer C.free(unsafe.Pointer(cname))
@@ -249,6 +291,10 @@
 
 func (repo *Repository) ListRemotes() ([]string, error) {
 	var r C.git_strarray
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ecode := C.git_remote_list(&r, repo.ptr)
 	if ecode < 0 {
 		return nil, MakeGitError(ecode)
@@ -278,6 +324,20 @@
 	return remote, nil
 }
 
+func (repo *Repository) DeleteRemote(name string) error {
+	cname := C.CString(name)
+	defer C.free(unsafe.Pointer(cname))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_remote_delete(repo.ptr, cname)
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
 func (repo *Repository) CreateRemoteWithFetchspec(name string, url string, fetch string) (*Remote, error) {
 	remote := &Remote{}
 
@@ -318,7 +378,7 @@
 	return remote, nil
 }
 
-func (repo *Repository) LoadRemote(name string) (*Remote, error) {
+func (repo *Repository) LookupRemote(name string) (*Remote, error) {
 	remote := &Remote{}
 
 	cname := C.CString(name)
@@ -327,7 +387,7 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	ret := C.git_remote_load(&remote.ptr, repo.ptr, cname)
+	ret := C.git_remote_lookup(&remote.ptr, repo.ptr, cname)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
 	}
@@ -559,6 +619,9 @@
 	crefspecs.strings = makeCStringsFromStrings(refspecs)
 	defer freeStrarray(&crefspecs)
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_remote_fetch(o.ptr, &crefspecs, csig, cmsg)
 	if ret < 0 {
 		return MakeGitError(ret)
@@ -589,6 +652,9 @@
 	var refs **C.git_remote_head
 	var length C.size_t
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	if ret := C.git_remote_ls(&refs, &length, o.ptr); ret != 0 {
 		return nil, MakeGitError(ret)
 	}
@@ -626,3 +692,54 @@
 
 	return heads, nil
 }
+
+func (o *Remote) Push(refspecs []string, opts *PushOptions, sig *Signature, msg string) error {
+	var csig *C.git_signature = nil
+	if sig != nil {
+		csig = sig.toC()
+		defer C.free(unsafe.Pointer(csig))
+	}
+
+	var cmsg *C.char
+	if msg == "" {
+		cmsg = nil
+	} else {
+		cmsg = C.CString(msg)
+		defer C.free(unsafe.Pointer(cmsg))
+	}
+
+	var copts C.git_push_options
+	C.git_push_init_options(&copts, C.GIT_PUSH_OPTIONS_VERSION)
+	if opts != nil {
+		copts.pb_parallelism = C.uint(opts.PbParallelism)
+	}
+
+	crefspecs := C.git_strarray{}
+	crefspecs.count = C.size_t(len(refspecs))
+	crefspecs.strings = makeCStringsFromStrings(refspecs)
+	defer freeStrarray(&crefspecs)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_remote_push(o.ptr, &crefspecs, &copts, csig, cmsg)
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
+func (o *Remote) PruneRefs() bool {
+	return C.git_remote_prune_refs(o.ptr) > 0
+}
+
+func (o *Remote) Prune() error {
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_remote_prune(o.ptr)
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
diff --git a/remote_test.go b/remote_test.go
index 631a6cd..54a66ed 100644
--- a/remote_test.go
+++ b/remote_test.go
@@ -1,8 +1,10 @@
 package git
 
 import (
+	"fmt"
 	"os"
 	"testing"
+	"time"
 )
 
 func TestRefspecs(t *testing.T) {
@@ -132,3 +134,84 @@
 		t.Fatalf("Expected head to have a name, but it's empty")
 	}
 }
+
+func TestRemotePruneRefs(t *testing.T) {
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+	defer repo.Free()
+
+	config, err := repo.Config()
+	checkFatal(t, err)
+	defer config.Free()
+
+	err = config.SetBool("remote.origin.prune", true)
+	checkFatal(t, err)
+
+	_, err = repo.CreateRemote("origin", "https://github.com/libgit2/TestGitRepository")
+	checkFatal(t, err)
+
+	remote, err := repo.LookupRemote("origin")
+	checkFatal(t, err)
+
+	if !remote.PruneRefs() {
+		t.Fatal("Expected remote to be configured to prune references")
+	}
+}
+
+func TestRemotePrune(t *testing.T) {
+	remoteRepo := createTestRepo(t)
+	defer os.RemoveAll(remoteRepo.Workdir())
+	defer remoteRepo.Free()
+
+	head, _ := seedTestRepo(t, remoteRepo)
+	commit, err := remoteRepo.LookupCommit(head)
+	checkFatal(t, err)
+	defer commit.Free()
+
+	sig := &Signature{
+		Name:  "Rand Om Hacker",
+		Email: "random@hacker.com",
+		When:  time.Now(),
+	}
+
+	remoteRef, err := remoteRepo.CreateBranch("test-prune", commit, true, sig, "branch test-prune")
+	checkFatal(t, err)
+
+	repo := createTestRepo(t)
+	defer os.RemoveAll(repo.Workdir())
+	defer repo.Free()
+
+	config, err := repo.Config()
+	checkFatal(t, err)
+	defer config.Free()
+
+	remoteUrl := fmt.Sprintf("file://%s", remoteRepo.Workdir())
+	remote, err := repo.CreateRemote("origin", remoteUrl)
+	checkFatal(t, err)
+
+	err = remote.Fetch([]string{"test-prune"}, sig, "")
+	checkFatal(t, err)
+
+	_, err = repo.CreateReference("refs/remotes/origin/test-prune", head, true, sig, "remote reference")
+	checkFatal(t, err)
+
+	err = remoteRef.Delete()
+	checkFatal(t, err)
+
+	err = config.SetBool("remote.origin.prune", true)
+	checkFatal(t, err)
+
+	rr, err := repo.LookupRemote("origin")
+	checkFatal(t, err)
+
+	err = rr.ConnectFetch()
+	checkFatal(t, err)
+
+	err = rr.Prune()
+	checkFatal(t, err)
+
+	_, err = repo.LookupReference("refs/remotes/origin/test-prune")
+	if err == nil {
+		t.Fatal("Expected error getting a pruned reference")
+	}
+}
diff --git a/repository.go b/repository.go
index 09f5fef..7760c3a 100644
--- a/repository.go
+++ b/repository.go
@@ -72,6 +72,9 @@
 func NewRepositoryWrapOdb(odb *Odb) (repo *Repository, err error) {
 	repo = new(Repository)
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_repository_wrap_odb(&repo.ptr, odb.ptr)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -386,6 +389,9 @@
 
 	ctarget := commit.gitObject.ptr
 
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
 	ret := C.git_tag_create(oid.toC(), v.ptr, cname, ctarget, taggerSig, cmessage, 0)
 	if ret < 0 {
 		return nil, MakeGitError(ret)
@@ -450,7 +456,7 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	if ret := C.git_treebuilder_create(&bld.ptr, nil); ret < 0 {
+	if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, nil); ret < 0 {
 		return nil, MakeGitError(ret)
 	}
 	runtime.SetFinalizer(bld, (*TreeBuilder).Free)
@@ -465,7 +471,7 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	if ret := C.git_treebuilder_create(&bld.ptr, tree.cast_ptr); ret < 0 {
+	if ret := C.git_treebuilder_new(&bld.ptr, v.ptr, tree.cast_ptr); ret < 0 {
 		return nil, MakeGitError(ret)
 	}
 	runtime.SetFinalizer(bld, (*TreeBuilder).Free)
@@ -524,3 +530,103 @@
 
 	return newReferenceFromC(ptr, v), nil
 }
+
+// CreateNote adds a note for an object
+func (v *Repository) CreateNote(
+	ref string, author, committer *Signature, id *Oid,
+	note string, force bool) (*Oid, error) {
+
+	oid := new(Oid)
+
+	var cref *C.char
+	if ref == "" {
+		cref = nil
+	} else {
+		cref = C.CString(ref)
+		defer C.free(unsafe.Pointer(cref))
+	}
+
+	authorSig := author.toC()
+	defer C.git_signature_free(authorSig)
+
+	committerSig := committer.toC()
+	defer C.git_signature_free(committerSig)
+
+	cnote := C.CString(note)
+	defer C.free(unsafe.Pointer(cnote))
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_note_create(
+		oid.toC(), v.ptr, cref, authorSig,
+		committerSig, id.toC(), cnote, cbool(force))
+
+	if ret < 0 {
+		return nil, MakeGitError(ret)
+	}
+	return oid, nil
+}
+
+// ReadNote reads the note for an object
+func (v *Repository) ReadNote(ref string, id *Oid) (*Note, error) {
+	var cref *C.char
+	if ref == "" {
+		cref = nil
+	} else {
+		cref = C.CString(ref)
+		defer C.free(unsafe.Pointer(cref))
+	}
+
+	note := new(Note)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if ret := C.git_note_read(&note.ptr, v.ptr, cref, id.toC()); ret < 0 {
+		return nil, MakeGitError(ret)
+	}
+
+	runtime.SetFinalizer(note, (*Note).Free)
+	return note, nil
+}
+
+// RemoveNote removes the note for an object
+func (v *Repository) RemoveNote(ref string, author, committer *Signature, id *Oid) error {
+	var cref *C.char
+	if ref == "" {
+		cref = nil
+	} else {
+		cref = C.CString(ref)
+		defer C.free(unsafe.Pointer(cref))
+	}
+
+	authorSig := author.toC()
+	defer C.git_signature_free(authorSig)
+
+	committerSig := committer.toC()
+	defer C.git_signature_free(committerSig)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_note_remove(v.ptr, cref, authorSig, committerSig, id.toC())
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+	return nil
+}
+
+// DefaultNoteRef returns the default notes reference for a repository
+func (v *Repository) DefaultNoteRef() (string, error) {
+	var ptr *C.char
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	if ret := C.git_note_default_ref(&ptr, v.ptr); ret < 0 {
+		return "", MakeGitError(ret)
+	}
+
+	return C.GoString(ptr), nil
+}
diff --git a/revparse.go b/revparse.go
index 308da4c..7eb04f1 100644
--- a/revparse.go
+++ b/revparse.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern void _go_git_revspec_free(git_revspec *revspec);
 */
diff --git a/script/check-MakeGitError-thread-lock.go b/script/check-MakeGitError-thread-lock.go
new file mode 100644
index 0000000..f6b01b3
--- /dev/null
+++ b/script/check-MakeGitError-thread-lock.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+	"bytes"
+	"fmt"
+	"go/ast"
+	"go/build"
+	"go/parser"
+	"go/printer"
+	"go/token"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+var (
+	fset = token.NewFileSet()
+)
+
+func main() {
+	log.SetFlags(0)
+
+	bpkg, err := build.ImportDir(".", 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	pkgs, err := parser.ParseDir(fset, bpkg.Dir, func(fi os.FileInfo) bool { return filepath.Ext(fi.Name()) == ".go" }, 0)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	for _, pkg := range pkgs {
+		if err := checkPkg(pkg); err != nil {
+			log.Fatal(err)
+		}
+	}
+	if len(pkgs) == 0 {
+		log.Fatal("No packages to check.")
+	}
+}
+
+var ignoreViolationsInFunc = map[string]bool{
+	"MakeGitError":  true,
+	"MakeGitError2": true,
+}
+
+func checkPkg(pkg *ast.Package) error {
+	var violations []string
+	ast.Inspect(pkg, func(node ast.Node) bool {
+		switch node := node.(type) {
+		case *ast.FuncDecl:
+			var b bytes.Buffer
+			if err := printer.Fprint(&b, fset, node); err != nil {
+				log.Fatal(err)
+			}
+			src := b.String()
+
+			if strings.Contains(src, "MakeGitError") && !strings.Contains(src, "runtime.LockOSThread()") && !strings.Contains(src, "defer runtime.UnlockOSThread()") && !ignoreViolationsInFunc[node.Name.Name] {
+				pos := fset.Position(node.Pos())
+				violations = append(violations, fmt.Sprintf("%s at %s:%d", node.Name.Name, pos.Filename, pos.Line))
+			}
+		}
+		return true
+	})
+	if len(violations) > 0 {
+		return fmt.Errorf("%d non-thread-locked calls to MakeGitError found. To fix, add the following to each func below that calls MakeGitError, before the cgo call that might produce the error:\n\n\truntime.LockOSThread()\n\tdefer runtime.UnlockOSThread()\n\n%s", len(violations), strings.Join(violations, "\n"))
+	}
+	return nil
+}
diff --git a/status.go b/status.go
index 1d1d098..c849aca 100644
--- a/status.go
+++ b/status.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 
diff --git a/submodule.go b/submodule.go
index 35ceeb0..6923c61 100644
--- a/submodule.go
+++ b/submodule.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern int _go_git_visit_submodule(git_repository *repo, void *fct);
 */
@@ -12,6 +11,14 @@
 	"unsafe"
 )
 
+// SubmoduleUpdateOptions
+type SubmoduleUpdateOptions struct {
+	*CheckoutOpts
+	*RemoteCallbacks
+	CloneCheckoutStrategy CheckoutStrategy
+	Signature             *Signature
+}
+
 // Submodule
 type Submodule struct {
 	ptr *C.git_submodule
@@ -21,10 +28,10 @@
 
 const (
 	SubmoduleUpdateReset    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_RESET
-	SubmoduleUpdateCheckout SubmoduleUpdate  = C.GIT_SUBMODULE_UPDATE_CHECKOUT
-	SubmoduleUpdateRebase   SubmoduleUpdate  = C.GIT_SUBMODULE_UPDATE_REBASE
-	SubmoduleUpdateMerge    SubmoduleUpdate  = C.GIT_SUBMODULE_UPDATE_MERGE
-	SubmoduleUpdateNone     SubmoduleUpdate  = C.GIT_SUBMODULE_UPDATE_NONE
+	SubmoduleUpdateCheckout SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_CHECKOUT
+	SubmoduleUpdateRebase   SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_REBASE
+	SubmoduleUpdateMerge    SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_MERGE
+	SubmoduleUpdateNone     SubmoduleUpdate = C.GIT_SUBMODULE_UPDATE_NONE
 )
 
 type SubmoduleIgnore int
@@ -90,10 +97,10 @@
 type SubmoduleCbk func(sub *Submodule, name string) int
 
 //export SubmoduleVisitor
-func SubmoduleVisitor(csub unsafe.Pointer, name string, cfct unsafe.Pointer) int {
+func SubmoduleVisitor(csub unsafe.Pointer, name *C.char, cfct unsafe.Pointer) C.int {
 	sub := &Submodule{(*C.git_submodule)(csub)}
 	fct := *(*SubmoduleCbk)(cfct)
-	return fct(sub, name)
+	return (C.int)(fct(sub, C.GoString(name)))
 }
 
 func (repo *Repository) ForeachSubmodule(cbk SubmoduleCbk) error {
@@ -227,8 +234,8 @@
 	return SubmoduleIgnore(o)
 }
 
-func (sub *Submodule) Update() SubmoduleUpdate {
-	o := C.git_submodule_update(sub.ptr)
+func (sub *Submodule) UpdateStrategy() SubmoduleUpdate {
+	o := C.git_submodule_update_strategy(sub.ptr)
 	return SubmoduleUpdate(o)
 }
 
@@ -308,3 +315,31 @@
 	}
 	return nil
 }
+
+func (sub *Submodule) Update(init bool, opts *SubmoduleUpdateOptions) error {
+	var copts C.git_submodule_update_options
+	populateSubmoduleUpdateOptions(&copts, opts)
+
+	runtime.LockOSThread()
+	defer runtime.UnlockOSThread()
+
+	ret := C.git_submodule_update(sub.ptr, cbool(init), &copts)
+	if ret < 0 {
+		return MakeGitError(ret)
+	}
+
+	return nil
+}
+
+func populateSubmoduleUpdateOptions(ptr *C.git_submodule_update_options, opts *SubmoduleUpdateOptions) {
+	C.git_submodule_update_init_options(ptr, C.GIT_SUBMODULE_UPDATE_OPTIONS_VERSION)
+
+	if opts == nil {
+		return
+	}
+
+	populateCheckoutOpts(&ptr.checkout_opts, opts.CheckoutOpts)
+	populateRemoteCallbacks(&ptr.remote_callbacks, opts.RemoteCallbacks)
+	ptr.clone_checkout_strategy = C.uint(opts.CloneCheckoutStrategy)
+	ptr.signature = opts.Signature.toC()
+}
diff --git a/submodule_test.go b/submodule_test.go
new file mode 100644
index 0000000..1c8f471
--- /dev/null
+++ b/submodule_test.go
@@ -0,0 +1,24 @@
+package git
+
+import (
+	"testing"
+)
+
+func TestSubmoduleForeach(t *testing.T) {
+	repo := createTestRepo(t)
+	seedTestRepo(t, repo)
+
+	_, err := repo.AddSubmodule("http://example.org/submodule", "submodule", true)
+	checkFatal(t, err)
+
+	i := 0
+	err = repo.ForeachSubmodule(func(sub *Submodule, name string) int {
+		i++
+		return 0
+	})
+	checkFatal(t, err)
+
+	if i != 1 {
+		t.Fatalf("expected one submodule found but got %i", i)
+	}
+}
diff --git a/tree.go b/tree.go
index 45de9f1..c18d02a 100644
--- a/tree.go
+++ b/tree.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 
 extern int _go_git_treewalk(git_tree *tree, git_treewalk_mode mode, void *ptr);
 */
@@ -162,7 +161,7 @@
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
 
-	err := C.git_treebuilder_write(oid.toC(), v.repo.ptr, v.ptr)
+	err := C.git_treebuilder_write(oid.toC(), v.ptr)
 
 	if err < 0 {
 		return nil, MakeGitError(err)
diff --git a/vendor/libgit2 b/vendor/libgit2
index d09458f..04bdd97 160000
--- a/vendor/libgit2
+++ b/vendor/libgit2
@@ -1 +1 @@
-Subproject commit d09458f3e9f24afa0689ce90b7d4191872372634
+Subproject commit 04bdd97f2b63793a8720fd19007911e946ba3c55
diff --git a/walk.go b/walk.go
index f243e21..d02044a 100644
--- a/walk.go
+++ b/walk.go
@@ -2,7 +2,6 @@
 
 /*
 #include <git2.h>
-#include <git2/errors.h>
 */
 import "C"
 
diff --git a/wrapper.c b/wrapper.c
index 6e33fa2..938fd17 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -1,9 +1,7 @@
 #include "_cgo_export.h"
-#include "git2.h"
-#include "git2/sys/odb_backend.h"
-#include "git2/sys/refdb_backend.h"
-#include "git2/submodule.h"
-#include "git2/pack.h"
+#include <git2.h>
+#include <git2/sys/odb_backend.h>
+#include <git2/sys/refdb_backend.h>
 
 typedef int (*gogit_submodule_cbk)(git_submodule *sm, const char *name, void *payload);
 
@@ -71,24 +69,17 @@
 void _go_git_setup_callbacks(git_remote_callbacks *callbacks) {
 	typedef int (*completion_cb)(git_remote_completion_type type, void *data);
 	typedef int (*update_tips_cb)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+	typedef int (*push_update_reference_cb)(const char *refname, const char *status, void *data);
+
 	callbacks->sideband_progress = (git_transport_message_cb)sidebandProgressCallback;
 	callbacks->completion = (completion_cb)completionCallback;
 	callbacks->credentials = (git_cred_acquire_cb)credentialsCallback;
 	callbacks->transfer_progress = (git_transfer_progress_cb)transferProgressCallback;
 	callbacks->update_tips = (update_tips_cb)updateTipsCallback;
 	callbacks->certificate_check = (git_transport_certificate_check_cb) certificateCheckCallback;
-}
-
-typedef int (*status_foreach_cb)(const char *ref, const char *msg, void *data);
-
-int _go_git_push_status_foreach(git_push *push, void *data)
-{
-	return git_push_status_foreach(push, (status_foreach_cb)statusForeach, data);
-}
-
-int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data)
-{
-	return git_push_set_callbacks(push, packbuilderProgress, packbuilder_progress_data, pushTransferProgress, transfer_progress_data);
+	callbacks->pack_progress = (git_packbuilder_progress) packProgressCallback;
+	callbacks->push_transfer_progress = (git_push_transfer_progress) pushTransferProgressCallback;
+	callbacks->push_update_reference = (push_update_reference_cb) pushUpdateReferenceCallback;
 }
 
 int _go_blob_chunk_cb(char *buffer, size_t maxLen, void *payload)