diff --git a/Bindings/Libgit2.hs b/Bindings/Libgit2.hs
--- a/Bindings/Libgit2.hs
+++ b/Bindings/Libgit2.hs
@@ -1,44 +1,98 @@
-
-module Bindings.Libgit2 (
-    module Bindings.Libgit2.Repository,
-    module Bindings.Libgit2.Config,
-    module Bindings.Libgit2.Types,
-    module Bindings.Libgit2.Revwalk,
-    module Bindings.Libgit2.OdbBackend,
-    module Bindings.Libgit2.Tag,
-    module Bindings.Libgit2.Net,
-    module Bindings.Libgit2.Refs,
-    module Bindings.Libgit2.Refspec,
-    module Bindings.Libgit2.Reflog,
-    module Bindings.Libgit2.Commit,
-    module Bindings.Libgit2.Index,
-    module Bindings.Libgit2.Signature,
-    module Bindings.Libgit2.Blob,
-    module Bindings.Libgit2.Errors,
-    module Bindings.Libgit2.Oid,
-    module Bindings.Libgit2.Tree,
-    module Bindings.Libgit2.Odb,
-    module Bindings.Libgit2.Common,
-    module Bindings.Libgit2.Remote,
-    module Bindings.Libgit2.Object) where
-import Bindings.Libgit2.Repository
+module Bindings.Libgit2
+    ( module Bindings.Libgit2.Attr
+    , module Bindings.Libgit2.Blob
+    , module Bindings.Libgit2.Branch
+    , module Bindings.Libgit2.Checkout
+    , module Bindings.Libgit2.Clone
+    , module Bindings.Libgit2.Commit
+    , module Bindings.Libgit2.Common
+    , module Bindings.Libgit2.Config
+    , module Bindings.Libgit2.CredHelpers
+    , module Bindings.Libgit2.Diff
+    , module Bindings.Libgit2.Errors
+    , module Bindings.Libgit2.Graph
+    , module Bindings.Libgit2.Ignore
+    , module Bindings.Libgit2.Index
+    , module Bindings.Libgit2.Indexer
+    , module Bindings.Libgit2.Merge
+    , module Bindings.Libgit2.Message
+    , module Bindings.Libgit2.Net
+    , module Bindings.Libgit2.Notes
+    , module Bindings.Libgit2.Object
+    , module Bindings.Libgit2.Odb
+    , module Bindings.Libgit2.OdbBackend
+    , module Bindings.Libgit2.Oid
+    , module Bindings.Libgit2.Pack
+    , module Bindings.Libgit2.Push
+    , module Bindings.Libgit2.Refdb
+    , module Bindings.Libgit2.RefdbBackend
+    , module Bindings.Libgit2.Reflog
+    , module Bindings.Libgit2.Refs
+    , module Bindings.Libgit2.Refspec
+    , module Bindings.Libgit2.Remote
+    , module Bindings.Libgit2.Repository
+    , module Bindings.Libgit2.Reset
+    , module Bindings.Libgit2.Revparse
+    , module Bindings.Libgit2.Revwalk
+    , module Bindings.Libgit2.Signature
+    , module Bindings.Libgit2.Stash
+    , module Bindings.Libgit2.Status
+    , module Bindings.Libgit2.Strarray
+    , module Bindings.Libgit2.Submodule
+    , module Bindings.Libgit2.Tag
+    , module Bindings.Libgit2.Threads
+    , module Bindings.Libgit2.Trace
+    , module Bindings.Libgit2.Transport
+    , module Bindings.Libgit2.Tree
+    , module Bindings.Libgit2.Types
+    , module Bindings.Libgit2.Version
+    , module Bindings.Libgit2.Windows
+    ) where
+import Bindings.Libgit2.Attr
+import Bindings.Libgit2.Blob
+import Bindings.Libgit2.Branch
+import Bindings.Libgit2.Checkout
+import Bindings.Libgit2.Clone
+import Bindings.Libgit2.Commit
+import Bindings.Libgit2.Common
 import Bindings.Libgit2.Config
-import Bindings.Libgit2.Types
-import Bindings.Libgit2.Revwalk
-import Bindings.Libgit2.OdbBackend
-import Bindings.Libgit2.Tag
+import Bindings.Libgit2.CredHelpers
+import Bindings.Libgit2.Diff
+import Bindings.Libgit2.Errors
+import Bindings.Libgit2.Graph
+import Bindings.Libgit2.Ignore
+import Bindings.Libgit2.Index
+import Bindings.Libgit2.Indexer
+import Bindings.Libgit2.Merge
+import Bindings.Libgit2.Message
 import Bindings.Libgit2.Net
+import Bindings.Libgit2.Notes
+import Bindings.Libgit2.Object
+import Bindings.Libgit2.Odb
+import Bindings.Libgit2.OdbBackend
+import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Pack
+import Bindings.Libgit2.Push
+import Bindings.Libgit2.Refdb
+import Bindings.Libgit2.RefdbBackend
+import Bindings.Libgit2.Reflog
 import Bindings.Libgit2.Refs
 import Bindings.Libgit2.Refspec
-import Bindings.Libgit2.Reflog
-import Bindings.Libgit2.Commit
-import Bindings.Libgit2.Index
+import Bindings.Libgit2.Remote
+import Bindings.Libgit2.Repository
+import Bindings.Libgit2.Reset
+import Bindings.Libgit2.Revparse
+import Bindings.Libgit2.Revwalk
 import Bindings.Libgit2.Signature
-import Bindings.Libgit2.Blob
-import Bindings.Libgit2.Errors
-import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Stash
+import Bindings.Libgit2.Status
+import Bindings.Libgit2.Strarray
+import Bindings.Libgit2.Submodule
+import Bindings.Libgit2.Tag
+import Bindings.Libgit2.Threads
+import Bindings.Libgit2.Trace
+import Bindings.Libgit2.Transport
 import Bindings.Libgit2.Tree
-import Bindings.Libgit2.Odb
-import Bindings.Libgit2.Common
-import Bindings.Libgit2.Remote
-import Bindings.Libgit2.Object
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Version
+import Bindings.Libgit2.Windows
diff --git a/Bindings/Libgit2/Attr.hsc b/Bindings/Libgit2/Attr.hsc
--- a/Bindings/Libgit2/Attr.hsc
+++ b/Bindings/Libgit2/Attr.hsc
@@ -1,16 +1,25 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 #include <git2/attr.h>
 module Bindings.Libgit2.Attr where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
-
-{- extern __attribute__((visibility("default"))) const char * git_attr__true; -}
-{- extern __attribute__((visibility("default"))) const char * git_attr__false; -}
-{- extern __attribute__((visibility("default"))) const char * git_attr__unset; -}
-
+{- typedef enum {
+            GIT_ATTR_UNSPECIFIED_T = 0,
+            GIT_ATTR_TRUE_T,
+            GIT_ATTR_FALSE_T,
+            GIT_ATTR_VALUE_T
+        } git_attr_t; -}
+#integral_t git_attr_t
+#num GIT_ATTR_UNSPECIFIED_T
+#num GIT_ATTR_TRUE_T
+#num GIT_ATTR_FALSE_T
+#num GIT_ATTR_VALUE_T
+#ccall git_attr_value , CString -> IO (<git_attr_t>)
 #ccall git_attr_get , Ptr (CString) -> Ptr <git_repository> -> CUInt -> CString -> CString -> IO (CInt)
 #ccall git_attr_get_many , Ptr (CString) -> Ptr <git_repository> -> CUInt -> CString -> CSize -> Ptr (CString) -> IO (CInt)
 #callback git_attr_foreach_callback , CString -> CString -> Ptr () -> IO CInt
diff --git a/Bindings/Libgit2/Blob.hsc b/Bindings/Libgit2/Blob.hsc
--- a/Bindings/Libgit2/Blob.hsc
+++ b/Bindings/Libgit2/Blob.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Blob where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -8,10 +10,17 @@
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Object
 #cinline git_blob_lookup , Ptr (Ptr <git_blob>) -> Ptr <git_repository> -> Ptr <git_oid> -> IO (CInt)
-#cinline git_blob_lookup_prefix , Ptr (Ptr <git_blob>) -> Ptr <git_repository> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#cinline git_blob_lookup_prefix , Ptr (Ptr <git_blob>) -> Ptr <git_repository> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #cinline git_blob_free , Ptr <git_blob> -> IO ()
+#cinline git_blob_id , Ptr <git_blob> -> IO (Ptr <git_oid>)
 #ccall git_blob_rawcontent , Ptr <git_blob> -> IO (Ptr ())
-#ccall git_blob_rawsize , Ptr <git_blob> -> IO (CSize)
-#ccall git_blob_create_fromfile , Ptr <git_oid> -> Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_blob_rawsize , Ptr <git_blob> -> IO (CLong)
+#ccall git_blob_create_fromworkdir , Ptr <git_oid> -> Ptr <git_repository> -> CString -> IO (CInt)
 #ccall git_blob_create_fromdisk , Ptr <git_oid> -> Ptr <git_repository> -> CString -> IO (CInt)
+{- typedef int (* git_blob_chunk_cb)(char * content,
+                                  size_t max_length,
+                                  void * payload); -}
+#callback git_blob_chunk_cb , Ptr Char -> CSize -> Ptr () -> IO CInt
+#ccall git_blob_create_fromchunks , Ptr <git_oid> -> Ptr <git_repository> -> CString -> <git_blob_chunk_cb> -> Ptr () -> IO (CInt)
 #ccall git_blob_create_frombuffer , Ptr <git_oid> -> Ptr <git_repository> -> Ptr () -> CSize -> IO (CInt)
+#ccall git_blob_is_binary , Ptr <git_blob> -> IO (CInt)
diff --git a/Bindings/Libgit2/Blob.hsc.helper.c b/Bindings/Libgit2/Blob.hsc.helper.c
--- a/Bindings/Libgit2/Blob.hsc.helper.c
+++ b/Bindings/Libgit2/Blob.hsc.helper.c
@@ -1,6 +1,7 @@
 #include <bindings.cmacros.h>
 #include <git2.h>
 
-BC_INLINE3(git_blob_lookup, git_blob * *, git_repository *, const git_oid *, int)
-BC_INLINE4(git_blob_lookup_prefix, git_blob * *, git_repository *, const git_oid *, unsigned int, int)
-BC_INLINE1VOID(git_blob_free, git_blob *)
+BC_INLINE3(git_blob_lookup, git_blob**, git_repository*, const git_oid*, int)
+BC_INLINE4(git_blob_lookup_prefix, git_blob**, git_repository*, const git_oid*, size_t, int)
+BC_INLINE1VOID(git_blob_free, git_blob*)
+BC_INLINE1(git_blob_id, const git_blob*, const git_oid*)
diff --git a/Bindings/Libgit2/Branch.hsc b/Bindings/Libgit2/Branch.hsc
--- a/Bindings/Libgit2/Branch.hsc
+++ b/Bindings/Libgit2/Branch.hsc
@@ -1,12 +1,25 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Branch where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Types
-#ccall git_branch_create , Ptr <git_oid> -> Ptr <git_repository> -> CString -> Ptr <git_object> -> CInt -> IO (CInt)
-#ccall git_branch_delete , Ptr <git_repository> -> CString -> <git_branch_t> -> IO (CInt)
-#ccall git_branch_list , Ptr <git_strarray> -> Ptr <git_repository> -> CUInt -> IO (CInt)
-#ccall git_branch_move , Ptr <git_repository> -> CString -> CString -> CInt -> IO (CInt)
+#ccall git_branch_create , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> Ptr <git_commit> -> CInt -> IO (CInt)
+#ccall git_branch_delete , Ptr <git_reference> -> IO (CInt)
+{- typedef int (* git_branch_foreach_cb)(const char * branch_name,
+                                      git_branch_t branch_type,
+                                      void * payload); -}
+#callback git_branch_foreach_cb , CString -> <git_branch_t> -> Ptr () -> IO (CInt)
+#ccall git_branch_foreach , Ptr <git_repository> -> CUInt -> <git_branch_foreach_cb> -> Ptr () -> IO (CInt)
+#ccall git_branch_move , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> CString -> CInt -> IO (CInt)
+#ccall git_branch_lookup , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> <git_branch_t> -> IO (CInt)
+#ccall git_branch_name , Ptr (CString) -> Ptr <git_reference> -> IO (CInt)
+#ccall git_branch_upstream , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> IO (CInt)
+#ccall git_branch_set_upstream , Ptr <git_reference> -> CString -> IO (CInt)
+#ccall git_branch_upstream_name , CString -> CSize -> Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_branch_is_head , Ptr <git_reference> -> IO (CInt)
+#ccall git_branch_remote_name , CString -> CSize -> Ptr <git_repository> -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Checkout.hsc b/Bindings/Libgit2/Checkout.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Checkout.hsc
@@ -0,0 +1,107 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/checkout.h>
+module Bindings.Libgit2.Checkout where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Strarray
+import Bindings.Libgit2.Diff
+{- typedef enum {
+            GIT_CHECKOUT_NONE = 0,
+            GIT_CHECKOUT_SAFE = 1u << 0,
+            GIT_CHECKOUT_SAFE_CREATE = 1u << 1,
+            GIT_CHECKOUT_FORCE = 1u << 2,
+            GIT_CHECKOUT_ALLOW_CONFLICTS = 1u << 4,
+            GIT_CHECKOUT_REMOVE_UNTRACKED = 1u << 5,
+            GIT_CHECKOUT_REMOVE_IGNORED = 1u << 6,
+            GIT_CHECKOUT_UPDATE_ONLY = 1u << 7,
+            GIT_CHECKOUT_DONT_UPDATE_INDEX = 1u << 8,
+            GIT_CHECKOUT_NO_REFRESH = 1u << 9,
+            GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = 1u << 13,
+            GIT_CHECKOUT_SKIP_UNMERGED = 1u << 10,
+            GIT_CHECKOUT_USE_OURS = 1u << 11,
+            GIT_CHECKOUT_USE_THEIRS = 1u << 12,
+            GIT_CHECKOUT_UPDATE_SUBMODULES = 1u << 16,
+            GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = 1u << 17
+        } git_checkout_strategy_t; -}
+#integral_t git_checkout_strategy_t
+#num GIT_CHECKOUT_NONE
+#num GIT_CHECKOUT_SAFE
+#num GIT_CHECKOUT_SAFE_CREATE
+#num GIT_CHECKOUT_FORCE
+#num GIT_CHECKOUT_ALLOW_CONFLICTS
+#num GIT_CHECKOUT_REMOVE_UNTRACKED
+#num GIT_CHECKOUT_REMOVE_IGNORED
+#num GIT_CHECKOUT_UPDATE_ONLY
+#num GIT_CHECKOUT_DONT_UPDATE_INDEX
+#num GIT_CHECKOUT_NO_REFRESH
+#num GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH
+#num GIT_CHECKOUT_SKIP_UNMERGED
+#num GIT_CHECKOUT_USE_OURS
+#num GIT_CHECKOUT_USE_THEIRS
+#num GIT_CHECKOUT_UPDATE_SUBMODULES
+#num GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED
+{- typedef enum {
+            GIT_CHECKOUT_NOTIFY_NONE = 0,
+            GIT_CHECKOUT_NOTIFY_CONFLICT = 1u << 0,
+            GIT_CHECKOUT_NOTIFY_DIRTY = 1u << 1,
+            GIT_CHECKOUT_NOTIFY_UPDATED = 1u << 2,
+            GIT_CHECKOUT_NOTIFY_UNTRACKED = 1u << 3,
+            GIT_CHECKOUT_NOTIFY_IGNORED = 1u << 4
+        } git_checkout_notify_t; -}
+#integral_t git_checkout_notify_t
+#num GIT_CHECKOUT_NOTIFY_NONE
+#num GIT_CHECKOUT_NOTIFY_CONFLICT
+#num GIT_CHECKOUT_NOTIFY_DIRTY
+#num GIT_CHECKOUT_NOTIFY_UPDATED
+#num GIT_CHECKOUT_NOTIFY_UNTRACKED
+#num GIT_CHECKOUT_NOTIFY_IGNORED
+{- typedef int (* git_checkout_notify_cb)(git_checkout_notify_t why,
+                                       const char * path,
+                                       const git_diff_file * baseline,
+                                       const git_diff_file * target,
+                                       const git_diff_file * workdir,
+                                       void * payload); -}
+#callback git_checkout_notify_cb , <git_checkout_notify_t> -> CString -> Ptr (<git_diff_file>) -> Ptr (<git_diff_file>) -> Ptr (<git_diff_file>) -> Ptr () -> IO CInt
+{- typedef void (* git_checkout_progress_cb)(const char * path,
+                                          size_t completed_steps,
+                                          size_t total_steps,
+                                          void * payload); -}
+#callback git_checkout_progress_cb , CString -> CSize -> CSize -> Ptr () -> IO ()
+{- typedef struct git_checkout_opts {
+            unsigned int version;
+            unsigned int checkout_strategy;
+            int disable_filters;
+            unsigned int dir_mode;
+            unsigned int file_mode;
+            int file_open_flags;
+            unsigned int notify_flags;
+            git_checkout_notify_cb notify_cb;
+            void * notify_payload;
+            git_checkout_progress_cb progress_cb;
+            void * progress_payload;
+            git_strarray paths;
+            git_tree * baseline;
+        } git_checkout_opts; -}
+#starttype git_checkout_opts
+#field version , CUInt
+#field checkout_strategy , CUInt
+#field disable_filters , CInt
+#field dir_mode , CUInt
+#field file_mode , CUInt
+#field file_open_flags , CInt
+#field notify_flags , CUInt
+#field notify_cb , <git_checkout_notify_cb>
+#field notify_payload , Ptr ()
+#field progress_cb , <git_checkout_progress_cb>
+#field progress_payload , Ptr ()
+#field paths , <git_strarray>
+#field baseline , Ptr <git_tree>
+#stoptype
+#ccall git_checkout_head , Ptr <git_repository> -> Ptr <git_checkout_opts> -> IO (CInt)
+#ccall git_checkout_index , Ptr <git_repository> -> Ptr <git_index> -> Ptr <git_checkout_opts> -> IO (CInt)
+#ccall git_checkout_tree , Ptr <git_repository> -> Ptr <git_object> -> Ptr <git_checkout_opts> -> IO (CInt)
diff --git a/Bindings/Libgit2/Clone.hsc b/Bindings/Libgit2/Clone.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Clone.hsc
@@ -0,0 +1,49 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/clone.h>
+module Bindings.Libgit2.Clone where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Indexer
+import Bindings.Libgit2.Checkout
+import Bindings.Libgit2.Remote
+import Bindings.Libgit2.Transport
+{- typedef struct git_clone_options {
+            unsigned int version;
+            git_checkout_opts checkout_opts;
+            int bare;
+            git_transfer_progress_callback fetch_progress_cb;
+            void * fetch_progress_payload;
+            const char * remote_name;
+            const char * pushurl;
+            const char * fetch_spec;
+            const char * push_spec;
+            git_cred_acquire_cb cred_acquire_cb;
+            void * cred_acquire_payload;
+            git_transport * transport;
+            git_remote_callbacks * remote_callbacks;
+            git_remote_autotag_option_t remote_autotag;
+            const char * checkout_branch;
+        } git_clone_options; -}
+#starttype git_clone_options
+#field version , CUInt
+#field checkout_opts , <git_checkout_opts>
+#field bare , CInt
+#field fetch_progress_cb , CInt
+#field fetch_progress_payload , Ptr ()
+#field remote_name , CString
+#field pushurl , CString
+#field fetch_spec , CString
+#field push_spec , CString
+#field cred_acquire_cb , CInt
+#field cred_acquire_payload , Ptr ()
+#field transport , Ptr <git_transport>
+#field remote_callbacks , Ptr <git_remote_callbacks>
+#field remote_autotag , <git_remote_autotag_option_t>
+#field checkout_branch , CString
+#stoptype
+#ccall git_clone , Ptr (Ptr <git_repository>) -> CString -> CString -> Ptr <git_clone_options> -> IO (CInt)
diff --git a/Bindings/Libgit2/Commit.hsc b/Bindings/Libgit2/Commit.hsc
--- a/Bindings/Libgit2/Commit.hsc
+++ b/Bindings/Libgit2/Commit.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Commit where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -8,9 +10,9 @@
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Object
 #cinline git_commit_lookup , Ptr (Ptr <git_commit>) -> Ptr <git_repository> -> Ptr <git_oid> -> IO (CInt)
-#cinline git_commit_lookup_prefix , Ptr (Ptr <git_commit>) -> Ptr <git_repository> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#cinline git_commit_lookup_prefix , Ptr (Ptr <git_commit>) -> Ptr <git_repository> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #cinline git_commit_free , Ptr <git_commit> -> IO ()
-#ccall git_commit_id , Ptr <git_commit> -> IO (Ptr <git_oid>)
+#cinline git_commit_id , Ptr <git_commit> -> IO (Ptr <git_oid>)
 #ccall git_commit_message_encoding , Ptr <git_commit> -> IO (CString)
 #ccall git_commit_message , Ptr <git_commit> -> IO (CString)
 #ccall git_commit_time , Ptr <git_commit> -> IO (CLong)
@@ -18,9 +20,11 @@
 #ccall git_commit_committer , Ptr <git_commit> -> IO (Ptr <git_signature>)
 #ccall git_commit_author , Ptr <git_commit> -> IO (Ptr <git_signature>)
 #ccall git_commit_tree , Ptr (Ptr <git_tree>) -> Ptr <git_commit> -> IO (CInt)
-#ccall git_commit_tree_oid , Ptr <git_commit> -> IO (Ptr <git_oid>)
+#ccall git_commit_tree_id , Ptr <git_commit> -> IO (Ptr <git_oid>)
 #ccall git_commit_parentcount , Ptr <git_commit> -> IO (CUInt)
 #ccall git_commit_parent , Ptr (Ptr <git_commit>) -> Ptr <git_commit> -> CUInt -> IO (CInt)
-#ccall git_commit_parent_oid , Ptr <git_commit> -> CUInt -> IO (Ptr <git_oid>)
+#ccall git_commit_parent_id , Ptr <git_commit> -> CUInt -> IO (Ptr <git_oid>)
+#ccall git_commit_nth_gen_ancestor , Ptr (Ptr <git_commit>) -> Ptr <git_commit> -> CUInt -> IO (CInt)
 #ccall git_commit_create , Ptr <git_oid> -> Ptr <git_repository> -> CString -> Ptr <git_signature> -> Ptr <git_signature> -> CString -> CString -> Ptr <git_tree> -> CInt -> Ptr (Ptr <git_commit>) -> IO (CInt)
+#ccall git_commit_create_v , Ptr <git_oid> -> Ptr <git_repository> -> CString -> Ptr <git_signature> -> Ptr <git_signature> -> CString -> CString -> Ptr <git_tree> -> CInt -> IO (CInt)
 #ccall git_commit_create_oid , Ptr <git_oid> -> Ptr <git_repository> -> CString -> Ptr <git_signature> -> Ptr <git_signature> -> CString -> CString -> Ptr <git_oid> -> CInt -> Ptr (Ptr <git_oid>) -> IO (CInt)
diff --git a/Bindings/Libgit2/Commit.hsc.helper.c b/Bindings/Libgit2/Commit.hsc.helper.c
--- a/Bindings/Libgit2/Commit.hsc.helper.c
+++ b/Bindings/Libgit2/Commit.hsc.helper.c
@@ -1,6 +1,7 @@
 #include <bindings.cmacros.h>
 #include <git2.h>
 
-BC_INLINE3(git_commit_lookup, git_commit * *, git_repository *, const git_oid *, int)
-BC_INLINE4(git_commit_lookup_prefix, git_commit * *, git_repository *, const git_oid *, unsigned, int)
-BC_INLINE1VOID(git_commit_free, git_commit *)
+BC_INLINE3(git_commit_lookup, git_commit**, git_repository*, const git_oid*, int)
+BC_INLINE4(git_commit_lookup_prefix, git_commit**, git_repository*, const git_oid*, size_t, int)
+BC_INLINE1VOID(git_commit_free, git_commit*)
+BC_INLINE1(git_commit_id, const git_commit*, const git_oid*)
diff --git a/Bindings/Libgit2/Common.hsc b/Bindings/Libgit2/Common.hsc
--- a/Bindings/Libgit2/Common.hsc
+++ b/Bindings/Libgit2/Common.hsc
@@ -1,20 +1,33 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Common where
+import Foreign.Ptr
 #strict_import
 
-{- typedef struct {
-            char * * strings; size_t count;
-        } git_strarray; -}
-#starttype git_strarray
-#field strings , Ptr (CString)
-#field count , CSize
-#stoptype
-#ccall git_strarray_free , Ptr <git_strarray> -> IO ()
-#ccall git_strarray_copy , Ptr <git_strarray> -> Ptr <git_strarray> -> IO (CInt)
 #ccall git_libgit2_version , Ptr CInt -> Ptr CInt -> Ptr CInt -> IO ()
-
-{- BEGIN MANUAL ENTRY -}
-#cinline GIT_PATH_LIST_SEPARATOR , CChar
-#num GIT_PATH_MAX
-{- END MANUAL ENTRY -}
+{- enum {
+    GIT_CAP_THREADS = 1 << 0, GIT_CAP_HTTPS = 1 << 1
+}; -}
+#num GIT_CAP_THREADS
+#num GIT_CAP_HTTPS
+#ccall git_libgit2_capabilities , IO (CInt)
+{- enum {
+    GIT_OPT_GET_MWINDOW_SIZE,
+    GIT_OPT_SET_MWINDOW_SIZE,
+    GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
+    GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
+    GIT_OPT_GET_SEARCH_PATH,
+    GIT_OPT_SET_SEARCH_PATH,
+    GIT_OPT_GET_ODB_CACHE_SIZE,
+    GIT_OPT_SET_ODB_CACHE_SIZE
+}; -}
+#num GIT_OPT_GET_MWINDOW_SIZE
+#num GIT_OPT_SET_MWINDOW_SIZE
+#num GIT_OPT_GET_MWINDOW_MAPPED_LIMIT
+#num GIT_OPT_SET_MWINDOW_MAPPED_LIMIT
+#num GIT_OPT_GET_SEARCH_PATH
+#num GIT_OPT_SET_SEARCH_PATH
+#num GIT_OPT_GET_ODB_CACHE_SIZE
+#num GIT_OPT_SET_ODB_CACHE_SIZE
+#ccall git_libgit2_opts , CInt -> IO (CInt)
diff --git a/Bindings/Libgit2/Config.hsc b/Bindings/Libgit2/Config.hsc
--- a/Bindings/Libgit2/Config.hsc
+++ b/Bindings/Libgit2/Config.hsc
@@ -1,54 +1,83 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Config where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
-{- struct git_config_file {
+{- enum {
+    GIT_CONFIG_LEVEL_SYSTEM = 1,
+    GIT_CONFIG_LEVEL_XDG = 2,
+    GIT_CONFIG_LEVEL_GLOBAL = 3,
+    GIT_CONFIG_LEVEL_LOCAL = 4,
+    GIT_CONFIG_HIGHEST_LEVEL = -1
+}; -}
+#num GIT_CONFIG_LEVEL_SYSTEM
+#num GIT_CONFIG_LEVEL_XDG
+#num GIT_CONFIG_LEVEL_GLOBAL
+#num GIT_CONFIG_LEVEL_LOCAL
+#num GIT_CONFIG_HIGHEST_LEVEL
+{- typedef struct {
+            const char * name; const char * value; unsigned int level;
+        } git_config_entry; -}
+#starttype git_config_entry
+#field name , CString
+#field value , CString
+#field level , CUInt
+#stoptype
+{- typedef int (* git_config_foreach_cb)(const git_config_entry *,
+                                      void *); -}
+#callback git_config_foreach_cb , Ptr (<git_config_entry>) -> Ptr () -> IO CInt
+{- struct git_config_backend {
+    unsigned int version;
     struct git_config * cfg;
-    int (* open)(struct git_config_file *);
-    int (* get)(struct git_config_file *,
+    int (* open)(struct git_config_backend *, unsigned int level);
+    int (* get)(const struct git_config_backend *,
                 const char * key,
-                const char * * value);
-    int (* get_multivar)(struct git_config_file *,
+                const git_config_entry * * entry);
+    int (* get_multivar)(struct git_config_backend *,
                          const char * key,
                          const char * regexp,
-                         int (* fn)(const char *, void *),
-                         void * data);
-    int (* set)(struct git_config_file *,
+                         git_config_foreach_cb callback,
+                         void * payload);
+    int (* set)(struct git_config_backend *,
                 const char * key,
                 const char * value);
-    int (* set_multivar)(git_config_file * cfg,
+    int (* set_multivar)(git_config_backend * cfg,
                          const char * name,
                          const char * regexp,
                          const char * value);
-    int (* del)(struct git_config_file *, const char * key);
-    int (* foreach)(struct git_config_file *,
-                    int (* fn)(const char *, const char *, void *),
-                    void * data);
-    void (* free)(struct git_config_file *);
+    int (* del)(struct git_config_backend *, const char * key);
+    int (* foreach)(struct git_config_backend *,
+                    const char *,
+                    git_config_foreach_cb callback,
+                    void * payload);
+    int (* refresh)(struct git_config_backend *);
+    void (* free)(struct git_config_backend *);
 }; -}
-#callback git_config_open_callback , Ptr <git_config_file> -> IO CInt
-#callback git_config_get_callback , Ptr <git_config_file> -> CString -> Ptr (CString) -> IO CInt
-#callback git_config_get_multivar_inner_callback , CString -> Ptr () -> IO CInt
-#callback git_config_get_multivar_callback , Ptr <git_config_file> -> CString -> CString -> <git_config_get_multivar_inner_callback> -> Ptr () -> IO CInt
-#callback git_config_set_callback , Ptr <git_config_file> -> CString -> CString -> IO CInt
-#callback git_config_set_multivar_callback , Ptr <git_config_file> -> CString -> CString -> CString -> IO CInt
-#callback git_config_del_callback , Ptr <git_config_file> -> CString -> IO CInt
-#callback git_config_foreach_inner_callback , CString -> CString -> Ptr () -> IO CInt
-#callback git_config_foreach_callback , Ptr <git_config_file> -> <git_config_foreach_inner_callback> -> Ptr () -> IO CInt
-#callback git_config_free_callback , Ptr <git_config_file> -> IO ()
-#starttype git_config_file
+#callback git_config_backend_open_callback , Ptr <git_config_backend> -> CUInt -> IO CInt
+#callback git_config_backend_get_callback , Ptr <git_config_backend> -> CString -> Ptr (Ptr <git_config_entry>) -> IO CInt
+#callback git_config_backend_get_multivar_callback , Ptr <git_config_backend> -> CString -> CString -> <git_config_foreach_cb> -> Ptr () -> IO CInt
+#callback git_config_backend_set_callback , Ptr <git_config_backend> -> CString -> CString -> IO CInt
+#callback git_config_backend_set_multivar_callback , Ptr <git_config_backend> -> CString -> CString -> CString -> IO CInt
+#callback git_config_backend_del_callback , Ptr <git_config_backend> -> CString -> IO CInt
+#callback git_config_backend_foreach_callback , Ptr <git_config_backend> -> CString -> <git_config_foreach_cb> -> Ptr () -> IO CInt
+#callback git_config_backend_refresh_callback , Ptr <git_config_backend> -> IO CInt
+#callback git_config_backend_free_callback , Ptr <git_config_backend> -> IO ()
+#starttype git_config_backend
+#field version , CUInt
 #field cfg , Ptr <git_config>
-#field open , <git_config_open_callback>
-#field get , <git_config_get_callback>
-#field get_multivar , <git_config_get_multivar_callback>
-#field set , <git_config_set_callback>
-#field set_multivar , <git_config_set_multivar_callback>
-#field del , <git_config_del_callback>
-#field foreach , <git_config_foreach_callback>
-#field free , <git_config_free_callback>
+#field open , <git_config_backend_open_callback>
+#field get , <git_config_backend_get_callback>
+#field get_multivar , <git_config_backend_get_multivar_callback>
+#field set , <git_config_backend_set_callback>
+#field set_multivar , <git_config_backend_set_multivar_callback>
+#field del , <git_config_backend_del_callback>
+#field foreach , <git_config_backend_foreach_callback>
+#field refresh , <git_config_backend_refresh_callback>
+#field free , <git_config_backend_free_callback>
 #stoptype
 {- typedef enum {
             GIT_CVAR_FALSE = 0,
@@ -70,24 +99,32 @@
 #field map_value , CInt
 #stoptype
 #ccall git_config_find_global , CString -> CSize -> IO (CInt)
+#ccall git_config_find_xdg , CString -> CSize -> IO (CInt)
 #ccall git_config_find_system , CString -> CSize -> IO (CInt)
-#ccall git_config_open_global , Ptr (Ptr <git_config>) -> IO (CInt)
-#ccall git_config_file__ondisk , Ptr (Ptr <git_config_file>) -> CString -> IO (CInt)
+#ccall git_config_open_default , Ptr (Ptr <git_config>) -> IO (CInt)
 #ccall git_config_new , Ptr (Ptr <git_config>) -> IO (CInt)
-#ccall git_config_add_file , Ptr <git_config> -> Ptr <git_config_file> -> CInt -> IO (CInt)
-#ccall git_config_add_file_ondisk , Ptr <git_config> -> CString -> CInt -> IO (CInt)
+#ccall git_config_add_backend , Ptr <git_config> -> Ptr <git_config_backend> -> CUInt -> CInt -> IO (CInt)
+#ccall git_config_add_file_ondisk , Ptr <git_config> -> CString -> CUInt -> CInt -> IO (CInt)
 #ccall git_config_open_ondisk , Ptr (Ptr <git_config>) -> CString -> IO (CInt)
+#ccall git_config_open_level , Ptr (Ptr <git_config>) -> Ptr <git_config> -> CUInt -> IO (CInt)
+#ccall git_config_refresh , Ptr <git_config> -> IO (CInt)
 #ccall git_config_free , Ptr <git_config> -> IO ()
+#ccall git_config_get_entry , Ptr (Ptr <git_config_entry>) -> Ptr <git_config> -> CString -> IO (CInt)
 #ccall git_config_get_int32 , Ptr CInt -> Ptr <git_config> -> CString -> IO (CInt)
 #ccall git_config_get_int64 , Ptr CLong -> Ptr <git_config> -> CString -> IO (CInt)
 #ccall git_config_get_bool , Ptr CInt -> Ptr <git_config> -> CString -> IO (CInt)
 #ccall git_config_get_string , Ptr (CString) -> Ptr <git_config> -> CString -> IO (CInt)
-#ccall git_config_get_multivar , Ptr <git_config> -> CString -> CString -> <git_config_get_multivar_inner_callback> -> Ptr () -> IO (CInt)
+#ccall git_config_get_multivar , Ptr <git_config> -> CString -> CString -> <git_config_foreach_cb> -> Ptr () -> IO (CInt)
 #ccall git_config_set_int32 , Ptr <git_config> -> CString -> CInt -> IO (CInt)
 #ccall git_config_set_int64 , Ptr <git_config> -> CString -> CLong -> IO (CInt)
 #ccall git_config_set_bool , Ptr <git_config> -> CString -> CInt -> IO (CInt)
 #ccall git_config_set_string , Ptr <git_config> -> CString -> CString -> IO (CInt)
 #ccall git_config_set_multivar , Ptr <git_config> -> CString -> CString -> CString -> IO (CInt)
-#ccall git_config_delete , Ptr <git_config> -> CString -> IO (CInt)
-#ccall git_config_foreach , Ptr <git_config> -> <git_config_foreach_inner_callback> -> Ptr () -> IO (CInt)
+#ccall git_config_delete_entry , Ptr <git_config> -> CString -> IO (CInt)
+#ccall git_config_foreach , Ptr <git_config> -> <git_config_foreach_cb> -> Ptr () -> IO (CInt)
+#ccall git_config_foreach_match , Ptr <git_config> -> CString -> <git_config_foreach_cb> -> Ptr () -> IO (CInt)
 #ccall git_config_get_mapped , Ptr CInt -> Ptr <git_config> -> CString -> Ptr <git_cvar_map> -> CSize -> IO (CInt)
+#ccall git_config_lookup_map_value , Ptr CInt -> Ptr <git_cvar_map> -> CSize -> CString -> IO (CInt)
+#ccall git_config_parse_bool , Ptr CInt -> CString -> IO (CInt)
+#ccall git_config_parse_int32 , Ptr CInt -> CString -> IO (CInt)
+#ccall git_config_parse_int64 , Ptr CLong -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/CredHelpers.hsc b/Bindings/Libgit2/CredHelpers.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/CredHelpers.hsc
@@ -0,0 +1,17 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/cred_helpers.h>
+module Bindings.Libgit2.CredHelpers where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Transport
+{- typedef struct git_cred_userpass_payload {
+            char * username; char * password;
+        } git_cred_userpass_payload; -}
+#starttype git_cred_userpass_payload
+#field username , CString
+#field password , CString
+#stoptype
+#ccall git_cred_userpass , Ptr (Ptr <git_cred>) -> CString -> CString -> CUInt -> Ptr () -> IO (CInt)
diff --git a/Bindings/Libgit2/Diff.hsc b/Bindings/Libgit2/Diff.hsc
--- a/Bindings/Libgit2/Diff.hsc
+++ b/Bindings/Libgit2/Diff.hsc
@@ -1,27 +1,39 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Diff where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
+import Bindings.Libgit2.Strarray
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Tree
 import Bindings.Libgit2.Refs
-{- enum {
-    GIT_DIFF_NORMAL = 0,
-    GIT_DIFF_REVERSE = 1 << 0,
-    GIT_DIFF_FORCE_TEXT = 1 << 1,
-    GIT_DIFF_IGNORE_WHITESPACE = 1 << 2,
-    GIT_DIFF_IGNORE_WHITESPACE_CHANGE = 1 << 3,
-    GIT_DIFF_IGNORE_WHITESPACE_EOL = 1 << 4,
-    GIT_DIFF_IGNORE_SUBMODULES = 1 << 5,
-    GIT_DIFF_PATIENCE = 1 << 6,
-    GIT_DIFF_INCLUDE_IGNORED = 1 << 7,
-    GIT_DIFF_INCLUDE_UNTRACKED = 1 << 8,
-    GIT_DIFF_INCLUDE_UNMODIFIED = 1 << 9,
-    GIT_DIFF_RECURSE_UNTRACKED_DIRS = 1 << 10
-}; -}
+{- typedef enum {
+            GIT_DIFF_NORMAL = 0,
+            GIT_DIFF_REVERSE = 1 << 0,
+            GIT_DIFF_FORCE_TEXT = 1 << 1,
+            GIT_DIFF_IGNORE_WHITESPACE = 1 << 2,
+            GIT_DIFF_IGNORE_WHITESPACE_CHANGE = 1 << 3,
+            GIT_DIFF_IGNORE_WHITESPACE_EOL = 1 << 4,
+            GIT_DIFF_IGNORE_SUBMODULES = 1 << 5,
+            GIT_DIFF_PATIENCE = 1 << 6,
+            GIT_DIFF_INCLUDE_IGNORED = 1 << 7,
+            GIT_DIFF_INCLUDE_UNTRACKED = 1 << 8,
+            GIT_DIFF_INCLUDE_UNMODIFIED = 1 << 9,
+            GIT_DIFF_RECURSE_UNTRACKED_DIRS = 1 << 10,
+            GIT_DIFF_DISABLE_PATHSPEC_MATCH = 1 << 11,
+            GIT_DIFF_DELTAS_ARE_ICASE = 1 << 12,
+            GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = 1 << 13,
+            GIT_DIFF_SKIP_BINARY_CHECK = 1 << 14,
+            GIT_DIFF_INCLUDE_TYPECHANGE = 1 << 15,
+            GIT_DIFF_INCLUDE_TYPECHANGE_TREES = 1 << 16,
+            GIT_DIFF_IGNORE_FILEMODE = 1 << 17,
+            GIT_DIFF_RECURSE_IGNORED_DIRS = 1 << 18
+        } git_diff_option_t; -}
+#integral_t git_diff_option_t
 #num GIT_DIFF_NORMAL
 #num GIT_DIFF_REVERSE
 #num GIT_DIFF_FORCE_TEXT
@@ -34,39 +46,26 @@
 #num GIT_DIFF_INCLUDE_UNTRACKED
 #num GIT_DIFF_INCLUDE_UNMODIFIED
 #num GIT_DIFF_RECURSE_UNTRACKED_DIRS
-{- typedef struct {
-            uint32_t flags;
-            uint16_t context_lines;
-            uint16_t interhunk_lines;
-            char * old_prefix;
-            char * new_prefix;
-            git_strarray pathspec;
-        } git_diff_options; -}
-#starttype git_diff_options
-#field flags , CUInt
-#field context_lines , CUShort
-#field interhunk_lines , CUShort
-#field old_prefix , CString
-#field new_prefix , CString
-#field pathspec , <git_strarray>
-#stoptype
+#num GIT_DIFF_DISABLE_PATHSPEC_MATCH
+#num GIT_DIFF_DELTAS_ARE_ICASE
+#num GIT_DIFF_INCLUDE_UNTRACKED_CONTENT
+#num GIT_DIFF_SKIP_BINARY_CHECK
+#num GIT_DIFF_INCLUDE_TYPECHANGE
+#num GIT_DIFF_INCLUDE_TYPECHANGE_TREES
+#num GIT_DIFF_IGNORE_FILEMODE
+#num GIT_DIFF_RECURSE_IGNORED_DIRS
 {- typedef struct git_diff_list git_diff_list; -}
 #opaque_t git_diff_list
-{- enum {
-    GIT_DIFF_FILE_VALID_OID = 1 << 0,
-    GIT_DIFF_FILE_FREE_PATH = 1 << 1,
-    GIT_DIFF_FILE_BINARY = 1 << 2,
-    GIT_DIFF_FILE_NOT_BINARY = 1 << 3,
-    GIT_DIFF_FILE_FREE_DATA = 1 << 4,
-    GIT_DIFF_FILE_UNMAP_DATA = 1 << 5
-}; -}
-#num GIT_DIFF_FILE_VALID_OID
-#num GIT_DIFF_FILE_FREE_PATH
-#num GIT_DIFF_FILE_BINARY
-#num GIT_DIFF_FILE_NOT_BINARY
-#num GIT_DIFF_FILE_FREE_DATA
-#num GIT_DIFF_FILE_UNMAP_DATA
 {- typedef enum {
+            GIT_DIFF_FLAG_BINARY = 1 << 0,
+            GIT_DIFF_FLAG_NOT_BINARY = 1 << 1,
+            GIT_DIFF_FLAG_VALID_OID = 1 << 2
+        } git_diff_flag_t; -}
+#integral_t git_diff_flag_t
+#num GIT_DIFF_FLAG_BINARY
+#num GIT_DIFF_FLAG_NOT_BINARY
+#num GIT_DIFF_FLAG_VALID_OID
+{- typedef enum {
             GIT_DELTA_UNMODIFIED = 0,
             GIT_DELTA_ADDED = 1,
             GIT_DELTA_DELETED = 2,
@@ -74,7 +73,8 @@
             GIT_DELTA_RENAMED = 4,
             GIT_DELTA_COPIED = 5,
             GIT_DELTA_IGNORED = 6,
-            GIT_DELTA_UNTRACKED = 7
+            GIT_DELTA_UNTRACKED = 7,
+            GIT_DELTA_TYPECHANGE = 8
         } git_delta_t; -}
 #integral_t git_delta_t
 #num GIT_DELTA_UNMODIFIED
@@ -85,39 +85,69 @@
 #num GIT_DELTA_COPIED
 #num GIT_DELTA_IGNORED
 #num GIT_DELTA_UNTRACKED
+#num GIT_DELTA_TYPECHANGE
 {- typedef struct {
             git_oid oid;
-            char * path;
-            uint16_t mode;
+            const char * path;
             git_off_t size;
-            unsigned int flags;
+            uint32_t flags;
+            uint16_t mode;
         } git_diff_file; -}
 #starttype git_diff_file
 #field oid , <git_oid>
 #field path , CString
-#field mode , CUShort
 #field size , CLong
 #field flags , CUInt
+#field mode , CUShort
 #stoptype
 {- typedef struct {
             git_diff_file old_file;
             git_diff_file new_file;
             git_delta_t status;
-            unsigned int similarity;
-            int binary;
+            uint32_t similarity;
+            uint32_t flags;
         } git_diff_delta; -}
 #starttype git_diff_delta
 #field old_file , <git_diff_file>
 #field new_file , <git_diff_file>
 #field status , <git_delta_t>
 #field similarity , CUInt
-#field binary , CInt
+#field flags , CUInt
 #stoptype
-{- typedef int (* git_diff_file_fn)(void * cb_data,
-                                 git_diff_delta * delta,
-                                 float progress); -}
-#synonym_t git_diff_file_fn , CInt
+{- typedef int (* git_diff_notify_cb)(const git_diff_list * diff_so_far,
+                                   const git_diff_delta * delta_to_add,
+                                   const char * matched_pathspec,
+                                   void * payload); -}
+#callback git_diff_notify_cb , Ptr (<git_diff_list>) -> Ptr (<git_diff_delta>) -> CString -> Ptr () -> IO CInt
 {- typedef struct {
+            unsigned int version;
+            uint32_t flags;
+            uint16_t context_lines;
+            uint16_t interhunk_lines;
+            const char * old_prefix;
+            const char * new_prefix;
+            git_strarray pathspec;
+            git_off_t max_size;
+            git_diff_notify_cb notify_cb;
+            void * notify_payload;
+        } git_diff_options; -}
+#starttype git_diff_options
+#field version , CUInt
+#field flags , CUInt
+#field context_lines , CUShort
+#field interhunk_lines , CUShort
+#field old_prefix , CString
+#field new_prefix , CString
+#field pathspec , <git_strarray>
+#field max_size , CLong
+#field notify_cb , <git_diff_notify_cb>
+#field notify_payload , Ptr ()
+#stoptype
+{- typedef int (* git_diff_file_cb)(const git_diff_delta * delta,
+                                 float progress,
+                                 void * payload); -}
+#callback git_diff_file_cb , Ptr (<git_diff_delta>) -> CFloat -> Ptr () -> IO CInt
+{- typedef struct {
             int old_start; int old_lines; int new_start; int new_lines;
         } git_diff_range; -}
 #starttype git_diff_range
@@ -126,22 +156,23 @@
 #field new_start , CInt
 #field new_lines , CInt
 #stoptype
-{- typedef int (* git_diff_hunk_fn)(void * cb_data,
-                                 git_diff_delta * delta,
-                                 git_diff_range * range,
+{- typedef int (* git_diff_hunk_cb)(const git_diff_delta * delta,
+                                 const git_diff_range * range,
                                  const char * header,
-                                 size_t header_len); -}
-#synonym_t git_diff_hunk_fn , CInt
-{- enum {
-    GIT_DIFF_LINE_CONTEXT = ' ',
-    GIT_DIFF_LINE_ADDITION = '+',
-    GIT_DIFF_LINE_DELETION = '-',
-    GIT_DIFF_LINE_ADD_EOFNL = '\n',
-    GIT_DIFF_LINE_DEL_EOFNL = '\0',
-    GIT_DIFF_LINE_FILE_HDR = 'F',
-    GIT_DIFF_LINE_HUNK_HDR = 'H',
-    GIT_DIFF_LINE_BINARY = 'B'
-}; -}
+                                 size_t header_len,
+                                 void * payload); -}
+#callback git_diff_hunk_cb , Ptr (<git_diff_delta>) -> Ptr (<git_diff_range>) -> CString -> CSize -> Ptr () -> IO CInt
+{- typedef enum {
+            GIT_DIFF_LINE_CONTEXT = ' ',
+            GIT_DIFF_LINE_ADDITION = '+',
+            GIT_DIFF_LINE_DELETION = '-',
+            GIT_DIFF_LINE_ADD_EOFNL = '\n',
+            GIT_DIFF_LINE_DEL_EOFNL = '\0',
+            GIT_DIFF_LINE_FILE_HDR = 'F',
+            GIT_DIFF_LINE_HUNK_HDR = 'H',
+            GIT_DIFF_LINE_BINARY = 'B'
+        } git_diff_line_t; -}
+#integral_t git_diff_line_t
 #num GIT_DIFF_LINE_CONTEXT
 #num GIT_DIFF_LINE_ADDITION
 #num GIT_DIFF_LINE_DELETION
@@ -150,20 +181,106 @@
 #num GIT_DIFF_LINE_FILE_HDR
 #num GIT_DIFF_LINE_HUNK_HDR
 #num GIT_DIFF_LINE_BINARY
-{- typedef int (* git_diff_data_fn)(void * cb_data,
-                                 git_diff_delta * delta,
-                                 git_diff_range * range,
+{- typedef int (* git_diff_data_cb)(const git_diff_delta * delta,
+                                 const git_diff_range * range,
                                  char line_origin,
                                  const char * content,
-                                 size_t content_len); -}
-#synonym_t git_diff_data_fn , CInt
+                                 size_t content_len,
+                                 void * payload); -}
+#callback git_diff_data_cb , Ptr (<git_diff_delta>) -> Ptr (<git_diff_range>) -> CChar -> CString -> CSize -> Ptr () -> IO CInt
+{- typedef struct git_diff_patch git_diff_patch; -}
+#opaque_t git_diff_patch
+{- typedef enum {
+            GIT_DIFF_FIND_RENAMES = 1 << 0,
+            GIT_DIFF_FIND_RENAMES_FROM_REWRITES = 1 << 1,
+            GIT_DIFF_FIND_COPIES = 1 << 2,
+            GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = 1 << 3,
+            GIT_DIFF_FIND_AND_BREAK_REWRITES = 1 << 4,
+            GIT_DIFF_FIND_ALL = 0x1f,
+            GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
+            GIT_DIFF_FIND_IGNORE_WHITESPACE = 1 << 6,
+            GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = 1 << 7
+        } git_diff_find_t; -}
+#integral_t git_diff_find_t
+#num GIT_DIFF_FIND_RENAMES
+#num GIT_DIFF_FIND_RENAMES_FROM_REWRITES
+#num GIT_DIFF_FIND_COPIES
+#num GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED
+#num GIT_DIFF_FIND_AND_BREAK_REWRITES
+#num GIT_DIFF_FIND_ALL
+#num GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE
+#num GIT_DIFF_FIND_IGNORE_WHITESPACE
+#num GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE
+{- typedef struct {
+            int (* file_signature)(void * * out,
+                                   const git_diff_file * file,
+                                   const char * fullpath,
+                                   void * payload);
+            int (* buffer_signature)(void * * out,
+                                     const git_diff_file * file,
+                                     const char * buf,
+                                     size_t buflen,
+                                     void * payload);
+            void (* free_signature)(void * sig, void * payload);
+            int (* similarity)(int * score,
+                               void * siga,
+                               void * sigb,
+                               void * payload);
+            void * payload;
+        } git_diff_similarity_metric; -}
+#callback git_diff_similarity_metric_file_signature_callback , Ptr (Ptr ()) -> Ptr <git_diff_file> -> CString -> Ptr () -> IO CInt
+#callback git_diff_similarity_metric_buffer_signature_callback , Ptr (Ptr ()) -> Ptr <git_diff_file> -> CString -> CSize -> Ptr () -> IO CInt
+#callback git_diff_similarity_metric_free_signature_callback , Ptr () -> Ptr () -> IO ()
+#callback git_diff_similarity_metric_similarity_callback , Ptr CInt -> Ptr () -> Ptr () -> Ptr () -> IO CInt
+#starttype git_diff_similarity_metric
+#field file_signature , <git_diff_similarity_metric_file_signature_callback>
+#field buffer_signature , <git_diff_similarity_metric_buffer_signature_callback>
+#field free_signature , <git_diff_similarity_metric_free_signature_callback>
+#field similarity , <git_diff_similarity_metric_similarity_callback>
+#field payload , Ptr ()
+#stoptype
+{- typedef struct {
+            unsigned int version;
+            unsigned int flags;
+            unsigned int rename_threshold;
+            unsigned int rename_from_rewrite_threshold;
+            unsigned int copy_threshold;
+            unsigned int break_rewrite_threshold;
+            unsigned int target_limit;
+            git_diff_similarity_metric * metric;
+        } git_diff_find_options; -}
+#starttype git_diff_find_options
+#field version , CUInt
+#field flags , CUInt
+#field rename_threshold , CUInt
+#field rename_from_rewrite_threshold , CUInt
+#field copy_threshold , CUInt
+#field break_rewrite_threshold , CUInt
+#field target_limit , CUInt
+#field metric , Ptr <git_diff_similarity_metric>
+#stoptype
 #ccall git_diff_list_free , Ptr <git_diff_list> -> IO ()
-#ccall git_diff_tree_to_tree , Ptr <git_repository> -> Ptr <git_diff_options> -> Ptr <git_tree> -> Ptr <git_tree> -> Ptr (Ptr <git_diff_list>) -> IO (CInt)
-#ccall git_diff_index_to_tree , Ptr <git_repository> -> Ptr <git_diff_options> -> Ptr <git_tree> -> Ptr (Ptr <git_diff_list>) -> IO (CInt)
-#ccall git_diff_workdir_to_index , Ptr <git_repository> -> Ptr <git_diff_options> -> Ptr (Ptr <git_diff_list>) -> IO (CInt)
-#ccall git_diff_workdir_to_tree , Ptr <git_repository> -> Ptr <git_diff_options> -> Ptr <git_tree> -> Ptr (Ptr <git_diff_list>) -> IO (CInt)
+#ccall git_diff_tree_to_tree , Ptr (Ptr <git_diff_list>) -> Ptr <git_repository> -> Ptr <git_tree> -> Ptr <git_tree> -> Ptr <git_diff_options> -> IO (CInt)
+#ccall git_diff_tree_to_index , Ptr (Ptr <git_diff_list>) -> Ptr <git_repository> -> Ptr <git_tree> -> Ptr <git_index> -> Ptr <git_diff_options> -> IO (CInt)
+#ccall git_diff_index_to_workdir , Ptr (Ptr <git_diff_list>) -> Ptr <git_repository> -> Ptr <git_index> -> Ptr <git_diff_options> -> IO (CInt)
+#ccall git_diff_tree_to_workdir , Ptr (Ptr <git_diff_list>) -> Ptr <git_repository> -> Ptr <git_tree> -> Ptr <git_diff_options> -> IO (CInt)
 #ccall git_diff_merge , Ptr <git_diff_list> -> Ptr <git_diff_list> -> IO (CInt)
-#ccall git_diff_foreach , Ptr <git_diff_list> -> Ptr () -> CInt -> CInt -> CInt -> IO (CInt)
-#ccall git_diff_print_compact , Ptr <git_diff_list> -> Ptr () -> CInt -> IO (CInt)
-#ccall git_diff_print_patch , Ptr <git_diff_list> -> Ptr () -> CInt -> IO (CInt)
-#ccall git_diff_blobs , Ptr <git_blob> -> Ptr <git_blob> -> Ptr <git_diff_options> -> Ptr () -> CInt -> CInt -> CInt -> IO (CInt)
+#ccall git_diff_find_similar , Ptr <git_diff_list> -> Ptr <git_diff_find_options> -> IO (CInt)
+#ccall git_diff_foreach , Ptr <git_diff_list> -> <git_diff_file_cb> -> <git_diff_hunk_cb> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
+#ccall git_diff_print_compact , Ptr <git_diff_list> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
+#ccall git_diff_status_char , <git_delta_t> -> IO (CChar)
+#ccall git_diff_print_patch , Ptr <git_diff_list> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
+#ccall git_diff_num_deltas , Ptr <git_diff_list> -> IO (CSize)
+#ccall git_diff_num_deltas_of_type , Ptr <git_diff_list> -> <git_delta_t> -> IO (CSize)
+#ccall git_diff_get_patch , Ptr (Ptr <git_diff_patch>) -> Ptr (Ptr <git_diff_delta>) -> Ptr <git_diff_list> -> CSize -> IO (CInt)
+#ccall git_diff_patch_free , Ptr <git_diff_patch> -> IO ()
+#ccall git_diff_patch_delta , Ptr <git_diff_patch> -> IO (Ptr <git_diff_delta>)
+#ccall git_diff_patch_num_hunks , Ptr <git_diff_patch> -> IO (CSize)
+#ccall git_diff_patch_line_stats , Ptr CSize -> Ptr CSize -> Ptr CSize -> Ptr <git_diff_patch> -> IO (CInt)
+#ccall git_diff_patch_get_hunk , Ptr (Ptr <git_diff_range>) -> Ptr (CString) -> Ptr CSize -> Ptr CSize -> Ptr <git_diff_patch> -> CSize -> IO (CInt)
+#ccall git_diff_patch_num_lines_in_hunk , Ptr <git_diff_patch> -> CSize -> IO (CInt)
+#ccall git_diff_patch_get_line_in_hunk , CString -> Ptr (CString) -> Ptr CSize -> Ptr CInt -> Ptr CInt -> Ptr <git_diff_patch> -> CSize -> CSize -> IO (CInt)
+#ccall git_diff_patch_print , Ptr <git_diff_patch> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
+#ccall git_diff_patch_to_str , Ptr (CString) -> Ptr <git_diff_patch> -> IO (CInt)
+#ccall git_diff_blobs , Ptr <git_blob> -> Ptr <git_blob> -> Ptr <git_diff_options> -> <git_diff_file_cb> -> <git_diff_hunk_cb> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
+#ccall git_diff_blob_to_buffer , Ptr <git_blob> -> CString -> CSize -> Ptr <git_diff_options> -> <git_diff_file_cb> -> <git_diff_hunk_cb> -> <git_diff_data_cb> -> Ptr () -> IO (CInt)
diff --git a/Bindings/Libgit2/Errors.hsc b/Bindings/Libgit2/Errors.hsc
--- a/Bindings/Libgit2/Errors.hsc
+++ b/Bindings/Libgit2/Errors.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Errors where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -11,8 +13,15 @@
     GIT_EEXISTS = -4,
     GIT_EAMBIGUOUS = -5,
     GIT_EBUFS = -6,
+    GIT_EUSER = -7,
+    GIT_EBAREREPO = -8,
+    GIT_EORPHANEDHEAD = -9,
+    GIT_EUNMERGED = -10,
+    GIT_ENONFASTFORWARD = -11,
+    GIT_EINVALIDSPEC = -12,
+    GIT_EMERGECONFLICT = -13,
     GIT_PASSTHROUGH = -30,
-    GIT_REVWALKOVER = -31
+    GIT_ITEROVER = -31
 }; -}
 #num GIT_OK
 #num GIT_ERROR
@@ -20,8 +29,15 @@
 #num GIT_EEXISTS
 #num GIT_EAMBIGUOUS
 #num GIT_EBUFS
+#num GIT_EUSER
+#num GIT_EBAREREPO
+#num GIT_EORPHANEDHEAD
+#num GIT_EUNMERGED
+#num GIT_ENONFASTFORWARD
+#num GIT_EINVALIDSPEC
+#num GIT_EMERGECONFLICT
 #num GIT_PASSTHROUGH
-#num GIT_REVWALKOVER
+#num GIT_ITEROVER
 {- typedef struct {
             char * message; int klass;
         } git_error; -}
@@ -44,7 +60,14 @@
             GITERR_NET,
             GITERR_TAG,
             GITERR_TREE,
-            GITERR_INDEXER
+            GITERR_INDEXER,
+            GITERR_SSL,
+            GITERR_SUBMODULE,
+            GITERR_THREAD,
+            GITERR_STASH,
+            GITERR_CHECKOUT,
+            GITERR_FETCHHEAD,
+            GITERR_MERGE
         } git_error_t; -}
 #integral_t git_error_t
 #num GITERR_NOMEMORY
@@ -62,5 +85,14 @@
 #num GITERR_TAG
 #num GITERR_TREE
 #num GITERR_INDEXER
+#num GITERR_SSL
+#num GITERR_SUBMODULE
+#num GITERR_THREAD
+#num GITERR_STASH
+#num GITERR_CHECKOUT
+#num GITERR_FETCHHEAD
+#num GITERR_MERGE
 #ccall giterr_last , IO (Ptr <git_error>)
 #ccall giterr_clear , IO ()
+#ccall giterr_set_str , CInt -> CString -> IO ()
+#ccall giterr_set_oom , IO ()
diff --git a/Bindings/Libgit2/Graph.hsc b/Bindings/Libgit2/Graph.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Graph.hsc
@@ -0,0 +1,12 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/graph.h>
+module Bindings.Libgit2.Graph where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Oid
+#ccall git_graph_ahead_behind , Ptr CSize -> Ptr CSize -> Ptr <git_repository> -> Ptr <git_oid> -> Ptr <git_oid> -> IO (CInt)
diff --git a/Bindings/Libgit2/Ignore.hsc b/Bindings/Libgit2/Ignore.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Ignore.hsc
@@ -0,0 +1,13 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/ignore.h>
+module Bindings.Libgit2.Ignore where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+#ccall git_ignore_add_rule , Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_ignore_clear_internal_rules , Ptr <git_repository> -> IO (CInt)
+#ccall git_ignore_path_is_ignored , Ptr CInt -> Ptr <git_repository> -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Index.hsc b/Bindings/Libgit2/Index.hsc
--- a/Bindings/Libgit2/Index.hsc
+++ b/Bindings/Libgit2/Index.hsc
@@ -1,9 +1,12 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Index where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
+import Bindings.Libgit2.Indexer
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 {- typedef struct {
@@ -41,30 +44,55 @@
 #field flags_extended , CUShort
 #field path , CString
 #stoptype
-{- typedef struct git_index_entry_unmerged {
+{- typedef struct git_index_reuc_entry {
             unsigned int mode[3]; git_oid oid[3]; char * path;
-        } git_index_entry_unmerged; -}
-#starttype git_index_entry_unmerged
+        } git_index_reuc_entry; -}
+#starttype git_index_reuc_entry
 #array_field mode , CUInt
 #array_field oid , <git_oid>
 #field path , CString
 #stoptype
+{- enum {
+    GIT_INDEXCAP_IGNORE_CASE = 1,
+    GIT_INDEXCAP_NO_FILEMODE = 2,
+    GIT_INDEXCAP_NO_SYMLINKS = 4,
+    GIT_INDEXCAP_FROM_OWNER = ~0u
+}; -}
+#num GIT_INDEXCAP_IGNORE_CASE
+#num GIT_INDEXCAP_NO_FILEMODE
+#num GIT_INDEXCAP_NO_SYMLINKS
+#num GIT_INDEXCAP_FROM_OWNER
 #ccall git_index_open , Ptr (Ptr <git_index>) -> CString -> IO (CInt)
-#ccall git_index_clear , Ptr <git_index> -> IO ()
+#ccall git_index_new , Ptr (Ptr <git_index>) -> IO (CInt)
 #ccall git_index_free , Ptr <git_index> -> IO ()
+#ccall git_index_owner , Ptr <git_index> -> IO (Ptr <git_repository>)
+#ccall git_index_caps , Ptr <git_index> -> IO (CUInt)
+#ccall git_index_set_caps , Ptr <git_index> -> CUInt -> IO (CInt)
 #ccall git_index_read , Ptr <git_index> -> IO (CInt)
 #ccall git_index_write , Ptr <git_index> -> IO (CInt)
-#ccall git_index_find , Ptr <git_index> -> CString -> IO (CInt)
-#ccall git_index_uniq , Ptr <git_index> -> IO ()
-#ccall git_index_add , Ptr <git_index> -> CString -> CInt -> IO (CInt)
-#ccall git_index_add2 , Ptr <git_index> -> Ptr <git_index_entry> -> IO (CInt)
-#ccall git_index_append , Ptr <git_index> -> CString -> CInt -> IO (CInt)
-#ccall git_index_append2 , Ptr <git_index> -> Ptr <git_index_entry> -> IO (CInt)
-#ccall git_index_remove , Ptr <git_index> -> CInt -> IO (CInt)
-#ccall git_index_get , Ptr <git_index> -> CUInt -> IO (Ptr <git_index_entry>)
-#ccall git_index_entrycount , Ptr <git_index> -> IO (CUInt)
-#ccall git_index_entrycount_unmerged , Ptr <git_index> -> IO (CUInt)
-#ccall git_index_get_unmerged_bypath , Ptr <git_index> -> CString -> IO (Ptr <git_index_entry_unmerged>)
-#ccall git_index_get_unmerged_byindex , Ptr <git_index> -> CUInt -> IO (Ptr <git_index_entry_unmerged>)
-#ccall git_index_entry_stage , Ptr <git_index_entry> -> IO (CInt)
 #ccall git_index_read_tree , Ptr <git_index> -> Ptr <git_tree> -> IO (CInt)
+#ccall git_index_write_tree , Ptr <git_oid> -> Ptr <git_index> -> IO (CInt)
+#ccall git_index_write_tree_to , Ptr <git_oid> -> Ptr <git_index> -> Ptr <git_repository> -> IO (CInt)
+#ccall git_index_entrycount , Ptr <git_index> -> IO (CSize)
+#ccall git_index_clear , Ptr <git_index> -> IO ()
+#ccall git_index_get_byindex , Ptr <git_index> -> CSize -> IO (Ptr <git_index_entry>)
+#ccall git_index_get_bypath , Ptr <git_index> -> CString -> CInt -> IO (Ptr <git_index_entry>)
+#ccall git_index_remove , Ptr <git_index> -> CString -> CInt -> IO (CInt)
+#ccall git_index_remove_directory , Ptr <git_index> -> CString -> CInt -> IO (CInt)
+#ccall git_index_add , Ptr <git_index> -> Ptr <git_index_entry> -> IO (CInt)
+#ccall git_index_entry_stage , Ptr <git_index_entry> -> IO (CInt)
+#ccall git_index_add_bypath , Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_remove_bypath , Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_find , Ptr CSize -> Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_conflict_add , Ptr <git_index> -> Ptr <git_index_entry> -> Ptr <git_index_entry> -> Ptr <git_index_entry> -> IO (CInt)
+#ccall git_index_conflict_get , Ptr (Ptr <git_index_entry>) -> Ptr (Ptr <git_index_entry>) -> Ptr (Ptr <git_index_entry>) -> Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_conflict_remove , Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_conflict_cleanup , Ptr <git_index> -> IO ()
+#ccall git_index_has_conflicts , Ptr <git_index> -> IO (CInt)
+#ccall git_index_reuc_entrycount , Ptr <git_index> -> IO (CUInt)
+#ccall git_index_reuc_find , Ptr CSize -> Ptr <git_index> -> CString -> IO (CInt)
+#ccall git_index_reuc_get_bypath , Ptr <git_index> -> CString -> IO (Ptr <git_index_reuc_entry>)
+#ccall git_index_reuc_get_byindex , Ptr <git_index> -> CSize -> IO (Ptr <git_index_reuc_entry>)
+#ccall git_index_reuc_add , Ptr <git_index> -> CString -> CInt -> Ptr <git_oid> -> CInt -> Ptr <git_oid> -> CInt -> Ptr <git_oid> -> IO (CInt)
+#ccall git_index_reuc_remove , Ptr <git_index> -> CSize -> IO (CInt)
+#ccall git_index_reuc_clear , Ptr <git_index> -> IO ()
diff --git a/Bindings/Libgit2/Indexer.hsc b/Bindings/Libgit2/Indexer.hsc
--- a/Bindings/Libgit2/Indexer.hsc
+++ b/Bindings/Libgit2/Indexer.hsc
@@ -1,28 +1,31 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Indexer where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Oid
-{- typedef struct git_indexer_stats {
-            unsigned int total; unsigned int processed;
-        } git_indexer_stats; -}
-#starttype git_indexer_stats
-#field total , CUInt
-#field processed , CUInt
+{- typedef struct git_transfer_progress {
+            unsigned int total_objects;
+            unsigned int indexed_objects;
+            unsigned int received_objects;
+            size_t received_bytes;
+        } git_transfer_progress; -}
+#starttype git_transfer_progress
+#field total_objects , CUInt
+#field indexed_objects , CUInt
+#field received_objects , CUInt
+#field received_bytes , CSize
 #stoptype
-{- typedef struct git_indexer git_indexer; -}
-#opaque_t git_indexer
+{- typedef int (* git_transfer_progress_callback)(const git_transfer_progress * stats,
+                                               void * payload); -}
+#callback git_transfer_progress_callback , Ptr (<git_transfer_progress>) -> Ptr () -> IO CInt
 {- typedef struct git_indexer_stream git_indexer_stream; -}
 #opaque_t git_indexer_stream
-#ccall git_indexer_stream_new , Ptr (Ptr <git_indexer_stream>) -> CString -> IO (CInt)
-#ccall git_indexer_stream_add , Ptr <git_indexer_stream> -> Ptr () -> CSize -> Ptr <git_indexer_stats> -> IO (CInt)
-#ccall git_indexer_stream_finalize , Ptr <git_indexer_stream> -> Ptr <git_indexer_stats> -> IO (CInt)
+#ccall git_indexer_stream_new , Ptr (Ptr <git_indexer_stream>) -> CString -> <git_transfer_progress_callback> -> Ptr () -> IO (CInt)
+#ccall git_indexer_stream_add , Ptr <git_indexer_stream> -> Ptr () -> CSize -> Ptr <git_transfer_progress> -> IO (CInt)
+#ccall git_indexer_stream_finalize , Ptr <git_indexer_stream> -> Ptr <git_transfer_progress> -> IO (CInt)
 #ccall git_indexer_stream_hash , Ptr <git_indexer_stream> -> IO (Ptr <git_oid>)
 #ccall git_indexer_stream_free , Ptr <git_indexer_stream> -> IO ()
-#ccall git_indexer_new , Ptr (Ptr <git_indexer>) -> CString -> IO (CInt)
-#ccall git_indexer_run , Ptr <git_indexer> -> Ptr <git_indexer_stats> -> IO (CInt)
-#ccall git_indexer_write , Ptr <git_indexer> -> IO (CInt)
-#ccall git_indexer_hash , Ptr <git_indexer> -> IO (Ptr <git_oid>)
-#ccall git_indexer_free , Ptr <git_indexer> -> IO ()
diff --git a/Bindings/Libgit2/Merge.hsc b/Bindings/Libgit2/Merge.hsc
--- a/Bindings/Libgit2/Merge.hsc
+++ b/Bindings/Libgit2/Merge.hsc
@@ -1,9 +1,12 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Merge where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 #ccall git_merge_base , Ptr <git_oid> -> Ptr <git_repository> -> Ptr <git_oid> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_merge_base_many , Ptr <git_oid> -> Ptr <git_repository> -> Ptr (<git_oid>) -> CSize -> IO (CInt)
diff --git a/Bindings/Libgit2/Message.hsc b/Bindings/Libgit2/Message.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Message.hsc
@@ -0,0 +1,10 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/message.h>
+module Bindings.Libgit2.Message where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+#ccall git_message_prettify , CString -> CSize -> CString -> CInt -> IO (CInt)
diff --git a/Bindings/Libgit2/Net.hsc b/Bindings/Libgit2/Net.hsc
--- a/Bindings/Libgit2/Net.hsc
+++ b/Bindings/Libgit2/Net.hsc
@@ -1,19 +1,28 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Net where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Types
+{- typedef enum {
+            GIT_DIRECTION_FETCH = 0, GIT_DIRECTION_PUSH = 1
+        } git_direction; -}
+#integral_t git_direction
+#num GIT_DIRECTION_FETCH
+#num GIT_DIRECTION_PUSH
 {- struct git_remote_head {
-    int local : 1; git_oid oid; git_oid loid; char * name;
+    int local; git_oid oid; git_oid loid; char * name;
 }; -}
 #starttype git_remote_head
--- #field local , CInt
+#field local , CInt
 #field oid , <git_oid>
 #field loid , <git_oid>
 #field name , CString
 #stoptype
-{- typedef int (* git_headlist_cb)(git_remote_head *, void *); -}
-#synonym_t git_headlist_cb , CInt
+{- typedef int (* git_headlist_cb)(git_remote_head * rhead,
+                                void * payload); -}
+#callback git_headlist_cb , Ptr (<git_remote_head>) -> Ptr () -> IO CInt
diff --git a/Bindings/Libgit2/Notes.hsc b/Bindings/Libgit2/Notes.hsc
--- a/Bindings/Libgit2/Notes.hsc
+++ b/Bindings/Libgit2/Notes.hsc
@@ -1,23 +1,27 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Notes where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Types
+{- typedef int (* git_note_foreach_cb)(const git_oid * blob_id,
+                                    const git_oid * annotated_object_id,
+                                    void * payload); -}
+#callback git_note_foreach_cb , Ptr (<git_oid>) -> Ptr (<git_oid>) -> Ptr () -> Ptr CInt
+{- typedef struct git_iterator git_note_iterator; -}
+#opaque_t git_iterator
+#synonym_t git_note_iterator , <git_iterator>
+#ccall git_note_iterator_new , Ptr (Ptr <git_iterator>) -> Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_note_iterator_free , Ptr <git_iterator> -> IO ()
+#ccall git_note_next , Ptr <git_oid> -> Ptr <git_oid> -> Ptr <git_iterator> -> IO (CInt)
 #ccall git_note_read , Ptr (Ptr <git_note>) -> Ptr <git_repository> -> CString -> Ptr <git_oid> -> IO (CInt)
 #ccall git_note_message , Ptr <git_note> -> IO (CString)
 #ccall git_note_oid , Ptr <git_note> -> IO (Ptr <git_oid>)
-#ccall git_note_create , Ptr <git_oid> -> Ptr <git_repository> -> Ptr <git_signature> -> Ptr <git_signature> -> CString -> Ptr <git_oid> -> CString -> IO (CInt)
+#ccall git_note_create , Ptr <git_oid> -> Ptr <git_repository> -> Ptr <git_signature> -> Ptr <git_signature> -> CString -> Ptr <git_oid> -> CString -> CInt -> IO (CInt)
 #ccall git_note_remove , Ptr <git_repository> -> CString -> Ptr <git_signature> -> Ptr <git_signature> -> Ptr <git_oid> -> IO (CInt)
 #ccall git_note_free , Ptr <git_note> -> IO ()
 #ccall git_note_default_ref , Ptr (CString) -> Ptr <git_repository> -> IO (CInt)
-{- typedef struct {
-            git_oid blob_oid; git_oid annotated_object_oid;
-        } git_note_data; -}
-#starttype git_note_data
-#field blob_oid , <git_oid>
-#field annotated_object_oid , <git_oid>
-#stoptype
-#callback git_note_foreach_callback , Ptr <git_note_data> -> Ptr () -> IO CInt
-#ccall git_note_foreach , Ptr <git_repository> -> CString -> <git_note_foreach_callback> -> Ptr () -> IO (CInt)
+#ccall git_note_foreach , Ptr <git_repository> -> CString -> <git_note_foreach_cb> -> Ptr () -> IO (CInt)
diff --git a/Bindings/Libgit2/Object.hsc b/Bindings/Libgit2/Object.hsc
--- a/Bindings/Libgit2/Object.hsc
+++ b/Bindings/Libgit2/Object.hsc
@@ -1,13 +1,15 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Object where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 #ccall git_object_lookup , Ptr (Ptr <git_object>) -> Ptr <git_repository> -> Ptr <git_oid> -> <git_otype> -> IO (CInt)
-#ccall git_object_lookup_prefix , Ptr (Ptr <git_object>) -> Ptr <git_repository> -> Ptr <git_oid> -> CUInt -> <git_otype> -> IO (CInt)
+#ccall git_object_lookup_prefix , Ptr (Ptr <git_object>) -> Ptr <git_repository> -> Ptr <git_oid> -> CSize -> <git_otype> -> IO (CInt)
 #ccall git_object_id , Ptr <git_object> -> IO (Ptr <git_oid>)
 #ccall git_object_type , Ptr <git_object> -> IO (<git_otype>)
 #ccall git_object_owner , Ptr <git_object> -> IO (Ptr <git_repository>)
@@ -16,3 +18,5 @@
 #ccall git_object_string2type , CString -> IO (<git_otype>)
 #ccall git_object_typeisloose , <git_otype> -> IO (CInt)
 #ccall git_object__size , <git_otype> -> IO (CSize)
+#ccall git_object_peel , Ptr (Ptr <git_object>) -> Ptr <git_object> -> <git_otype> -> IO (CInt)
+#ccall git_object_dup , Ptr (Ptr <git_object>) -> Ptr <git_object> -> IO (CInt)
diff --git a/Bindings/Libgit2/Odb.hsc b/Bindings/Libgit2/Odb.hsc
--- a/Bindings/Libgit2/Odb.hsc
+++ b/Bindings/Libgit2/Odb.hsc
@@ -1,24 +1,31 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Odb where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.OdbBackend
+import Bindings.Libgit2.Indexer
 #ccall git_odb_new , Ptr (Ptr <git_odb>) -> IO (CInt)
 #ccall git_odb_open , Ptr (Ptr <git_odb>) -> CString -> IO (CInt)
 #ccall git_odb_add_backend , Ptr <git_odb> -> Ptr <git_odb_backend> -> CInt -> IO (CInt)
 #ccall git_odb_add_alternate , Ptr <git_odb> -> Ptr <git_odb_backend> -> CInt -> IO (CInt)
+#ccall git_odb_add_disk_alternate , Ptr <git_odb> -> CString -> IO (CInt)
 #ccall git_odb_free , Ptr <git_odb> -> IO ()
 #ccall git_odb_read , Ptr (Ptr <git_odb_object>) -> Ptr <git_odb> -> Ptr <git_oid> -> IO (CInt)
-#ccall git_odb_read_prefix , Ptr (Ptr <git_odb_object>) -> Ptr <git_odb> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#ccall git_odb_read_prefix , Ptr (Ptr <git_odb_object>) -> Ptr <git_odb> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #ccall git_odb_read_header , Ptr CSize -> Ptr <git_otype> -> Ptr <git_odb> -> Ptr <git_oid> -> IO (CInt)
 #ccall git_odb_exists , Ptr <git_odb> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_odb_refresh , Ptr <git_odb> -> IO (CInt)
+#ccall git_odb_foreach , Ptr <git_odb> -> <git_odb_foreach_cb> -> Ptr () -> IO (CInt)
 #ccall git_odb_write , Ptr <git_oid> -> Ptr <git_odb> -> Ptr () -> CSize -> <git_otype> -> IO (CInt)
 #ccall git_odb_open_wstream , Ptr (Ptr <git_odb_stream>) -> Ptr <git_odb> -> CSize -> <git_otype> -> IO (CInt)
 #ccall git_odb_open_rstream , Ptr (Ptr <git_odb_stream>) -> Ptr <git_odb> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_odb_write_pack , Ptr (Ptr <git_odb_writepack>) -> Ptr <git_odb> -> <git_transfer_progress_callback> -> Ptr () -> IO (CInt)
 #ccall git_odb_hash , Ptr <git_oid> -> Ptr () -> CSize -> <git_otype> -> IO (CInt)
 #ccall git_odb_hashfile , Ptr <git_oid> -> CString -> <git_otype> -> IO (CInt)
 #ccall git_odb_object_free , Ptr <git_odb_object> -> IO ()
diff --git a/Bindings/Libgit2/OdbBackend.hsc b/Bindings/Libgit2/OdbBackend.hsc
--- a/Bindings/Libgit2/OdbBackend.hsc
+++ b/Bindings/Libgit2/OdbBackend.hsc
@@ -1,12 +1,23 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.OdbBackend where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Indexer
+{- struct git_odb_stream; -}
+{- #opaque_t git_odb_stream -}
+{- struct git_odb_writepack; -}
+{- #opaque_t git_odb_writepack -}
+{- typedef int (* git_odb_foreach_cb)(const git_oid * id,
+                                   void * payload); -}
+#callback git_odb_foreach_cb , Ptr (<git_oid>) -> Ptr () -> IO CInt
 {- struct git_odb_backend {
+    unsigned int version;
     git_odb * odb;
     int (* read)(void * *,
                  size_t *,
@@ -19,7 +30,7 @@
                         git_otype *,
                         struct git_odb_backend *,
                         const git_oid *,
-                        unsigned int);
+                        size_t);
     int (* read_header)(size_t *,
                         git_otype *,
                         struct git_odb_backend *,
@@ -37,17 +48,29 @@
                        struct git_odb_backend *,
                        const git_oid *);
     int (* exists)(struct git_odb_backend *, const git_oid *);
+    int (* refresh)(struct git_odb_backend *);
+    int (* foreach)(struct git_odb_backend *,
+                    git_odb_foreach_cb cb,
+                    void * payload);
+    int (* writepack)(struct git_odb_writepack * *,
+                      struct git_odb_backend *,
+                      git_transfer_progress_callback progress_cb,
+                      void * progress_payload);
     void (* free)(struct git_odb_backend *);
 }; -}
 #callback git_odb_backend_read_callback , Ptr (Ptr ()) -> Ptr CSize -> Ptr <git_otype> -> Ptr <git_odb_backend> -> Ptr <git_oid> -> IO CInt
-#callback git_odb_backend_read_prefix_callback , Ptr <git_oid> -> Ptr (Ptr ()) -> Ptr CSize -> Ptr <git_otype> -> Ptr <git_odb_backend> -> Ptr <git_oid> -> CUInt -> IO CInt
+#callback git_odb_backend_read_prefix_callback , Ptr <git_oid> -> Ptr (Ptr ()) -> Ptr CSize -> Ptr <git_otype> -> Ptr <git_odb_backend> -> Ptr <git_oid> -> CSize -> IO CInt
 #callback git_odb_backend_read_header_callback , Ptr CSize -> Ptr <git_otype> -> Ptr <git_odb_backend> -> Ptr <git_oid> -> IO CInt
 #callback git_odb_backend_write_callback , Ptr <git_oid> -> Ptr <git_odb_backend> -> Ptr () -> CSize -> <git_otype> -> IO CInt
 #callback git_odb_backend_writestream_callback , Ptr (Ptr <git_odb_stream>) -> Ptr <git_odb_backend> -> CSize -> <git_otype> -> IO CInt
 #callback git_odb_backend_readstream_callback , Ptr (Ptr <git_odb_stream>) -> Ptr <git_odb_backend> -> Ptr <git_oid> -> IO CInt
 #callback git_odb_backend_exists_callback , Ptr <git_odb_backend> -> Ptr <git_oid> -> IO CInt
+#callback git_odb_backend_refresh_callback , Ptr <git_odb_backend> -> IO CInt
+#callback git_odb_backend_foreach_callback , Ptr <git_odb_backend> -> <git_odb_foreach_cb> -> Ptr () -> IO CInt
+#callback git_odb_backend_writepack_callback , Ptr (Ptr <git_odb_writepack>) -> Ptr <git_odb_backend> -> <git_transfer_progress_callback> -> Ptr () -> IO CInt
 #callback git_odb_backend_free_callback , Ptr <git_odb_backend> -> IO ()
 #starttype git_odb_backend
+#field version , CUInt
 #field odb , Ptr <git_odb>
 #field read , <git_odb_backend_read_callback>
 #field read_prefix , <git_odb_backend_read_prefix_callback>
@@ -56,6 +79,9 @@
 #field writestream , <git_odb_backend_writestream_callback>
 #field readstream , <git_odb_backend_readstream_callback>
 #field exists , <git_odb_backend_exists_callback>
+#field refresh , <git_odb_backend_refresh_callback>
+#field foreach , <git_odb_backend_foreach_callback>
+#field writepack , <git_odb_backend_writepack_callback>
 #field free , <git_odb_backend_free_callback>
 #stoptype
 {- enum {
@@ -68,7 +94,7 @@
 #num GIT_STREAM_RW
 {- struct git_odb_stream {
     struct git_odb_backend * backend;
-    int mode;
+    unsigned int mode;
     int (* read)(struct git_odb_stream * stream,
                  char * buffer,
                  size_t len);
@@ -85,11 +111,32 @@
 #callback git_odb_stream_free_callback , Ptr <git_odb_stream> -> IO ()
 #starttype git_odb_stream
 #field backend , Ptr <git_odb_backend>
-#field mode , CInt
+#field mode , CUInt
 #field read , <git_odb_stream_read_callback>
 #field write , <git_odb_stream_write_callback>
 #field finalize_write , <git_odb_stream_finalize_write_callback>
 #field free , <git_odb_stream_free_callback>
 #stoptype
+{- struct git_odb_writepack {
+    struct git_odb_backend * backend;
+    int (* add)(struct git_odb_writepack * writepack,
+                const void * data,
+                size_t size,
+                git_transfer_progress * stats);
+    int (* commit)(struct git_odb_writepack * writepack,
+                   git_transfer_progress * stats);
+    void (* free)(struct git_odb_writepack * writepack);
+}; -}
+#callback git_odb_writepack_add_callback , Ptr <git_odb_writepack> -> Ptr () -> CSize -> Ptr <git_transfer_progress> -> IO CInt
+#callback git_odb_writepack_commit_callback , Ptr <git_odb_writepack> -> Ptr <git_transfer_progress> -> IO CInt
+#callback git_odb_writepack_free_callback , Ptr <git_odb_writepack> -> IO ()
+#starttype git_odb_writepack
+#field backend , Ptr <git_odb_backend>
+#field add , <git_odb_writepack_add_callback>
+#field commit , <git_odb_writepack_commit_callback>
+#field free , <git_odb_writepack_free_callback>
+#stoptype
+#ccall git_odb_backend_malloc , Ptr <git_odb_backend> -> CSize -> IO (Ptr ())
 #ccall git_odb_backend_pack , Ptr (Ptr <git_odb_backend>) -> CString -> IO (CInt)
 #ccall git_odb_backend_loose , Ptr (Ptr <git_odb_backend>) -> CString -> CInt -> CInt -> IO (CInt)
+#ccall git_odb_backend_one_pack , Ptr (Ptr <git_odb_backend>) -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Oid.hsc b/Bindings/Libgit2/Oid.hsc
--- a/Bindings/Libgit2/Oid.hsc
+++ b/Bindings/Libgit2/Oid.hsc
@@ -1,20 +1,20 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Oid where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
-#num GIT_OID_RAWSZ
-#num GIT_OID_HEXSZ
-#num GIT_OID_MINPREFIXLEN
-{- struct _git_oid {
-    unsigned char id[20];
-}; -}
+{- typedef struct git_oid {
+            unsigned char id[20];
+        } git_oid; -}
 #starttype git_oid
 #array_field id , CUChar
 #stoptype
 #ccall git_oid_fromstr , Ptr <git_oid> -> CString -> IO (CInt)
+#ccall git_oid_fromstrp , Ptr <git_oid> -> CString -> IO (CInt)
 #ccall git_oid_fromstrn , Ptr <git_oid> -> CString -> CSize -> IO (CInt)
 #ccall git_oid_fromraw , Ptr <git_oid> -> Ptr CUChar -> IO ()
 #ccall git_oid_fmt , CString -> Ptr <git_oid> -> IO ()
@@ -22,8 +22,9 @@
 #ccall git_oid_allocfmt , Ptr <git_oid> -> IO (CString)
 #ccall git_oid_tostr , CString -> CSize -> Ptr <git_oid> -> IO (CString)
 #ccall git_oid_cpy , Ptr <git_oid> -> Ptr <git_oid> -> IO ()
-#ccall git_oid_cmp , Ptr <git_oid> -> Ptr <git_oid> -> IO (CInt)
-#ccall git_oid_ncmp , Ptr <git_oid> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#cinline git_oid_cmp , Ptr <git_oid> -> Ptr <git_oid> -> IO (CInt)
+#cinline git_oid_equal , Ptr <git_oid> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_oid_ncmp , Ptr <git_oid> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #ccall git_oid_streq , Ptr <git_oid> -> CString -> IO (CInt)
 #ccall git_oid_iszero , Ptr <git_oid> -> IO (CInt)
 {- typedef struct git_oid_shorten git_oid_shorten; -}
diff --git a/Bindings/Libgit2/Oid.hsc.helper.c b/Bindings/Libgit2/Oid.hsc.helper.c
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Oid.hsc.helper.c
@@ -0,0 +1,5 @@
+#include <bindings.cmacros.h>
+#include <git2.h>
+
+BC_INLINE2(git_oid_cmp, const git_oid*, const git_oid*, int)
+BC_INLINE2(git_oid_equal, const git_oid*, const git_oid*, int)
diff --git a/Bindings/Libgit2/Pack.hsc b/Bindings/Libgit2/Pack.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Pack.hsc
@@ -0,0 +1,24 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/pack.h>
+module Bindings.Libgit2.Pack where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Oid
+#ccall git_packbuilder_new , Ptr (Ptr <git_packbuilder>) -> Ptr <git_repository> -> IO (CInt)
+#ccall git_packbuilder_set_threads , Ptr <git_packbuilder> -> CUInt -> IO (CUInt)
+#ccall git_packbuilder_insert , Ptr <git_packbuilder> -> Ptr <git_oid> -> CString -> IO (CInt)
+#ccall git_packbuilder_insert_tree , Ptr <git_packbuilder> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_packbuilder_write , Ptr <git_packbuilder> -> CString -> IO (CInt)
+{- typedef int (* git_packbuilder_foreach_cb)(void * buf,
+                                           size_t size,
+                                           void * payload); -}
+#callback git_packbuilder_foreach_cb , Ptr () -> CSize -> Ptr () -> IO CInt
+#ccall git_packbuilder_foreach , Ptr <git_packbuilder> -> <git_packbuilder_foreach_cb> -> Ptr () -> IO (CInt)
+#ccall git_packbuilder_object_count , Ptr <git_packbuilder> -> IO (CUInt)
+#ccall git_packbuilder_written , Ptr <git_packbuilder> -> IO (CUInt)
+#ccall git_packbuilder_free , Ptr <git_packbuilder> -> IO ()
diff --git a/Bindings/Libgit2/Push.hsc b/Bindings/Libgit2/Push.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Push.hsc
@@ -0,0 +1,25 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/push.h>
+module Bindings.Libgit2.Push where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+{- typedef struct {
+            unsigned int version; unsigned int pb_parallelism;
+        } git_push_options; -}
+#starttype git_push_options
+#field version , CUInt
+#field pb_parallelism , CUInt
+#stoptype
+#ccall git_push_new , Ptr (Ptr <git_push>) -> Ptr <git_remote> -> IO (CInt)
+#ccall git_push_set_options , Ptr <git_push> -> Ptr <git_push_options> -> IO (CInt)
+#ccall git_push_add_refspec , Ptr <git_push> -> CString -> IO (CInt)
+#ccall git_push_update_tips , Ptr <git_push> -> IO (CInt)
+#ccall git_push_finish , Ptr <git_push> -> IO (CInt)
+#ccall git_push_unpack_ok , Ptr <git_push> -> IO (CInt)
+#ccall git_push_status_foreach , Ptr <git_push> -> FunPtr (CString -> CString -> Ptr () -> CInt) -> Ptr () -> IO (CInt)
+#ccall git_push_free , Ptr <git_push> -> IO ()
diff --git a/Bindings/Libgit2/Refdb.hsc b/Bindings/Libgit2/Refdb.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Refdb.hsc
@@ -0,0 +1,19 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/refdb.h>
+module Bindings.Libgit2.Refdb where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Refs
+import Bindings.Libgit2.RefdbBackend
+#ccall git_reference__alloc , Ptr <git_refdb> -> CString -> Ptr <git_oid> -> CString -> IO (Ptr <git_reference>)
+#ccall git_refdb_new , Ptr (Ptr <git_refdb>) -> Ptr <git_repository> -> IO (CInt)
+#ccall git_refdb_open , Ptr (Ptr <git_refdb>) -> Ptr <git_repository> -> IO (CInt)
+#ccall git_refdb_compress , Ptr <git_refdb> -> IO (CInt)
+#ccall git_refdb_free , Ptr <git_refdb> -> IO ()
+#ccall git_refdb_set_backend , Ptr <git_refdb> -> Ptr <git_refdb_backend> -> IO (CInt)
diff --git a/Bindings/Libgit2/RefdbBackend.hsc b/Bindings/Libgit2/RefdbBackend.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/RefdbBackend.hsc
@@ -0,0 +1,56 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/refdb_backend.h>
+module Bindings.Libgit2.RefdbBackend where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Refs
+import Bindings.Libgit2.Oid
+{- struct git_refdb_backend {
+    unsigned int version;
+    int (* exists)(int * exists,
+                   struct git_refdb_backend * backend,
+                   const char * ref_name);
+    int (* lookup)(git_reference * * out,
+                   struct git_refdb_backend * backend,
+                   const char * ref_name);
+    int (* foreach)(struct git_refdb_backend * backend,
+                    unsigned int list_flags,
+                    git_reference_foreach_cb callback,
+                    void * payload);
+    int (* foreach_glob)(struct git_refdb_backend * backend,
+                         const char * glob,
+                         unsigned int list_flags,
+                         git_reference_foreach_cb callback,
+                         void * payload);
+    int (* write)(struct git_refdb_backend * backend,
+                  const git_reference * ref);
+    int (* delete)(struct git_refdb_backend * backend,
+                   const git_reference * ref);
+    int (* compress)(struct git_refdb_backend * backend);
+    void (* free)(struct git_refdb_backend * backend);
+}; -}
+#callback git_refdb_backend_exists_callback , Ptr CInt -> Ptr <git_refdb_backend> -> CString -> IO CInt
+#callback git_refdb_backend_lookup_callback , Ptr (Ptr <git_reference>) -> Ptr <git_refdb_backend> -> CString -> IO CInt
+#callback git_refdb_backend_foreach_callback , Ptr <git_refdb_backend> -> CUInt -> <git_reference_foreach_cb> -> Ptr () -> IO CInt
+#callback git_refdb_backend_foreach_glob_callback , Ptr <git_refdb_backend> -> CString -> CUInt -> <git_reference_foreach_cb> -> Ptr () -> IO CInt
+#callback git_refdb_backend_write_callback , Ptr <git_refdb_backend> -> Ptr <git_reference> -> IO CInt
+#callback git_refdb_backend_delete_callback , Ptr <git_refdb_backend> -> Ptr <git_reference> -> IO CInt
+#callback git_refdb_backend_compress_callback , Ptr <git_refdb_backend> -> IO CInt
+#callback git_refdb_backend_free_callback , Ptr <git_refdb_backend> -> IO ()
+#starttype git_refdb_backend
+#field version , CUInt
+#field exists , <git_refdb_backend_exists_callback>
+#field lookup , <git_refdb_backend_lookup_callback>
+#field foreach , <git_refdb_backend_foreach_callback>
+#field foreach_glob , <git_refdb_backend_foreach_glob_callback>
+#field write , <git_refdb_backend_write_callback>
+#field delete , <git_refdb_backend_delete_callback>
+#field compress , <git_refdb_backend_compress_callback>
+#field free , <git_refdb_backend_free_callback>
+#stoptype
+#ccall git_refdb_backend_fs , Ptr (Ptr <git_refdb_backend>) -> Ptr <git_repository> -> Ptr <git_refdb> -> IO (CInt)
diff --git a/Bindings/Libgit2/Reflog.hsc b/Bindings/Libgit2/Reflog.hsc
--- a/Bindings/Libgit2/Reflog.hsc
+++ b/Bindings/Libgit2/Reflog.hsc
@@ -1,19 +1,23 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Reflog where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 #ccall git_reflog_read , Ptr (Ptr <git_reflog>) -> Ptr <git_reference> -> IO (CInt)
-#ccall git_reflog_write , Ptr <git_reference> -> Ptr <git_oid> -> Ptr <git_signature> -> CString -> IO (CInt)
+#ccall git_reflog_write , Ptr <git_reflog> -> IO (CInt)
+#ccall git_reflog_append , Ptr <git_reflog> -> Ptr <git_oid> -> Ptr <git_signature> -> CString -> IO (CInt)
 #ccall git_reflog_rename , Ptr <git_reference> -> CString -> IO (CInt)
 #ccall git_reflog_delete , Ptr <git_reference> -> IO (CInt)
-#ccall git_reflog_entrycount , Ptr <git_reflog> -> IO (CUInt)
-#ccall git_reflog_entry_byindex , Ptr <git_reflog> -> CUInt -> IO (Ptr <git_reflog_entry>)
-#ccall git_reflog_entry_oidold , Ptr <git_reflog_entry> -> IO (Ptr <git_oid>)
-#ccall git_reflog_entry_oidnew , Ptr <git_reflog_entry> -> IO (Ptr <git_oid>)
+#ccall git_reflog_entrycount , Ptr <git_reflog> -> IO (CSize)
+#ccall git_reflog_entry_byindex , Ptr <git_reflog> -> CSize -> IO (Ptr <git_reflog_entry>)
+#ccall git_reflog_drop , Ptr <git_reflog> -> CSize -> CInt -> IO (CInt)
+#ccall git_reflog_entry_id_old , Ptr <git_reflog_entry> -> IO (Ptr <git_oid>)
+#ccall git_reflog_entry_id_new , Ptr <git_reflog_entry> -> IO (Ptr <git_oid>)
 #ccall git_reflog_entry_committer , Ptr <git_reflog_entry> -> IO (Ptr <git_signature>)
-#ccall git_reflog_entry_msg , Ptr <git_reflog_entry> -> IO (CString)
+#ccall git_reflog_entry_message , Ptr <git_reflog_entry> -> IO (CString)
 #ccall git_reflog_free , Ptr <git_reflog> -> IO ()
diff --git a/Bindings/Libgit2/Refs.hsc b/Bindings/Libgit2/Refs.hsc
--- a/Bindings/Libgit2/Refs.hsc
+++ b/Bindings/Libgit2/Refs.hsc
@@ -1,30 +1,48 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Refs where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Strarray
 #ccall git_reference_lookup , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> IO (CInt)
-#ccall git_reference_name_to_oid , Ptr <git_oid> -> Ptr <git_repository> -> CString -> IO (CInt)
-#ccall git_reference_create_symbolic , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> CString -> CInt -> IO (CInt)
-#ccall git_reference_create_oid , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> Ptr <git_oid> -> CInt -> IO (CInt)
-#ccall git_reference_oid , Ptr <git_reference> -> IO (Ptr <git_oid>)
-#ccall git_reference_target , Ptr <git_reference> -> IO (CString)
+#ccall git_reference_name_to_id , Ptr <git_oid> -> Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_reference_symbolic_create , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> CString -> CInt -> IO (CInt)
+#ccall git_reference_create , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> CString -> Ptr <git_oid> -> CInt -> IO (CInt)
+#ccall git_reference_target , Ptr <git_reference> -> IO (Ptr <git_oid>)
+#ccall git_reference_symbolic_target , Ptr <git_reference> -> IO (CString)
 #ccall git_reference_type , Ptr <git_reference> -> IO (<git_ref_t>)
 #ccall git_reference_name , Ptr <git_reference> -> IO (CString)
 #ccall git_reference_resolve , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> IO (CInt)
 #ccall git_reference_owner , Ptr <git_reference> -> IO (Ptr <git_repository>)
-#ccall git_reference_set_target , Ptr <git_reference> -> CString -> IO (CInt)
-#ccall git_reference_set_oid , Ptr <git_reference> -> Ptr <git_oid> -> IO (CInt)
-#ccall git_reference_rename , Ptr <git_reference> -> CString -> CInt -> IO (CInt)
+#ccall git_reference_symbolic_set_target , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> CString -> IO (CInt)
+#ccall git_reference_set_target , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_reference_rename , Ptr (Ptr <git_reference>) -> Ptr <git_reference> -> CString -> CInt -> IO (CInt)
 #ccall git_reference_delete , Ptr <git_reference> -> IO (CInt)
-#ccall git_reference_packall , Ptr <git_repository> -> IO (CInt)
 #ccall git_reference_list , Ptr <git_strarray> -> Ptr <git_repository> -> CUInt -> IO (CInt)
-#callback git_reference_foreach_callback , CString -> Ptr () -> IO CInt
-#ccall git_reference_foreach , Ptr <git_repository> -> CUInt -> <git_reference_foreach_callback> -> Ptr () -> IO (CInt)
-#ccall git_reference_is_packed , Ptr <git_reference> -> IO (CInt)
-#ccall git_reference_reload , Ptr <git_reference> -> IO (CInt)
+{- typedef int (* git_reference_foreach_cb)(const char * refname,
+                                         void * payload); -}
+#callback git_reference_foreach_cb , CString -> Ptr () -> Ptr CInt
+#ccall git_reference_foreach , Ptr <git_repository> -> CUInt -> <git_reference_foreach_cb> -> Ptr () -> IO (CInt)
 #ccall git_reference_free , Ptr <git_reference> -> IO ()
 #ccall git_reference_cmp , Ptr <git_reference> -> Ptr <git_reference> -> IO (CInt)
+#ccall git_reference_foreach_glob , Ptr <git_repository> -> CString -> CUInt -> <git_reference_foreach_cb> -> Ptr () -> IO (CInt)
+#ccall git_reference_has_log , Ptr <git_reference> -> IO (CInt)
+#ccall git_reference_is_branch , Ptr <git_reference> -> IO (CInt)
+#ccall git_reference_is_remote , Ptr <git_reference> -> IO (CInt)
+{- typedef enum {
+            GIT_REF_FORMAT_NORMAL = 0,
+            GIT_REF_FORMAT_ALLOW_ONELEVEL = 1 << 0,
+            GIT_REF_FORMAT_REFSPEC_PATTERN = 1 << 1
+        } git_reference_normalize_t; -}
+#integral_t git_reference_normalize_t
+#num GIT_REF_FORMAT_NORMAL
+#num GIT_REF_FORMAT_ALLOW_ONELEVEL
+#num GIT_REF_FORMAT_REFSPEC_PATTERN
+#ccall git_reference_normalize_name , CString -> CSize -> CString -> CUInt -> IO (CInt)
+#ccall git_reference_peel , Ptr (Ptr <git_object>) -> Ptr <git_reference> -> <git_otype> -> IO (CInt)
+#ccall git_reference_is_valid_name , CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Refspec.hsc b/Bindings/Libgit2/Refspec.hsc
--- a/Bindings/Libgit2/Refspec.hsc
+++ b/Bindings/Libgit2/Refspec.hsc
@@ -1,11 +1,16 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Refspec where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 #ccall git_refspec_src , Ptr <git_refspec> -> IO (CString)
 #ccall git_refspec_dst , Ptr <git_refspec> -> IO (CString)
+#ccall git_refspec_force , Ptr <git_refspec> -> IO (CInt)
 #ccall git_refspec_src_matches , Ptr <git_refspec> -> CString -> IO (CInt)
+#ccall git_refspec_dst_matches , Ptr <git_refspec> -> CString -> IO (CInt)
 #ccall git_refspec_transform , CString -> CSize -> Ptr <git_refspec> -> CString -> IO (CInt)
+#ccall git_refspec_rtransform , CString -> CSize -> Ptr <git_refspec> -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Remote.hsc b/Bindings/Libgit2/Remote.hsc
--- a/Bindings/Libgit2/Remote.hsc
+++ b/Bindings/Libgit2/Remote.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Remote where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -8,26 +10,85 @@
 import Bindings.Libgit2.Refspec
 import Bindings.Libgit2.Net
 import Bindings.Libgit2.Indexer
-import Bindings.Libgit2.Oid
+import Bindings.Libgit2.Strarray
+import Bindings.Libgit2.Transport
 import Bindings.Libgit2.Types
-#ccall git_remote_new , Ptr (Ptr <git_remote>) -> Ptr <git_repository> -> CString -> CString -> CString -> IO (CInt)
+import Bindings.Libgit2.Oid
+{- typedef int (* git_remote_rename_problem_cb)(const char * problematic_refspec,
+                                             void * payload); -}
+#callback git_remote_rename_problem_cb , CString -> Ptr () -> Ptr CInt
+#ccall git_remote_create , Ptr (Ptr <git_remote>) -> Ptr <git_repository> -> CString -> CString -> IO (CInt)
+#ccall git_remote_create_inmemory , Ptr (Ptr <git_remote>) -> Ptr <git_repository> -> CString -> CString -> IO (CInt)
 #ccall git_remote_load , Ptr (Ptr <git_remote>) -> Ptr <git_repository> -> CString -> IO (CInt)
 #ccall git_remote_save , Ptr <git_remote> -> IO (CInt)
 #ccall git_remote_name , Ptr <git_remote> -> IO (CString)
 #ccall git_remote_url , Ptr <git_remote> -> IO (CString)
+#ccall git_remote_pushurl , Ptr <git_remote> -> IO (CString)
+#ccall git_remote_set_url , Ptr <git_remote> -> CString -> IO (CInt)
+#ccall git_remote_set_pushurl , Ptr <git_remote> -> CString -> IO (CInt)
 #ccall git_remote_set_fetchspec , Ptr <git_remote> -> CString -> IO (CInt)
 #ccall git_remote_fetchspec , Ptr <git_remote> -> IO (Ptr <git_refspec>)
 #ccall git_remote_set_pushspec , Ptr <git_remote> -> CString -> IO (CInt)
 #ccall git_remote_pushspec , Ptr <git_remote> -> IO (Ptr <git_refspec>)
-#ccall git_remote_connect , Ptr <git_remote> -> CInt -> IO (CInt)
-#ccall git_remote_ls , Ptr <git_remote> -> CInt -> Ptr () -> IO (CInt)
-#ccall git_remote_download , Ptr <git_remote> -> Ptr CLong -> Ptr <git_indexer_stats> -> IO (CInt)
+#ccall git_remote_connect , Ptr <git_remote> -> <git_direction> -> IO (CInt)
+#ccall git_remote_ls , Ptr <git_remote> -> <git_headlist_cb> -> Ptr () -> IO (CInt)
+#ccall git_remote_download , Ptr <git_remote> -> <git_transfer_progress_callback> -> Ptr () -> IO (CInt)
 #ccall git_remote_connected , Ptr <git_remote> -> IO (CInt)
+#ccall git_remote_stop , Ptr <git_remote> -> IO ()
 #ccall git_remote_disconnect , Ptr <git_remote> -> IO ()
 #ccall git_remote_free , Ptr <git_remote> -> IO ()
-#callback git_remote_update_tips_callback , CString -> Ptr <git_oid> -> Ptr <git_oid> -> IO CInt
-#ccall git_remote_update_tips , Ptr <git_remote> -> <git_remote_update_tips_callback> -> IO (CInt)
+#ccall git_remote_update_tips , Ptr <git_remote> -> IO (CInt)
 #ccall git_remote_valid_url , CString -> IO (CInt)
 #ccall git_remote_supported_url , CString -> IO (CInt)
 #ccall git_remote_list , Ptr <git_strarray> -> Ptr <git_repository> -> IO (CInt)
-#ccall git_remote_add , Ptr (Ptr <git_remote>) -> Ptr <git_repository> -> CString -> CString -> IO (CInt)
+#ccall git_remote_check_cert , Ptr <git_remote> -> CInt -> IO ()
+#ccall git_remote_set_cred_acquire_cb , Ptr <git_remote> -> <git_cred_acquire_cb> -> Ptr () -> IO ()
+#ccall git_remote_set_transport , Ptr <git_remote> -> Ptr <git_transport> -> IO (CInt)
+{- typedef enum git_remote_completion_type {
+            GIT_REMOTE_COMPLETION_DOWNLOAD,
+            GIT_REMOTE_COMPLETION_INDEXING,
+            GIT_REMOTE_COMPLETION_ERROR
+        } git_remote_completion_type; -}
+#integral_t git_remote_completion_type
+#num GIT_REMOTE_COMPLETION_DOWNLOAD
+#num GIT_REMOTE_COMPLETION_INDEXING
+#num GIT_REMOTE_COMPLETION_ERROR
+{- struct git_remote_callbacks {
+    unsigned int version;
+    void (* progress)(const char * str, int len, void * data);
+    int (* completion)(git_remote_completion_type type, void * data);
+    int (* update_tips)(const char * refname,
+                        const git_oid * a,
+                        const git_oid * b,
+                        void * data);
+    void * payload;
+}; -}
+#callback git_remote_callbacks_progress_callback , CString -> CInt -> Ptr () -> IO ()
+#callback git_remote_callbacks_completion_callback , <git_remote_completion_type> -> Ptr () -> IO CInt
+#callback git_remote_callbacks_update_tips_callback , CString -> Ptr <git_oid> -> Ptr <git_oid> -> Ptr () -> IO CInt
+#starttype git_remote_callbacks
+#field version , CUInt
+#field progress , <git_remote_callbacks_progress_callback>
+#field completion , <git_remote_callbacks_completion_callback>
+#field update_tips , <git_remote_callbacks_update_tips_callback>
+#field payload , Ptr ()
+#stoptype
+#ccall git_remote_set_callbacks , Ptr <git_remote> -> Ptr <git_remote_callbacks> -> IO (CInt)
+#ccall git_remote_stats , Ptr <git_remote> -> IO (Ptr <git_transfer_progress>)
+{- typedef enum {
+            GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
+            GIT_REMOTE_DOWNLOAD_TAGS_NONE,
+            GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
+            GIT_REMOTE_DOWNLOAD_TAGS_ALL
+        } git_remote_autotag_option_t; -}
+#integral_t git_remote_autotag_option_t
+#num GIT_REMOTE_DOWNLOAD_TAGS_UNSET
+#num GIT_REMOTE_DOWNLOAD_TAGS_NONE
+#num GIT_REMOTE_DOWNLOAD_TAGS_AUTO
+#num GIT_REMOTE_DOWNLOAD_TAGS_ALL
+#ccall git_remote_autotag , Ptr <git_remote> -> IO (<git_remote_autotag_option_t>)
+#ccall git_remote_set_autotag , Ptr <git_remote> -> <git_remote_autotag_option_t> -> IO ()
+#ccall git_remote_rename , Ptr <git_remote> -> CString -> <git_remote_rename_problem_cb> -> Ptr () -> IO (CInt)
+#ccall git_remote_update_fetchhead , Ptr <git_remote> -> IO (CInt)
+#ccall git_remote_set_update_fetchhead , Ptr <git_remote> -> CInt -> IO ()
+#ccall git_remote_is_valid_name , CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Repository.hsc b/Bindings/Libgit2/Repository.hsc
--- a/Bindings/Libgit2/Repository.hsc
+++ b/Bindings/Libgit2/Repository.hsc
@@ -1,34 +1,127 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Repository where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 #ccall git_repository_open , Ptr (Ptr <git_repository>) -> CString -> IO (CInt)
+#ccall git_repository_wrap_odb , Ptr (Ptr <git_repository>) -> Ptr <git_odb> -> IO (CInt)
 #ccall git_repository_discover , CString -> CSize -> CString -> CInt -> CString -> IO (CInt)
-{- enum {
-    GIT_REPOSITORY_OPEN_NO_SEARCH = 1 << 0,
-    GIT_REPOSITORY_OPEN_CROSS_FS = 1 << 1
-}; -}
+{- typedef enum {
+            GIT_REPOSITORY_OPEN_NO_SEARCH = 1 << 0,
+            GIT_REPOSITORY_OPEN_CROSS_FS = 1 << 1
+        } git_repository_open_flag_t; -}
+#integral_t git_repository_open_flag_t
 #num GIT_REPOSITORY_OPEN_NO_SEARCH
 #num GIT_REPOSITORY_OPEN_CROSS_FS
 #ccall git_repository_open_ext , Ptr (Ptr <git_repository>) -> CString -> CUInt -> CString -> IO (CInt)
 #ccall git_repository_new , Ptr (Ptr <git_repository>) -> IO (CInt)
 #ccall git_repository_free , Ptr <git_repository> -> IO ()
 #ccall git_repository_init , Ptr (Ptr <git_repository>) -> CString -> CUInt -> IO (CInt)
+{- typedef enum {
+            GIT_REPOSITORY_INIT_BARE = 1u << 0,
+            GIT_REPOSITORY_INIT_NO_REINIT = 1u << 1,
+            GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = 1u << 2,
+            GIT_REPOSITORY_INIT_MKDIR = 1u << 3,
+            GIT_REPOSITORY_INIT_MKPATH = 1u << 4,
+            GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = 1u << 5
+        } git_repository_init_flag_t; -}
+#integral_t git_repository_init_flag_t
+#num GIT_REPOSITORY_INIT_BARE
+#num GIT_REPOSITORY_INIT_NO_REINIT
+#num GIT_REPOSITORY_INIT_NO_DOTGIT_DIR
+#num GIT_REPOSITORY_INIT_MKDIR
+#num GIT_REPOSITORY_INIT_MKPATH
+#num GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE
+{- typedef enum {
+            GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
+            GIT_REPOSITORY_INIT_SHARED_GROUP = 02775,
+            GIT_REPOSITORY_INIT_SHARED_ALL = 02777
+        } git_repository_init_mode_t; -}
+#integral_t git_repository_init_mode_t
+#num GIT_REPOSITORY_INIT_SHARED_UMASK
+#num GIT_REPOSITORY_INIT_SHARED_GROUP
+#num GIT_REPOSITORY_INIT_SHARED_ALL
+{- typedef struct {
+            unsigned int version;
+            uint32_t flags;
+            uint32_t mode;
+            const char * workdir_path;
+            const char * description;
+            const char * template_path;
+            const char * initial_head;
+            const char * origin_url;
+        } git_repository_init_options; -}
+#starttype git_repository_init_options
+#field version , CUInt
+#field flags , CUInt
+#field mode , CUInt
+#field workdir_path , CString
+#field description , CString
+#field template_path , CString
+#field initial_head , CString
+#field origin_url , CString
+#stoptype
+#ccall git_repository_init_ext , Ptr (Ptr <git_repository>) -> CString -> Ptr <git_repository_init_options> -> IO (CInt)
 #ccall git_repository_head , Ptr (Ptr <git_reference>) -> Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_head_detached , Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_head_orphan , Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_is_empty , Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_path , Ptr <git_repository> -> IO (CString)
 #ccall git_repository_workdir , Ptr <git_repository> -> IO (CString)
-#ccall git_repository_set_workdir , Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_repository_set_workdir , Ptr <git_repository> -> CString -> CInt -> IO (CInt)
 #ccall git_repository_is_bare , Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_config , Ptr (Ptr <git_config>) -> Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_set_config , Ptr <git_repository> -> Ptr <git_config> -> IO ()
 #ccall git_repository_odb , Ptr (Ptr <git_odb>) -> Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_set_odb , Ptr <git_repository> -> Ptr <git_odb> -> IO ()
+#ccall git_repository_refdb , Ptr (Ptr <git_refdb>) -> Ptr <git_repository> -> IO (CInt)
+#ccall git_repository_set_refdb , Ptr <git_repository> -> Ptr <git_refdb> -> IO ()
 #ccall git_repository_index , Ptr (Ptr <git_index>) -> Ptr <git_repository> -> IO (CInt)
 #ccall git_repository_set_index , Ptr <git_repository> -> Ptr <git_index> -> IO ()
+#ccall git_repository_message , CString -> CSize -> Ptr <git_repository> -> IO (CInt)
+#ccall git_repository_message_remove , Ptr <git_repository> -> IO (CInt)
+#ccall git_repository_merge_cleanup , Ptr <git_repository> -> IO (CInt)
+{- typedef int (* git_repository_fetchhead_foreach_cb)(const char * ref_name,
+                                                    const char * remote_url,
+                                                    const git_oid * oid,
+                                                    unsigned int is_merge,
+                                                    void * payload); -}
+#callback git_repository_fetchhead_foreach_cb , CString -> CString -> Ptr (<git_oid>) -> CUInt -> Ptr () -> IO CInt
+#ccall git_repository_fetchhead_foreach , Ptr <git_repository> -> <git_repository_fetchhead_foreach_cb> -> Ptr () -> IO (CInt)
+{- typedef int (* git_repository_mergehead_foreach_cb)(const git_oid * oid,
+                                                    void * payload); -}
+#callback git_repository_mergehead_foreach_cb , Ptr (<git_oid>) -> Ptr () -> IO CInt
+#ccall git_repository_mergehead_foreach , Ptr <git_repository> -> <git_repository_mergehead_foreach_cb> -> Ptr () -> IO (CInt)
+#ccall git_repository_hashfile , Ptr <git_oid> -> Ptr <git_repository> -> CString -> <git_otype> -> CString -> IO (CInt)
+#ccall git_repository_set_head , Ptr <git_repository> -> CString -> IO (CInt)
+#ccall git_repository_set_head_detached , Ptr <git_repository> -> Ptr <git_oid> -> IO (CInt)
+#ccall git_repository_detach_head , Ptr <git_repository> -> IO (CInt)
+{- typedef enum {
+            GIT_REPOSITORY_STATE_NONE,
+            GIT_REPOSITORY_STATE_MERGE,
+            GIT_REPOSITORY_STATE_REVERT,
+            GIT_REPOSITORY_STATE_CHERRY_PICK,
+            GIT_REPOSITORY_STATE_BISECT,
+            GIT_REPOSITORY_STATE_REBASE,
+            GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
+            GIT_REPOSITORY_STATE_REBASE_MERGE,
+            GIT_REPOSITORY_STATE_APPLY_MAILBOX,
+            GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE
+        } git_repository_state_t; -}
+#integral_t git_repository_state_t
+#num GIT_REPOSITORY_STATE_NONE
+#num GIT_REPOSITORY_STATE_MERGE
+#num GIT_REPOSITORY_STATE_REVERT
+#num GIT_REPOSITORY_STATE_CHERRY_PICK
+#num GIT_REPOSITORY_STATE_BISECT
+#num GIT_REPOSITORY_STATE_REBASE
+#num GIT_REPOSITORY_STATE_REBASE_INTERACTIVE
+#num GIT_REPOSITORY_STATE_REBASE_MERGE
+#num GIT_REPOSITORY_STATE_APPLY_MAILBOX
+#num GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE
+#ccall git_repository_state , Ptr <git_repository> -> IO (CInt)
diff --git a/Bindings/Libgit2/Reset.hsc b/Bindings/Libgit2/Reset.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Reset.hsc
@@ -0,0 +1,20 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/reset.h>
+module Bindings.Libgit2.Reset where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Strarray
+{- typedef enum {
+            GIT_RESET_SOFT = 1, GIT_RESET_MIXED = 2, GIT_RESET_HARD = 3
+        } git_reset_t; -}
+#integral_t git_reset_t
+#num GIT_RESET_SOFT
+#num GIT_RESET_MIXED
+#num GIT_RESET_HARD
+#ccall git_reset , Ptr <git_repository> -> Ptr <git_object> -> <git_reset_t> -> IO (CInt)
+#ccall git_reset_default , Ptr <git_repository> -> Ptr <git_object> -> Ptr <git_strarray> -> IO (CInt)
diff --git a/Bindings/Libgit2/Revparse.hsc b/Bindings/Libgit2/Revparse.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Revparse.hsc
@@ -0,0 +1,29 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/revparse.h>
+module Bindings.Libgit2.Revparse where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+#ccall git_revparse_single , Ptr (Ptr <git_object>) -> Ptr <git_repository> -> CString -> IO (CInt)
+{- typedef enum {
+            GIT_REVPARSE_SINGLE = 1 << 0,
+            GIT_REVPARSE_RANGE = 1 << 1,
+            GIT_REVPARSE_MERGE_BASE = 1 << 2
+        } git_revparse_mode_t; -}
+#integral_t git_revparse_mode_t
+#num GIT_REVPARSE_SINGLE
+#num GIT_REVPARSE_RANGE
+#num GIT_REVPARSE_MERGE_BASE
+{- typedef struct {
+            git_object * from; git_object * to; unsigned int flags;
+        } git_revspec; -}
+#starttype git_revspec
+#field from , Ptr <git_object>
+#field to , Ptr <git_object>
+#field flags , CUInt
+#stoptype
+#ccall git_revparse , Ptr <git_revspec> -> Ptr <git_repository> -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Revwalk.hsc b/Bindings/Libgit2/Revwalk.hsc
--- a/Bindings/Libgit2/Revwalk.hsc
+++ b/Bindings/Libgit2/Revwalk.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Revwalk where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -18,5 +20,6 @@
 #ccall git_revwalk_hide_ref , Ptr <git_revwalk> -> CString -> IO (CInt)
 #ccall git_revwalk_next , Ptr <git_oid> -> Ptr <git_revwalk> -> IO (CInt)
 #ccall git_revwalk_sorting , Ptr <git_revwalk> -> CUInt -> IO ()
+#ccall git_revwalk_push_range , Ptr <git_revwalk> -> CString -> IO (CInt)
 #ccall git_revwalk_free , Ptr <git_revwalk> -> IO ()
 #ccall git_revwalk_repository , Ptr <git_revwalk> -> IO (Ptr <git_repository>)
diff --git a/Bindings/Libgit2/Signature.hsc b/Bindings/Libgit2/Signature.hsc
--- a/Bindings/Libgit2/Signature.hsc
+++ b/Bindings/Libgit2/Signature.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Signature where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
diff --git a/Bindings/Libgit2/Stash.hsc b/Bindings/Libgit2/Stash.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Stash.hsc
@@ -0,0 +1,30 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/stash.h>
+module Bindings.Libgit2.Stash where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+import Bindings.Libgit2.Oid
+{- typedef enum {
+            GIT_STASH_DEFAULT = 0,
+            GIT_STASH_KEEP_INDEX = 1 << 0,
+            GIT_STASH_INCLUDE_UNTRACKED = 1 << 1,
+            GIT_STASH_INCLUDE_IGNORED = 1 << 2
+        } git_stash_flags; -}
+#integral_t git_stash_flags
+#num GIT_STASH_DEFAULT
+#num GIT_STASH_KEEP_INDEX
+#num GIT_STASH_INCLUDE_UNTRACKED
+#num GIT_STASH_INCLUDE_IGNORED
+#ccall git_stash_save , Ptr <git_oid> -> Ptr <git_repository> -> Ptr <git_signature> -> CString -> CUInt -> IO (CInt)
+{- typedef int (* git_stash_cb)(size_t index,
+                             const char * message,
+                             const git_oid * stash_id,
+                             void * payload); -}
+#callback git_stash_cb , CSize -> CString -> Ptr (<git_oid>) -> Ptr () -> IO CInt
+#ccall git_stash_foreach , Ptr <git_repository> -> <git_stash_cb> -> Ptr () -> IO (CInt)
+#ccall git_stash_drop , Ptr <git_repository> -> CSize -> IO (CInt)
diff --git a/Bindings/Libgit2/Status.hsc b/Bindings/Libgit2/Status.hsc
--- a/Bindings/Libgit2/Status.hsc
+++ b/Bindings/Libgit2/Status.hsc
@@ -1,30 +1,43 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Status where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
-{- enum {
-    GIT_STATUS_CURRENT = 0,
-    GIT_STATUS_INDEX_NEW = 1 << 0,
-    GIT_STATUS_INDEX_MODIFIED = 1 << 1,
-    GIT_STATUS_INDEX_DELETED = 1 << 2,
-    GIT_STATUS_WT_NEW = 1 << 3,
-    GIT_STATUS_WT_MODIFIED = 1 << 4,
-    GIT_STATUS_WT_DELETED = 1 << 5,
-    GIT_STATUS_IGNORED = 1 << 6
-}; -}
+import Bindings.Libgit2.Strarray
+{- typedef enum {
+            GIT_STATUS_CURRENT = 0,
+            GIT_STATUS_INDEX_NEW = 1u << 0,
+            GIT_STATUS_INDEX_MODIFIED = 1u << 1,
+            GIT_STATUS_INDEX_DELETED = 1u << 2,
+            GIT_STATUS_INDEX_RENAMED = 1u << 3,
+            GIT_STATUS_INDEX_TYPECHANGE = 1u << 4,
+            GIT_STATUS_WT_NEW = 1u << 7,
+            GIT_STATUS_WT_MODIFIED = 1u << 8,
+            GIT_STATUS_WT_DELETED = 1u << 9,
+            GIT_STATUS_WT_TYPECHANGE = 1u << 10,
+            GIT_STATUS_IGNORED = 1u << 14
+        } git_status_t; -}
+#integral_t git_status_t
 #num GIT_STATUS_CURRENT
 #num GIT_STATUS_INDEX_NEW
 #num GIT_STATUS_INDEX_MODIFIED
 #num GIT_STATUS_INDEX_DELETED
+#num GIT_STATUS_INDEX_RENAMED
+#num GIT_STATUS_INDEX_TYPECHANGE
 #num GIT_STATUS_WT_NEW
 #num GIT_STATUS_WT_MODIFIED
 #num GIT_STATUS_WT_DELETED
+#num GIT_STATUS_WT_TYPECHANGE
 #num GIT_STATUS_IGNORED
-#callback git_status_foreach_callback , CString -> CUInt -> Ptr () -> IO CInt
-#ccall git_status_foreach , Ptr <git_repository> -> <git_status_foreach_callback> -> Ptr () -> IO (CInt)
+{- typedef int (* git_status_cb)(const char * path,
+                              unsigned int status_flags,
+                              void * payload); -}
+#callback git_status_cb , CString -> CUInt -> Ptr () -> IO CInt
+#ccall git_status_foreach , Ptr <git_repository> -> <git_status_cb> -> Ptr () -> IO (CInt)
 {- typedef enum {
             GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
             GIT_STATUS_SHOW_INDEX_ONLY = 1,
@@ -36,27 +49,35 @@
 #num GIT_STATUS_SHOW_INDEX_ONLY
 #num GIT_STATUS_SHOW_WORKDIR_ONLY
 #num GIT_STATUS_SHOW_INDEX_THEN_WORKDIR
-{- enum {
-    GIT_STATUS_OPT_INCLUDE_UNTRACKED = 1 << 0,
-    GIT_STATUS_OPT_INCLUDE_IGNORED = 1 << 1,
-    GIT_STATUS_OPT_INCLUDE_UNMODIFIED = 1 << 2,
-    GIT_STATUS_OPT_EXCLUDE_SUBMODULED = 1 << 3,
-    GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = 1 << 4
-}; -}
+{- typedef enum {
+            GIT_STATUS_OPT_INCLUDE_UNTRACKED = 1u << 0,
+            GIT_STATUS_OPT_INCLUDE_IGNORED = 1u << 1,
+            GIT_STATUS_OPT_INCLUDE_UNMODIFIED = 1u << 2,
+            GIT_STATUS_OPT_EXCLUDE_SUBMODULES = 1u << 3,
+            GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = 1u << 4,
+            GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = 1u << 5,
+            GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = 1u << 6
+        } git_status_opt_t; -}
+#integral_t git_status_opt_t
 #num GIT_STATUS_OPT_INCLUDE_UNTRACKED
 #num GIT_STATUS_OPT_INCLUDE_IGNORED
 #num GIT_STATUS_OPT_INCLUDE_UNMODIFIED
-#num GIT_STATUS_OPT_EXCLUDE_SUBMODULED
+#num GIT_STATUS_OPT_EXCLUDE_SUBMODULES
 #num GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS
+#num GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH
+#num GIT_STATUS_OPT_RECURSE_IGNORED_DIRS
 {- typedef struct {
-            git_status_show_t show; unsigned int flags; git_strarray pathspec;
+            unsigned int version;
+            git_status_show_t show;
+            unsigned int flags;
+            git_strarray pathspec;
         } git_status_options; -}
 #starttype git_status_options
+#field version , CUInt
 #field show , <git_status_show_t>
 #field flags , CUInt
 #field pathspec , <git_strarray>
 #stoptype
-#callback git_status_foreach_ext_callback , CString -> CUInt -> Ptr () -> IO CInt
-#ccall git_status_foreach_ext , Ptr <git_repository> -> Ptr <git_status_options> -> <git_status_foreach_ext_callback> -> Ptr () -> IO (CInt)
+#ccall git_status_foreach_ext , Ptr <git_repository> -> Ptr <git_status_options> -> <git_status_cb> -> Ptr () -> IO (CInt)
 #ccall git_status_file , Ptr CUInt -> Ptr <git_repository> -> CString -> IO (CInt)
 #ccall git_status_should_ignore , Ptr CInt -> Ptr <git_repository> -> CString -> IO (CInt)
diff --git a/Bindings/Libgit2/Strarray.hsc b/Bindings/Libgit2/Strarray.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Strarray.hsc
@@ -0,0 +1,18 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/strarray.h>
+module Bindings.Libgit2.Strarray where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+{- typedef struct git_strarray {
+            char * * strings; size_t count;
+        } git_strarray; -}
+#starttype git_strarray
+#field strings , Ptr (CString)
+#field count , CSize
+#stoptype
+#ccall git_strarray_free , Ptr <git_strarray> -> IO ()
+#ccall git_strarray_copy , Ptr <git_strarray> -> Ptr <git_strarray> -> IO (CInt)
diff --git a/Bindings/Libgit2/Submodule.hsc b/Bindings/Libgit2/Submodule.hsc
--- a/Bindings/Libgit2/Submodule.hsc
+++ b/Bindings/Libgit2/Submodule.hsc
@@ -1,51 +1,97 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Submodule where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
+{- typedef struct git_submodule git_submodule; -}
+#opaque_t git_submodule
 {- typedef enum {
+            GIT_SUBMODULE_UPDATE_DEFAULT = -1,
             GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
             GIT_SUBMODULE_UPDATE_REBASE = 1,
-            GIT_SUBMODULE_UPDATE_MERGE = 2
+            GIT_SUBMODULE_UPDATE_MERGE = 2,
+            GIT_SUBMODULE_UPDATE_NONE = 3
         } git_submodule_update_t; -}
 #integral_t git_submodule_update_t
+#num GIT_SUBMODULE_UPDATE_DEFAULT
 #num GIT_SUBMODULE_UPDATE_CHECKOUT
 #num GIT_SUBMODULE_UPDATE_REBASE
 #num GIT_SUBMODULE_UPDATE_MERGE
+#num GIT_SUBMODULE_UPDATE_NONE
 {- typedef enum {
-            GIT_SUBMODULE_IGNORE_ALL = 0,
-            GIT_SUBMODULE_IGNORE_DIRTY = 1,
-            GIT_SUBMODULE_IGNORE_UNTRACKED = 2,
-            GIT_SUBMODULE_IGNORE_NONE = 3
+            GIT_SUBMODULE_IGNORE_DEFAULT = -1,
+            GIT_SUBMODULE_IGNORE_NONE = 0,
+            GIT_SUBMODULE_IGNORE_UNTRACKED = 1,
+            GIT_SUBMODULE_IGNORE_DIRTY = 2,
+            GIT_SUBMODULE_IGNORE_ALL = 3
         } git_submodule_ignore_t; -}
 #integral_t git_submodule_ignore_t
-#num GIT_SUBMODULE_IGNORE_ALL
-#num GIT_SUBMODULE_IGNORE_DIRTY
-#num GIT_SUBMODULE_IGNORE_UNTRACKED
+#num GIT_SUBMODULE_IGNORE_DEFAULT
 #num GIT_SUBMODULE_IGNORE_NONE
-{- typedef struct {
-            char * name;
-            char * path;
-            char * url;
-            git_oid oid;
-            git_submodule_update_t update;
-            git_submodule_ignore_t ignore;
-            int fetch_recurse;
-            int refcount;
-        } git_submodule; -}
-#starttype git_submodule
-#field name , CString
-#field path , CString
-#field url , CString
-#field oid , <git_oid>
-#field update , <git_submodule_update_t>
-#field ignore , <git_submodule_ignore_t>
-#field fetch_recurse , CInt
-#field refcount , CInt
-#stoptype
-#callback git_submodule_foreach_callback , CString -> Ptr () -> IO CInt
-#ccall git_submodule_foreach , Ptr <git_repository> -> <git_submodule_foreach_callback> -> Ptr () -> IO (CInt)
+#num GIT_SUBMODULE_IGNORE_UNTRACKED
+#num GIT_SUBMODULE_IGNORE_DIRTY
+#num GIT_SUBMODULE_IGNORE_ALL
+{- typedef enum {
+            GIT_SUBMODULE_STATUS_IN_HEAD = 1u << 0,
+            GIT_SUBMODULE_STATUS_IN_INDEX = 1u << 1,
+            GIT_SUBMODULE_STATUS_IN_CONFIG = 1u << 2,
+            GIT_SUBMODULE_STATUS_IN_WD = 1u << 3,
+            GIT_SUBMODULE_STATUS_INDEX_ADDED = 1u << 4,
+            GIT_SUBMODULE_STATUS_INDEX_DELETED = 1u << 5,
+            GIT_SUBMODULE_STATUS_INDEX_MODIFIED = 1u << 6,
+            GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = 1u << 7,
+            GIT_SUBMODULE_STATUS_WD_ADDED = 1u << 8,
+            GIT_SUBMODULE_STATUS_WD_DELETED = 1u << 9,
+            GIT_SUBMODULE_STATUS_WD_MODIFIED = 1u << 10,
+            GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = 1u << 11,
+            GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = 1u << 12,
+            GIT_SUBMODULE_STATUS_WD_UNTRACKED = 1u << 13
+        } git_submodule_status_t; -}
+#integral_t git_submodule_status_t
+#num GIT_SUBMODULE_STATUS_IN_HEAD
+#num GIT_SUBMODULE_STATUS_IN_INDEX
+#num GIT_SUBMODULE_STATUS_IN_CONFIG
+#num GIT_SUBMODULE_STATUS_IN_WD
+#num GIT_SUBMODULE_STATUS_INDEX_ADDED
+#num GIT_SUBMODULE_STATUS_INDEX_DELETED
+#num GIT_SUBMODULE_STATUS_INDEX_MODIFIED
+#num GIT_SUBMODULE_STATUS_WD_UNINITIALIZED
+#num GIT_SUBMODULE_STATUS_WD_ADDED
+#num GIT_SUBMODULE_STATUS_WD_DELETED
+#num GIT_SUBMODULE_STATUS_WD_MODIFIED
+#num GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED
+#num GIT_SUBMODULE_STATUS_WD_WD_MODIFIED
+#num GIT_SUBMODULE_STATUS_WD_UNTRACKED
 #ccall git_submodule_lookup , Ptr (Ptr <git_submodule>) -> Ptr <git_repository> -> CString -> IO (CInt)
+#callback git_submodule_foreach_callback , Ptr (<git_submodule>) -> CString -> Ptr () -> IO CInt
+#ccall git_submodule_foreach , Ptr <git_repository> -> <git_submodule_foreach_callback> -> Ptr () -> IO (CInt)
+#ccall git_submodule_add_setup , Ptr (Ptr <git_submodule>) -> Ptr <git_repository> -> CString -> CString -> CInt -> IO (CInt)
+#ccall git_submodule_add_finalize , Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_add_to_index , Ptr <git_submodule> -> CInt -> IO (CInt)
+#ccall git_submodule_save , Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_owner , Ptr <git_submodule> -> IO (Ptr <git_repository>)
+#ccall git_submodule_name , Ptr <git_submodule> -> IO (CString)
+#ccall git_submodule_path , Ptr <git_submodule> -> IO (CString)
+#ccall git_submodule_url , Ptr <git_submodule> -> IO (CString)
+#ccall git_submodule_set_url , Ptr <git_submodule> -> CString -> IO (CInt)
+#ccall git_submodule_index_id , Ptr <git_submodule> -> IO (Ptr <git_oid>)
+#ccall git_submodule_head_id , Ptr <git_submodule> -> IO (Ptr <git_oid>)
+#ccall git_submodule_wd_id , Ptr <git_submodule> -> IO (Ptr <git_oid>)
+#ccall git_submodule_ignore , Ptr <git_submodule> -> IO (<git_submodule_ignore_t>)
+#ccall git_submodule_set_ignore , Ptr <git_submodule> -> <git_submodule_ignore_t> -> IO (<git_submodule_ignore_t>)
+#ccall git_submodule_update , Ptr <git_submodule> -> IO (<git_submodule_update_t>)
+#ccall git_submodule_set_update , Ptr <git_submodule> -> <git_submodule_update_t> -> IO (<git_submodule_update_t>)
+#ccall git_submodule_fetch_recurse_submodules , Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_set_fetch_recurse_submodules , Ptr <git_submodule> -> CInt -> IO (CInt)
+#ccall git_submodule_init , Ptr <git_submodule> -> CInt -> IO (CInt)
+#ccall git_submodule_sync , Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_open , Ptr (Ptr <git_repository>) -> Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_reload , Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_reload_all , Ptr <git_repository> -> IO (CInt)
+#ccall git_submodule_status , Ptr CUInt -> Ptr <git_submodule> -> IO (CInt)
+#ccall git_submodule_location , Ptr CUInt -> Ptr <git_submodule> -> IO (CInt)
diff --git a/Bindings/Libgit2/Tag.hsc b/Bindings/Libgit2/Tag.hsc
--- a/Bindings/Libgit2/Tag.hsc
+++ b/Bindings/Libgit2/Tag.hsc
@@ -1,19 +1,22 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Tag where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
 import Bindings.Libgit2.Types
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Object
+import Bindings.Libgit2.Strarray
 #cinline git_tag_lookup , Ptr (Ptr <git_tag>) -> Ptr <git_repository> -> Ptr <git_oid> -> IO (CInt)
-#cinline git_tag_lookup_prefix , Ptr (Ptr <git_tag>) -> Ptr <git_repository> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#cinline git_tag_lookup_prefix , Ptr (Ptr <git_tag>) -> Ptr <git_repository> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #cinline git_tag_free , Ptr <git_tag> -> IO ()
 #ccall git_tag_id , Ptr <git_tag> -> IO (Ptr <git_oid>)
 #ccall git_tag_target , Ptr (Ptr <git_object>) -> Ptr <git_tag> -> IO (CInt)
-#ccall git_tag_target_oid , Ptr <git_tag> -> IO (Ptr <git_oid>)
-#ccall git_tag_type , Ptr <git_tag> -> IO (<git_otype>)
+#ccall git_tag_target_id , Ptr <git_tag> -> IO (Ptr <git_oid>)
+#ccall git_tag_target_type , Ptr <git_tag> -> IO (<git_otype>)
 #ccall git_tag_name , Ptr <git_tag> -> IO (CString)
 #ccall git_tag_tagger , Ptr <git_tag> -> IO (Ptr <git_signature>)
 #ccall git_tag_message , Ptr <git_tag> -> IO (CString)
@@ -23,4 +26,9 @@
 #ccall git_tag_delete , Ptr <git_repository> -> CString -> IO (CInt)
 #ccall git_tag_list , Ptr <git_strarray> -> Ptr <git_repository> -> IO (CInt)
 #ccall git_tag_list_match , Ptr <git_strarray> -> CString -> Ptr <git_repository> -> IO (CInt)
+{- typedef int (* git_tag_foreach_cb)(const char * name,
+                                   git_oid * oid,
+                                   void * payload); -}
+#callback git_tag_foreach_cb , CString -> Ptr (<git_oid>) -> Ptr () -> IO CInt
+#ccall git_tag_foreach , Ptr <git_repository> -> <git_tag_foreach_cb> -> Ptr () -> IO (CInt)
 #ccall git_tag_peel , Ptr (Ptr <git_object>) -> Ptr <git_tag> -> IO (CInt)
diff --git a/Bindings/Libgit2/Tag.hsc.helper.c b/Bindings/Libgit2/Tag.hsc.helper.c
--- a/Bindings/Libgit2/Tag.hsc.helper.c
+++ b/Bindings/Libgit2/Tag.hsc.helper.c
@@ -2,5 +2,5 @@
 #include <git2.h>
 
 BC_INLINE3(git_tag_lookup, git_tag**, git_repository*, const git_oid*, int)
-BC_INLINE4(git_tag_lookup_prefix, git_tag**, git_repository*, const git_oid*, unsigned int, int)
+BC_INLINE4(git_tag_lookup_prefix, git_tag**, git_repository*, const git_oid*, size_t, int)
 BC_INLINE1VOID(git_tag_free, git_tag*)
diff --git a/Bindings/Libgit2/Threads.hsc b/Bindings/Libgit2/Threads.hsc
--- a/Bindings/Libgit2/Threads.hsc
+++ b/Bindings/Libgit2/Threads.hsc
@@ -1,8 +1,10 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Threads where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
-#ccall git_threads_init , IO ()
+#ccall git_threads_init , IO (CInt)
 #ccall git_threads_shutdown , IO ()
diff --git a/Bindings/Libgit2/Trace.hsc b/Bindings/Libgit2/Trace.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Trace.hsc
@@ -0,0 +1,31 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/trace.h>
+module Bindings.Libgit2.Trace where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Common
+import Bindings.Libgit2.Types
+{- typedef enum {
+            GIT_TRACE_NONE = 0,
+            GIT_TRACE_FATAL = 1,
+            GIT_TRACE_ERROR = 2,
+            GIT_TRACE_WARN = 3,
+            GIT_TRACE_INFO = 4,
+            GIT_TRACE_DEBUG = 5,
+            GIT_TRACE_TRACE = 6
+        } git_trace_level_t; -}
+#integral_t git_trace_level_t
+#num GIT_TRACE_NONE
+#num GIT_TRACE_FATAL
+#num GIT_TRACE_ERROR
+#num GIT_TRACE_WARN
+#num GIT_TRACE_INFO
+#num GIT_TRACE_DEBUG
+#num GIT_TRACE_TRACE
+{- typedef void (* git_trace_callback)(git_trace_level_t level,
+                                    const char * msg); -}
+#callback git_trace_callback , <git_trace_level_t> -> CString -> IO ()
+#ccall git_trace_set , <git_trace_level_t> -> <git_trace_callback> -> IO (CInt)
diff --git a/Bindings/Libgit2/Transport.hsc b/Bindings/Libgit2/Transport.hsc
new file mode 100644
--- /dev/null
+++ b/Bindings/Libgit2/Transport.hsc
@@ -0,0 +1,174 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
+#include <bindings.dsl.h>
+#include <git2.h>
+#include <git2/transport.h>
+module Bindings.Libgit2.Transport where
+import Foreign.Ptr
+#strict_import
+
+import Bindings.Libgit2.Indexer
+import Bindings.Libgit2.Net
+import Bindings.Libgit2.Types
+{- typedef enum {
+            GIT_CREDTYPE_USERPASS_PLAINTEXT = 1
+        } git_credtype_t; -}
+#integral_t git_credtype_t
+#num GIT_CREDTYPE_USERPASS_PLAINTEXT
+{- typedef struct git_cred {
+            git_credtype_t credtype; void (* free)(struct git_cred * cred);
+        } git_cred; -}
+#callback git_cred_free_callback , Ptr <git_cred> -> IO ()
+#starttype git_cred
+#field credtype , <git_credtype_t>
+#field free , <git_cred_free_callback>
+#stoptype
+{- typedef struct git_cred_userpass_plaintext {
+            git_cred parent; char * username; char * password;
+        } git_cred_userpass_plaintext; -}
+#starttype git_cred_userpass_plaintext
+#field parent , <git_cred>
+#field username , CString
+#field password , CString
+#stoptype
+#ccall git_cred_userpass_plaintext_new , Ptr (Ptr <git_cred>) -> CString -> CString -> IO (CInt)
+{- typedef int (* git_cred_acquire_cb)(git_cred * * cred,
+                                    const char * url,
+                                    const char * username_from_url,
+                                    unsigned int allowed_types,
+                                    void * payload); -}
+#callback git_cred_acquire_cb , Ptr (Ptr (<git_cred>)) -> CString -> CString -> CUInt -> Ptr () -> IO CInt
+{- typedef enum {
+            GIT_TRANSPORTFLAGS_NONE = 0, GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
+        } git_transport_flags_t; -}
+#integral_t git_transport_flags_t
+#num GIT_TRANSPORTFLAGS_NONE
+#num GIT_TRANSPORTFLAGS_NO_CHECK_CERT
+{- typedef void (* git_transport_message_cb)(const char * str,
+                                          int len,
+                                          void * data); -}
+#callback git_transport_message_cb , CString -> CInt -> Ptr () -> IO ()
+{- typedef struct git_transport {
+            unsigned int version;
+            int (* set_callbacks)(struct git_transport * transport,
+                                  git_transport_message_cb progress_cb,
+                                  git_transport_message_cb error_cb,
+                                  void * payload);
+            int (* connect)(struct git_transport * transport,
+                            const char * url,
+                            git_cred_acquire_cb cred_acquire_cb,
+                            void * cred_acquire_payload,
+                            int direction,
+                            int flags);
+            int (* ls)(struct git_transport * transport,
+                       git_headlist_cb list_cb,
+                       void * payload);
+            int (* push)(struct git_transport * transport, git_push * push);
+            int (* negotiate_fetch)(struct git_transport * transport,
+                                    git_repository * repo,
+                                    const git_remote_head * const * refs,
+                                    size_t count);
+            int (* download_pack)(struct git_transport * transport,
+                                  git_repository * repo,
+                                  git_transfer_progress * stats,
+                                  git_transfer_progress_callback progress_cb,
+                                  void * progress_payload);
+            int (* is_connected)(struct git_transport * transport);
+            int (* read_flags)(struct git_transport * transport, int * flags);
+            void (* cancel)(struct git_transport * transport);
+            int (* close)(struct git_transport * transport);
+            void (* free)(struct git_transport * transport);
+        } git_transport; -}
+#callback git_transport_set_callbacks_callback , Ptr <git_transport> -> <git_transport_message_cb> -> <git_transport_message_cb> -> Ptr () -> IO CInt
+#callback git_transport_connect_callback , Ptr <git_transport> -> CString -> <git_cred_acquire_cb> -> Ptr () -> CInt -> CInt -> IO CInt
+#callback git_transport_ls_callback , Ptr <git_transport> -> <git_headlist_cb> -> Ptr () -> IO CInt
+#callback git_transport_push_callback , Ptr <git_transport> -> Ptr <git_push> -> IO CInt
+#callback git_transport_negotiate_fetch_callback , Ptr <git_transport> -> Ptr <git_repository> -> Ptr (Ptr <git_remote_head>) -> CSize -> IO CInt
+#callback git_transport_download_pack_callback , Ptr <git_transport> -> Ptr <git_repository> -> Ptr <git_transfer_progress> -> <git_transfer_progress_callback> -> Ptr () -> IO CInt
+#callback git_transport_is_connected_callback , Ptr <git_transport> -> IO CInt
+#callback git_transport_read_flags_callback , Ptr <git_transport> -> Ptr CInt -> IO CInt
+#callback git_transport_cancel_callback , Ptr <git_transport> -> IO ()
+#callback git_transport_close_callback , Ptr <git_transport> -> IO CInt
+#callback git_transport_free_callback , Ptr <git_transport> -> IO ()
+#starttype git_transport
+#field version , CUInt
+#field set_callbacks , <git_transport_set_callbacks_callback>
+#field connect , <git_transport_connect_callback>
+#field ls , <git_transport_ls_callback>
+#field push , <git_transport_push_callback>
+#field negotiate_fetch , <git_transport_negotiate_fetch_callback>
+#field download_pack , <git_transport_download_pack_callback>
+#field is_connected , <git_transport_is_connected_callback>
+#field read_flags , <git_transport_read_flags_callback>
+#field cancel , <git_transport_cancel_callback>
+#field close , <git_transport_close_callback>
+#field free , <git_transport_free_callback>
+#stoptype
+#ccall git_transport_new , Ptr (Ptr <git_transport>) -> Ptr <git_remote> -> CString -> IO (CInt)
+{- typedef int (* git_transport_cb)(git_transport * * out,
+                                 git_remote * owner,
+                                 void * param); -}
+#callback git_transport_cb , Ptr (Ptr (<git_transport>)) -> Ptr (<git_remote>) -> Ptr () -> IO CInt
+#ccall git_transport_dummy , Ptr (Ptr <git_transport>) -> Ptr <git_remote> -> Ptr () -> IO (CInt)
+#ccall git_transport_local , Ptr (Ptr <git_transport>) -> Ptr <git_remote> -> Ptr () -> IO (CInt)
+#ccall git_transport_smart , Ptr (Ptr <git_transport>) -> Ptr <git_remote> -> Ptr () -> IO (CInt)
+{- typedef enum {
+            GIT_SERVICE_UPLOADPACK_LS = 1,
+            GIT_SERVICE_UPLOADPACK = 2,
+            GIT_SERVICE_RECEIVEPACK_LS = 3,
+            GIT_SERVICE_RECEIVEPACK = 4
+        } git_smart_service_t; -}
+#integral_t git_smart_service_t
+#num GIT_SERVICE_UPLOADPACK_LS
+#num GIT_SERVICE_UPLOADPACK
+#num GIT_SERVICE_RECEIVEPACK_LS
+#num GIT_SERVICE_RECEIVEPACK
+{- struct git_smart_subtransport; -}
+{- #opaque_t git_smart_subtransport -}
+{- typedef struct git_smart_subtransport_stream {
+            struct git_smart_subtransport * subtransport;
+            int (* read)(struct git_smart_subtransport_stream * stream,
+                         char * buffer,
+                         size_t buf_size,
+                         size_t * bytes_read);
+            int (* write)(struct git_smart_subtransport_stream * stream,
+                          const char * buffer,
+                          size_t len);
+            void (* free)(struct git_smart_subtransport_stream * stream);
+        } git_smart_subtransport_stream; -}
+#callback git_smart_subtransport_stream_read_callback , Ptr <git_smart_subtransport_stream> -> CString -> CSize -> Ptr CSize -> IO CInt
+#callback git_smart_subtransport_stream_write_callback , Ptr <git_smart_subtransport_stream> -> CString -> CSize -> IO CInt
+#callback git_smart_subtransport_stream_free_callback , Ptr <git_smart_subtransport_stream> -> IO ()
+#starttype git_smart_subtransport_stream
+#field subtransport , Ptr <git_smart_subtransport>
+#field read , <git_smart_subtransport_stream_read_callback>
+#field write , <git_smart_subtransport_stream_write_callback>
+#field free , <git_smart_subtransport_stream_free_callback>
+#stoptype
+{- typedef struct git_smart_subtransport {
+            int (* action)(git_smart_subtransport_stream * * out,
+                           struct git_smart_subtransport * transport,
+                           const char * url,
+                           git_smart_service_t action);
+            int (* close)(struct git_smart_subtransport * transport);
+            void (* free)(struct git_smart_subtransport * transport);
+        } git_smart_subtransport; -}
+#callback git_smart_subtransport_action_callback , Ptr (Ptr <git_smart_subtransport_stream>) -> Ptr <git_smart_subtransport> -> CString -> <git_smart_service_t> -> IO CInt
+#callback git_smart_subtransport_close_callback , Ptr <git_smart_subtransport> -> IO CInt
+#callback git_smart_subtransport_free_callback , Ptr <git_smart_subtransport> -> IO ()
+#starttype git_smart_subtransport
+#field action , <git_smart_subtransport_action_callback>
+#field close , <git_smart_subtransport_close_callback>
+#field free , <git_smart_subtransport_free_callback>
+#stoptype
+{- typedef int (* git_smart_subtransport_cb)(git_smart_subtransport * * out,
+                                          git_transport * owner); -}
+#callback git_smart_subtransport_cb , Ptr (Ptr (<git_smart_subtransport>)) -> Ptr (<git_transport>) -> IO CInt
+{- typedef struct git_smart_subtransport_definition {
+            git_smart_subtransport_cb callback; unsigned rpc;
+        } git_smart_subtransport_definition; -}
+#starttype git_smart_subtransport_definition
+#field callback , <git_smart_subtransport_cb>
+#field rpc , CUInt
+#stoptype
+#ccall git_smart_subtransport_http , Ptr (Ptr <git_smart_subtransport>) -> Ptr <git_transport> -> IO (CInt)
+#ccall git_smart_subtransport_git , Ptr (Ptr <git_smart_subtransport>) -> Ptr <git_transport> -> IO (CInt)
diff --git a/Bindings/Libgit2/Tree.hsc b/Bindings/Libgit2/Tree.hsc
--- a/Bindings/Libgit2/Tree.hsc
+++ b/Bindings/Libgit2/Tree.hsc
@@ -1,7 +1,9 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 #include <git2/tree.h>
 module Bindings.Libgit2.Tree where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -9,36 +11,43 @@
 import Bindings.Libgit2.Oid
 import Bindings.Libgit2.Object
 #cinline git_tree_lookup , Ptr (Ptr <git_tree>) -> Ptr <git_repository> -> Ptr <git_oid> -> IO (CInt)
-#cinline git_tree_lookup_prefix , Ptr (Ptr <git_tree>) -> Ptr <git_repository> -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#cinline git_tree_lookup_prefix , Ptr (Ptr <git_tree>) -> Ptr <git_repository> -> Ptr <git_oid> -> CSize -> IO (CInt)
 #cinline git_tree_free , Ptr <git_tree> -> IO ()
 #ccall git_tree_id , Ptr <git_tree> -> IO (Ptr <git_oid>)
-#ccall git_tree_entrycount , Ptr <git_tree> -> IO (CUInt)
-#ccall git_treebuilder_entrycount , Ptr <git_treebuilder> -> IO (CUInt)
+#ccall git_tree_owner , Ptr <git_tree> -> IO (Ptr <git_repository>)
+#ccall git_tree_entrycount , Ptr <git_tree> -> IO (CSize)
 #ccall git_tree_entry_byname , Ptr <git_tree> -> CString -> IO (Ptr <git_tree_entry>)
-#ccall git_tree_entry_byindex , Ptr <git_tree> -> CUInt -> IO (Ptr <git_tree_entry>)
-#ccall git_tree_entry_attributes , Ptr <git_tree_entry> -> IO (CUInt)
+#ccall git_tree_entry_byindex , Ptr <git_tree> -> CSize -> IO (Ptr <git_tree_entry>)
+#ccall git_tree_entry_byoid , Ptr <git_tree> -> Ptr <git_oid> -> IO (Ptr <git_tree_entry>)
+#ccall git_tree_entry_bypath , Ptr (Ptr <git_tree_entry>) -> Ptr <git_tree> -> CString -> IO (CInt)
+#ccall git_tree_entry_dup , Ptr <git_tree_entry> -> IO (Ptr <git_tree_entry>)
+#ccall git_tree_entry_free , Ptr <git_tree_entry> -> IO ()
 #ccall git_tree_entry_name , Ptr <git_tree_entry> -> IO (CString)
 #ccall git_tree_entry_id , Ptr <git_tree_entry> -> IO (Ptr <git_oid>)
 #ccall git_tree_entry_type , Ptr <git_tree_entry> -> IO (<git_otype>)
+#ccall git_tree_entry_filemode , Ptr <git_tree_entry> -> IO (<git_filemode_t>)
+#ccall git_tree_entry_cmp , Ptr <git_tree_entry> -> Ptr <git_tree_entry> -> IO (CInt)
 #ccall git_tree_entry_to_object , Ptr (Ptr <git_object>) -> Ptr <git_repository> -> Ptr <git_tree_entry> -> IO (CInt)
-#ccall git_tree_create_fromindex , Ptr <git_oid> -> Ptr <git_index> -> IO (CInt)
 #ccall git_treebuilder_create , Ptr (Ptr <git_treebuilder>) -> Ptr <git_tree> -> IO (CInt)
 #ccall git_treebuilder_clear , Ptr <git_treebuilder> -> IO ()
+#ccall git_treebuilder_entrycount , Ptr <git_treebuilder> -> IO (CUInt)
 #ccall git_treebuilder_free , Ptr <git_treebuilder> -> IO ()
 #ccall git_treebuilder_get , Ptr <git_treebuilder> -> CString -> IO (Ptr <git_tree_entry>)
-#ccall git_treebuilder_insert , Ptr (Ptr <git_tree_entry>) -> Ptr <git_treebuilder> -> CString -> Ptr <git_oid> -> CUInt -> IO (CInt)
+#ccall git_treebuilder_insert , Ptr (Ptr <git_tree_entry>) -> Ptr <git_treebuilder> -> CString -> Ptr <git_oid> -> <git_filemode_t> -> IO (CInt)
 #ccall git_treebuilder_remove , Ptr <git_treebuilder> -> CString -> IO (CInt)
-#callback git_treebuilder_filter_callback , Ptr <git_tree_entry> -> Ptr () -> IO CInt
-#ccall git_treebuilder_filter , Ptr <git_treebuilder> -> <git_treebuilder_filter_callback> -> Ptr () -> IO ()
+{- typedef int (* git_treebuilder_filter_cb)(const git_tree_entry * entry,
+                                          void * payload); -}
+#callback git_treebuilder_filter_cb , Ptr (<git_tree_entry>) -> Ptr () -> IO CInt
+#ccall git_treebuilder_filter , Ptr <git_treebuilder> -> <git_treebuilder_filter_cb> -> Ptr () -> IO ()
 #ccall git_treebuilder_write , Ptr <git_oid> -> Ptr <git_repository> -> Ptr <git_treebuilder> -> IO (CInt)
-#ccall git_tree_get_subtree , Ptr (Ptr <git_tree>) -> Ptr <git_tree> -> CString -> IO (CInt)
 {- typedef int (* git_treewalk_cb)(const char * root,
-                                git_tree_entry * entry,
+                                const git_tree_entry * entry,
                                 void * payload); -}
-#synonym_t git_treewalk_cb , CInt
-{- enum git_treewalk_mode {
-    GIT_TREEWALK_PRE = 0, GIT_TREEWALK_POST = 1
-}; -}
+#callback git_treewalk_cb , CString -> Ptr (<git_tree_entry>) -> Ptr () -> IO CInt
+{- typedef enum {
+            GIT_TREEWALK_PRE = 0, GIT_TREEWALK_POST = 1
+        } git_treewalk_mode; -}
+#integral_t git_treewalk_mode
 #num GIT_TREEWALK_PRE
 #num GIT_TREEWALK_POST
-#ccall git_tree_walk , Ptr <git_tree> -> CInt -> CInt -> Ptr () -> IO (CInt)
+#ccall git_tree_walk , Ptr <git_tree> -> <git_treewalk_mode> -> <git_treewalk_cb> -> Ptr () -> IO (CInt)
diff --git a/Bindings/Libgit2/Tree.hsc.helper.c b/Bindings/Libgit2/Tree.hsc.helper.c
--- a/Bindings/Libgit2/Tree.hsc.helper.c
+++ b/Bindings/Libgit2/Tree.hsc.helper.c
@@ -2,5 +2,5 @@
 #include <git2.h>
 
 BC_INLINE3(git_tree_lookup, git_tree**, git_repository*, const git_oid*, int)
-BC_INLINE4(git_tree_lookup_prefix, git_tree**, git_repository*, const git_oid*, unsigned int, int)
+BC_INLINE4(git_tree_lookup_prefix, git_tree**, git_repository*, const git_oid*, size_t, int)
 BC_INLINE1VOID(git_tree_free, git_tree*)
diff --git a/Bindings/Libgit2/Types.hsc b/Bindings/Libgit2/Types.hsc
--- a/Bindings/Libgit2/Types.hsc
+++ b/Bindings/Libgit2/Types.hsc
@@ -1,6 +1,8 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Types where
+import Foreign.Ptr
 #strict_import
 
 import Bindings.Libgit2.Common
@@ -33,12 +35,18 @@
 #num GIT_OBJ_REF_DELTA
 {- typedef struct git_odb git_odb; -}
 #opaque_t git_odb
--- {- typedef struct git_odb_backend git_odb_backend; -}
--- #opaque_t git_odb_backend
+{- typedef struct git_odb_backend git_odb_backend; -}
+{- #opaque_t git_odb_backend -}
 {- typedef struct git_odb_object git_odb_object; -}
 #opaque_t git_odb_object
--- {- typedef struct git_odb_stream git_odb_stream; -}
--- #opaque_t git_odb_stream
+{- typedef struct git_odb_stream git_odb_stream; -}
+{- #opaque_t git_odb_stream -}
+{- typedef struct git_odb_writepack git_odb_writepack; -}
+{- #opaque_t git_odb_writepack -}
+{- typedef struct git_refdb git_refdb; -}
+#opaque_t git_refdb
+{- typedef struct git_refdb_backend git_refdb_backend; -}
+{- #opaque_t git_refdb_backend -}
 {- typedef struct git_repository git_repository; -}
 #opaque_t git_repository
 {- typedef struct git_object git_object; -}
@@ -61,14 +69,16 @@
 #opaque_t git_index
 {- typedef struct git_config git_config; -}
 #opaque_t git_config
--- {- typedef struct git_config_file git_config_file; -}
--- #opaque_t git_config_file
+{- typedef struct git_config_backend git_config_backend; -}
+{- #opaque_t git_config_backend -}
 {- typedef struct git_reflog_entry git_reflog_entry; -}
 #opaque_t git_reflog_entry
 {- typedef struct git_reflog git_reflog; -}
 #opaque_t git_reflog
 {- typedef struct git_note git_note; -}
 #opaque_t git_note
+{- typedef struct git_packbuilder git_packbuilder; -}
+#opaque_t git_packbuilder
 {- typedef struct git_time {
             git_time_t time; int offset;
         } git_time; -}
@@ -90,16 +100,12 @@
             GIT_REF_INVALID = 0,
             GIT_REF_OID = 1,
             GIT_REF_SYMBOLIC = 2,
-            GIT_REF_PACKED = 4,
-            GIT_REF_HAS_PEEL = 8,
-            GIT_REF_LISTALL = GIT_REF_OID | GIT_REF_SYMBOLIC | GIT_REF_PACKED
+            GIT_REF_LISTALL = GIT_REF_OID | GIT_REF_SYMBOLIC
         } git_ref_t; -}
 #integral_t git_ref_t
 #num GIT_REF_INVALID
 #num GIT_REF_OID
 #num GIT_REF_SYMBOLIC
-#num GIT_REF_PACKED
-#num GIT_REF_HAS_PEEL
 #num GIT_REF_LISTALL
 {- typedef enum {
             GIT_BRANCH_LOCAL = 1, GIT_BRANCH_REMOTE = 2
@@ -107,9 +113,28 @@
 #integral_t git_branch_t
 #num GIT_BRANCH_LOCAL
 #num GIT_BRANCH_REMOTE
+{- typedef enum {
+            GIT_FILEMODE_NEW = 00,
+            GIT_FILEMODE_TREE = 040000,
+            GIT_FILEMODE_BLOB = 0100644,
+            GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
+            GIT_FILEMODE_LINK = 0120000,
+            GIT_FILEMODE_COMMIT = 0160000
+        } git_filemode_t; -}
+#integral_t git_filemode_t
+#num GIT_FILEMODE_NEW
+#num GIT_FILEMODE_TREE
+#num GIT_FILEMODE_BLOB
+#num GIT_FILEMODE_BLOB_EXECUTABLE
+#num GIT_FILEMODE_LINK
+#num GIT_FILEMODE_COMMIT
 {- typedef struct git_refspec git_refspec; -}
 #opaque_t git_refspec
 {- typedef struct git_remote git_remote; -}
 #opaque_t git_remote
--- {- typedef struct git_remote_head git_remote_head; -}
--- #opaque_t git_remote_head
+{- typedef struct git_push git_push; -}
+#opaque_t git_push
+{- typedef struct git_remote_head git_remote_head; -}
+{- #opaque_t git_remote_head -}
+{- typedef struct git_remote_callbacks git_remote_callbacks; -}
+{- #opaque_t git_remote_callbacks -}
diff --git a/Bindings/Libgit2/Version.hsc b/Bindings/Libgit2/Version.hsc
--- a/Bindings/Libgit2/Version.hsc
+++ b/Bindings/Libgit2/Version.hsc
@@ -1,5 +1,7 @@
+{-# OPTIONS_GHC -fno-warn-unused-imports #-}
 #include <bindings.dsl.h>
 #include <git2.h>
 module Bindings.Libgit2.Version where
+import Foreign.Ptr
 #strict_import
 
diff --git a/hlibgit2.cabal b/hlibgit2.cabal
--- a/hlibgit2.cabal
+++ b/hlibgit2.cabal
@@ -1,7 +1,7 @@
 Name:                hlibgit2
-Version:             0.17.0.8
+Version:             0.18.0.1
 Synopsis:            Low-level bindings to libgit2
-Description:         Bindings to libgit2 v0.17.0.
+Description:         Bindings to libgit2 v0.18.0.
 License-file:        LICENSE
 License:             MIT
 Author:              John Wiegley, Sakari Jokinen, Jacob Stanleyyeah,
@@ -15,7 +15,6 @@
   libgit2/include/git2.h
   libgit2/include/git2/*.h
   libgit2/src/*.h
-  libgit2/src/compat/*.h
   libgit2/src/unix/*.h
   libgit2/src/win32/*.h
   libgit2/src/xdiff/*.h
@@ -49,31 +48,47 @@
     Bindings.Libgit2.Attr
     Bindings.Libgit2.Blob
     Bindings.Libgit2.Branch
+    Bindings.Libgit2.Checkout
+    Bindings.Libgit2.Clone
     Bindings.Libgit2.Commit
     Bindings.Libgit2.Common
     Bindings.Libgit2.Config
+    Bindings.Libgit2.CredHelpers
     Bindings.Libgit2.Diff
     Bindings.Libgit2.Errors
+    Bindings.Libgit2.Graph
+    Bindings.Libgit2.Ignore
     Bindings.Libgit2.Index
     Bindings.Libgit2.Indexer
     Bindings.Libgit2.Merge
+    Bindings.Libgit2.Message
     Bindings.Libgit2.Net
     Bindings.Libgit2.Notes
     Bindings.Libgit2.Object
     Bindings.Libgit2.Odb
     Bindings.Libgit2.OdbBackend
     Bindings.Libgit2.Oid
+    Bindings.Libgit2.Pack
+    Bindings.Libgit2.Push
+    Bindings.Libgit2.Refdb
+    Bindings.Libgit2.RefdbBackend
     Bindings.Libgit2.Reflog
     Bindings.Libgit2.Refs
     Bindings.Libgit2.Refspec
     Bindings.Libgit2.Remote
     Bindings.Libgit2.Repository
+    Bindings.Libgit2.Reset
+    Bindings.Libgit2.Revparse
     Bindings.Libgit2.Revwalk
     Bindings.Libgit2.Signature
+    Bindings.Libgit2.Stash
     Bindings.Libgit2.Status
+    Bindings.Libgit2.Strarray
     Bindings.Libgit2.Submodule
     Bindings.Libgit2.Tag
     Bindings.Libgit2.Threads
+    Bindings.Libgit2.Trace
+    Bindings.Libgit2.Transport
     Bindings.Libgit2.Tree
     Bindings.Libgit2.Types
     Bindings.Libgit2.Version
@@ -84,33 +99,47 @@
     Bindings/Libgit2/Blob.hsc.helper.c
     Bindings/Libgit2/Commit.hsc.helper.c
     Bindings/Libgit2/Common.hsc.helper.c
+    Bindings/Libgit2/Oid.hsc.helper.c
     Bindings/Libgit2/Tag.hsc.helper.c
     Bindings/Libgit2/Tree.hsc.helper.c
     libgit2/src/attr.c
     libgit2/src/attr_file.c
     libgit2/src/blob.c
     libgit2/src/branch.c
+    libgit2/src/buf_text.c
     libgit2/src/buffer.c
     libgit2/src/cache.c
+    libgit2/src/checkout.c
+    libgit2/src/clone.c
     libgit2/src/commit.c
+    libgit2/src/commit_list.c
+    libgit2/src/compress.c
     libgit2/src/config.c
     libgit2/src/config_cache.c
     libgit2/src/config_file.c
     libgit2/src/crlf.c
+    libgit2/src/date.c
     libgit2/src/delta-apply.c
+    libgit2/src/delta.c
     libgit2/src/diff.c
     libgit2/src/diff_output.c
+    libgit2/src/diff_tform.c
     libgit2/src/errors.c
     libgit2/src/fetch.c
+    libgit2/src/fetchhead.c
     libgit2/src/filebuf.c
     libgit2/src/fileops.c
     libgit2/src/filter.c
+    libgit2/src/fnmatch.c
     libgit2/src/global.c
+    libgit2/src/graph.c
     libgit2/src/hash.c
+    libgit2/src/hashsig.c
     libgit2/src/ignore.c
     libgit2/src/index.c
     libgit2/src/indexer.c
     libgit2/src/iterator.c
+    libgit2/src/merge.c
     libgit2/src/message.c
     libgit2/src/mwindow.c
     libgit2/src/netops.c
@@ -120,35 +149,46 @@
     libgit2/src/odb_loose.c
     libgit2/src/odb_pack.c
     libgit2/src/oid.c
+    libgit2/src/pack-objects.c
     libgit2/src/pack.c
     libgit2/src/path.c
-    libgit2/src/pkt.c
+    libgit2/src/pathspec.c
     libgit2/src/pool.c
     libgit2/src/posix.c
     libgit2/src/pqueue.c
-    libgit2/src/protocol.c
+    libgit2/src/push.c
+    libgit2/src/refdb.c
+    libgit2/src/refdb_fs.c
     libgit2/src/reflog.c
     libgit2/src/refs.c
     libgit2/src/refspec.c
     libgit2/src/remote.c
     libgit2/src/repository.c
+    libgit2/src/reset.c
+    libgit2/src/revparse.c
     libgit2/src/revwalk.c
-    libgit2/src/sha1.c
     libgit2/src/sha1_lookup.c
     libgit2/src/signature.c
+    libgit2/src/stash.c
     libgit2/src/status.c
     libgit2/src/submodule.c
     libgit2/src/tag.c
     libgit2/src/thread-utils.c
+    libgit2/src/trace.c
     libgit2/src/transport.c
     libgit2/src/tree-cache.c
     libgit2/src/tree.c
     libgit2/src/tsort.c
     libgit2/src/util.c
     libgit2/src/vector.c
+    libgit2/src/transports/cred.c
+    libgit2/src/transports/cred_helpers.c
     libgit2/src/transports/git.c
     libgit2/src/transports/http.c
     libgit2/src/transports/local.c
+    libgit2/src/transports/smart.c
+    libgit2/src/transports/smart_pkt.c
+    libgit2/src/transports/smart_protocol.c
     libgit2/src/xdiff/xdiffi.c
     libgit2/src/xdiff/xemit.c
     libgit2/src/xdiff/xhistogram.c
@@ -163,21 +203,30 @@
     libgit2/src
     libgit2/deps/http-parser
 
-  cc-options: -g -DGIT_THREADS -D_FILE_OFFSET_BITS=64
+  cc-options: -g -DGIT_THREADS -D_FILE_OFFSET_BITS=64 -DGIT_SSL
   ld-options: -g
   if os(windows)
-    cc-options: -DGIT_WIN32 -DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501
+    cc-options: -DGIT_WIN32 -DWIN32 -DWIN32_SHA1 -D_DEBUG -D_WIN32_WINNT=0x0501 -DGIT_WINHTTP
     c-sources:
+      libgit2/src/hash/hash_win32.c
+      libgit2/src/transports/winhttp.c
       libgit2/src/win32/dir.c
-      libgit2/src/win32/pthread.c
-      libgit2/src/win32/utf-conv.c
+      libgit2/src/win32/error.c
+      libgit2/src/win32/findfile.c
       libgit2/src/win32/map.c
       libgit2/src/win32/posix_w32.c
+      libgit2/src/win32/precompiled.c
+      libgit2/src/win32/pthread.c
+      libgit2/src/win32/utf-conv.c
     include-dirs:
       libgit2/src/win32
   else
-    cc-options: -D_GNU_SOURCE
+    cc-options: -D_GNU_SOURCE -DOPENSSL_SHA1 -Wno-deprecated-declarations
     c-sources:
+      -- libgit2/src/hash/hash_generic.c
       libgit2/src/unix/map.c
+      libgit2/src/unix/realpath.c
     include-dirs:
       libgit2/src/unix
+    extra-libraries:
+      ssl, crypto
diff --git a/libgit2/deps/http-parser/http_parser.c b/libgit2/deps/http-parser/http_parser.c
--- a/libgit2/deps/http-parser/http_parser.c
+++ b/libgit2/deps/http-parser/http_parser.c
@@ -21,1758 +21,2154 @@
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  * IN THE SOFTWARE.
  */
-#include <http_parser.h>
-#include <assert.h>
-#include <stddef.h>
-
-
-#ifndef MIN
-# define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-
-#if HTTP_PARSER_DEBUG
-#define SET_ERRNO(e)                                                 \
-do {                                                                 \
-  parser->http_errno = (e);                                          \
-  parser->error_lineno = __LINE__;                                   \
-} while (0)
-#else
-#define SET_ERRNO(e)                                                 \
-do {                                                                 \
-  parser->http_errno = (e);                                          \
-} while(0)
-#endif
-
-
-#define CALLBACK2(FOR)                                               \
-do {                                                                 \
-  if (settings->on_##FOR) {                                          \
-    if (0 != settings->on_##FOR(parser)) {                           \
-      SET_ERRNO(HPE_CB_##FOR);                                       \
-      return (p - data);                                             \
-    }                                                                \
-  }                                                                  \
-} while (0)
-
-
-#define MARK(FOR)                                                    \
-do {                                                                 \
-  FOR##_mark = p;                                                    \
-} while (0)
-
-#define CALLBACK(FOR)                                                \
-do {                                                                 \
-  if (FOR##_mark) {                                                  \
-    if (settings->on_##FOR) {                                        \
-      if (0 != settings->on_##FOR(parser,                            \
-                                 FOR##_mark,                         \
-                                 p - FOR##_mark))                    \
-      {                                                              \
-        SET_ERRNO(HPE_CB_##FOR);                                     \
-        return (p - data);                                           \
-      }                                                              \
-    }                                                                \
-    FOR##_mark = NULL;                                               \
-  }                                                                  \
-} while (0)
-
-
-#define PROXY_CONNECTION "proxy-connection"
-#define CONNECTION "connection"
-#define CONTENT_LENGTH "content-length"
-#define TRANSFER_ENCODING "transfer-encoding"
-#define UPGRADE "upgrade"
-#define CHUNKED "chunked"
-#define KEEP_ALIVE "keep-alive"
-#define CLOSE "close"
-
-
-static const char *method_strings[] =
-  { "DELETE"
-  , "GET"
-  , "HEAD"
-  , "POST"
-  , "PUT"
-  , "CONNECT"
-  , "OPTIONS"
-  , "TRACE"
-  , "COPY"
-  , "LOCK"
-  , "MKCOL"
-  , "MOVE"
-  , "PROPFIND"
-  , "PROPPATCH"
-  , "UNLOCK"
-  , "REPORT"
-  , "MKACTIVITY"
-  , "CHECKOUT"
-  , "MERGE"
-  , "M-SEARCH"
-  , "NOTIFY"
-  , "SUBSCRIBE"
-  , "UNSUBSCRIBE"
-  , "PATCH"
-  };
-
-
-/* Tokens as defined by rfc 2616. Also lowercases them.
- *        token       = 1*<any CHAR except CTLs or separators>
- *     separators     = "(" | ")" | "<" | ">" | "@"
- *                    | "," | ";" | ":" | "\" | <">
- *                    | "/" | "[" | "]" | "?" | "="
- *                    | "{" | "}" | SP | HT
- */
-static const char tokens[256] = {
-/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-       ' ',      '!',     '"',     '#',     '$',     '%',     '&',    '\'',
-/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-        0,       0,      '*',     '+',      0,      '-',     '.',     '/',
-/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
-/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-       '8',     '9',      0,       0,       0,       0,       0,       0,
-/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
-/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
-/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
-/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
-/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
-/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-       'x',     'y',     'z',      0,      '|',     '}',     '~',       0 };
-
-
-static const int8_t unhex[256] =
-  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
-  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-  };
-
-
-static const uint8_t normal_url_char[256] = {
-/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
-        0,       0,       0,       0,       0,       0,       0,       0,
-/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
-        0,       1,       1,       0,       1,       1,       1,       1,
-/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
-        1,       1,       1,       1,       1,       1,       1,       0,
-/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
-        1,       1,       1,       1,       1,       1,       1,       1,
-/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
-        1,       1,       1,       1,       1,       1,       1,       0, };
-
-
-enum state
-  { s_dead = 1 /* important that this is > 0 */
-
-  , s_start_req_or_res
-  , s_res_or_resp_H
-  , s_start_res
-  , s_res_H
-  , s_res_HT
-  , s_res_HTT
-  , s_res_HTTP
-  , s_res_first_http_major
-  , s_res_http_major
-  , s_res_first_http_minor
-  , s_res_http_minor
-  , s_res_first_status_code
-  , s_res_status_code
-  , s_res_status
-  , s_res_line_almost_done
-
-  , s_start_req
-
-  , s_req_method
-  , s_req_spaces_before_url
-  , s_req_schema
-  , s_req_schema_slash
-  , s_req_schema_slash_slash
-  , s_req_host
-  , s_req_port
-  , s_req_path
-  , s_req_query_string_start
-  , s_req_query_string
-  , s_req_fragment_start
-  , s_req_fragment
-  , s_req_http_start
-  , s_req_http_H
-  , s_req_http_HT
-  , s_req_http_HTT
-  , s_req_http_HTTP
-  , s_req_first_http_major
-  , s_req_http_major
-  , s_req_first_http_minor
-  , s_req_http_minor
-  , s_req_line_almost_done
-
-  , s_header_field_start
-  , s_header_field
-  , s_header_value_start
-  , s_header_value
-  , s_header_value_lws
-
-  , s_header_almost_done
-
-  , s_chunk_size_start
-  , s_chunk_size
-  , s_chunk_parameters
-  , s_chunk_size_almost_done
-  
-  , s_headers_almost_done
-  /* Important: 's_headers_almost_done' must be the last 'header' state. All
-   * states beyond this must be 'body' states. It is used for overflow
-   * checking. See the PARSING_HEADER() macro.
-   */
-
-  , s_chunk_data
-  , s_chunk_data_almost_done
-  , s_chunk_data_done
-
-  , s_body_identity
-  , s_body_identity_eof
-  };
-
-
-#define PARSING_HEADER(state) (state <= s_headers_almost_done)
-
-
-enum header_states
-  { h_general = 0
-  , h_C
-  , h_CO
-  , h_CON
-
-  , h_matching_connection
-  , h_matching_proxy_connection
-  , h_matching_content_length
-  , h_matching_transfer_encoding
-  , h_matching_upgrade
-
-  , h_connection
-  , h_content_length
-  , h_transfer_encoding
-  , h_upgrade
-
-  , h_matching_transfer_encoding_chunked
-  , h_matching_connection_keep_alive
-  , h_matching_connection_close
-
-  , h_transfer_encoding_chunked
-  , h_connection_keep_alive
-  , h_connection_close
-  };
-
-
-/* Macros for character classes; depends on strict-mode  */
-#define CR                  '\r'
-#define LF                  '\n'
-#define LOWER(c)            (unsigned char)(c | 0x20)
-#define TOKEN(c)            (tokens[(unsigned char)c])
-#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
-#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
-#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
-
-#if HTTP_PARSER_STRICT
-#define IS_URL_CHAR(c)      (normal_url_char[(unsigned char) (c)])
-#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
-#else
-#define IS_URL_CHAR(c)                                                         \
-  (normal_url_char[(unsigned char) (c)] || ((c) & 0x80))
-#define IS_HOST_CHAR(c)                                                        \
-  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
-#endif
-
-
-#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
-
-
-#if HTTP_PARSER_STRICT
-# define STRICT_CHECK(cond)                                          \
-do {                                                                 \
-  if (cond) {                                                        \
-    SET_ERRNO(HPE_STRICT);                                           \
-    goto error;                                                      \
-  }                                                                  \
-} while (0)
-# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
-#else
-# define STRICT_CHECK(cond)
-# define NEW_MESSAGE() start_state
-#endif
-
-
-/* Map errno values to strings for human-readable output */
-#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
-static struct {
-  const char *name;
-  const char *description;
-} http_strerror_tab[] = {
-  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
-};
-#undef HTTP_STRERROR_GEN
-
-
-size_t http_parser_execute (http_parser *parser,
-                            const http_parser_settings *settings,
-                            const char *data,
-                            size_t len)
-{
-  char c, ch;
-  int8_t unhex_val;
-  const char *p = data, *pe;
-  size_t to_read;
-  enum state state;
-  enum header_states header_state;
-  size_t index = parser->index;
-  size_t nread = parser->nread;
-  const char *header_field_mark, *header_value_mark, *url_mark;
-  const char *matcher;
-
-  /* We're in an error state. Don't bother doing anything. */
-  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
-    return 0;
-  }
-
-  state = (enum state) parser->state;
-  header_state = (enum header_states) parser->header_state;
-
-  if (len == 0) {
-    switch (state) {
-      case s_body_identity_eof:
-        CALLBACK2(message_complete);
-        return 0;
-
-      case s_dead:
-      case s_start_req_or_res:
-      case s_start_res:
-      case s_start_req:
-        return 0;
-
-      default:
-        SET_ERRNO(HPE_INVALID_EOF_STATE);
-        return 1;
-    }
-  }
-
-  /* technically we could combine all of these (except for url_mark) into one
-     variable, saving stack space, but it seems more clear to have them
-     separated. */
-  header_field_mark = 0;
-  header_value_mark = 0;
-  url_mark = 0;
-
-  if (state == s_header_field)
-    header_field_mark = data;
-  if (state == s_header_value)
-    header_value_mark = data;
-  if (state == s_req_path || state == s_req_schema || state == s_req_schema_slash
-      || state == s_req_schema_slash_slash || state == s_req_port
-      || state == s_req_query_string_start || state == s_req_query_string
-      || state == s_req_host
-      || state == s_req_fragment_start || state == s_req_fragment)
-    url_mark = data;
-
-  for (p=data, pe=data+len; p != pe; p++) {
-    ch = *p;
-
-    if (PARSING_HEADER(state)) {
-      ++nread;
-      /* Buffer overflow attack */
-      if (nread > HTTP_MAX_HEADER_SIZE) {
-        SET_ERRNO(HPE_HEADER_OVERFLOW);
-        goto error;
-      }
-    }
-
-    switch (state) {
-
-      case s_dead:
-        /* this state is used after a 'Connection: close' message
-         * the parser will error out if it reads another message
-         */
-        SET_ERRNO(HPE_CLOSED_CONNECTION);
-        goto error;
-
-      case s_start_req_or_res:
-      {
-        if (ch == CR || ch == LF)
-          break;
-        parser->flags = 0;
-        parser->content_length = -1;
-
-        CALLBACK2(message_begin);
-
-        if (ch == 'H')
-          state = s_res_or_resp_H;
-        else {
-          parser->type = HTTP_REQUEST;
-          goto start_req_method_assign;
-        }
-        break;
-      }
-
-      case s_res_or_resp_H:
-        if (ch == 'T') {
-          parser->type = HTTP_RESPONSE;
-          state = s_res_HT;
-        } else {
-          if (ch != 'E') {
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-          }
-
-          parser->type = HTTP_REQUEST;
-          parser->method = HTTP_HEAD;
-          index = 2;
-          state = s_req_method;
-        }
-        break;
-
-      case s_start_res:
-      {
-        parser->flags = 0;
-        parser->content_length = -1;
-
-        CALLBACK2(message_begin);
-
-        switch (ch) {
-          case 'H':
-            state = s_res_H;
-            break;
-
-          case CR:
-          case LF:
-            break;
-
-          default:
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-        }
-        break;
-      }
-
-      case s_res_H:
-        STRICT_CHECK(ch != 'T');
-        state = s_res_HT;
-        break;
-
-      case s_res_HT:
-        STRICT_CHECK(ch != 'T');
-        state = s_res_HTT;
-        break;
-
-      case s_res_HTT:
-        STRICT_CHECK(ch != 'P');
-        state = s_res_HTTP;
-        break;
-
-      case s_res_HTTP:
-        STRICT_CHECK(ch != '/');
-        state = s_res_first_http_major;
-        break;
-
-      case s_res_first_http_major:
-        if (ch < '1' || ch > '9') {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major = ch - '0';
-        state = s_res_http_major;
-        break;
-
-      /* major HTTP version or dot */
-      case s_res_http_major:
-      {
-        if (ch == '.') {
-          state = s_res_first_http_minor;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major *= 10;
-        parser->http_major += ch - '0';
-
-        if (parser->http_major > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* first digit of minor HTTP version */
-      case s_res_first_http_minor:
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor = ch - '0';
-        state = s_res_http_minor;
-        break;
-
-      /* minor HTTP version or end of request line */
-      case s_res_http_minor:
-      {
-        if (ch == ' ') {
-          state = s_res_first_status_code;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor *= 10;
-        parser->http_minor += ch - '0';
-
-        if (parser->http_minor > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      case s_res_first_status_code:
-      {
-        if (!IS_NUM(ch)) {
-          if (ch == ' ') {
-            break;
-          }
-
-          SET_ERRNO(HPE_INVALID_STATUS);
-          goto error;
-        }
-        parser->status_code = ch - '0';
-        state = s_res_status_code;
-        break;
-      }
-
-      case s_res_status_code:
-      {
-        if (!IS_NUM(ch)) {
-          switch (ch) {
-            case ' ':
-              state = s_res_status;
-              break;
-            case CR:
-              state = s_res_line_almost_done;
-              break;
-            case LF:
-              state = s_header_field_start;
-              break;
-            default:
-              SET_ERRNO(HPE_INVALID_STATUS);
-              goto error;
-          }
-          break;
-        }
-
-        parser->status_code *= 10;
-        parser->status_code += ch - '0';
-
-        if (parser->status_code > 999) {
-          SET_ERRNO(HPE_INVALID_STATUS);
-          goto error;
-        }
-
-        break;
-      }
-
-      case s_res_status:
-        /* the human readable status. e.g. "NOT FOUND"
-         * we are not humans so just ignore this */
-        if (ch == CR) {
-          state = s_res_line_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          state = s_header_field_start;
-          break;
-        }
-        break;
-
-      case s_res_line_almost_done:
-        STRICT_CHECK(ch != LF);
-        state = s_header_field_start;
-        break;
-
-      case s_start_req:
-      {
-        if (ch == CR || ch == LF)
-          break;
-        parser->flags = 0;
-        parser->content_length = -1;
-
-        CALLBACK2(message_begin);
-
-        if (!IS_ALPHA(ch)) {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-      start_req_method_assign:
-        parser->method = (enum http_method) 0;
-        index = 1;
-        switch (ch) {
-          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
-          case 'D': parser->method = HTTP_DELETE; break;
-          case 'G': parser->method = HTTP_GET; break;
-          case 'H': parser->method = HTTP_HEAD; break;
-          case 'L': parser->method = HTTP_LOCK; break;
-          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
-          case 'N': parser->method = HTTP_NOTIFY; break;
-          case 'O': parser->method = HTTP_OPTIONS; break;
-          case 'P': parser->method = HTTP_POST;
-            /* or PROPFIND or PROPPATCH or PUT or PATCH */
-            break;
-          case 'R': parser->method = HTTP_REPORT; break;
-          case 'S': parser->method = HTTP_SUBSCRIBE; break;
-          case 'T': parser->method = HTTP_TRACE; break;
-          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
-          default:
-            SET_ERRNO(HPE_INVALID_METHOD);
-            goto error;
-        }
-        state = s_req_method;
-        break;
-      }
-
-      case s_req_method:
-      {
-        if (ch == '\0') {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-        matcher = method_strings[parser->method];
-        if (ch == ' ' && matcher[index] == '\0') {
-          state = s_req_spaces_before_url;
-        } else if (ch == matcher[index]) {
-          ; /* nada */
-        } else if (parser->method == HTTP_CONNECT) {
-          if (index == 1 && ch == 'H') {
-            parser->method = HTTP_CHECKOUT;
-          } else if (index == 2  && ch == 'P') {
-            parser->method = HTTP_COPY;
-          } else {
-            goto error;
-          }
-        } else if (parser->method == HTTP_MKCOL) {
-          if (index == 1 && ch == 'O') {
-            parser->method = HTTP_MOVE;
-          } else if (index == 1 && ch == 'E') {
-            parser->method = HTTP_MERGE;
-          } else if (index == 1 && ch == '-') {
-            parser->method = HTTP_MSEARCH;
-          } else if (index == 2 && ch == 'A') {
-            parser->method = HTTP_MKACTIVITY;
-          } else {
-            goto error;
-          }
-        } else if (index == 1 && parser->method == HTTP_POST) {
-          if (ch == 'R') {
-            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
-          } else if (ch == 'U') {
-            parser->method = HTTP_PUT;
-          } else if (ch == 'A') {
-            parser->method = HTTP_PATCH;
-          } else {
-            goto error;
-          }
-        } else if (index == 2 && parser->method == HTTP_UNLOCK && ch == 'S') {
-          parser->method = HTTP_UNSUBSCRIBE;
-        } else if (index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
-          parser->method = HTTP_PROPPATCH;
-        } else {
-          SET_ERRNO(HPE_INVALID_METHOD);
-          goto error;
-        }
-
-        ++index;
-        break;
-      }
-      case s_req_spaces_before_url:
-      {
-        if (ch == ' ') break;
-
-        if (ch == '/' || ch == '*') {
-          MARK(url);
-          state = s_req_path;
-          break;
-        }
-
-        /* Proxied requests are followed by scheme of an absolute URI (alpha).
-         * CONNECT is followed by a hostname, which begins with alphanum.
-         * All other methods are followed by '/' or '*' (handled above).
-         */
-        if (IS_ALPHA(ch) || (parser->method == HTTP_CONNECT && IS_NUM(ch))) {
-          MARK(url);
-          state = (parser->method == HTTP_CONNECT) ? s_req_host : s_req_schema;
-          break;
-        }
-
-        SET_ERRNO(HPE_INVALID_URL);
-        goto error;
-      }
-
-      case s_req_schema:
-      {
-        if (IS_ALPHA(ch)) break;
-
-        if (ch == ':') {
-          state = s_req_schema_slash;
-          break;
-        }
-
-        SET_ERRNO(HPE_INVALID_URL);
-        goto error;
-      }
-
-      case s_req_schema_slash:
-        STRICT_CHECK(ch != '/');
-        state = s_req_schema_slash_slash;
-        break;
-
-      case s_req_schema_slash_slash:
-        STRICT_CHECK(ch != '/');
-        state = s_req_host;
-        break;
-
-      case s_req_host:
-      {
-        if (IS_HOST_CHAR(ch)) break;
-        switch (ch) {
-          case ':':
-            state = s_req_port;
-            break;
-          case '/':
-            state = s_req_path;
-            break;
-          case ' ':
-            /* The request line looks like:
-             *   "GET http://foo.bar.com HTTP/1.1"
-             * That is, there is no path.
-             */
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case '?':
-            state = s_req_query_string_start;
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_HOST);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_port:
-      {
-        if (IS_NUM(ch)) break;
-        switch (ch) {
-          case '/':
-            state = s_req_path;
-            break;
-          case ' ':
-            /* The request line looks like:
-             *   "GET http://foo.bar.com:1234 HTTP/1.1"
-             * That is, there is no path.
-             */
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case '?':
-            state = s_req_query_string_start;
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_PORT);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_path:
-      {
-        if (IS_URL_CHAR(ch)) break;
-
-        switch (ch) {
-          case ' ':
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case CR:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_req_line_almost_done;
-            break;
-          case LF:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_header_field_start;
-            break;
-          case '?':
-            state = s_req_query_string_start;
-            break;
-          case '#':
-            state = s_req_fragment_start;
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_PATH);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_query_string_start:
-      {
-        if (IS_URL_CHAR(ch)) {
-          state = s_req_query_string;
-          break;
-        }
-
-        switch (ch) {
-          case '?':
-            break; /* XXX ignore extra '?' ... is this right? */
-          case ' ':
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case CR:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_req_line_almost_done;
-            break;
-          case LF:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_header_field_start;
-            break;
-          case '#':
-            state = s_req_fragment_start;
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_QUERY_STRING);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_query_string:
-      {
-        if (IS_URL_CHAR(ch)) break;
-
-        switch (ch) {
-          case '?':
-            /* allow extra '?' in query string */
-            break;
-          case ' ':
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case CR:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_req_line_almost_done;
-            break;
-          case LF:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_header_field_start;
-            break;
-          case '#':
-            state = s_req_fragment_start;
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_QUERY_STRING);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_fragment_start:
-      {
-        if (IS_URL_CHAR(ch)) {
-          state = s_req_fragment;
-          break;
-        }
-
-        switch (ch) {
-          case ' ':
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case CR:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_req_line_almost_done;
-            break;
-          case LF:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_header_field_start;
-            break;
-          case '?':
-            state = s_req_fragment;
-            break;
-          case '#':
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_FRAGMENT);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_fragment:
-      {
-        if (IS_URL_CHAR(ch)) break;
-
-        switch (ch) {
-          case ' ':
-            CALLBACK(url);
-            state = s_req_http_start;
-            break;
-          case CR:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_req_line_almost_done;
-            break;
-          case LF:
-            CALLBACK(url);
-            parser->http_major = 0;
-            parser->http_minor = 9;
-            state = s_header_field_start;
-            break;
-          case '?':
-          case '#':
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_FRAGMENT);
-            goto error;
-        }
-        break;
-      }
-
-      case s_req_http_start:
-        switch (ch) {
-          case 'H':
-            state = s_req_http_H;
-            break;
-          case ' ':
-            break;
-          default:
-            SET_ERRNO(HPE_INVALID_CONSTANT);
-            goto error;
-        }
-        break;
-
-      case s_req_http_H:
-        STRICT_CHECK(ch != 'T');
-        state = s_req_http_HT;
-        break;
-
-      case s_req_http_HT:
-        STRICT_CHECK(ch != 'T');
-        state = s_req_http_HTT;
-        break;
-
-      case s_req_http_HTT:
-        STRICT_CHECK(ch != 'P');
-        state = s_req_http_HTTP;
-        break;
-
-      case s_req_http_HTTP:
-        STRICT_CHECK(ch != '/');
-        state = s_req_first_http_major;
-        break;
-
-      /* first digit of major HTTP version */
-      case s_req_first_http_major:
-        if (ch < '1' || ch > '9') {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major = ch - '0';
-        state = s_req_http_major;
-        break;
-
-      /* major HTTP version or dot */
-      case s_req_http_major:
-      {
-        if (ch == '.') {
-          state = s_req_first_http_minor;
-          break;
-        }
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_major *= 10;
-        parser->http_major += ch - '0';
-
-        if (parser->http_major > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* first digit of minor HTTP version */
-      case s_req_first_http_minor:
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor = ch - '0';
-        state = s_req_http_minor;
-        break;
-
-      /* minor HTTP version or end of request line */
-      case s_req_http_minor:
-      {
-        if (ch == CR) {
-          state = s_req_line_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          state = s_header_field_start;
-          break;
-        }
-
-        /* XXX allow spaces after digit? */
-
-        if (!IS_NUM(ch)) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        parser->http_minor *= 10;
-        parser->http_minor += ch - '0';
-
-        if (parser->http_minor > 999) {
-          SET_ERRNO(HPE_INVALID_VERSION);
-          goto error;
-        }
-
-        break;
-      }
-
-      /* end of request line */
-      case s_req_line_almost_done:
-      {
-        if (ch != LF) {
-          SET_ERRNO(HPE_LF_EXPECTED);
-          goto error;
-        }
-
-        state = s_header_field_start;
-        break;
-      }
-
-      case s_header_field_start:
-      header_field_start:
-      {
-        if (ch == CR) {
-          state = s_headers_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          /* they might be just sending \n instead of \r\n so this would be
-           * the second \n to denote the end of headers*/
-          state = s_headers_almost_done;
-          goto headers_almost_done;
-        }
-
-        c = TOKEN(ch);
-
-        if (!c) {
-          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-          goto error;
-        }
-
-        MARK(header_field);
-
-        index = 0;
-        state = s_header_field;
-
-        switch (c) {
-          case 'c':
-            header_state = h_C;
-            break;
-
-          case 'p':
-            header_state = h_matching_proxy_connection;
-            break;
-
-          case 't':
-            header_state = h_matching_transfer_encoding;
-            break;
-
-          case 'u':
-            header_state = h_matching_upgrade;
-            break;
-
-          default:
-            header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_field:
-      {
-        c = TOKEN(ch);
-
-        if (c) {
-          switch (header_state) {
-            case h_general:
-              break;
-
-            case h_C:
-              index++;
-              header_state = (c == 'o' ? h_CO : h_general);
-              break;
-
-            case h_CO:
-              index++;
-              header_state = (c == 'n' ? h_CON : h_general);
-              break;
-
-            case h_CON:
-              index++;
-              switch (c) {
-                case 'n':
-                  header_state = h_matching_connection;
-                  break;
-                case 't':
-                  header_state = h_matching_content_length;
-                  break;
-                default:
-                  header_state = h_general;
-                  break;
-              }
-              break;
-
-            /* connection */
-
-            case h_matching_connection:
-              index++;
-              if (index > sizeof(CONNECTION)-1
-                  || c != CONNECTION[index]) {
-                header_state = h_general;
-              } else if (index == sizeof(CONNECTION)-2) {
-                header_state = h_connection;
-              }
-              break;
-
-            /* proxy-connection */
-
-            case h_matching_proxy_connection:
-              index++;
-              if (index > sizeof(PROXY_CONNECTION)-1
-                  || c != PROXY_CONNECTION[index]) {
-                header_state = h_general;
-              } else if (index == sizeof(PROXY_CONNECTION)-2) {
-                header_state = h_connection;
-              }
-              break;
-
-            /* content-length */
-
-            case h_matching_content_length:
-              index++;
-              if (index > sizeof(CONTENT_LENGTH)-1
-                  || c != CONTENT_LENGTH[index]) {
-                header_state = h_general;
-              } else if (index == sizeof(CONTENT_LENGTH)-2) {
-                header_state = h_content_length;
-              }
-              break;
-
-            /* transfer-encoding */
-
-            case h_matching_transfer_encoding:
-              index++;
-              if (index > sizeof(TRANSFER_ENCODING)-1
-                  || c != TRANSFER_ENCODING[index]) {
-                header_state = h_general;
-              } else if (index == sizeof(TRANSFER_ENCODING)-2) {
-                header_state = h_transfer_encoding;
-              }
-              break;
-
-            /* upgrade */
-
-            case h_matching_upgrade:
-              index++;
-              if (index > sizeof(UPGRADE)-1
-                  || c != UPGRADE[index]) {
-                header_state = h_general;
-              } else if (index == sizeof(UPGRADE)-2) {
-                header_state = h_upgrade;
-              }
-              break;
-
-            case h_connection:
-            case h_content_length:
-            case h_transfer_encoding:
-            case h_upgrade:
-              if (ch != ' ') header_state = h_general;
-              break;
-
-            default:
-              assert(0 && "Unknown header_state");
-              break;
-          }
-          break;
-        }
-
-        if (ch == ':') {
-          CALLBACK(header_field);
-          state = s_header_value_start;
-          break;
-        }
-
-        if (ch == CR) {
-          state = s_header_almost_done;
-          CALLBACK(header_field);
-          break;
-        }
-
-        if (ch == LF) {
-          CALLBACK(header_field);
-          state = s_header_field_start;
-          break;
-        }
-
-        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
-        goto error;
-      }
-
-      case s_header_value_start:
-      {
-        if (ch == ' ' || ch == '\t') break;
-
-        MARK(header_value);
-
-        state = s_header_value;
-        index = 0;
-
-        if (ch == CR) {
-          CALLBACK(header_value);
-          header_state = h_general;
-          state = s_header_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          CALLBACK(header_value);
-          state = s_header_field_start;
-          break;
-        }
-
-        c = LOWER(ch);
-
-        switch (header_state) {
-          case h_upgrade:
-            parser->flags |= F_UPGRADE;
-            header_state = h_general;
-            break;
-
-          case h_transfer_encoding:
-            /* looking for 'Transfer-Encoding: chunked' */
-            if ('c' == c) {
-              header_state = h_matching_transfer_encoding_chunked;
-            } else {
-              header_state = h_general;
-            }
-            break;
-
-          case h_content_length:
-            if (!IS_NUM(ch)) {
-              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-              goto error;
-            }
-
-            parser->content_length = ch - '0';
-            break;
-
-          case h_connection:
-            /* looking for 'Connection: keep-alive' */
-            if (c == 'k') {
-              header_state = h_matching_connection_keep_alive;
-            /* looking for 'Connection: close' */
-            } else if (c == 'c') {
-              header_state = h_matching_connection_close;
-            } else {
-              header_state = h_general;
-            }
-            break;
-
-          default:
-            header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_value:
-      {
-
-        if (ch == CR) {
-          CALLBACK(header_value);
-          state = s_header_almost_done;
-          break;
-        }
-
-        if (ch == LF) {
-          CALLBACK(header_value);
-          goto header_almost_done;
-        }
-
-        c = LOWER(ch);
-
-        switch (header_state) {
-          case h_general:
-            break;
-
-          case h_connection:
-          case h_transfer_encoding:
-            assert(0 && "Shouldn't get here.");
-            break;
-
-          case h_content_length:
-            if (ch == ' ') break;
-            if (!IS_NUM(ch)) {
-              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
-              goto error;
-            }
-
-            parser->content_length *= 10;
-            parser->content_length += ch - '0';
-            break;
-
-          /* Transfer-Encoding: chunked */
-          case h_matching_transfer_encoding_chunked:
-            index++;
-            if (index > sizeof(CHUNKED)-1
-                || c != CHUNKED[index]) {
-              header_state = h_general;
-            } else if (index == sizeof(CHUNKED)-2) {
-              header_state = h_transfer_encoding_chunked;
-            }
-            break;
-
-          /* looking for 'Connection: keep-alive' */
-          case h_matching_connection_keep_alive:
-            index++;
-            if (index > sizeof(KEEP_ALIVE)-1
-                || c != KEEP_ALIVE[index]) {
-              header_state = h_general;
-            } else if (index == sizeof(KEEP_ALIVE)-2) {
-              header_state = h_connection_keep_alive;
-            }
-            break;
-
-          /* looking for 'Connection: close' */
-          case h_matching_connection_close:
-            index++;
-            if (index > sizeof(CLOSE)-1 || c != CLOSE[index]) {
-              header_state = h_general;
-            } else if (index == sizeof(CLOSE)-2) {
-              header_state = h_connection_close;
-            }
-            break;
-
-          case h_transfer_encoding_chunked:
-          case h_connection_keep_alive:
-          case h_connection_close:
-            if (ch != ' ') header_state = h_general;
-            break;
-
-          default:
-            state = s_header_value;
-            header_state = h_general;
-            break;
-        }
-        break;
-      }
-
-      case s_header_almost_done:
-      header_almost_done:
-      {
-        STRICT_CHECK(ch != LF);
-
-        state = s_header_value_lws;
-
-        switch (header_state) {
-          case h_connection_keep_alive:
-            parser->flags |= F_CONNECTION_KEEP_ALIVE;
-            break;
-          case h_connection_close:
-            parser->flags |= F_CONNECTION_CLOSE;
-            break;
-          case h_transfer_encoding_chunked:
-            parser->flags |= F_CHUNKED;
-            break;
-          default:
-            break;
-        }
-        break;
-      }
-
-      case s_header_value_lws:
-      {
-        if (ch == ' ' || ch == '\t')
-          state = s_header_value_start;
-        else
-        {
-          state = s_header_field_start;
-          goto header_field_start;
-        }
-        break;
-      }
-
-      case s_headers_almost_done:
-      headers_almost_done:
-      {
-        STRICT_CHECK(ch != LF);
-
-        if (parser->flags & F_TRAILING) {
-          /* End of a chunked request */
-          CALLBACK2(message_complete);
-          state = NEW_MESSAGE();
-          break;
-        }
-
-        nread = 0;
-
-        if (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT) {
-          parser->upgrade = 1;
-        }
-
-        /* Here we call the headers_complete callback. This is somewhat
-         * different than other callbacks because if the user returns 1, we
-         * will interpret that as saying that this message has no body. This
-         * is needed for the annoying case of recieving a response to a HEAD
-         * request.
-         */
-        if (settings->on_headers_complete) {
-          switch (settings->on_headers_complete(parser)) {
-            case 0:
-              break;
-
-            case 1:
-              parser->flags |= F_SKIPBODY;
-              break;
-
-            default:
-              parser->state = state;
-              SET_ERRNO(HPE_CB_headers_complete);
-              return p - data; /* Error */
-          }
-        }
-
-        /* Exit, the rest of the connect is in a different protocol. */
-        if (parser->upgrade) {
-          CALLBACK2(message_complete);
-          return (p - data) + 1;
-        }
-
-        if (parser->flags & F_SKIPBODY) {
-          CALLBACK2(message_complete);
-          state = NEW_MESSAGE();
-        } else if (parser->flags & F_CHUNKED) {
-          /* chunked encoding - ignore Content-Length header */
-          state = s_chunk_size_start;
-        } else {
-          if (parser->content_length == 0) {
-            /* Content-Length header given but zero: Content-Length: 0\r\n */
-            CALLBACK2(message_complete);
-            state = NEW_MESSAGE();
-          } else if (parser->content_length > 0) {
-            /* Content-Length header given and non-zero */
-            state = s_body_identity;
-          } else {
-            if (parser->type == HTTP_REQUEST || http_should_keep_alive(parser)) {
-              /* Assume content-length 0 - read the next */
-              CALLBACK2(message_complete);
-              state = NEW_MESSAGE();
-            } else {
-              /* Read body until EOF */
-              state = s_body_identity_eof;
-            }
-          }
-        }
-
-        break;
-      }
-
-      case s_body_identity:
-        to_read = (size_t)MIN(pe - p, parser->content_length);
-        if (to_read > 0) {
-          if (settings->on_body) settings->on_body(parser, p, to_read);
-          p += to_read - 1;
-          parser->content_length -= to_read;
-          if (parser->content_length == 0) {
-            CALLBACK2(message_complete);
-            state = NEW_MESSAGE();
-          }
-        }
-        break;
-
-      /* read until EOF */
-      case s_body_identity_eof:
-        to_read = pe - p;
-        if (to_read > 0) {
-          if (settings->on_body) settings->on_body(parser, p, to_read);
-          p += to_read - 1;
-        }
-        break;
-
-      case s_chunk_size_start:
-      {
-        assert(nread == 1);
-        assert(parser->flags & F_CHUNKED);
-
-        unhex_val = unhex[(unsigned char)ch];
-        if (unhex_val == -1) {
-          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-          goto error;
-        }
-
-        parser->content_length = unhex_val;
-        state = s_chunk_size;
-        break;
-      }
-
-      case s_chunk_size:
-      {
-        assert(parser->flags & F_CHUNKED);
-
-        if (ch == CR) {
-          state = s_chunk_size_almost_done;
-          break;
-        }
-
-        unhex_val = unhex[(unsigned char)ch];
-
-        if (unhex_val == -1) {
-          if (ch == ';' || ch == ' ') {
-            state = s_chunk_parameters;
-            break;
-          }
-
-          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
-          goto error;
-        }
-
-        parser->content_length *= 16;
-        parser->content_length += unhex_val;
-        break;
-      }
-
-      case s_chunk_parameters:
-      {
-        assert(parser->flags & F_CHUNKED);
-        /* just ignore this shit. TODO check for overflow */
-        if (ch == CR) {
-          state = s_chunk_size_almost_done;
-          break;
-        }
-        break;
-      }
-
-      case s_chunk_size_almost_done:
-      {
-        assert(parser->flags & F_CHUNKED);
-        STRICT_CHECK(ch != LF);
-
-        nread = 0;
-
-        if (parser->content_length == 0) {
-          parser->flags |= F_TRAILING;
-          state = s_header_field_start;
-        } else {
-          state = s_chunk_data;
-        }
-        break;
-      }
-
-      case s_chunk_data:
-      {
-        assert(parser->flags & F_CHUNKED);
-
-        to_read = (size_t)MIN(pe - p, parser->content_length);
-
-        if (to_read > 0) {
-          if (settings->on_body) settings->on_body(parser, p, to_read);
-          p += to_read - 1;
-        }
-
-        if ((signed)to_read == parser->content_length) {
-          state = s_chunk_data_almost_done;
-        }
-
-        parser->content_length -= to_read;
-        break;
-      }
-
-      case s_chunk_data_almost_done:
-        assert(parser->flags & F_CHUNKED);
-        STRICT_CHECK(ch != CR);
-        state = s_chunk_data_done;
-        break;
-
-      case s_chunk_data_done:
-        assert(parser->flags & F_CHUNKED);
-        STRICT_CHECK(ch != LF);
-        state = s_chunk_size_start;
-        break;
-
-      default:
-        assert(0 && "unhandled state");
-        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
-        goto error;
-    }
-  }
-
-  CALLBACK(header_field);
-  CALLBACK(header_value);
-  CALLBACK(url);
-
-  parser->state = state;
-  parser->header_state = header_state;
-  parser->index = (unsigned char) index;
-  parser->nread = nread;
-
-  return len;
-
-error:
-  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
-    SET_ERRNO(HPE_UNKNOWN);
-  }
-
-  return (p - data);
-}
-
-
-int
-http_should_keep_alive (http_parser *parser)
-{
-  if (parser->http_major > 0 && parser->http_minor > 0) {
-    /* HTTP/1.1 */
-    if (parser->flags & F_CONNECTION_CLOSE) {
-      return 0;
-    } else {
-      return 1;
-    }
-  } else {
-    /* HTTP/1.0 or earlier */
-    if (parser->flags & F_CONNECTION_KEEP_ALIVE) {
-      return 1;
-    } else {
-      return 0;
-    }
-  }
-}
-
-
-const char * http_method_str (enum http_method m)
-{
-  return method_strings[m];
-}
-
-
-void
-http_parser_init (http_parser *parser, enum http_parser_type t)
-{
-  parser->type = t;
-  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
-  parser->nread = 0;
-  parser->upgrade = 0;
-  parser->flags = 0;
-  parser->method = 0;
-  parser->http_errno = 0;
-}
-
-const char *
-http_errno_name(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
-  return http_strerror_tab[err].name;
-}
-
-const char *
-http_errno_description(enum http_errno err) {
-  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
-  return http_strerror_tab[err].description;
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i)                                                \
+  (!!((unsigned int) (a)[(unsigned int) (i) >> 3] &                  \
+   (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e)                                                 \
+do {                                                                 \
+  parser->http_errno = (e);                                          \
+} while(0)
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER)                                    \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (settings->on_##FOR) {                                          \
+    if (0 != settings->on_##FOR(parser)) {                           \
+      SET_ERRNO(HPE_CB_##FOR);                                       \
+    }                                                                \
+                                                                     \
+    /* We either errored above or got paused; get out */             \
+    if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                       \
+      return (ER);                                                   \
+    }                                                                \
+  }                                                                  \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR)            CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR)  CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER)                                 \
+do {                                                                 \
+  assert(HTTP_PARSER_ERRNO(parser) == HPE_OK);                       \
+                                                                     \
+  if (FOR##_mark) {                                                  \
+    if (settings->on_##FOR) {                                        \
+      if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) {      \
+        SET_ERRNO(HPE_CB_##FOR);                                     \
+      }                                                              \
+                                                                     \
+      /* We either errored above or got paused; get out */           \
+      if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {                     \
+        return (ER);                                                 \
+      }                                                              \
+    }                                                                \
+    FOR##_mark = NULL;                                               \
+  }                                                                  \
+} while (0)
+  
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR)                                           \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR)                                 \
+    CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR)                                                    \
+do {                                                                 \
+  if (!FOR##_mark) {                                                 \
+    FOR##_mark = p;                                                  \
+  }                                                                  \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+  {
+#define XX(num, name, string) #string,
+  HTTP_METHOD_MAP(XX)
+#undef XX
+  };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ *        token       = 1*<any CHAR except CTLs or separators>
+ *     separators     = "(" | ")" | "<" | ">" | "@"
+ *                    | "," | ";" | ":" | "\" | <">
+ *                    | "/" | "[" | "]" | "?" | "="
+ *                    | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0,       0,       0,       0,       0,       0,       0,       0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0,      '!',      0,      '#',     '$',     '%',     '&',    '\'',
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        0,       0,      '*',     '+',      0,      '-',     '.',      0,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+       '0',     '1',     '2',     '3',     '4',     '5',     '6',     '7',
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+       '8',     '9',      0,       0,       0,       0,       0,       0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        0,      'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+       'x',     'y',     'z',      0,       0,       0,      '^',     '_',
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+       '`',     'a',     'b',     'c',     'd',     'e',     'f',     'g',
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+       'h',     'i',     'j',     'k',     'l',     'm',     'n',     'o',
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+       'p',     'q',     'r',     's',     't',     'u',     'v',     'w',
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+       'x',     'y',     'z',      0,      '|',      0,      '~',       0 };
+
+
+static const int8_t unhex[256] =
+  {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+  };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/*   0 nul    1 soh    2 stx    3 etx    4 eot    5 enq    6 ack    7 bel  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*   8 bs     9 ht    10 nl    11 vt    12 np    13 cr    14 so    15 si   */
+        0    | T(2)   |   0    |   0    | T(16)  |   0    |   0    |   0,
+/*  16 dle   17 dc1   18 dc2   19 dc3   20 dc4   21 nak   22 syn   23 etb */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  24 can   25 em    26 sub   27 esc   28 fs    29 gs    30 rs    31 us  */
+        0    |   0    |   0    |   0    |   0    |   0    |   0    |   0,
+/*  32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '  */
+        0    |   2    |   4    |   0    |   16   |   32   |   64   |  128,
+/*  40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0,
+/*  64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/*  96  `    97  a    98  b    99  c   100  d   101  e   102  f   103  g  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 104  h   105  i   106  j   107  k   108  l   109  m   110  n   111  o  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 112  p   113  q   114  r   115  s   116  t   117  u   118  v   119  w  */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |  128,
+/* 120  x   121  y   122  z   123  {   124  |   125  }   126  ~   127 del */
+        1    |   2    |   4    |   8    |   16   |   32   |   64   |   0, };
+
+#undef T
+
+enum state
+  { s_dead = 1 /* important that this is > 0 */
+
+  , s_start_req_or_res
+  , s_res_or_resp_H
+  , s_start_res
+  , s_res_H
+  , s_res_HT
+  , s_res_HTT
+  , s_res_HTTP
+  , s_res_first_http_major
+  , s_res_http_major
+  , s_res_first_http_minor
+  , s_res_http_minor
+  , s_res_first_status_code
+  , s_res_status_code
+  , s_res_status
+  , s_res_line_almost_done
+
+  , s_start_req
+
+  , s_req_method
+  , s_req_spaces_before_url
+  , s_req_schema
+  , s_req_schema_slash
+  , s_req_schema_slash_slash
+  , s_req_server_start
+  , s_req_server
+  , s_req_server_with_at
+  , s_req_path
+  , s_req_query_string_start
+  , s_req_query_string
+  , s_req_fragment_start
+  , s_req_fragment
+  , s_req_http_start
+  , s_req_http_H
+  , s_req_http_HT
+  , s_req_http_HTT
+  , s_req_http_HTTP
+  , s_req_first_http_major
+  , s_req_http_major
+  , s_req_first_http_minor
+  , s_req_http_minor
+  , s_req_line_almost_done
+
+  , s_header_field_start
+  , s_header_field
+  , s_header_value_start
+  , s_header_value
+  , s_header_value_lws
+
+  , s_header_almost_done
+
+  , s_chunk_size_start
+  , s_chunk_size
+  , s_chunk_parameters
+  , s_chunk_size_almost_done
+
+  , s_headers_almost_done
+  , s_headers_done
+
+  /* Important: 's_headers_done' must be the last 'header' state. All
+   * states beyond this must be 'body' states. It is used for overflow
+   * checking. See the PARSING_HEADER() macro.
+   */
+
+  , s_chunk_data
+  , s_chunk_data_almost_done
+  , s_chunk_data_done
+
+  , s_body_identity
+  , s_body_identity_eof
+
+  , s_message_done
+  };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+  { h_general = 0
+  , h_C
+  , h_CO
+  , h_CON
+
+  , h_matching_connection
+  , h_matching_proxy_connection
+  , h_matching_content_length
+  , h_matching_transfer_encoding
+  , h_matching_upgrade
+
+  , h_connection
+  , h_content_length
+  , h_transfer_encoding
+  , h_upgrade
+
+  , h_matching_transfer_encoding_chunked
+  , h_matching_connection_keep_alive
+  , h_matching_connection_close
+
+  , h_transfer_encoding_chunked
+  , h_connection_keep_alive
+  , h_connection_close
+  };
+
+enum http_host_state
+  {
+    s_http_host_dead = 1
+  , s_http_userinfo_start
+  , s_http_userinfo
+  , s_http_host_start
+  , s_http_host_v6_start
+  , s_http_host
+  , s_http_host_v6
+  , s_http_host_v6_end
+  , s_http_host_port_start
+  , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode  */
+#define CR                  '\r'
+#define LF                  '\n'
+#define LOWER(c)            (unsigned char)(c | 0x20)
+#define IS_ALPHA(c)         (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c)           ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c)      (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c)           (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c)          ((c) == '-' || (c) == '_' || (c) == '.' || \
+  (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+  (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+  (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+  (c) == '$' || (c) == ',')
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c)            (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c)      (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c)     (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c)            ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c)                                                         \
+  (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c)                                                        \
+  (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond)                                          \
+do {                                                                 \
+  if (cond) {                                                        \
+    SET_ERRNO(HPE_STRICT);                                           \
+    goto error;                                                      \
+  }                                                                  \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+  const char *name;
+  const char *description;
+} http_strerror_tab[] = {
+  HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+  if (ch == ' ' || ch == '\r' || ch == '\n') {
+    return s_dead;
+  }
+
+#if HTTP_PARSER_STRICT
+  if (ch == '\t' || ch == '\f') {
+    return s_dead;
+  }
+#endif
+
+  switch (s) {
+    case s_req_spaces_before_url:
+      /* Proxied requests are followed by scheme of an absolute URI (alpha).
+       * All methods except CONNECT are followed by '/' or '*'.
+       */
+
+      if (ch == '/' || ch == '*') {
+        return s_req_path;
+      }
+
+      if (IS_ALPHA(ch)) {
+        return s_req_schema;
+      }
+
+      break;
+
+    case s_req_schema:
+      if (IS_ALPHA(ch)) {
+        return s;
+      }
+
+      if (ch == ':') {
+        return s_req_schema_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash:
+      if (ch == '/') {
+        return s_req_schema_slash_slash;
+      }
+
+      break;
+
+    case s_req_schema_slash_slash:
+      if (ch == '/') {
+        return s_req_server_start;
+      }
+
+      break;
+
+    case s_req_server_with_at:
+      if (ch == '@') {
+        return s_dead;
+      }
+
+    /* FALLTHROUGH */
+    case s_req_server_start:
+    case s_req_server:
+      if (ch == '/') {
+        return s_req_path;
+      }
+
+      if (ch == '?') {
+        return s_req_query_string_start;
+      }
+
+      if (ch == '@') {
+        return s_req_server_with_at;
+      }
+
+      if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+        return s_req_server;
+      }
+
+      break;
+
+    case s_req_path:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_query_string_start;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_query_string_start:
+    case s_req_query_string:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_query_string;
+      }
+
+      switch (ch) {
+        case '?':
+          /* allow extra '?' in query string */
+          return s_req_query_string;
+
+        case '#':
+          return s_req_fragment_start;
+      }
+
+      break;
+
+    case s_req_fragment_start:
+      if (IS_URL_CHAR(ch)) {
+        return s_req_fragment;
+      }
+
+      switch (ch) {
+        case '?':
+          return s_req_fragment;
+
+        case '#':
+          return s;
+      }
+
+      break;
+
+    case s_req_fragment:
+      if (IS_URL_CHAR(ch)) {
+        return s;
+      }
+
+      switch (ch) {
+        case '?':
+        case '#':
+          return s;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+
+  /* We should never fall out of the switch above unless there's an error */
+  return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+                            const http_parser_settings *settings,
+                            const char *data,
+                            size_t len)
+{
+  char c, ch;
+  int8_t unhex_val;
+  const char *p = data;
+  const char *header_field_mark = 0;
+  const char *header_value_mark = 0;
+  const char *url_mark = 0;
+  const char *body_mark = 0;
+
+  /* We're in an error state. Don't bother doing anything. */
+  if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+    return 0;
+  }
+
+  if (len == 0) {
+    switch (parser->state) {
+      case s_body_identity_eof:
+        /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+         * we got paused.
+         */
+        CALLBACK_NOTIFY_NOADVANCE(message_complete);
+        return 0;
+
+      case s_dead:
+      case s_start_req_or_res:
+      case s_start_res:
+      case s_start_req:
+        return 0;
+
+      default:
+        SET_ERRNO(HPE_INVALID_EOF_STATE);
+        return 1;
+    }
+  }
+
+
+  if (parser->state == s_header_field)
+    header_field_mark = data;
+  if (parser->state == s_header_value)
+    header_value_mark = data;
+  switch (parser->state) {
+  case s_req_path:
+  case s_req_schema:
+  case s_req_schema_slash:
+  case s_req_schema_slash_slash:
+  case s_req_server_start:
+  case s_req_server:
+  case s_req_server_with_at:
+  case s_req_query_string_start:
+  case s_req_query_string:
+  case s_req_fragment_start:
+  case s_req_fragment:
+    url_mark = data;
+    break;
+  }
+
+  for (p=data; p != data + len; p++) {
+    ch = *p;
+
+    if (PARSING_HEADER(parser->state)) {
+      ++parser->nread;
+      /* Buffer overflow attack */
+      if (parser->nread > HTTP_MAX_HEADER_SIZE) {
+        SET_ERRNO(HPE_HEADER_OVERFLOW);
+        goto error;
+      }
+    }
+
+    reexecute_byte:
+    switch (parser->state) {
+
+      case s_dead:
+        /* this state is used after a 'Connection: close' message
+         * the parser will error out if it reads another message
+         */
+        if (ch == CR || ch == LF)
+          break;
+
+        SET_ERRNO(HPE_CLOSED_CONNECTION);
+        goto error;
+
+      case s_start_req_or_res:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (ch == 'H') {
+          parser->state = s_res_or_resp_H;
+
+          CALLBACK_NOTIFY(message_begin);
+        } else {
+          parser->type = HTTP_REQUEST;
+          parser->state = s_start_req;
+          goto reexecute_byte;
+        }
+
+        break;
+      }
+
+      case s_res_or_resp_H:
+        if (ch == 'T') {
+          parser->type = HTTP_RESPONSE;
+          parser->state = s_res_HT;
+        } else {
+          if (ch != 'E') {
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+          }
+
+          parser->type = HTTP_REQUEST;
+          parser->method = HTTP_HEAD;
+          parser->index = 2;
+          parser->state = s_req_method;
+        }
+        break;
+
+      case s_start_res:
+      {
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        switch (ch) {
+          case 'H':
+            parser->state = s_res_H;
+            break;
+
+          case CR:
+          case LF:
+            break;
+
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+
+        CALLBACK_NOTIFY(message_begin);
+        break;
+      }
+
+      case s_res_H:
+        STRICT_CHECK(ch != 'T');
+        parser->state = s_res_HT;
+        break;
+
+      case s_res_HT:
+        STRICT_CHECK(ch != 'T');
+        parser->state = s_res_HTT;
+        break;
+
+      case s_res_HTT:
+        STRICT_CHECK(ch != 'P');
+        parser->state = s_res_HTTP;
+        break;
+
+      case s_res_HTTP:
+        STRICT_CHECK(ch != '/');
+        parser->state = s_res_first_http_major;
+        break;
+
+      case s_res_first_http_major:
+        if (ch < '0' || ch > '9') {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        parser->state = s_res_http_major;
+        break;
+
+      /* major HTTP version or dot */
+      case s_res_http_major:
+      {
+        if (ch == '.') {
+          parser->state = s_res_first_http_minor;
+          break;
+        }
+
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major *= 10;
+        parser->http_major += ch - '0';
+
+        if (parser->http_major > 999) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* first digit of minor HTTP version */
+      case s_res_first_http_minor:
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        parser->state = s_res_http_minor;
+        break;
+
+      /* minor HTTP version or end of request line */
+      case s_res_http_minor:
+      {
+        if (ch == ' ') {
+          parser->state = s_res_first_status_code;
+          break;
+        }
+
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor *= 10;
+        parser->http_minor += ch - '0';
+
+        if (parser->http_minor > 999) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_first_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          if (ch == ' ') {
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+        parser->status_code = ch - '0';
+        parser->state = s_res_status_code;
+        break;
+      }
+
+      case s_res_status_code:
+      {
+        if (!IS_NUM(ch)) {
+          switch (ch) {
+            case ' ':
+              parser->state = s_res_status;
+              break;
+            case CR:
+              parser->state = s_res_line_almost_done;
+              break;
+            case LF:
+              parser->state = s_header_field_start;
+              break;
+            default:
+              SET_ERRNO(HPE_INVALID_STATUS);
+              goto error;
+          }
+          break;
+        }
+
+        parser->status_code *= 10;
+        parser->status_code += ch - '0';
+
+        if (parser->status_code > 999) {
+          SET_ERRNO(HPE_INVALID_STATUS);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_res_status:
+        /* the human readable status. e.g. "NOT FOUND"
+         * we are not humans so just ignore this */
+        if (ch == CR) {
+          parser->state = s_res_line_almost_done;
+          break;
+        }
+
+        if (ch == LF) {
+          parser->state = s_header_field_start;
+          break;
+        }
+        break;
+
+      case s_res_line_almost_done:
+        STRICT_CHECK(ch != LF);
+        parser->state = s_header_field_start;
+        break;
+
+      case s_start_req:
+      {
+        if (ch == CR || ch == LF)
+          break;
+        parser->flags = 0;
+        parser->content_length = ULLONG_MAX;
+
+        if (!IS_ALPHA(ch)) {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        parser->method = (enum http_method) 0;
+        parser->index = 1;
+        switch (ch) {
+          case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+          case 'D': parser->method = HTTP_DELETE; break;
+          case 'G': parser->method = HTTP_GET; break;
+          case 'H': parser->method = HTTP_HEAD; break;
+          case 'L': parser->method = HTTP_LOCK; break;
+          case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break;
+          case 'N': parser->method = HTTP_NOTIFY; break;
+          case 'O': parser->method = HTTP_OPTIONS; break;
+          case 'P': parser->method = HTTP_POST;
+            /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+            break;
+          case 'R': parser->method = HTTP_REPORT; break;
+          case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
+          case 'T': parser->method = HTTP_TRACE; break;
+          case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break;
+          default:
+            SET_ERRNO(HPE_INVALID_METHOD);
+            goto error;
+        }
+        parser->state = s_req_method;
+
+        CALLBACK_NOTIFY(message_begin);
+
+        break;
+      }
+
+      case s_req_method:
+      {
+        const char *matcher;
+        if (ch == '\0') {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        matcher = method_strings[parser->method];
+        if (ch == ' ' && matcher[parser->index] == '\0') {
+          parser->state = s_req_spaces_before_url;
+        } else if (ch == matcher[parser->index]) {
+          ; /* nada */
+        } else if (parser->method == HTTP_CONNECT) {
+          if (parser->index == 1 && ch == 'H') {
+            parser->method = HTTP_CHECKOUT;
+          } else if (parser->index == 2  && ch == 'P') {
+            parser->method = HTTP_COPY;
+          } else {
+            goto error;
+          }
+        } else if (parser->method == HTTP_MKCOL) {
+          if (parser->index == 1 && ch == 'O') {
+            parser->method = HTTP_MOVE;
+          } else if (parser->index == 1 && ch == 'E') {
+            parser->method = HTTP_MERGE;
+          } else if (parser->index == 1 && ch == '-') {
+            parser->method = HTTP_MSEARCH;
+          } else if (parser->index == 2 && ch == 'A') {
+            parser->method = HTTP_MKACTIVITY;
+          } else {
+            goto error;
+          }
+        } else if (parser->method == HTTP_SUBSCRIBE) {
+          if (parser->index == 1 && ch == 'E') {
+            parser->method = HTTP_SEARCH;
+          } else {
+            goto error;
+          }
+        } else if (parser->index == 1 && parser->method == HTTP_POST) {
+          if (ch == 'R') {
+            parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */
+          } else if (ch == 'U') {
+            parser->method = HTTP_PUT; /* or HTTP_PURGE */
+          } else if (ch == 'A') {
+            parser->method = HTTP_PATCH;
+          } else {
+            goto error;
+          }
+        } else if (parser->index == 2) {
+          if (parser->method == HTTP_PUT) {
+            if (ch == 'R') parser->method = HTTP_PURGE;
+          } else if (parser->method == HTTP_UNLOCK) {
+            if (ch == 'S') parser->method = HTTP_UNSUBSCRIBE;
+          }
+        } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') {
+          parser->method = HTTP_PROPPATCH;
+        } else {
+          SET_ERRNO(HPE_INVALID_METHOD);
+          goto error;
+        }
+
+        ++parser->index;
+        break;
+      }
+
+      case s_req_spaces_before_url:
+      {
+        if (ch == ' ') break;
+
+        MARK(url);
+        if (parser->method == HTTP_CONNECT) {
+          parser->state = s_req_server_start;
+        }
+
+        parser->state = parse_url_char((enum state)parser->state, ch);
+        if (parser->state == s_dead) {
+          SET_ERRNO(HPE_INVALID_URL);
+          goto error;
+        }
+
+        break;
+      }
+
+      case s_req_schema:
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      {
+        switch (ch) {
+          /* No whitespace allowed here */
+          case ' ':
+          case CR:
+          case LF:
+            SET_ERRNO(HPE_INVALID_URL);
+            goto error;
+          default:
+            parser->state = parse_url_char((enum state)parser->state, ch);
+            if (parser->state == s_dead) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+
+        break;
+      }
+
+      case s_req_server:
+      case s_req_server_with_at:
+      case s_req_path:
+      case s_req_query_string_start:
+      case s_req_query_string:
+      case s_req_fragment_start:
+      case s_req_fragment:
+      {
+        switch (ch) {
+          case ' ':
+            parser->state = s_req_http_start;
+            CALLBACK_DATA(url);
+            break;
+          case CR:
+          case LF:
+            parser->http_major = 0;
+            parser->http_minor = 9;
+            parser->state = (ch == CR) ?
+              s_req_line_almost_done :
+              s_header_field_start;
+            CALLBACK_DATA(url);
+            break;
+          default:
+            parser->state = parse_url_char((enum state)parser->state, ch);
+            if (parser->state == s_dead) {
+              SET_ERRNO(HPE_INVALID_URL);
+              goto error;
+            }
+        }
+        break;
+      }
+
+      case s_req_http_start:
+        switch (ch) {
+          case 'H':
+            parser->state = s_req_http_H;
+            break;
+          case ' ':
+            break;
+          default:
+            SET_ERRNO(HPE_INVALID_CONSTANT);
+            goto error;
+        }
+        break;
+
+      case s_req_http_H:
+        STRICT_CHECK(ch != 'T');
+        parser->state = s_req_http_HT;
+        break;
+
+      case s_req_http_HT:
+        STRICT_CHECK(ch != 'T');
+        parser->state = s_req_http_HTT;
+        break;
+
+      case s_req_http_HTT:
+        STRICT_CHECK(ch != 'P');
+        parser->state = s_req_http_HTTP;
+        break;
+
+      case s_req_http_HTTP:
+        STRICT_CHECK(ch != '/');
+        parser->state = s_req_first_http_major;
+        break;
+
+      /* first digit of major HTTP version */
+      case s_req_first_http_major:
+        if (ch < '1' || ch > '9') {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major = ch - '0';
+        parser->state = s_req_http_major;
+        break;
+
+      /* major HTTP version or dot */
+      case s_req_http_major:
+      {
+        if (ch == '.') {
+          parser->state = s_req_first_http_minor;
+          break;
+        }
+
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_major *= 10;
+        parser->http_major += ch - '0';
+
+        if (parser->http_major > 999) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* first digit of minor HTTP version */
+      case s_req_first_http_minor:
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor = ch - '0';
+        parser->state = s_req_http_minor;
+        break;
+
+      /* minor HTTP version or end of request line */
+      case s_req_http_minor:
+      {
+        if (ch == CR) {
+          parser->state = s_req_line_almost_done;
+          break;
+        }
+
+        if (ch == LF) {
+          parser->state = s_header_field_start;
+          break;
+        }
+
+        /* XXX allow spaces after digit? */
+
+        if (!IS_NUM(ch)) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        parser->http_minor *= 10;
+        parser->http_minor += ch - '0';
+
+        if (parser->http_minor > 999) {
+          SET_ERRNO(HPE_INVALID_VERSION);
+          goto error;
+        }
+
+        break;
+      }
+
+      /* end of request line */
+      case s_req_line_almost_done:
+      {
+        if (ch != LF) {
+          SET_ERRNO(HPE_LF_EXPECTED);
+          goto error;
+        }
+
+        parser->state = s_header_field_start;
+        break;
+      }
+
+      case s_header_field_start:
+      {
+        if (ch == CR) {
+          parser->state = s_headers_almost_done;
+          break;
+        }
+
+        if (ch == LF) {
+          /* they might be just sending \n instead of \r\n so this would be
+           * the second \n to denote the end of headers*/
+          parser->state = s_headers_almost_done;
+          goto reexecute_byte;
+        }
+
+        c = TOKEN(ch);
+
+        if (!c) {
+          SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+          goto error;
+        }
+
+        MARK(header_field);
+
+        parser->index = 0;
+        parser->state = s_header_field;
+
+        switch (c) {
+          case 'c':
+            parser->header_state = h_C;
+            break;
+
+          case 'p':
+            parser->header_state = h_matching_proxy_connection;
+            break;
+
+          case 't':
+            parser->header_state = h_matching_transfer_encoding;
+            break;
+
+          case 'u':
+            parser->header_state = h_matching_upgrade;
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_field:
+      {
+        c = TOKEN(ch);
+
+        if (c) {
+          switch (parser->header_state) {
+            case h_general:
+              break;
+
+            case h_C:
+              parser->index++;
+              parser->header_state = (c == 'o' ? h_CO : h_general);
+              break;
+
+            case h_CO:
+              parser->index++;
+              parser->header_state = (c == 'n' ? h_CON : h_general);
+              break;
+
+            case h_CON:
+              parser->index++;
+              switch (c) {
+                case 'n':
+                  parser->header_state = h_matching_connection;
+                  break;
+                case 't':
+                  parser->header_state = h_matching_content_length;
+                  break;
+                default:
+                  parser->header_state = h_general;
+                  break;
+              }
+              break;
+
+            /* connection */
+
+            case h_matching_connection:
+              parser->index++;
+              if (parser->index > sizeof(CONNECTION)-1
+                  || c != CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* proxy-connection */
+
+            case h_matching_proxy_connection:
+              parser->index++;
+              if (parser->index > sizeof(PROXY_CONNECTION)-1
+                  || c != PROXY_CONNECTION[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+                parser->header_state = h_connection;
+              }
+              break;
+
+            /* content-length */
+
+            case h_matching_content_length:
+              parser->index++;
+              if (parser->index > sizeof(CONTENT_LENGTH)-1
+                  || c != CONTENT_LENGTH[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+                parser->header_state = h_content_length;
+              }
+              break;
+
+            /* transfer-encoding */
+
+            case h_matching_transfer_encoding:
+              parser->index++;
+              if (parser->index > sizeof(TRANSFER_ENCODING)-1
+                  || c != TRANSFER_ENCODING[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+                parser->header_state = h_transfer_encoding;
+              }
+              break;
+
+            /* upgrade */
+
+            case h_matching_upgrade:
+              parser->index++;
+              if (parser->index > sizeof(UPGRADE)-1
+                  || c != UPGRADE[parser->index]) {
+                parser->header_state = h_general;
+              } else if (parser->index == sizeof(UPGRADE)-2) {
+                parser->header_state = h_upgrade;
+              }
+              break;
+
+            case h_connection:
+            case h_content_length:
+            case h_transfer_encoding:
+            case h_upgrade:
+              if (ch != ' ') parser->header_state = h_general;
+              break;
+
+            default:
+              assert(0 && "Unknown header_state");
+              break;
+          }
+          break;
+        }
+
+        if (ch == ':') {
+          parser->state = s_header_value_start;
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        if (ch == CR) {
+          parser->state = s_header_almost_done;
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        if (ch == LF) {
+          parser->state = s_header_field_start;
+          CALLBACK_DATA(header_field);
+          break;
+        }
+
+        SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+        goto error;
+      }
+
+      case s_header_value_start:
+      {
+        if (ch == ' ' || ch == '\t') break;
+
+        MARK(header_value);
+
+        parser->state = s_header_value;
+        parser->index = 0;
+
+        if (ch == CR) {
+          parser->header_state = h_general;
+          parser->state = s_header_almost_done;
+          CALLBACK_DATA(header_value);
+          break;
+        }
+
+        if (ch == LF) {
+          parser->state = s_header_field_start;
+          CALLBACK_DATA(header_value);
+          break;
+        }
+
+        c = LOWER(ch);
+
+        switch (parser->header_state) {
+          case h_upgrade:
+            parser->flags |= F_UPGRADE;
+            parser->header_state = h_general;
+            break;
+
+          case h_transfer_encoding:
+            /* looking for 'Transfer-Encoding: chunked' */
+            if ('c' == c) {
+              parser->header_state = h_matching_transfer_encoding_chunked;
+            } else {
+              parser->header_state = h_general;
+            }
+            break;
+
+          case h_content_length:
+            if (!IS_NUM(ch)) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            parser->content_length = ch - '0';
+            break;
+
+          case h_connection:
+            /* looking for 'Connection: keep-alive' */
+            if (c == 'k') {
+              parser->header_state = h_matching_connection_keep_alive;
+            /* looking for 'Connection: close' */
+            } else if (c == 'c') {
+              parser->header_state = h_matching_connection_close;
+            } else {
+              parser->header_state = h_general;
+            }
+            break;
+
+          default:
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_value:
+      {
+
+        if (ch == CR) {
+          parser->state = s_header_almost_done;
+          CALLBACK_DATA(header_value);
+          break;
+        }
+
+        if (ch == LF) {
+          parser->state = s_header_almost_done;
+          CALLBACK_DATA_NOADVANCE(header_value);
+          goto reexecute_byte;
+        }
+
+        c = LOWER(ch);
+
+        switch (parser->header_state) {
+          case h_general:
+            break;
+
+          case h_connection:
+          case h_transfer_encoding:
+            assert(0 && "Shouldn't get here.");
+            break;
+
+          case h_content_length:
+          {
+            uint64_t t;
+
+            if (ch == ' ') break;
+
+            if (!IS_NUM(ch)) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            t = parser->content_length;
+            t *= 10;
+            t += ch - '0';
+
+            /* Overflow? */
+            if (t < parser->content_length || t == ULLONG_MAX) {
+              SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+              goto error;
+            }
+
+            parser->content_length = t;
+            break;
+          }
+
+          /* Transfer-Encoding: chunked */
+          case h_matching_transfer_encoding_chunked:
+            parser->index++;
+            if (parser->index > sizeof(CHUNKED)-1
+                || c != CHUNKED[parser->index]) {
+              parser->header_state = h_general;
+            } else if (parser->index == sizeof(CHUNKED)-2) {
+              parser->header_state = h_transfer_encoding_chunked;
+            }
+            break;
+
+          /* looking for 'Connection: keep-alive' */
+          case h_matching_connection_keep_alive:
+            parser->index++;
+            if (parser->index > sizeof(KEEP_ALIVE)-1
+                || c != KEEP_ALIVE[parser->index]) {
+              parser->header_state = h_general;
+            } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+              parser->header_state = h_connection_keep_alive;
+            }
+            break;
+
+          /* looking for 'Connection: close' */
+          case h_matching_connection_close:
+            parser->index++;
+            if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+              parser->header_state = h_general;
+            } else if (parser->index == sizeof(CLOSE)-2) {
+              parser->header_state = h_connection_close;
+            }
+            break;
+
+          case h_transfer_encoding_chunked:
+          case h_connection_keep_alive:
+          case h_connection_close:
+            if (ch != ' ') parser->header_state = h_general;
+            break;
+
+          default:
+            parser->state = s_header_value;
+            parser->header_state = h_general;
+            break;
+        }
+        break;
+      }
+
+      case s_header_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        parser->state = s_header_value_lws;
+
+        switch (parser->header_state) {
+          case h_connection_keep_alive:
+            parser->flags |= F_CONNECTION_KEEP_ALIVE;
+            break;
+          case h_connection_close:
+            parser->flags |= F_CONNECTION_CLOSE;
+            break;
+          case h_transfer_encoding_chunked:
+            parser->flags |= F_CHUNKED;
+            break;
+          default:
+            break;
+        }
+
+        break;
+      }
+
+      case s_header_value_lws:
+      {
+        if (ch == ' ' || ch == '\t')
+          parser->state = s_header_value_start;
+        else
+        {
+          parser->state = s_header_field_start;
+          goto reexecute_byte;
+        }
+        break;
+      }
+
+      case s_headers_almost_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        if (parser->flags & F_TRAILING) {
+          /* End of a chunked request */
+          parser->state = NEW_MESSAGE();
+          CALLBACK_NOTIFY(message_complete);
+          break;
+        }
+
+        parser->state = s_headers_done;
+
+        /* Set this here so that on_headers_complete() callbacks can see it */
+        parser->upgrade =
+          (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
+
+        /* Here we call the headers_complete callback. This is somewhat
+         * different than other callbacks because if the user returns 1, we
+         * will interpret that as saying that this message has no body. This
+         * is needed for the annoying case of recieving a response to a HEAD
+         * request.
+         *
+         * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+         * we have to simulate it by handling a change in errno below.
+         */
+        if (settings->on_headers_complete) {
+          switch (settings->on_headers_complete(parser)) {
+            case 0:
+              break;
+
+            case 1:
+              parser->flags |= F_SKIPBODY;
+              break;
+
+            default:
+              SET_ERRNO(HPE_CB_headers_complete);
+              return p - data; /* Error */
+          }
+        }
+
+        if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+          return p - data;
+        }
+
+        goto reexecute_byte;
+      }
+
+      case s_headers_done:
+      {
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+
+        /* Exit, the rest of the connect is in a different protocol. */
+        if (parser->upgrade) {
+          parser->state = NEW_MESSAGE();
+          CALLBACK_NOTIFY(message_complete);
+          return (p - data) + 1;
+        }
+
+        if (parser->flags & F_SKIPBODY) {
+          parser->state = NEW_MESSAGE();
+          CALLBACK_NOTIFY(message_complete);
+        } else if (parser->flags & F_CHUNKED) {
+          /* chunked encoding - ignore Content-Length header */
+          parser->state = s_chunk_size_start;
+        } else {
+          if (parser->content_length == 0) {
+            /* Content-Length header given but zero: Content-Length: 0\r\n */
+            parser->state = NEW_MESSAGE();
+            CALLBACK_NOTIFY(message_complete);
+          } else if (parser->content_length != ULLONG_MAX) {
+            /* Content-Length header given and non-zero */
+            parser->state = s_body_identity;
+          } else {
+            if (parser->type == HTTP_REQUEST ||
+                !http_message_needs_eof(parser)) {
+              /* Assume content-length 0 - read the next */
+              parser->state = NEW_MESSAGE();
+              CALLBACK_NOTIFY(message_complete);
+            } else {
+              /* Read body until EOF */
+              parser->state = s_body_identity_eof;
+            }
+          }
+        }
+
+        break;
+      }
+
+      case s_body_identity:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* The difference between advancing content_length and p is because
+         * the latter will automaticaly advance on the next loop iteration.
+         * Further, if content_length ends up at 0, we want to see the last
+         * byte again for our message complete callback.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          parser->state = s_message_done;
+
+          /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+           *
+           * The alternative to doing this is to wait for the next byte to
+           * trigger the data callback, just as in every other case. The
+           * problem with this is that this makes it difficult for the test
+           * harness to distinguish between complete-on-EOF and
+           * complete-on-length. It's not clear that this distinction is
+           * important for applications, but let's keep it for now.
+           */
+          CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+          goto reexecute_byte;
+        }
+
+        break;
+      }
+
+      /* read until EOF */
+      case s_body_identity_eof:
+        MARK(body);
+        p = data + len - 1;
+
+        break;
+
+      case s_message_done:
+        parser->state = NEW_MESSAGE();
+        CALLBACK_NOTIFY(message_complete);
+        break;
+
+      case s_chunk_size_start:
+      {
+        assert(parser->nread == 1);
+        assert(parser->flags & F_CHUNKED);
+
+        unhex_val = unhex[(unsigned char)ch];
+        if (unhex_val == -1) {
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        parser->content_length = unhex_val;
+        parser->state = s_chunk_size;
+        break;
+      }
+
+      case s_chunk_size:
+      {
+        uint64_t t;
+
+        assert(parser->flags & F_CHUNKED);
+
+        if (ch == CR) {
+          parser->state = s_chunk_size_almost_done;
+          break;
+        }
+
+        unhex_val = unhex[(unsigned char)ch];
+
+        if (unhex_val == -1) {
+          if (ch == ';' || ch == ' ') {
+            parser->state = s_chunk_parameters;
+            break;
+          }
+
+          SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+          goto error;
+        }
+
+        t = parser->content_length;
+        t *= 16;
+        t += unhex_val;
+
+        /* Overflow? */
+        if (t < parser->content_length || t == ULLONG_MAX) {
+          SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+          goto error;
+        }
+
+        parser->content_length = t;
+        break;
+      }
+
+      case s_chunk_parameters:
+      {
+        assert(parser->flags & F_CHUNKED);
+        /* just ignore this. TODO check for overflow */
+        if (ch == CR) {
+          parser->state = s_chunk_size_almost_done;
+          break;
+        }
+        break;
+      }
+
+      case s_chunk_size_almost_done:
+      {
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+
+        parser->nread = 0;
+
+        if (parser->content_length == 0) {
+          parser->flags |= F_TRAILING;
+          parser->state = s_header_field_start;
+        } else {
+          parser->state = s_chunk_data;
+        }
+        break;
+      }
+
+      case s_chunk_data:
+      {
+        uint64_t to_read = MIN(parser->content_length,
+                               (uint64_t) ((data + len) - p));
+
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length != 0
+            && parser->content_length != ULLONG_MAX);
+
+        /* See the explanation in s_body_identity for why the content
+         * length and data pointers are managed this way.
+         */
+        MARK(body);
+        parser->content_length -= to_read;
+        p += to_read - 1;
+
+        if (parser->content_length == 0) {
+          parser->state = s_chunk_data_almost_done;
+        }
+
+        break;
+      }
+
+      case s_chunk_data_almost_done:
+        assert(parser->flags & F_CHUNKED);
+        assert(parser->content_length == 0);
+        STRICT_CHECK(ch != CR);
+        parser->state = s_chunk_data_done;
+        CALLBACK_DATA(body);
+        break;
+
+      case s_chunk_data_done:
+        assert(parser->flags & F_CHUNKED);
+        STRICT_CHECK(ch != LF);
+        parser->nread = 0;
+        parser->state = s_chunk_size_start;
+        break;
+
+      default:
+        assert(0 && "unhandled state");
+        SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+        goto error;
+    }
+  }
+
+  /* Run callbacks for any marks that we have leftover after we ran our of
+   * bytes. There should be at most one of these set, so it's OK to invoke
+   * them in series (unset marks will not result in callbacks).
+   *
+   * We use the NOADVANCE() variety of callbacks here because 'p' has already
+   * overflowed 'data' and this allows us to correct for the off-by-one that
+   * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+   * value that's in-bounds).
+   */
+
+  assert(((header_field_mark ? 1 : 0) +
+          (header_value_mark ? 1 : 0) +
+          (url_mark ? 1 : 0)  +
+          (body_mark ? 1 : 0)) <= 1);
+
+  CALLBACK_DATA_NOADVANCE(header_field);
+  CALLBACK_DATA_NOADVANCE(header_value);
+  CALLBACK_DATA_NOADVANCE(url);
+  CALLBACK_DATA_NOADVANCE(body);
+
+  return len;
+
+error:
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+    SET_ERRNO(HPE_UNKNOWN);
+  }
+
+  return (p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+  if (parser->type == HTTP_REQUEST) {
+    return 0;
+  }
+
+  /* See RFC 2616 section 4.4 */
+  if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+      parser->status_code == 204 ||     /* No Content */
+      parser->status_code == 304 ||     /* Not Modified */
+      parser->flags & F_SKIPBODY) {     /* response to a HEAD request */
+    return 0;
+  }
+
+  if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+  if (parser->http_major > 0 && parser->http_minor > 0) {
+    /* HTTP/1.1 */
+    if (parser->flags & F_CONNECTION_CLOSE) {
+      return 0;
+    }
+  } else {
+    /* HTTP/1.0 or earlier */
+    if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+      return 0;
+    }
+  }
+
+  return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+  return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+  void *data = parser->data; /* preserve application data */
+  memset(parser, 0, sizeof(*parser));
+  parser->data = data;
+  parser->type = t;
+  parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+  parser->http_errno = HPE_OK;
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+  return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+  assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0])));
+  return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+  switch(s) {
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      if (ch == '@') {
+        return s_http_host_start;
+      }
+
+      if (IS_USERINFO_CHAR(ch)) {
+        return s_http_userinfo;
+      }
+      break;
+
+    case s_http_host_start:
+      if (ch == '[') {
+        return s_http_host_v6_start;
+      }
+
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+      break;
+
+    case s_http_host:
+      if (IS_HOST_CHAR(ch)) {
+        return s_http_host;
+      }
+
+    /* FALLTHROUGH */
+    case s_http_host_v6_end:
+      if (ch == ':') {
+        return s_http_host_port_start;
+      }
+
+      break;
+
+    case s_http_host_v6:
+      if (ch == ']') {
+        return s_http_host_v6_end;
+      }
+
+    /* FALLTHROUGH */
+    case s_http_host_v6_start:
+      if (IS_HEX(ch) || ch == ':') {
+        return s_http_host_v6;
+      }
+
+      break;
+
+    case s_http_host_port:
+    case s_http_host_port_start:
+      if (IS_NUM(ch)) {
+        return s_http_host_port;
+      }
+
+      break;
+
+    default:
+      break;
+  }
+  return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+  enum http_host_state s;
+
+  const char *p;
+  size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+  u->field_data[UF_HOST].len = 0;
+
+  s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+  for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+    enum http_host_state new_s = http_parse_host_char(s, *p);
+
+    if (new_s == s_http_host_dead) {
+      return 1;
+    }
+
+    switch(new_s) {
+      case s_http_host:
+        if (s != s_http_host) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_v6:
+        if (s != s_http_host_v6) {
+          u->field_data[UF_HOST].off = p - buf;
+        }
+        u->field_data[UF_HOST].len++;
+        break;
+
+      case s_http_host_port:
+        if (s != s_http_host_port) {
+          u->field_data[UF_PORT].off = p - buf;
+          u->field_data[UF_PORT].len = 0;
+          u->field_set |= (1 << UF_PORT);
+        }
+        u->field_data[UF_PORT].len++;
+        break;
+
+      case s_http_userinfo:
+        if (s != s_http_userinfo) {
+          u->field_data[UF_USERINFO].off = p - buf ;
+          u->field_data[UF_USERINFO].len = 0;
+          u->field_set |= (1 << UF_USERINFO);
+        }
+        u->field_data[UF_USERINFO].len++;
+        break;
+
+      default:
+        break;
+    }
+    s = new_s;
+  }
+
+  /* Make sure we don't end somewhere unexpected */
+  switch (s) {
+    case s_http_host_start:
+    case s_http_host_v6_start:
+    case s_http_host_v6:
+    case s_http_host_port_start:
+    case s_http_userinfo:
+    case s_http_userinfo_start:
+      return 1;
+    default:
+      break;
+  }
+
+  return 0;
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+                      struct http_parser_url *u)
+{
+  enum state s;
+  const char *p;
+  enum http_parser_url_fields uf, old_uf;
+  int found_at = 0;
+
+  u->port = u->field_set = 0;
+  s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+  uf = old_uf = UF_MAX;
+
+  for (p = buf; p < buf + buflen; p++) {
+    s = parse_url_char(s, *p);
+
+    /* Figure out the next field that we're operating on */
+    switch (s) {
+      case s_dead:
+        return 1;
+
+      /* Skip delimeters */
+      case s_req_schema_slash:
+      case s_req_schema_slash_slash:
+      case s_req_server_start:
+      case s_req_query_string_start:
+      case s_req_fragment_start:
+        continue;
+
+      case s_req_schema:
+        uf = UF_SCHEMA;
+        break;
+
+      case s_req_server_with_at:
+        found_at = 1;
+
+      /* FALLTROUGH */
+      case s_req_server:
+        uf = UF_HOST;
+        break;
+
+      case s_req_path:
+        uf = UF_PATH;
+        break;
+
+      case s_req_query_string:
+        uf = UF_QUERY;
+        break;
+
+      case s_req_fragment:
+        uf = UF_FRAGMENT;
+        break;
+
+      default:
+        assert(!"Unexpected state");
+        return 1;
+    }
+
+    /* Nothing's changed; soldier on */
+    if (uf == old_uf) {
+      u->field_data[uf].len++;
+      continue;
+    }
+
+    u->field_data[uf].off = p - buf;
+    u->field_data[uf].len = 1;
+
+    u->field_set |= (1 << uf);
+    old_uf = uf;
+  }
+
+  /* host must be present if there is a schema */
+  /* parsing http:///toto will fail */
+  if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) {
+    if (http_parse_host(buf, u, found_at) != 0) {
+      return 1;
+    }
+  }
+
+  /* CONNECT requests can only contain "hostname:port" */
+  if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+    return 1;
+  }
+
+  if (u->field_set & (1 << UF_PORT)) {
+    /* Don't bother with endp; we've already validated the string */
+    unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+    /* Ports have a max value of 2^16 */
+    if (v > 0xffff) {
+      return 1;
+    }
+
+    u->port = (uint16_t) v;
+  }
+
+  return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+  /* Users should only be pausing/unpausing a parser that is not in an error
+   * state. In non-debug builds, there's not much that we can do about this
+   * other than ignore it.
+   */
+  if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+      HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+    SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+  } else {
+    assert(0 && "Attempting to pause parser in error state");
+  }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+    return parser->state == s_message_done;
 }
diff --git a/libgit2/deps/http-parser/http_parser.h b/libgit2/deps/http-parser/http_parser.h
--- a/libgit2/deps/http-parser/http_parser.h
+++ b/libgit2/deps/http-parser/http_parser.h
@@ -24,16 +24,25 @@
 extern "C" {
 #endif
 
-#define HTTP_PARSER_VERSION_MAJOR 1
+#define HTTP_PARSER_VERSION_MAJOR 2
 #define HTTP_PARSER_VERSION_MINOR 0
 
-#ifdef _MSC_VER
-	/* disable silly warnings */
-#	pragma warning(disable: 4127 4214)
-#endif
-
 #include <sys/types.h>
-#include "git2/common.h"
+#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
+#include <BaseTsd.h>
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+typedef SIZE_T size_t;
+typedef SSIZE_T ssize_t;
+#else
+#include <stdint.h>
+#endif
 
 /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
  * faster
@@ -42,21 +51,12 @@
 # define HTTP_PARSER_STRICT 1
 #endif
 
-/* Compile with -DHTTP_PARSER_DEBUG=1 to add extra debugging information to
- * the error reporting facility.
- */
-#ifndef HTTP_PARSER_DEBUG
-# define HTTP_PARSER_DEBUG 0
-#endif
-
-
 /* Maximium header size allowed */
 #define HTTP_MAX_HEADER_SIZE (80*1024)
 
 
 typedef struct http_parser http_parser;
 typedef struct http_parser_settings http_parser_settings;
-typedef struct http_parser_result http_parser_result;
 
 
 /* Callbacks should return non-zero to indicate an error. The parser will
@@ -69,7 +69,7 @@
  * chunked' headers that indicate the presence of a body.
  *
  * http_data_cb does not return data chunks. It will be call arbitrarally
- * many times for each string. E.G. you might get 10 callbacks for "on_path"
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
  * each providing just a few characters more data.
  */
 typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
@@ -77,36 +77,44 @@
 
 
 /* Request Methods */
+#define HTTP_METHOD_MAP(XX)         \
+  XX(0,  DELETE,      DELETE)       \
+  XX(1,  GET,         GET)          \
+  XX(2,  HEAD,        HEAD)         \
+  XX(3,  POST,        POST)         \
+  XX(4,  PUT,         PUT)          \
+  /* pathological */                \
+  XX(5,  CONNECT,     CONNECT)      \
+  XX(6,  OPTIONS,     OPTIONS)      \
+  XX(7,  TRACE,       TRACE)        \
+  /* webdav */                      \
+  XX(8,  COPY,        COPY)         \
+  XX(9,  LOCK,        LOCK)         \
+  XX(10, MKCOL,       MKCOL)        \
+  XX(11, MOVE,        MOVE)         \
+  XX(12, PROPFIND,    PROPFIND)     \
+  XX(13, PROPPATCH,   PROPPATCH)    \
+  XX(14, SEARCH,      SEARCH)       \
+  XX(15, UNLOCK,      UNLOCK)       \
+  /* subversion */                  \
+  XX(16, REPORT,      REPORT)       \
+  XX(17, MKACTIVITY,  MKACTIVITY)   \
+  XX(18, CHECKOUT,    CHECKOUT)     \
+  XX(19, MERGE,       MERGE)        \
+  /* upnp */                        \
+  XX(20, MSEARCH,     M-SEARCH)     \
+  XX(21, NOTIFY,      NOTIFY)       \
+  XX(22, SUBSCRIBE,   SUBSCRIBE)    \
+  XX(23, UNSUBSCRIBE, UNSUBSCRIBE)  \
+  /* RFC-5789 */                    \
+  XX(24, PATCH,       PATCH)        \
+  XX(25, PURGE,       PURGE)        \
+
 enum http_method
-  { HTTP_DELETE    = 0
-  , HTTP_GET
-  , HTTP_HEAD
-  , HTTP_POST
-  , HTTP_PUT
-  /* pathological */
-  , HTTP_CONNECT
-  , HTTP_OPTIONS
-  , HTTP_TRACE
-  /* webdav */
-  , HTTP_COPY
-  , HTTP_LOCK
-  , HTTP_MKCOL
-  , HTTP_MOVE
-  , HTTP_PROPFIND
-  , HTTP_PROPPATCH
-  , HTTP_UNLOCK
-  /* subversion */
-  , HTTP_REPORT
-  , HTTP_MKACTIVITY
-  , HTTP_CHECKOUT
-  , HTTP_MERGE
-  /* upnp */
-  , HTTP_MSEARCH
-  , HTTP_NOTIFY
-  , HTTP_SUBSCRIBE
-  , HTTP_UNSUBSCRIBE
-  /* RFC-5789 */
-  , HTTP_PATCH
+  {
+#define XX(num, name, string) HTTP_##name = num,
+  HTTP_METHOD_MAP(XX)
+#undef XX
   };
 
 
@@ -134,10 +142,7 @@
                                                                      \
   /* Callback-related errors */                                      \
   XX(CB_message_begin, "the on_message_begin callback failed")       \
-  XX(CB_path, "the on_path callback failed")                         \
-  XX(CB_query_string, "the on_query_string callback failed")         \
   XX(CB_url, "the on_url callback failed")                           \
-  XX(CB_fragment, "the on_fragment callback failed")                 \
   XX(CB_header_field, "the on_header_field callback failed")         \
   XX(CB_header_value, "the on_header_value callback failed")         \
   XX(CB_headers_complete, "the on_headers_complete callback failed") \
@@ -168,6 +173,7 @@
   XX(INVALID_CONSTANT, "invalid constant string")                    \
   XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
   XX(STRICT, "strict mode assertion failed")                         \
+  XX(PAUSED, "parser is paused")                                     \
   XX(UNKNOWN, "an unknown error occurred")
 
 
@@ -182,30 +188,23 @@
 /* Get an http_errno value from an http_parser */
 #define HTTP_PARSER_ERRNO(p)            ((enum http_errno) (p)->http_errno)
 
-/* Get the line number that generated the current error */
-#if HTTP_PARSER_DEBUG
-#define HTTP_PARSER_ERRNO_LINE(p)       ((p)->error_lineno)
-#else
-#define HTTP_PARSER_ERRNO_LINE(p)       0
-#endif
 
-
 struct http_parser {
   /** PRIVATE **/
-  unsigned char type : 2;
-  unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
-  unsigned char state;
-  unsigned char header_state;
-  unsigned char index;
+  unsigned char type : 2;     /* enum http_parser_type */
+  unsigned char flags : 6;    /* F_* values from 'flags' enum; semi-public */
+  unsigned char state;        /* enum state from http_parser.c */
+  unsigned char header_state; /* enum header_state from http_parser.c */
+  unsigned char index;        /* index into current matcher */
 
-  size_t nread;
-  int64_t content_length;
+  uint32_t nread;          /* # bytes read in various scenarios */
+  uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
 
   /** READ-ONLY **/
   unsigned short http_major;
   unsigned short http_minor;
   unsigned short status_code; /* responses only */
-  unsigned char method;    /* requests only */
+  unsigned char method;       /* requests only */
   unsigned char http_errno : 7;
 
   /* 1 = Upgrade header was present and the parser has exited because of that.
@@ -215,10 +214,6 @@
    */
   unsigned char upgrade : 1;
 
-#if HTTP_PARSER_DEBUG
-  uint32_t error_lineno;
-#endif
-
   /** PUBLIC **/
   void *data; /* A pointer to get hook to the "connection" or "socket" object */
 };
@@ -235,6 +230,36 @@
 };
 
 
+enum http_parser_url_fields
+  { UF_SCHEMA           = 0
+  , UF_HOST             = 1
+  , UF_PORT             = 2
+  , UF_PATH             = 3
+  , UF_QUERY            = 4
+  , UF_FRAGMENT         = 5
+  , UF_USERINFO         = 6
+  , UF_MAX              = 7
+  };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+  uint16_t field_set;           /* Bitmask of (1 << UF_*) values */
+  uint16_t port;                /* Converted UF_PORT string */
+
+  struct {
+    uint16_t off;               /* Offset into buffer in which field starts */
+    uint16_t len;               /* Length of run in buffer */
+  } field_data[UF_MAX];
+};
+
+
 void http_parser_init(http_parser *parser, enum http_parser_type type);
 
 
@@ -245,12 +270,12 @@
 
 
 /* If http_should_keep_alive() in the on_headers_complete or
- * on_message_complete callback returns true, then this will be should be
+ * on_message_complete callback returns 0, then this should be
  * the last message on the connection.
  * If you are the server, respond with the "Connection: close" header.
  * If you are the client, close the connection.
  */
-int http_should_keep_alive(http_parser *parser);
+int http_should_keep_alive(const http_parser *parser);
 
 /* Returns a string version of the HTTP method. */
 const char *http_method_str(enum http_method m);
@@ -260,6 +285,17 @@
 
 /* Return a string description of the given error */
 const char *http_errno_description(enum http_errno err);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+                          int is_connect,
+                          struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
 
 #ifdef __cplusplus
 }
diff --git a/libgit2/include/git2.h b/libgit2/include/git2.h
--- a/libgit2/include/git2.h
+++ b/libgit2/include/git2.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -23,8 +23,10 @@
 #include "git2/repository.h"
 #include "git2/revwalk.h"
 #include "git2/merge.h"
+#include "git2/graph.h"
 #include "git2/refs.h"
 #include "git2/reflog.h"
+#include "git2/revparse.h"
 
 #include "git2/object.h"
 #include "git2/blob.h"
@@ -35,13 +37,24 @@
 
 #include "git2/index.h"
 #include "git2/config.h"
+#include "git2/transport.h"
 #include "git2/remote.h"
+#include "git2/clone.h"
+#include "git2/checkout.h"
+#include "git2/push.h"
 
+#include "git2/attr.h"
+#include "git2/ignore.h"
+#include "git2/branch.h"
 #include "git2/refspec.h"
 #include "git2/net.h"
 #include "git2/status.h"
 #include "git2/indexer.h"
 #include "git2/submodule.h"
 #include "git2/notes.h"
+#include "git2/reset.h"
+#include "git2/message.h"
+#include "git2/pack.h"
+#include "git2/stash.h"
 
 #endif
diff --git a/libgit2/include/git2/attr.h b/libgit2/include/git2/attr.h
--- a/libgit2/include/git2/attr.h
+++ b/libgit2/include/git2/attr.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -30,7 +30,7 @@
  * Then for file `xyz.c` looking up attribute "foo" gives a value for
  * which `GIT_ATTR_TRUE(value)` is true.
  */
-#define GIT_ATTR_TRUE(attr)		((attr) == git_attr__true)
+#define GIT_ATTR_TRUE(attr)	(git_attr_value(attr) == GIT_ATTR_TRUE_T)
 
 /**
  * GIT_ATTR_FALSE checks if an attribute is set off.  In core git
@@ -44,7 +44,7 @@
  * Then for file `zyx.h` looking up attribute "foo" gives a value for
  * which `GIT_ATTR_FALSE(value)` is true.
  */
-#define GIT_ATTR_FALSE(attr)	((attr) == git_attr__false)
+#define GIT_ATTR_FALSE(attr) (git_attr_value(attr) == GIT_ATTR_FALSE_T)
 
 /**
  * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified.  This
@@ -62,11 +62,11 @@
  * file `onefile.rb` or looking up "bar" on any file will all give
  * `GIT_ATTR_UNSPECIFIED(value)` of true.
  */
-#define GIT_ATTR_UNSPECIFIED(attr)	(!(attr) || (attr) == git_attr__unset)
+#define GIT_ATTR_UNSPECIFIED(attr) (git_attr_value(attr) == GIT_ATTR_UNSPECIFIED_T)
 
 /**
  * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as
- * opposied to TRUE, FALSE or UNSPECIFIED).  This would be the case if
+ * opposed to TRUE, FALSE or UNSPECIFIED).  This would be the case if
  * for a file with something like:
  *
  *    *.txt eol=lf
@@ -74,14 +74,30 @@
  * Given this, looking up "eol" for `onefile.txt` will give back the
  * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
  */
-#define GIT_ATTR_HAS_VALUE(attr) \
-	((attr) && (attr) != git_attr__unset && \
-	 (attr) != git_attr__true && (attr) != git_attr__false)
+#define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_T)
 
-GIT_EXTERN(const char *) git_attr__true;
-GIT_EXTERN(const char *) git_attr__false;
-GIT_EXTERN(const char *) git_attr__unset;
+typedef enum {
+	GIT_ATTR_UNSPECIFIED_T = 0,
+	GIT_ATTR_TRUE_T,
+	GIT_ATTR_FALSE_T,
+	GIT_ATTR_VALUE_T,
+} git_attr_t;
 
+/*
+ *	Return the value type for a given attribute.
+ *
+ *	This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute
+ *	was not set at all), or `VALUE`, if the attribute was set to
+ *	an actual string.
+ *
+ *	If the attribute has a `VALUE` string, it can be accessed normally
+ *	as a NULL-terminated C string.
+ *
+ *	@param attr The attribute
+ *	@return the value type for the attribute
+ */
+GIT_EXTERN(git_attr_t) git_attr_value(const char *attr);
+
 /**
  * Check attribute flags: Reading values from index and working directory.
  *
@@ -167,29 +183,30 @@
 	size_t num_attr,
 	const char **names);
 
+typedef int (*git_attr_foreach_cb)(const char *name, const char *value, void *payload);
+
 /**
  * Loop over all the git attributes for a path.
  *
  * @param repo The repository containing the path.
  * @param flags A combination of GIT_ATTR_CHECK... flags.
- * @param path The path inside the repo to check attributes.  This
- *             does not have to exist, but if it does not, then
- *             it will be treated as a plain file (i.e. not a directory).
- * @param callback The function that will be invoked on each attribute
- *             and attribute value.  The name parameter will be the name
- *             of the attribute and the value will be the value it is
- *             set to, including possibly NULL if the attribute is
- *             explicitly set to UNSPECIFIED using the ! sign.  This
- *             will be invoked only once per attribute name, even if
- *             there are multiple rules for a given file.  The highest
- *             priority rule will be used.
+ * @param path Path inside the repo to check attributes.  This does not have
+ *             to exist, but if it does not, then it will be treated as a
+ *             plain file (i.e. not a directory).
+ * @param callback Function to invoke on each attribute name and value.  The
+ *             value may be NULL is the attribute is explicitly set to
+ *             UNSPECIFIED using the '!' sign.  Callback will be invoked
+ *             only once per attribute name, even if there are multiple
+ *             rules for a given file.  The highest priority rule will be
+ *             used.  Return a non-zero value from this to stop looping.
  * @param payload Passed on as extra parameter to callback function.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_attr_foreach(
 	git_repository *repo,
 	uint32_t flags,
 	const char *path,
-	int (*callback)(const char *name, const char *value, void *payload),
+	git_attr_foreach_cb callback,
 	void *payload);
 
 /**
diff --git a/libgit2/include/git2/blob.h b/libgit2/include/git2/blob.h
--- a/libgit2/include/git2/blob.h
+++ b/libgit2/include/git2/blob.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -46,7 +46,7 @@
  * @param len the length of the short identifier
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len)
+GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len)
 {
 	return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
 }
@@ -68,7 +68,18 @@
 	git_object_free((git_object *) blob);
 }
 
+/**
+ * Get the id of a blob.
+ *
+ * @param blob a previously loaded blob.
+ * @return SHA1 hash for this blob.
+ */
+GIT_INLINE(const git_oid *) git_blob_id(const git_blob *blob)
+{
+	return git_object_id((const git_object *)blob);
+}
 
+
 /**
  * Get a read-only buffer with the raw content of a blob.
  *
@@ -80,7 +91,7 @@
  * @param blob pointer to the blob
  * @return the pointer; NULL if the blob has no contents
  */
-GIT_EXTERN(const void *) git_blob_rawcontent(git_blob *blob);
+GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
 
 /**
  * Get the size in bytes of the contents of a blob
@@ -88,35 +99,80 @@
  * @param blob pointer to the blob
  * @return size on bytes
  */
-GIT_EXTERN(size_t) git_blob_rawsize(git_blob *blob);
+GIT_EXTERN(git_off_t) git_blob_rawsize(const git_blob *blob);
 
 /**
  * Read a file from the working folder of a repository
  * and write it to the Object Database as a loose blob
  *
- * @param oid return the id of the written blob
+ * @param id return the id of the written blob
  * @param repo repository where the blob will be written.
  *	this repository cannot be bare
- * @param path file from which the blob will be created,
+ * @param relative_path file from which the blob will be created,
  *	relative to the repository's working dir
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path);
+GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path);
 
 /**
  * Read a file from the filesystem and write its content
  * to the Object Database as a loose blob
  *
- * @param oid return the id of the written blob
+ * @param id return the id of the written blob
  * @param repo repository where the blob will be written.
  *	this repository can be bare or not
  * @param path file from which the blob will be created
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path);
+GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path);
 
 
+typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload);
+
 /**
+ * Write a loose blob to the Object Database from a
+ * provider of chunks of data.
+ *
+ * Provided the `hintpath` parameter is filled, its value
+ * will help to determine what git filters should be applied
+ * to the object before it can be placed to the object database.
+ *
+ *
+ * The implementation of the callback has to respect the
+ * following rules:
+ *
+ *  - `content` will have to be filled by the consumer. The maximum number
+ * of bytes that the buffer can accept per call is defined by the
+ * `max_length` parameter. Allocation and freeing of the buffer will be taken
+ * care of by the function.
+ *
+ *  - The callback is expected to return the number of bytes
+ * that `content` have been filled with.
+ *
+ *  - When there is no more data to stream, the callback should
+ * return 0. This will prevent it from being invoked anymore.
+ *
+ *  - When an error occurs, the callback should return -1.
+ *
+ *
+ * @param id Return the id of the written blob
+ *
+ * @param repo repository where the blob will be written.
+ * This repository can be bare or not.
+ *
+ * @param hintpath if not NULL, will help selecting the filters
+ * to apply onto the content of the blob to be created.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_fromchunks(
+	git_oid *id,
+	git_repository *repo,
+	const char *hintpath,
+	git_blob_chunk_cb callback,
+	void *payload);
+
+/**
  * Write an in-memory buffer to the ODB as a blob
  *
  * @param oid return the oid of the written blob
@@ -126,6 +182,19 @@
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len);
+
+/**
+ * Determine if the blob content is most certainly binary or not.
+ *
+ * The heuristic used to guess if a file is binary is taken from core git:
+ * Searching for NUL bytes and looking for a reasonable ratio of printable
+ * to non-printable characters among the first 4000 bytes.
+ *
+ * @param blob The blob which content should be analyzed
+ * @return 1 if the content of the blob is detected
+ * as binary; 0 otherwise.
+ */
+GIT_EXTERN(int) git_blob_is_binary(git_blob *blob);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/branch.h b/libgit2/include/git2/branch.h
--- a/libgit2/include/git2/branch.h
+++ b/libgit2/include/git2/branch.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,6 +8,7 @@
 #define INCLUDE_git_branch_h__
 
 #include "common.h"
+#include "oid.h"
 #include "types.h"
 
 /**
@@ -26,61 +27,53 @@
  * this target commit. If `force` is true and a reference
  * already exists with the given name, it'll be replaced.
  *
- * @param oid_out Pointer where to store the OID of the target commit.
+ * The returned reference must be freed by the user.
  *
- * @param repo Repository where to store the branch.
+ * The branch name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
  *
+ * @param out Pointer where to store the underlying reference.
+ *
  * @param branch_name Name for the branch; this name is
  * validated for consistency. It should also not conflict with
  * an already existing branch name.
  *
- * @param target Object to which this branch should point. This object
- * must belong to the given `repo` and can either be a git_commit or a
- * git_tag. When a git_tag is being passed, it should be dereferencable
- * to a git_commit which oid will be used as the target of the branch.
+ * @param target Commit to which this branch should point. This object
+ * must belong to the given `repo`.
  *
  * @param force Overwrite existing branch.
  *
- * @return 0 or an error code.
+ * @return 0, GIT_EINVALIDSPEC or an error code.
  * A proper reference is written in the refs/heads namespace
  * pointing to the provided target commit.
  */
 GIT_EXTERN(int) git_branch_create(
-		git_oid *oid_out,
-		git_repository *repo,
-		const char *branch_name,
-		const git_object *target,
-		int force);
+	git_reference **out,
+	git_repository *repo,
+	const char *branch_name,
+	const git_commit *target,
+	int force);
 
 /**
  * Delete an existing branch reference.
  *
- * @param repo Repository where lives the branch.
- *
- * @param branch_name Name of the branch to be deleted;
- * this name is validated for consistency.
- *
- * @param branch_type Type of the considered branch. This should
- * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
+ * If the branch is successfully deleted, the passed reference
+ * object will be freed and invalidated.
  *
- * @return 0 on success, GIT_ENOTFOUND if the branch
- * doesn't exist or an error code.
+ * @param branch A valid reference representing a branch
+ * @return 0 on success, or an error code.
  */
-GIT_EXTERN(int) git_branch_delete(
-		git_repository *repo,
-		const char *branch_name,
-		git_branch_t branch_type);
+GIT_EXTERN(int) git_branch_delete(git_reference *branch);
 
+typedef int (*git_branch_foreach_cb)(
+	const char *branch_name,
+	git_branch_t branch_type,
+	void *payload);
+
 /**
- * Fill a list with all the branches in the Repository
- *
- * The string array will be filled with the names of the
- * matching branches; these values are owned by the user and
- * should be free'd manually when no longer needed, using
- * `git_strarray_free`.
+ * Loop over all the branches and issue a callback for each one.
  *
- * @param branch_names Pointer to a git_strarray structure
- * where the branch names will be stored.
+ * If the callback returns a non-zero value, this will stop looping.
  *
  * @param repo Repository where to find the branches.
  *
@@ -88,34 +81,171 @@
  * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
  * or a combination of the two.
  *
- * @return 0 or an error code.
+ * @param branch_cb Callback to invoke per found branch.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
-GIT_EXTERN(int) git_branch_list(
-		git_strarray *branch_names,
-		git_repository *repo,
-		unsigned int list_flags);
+GIT_EXTERN(int) git_branch_foreach(
+	git_repository *repo,
+	unsigned int list_flags,
+	git_branch_foreach_cb branch_cb,
+	void *payload);
 
 /**
- * Move/rename an existing branch reference.
+ * Move/rename an existing local branch reference.
  *
- * @param repo Repository where lives the branch.
+ * The new branch name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
  *
- * @param old_branch_name Current name of the branch to be moved;
- * this name is validated for consistency.
+ * @param branch Current underlying reference of the branch.
  *
  * @param new_branch_name Target name of the branch once the move
  * is performed; this name is validated for consistency.
  *
  * @param force Overwrite existing branch.
  *
- * @return 0 on success, GIT_ENOTFOUND if the branch
- * doesn't exist or an error code.
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code.
  */
 GIT_EXTERN(int) git_branch_move(
-		git_repository *repo,
-		const char *old_branch_name,
-		const char *new_branch_name,
-		int force);
+	git_reference **out,
+	git_reference *branch,
+	const char *new_branch_name,
+	int force);
+
+/**
+ * Lookup a branch by its name in a repository.
+ *
+ * The generated reference must be freed by the user.
+ *
+ * The branch name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * @param out pointer to the looked-up branch reference
+ *
+ * @param repo the repository to look up the branch
+ *
+ * @param branch_name Name of the branch to be looked-up;
+ * this name is validated for consistency.
+ *
+ * @param branch_type Type of the considered branch. This should
+ * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
+ *
+ * @return 0 on success; GIT_ENOTFOUND when no matching branch
+ * exists, GIT_EINVALIDSPEC, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_lookup(
+	git_reference **out,
+	git_repository *repo,
+	const char *branch_name,
+	git_branch_t branch_type);
+
+/**
+ * Return the name of the given local or remote branch.
+ *
+ * The name of the branch matches the definition of the name
+ * for git_branch_lookup. That is, if the returned name is given
+ * to git_branch_lookup() then the reference is returned that
+ * was given to this function.
+ *
+ * @param out where the pointer of branch name is stored;
+ * this is valid as long as the ref is not freed.
+ * @param ref the reference ideally pointing to a branch
+ *
+ * @return 0 on success; otherwise an error code (e.g., if the
+ *  ref is no local or remote branch).
+ */
+GIT_EXTERN(int) git_branch_name(const char **out,
+		git_reference *ref);
+
+/**
+ * Return the reference supporting the remote tracking branch,
+ * given a local branch reference.
+ *
+ * @param out Pointer where to store the retrieved
+ * reference.
+ *
+ * @param branch Current underlying reference of the branch.
+ *
+ * @return 0 on success; GIT_ENOTFOUND when no remote tracking
+ * reference exists, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_upstream(
+	git_reference **out,
+	git_reference *branch);
+
+/**
+ * Set the upstream configuration for a given local branch
+ *
+ * @param branch the branch to configure
+ *
+ * @param upstream_name remote-tracking or local branch to set as
+ * upstream. Pass NULL to unset.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_branch_set_upstream(git_reference *branch, const char *upstream_name);
+
+/**
+ * Return the name of the reference supporting the remote tracking branch,
+ * given the name of a local branch reference.
+ *
+ * @param tracking_branch_name_out The user-allocated buffer which will be
+ *     filled with the name of the reference. Pass NULL if you just want to
+ *     get the needed size of the name of the reference as the output value.
+ *
+ * @param buffer_size Size of the `out` buffer in bytes.
+ *
+ * @param repo the repository where the branches live
+ *
+ * @param canonical_branch_name name of the local branch.
+ *
+ * @return number of characters in the reference name
+ *     including the trailing NUL byte; GIT_ENOTFOUND when no remote tracking
+ *     reference exists, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_upstream_name(
+	char *tracking_branch_name_out,
+	size_t buffer_size,
+	git_repository *repo,
+	const char *canonical_branch_name);
+
+/**
+ * Determine if the current local branch is pointed at by HEAD.
+ *
+ * @param branch Current underlying reference of the branch.
+ *
+ * @return 1 if HEAD points at the branch, 0 if it isn't,
+ * error code otherwise.
+ */
+GIT_EXTERN(int) git_branch_is_head(
+	git_reference *branch);
+
+/**
+ * Return the name of remote that the remote tracking branch belongs to.
+ *
+ * @param remote_name_out The user-allocated buffer which will be
+ *     filled with the name of the remote. Pass NULL if you just want to
+ *     get the needed size of the name of the remote as the output value.
+ *
+ * @param buffer_size Size of the `out` buffer in bytes.
+ *
+ * @param repo The repository where the branch lives.
+ *
+ * @param canonical_branch_name name of the remote tracking branch.
+ *
+ * @return Number of characters in the reference name
+ *     including the trailing NUL byte; GIT_ENOTFOUND
+ *     when no remote matching remote was gound,
+ *     GIT_EAMBIGUOUS when the branch maps to several remotes,
+ *     otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_remote_name(
+	char *remote_name_out,
+	size_t buffer_size,
+	git_repository *repo,
+	const char *canonical_branch_name);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/checkout.h b/libgit2/include/git2/checkout.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/checkout.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_checkout_h__
+#define INCLUDE_git_checkout_h__
+
+#include "common.h"
+#include "types.h"
+#include "diff.h"
+
+/**
+ * @file git2/checkout.h
+ * @brief Git checkout routines
+ * @defgroup git_checkout Git checkout routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Checkout behavior flags
+ *
+ * In libgit2, checkout is used to update the working directory and index
+ * to match a target tree.  Unlike git checkout, it does not move the HEAD
+ * commit for you - use `git_repository_set_head` or the like to do that.
+ *
+ * Checkout looks at (up to) four things: the "target" tree you want to
+ * check out, the "baseline" tree of what was checked out previously, the
+ * working directory for actual files, and the index for staged changes.
+ *
+ * You give checkout one of four strategies for update:
+ *
+ * - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts,
+ *   etc., but doesn't make any actual changes.
+ *
+ * - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to
+ *   make the working directory match the target (including potentially
+ *   discarding modified files).
+ *
+ * In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
+ * both of which only make modifications that will not lose changes.
+ *
+ *                      |  target == baseline   |  target != baseline  |
+ * ---------------------|-----------------------|----------------------|
+ *  workdir == baseline |       no action       |  create, update, or  |
+ *                      |                       |     delete file      |
+ * ---------------------|-----------------------|----------------------|
+ *  workdir exists and  |       no action       |   conflict (notify   |
+ *    is != baseline    | notify dirty MODIFIED | and cancel checkout) |
+ * ---------------------|-----------------------|----------------------|
+ *   workdir missing,   | create if SAFE_CREATE |     create file      |
+ *   baseline present   | notify dirty DELETED  |                      |
+ * ---------------------|-----------------------|----------------------|
+ *
+ * The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE
+ * will cause a file to be checked out if it is missing from the working
+ * directory even if it is not modified between the target and baseline.
+ *
+ *
+ * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout
+ * notification callback (see below) that displays information about dirty
+ * files.  The default behavior will cancel checkout on conflicts.
+ *
+ * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE_CREATE` with a
+ * notification callback that cancels the operation if a dirty-but-existing
+ * file is found in the working directory.  This core git command isn't
+ * quite "force" but is sensitive about some types of changes.
+ *
+ * To emulate `git checkout -f`, use `GIT_CHECKOUT_FORCE`.
+ *
+ * To emulate `git clone` use `GIT_CHECKOUT_SAFE_CREATE` in the options.
+ *
+ *
+ * There are some additional flags to modified the behavior of checkout:
+ *
+ * - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates
+ *   even if there are conflicts (instead of cancelling the checkout).
+ *
+ * - GIT_CHECKOUT_REMOVE_UNTRACKED means remove untracked files (i.e. not
+ *   in target, baseline, or index, and not ignored) from the working dir.
+ *
+ * - GIT_CHECKOUT_REMOVE_IGNORED means remove ignored files (that are also
+ *   untracked) from the working directory as well.
+ *
+ * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that
+ *   already exist.  Files will not be created nor deleted.  This just skips
+ *   applying adds, deletes, and typechanges.
+ *
+ * - GIT_CHECKOUT_DONT_UPDATE_INDEX prevents checkout from writing the
+ *   updated files' information to the index.
+ *
+ * - Normally, checkout will reload the index and git attributes from disk
+ *   before any operations.  GIT_CHECKOUT_NO_REFRESH prevents this reload.
+ *
+ * - Unmerged index entries are conflicts.  GIT_CHECKOUT_SKIP_UNMERGED skips
+ *   files with unmerged index entries instead.  GIT_CHECKOUT_USE_OURS and
+ *   GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the
+ *   stage 2 ("ours") or stage 3 ("theirs") version of files in the index.
+ */
+typedef enum {
+	GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */
+
+	/** Allow safe updates that cannot overwrite uncommitted data */
+	GIT_CHECKOUT_SAFE = (1u << 0),
+
+	/** Allow safe updates plus creation of missing files */
+	GIT_CHECKOUT_SAFE_CREATE = (1u << 1),
+
+	/** Allow all updates to force working directory to look like index */
+	GIT_CHECKOUT_FORCE = (1u << 2),
+
+
+	/** Allow checkout to make safe updates even if conflicts are found */
+	GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4),
+
+	/** Remove untracked files not in index (that are not ignored) */
+	GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5),
+
+	/** Remove ignored files not in index */
+	GIT_CHECKOUT_REMOVE_IGNORED = (1u << 6),
+
+	/** Only update existing files, don't create new ones */
+	GIT_CHECKOUT_UPDATE_ONLY = (1u << 7),
+
+	/** Normally checkout updates index entries as it goes; this stops that */
+	GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8),
+
+	/** Don't refresh index/config/etc before doing checkout */
+	GIT_CHECKOUT_NO_REFRESH = (1u << 9),
+
+	/** Treat pathspec as simple list of exact match file paths */
+	GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
+
+	/**
+	 * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
+	 */
+
+	/** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
+	GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
+	/** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
+	GIT_CHECKOUT_USE_OURS = (1u << 11),
+	/** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
+	GIT_CHECKOUT_USE_THEIRS = (1u << 12),
+
+	/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
+	GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
+	/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
+	GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17),
+
+} git_checkout_strategy_t;
+
+/**
+ * Checkout notification flags
+ *
+ * Checkout will invoke an options notification callback (`notify_cb`) for
+ * certain cases - you pick which ones via `notify_flags`:
+ *
+ * - GIT_CHECKOUT_NOTIFY_CONFLICT invokes checkout on conflicting paths.
+ *
+ * - GIT_CHECKOUT_NOTIFY_DIRTY notifies about "dirty" files, i.e. those that
+ *   do not need an update but no longer match the baseline.  Core git
+ *   displays these files when checkout runs, but won't stop the checkout.
+ *
+ * - GIT_CHECKOUT_NOTIFY_UPDATED sends notification for any file changed.
+ *
+ * - GIT_CHECKOUT_NOTIFY_UNTRACKED notifies about untracked files.
+ *
+ * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files.
+ *
+ * Returning a non-zero value from this callback will cancel the checkout.
+ * Notification callbacks are made prior to modifying any files on disk.
+ */
+typedef enum {
+	GIT_CHECKOUT_NOTIFY_NONE      = 0,
+	GIT_CHECKOUT_NOTIFY_CONFLICT  = (1u << 0),
+	GIT_CHECKOUT_NOTIFY_DIRTY     = (1u << 1),
+	GIT_CHECKOUT_NOTIFY_UPDATED   = (1u << 2),
+	GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
+	GIT_CHECKOUT_NOTIFY_IGNORED   = (1u << 4),
+} git_checkout_notify_t;
+
+/** Checkout notification callback function */
+typedef int (*git_checkout_notify_cb)(
+	git_checkout_notify_t why,
+	const char *path,
+	const git_diff_file *baseline,
+	const git_diff_file *target,
+	const git_diff_file *workdir,
+	void *payload);
+
+/** Checkout progress notification function */
+typedef void (*git_checkout_progress_cb)(
+	const char *path,
+	size_t completed_steps,
+	size_t total_steps,
+	void *payload);
+
+/**
+ * Checkout options structure
+ *
+ * Zero out for defaults.  Initialize with `GIT_CHECKOUT_OPTS_INIT` macro to
+ * correctly set the `version` field.  E.g.
+ *
+ *		git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+ */
+typedef struct git_checkout_opts {
+	unsigned int version;
+
+	unsigned int checkout_strategy; /** default will be a dry run */
+
+	int disable_filters;    /** don't apply filters like CRLF conversion */
+	unsigned int dir_mode;  /** default is 0755 */
+	unsigned int file_mode; /** default is 0644 or 0755 as dictated by blob */
+	int file_open_flags;    /** default is O_CREAT | O_TRUNC | O_WRONLY */
+
+	unsigned int notify_flags; /** see `git_checkout_notify_t` above */
+	git_checkout_notify_cb notify_cb;
+	void *notify_payload;
+
+	/* Optional callback to notify the consumer of checkout progress. */
+	git_checkout_progress_cb progress_cb;
+	void *progress_payload;
+
+	/** When not zeroed out, array of fnmatch patterns specifying which
+	 *  paths should be taken into account, otherwise all files.  Use
+	 *  GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as simple list.
+	 */
+	git_strarray paths;
+
+	git_tree *baseline; /** expected content of workdir, defaults to HEAD */
+} git_checkout_opts;
+
+#define GIT_CHECKOUT_OPTS_VERSION 1
+#define GIT_CHECKOUT_OPTS_INIT {GIT_CHECKOUT_OPTS_VERSION}
+
+/**
+ * Updates files in the index and the working tree to match the content of
+ * the commit pointed at by HEAD.
+ *
+ * @param repo repository to check out (must be non-bare)
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * branch, GIT_ERROR otherwise (use giterr_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_checkout_head(
+	git_repository *repo,
+	git_checkout_opts *opts);
+
+/**
+ * Updates files in the working tree to match the content of the index.
+ *
+ * @param repo repository into which to check out (must be non-bare)
+ * @param index index to be checked out (or NULL to use repository index)
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_checkout_index(
+	git_repository *repo,
+	git_index *index,
+	git_checkout_opts *opts);
+
+/**
+ * Updates files in the index and working tree to match the content of the
+ * tree pointed at by the treeish.
+ *
+ * @param repo repository to check out (must be non-bare)
+ * @param treeish a commit, tag or tree which content will be used to update
+ * the working directory
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_checkout_tree(
+	git_repository *repo,
+	const git_object *treeish,
+	git_checkout_opts *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/clone.h b/libgit2/include/git2/clone.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/clone.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_clone_h__
+#define INCLUDE_git_clone_h__
+
+#include "common.h"
+#include "types.h"
+#include "indexer.h"
+#include "checkout.h"
+#include "remote.h"
+
+
+/**
+ * @file git2/clone.h
+ * @brief Git cloning routines
+ * @defgroup git_clone Git cloning routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Clone options structure
+ *
+ * Use zeros to indicate default settings.  It's easiest to use the
+ * `GIT_CLONE_OPTIONS_INIT` macro:
+ *
+ *		git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ *
+ * - `checkout_opts` is options for the checkout step.  To disable checkout,
+ *   set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
+ * - `bare` should be set to zero to create a standard repo, non-zero for
+ *   a bare repo
+ * - `fetch_progress_cb` is optional callback for fetch progress. Be aware that
+ *   this is called inline with network and indexing operations, so performance
+ *   may be affected.
+ * - `fetch_progress_payload` is payload for fetch_progress_cb
+ *
+ *   ** "origin" remote options: **
+ * - `remote_name` is the name given to the "origin" remote.  The default is
+ *   "origin".
+ * - `pushurl` is a URL to be used for pushing.  NULL means use the fetch url.
+ * - `fetch_spec` is the fetch specification to be used for fetching.  NULL
+ *   results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.
+ * - `push_spec` is the fetch specification to be used for pushing.  NULL means
+ *   use the same spec as for fetching.
+ * - `cred_acquire_cb` is a callback to be used if credentials are required
+ *   during the initial fetch.
+ * - `cred_acquire_payload` is the payload for the above callback.
+ * - `transport` is a custom transport to be used for the initial fetch.  NULL
+ *   means use the transport autodetected from the URL.
+ * - `remote_callbacks` may be used to specify custom progress callbacks for
+ *   the origin remote before the fetch is initiated.
+ * - `remote_autotag` may be used to specify the autotag setting before the
+ *   initial fetch.  The default is GIT_REMOTE_DOWNLOAD_TAGS_ALL.
+ * - `checkout_branch` gives the name of the branch to checkout. NULL means
+ *   use the remote's HEAD.
+ */
+
+typedef struct git_clone_options {
+	unsigned int version;
+
+	git_checkout_opts checkout_opts;
+	int bare;
+	git_transfer_progress_callback fetch_progress_cb;
+	void *fetch_progress_payload;
+
+	const char *remote_name;
+	const char *pushurl;
+	const char *fetch_spec;
+	const char *push_spec;
+	git_cred_acquire_cb cred_acquire_cb;
+	void *cred_acquire_payload;
+	git_transport *transport;
+	git_remote_callbacks *remote_callbacks;
+	git_remote_autotag_option_t remote_autotag;
+	const char* checkout_branch;
+} git_clone_options;
+
+#define GIT_CLONE_OPTIONS_VERSION 1
+#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}}
+
+/**
+ * Clone a remote repository, and checkout the branch pointed to by the remote
+ * HEAD.
+ *
+ * @param out pointer that will receive the resulting repository object
+ * @param url the remote repository to clone
+ * @param local_path local directory to clone to
+ * @param options configuration options for the clone.  If NULL, the function
+ * works as though GIT_OPTIONS_INIT were passed.
+ * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_clone(
+		git_repository **out,
+		const char *url,
+		const char *local_path,
+		const git_clone_options *options);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/commit.h b/libgit2/include/git2/commit.h
--- a/libgit2/include/git2/commit.h
+++ b/libgit2/include/git2/commit.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -48,7 +48,7 @@
  * @param len the length of the short identifier
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len)
+GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len)
 {
 	return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
 }
@@ -76,7 +76,10 @@
  * @param commit a previously loaded commit.
  * @return object identity for the commit.
  */
-GIT_EXTERN(const git_oid *) git_commit_id(git_commit *commit);
+GIT_INLINE(const git_oid *) git_commit_id(const git_commit *commit)
+{
+	return git_object_id((const git_object *)commit);
+}
 
 /**
  * Get the encoding for the message of a commit,
@@ -88,7 +91,7 @@
  * @param commit a previously loaded commit.
  * @return NULL, or the encoding
  */
-GIT_EXTERN(const char *) git_commit_message_encoding(git_commit *commit);
+GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
 
 /**
  * Get the full message of a commit.
@@ -96,7 +99,7 @@
  * @param commit a previously loaded commit.
  * @return the message of a commit
  */
-GIT_EXTERN(const char *) git_commit_message(git_commit *commit);
+GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
 
 /**
  * Get the commit time (i.e. committer time) of a commit.
@@ -104,7 +107,7 @@
  * @param commit a previously loaded commit.
  * @return the time of a commit
  */
-GIT_EXTERN(git_time_t) git_commit_time(git_commit *commit);
+GIT_EXTERN(git_time_t) git_commit_time(const git_commit *commit);
 
 /**
  * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
@@ -112,7 +115,7 @@
  * @param commit a previously loaded commit.
  * @return positive or negative timezone offset, in minutes from UTC
  */
-GIT_EXTERN(int) git_commit_time_offset(git_commit *commit);
+GIT_EXTERN(int) git_commit_time_offset(const git_commit *commit);
 
 /**
  * Get the committer of a commit.
@@ -120,7 +123,7 @@
  * @param commit a previously loaded commit.
  * @return the committer of a commit
  */
-GIT_EXTERN(const git_signature *) git_commit_committer(git_commit *commit);
+GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit);
 
 /**
  * Get the author of a commit.
@@ -128,7 +131,7 @@
  * @param commit a previously loaded commit.
  * @return the author of a commit
  */
-GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit);
+GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
 
 /**
  * Get the tree pointed to by a commit.
@@ -137,7 +140,7 @@
  * @param commit a previously loaded commit.
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, git_commit *commit);
+GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, const git_commit *commit);
 
 /**
  * Get the id of the tree pointed to by a commit. This differs from
@@ -147,7 +150,7 @@
  * @param commit a previously loaded commit.
  * @return the id of tree pointed to by commit.
  */
-GIT_EXTERN(const git_oid *) git_commit_tree_oid(git_commit *commit);
+GIT_EXTERN(const git_oid *) git_commit_tree_id(const git_commit *commit);
 
 /**
  * Get the number of parents of this commit
@@ -155,17 +158,17 @@
  * @param commit a previously loaded commit.
  * @return integer of count of parents
  */
-GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit);
+GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit);
 
 /**
  * Get the specified parent of the commit.
  *
- * @param parent Pointer where to store the parent commit
+ * @param out Pointer where to store the parent commit
  * @param commit a previously loaded commit.
  * @param n the position of the parent (from 0 to `parentcount`)
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n);
+GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned int n);
 
 /**
  * Get the oid of a specified parent for a commit. This is different from
@@ -176,16 +179,35 @@
  * @param n the position of the parent (from 0 to `parentcount`)
  * @return the id of the parent, NULL on error.
  */
-GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned int n);
+GIT_EXTERN(const git_oid *) git_commit_parent_id(git_commit *commit, unsigned int n);
 
 /**
+ * Get the commit object that is the <n>th generation ancestor
+ * of the named commit object, following only the first parents.
+ * The returned commit has to be freed by the caller.
+ *
+ * Passing `0` as the generation number returns another instance of the
+ * base commit itself.
+ *
+ * @param ancestor Pointer where to store the ancestor commit
+ * @param commit a previously loaded commit.
+ * @param n the requested generation
+ * @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists
+ * or an error code
+ */
+GIT_EXTERN(int) git_commit_nth_gen_ancestor(
+	git_commit **ancestor,
+	const git_commit *commit,
+	unsigned int n);
+
+/**
  * Create a new commit in the repository using `git_object`
  * instances as parameters.
  *
- * The message will be cleaned up from excess whitespace
- * it will be made sure that the last line ends with a '\n'.
+ * The message will not be cleaned up. This can be achieved
+ * through `git_message_prettify()`.
  *
- * @param oid Pointer where to store the OID of the
+ * @param id Pointer where to store the OID of the
  *	newly created commit
  *
  * @param repo Repository where to store the commit
@@ -226,7 +248,7 @@
  *	the given reference will be updated to point to it
  */
 GIT_EXTERN(int) git_commit_create(
-		git_oid *oid,
+		git_oid *id,
 		git_repository *repo,
 		const char *update_ref,
 		const git_signature *author,
@@ -254,7 +276,7 @@
  * @see git_commit_create
  */
 GIT_EXTERN(int) git_commit_create_v(
-		git_oid *oid,
+		git_oid *id,
 		git_repository *repo,
 		const char *update_ref,
 		const git_signature *author,
diff --git a/libgit2/include/git2/common.h b/libgit2/include/git2/common.h
--- a/libgit2/include/git2/common.h
+++ b/libgit2/include/git2/common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -55,6 +55,10 @@
 #define GIT_WIN32 1
 #endif
 
+#ifdef __amigaos4__
+#include <netinet/in.h>
+#endif
+
 /**
  * @file git2/common.h
  * @brief Git common platform definitions
@@ -81,13 +85,10 @@
  */
 #define GIT_PATH_MAX 4096
 
-typedef struct {
-	char **strings;
-	size_t count;
-} git_strarray;
-
-GIT_EXTERN(void) git_strarray_free(git_strarray *array);
-GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+/**
+ * The string representation of the null object ID.
+ */
+#define GIT_OID_HEX_ZERO "0000000000000000000000000000000000000000"
 
 /**
  * Return the version of the libgit2 library
@@ -99,6 +100,91 @@
  */
 GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
 
+/**
+ * Combinations of these values describe the capabilities of libgit2.
+ */
+enum {
+	GIT_CAP_THREADS			= ( 1 << 0 ),
+	GIT_CAP_HTTPS			= ( 1 << 1 )
+};
+
+/**
+ * Query compile time options for libgit2.
+ *
+ * @return A combination of GIT_CAP_* values.
+ *
+ * - GIT_CAP_THREADS
+ *   Libgit2 was compiled with thread support. Note that thread support is still to be seen as a
+ *   'work in progress'.
+ *
+ * - GIT_CAP_HTTPS
+ *   Libgit2 supports the https:// protocol. This requires the open ssl library to be
+ *   found when compiling libgit2.
+ */
+GIT_EXTERN(int) git_libgit2_capabilities(void);
+
+
+enum {
+	GIT_OPT_GET_MWINDOW_SIZE,
+	GIT_OPT_SET_MWINDOW_SIZE,
+	GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
+	GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
+	GIT_OPT_GET_SEARCH_PATH,
+	GIT_OPT_SET_SEARCH_PATH,
+	GIT_OPT_GET_ODB_CACHE_SIZE,
+	GIT_OPT_SET_ODB_CACHE_SIZE,
+};
+
+/**
+ * Set or query a library global option
+ *
+ * Available options:
+ *
+ *	opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
+ *		Get the maximum mmap window size
+ *
+ *	opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
+ *		Set the maximum mmap window size
+ *
+ *	opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
+ *		Get the maximum memory that will be mapped in total by the library
+ *
+ *	opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
+ *		Set the maximum amount of memory that can be mapped at any time
+ *		by the library
+ *
+ *	opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
+ *		Get the search path for a given level of config data.  "level" must
+ *		be one of GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL, or
+ *		GIT_CONFIG_LEVEL_XDG.  The search path is written to the `out`
+ *		buffer up to size `len`.  Returns GIT_EBUFS if buffer is too small.
+ *
+ *	opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
+ *		Set the search path for a level of config data.  The search path
+ *		applied to shared attributes and ignore files, too.
+ *      - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
+ *		  Pass NULL to reset to the default (generally based on environment
+ *		  variables).  Use magic path `$PATH` to include the old value
+ *		  of the path (if you want to prepend or append, for instance).
+ *		- `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
+ *		  or GIT_CONFIG_LEVEL_XDG.
+ *
+ *	opts(GIT_OPT_GET_ODB_CACHE_SIZE):
+ *		Get the size of the libgit2 odb cache.
+ *
+ *	opts(GIT_OPT_SET_ODB_CACHE_SIZE):
+ *		Set the size of the of the libgit2 odb cache. This needs
+ *		to be done before git_repository_open is called, since
+ *		git_repository_open initializes the odb layer. Defaults
+ *		to 128.
+ *
+ * @param option Option key
+ * @param ... value to set the option
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_libgit2_opts(int option, ...);
+
 /** @} */
 GIT_END_DECL
+
 #endif
diff --git a/libgit2/include/git2/config.h b/libgit2/include/git2/config.h
--- a/libgit2/include/git2/config.h
+++ b/libgit2/include/git2/config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -20,22 +20,51 @@
 GIT_BEGIN_DECL
 
 /**
+ * Priority level of a config file.
+ * These priority levels correspond to the natural escalation logic
+ * (from higher to lower) when searching for config entries in git.git.
+ *
+ * git_config_open_default() and git_repository_config() honor those
+ * priority levels as well.
+ */
+enum {
+	GIT_CONFIG_LEVEL_SYSTEM = 1,	/**< System-wide configuration file. */
+	GIT_CONFIG_LEVEL_XDG = 2,		/**< XDG compatible configuration file (.config/git/config). */
+	GIT_CONFIG_LEVEL_GLOBAL = 3,	/**< User-specific configuration file, also called Global configuration file. */
+	GIT_CONFIG_LEVEL_LOCAL = 4,		/**< Repository specific configuration file. */
+	GIT_CONFIG_HIGHEST_LEVEL = -1,	/**< Represents the highest level of a config file. */
+};
+
+typedef struct {
+	const char *name;
+	const char *value;
+	unsigned int level;
+} git_config_entry;
+
+typedef int  (*git_config_foreach_cb)(const git_config_entry *, void *);
+
+
+/**
  * Generic backend that implements the interface to
  * access a configuration file
  */
-struct git_config_file {
+struct git_config_backend {
+	unsigned int version;
 	struct git_config *cfg;
 
 	/* Open means open the file/database and parse if necessary */
-	int (*open)(struct git_config_file *);
-	int (*get)(struct git_config_file *, const char *key, const char **value);
-	int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data);
-	int (*set)(struct git_config_file *, const char *key, const char *value);
-	int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
-	int (*del)(struct git_config_file *, const char *key);
-	int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data);
-	void (*free)(struct git_config_file *);
+	int (*open)(struct git_config_backend *, unsigned int level);
+	int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
+	int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
+	int (*set)(struct git_config_backend *, const char *key, const char *value);
+	int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
+	int (*del)(struct git_config_backend *, const char *key);
+	int (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
+	int (*refresh)(struct git_config_backend *);
+	void (*free)(struct git_config_backend *);
 };
+#define GIT_CONFIG_BACKEND_VERSION 1
+#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
 
 typedef enum {
 	GIT_CVAR_FALSE = 0,
@@ -61,11 +90,32 @@
  * may be used on any `git_config` call to load the
  * global configuration file.
  *
- * @param global_config_path Buffer of GIT_PATH_MAX length to store the path
- * @return 0 if a global configuration file has been
+ * This method will not guess the path to the xdg compatible
+ * config file (.config/git/config).
+ *
+ * @param out Buffer to store the path in
+ * @param length size of the buffer in bytes
+ * @return 0 if a global configuration file has been found. Its path will be stored in `buffer`.
+ */
+GIT_EXTERN(int) git_config_find_global(char *out, size_t length);
+
+/**
+ * Locate the path to the global xdg compatible configuration file
+ *
+ * The xdg compatible configuration file is usually
+ * located in `$HOME/.config/git/config`.
+ *
+ * This method will try to guess the full path to that
+ * file, if the file exists. The returned path
+ * may be used on any `git_config` call to load the
+ * xdg compatible configuration file.
+ *
+ * @param out Buffer to store the path in
+ * @param length size of the buffer in bytes
+ * @return 0 if a xdg compatible configuration file has been
  *	found. Its path will be stored in `buffer`.
  */
-GIT_EXTERN(int) git_config_find_global(char *global_config_path, size_t length);
+GIT_EXTERN(int) git_config_find_xdg(char *out, size_t length);
 
 /**
  * Locate the path to the system configuration file
@@ -73,35 +123,24 @@
  * If /etc/gitconfig doesn't exist, it will look for
  * %PROGRAMFILES%\Git\etc\gitconfig.
 
- * @param system_config_path Buffer of GIT_PATH_MAX length to store the path
+ * @param global_config_path Buffer to store the path in
+ * @param length size of the buffer in bytes
  * @return 0 if a system configuration file has been
  *	found. Its path will be stored in `buffer`.
  */
-GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length);
+GIT_EXTERN(int) git_config_find_system(char *out, size_t length);
 
 /**
- * Open the global configuration file
+ * Open the global, XDG and system configuration files
  *
- * Utility wrapper that calls `git_config_find_global`
- * and opens the located file, if it exists.
+ * Utility wrapper that finds the global, XDG and system configuration files
+ * and opens them into a single prioritized config object that can be
+ * used when accessing default config data outside a repository.
  *
  * @param out Pointer to store the config instance
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_open_global(git_config **out);
-
-/**
- * Create a configuration file backend for ondisk files
- *
- * These are the normal `.gitconfig` files that Core Git
- * processes. Note that you first have to add this file to a
- * configuration object before you can query it for configuration
- * variables.
- *
- * @param out the new backend
- * @param path where the config file is located
- */
-GIT_EXTERN(int) git_config_file__ondisk(struct git_config_file **out, const char *path);
+GIT_EXTERN(int) git_config_open_default(git_config **out);
 
 /**
  * Allocate a new configuration object
@@ -122,14 +161,21 @@
  *
  * Further queries on this config object will access each
  * of the config file instances in order (instances with
- * a higher priority will be accessed first).
+ * a higher priority level will be accessed first).
  *
  * @param cfg the configuration to add the file to
  * @param file the configuration file (backend) to add
- * @param priority the priority the backend should have
- * @return 0 or an error code
+ * @param level the priority level of the backend
+ * @param force if a config file already exists for the given
+ *  priority level, replace it
+ * @return 0 on success, GIT_EEXISTS when adding more than one file
+ *  for a given priority level (and force_replace set to 0), or error code
  */
-GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority);
+GIT_EXTERN(int) git_config_add_backend(
+	git_config *cfg,
+	git_config_backend *file,
+	unsigned int level,
+	int force);
 
 /**
  * Add an on-disk config file instance to an existing config
@@ -143,15 +189,22 @@
  *
  * Further queries on this config object will access each
  * of the config file instances in order (instances with
- * a higher priority will be accessed first).
+ * a higher priority level will be accessed first).
  *
  * @param cfg the configuration to add the file to
  * @param path path to the configuration file (backend) to add
- * @param priority the priority the backend should have
- * @return 0 or an error code
+ * @param level the priority level of the backend
+ * @param force if a config file already exists for the given
+ *  priority level, replace it
+ * @return 0 on success, GIT_EEXISTS when adding more than one file
+ *  for a given priority level (and force_replace set to 0),
+ *  GIT_ENOTFOUND when the file doesn't exist or error code
  */
-GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, int priority);
-
+GIT_EXTERN(int) git_config_add_file_ondisk(
+	git_config *cfg,
+	const char *path,
+	unsigned int level,
+	int force);
 
 /**
  * Create a new config instance containing a single on-disk file
@@ -161,11 +214,46 @@
  *	- git_config_new
  *	- git_config_add_file_ondisk
  *
- * @param cfg The configuration instance to create
+ * @param out The configuration instance to create
  * @param path Path to the on-disk file to open
+ * @return 0 on success, GIT_ENOTFOUND when the file doesn't exist
+ * or an error code
+ */
+GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path);
+
+/**
+ * Build a single-level focused config object from a multi-level one.
+ *
+ * The returned config object can be used to perform get/set/delete operations
+ * on a single specific level.
+ *
+ * Getting several times the same level from the same parent multi-level config
+ * will return different config instances, but containing the same config_file
+ * instance.
+ *
+ * @param out The configuration instance to create
+ * @param parent Multi-level config to search for the given level
+ * @param level Configuration level to search for
+ * @return 0, GIT_ENOTFOUND if the passed level cannot be found in the
+ * multi-level parent config, or an error code
+ */
+GIT_EXTERN(int) git_config_open_level(
+    git_config **out,
+    const git_config *parent,
+    unsigned int level);
+
+/**
+ * Reload changed config files
+ *
+ * A config file may be changed on disk out from under the in-memory
+ * config object.  This function causes us to look for files that have
+ * been modified since we last loaded them and refresh the config with
+ * the latest information.
+ *
+ * @param cfg The configuration to refresh
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path);
+GIT_EXTERN(int) git_config_refresh(git_config *cfg);
 
 /**
  * Free the configuration and its associated memory and files
@@ -175,24 +263,48 @@
 GIT_EXTERN(void) git_config_free(git_config *cfg);
 
 /**
+ * Get the git_config_entry of a config variable.
+ *
+ * The git_config_entry is owned by the config and should not be freed by the
+ * user.
+
+ * @param out pointer to the variable git_config_entry
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_entry(
+   const git_config_entry **out,
+	const git_config *cfg,
+	const char *name);
+
+/**
  * Get the value of an integer config variable.
  *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurence of the variable will be returned here.
+ *
  * @param out pointer to the variable where the value should be stored
  * @param cfg where to look for the variable
  * @param name the variable's name
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_get_int32(int32_t *out, git_config *cfg, const char *name);
+GIT_EXTERN(int) git_config_get_int32(int32_t *out, const git_config *cfg, const char *name);
 
 /**
  * Get the value of a long integer config variable.
  *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
  * @param out pointer to the variable where the value should be stored
  * @param cfg where to look for the variable
  * @param name the variable's name
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_get_int64(int64_t *out, git_config *cfg, const char *name);
+GIT_EXTERN(int) git_config_get_int64(int64_t *out, const git_config *cfg, const char *name);
 
 /**
  * Get the value of a boolean config variable.
@@ -200,12 +312,16 @@
  * This function uses the usual C convention of 0 being false and
  * anything else true.
  *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
  * @param out pointer to the variable where the value should be stored
  * @param cfg where to look for the variable
  * @param name the variable's name
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_get_bool(int *out, git_config *cfg, const char *name);
+GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char *name);
 
 /**
  * Get the value of a string config variable.
@@ -213,12 +329,16 @@
  * The string is owned by the variable and should not be freed by the
  * user.
  *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
  * @param out pointer to the variable's value
  * @param cfg where to look for the variable
  * @param name the variable's name
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_config_get_string(const char **out, git_config *cfg, const char *name);
+GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
 
 /**
  * Get each value of a multivar.
@@ -232,10 +352,11 @@
  * @param fn the function to be called on each value of the variable
  * @param data opaque pointer to pass to the callback
  */
-GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data);
+GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
 
 /**
- * Set the value of an integer config variable.
+ * Set the value of an integer config variable in the config file
+ * with the highest level (usually the local one).
  *
  * @param cfg where to look for the variable
  * @param name the variable's name
@@ -245,7 +366,8 @@
 GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value);
 
 /**
- * Set the value of a long integer config variable.
+ * Set the value of a long integer config variable in the config file
+ * with the highest level (usually the local one).
  *
  * @param cfg where to look for the variable
  * @param name the variable's name
@@ -255,7 +377,8 @@
 GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value);
 
 /**
- * Set the value of a boolean config variable.
+ * Set the value of a boolean config variable in the config file
+ * with the highest level (usually the local one).
  *
  * @param cfg where to look for the variable
  * @param name the variable's name
@@ -265,7 +388,8 @@
 GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
 
 /**
- * Set the value of a string config variable.
+ * Set the value of a string config variable in the config file
+ * with the highest level (usually the local one).
  *
  * A copy of the string is made and the user is free to use it
  * afterwards.
@@ -277,9 +401,8 @@
  */
 GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
 
-
 /**
- * Set a multivar
+ * Set a multivar in the local config file.
  *
  * @param cfg where to look for the variable
  * @param name the variable's name
@@ -289,12 +412,13 @@
 GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
 
 /**
- * Delete a config variable
+ * Delete a config variable from the config file
+ * with the highest level (usually the local one).
  *
  * @param cfg the configuration
  * @param name the variable to delete
  */
-GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name);
+GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
 
 /**
  * Perform an operation on each config variable.
@@ -302,18 +426,36 @@
  * The callback receives the normalized name and value of each variable
  * in the config backend, and the data pointer passed to this function.
  * As soon as one of the callback functions returns something other than 0,
- * this function returns that value.
+ * this function stops iterating and returns `GIT_EUSER`.
  *
  * @param cfg where to get the variables from
  * @param callback the function to call on each variable
  * @param payload the data to pass to the callback
- * @return 0 or the return value of the callback which didn't return 0
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_config_foreach(
-	git_config *cfg,
-	int (*callback)(const char *var_name, const char *value, void *payload),
+	const git_config *cfg,
+	git_config_foreach_cb callback,
 	void *payload);
 
+/**
+ * Perform an operation on each config variable matching a regular expression.
+ *
+ * This behaviors like `git_config_foreach` with an additional filter of a
+ * regular expression that filters which config keys are passed to the
+ * callback.
+ *
+ * @param cfg where to get the variables from
+ * @param regexp regular expression to match against config names
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ * @return 0 or the return value of the callback which didn't return 0
+ */
+GIT_EXTERN(int) git_config_foreach_match(
+	const git_config *cfg,
+	const char *regexp,
+	git_config_foreach_cb callback,
+	void *payload);
 
 /**
  * Query the value of a config variable and return it mapped to
@@ -324,7 +466,7 @@
  *
  * A mapping array looks as follows:
  *
- *	git_cvar_map autocrlf_mapping[3] = {
+ *	git_cvar_map autocrlf_mapping[] = {
  *		{GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
  *		{GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
  *		{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
@@ -349,7 +491,63 @@
  * @param map_n number of mapping objects in `maps`
  * @return 0 on success, error code otherwise
  */
-GIT_EXTERN(int) git_config_get_mapped(int *out, git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n);
+GIT_EXTERN(int) git_config_get_mapped(
+      int *out,
+      const git_config *cfg,
+      const char *name,
+      const git_cvar_map *maps,
+      size_t map_n);
+
+/**
+ * Maps a string value to an integer constant
+ *
+ * @param out place to store the result of the parsing
+ * @param maps array of `git_cvar_map` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @param value value to parse
+ */
+GIT_EXTERN(int) git_config_lookup_map_value(
+	int *out,
+	const git_cvar_map *maps,
+	size_t map_n,
+	const char *value);
+
+/**
+ * Parse a string value as a bool.
+ *
+ * Valid values for true are: 'true', 'yes', 'on', 1 or any
+ *  number different from 0
+ * Valid values for false are: 'false', 'no', 'off', 0
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ */
+GIT_EXTERN(int) git_config_parse_bool(int *out, const char *value);
+
+/**
+ * Parse a string value as an int32.
+ *
+ * An optional value suffix of 'k', 'm', or 'g' will
+ * cause the value to be multiplied by 1024, 1048576,
+ * or 1073741824 prior to output.
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ */
+GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
+
+/**
+ * Parse a string value as an int64.
+ *
+ * An optional value suffix of 'k', 'm', or 'g' will
+ * cause the value to be multiplied by 1024, 1048576,
+ * or 1073741824 prior to output.
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ */
+GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
+
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/cred_helpers.h b/libgit2/include/git2/cred_helpers.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/cred_helpers.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_cred_helpers_h__
+#define INCLUDE_git_cred_helpers_h__
+
+#include "transport.h"
+
+/**
+ * @file git2/cred_helpers.h
+ * @brief Utility functions for credential management
+ * @defgroup git_cred_helpers credential management helpers
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Payload for git_cred_stock_userpass_plaintext.
+ */
+typedef struct git_cred_userpass_payload {
+	char *username;
+	char *password;
+} git_cred_userpass_payload;
+
+
+/**
+ * Stock callback usable as a git_cred_acquire_cb.  This calls
+ * git_cred_userpass_plaintext_new unless the protocol has not specified
+ * GIT_CREDTYPE_USERPASS_PLAINTEXT as an allowed type.
+ *
+ * @param cred The newly created credential object.
+ * @param url The resource for which we are demanding a credential.
+ * @param username_from_url The username that was embedded in a "user@host"
+ *                          remote url, or NULL if not included.
+ * @param allowed_types A bitmask stating which cred types are OK to return.
+ * @param payload The payload provided when specifying this callback.  (This is
+ *        interpreted as a `git_cred_userpass_payload*`.)
+ */
+GIT_EXTERN(int) git_cred_userpass(
+		git_cred **cred,
+		const char *url,
+		const char *user_from_url,
+		unsigned int allowed_types,
+		void *payload);
+
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/diff.h b/libgit2/include/git2/diff.h
--- a/libgit2/include/git2/diff.h
+++ b/libgit2/include/git2/diff.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,6 +17,9 @@
  * @file git2/diff.h
  * @brief Git tree and file differencing routines.
  *
+ * Overview
+ * --------
+ *
  * Calculating diffs is generally done in two phases: building a diff list
  * then traversing the diff list.  This makes is easier to share logic
  * across the various types of diffs (tree vs tree, workdir vs index, etc.),
@@ -24,156 +27,327 @@
  * such as rename detected, in between the steps.  When you are done with a
  * diff list object, it must be freed.
  *
+ * Terminology
+ * -----------
+ *
+ * To understand the diff APIs, you should know the following terms:
+ *
+ * - A `diff` or `diff list` represents the cumulative list of differences
+ *   between two snapshots of a repository (possibly filtered by a set of
+ *   file name patterns).  This is the `git_diff_list` object.
+ * - A `delta` is a file pair with an old and new revision.  The old version
+ *   may be absent if the file was just created and the new version may be
+ *   absent if the file was deleted.  A diff is mostly just a list of deltas.
+ * - A `binary` file / delta is a file (or pair) for which no text diffs
+ *   should be generated.  A diff list can contain delta entries that are
+ *   binary, but no diff content will be output for those files.  There is
+ *   a base heuristic for binary detection and you can further tune the
+ *   behavior with git attributes or diff flags and option settings.
+ * - A `hunk` is a span of modified lines in a delta along with some stable
+ *   surrounding context.  You can configure the amount of context and other
+ *   properties of how hunks are generated.  Each hunk also comes with a
+ *   header that described where it starts and ends in both the old and new
+ *   versions in the delta.
+ * - A `line` is a range of characters inside a hunk.  It could be a context
+ *   line (i.e. in both old and new versions), an added line (i.e. only in
+ *   the new version), or a removed line (i.e. only in the old version).
+ *   Unfortunately, we don't know anything about the encoding of data in the
+ *   file being diffed, so we cannot tell you much about the line content.
+ *   Line data will not be NUL-byte terminated, however, because it will be
+ *   just a span of bytes inside the larger file.
+ *
  * @ingroup Git
  * @{
  */
 GIT_BEGIN_DECL
 
-enum {
+/**
+ * Flags for diff options.  A combination of these flags can be passed
+ * in via the `flags` value in the `git_diff_options`.
+ */
+typedef enum {
+	/** Normal diff, the default */
 	GIT_DIFF_NORMAL = 0,
+	/** Reverse the sides of the diff */
 	GIT_DIFF_REVERSE = (1 << 0),
+	/** Treat all files as text, disabling binary attributes & detection */
 	GIT_DIFF_FORCE_TEXT = (1 << 1),
+	/** Ignore all whitespace */
 	GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
+	/** Ignore changes in amount of whitespace */
 	GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
+	/** Ignore whitespace at end of line */
 	GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
+	/** Exclude submodules from the diff completely */
 	GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
+	/** Use the "patience diff" algorithm (currently unimplemented) */
 	GIT_DIFF_PATIENCE = (1 << 6),
+	/** Include ignored files in the diff list */
 	GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
+	/** Include untracked files in the diff list */
 	GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
+	/** Include unmodified files in the diff list */
 	GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
+	/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory
+	 *  will be marked with only a single entry in the diff list; this flag
+	 *  adds all files under the directory as UNTRACKED entries, too.
+	 */
 	GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
-};
+	/** If the pathspec is set in the diff options, this flags means to
+	 *  apply it as an exact match instead of as an fnmatch pattern.
+	 */
+	GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
+	/** Use case insensitive filename comparisons */
+	GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
+	/** When generating patch text, include the content of untracked files */
+	GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
+	/** Disable updating of the `binary` flag in delta records.  This is
+	 *  useful when iterating over a diff if you don't need hunk and data
+	 *  callbacks and want to avoid having to load file completely.
+	 */
+	GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
+	/** Normally, a type change between files will be converted into a
+	 *  DELETED record for the old and an ADDED record for the new; this
+	 *  options enabled the generation of TYPECHANGE delta records.
+	 */
+	GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
+	/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
+	 *  generally show as a DELETED blob.  This flag tries to correctly
+	 *  label blob->tree transitions as TYPECHANGE records with new_file's
+	 *  mode set to tree.  Note: the tree SHA will not be available.
+	 */
+	GIT_DIFF_INCLUDE_TYPECHANGE_TREES  = (1 << 16),
+	/** Ignore file mode changes */
+	GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
+	/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
+	 *  will be marked with only a single entry in the diff list; this flag
+	 *  adds all files under the directory as IGNORED entries, too.
+	 */
+	GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
+} git_diff_option_t;
 
 /**
- * Structure describing options about how the diff should be executed.
- *
- * Setting all values of the structure to zero will yield the default
- * values.  Similarly, passing NULL for the options structure will
- * give the defaults.  The default values are marked below.
+ * The diff list object that contains all individual file deltas.
  *
- * @todo Most of the parameters here are not actually supported at this time.
+ * This is an opaque structure which will be allocated by one of the diff
+ * generator functions below (such as `git_diff_tree_to_tree`).  You are
+ * responsible for releasing the object memory when done, using the
+ * `git_diff_list_free()` function.
  */
-typedef struct {
-	uint32_t flags;				/**< defaults to GIT_DIFF_NORMAL */
-	uint16_t context_lines;		/**< defaults to 3 */
-	uint16_t interhunk_lines;	/**< defaults to 3 */
-	char *old_prefix;			/**< defaults to "a" */
-	char *new_prefix;			/**< defaults to "b" */
-	git_strarray pathspec;		/**< defaults to show all paths */
-} git_diff_options;
+typedef struct git_diff_list git_diff_list;
 
 /**
- * The diff list object that contains all individual file deltas.
+ * Flags for the delta object and the file objects on each side.
+ *
+ * These flags are used for both the `flags` value of the `git_diff_delta`
+ * and the flags for the `git_diff_file` objects representing the old and
+ * new sides of the delta.  Values outside of this public range should be
+ * considered reserved for internal or future use.
  */
-typedef struct git_diff_list git_diff_list;
-
-enum {
-	GIT_DIFF_FILE_VALID_OID  = (1 << 0),
-	GIT_DIFF_FILE_FREE_PATH  = (1 << 1),
-	GIT_DIFF_FILE_BINARY     = (1 << 2),
-	GIT_DIFF_FILE_NOT_BINARY = (1 << 3),
-	GIT_DIFF_FILE_FREE_DATA  = (1 << 4),
-	GIT_DIFF_FILE_UNMAP_DATA = (1 << 5)
-};
+typedef enum {
+	GIT_DIFF_FLAG_BINARY     = (1 << 0), /** file(s) treated as binary data */
+	GIT_DIFF_FLAG_NOT_BINARY = (1 << 1), /** file(s) treated as text data */
+	GIT_DIFF_FLAG_VALID_OID  = (1 << 2), /** `oid` value is known correct */
+} git_diff_flag_t;
 
 /**
  * What type of change is described by a git_diff_delta?
+ *
+ * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run
+ * `git_diff_find_similar()` on the diff list object.
+ *
+ * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE`
+ * in the option flags (otherwise type changes will be split into ADDED /
+ * DELETED pairs).
  */
 typedef enum {
-	GIT_DELTA_UNMODIFIED = 0,
-	GIT_DELTA_ADDED = 1,
-	GIT_DELTA_DELETED = 2,
-	GIT_DELTA_MODIFIED = 3,
-	GIT_DELTA_RENAMED = 4,
-	GIT_DELTA_COPIED = 5,
-	GIT_DELTA_IGNORED = 6,
-	GIT_DELTA_UNTRACKED = 7
+	GIT_DELTA_UNMODIFIED = 0, /** no changes */
+	GIT_DELTA_ADDED = 1,	  /** entry does not exist in old version */
+	GIT_DELTA_DELETED = 2,	  /** entry does not exist in new version */
+	GIT_DELTA_MODIFIED = 3,   /** entry content changed between old and new */
+	GIT_DELTA_RENAMED = 4,    /** entry was renamed between old and new */
+	GIT_DELTA_COPIED = 5,     /** entry was copied from another old entry */
+	GIT_DELTA_IGNORED = 6,    /** entry is ignored item in workdir */
+	GIT_DELTA_UNTRACKED = 7,  /** entry is untracked item in workdir */
+	GIT_DELTA_TYPECHANGE = 8, /** type of entry changed between old and new */
 } git_delta_t;
 
 /**
- * Description of one side of a diff.
+ * Description of one side of a diff entry.
+ *
+ * Although this is called a "file", it may actually represent a file, a
+ * symbolic link, a submodule commit id, or even a tree (although that only
+ * if you are tracking type changes or ignored/untracked directories).
+ *
+ * The `oid` is the `git_oid` of the item.  If the entry represents an
+ * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
+ * then the oid will be zeroes.
+ *
+ * `path` is the NUL-terminated path to the entry relative to the working
+ * directory of the repository.
+ *
+ * `size` is the size of the entry in bytes.
+ *
+ * `flags` is a combination of the `git_diff_flag_t` types
+ *
+ * `mode` is, roughly, the stat() `st_mode` value for the item.  This will
+ * be restricted to one of the `git_filemode_t` values.
  */
 typedef struct {
-	git_oid oid;
-	char *path;
-	uint16_t mode;
-	git_off_t size;
-	unsigned int flags;
+	git_oid     oid;
+	const char *path;
+	git_off_t   size;
+	uint32_t    flags;
+	uint16_t    mode;
 } git_diff_file;
 
 /**
- * Description of changes to one file.
+ * Description of changes to one entry.
  *
- * When iterating over a diff list object, this will generally be passed to
- * most callback functions and you can use the contents to understand
- * exactly what has changed.
+ * When iterating over a diff list object, this will be passed to most
+ * callback functions and you can use the contents to understand exactly
+ * what has changed.
  *
- * Under some circumstances, not all fields will be filled in, but the code
- * generally tries to fill in as much as possible.  One example is that the
- * "binary" field will not actually look at file contents if you do not
- * pass in hunk and/or line callbacks to the diff foreach iteration function.
- * It will just use the git attributes for those files.
+ * The `old_file` represents the "from" side of the diff and the `new_file`
+ * represents to "to" side of the diff.  What those means depend on the
+ * function that was used to generate the diff and will be documented below.
+ * You can also use the `GIT_DIFF_REVERSE` flag to flip it around.
+ *
+ * Although the two sides of the delta are named "old_file" and "new_file",
+ * they actually may correspond to entries that represent a file, a symbolic
+ * link, a submodule commit id, or even a tree (if you are tracking type
+ * changes or ignored/untracked directories).
+ *
+ * Under some circumstances, in the name of efficiency, not all fields will
+ * be filled in, but we generally try to fill in as much as possible.  One
+ * example is that the "flags" field may not have either the `BINARY` or the
+ * `NOT_BINARY` flag set to avoid examining file contents if you do not pass
+ * in hunk and/or line callbacks to the diff foreach iteration function.  It
+ * will just use the git attributes for those files.
  */
 typedef struct {
 	git_diff_file old_file;
 	git_diff_file new_file;
 	git_delta_t   status;
-	unsigned int  similarity; /**< for RENAMED and COPIED, value 0-100 */
-	int           binary;
+	uint32_t      similarity; /**< for RENAMED and COPIED, value 0-100 */
+	uint32_t      flags;
 } git_diff_delta;
 
 /**
+ * Diff notification callback function.
+ *
+ * The callback will be called for each file, just before the `git_delta_t`
+ * gets inserted into the diff list.
+ *
+ * When the callback:
+ * - returns < 0, the diff process will be aborted.
+ * - returns > 0, the delta will not be inserted into the diff list, but the
+ *		diff process continues.
+ * - returns 0, the delta is inserted into the diff list, and the diff process
+ *		continues.
+ */
+typedef int (*git_diff_notify_cb)(
+	const git_diff_list *diff_so_far,
+	const git_diff_delta *delta_to_add,
+	const char *matched_pathspec,
+	void *payload);
+
+/**
+ * Structure describing options about how the diff should be executed.
+ *
+ * Setting all values of the structure to zero will yield the default
+ * values.  Similarly, passing NULL for the options structure will
+ * give the defaults.  The default values are marked below.
+ *
+ * - `flags` is a combination of the `git_diff_option_t` values above
+ * - `context_lines` is the number of unchanged lines that define the
+ *    boundary of a hunk (and to display before and after)
+ * - `interhunk_lines` is the maximum number of unchanged lines between
+ *    hunk boundaries before the hunks will be merged into a one.
+ * - `old_prefix` is the virtual "directory" to prefix to old file names
+ *   in hunk headers (default "a")
+ * - `new_prefix` is the virtual "directory" to prefix to new file names
+ *   in hunk headers (default "b")
+ * - `pathspec` is an array of paths / fnmatch patterns to constrain diff
+ * - `max_size` is a file size (in bytes) above which a blob will be marked
+ *   as binary automatically; pass a negative value to disable.
+ * - `notify_cb` is an optional callback function, notifying the consumer of
+ *   which files are being examined as the diff is generated
+ * - `notify_payload` is the payload data to pass to the `notify_cb` function
+ */
+typedef struct {
+	unsigned int version;      /**< version for the struct */
+	uint32_t flags;            /**< defaults to GIT_DIFF_NORMAL */
+	uint16_t context_lines;    /**< defaults to 3 */
+	uint16_t interhunk_lines;  /**< defaults to 0 */
+	const char *old_prefix;    /**< defaults to "a" */
+	const char *new_prefix;    /**< defaults to "b" */
+	git_strarray pathspec;     /**< defaults to include all paths */
+	git_off_t max_size;        /**< defaults to 512MB */
+	git_diff_notify_cb notify_cb;
+	void *notify_payload;
+} git_diff_options;
+
+#define GIT_DIFF_OPTIONS_VERSION 1
+#define GIT_DIFF_OPTIONS_INIT {GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_NORMAL, 3}
+
+/**
  * When iterating over a diff, callback that will be made per file.
+ *
+ * @param delta A pointer to the delta data for the file
+ * @param progress Goes from 0 to 1 over the diff list
+ * @param payload User-specified pointer from foreach function
  */
-typedef int (*git_diff_file_fn)(
-	void *cb_data,
-	git_diff_delta *delta,
-	float progress);
+typedef int (*git_diff_file_cb)(
+	const git_diff_delta *delta,
+	float progress,
+	void *payload);
 
 /**
  * Structure describing a hunk of a diff.
  */
 typedef struct {
-	int old_start;
-	int old_lines;
-	int new_start;
-	int new_lines;
+	int old_start; /** Starting line number in old_file */
+	int old_lines; /** Number of lines in old_file */
+	int new_start; /** Starting line number in new_file */
+	int new_lines; /** Number of lines in new_file */
 } git_diff_range;
 
 /**
  * When iterating over a diff, callback that will be made per hunk.
  */
-typedef int (*git_diff_hunk_fn)(
-	void *cb_data,
-	git_diff_delta *delta,
-	git_diff_range *range,
+typedef int (*git_diff_hunk_cb)(
+	const git_diff_delta *delta,
+	const git_diff_range *range,
 	const char *header,
-	size_t header_len);
+	size_t header_len,
+	void *payload);
 
 /**
  * Line origin constants.
  *
  * These values describe where a line came from and will be passed to
- * the git_diff_data_fn when iterating over a diff.  There are some
- * special origin contants at the end that are used for the text
+ * the git_diff_data_cb when iterating over a diff.  There are some
+ * special origin constants at the end that are used for the text
  * output callbacks to demarcate lines that are actually part of
  * the file or hunk headers.
  */
-enum {
-	/* these values will be sent to `git_diff_data_fn` along with the line */
+typedef enum {
+	/* These values will be sent to `git_diff_data_cb` along with the line */
 	GIT_DIFF_LINE_CONTEXT   = ' ',
 	GIT_DIFF_LINE_ADDITION  = '+',
 	GIT_DIFF_LINE_DELETION  = '-',
-	GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
+	GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< Removed line w/o LF & added one with */
 	GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
-	/* these values will only be sent to a `git_diff_data_fn` when the content
-	 * of a diff is being formatted (eg. through git_diff_print_patch() or
-	 * git_diff_print_compact(), for instance).
+
+	/* The following values will only be sent to a `git_diff_data_cb` when
+	 * the content of a diff is being formatted (eg. through
+	 * git_diff_print_patch() or git_diff_print_compact(), for instance).
 	 */
 	GIT_DIFF_LINE_FILE_HDR  = 'F',
 	GIT_DIFF_LINE_HUNK_HDR  = 'H',
 	GIT_DIFF_LINE_BINARY    = 'B'
-};
+} git_diff_line_t;
 
 /**
  * When iterating over a diff, callback that will be made per text diff
@@ -183,14 +357,109 @@
  * of text.  This uses some extra GIT_DIFF_LINE_... constants for output
  * of lines of file and hunk headers.
  */
-typedef int (*git_diff_data_fn)(
-	void *cb_data,
-	git_diff_delta *delta,
-	git_diff_range *range,
-	char line_origin, /**< GIT_DIFF_LINE_... value from above */
-	const char *content,
-	size_t content_len);
+typedef int (*git_diff_data_cb)(
+	const git_diff_delta *delta, /** delta that contains this data */
+	const git_diff_range *range, /** range of lines containing this data */
+	char line_origin,            /** git_diff_list_t value from above */
+	const char *content,         /** diff data - not NUL terminated */
+	size_t content_len,          /** number of bytes of diff data */
+	void *payload);              /** user reference data */
 
+/**
+ * The diff patch is used to store all the text diffs for a delta.
+ *
+ * You can easily loop over the content of patches and get information about
+ * them.
+ */
+typedef struct git_diff_patch git_diff_patch;
+
+/**
+ * Flags to control the behavior of diff rename/copy detection.
+ */
+typedef enum {
+	/** look for renames? (`--find-renames`) */
+	GIT_DIFF_FIND_RENAMES = (1 << 0),
+	/** consider old side of modified for renames? (`--break-rewrites=N`) */
+	GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1 << 1),
+
+	/** look for copies? (a la `--find-copies`) */
+	GIT_DIFF_FIND_COPIES = (1 << 2),
+	/** consider unmodified as copy sources? (`--find-copies-harder`) */
+	GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
+
+	/** split large rewrites into delete/add pairs (`--break-rewrites=/M`) */
+	GIT_DIFF_FIND_AND_BREAK_REWRITES = (1 << 4),
+
+	/** turn on all finding features */
+	GIT_DIFF_FIND_ALL = (0x1f),
+
+	/** measure similarity ignoring leading whitespace (default) */
+	GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
+	/** measure similarity ignoring all whitespace */
+	GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 6),
+	/** measure similarity including all data */
+	GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 7),
+} git_diff_find_t;
+
+/**
+ * Pluggable similarity metric
+ */
+typedef struct {
+	int (*file_signature)(
+		void **out, const git_diff_file *file,
+		const char *fullpath, void *payload);
+	int (*buffer_signature)(
+		void **out, const git_diff_file *file,
+		const char *buf, size_t buflen, void *payload);
+	void (*free_signature)(void *sig, void *payload);
+	int (*similarity)(int *score, void *siga, void *sigb, void *payload);
+	void *payload;
+} git_diff_similarity_metric;
+
+/**
+ * Control behavior of rename and copy detection
+ *
+ * These options mostly mimic parameters that can be passed to git-diff.
+ *
+ * - `rename_threshold` is the same as the -M option with a value
+ * - `copy_threshold` is the same as the -C option with a value
+ * - `rename_from_rewrite_threshold` matches the top of the -B option
+ * - `break_rewrite_threshold` matches the bottom of the -B option
+ * - `target_limit` matches the -l option
+ *
+ * The `metric` option allows you to plug in a custom similarity metric.
+ * Set it to NULL for the default internal metric which is based on sampling
+ * hashes of ranges of data in the file.  The default metric is a pretty
+ * good similarity approximation that should work fairly well for both text
+ * and binary data, and is pretty fast with fixed memory overhead.
+ */
+typedef struct {
+	unsigned int version;
+
+	/** Combination of git_diff_find_t values (default FIND_RENAMES) */
+	unsigned int flags;
+
+	/** Similarity to consider a file renamed (default 50) */
+	unsigned int rename_threshold;
+	/** Similarity of modified to be eligible rename source (default 50) */
+	unsigned int rename_from_rewrite_threshold;
+	/** Similarity to consider a file a copy (default 50) */
+	unsigned int copy_threshold;
+	/** Similarity to split modify into delete/add pair (default 60) */
+	unsigned int break_rewrite_threshold;
+
+	/** Maximum similarity sources to examine (a la diff's `-l` option or
+	 *  the `diff.renameLimit` config) (default 200)
+	 */
+	unsigned int target_limit;
+
+	/** Pluggable similarity metric; pass NULL to use internal metric */
+	git_diff_similarity_metric *metric;
+} git_diff_find_options;
+
+#define GIT_DIFF_FIND_OPTIONS_VERSION 1
+#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
+
 /** @name Diff List Generator Functions
  *
  * These are the functions you would use to create (or destroy) a
@@ -204,69 +473,104 @@
 GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
 
 /**
- * Compute a difference between two tree objects.
+ * Create a diff list with the difference between two tree objects.
  *
+ * This is equivalent to `git diff <old-tree> <new-tree>`
+ *
+ * The first tree will be used for the "old_file" side of the delta and the
+ * second tree will be used for the "new_file" side of the delta.
+ *
+ * @param diff Output pointer to a git_diff_list pointer to be allocated.
  * @param repo The repository containing the trees.
- * @param opts Structure with options to influence diff or NULL for defaults.
  * @param old_tree A git_tree object to diff from.
  * @param new_tree A git_tree object to diff to.
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ * @param opts Structure with options to influence diff or NULL for defaults.
  */
 GIT_EXTERN(int) git_diff_tree_to_tree(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
 	git_tree *old_tree,
 	git_tree *new_tree,
-	git_diff_list **diff);
+	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Compute a difference between a tree and the index.
+ * Create a diff list between a tree and repository index.
  *
+ * This is equivalent to `git diff --cached <treeish>` or if you pass
+ * the HEAD tree, then like `git diff --cached`.
+ *
+ * The tree you pass will be used for the "old_file" side of the delta, and
+ * the index will be used for the "new_file" side of the delta.
+ *
+ * @param diff Output pointer to a git_diff_list pointer to be allocated.
  * @param repo The repository containing the tree and index.
- * @param opts Structure with options to influence diff or NULL for defaults.
  * @param old_tree A git_tree object to diff from.
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ * @param index The index to diff with; repo index used if NULL.
+ * @param opts Structure with options to influence diff or NULL for defaults.
  */
-GIT_EXTERN(int) git_diff_index_to_tree(
+GIT_EXTERN(int) git_diff_tree_to_index(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
 	git_tree *old_tree,
-	git_diff_list **diff);
+	git_index *index,
+	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Compute a difference between the working directory and the index.
+ * Create a diff list between the repository index and the workdir directory.
  *
+ * This matches the `git diff` command.  See the note below on
+ * `git_diff_tree_to_workdir` for a discussion of the difference between
+ * `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
+ * using libgit2.
+ *
+ * The index will be used for the "old_file" side of the delta, and the
+ * working directory will be used for the "new_file" side of the delta.
+ *
+ * @param diff Output pointer to a git_diff_list pointer to be allocated.
  * @param repo The repository.
+ * @param index The index to diff from; repo index used if NULL.
  * @param opts Structure with options to influence diff or NULL for defaults.
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
  */
-GIT_EXTERN(int) git_diff_workdir_to_index(
+GIT_EXTERN(int) git_diff_index_to_workdir(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
-	git_diff_list **diff);
+	git_index *index,
+	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
- * Compute a difference between the working directory and a tree.
+ * Create a diff list between a tree and the working directory.
  *
- * This returns strictly the differences between the tree and the
- * files contained in the working directory, regardless of the state
- * of files in the index.  There is no direct equivalent in C git.
+ * The tree you provide will be used for the "old_file" side of the delta,
+ * and the working directory will be used for the "new_file" side.
  *
- * This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'.  Those
- * commands diff the tree, the index, and the workdir.  To emulate those
- * functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
- * then call `git_diff_merge` on the results.
+ * Please note: this is *NOT* the same as `git diff <treeish>`.  Running
+ * `git diff HEAD` or the like actually uses information from the index,
+ * along with the tree and working directory info.
  *
+ * This function returns strictly the differences between the tree and the
+ * files contained in the working directory, regardless of the state of
+ * files in the index.  It may come as a surprise, but there is no direct
+ * equivalent in core git.
+ *
+ * To emulate `git diff <treeish>`, call both `git_diff_tree_to_index` and
+ * `git_diff_index_to_workdir`, then call `git_diff_merge` on the results.
+ * That will yield a `git_diff_list` that matches the git output.
+ *
+ * If this seems confusing, take the case of a file with a staged deletion
+ * where the file has then been put back into the working dir and modified.
+ * The tree-to-workdir diff for that file is 'modified', but core git would
+ * show status 'deleted' since there is a pending deletion in the index.
+ *
+ * @param diff A pointer to a git_diff_list pointer that will be allocated.
  * @param repo The repository containing the tree.
- * @param opts Structure with options to influence diff or NULL for defaults.
  * @param old_tree A git_tree object to diff from.
- * @param diff A pointer to a git_diff_list pointer that will be allocated.
+ * @param opts Structure with options to influence diff or NULL for defaults.
  */
-GIT_EXTERN(int) git_diff_workdir_to_tree(
+GIT_EXTERN(int) git_diff_tree_to_workdir(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
 	git_tree *old_tree,
-	git_diff_list **diff);
+	const git_diff_options *opts); /**< can be NULL for defaults */
 
 /**
  * Merge one diff list into another.
@@ -285,6 +589,22 @@
 	git_diff_list *onto,
 	const git_diff_list *from);
 
+/**
+ * Transform a diff list marking file renames, copies, etc.
+ *
+ * This modifies a diff list in place, replacing old entries that look
+ * like renames or copies with new entries reflecting those changes.
+ * This also will, if requested, break modified files into add/remove
+ * pairs if the amount of change is above a threshold.
+ *
+ * @param diff Diff list to run detection algorithms on
+ * @param options Control how detection should be run, NULL for defaults
+ * @return 0 on success, -1 on failure
+ */
+GIT_EXTERN(int) git_diff_find_similar(
+	git_diff_list *diff,
+	git_diff_find_options *options);
+
 /**@}*/
 
 
@@ -296,38 +616,274 @@
 /**@{*/
 
 /**
- * Iterate over a diff list issuing callbacks.
+ * Loop over all deltas in a diff list issuing callbacks.
  *
- * If the hunk and/or line callbacks are not NULL, then this will calculate
- * text diffs for all files it thinks are not binary.  If those are both
- * NULL, then this will not bother with the text diffs, so it can be
- * efficient.
+ * This will iterate through all of the files described in a diff.  You
+ * should provide a file callback to learn about each file.
+ *
+ * The "hunk" and "line" callbacks are optional, and the text diff of the
+ * files will only be calculated if they are not NULL.  Of course, these
+ * callbacks will not be invoked for binary files on the diff list or for
+ * files whose only changed is a file mode change.
+ *
+ * Returning a non-zero value from any of the callbacks will terminate
+ * the iteration and cause this return `GIT_EUSER`.
+ *
+ * @param diff A git_diff_list generated by one of the above functions.
+ * @param file_cb Callback function to make per file in the diff.
+ * @param hunk_cb Optional callback to make per hunk of text diff.  This
+ *                callback is called to describe a range of lines in the
+ *                diff.  It will not be issued for binary files.
+ * @param line_cb Optional callback to make per line of diff text.  This
+ *                same callback will be made for context lines, added, and
+ *                removed lines, and even for a deleted trailing newline.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_diff_foreach(
 	git_diff_list *diff,
-	void *cb_data,
-	git_diff_file_fn file_cb,
-	git_diff_hunk_fn hunk_cb,
-	git_diff_data_fn line_cb);
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb line_cb,
+	void *payload);
 
 /**
  * Iterate over a diff generating text output like "git diff --name-status".
+ *
+ * Returning a non-zero value from the callbacks will terminate the
+ * iteration and cause this return `GIT_EUSER`.
+ *
+ * @param diff A git_diff_list generated by one of the above functions.
+ * @param print_cb Callback to make per line of diff text.
+ * @param payload Reference pointer that will be passed to your callback.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_diff_print_compact(
 	git_diff_list *diff,
-	void *cb_data,
-	git_diff_data_fn print_cb);
+	git_diff_data_cb print_cb,
+	void *payload);
 
 /**
+ * Look up the single character abbreviation for a delta status code.
+ *
+ * When you call `git_diff_print_compact` it prints single letter codes into
+ * the output such as 'A' for added, 'D' for deleted, 'M' for modified, etc.
+ * It is sometimes convenient to convert a git_delta_t value into these
+ * letters for your own purposes.  This function does just that.  By the
+ * way, unmodified will return a space (i.e. ' ').
+ *
+ * @param delta_t The git_delta_t value to look up
+ * @return The single character label for that code
+ */
+GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
+
+/**
  * Iterate over a diff generating text output like "git diff".
  *
  * This is a super easy way to generate a patch from a diff.
+ *
+ * Returning a non-zero value from the callbacks will terminate the
+ * iteration and cause this return `GIT_EUSER`.
+ *
+ * @param diff A git_diff_list generated by one of the above functions.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @param print_cb Callback function to output lines of the diff.  This
+ *                 same function will be called for file headers, hunk
+ *                 headers, and diff lines.  Fortunately, you can probably
+ *                 use various GIT_DIFF_LINE constants to determine what
+ *                 text you are given.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_diff_print_patch(
 	git_diff_list *diff,
-	void *cb_data,
-	git_diff_data_fn print_cb);
+	git_diff_data_cb print_cb,
+	void *payload);
 
+/**
+ * Query how many diff records are there in a diff list.
+ *
+ * @param diff A git_diff_list generated by one of the above functions
+ * @return Count of number of deltas in the list
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas(git_diff_list *diff);
+
+/**
+ * Query how many diff deltas are there in a diff list filtered by type.
+ *
+ * This works just like `git_diff_entrycount()` with an extra parameter
+ * that is a `git_delta_t` and returns just the count of how many deltas
+ * match that particular type.
+ *
+ * @param diff A git_diff_list generated by one of the above functions
+ * @param type A git_delta_t value to filter the count
+ * @return Count of number of deltas matching delta_t type
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
+	git_diff_list *diff,
+	git_delta_t type);
+
+/**
+ * Return the diff delta and patch for an entry in the diff list.
+ *
+ * The `git_diff_patch` is a newly created object contains the text diffs
+ * for the delta.  You have to call `git_diff_patch_free()` when you are
+ * done with it.  You can use the patch object to loop over all the hunks
+ * and lines in the diff of the one delta.
+ *
+ * For an unchanged file or a binary file, no `git_diff_patch` will be
+ * created, the output will be set to NULL, and the `binary` flag will be
+ * set true in the `git_diff_delta` structure.
+ *
+ * The `git_diff_delta` pointer points to internal data and you do not have
+ * to release it when you are done with it.  It will go away when the
+ * `git_diff_list` and `git_diff_patch` go away.
+ *
+ * It is okay to pass NULL for either of the output parameters; if you pass
+ * NULL for the `git_diff_patch`, then the text diff will not be calculated.
+ *
+ * @param patch_out Output parameter for the delta patch object
+ * @param delta_out Output parameter for the delta object
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return 0 on success, other value < 0 on error
+ */
+GIT_EXTERN(int) git_diff_get_patch(
+	git_diff_patch **patch_out,
+	const git_diff_delta **delta_out,
+	git_diff_list *diff,
+	size_t idx);
+
+/**
+ * Free a git_diff_patch object.
+ */
+GIT_EXTERN(void) git_diff_patch_free(
+	git_diff_patch *patch);
+
+/**
+ * Get the delta associated with a patch
+ */
+GIT_EXTERN(const git_diff_delta *) git_diff_patch_delta(
+	git_diff_patch *patch);
+
+/**
+ * Get the number of hunks in a patch
+ */
+GIT_EXTERN(size_t) git_diff_patch_num_hunks(
+	git_diff_patch *patch);
+
+/**
+ * Get line counts of each type in a patch.
+ *
+ * This helps imitate a diff --numstat type of output.  For that purpose,
+ * you only need the `total_additions` and `total_deletions` values, but we
+ * include the `total_context` line count in case you want the total number
+ * of lines of diff output that will be generated.
+ *
+ * All outputs are optional. Pass NULL if you don't need a particular count.
+ *
+ * @param total_context Count of context lines in output, can be NULL.
+ * @param total_additions Count of addition lines in output, can be NULL.
+ * @param total_deletions Count of deletion lines in output, can be NULL.
+ * @param patch The git_diff_patch object
+ * @return Number of lines in hunk or -1 if invalid hunk index
+ */
+GIT_EXTERN(int) git_diff_patch_line_stats(
+	size_t *total_context,
+	size_t *total_additions,
+	size_t *total_deletions,
+	const git_diff_patch *patch);
+
+/**
+ * Get the information about a hunk in a patch
+ *
+ * Given a patch and a hunk index into the patch, this returns detailed
+ * information about that hunk.  Any of the output pointers can be passed
+ * as NULL if you don't care about that particular piece of information.
+ *
+ * @param range Output pointer to git_diff_range of hunk
+ * @param header Output pointer to header string for hunk.  Unlike the
+ *               content pointer for each line, this will be NUL-terminated
+ * @param header_len Output value of characters in header string
+ * @param lines_in_hunk Output count of total lines in this hunk
+ * @param patch Input pointer to patch object
+ * @param hunk_idx Input index of hunk to get information about
+ * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
+ */
+GIT_EXTERN(int) git_diff_patch_get_hunk(
+	const git_diff_range **range,
+	const char **header,
+	size_t *header_len,
+	size_t *lines_in_hunk,
+	git_diff_patch *patch,
+	size_t hunk_idx);
+
+/**
+ * Get the number of lines in a hunk.
+ *
+ * @param patch The git_diff_patch object
+ * @param hunk_idx Index of the hunk
+ * @return Number of lines in hunk or -1 if invalid hunk index
+ */
+GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk(
+	git_diff_patch *patch,
+	size_t hunk_idx);
+
+/**
+ * Get data about a line in a hunk of a patch.
+ *
+ * Given a patch, a hunk index, and a line index in the hunk, this
+ * will return a lot of details about that line.  If you pass a hunk
+ * index larger than the number of hunks or a line index larger than
+ * the number of lines in the hunk, this will return -1.
+ *
+ * @param line_origin A GIT_DIFF_LINE constant from above
+ * @param content Pointer to content of diff line, not NUL-terminated
+ * @param content_len Number of characters in content
+ * @param old_lineno Line number in old file or -1 if line is added
+ * @param new_lineno Line number in new file or -1 if line is deleted
+ * @param patch The patch to look in
+ * @param hunk_idx The index of the hunk
+ * @param line_of_index The index of the line in the hunk
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
+	char *line_origin,
+	const char **content,
+	size_t *content_len,
+	int *old_lineno,
+	int *new_lineno,
+	git_diff_patch *patch,
+	size_t hunk_idx,
+	size_t line_of_hunk);
+
+/**
+ * Serialize the patch to text via callback.
+ *
+ * Returning a non-zero value from the callback will terminate the iteration
+ * and cause this return `GIT_EUSER`.
+ *
+ * @param patch A git_diff_patch representing changes to one file
+ * @param print_cb Callback function to output lines of the patch.  Will be
+ *                 called for file headers, hunk headers, and diff lines.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_diff_patch_print(
+	git_diff_patch *patch,
+	git_diff_data_cb print_cb,
+	void *payload);
+
+/**
+ * Get the content of a patch as a single diff text.
+ *
+ * @param string Allocated string; caller must free.
+ * @param patch A git_diff_patch representing changes to one file
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_diff_patch_to_str(
+	char **string,
+	git_diff_patch *patch);
+
 /**@}*/
 
 
@@ -336,24 +892,55 @@
  */
 
 /**
- * Directly run a text diff on two blobs.
+ * Directly run a diff on two blobs.
  *
- * Compared to a file, a blob lacks some contextual information. As such, the
- * `git_diff_file` parameters of the callbacks will be filled accordingly to the following:
- * `mode` will be set to 0, `path` will be set to NULL. When dealing with a NULL blob, `oid`
- * will be set to 0.
+ * Compared to a file, a blob lacks some contextual information. As such,
+ * the `git_diff_file` given to the callback will have some fake data; i.e.
+ * `mode` will be 0 and `path` will be NULL.
  *
- * When at least one of the blobs being dealt with is binary, the `git_diff_delta` binary
- * attribute will be set to 1 and no call to the hunk_cb nor line_cb will be made.
+ * NULL is allowed for either `old_blob` or `new_blob` and will be treated
+ * as an empty blob, with the `oid` set to NULL in the `git_diff_file` data.
+ * Passing NULL for both blobs is a noop; no callbacks will be made at all.
+ *
+ * We do run a binary content check on the blob content and if either blob
+ * looks like binary data, the `git_diff_delta` binary attribute will be set
+ * to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass
+ * `GIT_DIFF_FORCE_TEXT` of course).
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_diff_blobs(
-	git_blob *old_blob,
-	git_blob *new_blob,
-	git_diff_options *options,
-	void *cb_data,
-	git_diff_file_fn file_cb,
-	git_diff_hunk_fn hunk_cb,
-	git_diff_data_fn line_cb);
+	const git_blob *old_blob,
+	const git_blob *new_blob,
+	const git_diff_options *options,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb line_cb,
+	void *payload);
+
+/**
+ * Directly run a diff between a blob and a buffer.
+ *
+ * As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
+ * so the `git_diff_file` parameters to the callbacks will be faked a la the
+ * rules for `git_diff_blobs()`.
+ *
+ * Passing NULL for `old_blob` will be treated as an empty blob (i.e. the
+ * `file_cb` will be invoked with GIT_DELTA_ADDED and the diff will be the
+ * entire content of the buffer added).  Passing NULL to the buffer will do
+ * the reverse, with GIT_DELTA_REMOVED and blob content removed.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_diff_blob_to_buffer(
+	const git_blob *old_blob,
+	const char *buffer,
+	size_t buffer_len,
+	const git_diff_options *options,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload);
 
 GIT_END_DECL
 
diff --git a/libgit2/include/git2/errors.h b/libgit2/include/git2/errors.h
--- a/libgit2/include/git2/errors.h
+++ b/libgit2/include/git2/errors.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,43 +17,6 @@
  */
 GIT_BEGIN_DECL
 
-#ifdef GIT_OLD_ERRORS
-enum {
-	GIT_SUCCESS = 0,
-	GIT_ENOTOID = -2,
-	GIT_ENOTFOUND = -3,
-	GIT_ENOMEM = -4,
-	GIT_EOSERR = -5,
-	GIT_EOBJTYPE = -6,
-	GIT_ENOTAREPO = -7,
-	GIT_EINVALIDTYPE = -8,
-	GIT_EMISSINGOBJDATA = -9,
-	GIT_EPACKCORRUPTED = -10,
-	GIT_EFLOCKFAIL = -11,
-	GIT_EZLIB = -12,
-	GIT_EBUSY = -13,
-	GIT_EBAREINDEX = -14,
-	GIT_EINVALIDREFNAME = -15,
-	GIT_EREFCORRUPTED = -16,
-	GIT_ETOONESTEDSYMREF = -17,
-	GIT_EPACKEDREFSCORRUPTED = -18,
-	GIT_EINVALIDPATH = -19,
-	GIT_EREVWALKOVER = -20,
-	GIT_EINVALIDREFSTATE = -21,
-	GIT_ENOTIMPLEMENTED = -22,
-	GIT_EEXISTS = -23,
-	GIT_EOVERFLOW = -24,
-	GIT_ENOTNUM = -25,
-	GIT_ESTREAM = -26,
-	GIT_EINVALIDARGS = -27,
-	GIT_EOBJCORRUPTED = -28,
-	GIT_EAMBIGUOUS = -29,
-	GIT_EPASSTHROUGH = -30,
-	GIT_ENOMATCH = -31,
-	GIT_ESHORTBUFFER = -32,
-};
-#endif
-
 /** Generic return codes */
 enum {
 	GIT_OK = 0,
@@ -62,9 +25,16 @@
 	GIT_EEXISTS = -4,
 	GIT_EAMBIGUOUS = -5,
 	GIT_EBUFS = -6,
+	GIT_EUSER = -7,
+	GIT_EBAREREPO = -8,
+	GIT_EORPHANEDHEAD = -9,
+	GIT_EUNMERGED = -10,
+	GIT_ENONFASTFORWARD = -11,
+	GIT_EINVALIDSPEC = -12,
+	GIT_EMERGECONFLICT = -13,
 
 	GIT_PASSTHROUGH = -30,
-	GIT_REVWALKOVER = -31,
+	GIT_ITEROVER = -31,
 };
 
 typedef struct {
@@ -72,6 +42,7 @@
 	int klass;
 } git_error;
 
+/** Error classes */
 typedef enum {
 	GITERR_NOMEMORY,
 	GITERR_OS,
@@ -88,6 +59,13 @@
 	GITERR_TAG,
 	GITERR_TREE,
 	GITERR_INDEXER,
+	GITERR_SSL,
+	GITERR_SUBMODULE,
+	GITERR_THREAD,
+	GITERR_STASH,
+	GITERR_CHECKOUT,
+	GITERR_FETCHHEAD,
+	GITERR_MERGE,
 } git_error_t;
 
 /**
@@ -102,6 +80,40 @@
  * Clear the last library error that occurred for this thread.
  */
 GIT_EXTERN(void) giterr_clear(void);
+
+/**
+ * Set the error message string for this thread.
+ *
+ * This function is public so that custom ODB backends and the like can
+ * relay an error message through libgit2.  Most regular users of libgit2
+ * will never need to call this function -- actually, calling it in most
+ * circumstances (for example, calling from within a callback function)
+ * will just end up having the value overwritten by libgit2 internals.
+ *
+ * This error message is stored in thread-local storage and only applies
+ * to the particular thread that this libgit2 call is made from.
+ *
+ * NOTE: Passing the `error_class` as GITERR_OS has a special behavior: we
+ * attempt to append the system default error message for the last OS error
+ * that occurred and then clear the last error.  The specific implementation
+ * of looking up and clearing this last OS error will vary by platform.
+ *
+ * @param error_class One of the `git_error_t` enum above describing the
+ *                    general subsystem that is responsible for the error.
+ * @param message The formatted error message to keep
+ */
+GIT_EXTERN(void) giterr_set_str(int error_class, const char *string);
+
+/**
+ * Set the error message to a special value for memory allocation failure.
+ *
+ * The normal `giterr_set_str()` function attempts to `strdup()` the string
+ * that is passed in.  This is not a good idea when the error in question
+ * is a memory allocation failure.  That circumstance has a special setter
+ * function that sets the error string to a known and statically allocated
+ * internal value.
+ */
+GIT_EXTERN(void) giterr_set_oom(void);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/graph.h b/libgit2/include/git2/graph.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/graph.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_graph_h__
+#define INCLUDE_git_graph_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/graph.h
+ * @brief Git graph traversal routines
+ * @defgroup git_revwalk Git graph traversal routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Count the number of unique commits between two commit objects
+ *
+ * There is no need for branches containing the commits to have any
+ * upstream relationship, but it helps to think of one as a branch and
+ * the other as its upstream, the `ahead` and `behind` values will be
+ * what git would report for the branches.
+ *
+ * @param ahead number of unique from commits in `upstream`
+ * @param behind number of unique from commits in `local`
+ * @param repo the repository where the commits exist
+ * @param local the commit for local
+ * @param upstream the commit for upstream
+ */
+GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/ignore.h b/libgit2/include/git2/ignore.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/ignore.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_ignore_h__
+#define INCLUDE_git_ignore_h__
+
+#include "common.h"
+#include "types.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Add ignore rules for a repository.
+ *
+ * Excludesfile rules (i.e. .gitignore rules) are generally read from
+ * .gitignore files in the repository tree or from a shared system file
+ * only if a "core.excludesfile" config value is set.  The library also
+ * keeps a set of per-repository internal ignores that can be configured
+ * in-memory and will not persist.  This function allows you to add to
+ * that internal rules list.
+ *
+ * Example usage:
+ *
+ *     error = git_ignore_add_rule(myrepo, "*.c\ndir/\nFile with space\n");
+ *
+ * This would add three rules to the ignores.
+ *
+ * @param repo The repository to add ignore rules to.
+ * @param rules Text of rules, a la the contents of a .gitignore file.
+ *              It is okay to have multiple rules in the text; if so,
+ *              each rule should be terminated with a newline.
+ * @return 0 on success
+ */
+GIT_EXTERN(int) git_ignore_add_rule(
+	git_repository *repo,
+	const char *rules);
+
+/**
+ * Clear ignore rules that were explicitly added.
+ *
+ * Resets to the default internal ignore rules.  This will not turn off
+ * rules in .gitignore files that actually exist in the filesystem.
+ *
+ * The default internal ignores ignore ".", ".." and ".git" entries.
+ *
+ * @param repo The repository to remove ignore rules from.
+ * @return 0 on success
+ */
+GIT_EXTERN(int) git_ignore_clear_internal_rules(
+	git_repository *repo);
+
+/**
+ * Test if the ignore rules apply to a given path.
+ *
+ * This function checks the ignore rules to see if they would apply to the
+ * given file.  This indicates if the file would be ignored regardless of
+ * whether the file is already in the index or committed to the repository.
+ *
+ * One way to think of this is if you were to do "git add ." on the
+ * directory containing the file, would it be added or not?
+ *
+ * @param ignored boolean returning 0 if the file is not ignored, 1 if it is
+ * @param repo a repository object
+ * @param path the file to check ignores for, relative to the repo's workdir.
+ * @return 0 if ignore rules could be processed for the file (regardless
+ *         of whether it exists or not), or an error < 0 if they could not.
+ */
+GIT_EXTERN(int) git_ignore_path_is_ignored(
+	int *ignored,
+	git_repository *repo,
+	const char *path);
+
+GIT_END_DECL
+
+#endif
diff --git a/libgit2/include/git2/index.h b/libgit2/include/git2/index.h
--- a/libgit2/include/git2/index.h
+++ b/libgit2/include/git2/index.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,6 +8,7 @@
 #define INCLUDE_git_index_h__
 
 #include "common.h"
+#include "indexer.h"
 #include "types.h"
 #include "oid.h"
 
@@ -20,10 +21,10 @@
  */
 GIT_BEGIN_DECL
 
-#define GIT_IDXENTRY_NAMEMASK (0x0fff)
+#define GIT_IDXENTRY_NAMEMASK  (0x0fff)
 #define GIT_IDXENTRY_STAGEMASK (0x3000)
-#define GIT_IDXENTRY_EXTENDED (0x4000)
-#define GIT_IDXENTRY_VALID		(0x8000)
+#define GIT_IDXENTRY_EXTENDED  (0x4000)
+#define GIT_IDXENTRY_VALID     (0x8000)
 #define GIT_IDXENTRY_STAGESHIFT 12
 
 /*
@@ -33,26 +34,26 @@
  *
  * In-memory only flags:
  */
-#define GIT_IDXENTRY_UPDATE			(1 << 0)
-#define GIT_IDXENTRY_REMOVE			(1 << 1)
-#define GIT_IDXENTRY_UPTODATE			(1 << 2)
-#define GIT_IDXENTRY_ADDED				(1 << 3)
+#define GIT_IDXENTRY_UPDATE            (1 << 0)
+#define GIT_IDXENTRY_REMOVE            (1 << 1)
+#define GIT_IDXENTRY_UPTODATE          (1 << 2)
+#define GIT_IDXENTRY_ADDED             (1 << 3)
 
-#define GIT_IDXENTRY_HASHED			(1 << 4)
-#define GIT_IDXENTRY_UNHASHED			(1 << 5)
-#define GIT_IDXENTRY_WT_REMOVE			(1 << 6) /* remove in work directory */
-#define GIT_IDXENTRY_CONFLICTED		(1 << 7)
+#define GIT_IDXENTRY_HASHED            (1 << 4)
+#define GIT_IDXENTRY_UNHASHED          (1 << 5)
+#define GIT_IDXENTRY_WT_REMOVE         (1 << 6) /* remove in work directory */
+#define GIT_IDXENTRY_CONFLICTED        (1 << 7)
 
-#define GIT_IDXENTRY_UNPACKED			(1 << 8)
+#define GIT_IDXENTRY_UNPACKED          (1 << 8)
 #define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
 
 /*
  * Extended on-disk flags:
  */
-#define GIT_IDXENTRY_INTENT_TO_ADD		(1 << 13)
-#define GIT_IDXENTRY_SKIP_WORKTREE		(1 << 14)
+#define GIT_IDXENTRY_INTENT_TO_ADD     (1 << 13)
+#define GIT_IDXENTRY_SKIP_WORKTREE     (1 << 14)
 /* GIT_IDXENTRY_EXTENDED2 is for future extension */
-#define GIT_IDXENTRY_EXTENDED2			(1 << 15)
+#define GIT_IDXENTRY_EXTENDED2         (1 << 15)
 
 #define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
 
@@ -83,13 +84,27 @@
 	char *path;
 } git_index_entry;
 
-/** Representation of an unmerged file entry in the index. */
-typedef struct git_index_entry_unmerged {
+/** Representation of a resolve undo entry in the index. */
+typedef struct git_index_reuc_entry {
 	unsigned int mode[3];
 	git_oid oid[3];
 	char *path;
-} git_index_entry_unmerged;
+} git_index_reuc_entry;
 
+/** Capabilities of system that affect index actions. */
+enum {
+	GIT_INDEXCAP_IGNORE_CASE = 1,
+	GIT_INDEXCAP_NO_FILEMODE = 2,
+	GIT_INDEXCAP_NO_SYMLINKS = 4,
+	GIT_INDEXCAP_FROM_OWNER  = ~0u
+};
+
+/** @name Index File Functions
+ *
+ * These functions work on the index file itself.
+ */
+/**@{*/
+
 /**
  * Create a new bare Git index object as a memory representation
  * of the Git index file in 'index_path', without a repository
@@ -104,20 +119,24 @@
  *
  * The index must be freed once it's no longer in use.
  *
- * @param index the pointer for the new index
+ * @param out the pointer for the new index
  * @param index_path the path to the index file in disk
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path);
+GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path);
 
 /**
- * Clear the contents (all the entries) of an index object.
- * This clears the index object in memory; changes must be manually
- * written to disk for them to take effect.
+ * Create an in-memory index object.
  *
- * @param index an existing index object
+ * This index object cannot be read/written to the filesystem,
+ * but may be used to perform in-memory index operations.
+ *
+ * The index must be freed once it's no longer in use.
+ *
+ * @param out the pointer for the new index
+ * @return 0 or an error code
  */
-GIT_EXTERN(void) git_index_clear(git_index *index);
+GIT_EXTERN(int) git_index_new(git_index **out);
 
 /**
  * Free an existing index object.
@@ -127,6 +146,35 @@
 GIT_EXTERN(void) git_index_free(git_index *index);
 
 /**
+ * Get the repository this index relates to
+ *
+ * @param index The index
+ * @return A pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_index_owner(const git_index *index);
+
+/**
+ * Read index capabilities flags.
+ *
+ * @param index An existing index object
+ * @return A combination of GIT_INDEXCAP values
+ */
+GIT_EXTERN(unsigned int) git_index_caps(const git_index *index);
+
+/**
+ * Set index capabilities flags.
+ *
+ * If you pass `GIT_INDEXCAP_FROM_OWNER` for the caps, then the
+ * capabilities will be read from the config of the owner object,
+ * looking at `core.ignorecase`, `core.filemode`, `core.symlinks`.
+ *
+ * @param index An existing index object
+ * @param caps A combination of GIT_INDEXCAP values
+ * @return 0 on success, -1 on failure
+ */
+GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps);
+
+/**
  * Update the contents of an existing index object in memory
  * by reading from the hard disk.
  *
@@ -145,44 +193,143 @@
 GIT_EXTERN(int) git_index_write(git_index *index);
 
 /**
- * Find the first index of any entries which point to given
- * path in the Git index.
+ * Read a tree into the index file with stats
  *
+ * The current index contents will be replaced by the specified tree.
+ *
  * @param index an existing index object
- * @param path path to search
- * @return an index >= 0 if found, -1 otherwise
+ * @param tree tree to read
+ * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_find(git_index *index, const char *path);
+GIT_EXTERN(int) git_index_read_tree(git_index *index, const git_tree *tree);
 
 /**
- * Remove all entries with equal path except last added
+ * Write the index as a tree
  *
+ * This method will scan the index and write a representation
+ * of its current state back to disk; it recursively creates
+ * tree objects for each of the subtrees stored in the index,
+ * but only returns the OID of the root tree. This is the OID
+ * that can be used e.g. to create a commit.
+ *
+ * The index instance cannot be bare, and needs to be associated
+ * to an existing repository.
+ *
+ * The index must not contain any file in conflict.
+ *
+ * @param out Pointer where to store the OID of the written tree
+ * @param index Index to write
+ * @return 0 on success, GIT_EUNMERGED when the index is not clean
+ * or an error code
+ */
+GIT_EXTERN(int) git_index_write_tree(git_oid *out, git_index *index);
+
+/**
+ * Write the index as a tree to the given repository
+ *
+ * This method will do the same as `git_index_write_tree`, but
+ * letting the user choose the repository where the tree will
+ * be written.
+ *
+ * The index must not contain any file in conflict.
+ *
+ * @param out Pointer where to store OID of the the written tree
+ * @param index Index to write
+ * @param repo Repository where to write the tree
+ * @return 0 on success, GIT_EUNMERGED when the index is not clean
+ * or an error code
+ */
+GIT_EXTERN(int) git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo);
+
+/**@}*/
+
+/** @name Raw Index Entry Functions
+ *
+ * These functions work on index entries, and allow for raw manipulation
+ * of the entries.
+ */
+/**@{*/
+
+/* Index entry manipulation */
+
+/**
+ * Get the count of entries currently in the index
+ *
  * @param index an existing index object
+ * @return integer of count of current entries
  */
-GIT_EXTERN(void) git_index_uniq(git_index *index);
+GIT_EXTERN(size_t) git_index_entrycount(const git_index *index);
 
 /**
- * Add or update an index entry from a file in disk
+ * Clear the contents (all the entries) of an index object.
+ * This clears the index object in memory; changes must be manually
+ * written to disk for them to take effect.
  *
- * The file `path` must be relative to the repository's
- * working folder and must be readable.
+ * @param index an existing index object
+ */
+GIT_EXTERN(void) git_index_clear(git_index *index);
+
+/**
+ * Get a pointer to one of the entries in the index
  *
- * This method will fail in bare index instances.
+ * The values of this entry can be modified (except the path)
+ * and the changes will be written back to disk on the next
+ * write() call.
  *
- * This forces the file to be added to the index, not looking
- * at gitignore rules.  Those rules can be evaluated through
- * the git_status APIs (in status.h) before calling this.
+ * The entry should not be freed by the caller.
  *
  * @param index an existing index object
- * @param path filename to add
- * @param stage stage for the entry
+ * @param n the position of the entry
+ * @return a pointer to the entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_index_entry *) git_index_get_byindex(
+	git_index *index, size_t n);
+
+/**
+ * Get a pointer to one of the entries in the index
+ *
+ * The values of this entry can be modified (except the path)
+ * and the changes will be written back to disk on the next
+ * write() call.
+ *
+ * The entry should not be freed by the caller.
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
+ * @return a pointer to the entry; NULL if it was not found
+ */
+GIT_EXTERN(const git_index_entry *) git_index_get_bypath(
+	git_index *index, const char *path, int stage);
+
+/**
+ * Remove an entry from the index
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
+GIT_EXTERN(int) git_index_remove(git_index *index, const char *path, int stage);
 
 /**
+ * Remove all entries from the index under a given directory
+ *
+ * @param index an existing index object
+ * @param dir container directory path
+ * @param stage stage to search
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_remove_directory(
+	git_index *index, const char *dir, int stage);
+
+/**
  * Add or update an index entry from an in-memory struct
  *
+ * If a previous index entry exists that has the same path and stage
+ * as the given 'source_entry', it will be replaced.  Otherwise, the
+ * 'source_entry' will be added.
+ *
  * A full copy (including the 'path' string) of the given
  * 'source_entry' will be inserted on the index.
  *
@@ -190,131 +337,242 @@
  * @param source_entry new entry object
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
+GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_entry);
 
 /**
- * Add (append) an index entry from a file in disk
+ * Return the stage number from a git index entry
  *
- * A new entry will always be inserted into the index;
- * if the index already contains an entry for such
- * path, the old entry will **not** be replaced.
+ * This entry is calculated from the entry's flag
+ * attribute like this:
  *
+ *	(entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ *
+ * @param entry The entry
+ * @returns the stage number
+ */
+GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
+
+/**@}*/
+
+/** @name Workdir Index Entry Functions
+ *
+ * These functions work on index entries specifically in the working
+ * directory (ie, stage 0).
+ */
+/**@{*/
+
+/**
+ * Add or update an index entry from a file on disk
+ *
  * The file `path` must be relative to the repository's
  * working folder and must be readable.
  *
  * This method will fail in bare index instances.
  *
+ * This forces the file to be added to the index, not looking
+ * at gitignore rules.  Those rules can be evaluated through
+ * the git_status APIs (in status.h) before calling this.
+ *
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting.  The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
+ *
  * @param index an existing index object
  * @param path filename to add
- * @param stage stage for the entry
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
+GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path);
 
 /**
- * Add (append) an index entry from an in-memory struct
+ * Remove an index entry corresponding to a file on disk
  *
- * A new entry will always be inserted into the index;
- * if the index already contains an entry for the path
- * in the `entry` struct, the old entry will **not** be
- * replaced.
+ * The file `path` must be relative to the repository's
+ * working folder.  It may exist.
  *
- * A full copy (including the 'path' string) of the given
- * 'source_entry' will be inserted on the index.
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting.  The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
  *
  * @param index an existing index object
- * @param source_entry new entry object
+ * @param path filename to remove
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *source_entry);
+GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
 
 /**
- * Remove an entry from the index
+ * Find the first position of any entries which point to given
+ * path in the Git index.
  *
+ * @param at_pos the address to which the position of the index entry is written (optional)
  * @param index an existing index object
- * @param position position of the entry to remove
- * @return 0 or an error code
+ * @param path path to search
+ * @return a zero-based position in the index if found;
+ * GIT_ENOTFOUND otherwise
  */
-GIT_EXTERN(int) git_index_remove(git_index *index, int position);
+GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
 
+/**@}*/
 
+/** @name Conflict Index Entry Functions
+ *
+ * These functions work on conflict index entries specifically (ie, stages 1-3)
+ */
+/**@{*/
+
 /**
- * Get a pointer to one of the entries in the index
+ * Add or update index entries to represent a conflict
  *
- * This entry can be modified, and the changes will be written
- * back to disk on the next write() call.
+ * The entries are the entries from the tree included in the merge.  Any
+ * entry may be null to indicate that that file was not present in the
+ * trees during the merge.  For example, ancestor_entry may be NULL to
+ * indicate that a file was added in both branches and must be resolved.
  *
- * The entry should not be freed by the caller.
+ * @param index an existing index object
+ * @param ancestor_entry the entry data for the ancestor of the conflict
+ * @param our_entry the entry data for our side of the merge conflict
+ * @param their_entry the entry data for their side of the merge conflict
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_add(
+   git_index *index,
+	const git_index_entry *ancestor_entry,
+	const git_index_entry *our_entry,
+	const git_index_entry *their_entry);
+
+/**
+ * Get the index entries that represent a conflict of a single file.
  *
+ * The values of this entry can be modified (except the paths)
+ * and the changes will be written back to disk on the next
+ * write() call.
+ *
+ * @param ancestor_out Pointer to store the ancestor entry
+ * @param our_out Pointer to store the our entry
+ * @param their_out Pointer to store the their entry
  * @param index an existing index object
- * @param n the position of the entry
- * @return a pointer to the entry; NULL if out of bounds
+ * @param path path to search
  */
-GIT_EXTERN(git_index_entry *) git_index_get(git_index *index, unsigned int n);
+GIT_EXTERN(int) git_index_conflict_get(git_index_entry **ancestor_out, git_index_entry **our_out, git_index_entry **their_out, git_index *index, const char *path);
 
 /**
- * Get the count of entries currently in the index
+ * Removes the index entries that represent a conflict of a single file.
  *
  * @param index an existing index object
- * @return integer of count of current entries
+ * @param path to search
  */
-GIT_EXTERN(unsigned int) git_index_entrycount(git_index *index);
+GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
 
 /**
- * Get the count of unmerged entries currently in the index
+ * Remove all conflicts in the index (entries with a stage greater than 0.)
  *
  * @param index an existing index object
- * @return integer of count of current unmerged entries
  */
-GIT_EXTERN(unsigned int) git_index_entrycount_unmerged(git_index *index);
+GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
 
 /**
- * Get an unmerged entry from the index.
+ * Determine if the index contains entries representing file conflicts.
  *
+ * @return 1 if at least one conflict is found, 0 otherwise.
+ */
+GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
+
+/**@}*/
+
+/** @name Resolve Undo (REUC) index entry manipulation.
+ *
+ * These functions work on the Resolve Undo index extension and contains
+ * data about the original files that led to a merge conflict.
+ */
+/**@{*/
+
+/**
+ * Get the count of resolve undo entries currently in the index.
+ *
+ * @param index an existing index object
+ * @return integer of count of current resolve undo entries
+ */
+GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index);
+
+/**
+ * Finds the resolve undo entry that points to the given path in the Git
+ * index.
+ *
+ * @param at_pos the address to which the position of the reuc entry is written (optional)
+ * @param index an existing index object
+ * @param path path to search
+ * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
+ */
+GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
+
+/**
+ * Get a resolve undo entry from the index.
+ *
  * The returned entry is read-only and should not be modified
- * of freed by the caller.
+ * or freed by the caller.
  *
  * @param index an existing index object
  * @param path path to search
- * @return the unmerged entry; NULL if not found
+ * @return the resolve undo entry; NULL if not found
  */
-GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_bypath(git_index *index, const char *path);
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
 
 /**
- * Get an unmerged entry from the index.
+ * Get a resolve undo entry from the index.
  *
  * The returned entry is read-only and should not be modified
- * of freed by the caller.
+ * or freed by the caller.
  *
  * @param index an existing index object
  * @param n the position of the entry
- * @return a pointer to the unmerged entry; NULL if out of bounds
+ * @return a pointer to the resolve undo entry; NULL if out of bounds
  */
-GIT_EXTERN(const git_index_entry_unmerged *) git_index_get_unmerged_byindex(git_index *index, unsigned int n);
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
 
 /**
- * Return the stage number from a git index entry
+ * Adds a resolve undo entry for a file based on the given parameters.
  *
- * This entry is calculated from the entrie's flag
- * attribute like this:
+ * The resolve undo entry contains the OIDs of files that were involved
+ * in a merge conflict after the conflict has been resolved.  This allows
+ * conflicts to be re-resolved later.
  *
- *	(entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
+ * If there exists a resolve undo entry for the given path in the index,
+ * it will be removed.
  *
- * @param entry The entry
- * @returns the stage number
+ * This method will fail in bare index instances.
+ *
+ * @param index an existing index object
+ * @param path filename to add
+ * @param ancestor_mode mode of the ancestor file
+ * @param ancestor_id oid of the ancestor file
+ * @param our_mode mode of our file
+ * @param our_id oid of our file
+ * @param their_mode mode of their file
+ * @param their_id oid of their file
+ * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
+GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
+	int ancestor_mode, git_oid *ancestor_id,
+	int our_mode, git_oid *our_id,
+	int their_mode, git_oid *their_id);
 
 /**
- * Read a tree into the index file
+ * Remove an resolve undo entry from the index
  *
- * The current index contents will be replaced by the specified tree.
+ * @param index an existing index object
+ * @param n position of the resolve undo entry to remove
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
+
+/**
+ * Remove all resolve undo entries from the index
  *
  * @param index an existing index object
- * @param tree tree to read
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree);
+GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
+
+/**@}*/
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/indexer.h b/libgit2/include/git2/indexer.h
--- a/libgit2/include/git2/indexer.h
+++ b/libgit2/include/git2/indexer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -16,32 +16,48 @@
  * This is passed as the first argument to the callback to allow the
  * user to see the progress.
  */
-typedef struct git_indexer_stats {
-	unsigned int total;
-	unsigned int processed;
-} git_indexer_stats;
+typedef struct git_transfer_progress {
+	unsigned int total_objects;
+	unsigned int indexed_objects;
+	unsigned int received_objects;
+	size_t received_bytes;
+} git_transfer_progress;
 
 
-typedef struct git_indexer git_indexer;
+/**
+ * Type for progress callbacks during indexing.  Return a value less than zero
+ * to cancel the transfer.
+ *
+ * @param stats Structure containing information about the state of the transfer
+ * @param payload Payload provided by caller
+ */
+typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
+
 typedef struct git_indexer_stream git_indexer_stream;
 
 /**
  * Create a new streaming indexer instance
  *
- * @param out where to store the inexer instance
- * @param path to the gitdir (metadata directory)
+ * @param out where to store the indexer instance
+ * @param path to the directory where the packfile should be stored
+ * @param progress_cb function to call with progress information
+ * @param progress_payload payload for the progress callback
  */
-GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *gitdir);
+GIT_EXTERN(int) git_indexer_stream_new(
+		git_indexer_stream **out,
+		const char *path,
+		git_transfer_progress_callback progress_cb,
+		void *progress_cb_payload);
 
 /**
  * Add data to the indexer
  *
  * @param idx the indexer
  * @param data the data to add
- * @param size the size of the data
+ * @param size the size of the data in bytes
  * @param stats stat storage
  */
-GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats);
+GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats);
 
 /**
  * Finalize the pack and index
@@ -50,7 +66,7 @@
  *
  * @param idx the indexer
  */
-GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats);
+GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats);
 
 /**
  * Get the packfile's hash
@@ -60,7 +76,7 @@
  *
  * @param idx the indexer instance
  */
-GIT_EXTERN(const git_oid *) git_indexer_stream_hash(git_indexer_stream *idx);
+GIT_EXTERN(const git_oid *) git_indexer_stream_hash(const git_indexer_stream *idx);
 
 /**
  * Free the indexer and its resources
@@ -68,53 +84,6 @@
  * @param idx the indexer to free
  */
 GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
-
-/**
- * Create a new indexer instance
- *
- * @param out where to store the indexer instance
- * @param packname the absolute filename of the packfile to index
- */
-GIT_EXTERN(int) git_indexer_new(git_indexer **out, const char *packname);
-
-/**
- * Iterate over the objects in the packfile and extract the information
- *
- * Indexing a packfile can be very expensive so this function is
- * expected to be run in a worker thread and the stats used to provide
- * feedback the user.
- *
- * @param idx the indexer instance
- * @param stats storage for the running state
- */
-GIT_EXTERN(int) git_indexer_run(git_indexer *idx, git_indexer_stats *stats);
-
-/**
- * Write the index file to disk.
- *
- * The file will be stored as pack-$hash.idx in the same directory as
- * the packfile.
- *
- * @param idx the indexer instance
- */
-GIT_EXTERN(int) git_indexer_write(git_indexer *idx);
-
-/**
- * Get the packfile's hash
- *
- * A packfile's name is derived from the sorted hashing of all object
- * names. This is only correct after the index has been written to disk.
- *
- * @param idx the indexer instance
- */
-GIT_EXTERN(const git_oid *) git_indexer_hash(git_indexer *idx);
-
-/**
- * Free the indexer and its resources
- *
- * @param idx the indexer to free
- */
-GIT_EXTERN(void) git_indexer_free(git_indexer *idx);
 
 GIT_END_DECL
 
diff --git a/libgit2/include/git2/inttypes.h b/libgit2/include/git2/inttypes.h
--- a/libgit2/include/git2/inttypes.h
+++ b/libgit2/include/git2/inttypes.h
@@ -40,7 +40,11 @@
 #pragma once
 #endif
 
+#if _MSC_VER >= 1600
+#include <stdint.h>
+#else
 #include "stdint.h"
+#endif
 
 // 7.8 Format conversion of integer types
 
diff --git a/libgit2/include/git2/merge.h b/libgit2/include/git2/merge.h
--- a/libgit2/include/git2/merge.h
+++ b/libgit2/include/git2/merge.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -27,8 +27,28 @@
  * @param repo the repository where the commits exist
  * @param one one of the commits
  * @param two the other commit
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
  */
-GIT_EXTERN(int) git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two);
+GIT_EXTERN(int) git_merge_base(
+	git_oid *out,
+	git_repository *repo,
+	const git_oid *one,
+	const git_oid *two);
+
+/**
+ * Find a merge base given a list of commits
+ *
+ * @param out the OID of a merge base considering all the commits
+ * @param repo the repository where the commits exist
+ * @param input_array oids of the commits
+ * @param length The number of commits in the provided `input_array`
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_base_many(
+	git_oid *out,
+	git_repository *repo,
+	const git_oid input_array[],
+	size_t length);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/message.h b/libgit2/include/git2/message.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/message.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_message_h__
+#define INCLUDE_git_message_h__
+
+#include "common.h"
+
+/**
+ * @file git2/message.h
+ * @brief Git message management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Clean up message from excess whitespace and make sure that the last line
+ * ends with a '\n'.
+ *
+ * Optionally, can remove lines starting with a "#".
+ *
+ * @param out The user-allocated buffer which will be filled with the
+ *     cleaned up message. Pass NULL if you just want to get the needed
+ *     size of the prettified message as the output value.
+ *
+ * @param out_size Size of the `out` buffer in bytes.
+ *
+ * @param message The message to be prettified.
+ *
+ * @param strip_comments Non-zero to remove lines starting with "#", 0 to
+ *     leave them in.
+ *
+ * @return -1 on error, else number of characters in prettified message
+ *     including the trailing NUL byte
+ */
+GIT_EXTERN(int) git_message_prettify(
+	char *out,
+	size_t out_size,
+	const char *message,
+	int strip_comments);
+
+/** @} */
+GIT_END_DECL
+
+#endif /* INCLUDE_git_message_h__ */
diff --git a/libgit2/include/git2/net.h b/libgit2/include/git2/net.h
--- a/libgit2/include/git2/net.h
+++ b/libgit2/include/git2/net.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
-#ifndef INCLUDE_net_h__
-#define INCLUDE_net_h__
+#ifndef INCLUDE_git_net_h__
+#define INCLUDE_git_net_h__
 
 #include "common.h"
 #include "oid.h"
@@ -27,15 +27,17 @@
  * gets called.
  */
 
-#define GIT_DIR_FETCH 0
-#define GIT_DIR_PUSH 1
+typedef enum {
+	GIT_DIRECTION_FETCH = 0,
+	GIT_DIRECTION_PUSH  = 1
+} git_direction;
 
 
 /**
  * Remote head description, given out on `ls` calls.
  */
 struct git_remote_head {
-	int local:1; /* available locally */
+	int local; /* available locally */
 	git_oid oid;
 	git_oid loid;
 	char *name;
@@ -44,7 +46,7 @@
 /**
  * Callback for listing the remote heads
  */
-typedef int (*git_headlist_cb)(git_remote_head *, void *);
+typedef int (*git_headlist_cb)(git_remote_head *rhead, void *payload);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/notes.h b/libgit2/include/git2/notes.h
--- a/libgit2/include/git2/notes.h
+++ b/libgit2/include/git2/notes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -19,19 +19,82 @@
 GIT_BEGIN_DECL
 
 /**
+ * Callback for git_note_foreach.
+ *
+ * Receives:
+ * - blob_id: Oid of the blob containing the message
+ * - annotated_object_id: Oid of the git object being annotated
+ * - payload: Payload data passed to `git_note_foreach`
+ */
+typedef int (*git_note_foreach_cb)(
+	const git_oid *blob_id, const git_oid *annotated_object_id, void *payload);
+
+/**
+ * note iterator
+ */
+typedef struct git_iterator git_note_iterator;
+
+/**
+ * Creates a new iterator for notes
+ *
+ * The iterator must be freed manually by the user.
+ *
+ * @param out pointer to the iterator
+ * @param repo repository where to look up the note
+ * @param notes_ref canonical name of the reference to use (optional); defaults to
+ *                  "refs/notes/commits"
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_iterator_new(
+	git_note_iterator **out,
+	git_repository *repo,
+	const char *notes_ref);
+
+/**
+ * Frees an git_note_iterator
+ *
+ * @param it pointer to the iterator
+ */
+GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it);
+
+/**
+ * Returns the current item (note_id and annotated_id) and advance the iterator
+ * internally to the next value
+ *
+ * The notes must not be freed manually by the user.
+ *
+ * @param it pointer to the iterator
+ * @param note_id id of blob containing the message
+ * @param annotated_id id of the git object being annotated
+ *
+ * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
+ *         (negative value)
+ */
+GIT_EXTERN(int) git_note_next(
+	git_oid* note_id,
+	git_oid* annotated_id,
+	git_note_iterator *it);
+
+
+/**
  * Read the note for an object
  *
  * The note must be freed manually by the user.
  *
- * @param note the note; NULL in case of error
- * @param repo the Git repository
- * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
- * @param oid OID of the object
+ * @param out pointer to the read note; NULL in case of error
+ * @param repo repository where to look up the note
+ * @param notes_ref canonical name of the reference to use (optional); defaults to
+ *                  "refs/notes/commits"
+ * @param oid OID of the git object to read the note from
  *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_note_read(git_note **note, git_repository *repo,
-			      const char *notes_ref, const git_oid *oid);
+GIT_EXTERN(int) git_note_read(
+	git_note **out,
+	git_repository *repo,
+	const char *notes_ref,
+	const git_oid *oid);
 
 /**
  * Get the note message
@@ -39,7 +102,7 @@
  * @param note
  * @return the note message
  */
-GIT_EXTERN(const char *) git_note_message(git_note *note);
+GIT_EXTERN(const char *) git_note_message(const git_note *note);
 
 
 /**
@@ -48,42 +111,52 @@
  * @param note
  * @return the note object OID
  */
-GIT_EXTERN(const git_oid *) git_note_oid(git_note *note);
-
+GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note);
 
 /**
  * Add a note for an object
  *
- * @param oid pointer to store the OID (optional); NULL in case of error
- * @param repo the Git repository
+ * @param out pointer to store the OID (optional); NULL in case of error
+ * @param repo repository where to store the note
  * @param author signature of the notes commit author
  * @param committer signature of the notes commit committer
- * @param notes_ref OID reference to update (optional); defaults to "refs/notes/commits"
- * @param oid The OID of the object
- * @param oid The note to add for object oid
+ * @param notes_ref canonical name of the reference to use (optional);
+ *					defaults to "refs/notes/commits"
+ * @param oid OID of the git object to decorate
+ * @param note Content of the note to add for object oid
+ * @param force Overwrite existing note
  *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_note_create(git_oid *out, git_repository *repo,
-				git_signature *author, git_signature *committer,
-				const char *notes_ref, const git_oid *oid,
-				 const char *note);
+GIT_EXTERN(int) git_note_create(
+	git_oid *out,
+	git_repository *repo,
+	const git_signature *author,
+	const git_signature *committer,
+	const char *notes_ref,
+	const git_oid *oid,
+	const char *note,
+	int force);
 
 
 /**
  * Remove the note for an object
  *
- * @param repo the Git repository
- * @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
+ * @param repo repository where the note lives
+ * @param notes_ref canonical name of the reference to use (optional);
+ *					defaults to "refs/notes/commits"
  * @param author signature of the notes commit author
  * @param committer signature of the notes commit committer
- * @param oid the oid which note's to be removed
+ * @param oid OID of the git object to remove the note from
  *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_note_remove(git_repository *repo, const char *notes_ref,
-				git_signature *author, git_signature *committer,
-				const git_oid *oid);
+GIT_EXTERN(int) git_note_remove(
+	git_repository *repo,
+	const char *notes_ref,
+	const git_signature *author,
+	const git_signature *committer,
+	const git_oid *oid);
 
 /**
  * Free a git_note object
@@ -103,36 +176,26 @@
 GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
 
 /**
- * Basic components of a note
- *
- *  - Oid of the blob containing the message
- *  - Oid of the git object being annotated
- */
-typedef struct {
-	git_oid blob_oid;
-	git_oid annotated_object_oid;
-} git_note_data;
-
-/**
  * Loop over all the notes within a specified namespace
  * and issue a callback for each one.
  *
  * @param repo Repository where to find the notes.
  *
- * @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits".
+ * @param notes_ref Reference to read from (optional); defaults to
+ *        "refs/notes/commits".
  *
- * @param note_cb Callback to invoke per found annotation.
+ * @param note_cb Callback to invoke per found annotation.  Return non-zero
+ *        to stop looping.
  *
  * @param payload Extra parameter to callback function.
  *
- * @return 0 or an error code.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_note_foreach(
-		git_repository *repo,
-		const char *notes_ref,
-		int (*note_cb)(git_note_data *note_data, void *payload),
-		void *payload
-);
+	git_repository *repo,
+	const char *notes_ref,
+	git_note_foreach_cb note_cb,
+	void *payload);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/object.h b/libgit2/include/git2/object.h
--- a/libgit2/include/git2/object.h
+++ b/libgit2/include/git2/object.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -21,7 +21,7 @@
 GIT_BEGIN_DECL
 
 /**
- * Lookup a reference to one of the objects in a repostory.
+ * Lookup a reference to one of the objects in a repository.
  *
  * The generated reference is owned by the repository and
  * should be closed with the `git_object_free` method
@@ -45,7 +45,7 @@
 		git_otype type);
 
 /**
- * Lookup a reference to one of the objects in a repostory,
+ * Lookup a reference to one of the objects in a repository,
  * given a prefix of its identifier (short id).
  *
  * The object obtained will be so that its identifier
@@ -75,7 +75,7 @@
 		git_object **object_out,
 		git_repository *repo,
 		const git_oid *id,
-		unsigned int len,
+		size_t len,
 		git_otype type);
 
 /**
@@ -114,7 +114,7 @@
  * This method instructs the library to close an existing
  * object; note that git_objects are owned and cached by the repository
  * so the object may or may not be freed after this library call,
- * depending on how agressive is the caching mechanism used
+ * depending on how aggressive is the caching mechanism used
  * by the repository.
  *
  * IMPORTANT:
@@ -166,6 +166,36 @@
  * @return size in bytes of the object
  */
 GIT_EXTERN(size_t) git_object__size(git_otype type);
+
+/**
+ * Recursively peel an object until an object of the specified type is met.
+ *
+ * The retrieved `peeled` object is owned by the repository and should be
+ * closed with the `git_object_free` method.
+ *
+ * If you pass `GIT_OBJ_ANY` as the target type, then the object will be
+ * peeled until the type changes (e.g. a tag will be chased until the
+ * referenced object is no longer a tag).
+ *
+ * @param peeled Pointer to the peeled git_object
+ * @param object The object to be processed
+ * @param target_type The type of the requested object (GIT_OBJ_COMMIT,
+ * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY).
+ * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code
+ */
+GIT_EXTERN(int) git_object_peel(
+	git_object **peeled,
+	const git_object *object,
+	git_otype target_type);
+
+/**
+ * Create an in-memory copy of a Git object. The copy must be
+ * explicitly free'd or it will leak.
+ *
+ * @param dest Pointer to store the copy of the object
+ * @param source Original object to copy
+ */
+GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/odb.h b/libgit2/include/git2/odb.h
--- a/libgit2/include/git2/odb.h
+++ b/libgit2/include/git2/odb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
 #include "types.h"
 #include "oid.h"
 #include "odb_backend.h"
+#include "indexer.h"
 
 /**
  * @file git2/odb.h
@@ -62,7 +63,7 @@
  * @param odb database to add the backend to
  * @param backend pointer to a git_odb_backend instance
  * @param priority Value for ordering the backends queue
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
 GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
 
@@ -83,11 +84,28 @@
  * @param odb database to add the backend to
  * @param backend pointer to a git_odb_backend instance
  * @param priority Value for ordering the backends queue
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
 GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
 
 /**
+ * Add an on-disk alternate to an existing Object DB.
+ *
+ * Note that the added path must point to an `objects`, not
+ * to a full repository, to use it as an alternate store.
+ *
+ * Alternate backends are always checked for objects *after*
+ * all the main backends have been exhausted.
+ *
+ * Writing is disabled on alternate backends.
+ *
+ * @param odb database to add the backend to
+ * @param path path to the objects folder for the alternate
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_disk_alternate(git_odb *odb, const char *path);
+
+/**
  * Close an open object database.
  *
  * @param db database pointer to close. If NULL no action is taken.
@@ -135,11 +153,12 @@
  * @param db database to search for the object in.
  * @param short_id a prefix of the id of the object to read.
  * @param len the length of the prefix
- * @return 0 if the object was read;
- *	GIT_ENOTFOUND if the object is not in the database.
- *	GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
+ * @return 
+ * - 0 if the object was read;
+ * - GIT_ENOTFOUND if the object is not in the database.
+ * - GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
  */
-GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len);
+GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len);
 
 /**
  * Read the header of an object from the database, without
@@ -151,15 +170,15 @@
  * of an object, so the whole object will be read and then the
  * header will be returned.
  *
- * @param len_p pointer where to store the length
- * @param type_p pointer where to store the type
+ * @param len_out pointer where to store the length
+ * @param type_out pointer where to store the type
  * @param db database to search for the object in.
  * @param id identity of the object to read.
  * @return
  * - 0 if the object was read;
  * - GIT_ENOTFOUND if the object is not in the database.
  */
-GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id);
+GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_odb *db, const git_oid *id);
 
 /**
  * Determine if the given object can be found in the object database.
@@ -173,6 +192,41 @@
 GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
 
 /**
+ * Refresh the object database to load newly added files.
+ *
+ * If the object databases have changed on disk while the library
+ * is running, this function will force a reload of the underlying
+ * indexes.
+ *
+ * Use this function when you're confident that an external
+ * application has tampered with the ODB.
+ *
+ * NOTE that it is not necessary to call this function at all. The
+ * library will automatically attempt to refresh the ODB
+ * when a lookup fails, to see if the looked up object exists
+ * on disk but hasn't been loaded yet.
+ *
+ * @param db database to refresh
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_refresh(struct git_odb *db);
+
+/**
+ * List all objects available in the database
+ *
+ * The callback will be called for each object available in the
+ * database. Note that the objects are likely to be returned in the index
+ * order, which would make accessing the objects in that order inefficient.
+ * Return a non-zero value from the callback to stop looping.
+ *
+ * @param db database to use
+ * @param cb the callback to call for each object
+ * @param payload data to pass to the callback
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload);
+
+/**
  * Write an object directly into the ODB
  *
  * This method writes a full object straight into the ODB.
@@ -183,14 +237,14 @@
  * This method is provided for compatibility with custom backends
  * which are not able to support streaming writes
  *
- * @param oid pointer to store the OID result of the write
+ * @param out pointer to store the OID result of the write
  * @param odb object database where to store the object
- * @param data buffer with the data to storr
+ * @param data buffer with the data to store
  * @param len size of the buffer
  * @param type type of the data to store
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_odb_write(git_oid *oid, git_odb *odb, const void *data, size_t len, git_otype type);
+GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size_t len, git_otype type);
 
 /**
  * Open a stream to write an object into the ODB
@@ -213,13 +267,13 @@
  *
  * @see git_odb_stream
  *
- * @param stream pointer where to store the stream
+ * @param out pointer where to store the stream
  * @param db object database where the stream will write
  * @param size final size of the object that will be written
  * @param type type of the object that will be written
  * @return 0 if the stream was created; error code otherwise
  */
-GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **stream, git_odb *db, size_t size, git_otype type);
+GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, size_t size, git_otype type);
 
 /**
  * Open a stream to read an object from the ODB
@@ -240,32 +294,58 @@
  *
  * @see git_odb_stream
  *
- * @param stream pointer where to store the stream
+ * @param out pointer where to store the stream
  * @param db object database where the stream will read from
  * @param oid oid of the object the stream will read from
  * @return 0 if the stream was created; error code otherwise
  */
-GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid);
+GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **out, git_odb *db, const git_oid *oid);
 
 /**
+ * Open a stream for writing a pack file to the ODB.
+ *
+ * If the ODB layer understands pack files, then the given
+ * packfile will likely be streamed directly to disk (and a
+ * corresponding index created).  If the ODB layer does not
+ * understand pack files, the objects will be stored in whatever
+ * format the ODB layer uses.
+ *
+ * @see git_odb_writepack
+ *
+ * @param out pointer to the writepack functions
+ * @param db object database where the stream will read from
+ * @param progress_cb function to call with progress information.
+ * Be aware that this is called inline with network and indexing operations,
+ * so performance may be affected.
+ * @param progress_payload payload for the progress callback
+ */
+GIT_EXTERN(int) git_odb_write_pack(
+	git_odb_writepack **out,
+	git_odb *db,
+	git_transfer_progress_callback progress_cb,
+	void *progress_payload);
+
+/**
  * Determine the object-ID (sha1 hash) of a data buffer
  *
- * The resulting SHA-1 OID will the itentifier for the data
+ * The resulting SHA-1 OID will be the identifier for the data
  * buffer as if the data buffer it were to written to the ODB.
  *
- * @param id the resulting object-ID.
+ * @param out the resulting object-ID.
  * @param data data to hash
  * @param len size of the data
  * @param type of the data to hash
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type);
+GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_otype type);
 
 /**
  * Read a file from disk and fill a git_oid with the object id
  * that the file would have if it were written to the Object
- * Database as an object of the given type. Similar functionality
- * to git.git's `git hash-object` without the `-w` flag.
+ * Database as an object of the given type (w/o applying filters).
+ * Similar functionality to git.git's `git hash-object` without
+ * the `-w` flag, however, with the --no-filters flag.
+ * If you need filters, see git_repository_hashfile.
  *
  * @param out oid structure the result is written into.
  * @param path file to read and determine object id for
diff --git a/libgit2/include/git2/odb_backend.h b/libgit2/include/git2/odb_backend.h
--- a/libgit2/include/git2/odb_backend.h
+++ b/libgit2/include/git2/odb_backend.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
 #include "common.h"
 #include "types.h"
 #include "oid.h"
+#include "indexer.h"
 
 /**
  * @file git2/backend.h
@@ -21,11 +22,24 @@
 GIT_BEGIN_DECL
 
 struct git_odb_stream;
+struct git_odb_writepack;
 
-/** An instance for a custom backend */
+/**
+ * Function type for callbacks from git_odb_foreach.
+ */
+typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
+
+/**
+ * An instance for a custom backend
+ */
 struct git_odb_backend {
+	unsigned int version;
 	git_odb *odb;
 
+	/* read and read_prefix each return to libgit2 a buffer which
+	 * will be freed later. The buffer should be allocated using
+	 * the function git_odb_backend_malloc to ensure that it can
+	 * be safely freed later. */
 	int (* read)(
 			void **, size_t *, git_otype *,
 			struct git_odb_backend *,
@@ -42,13 +56,17 @@
 			void **, size_t *, git_otype *,
 			struct git_odb_backend *,
 			const git_oid *,
-			unsigned int);
+			size_t);
 
 	int (* read_header)(
 			size_t *, git_otype *,
 			struct git_odb_backend *,
 			const git_oid *);
 
+	/* The writer may assume that the object
+	 * has already been hashed and is passed
+	 * in the first parameter.
+	 */
 	int (* write)(
 			git_oid *,
 			struct git_odb_backend *,
@@ -71,9 +89,25 @@
 			struct git_odb_backend *,
 			const git_oid *);
 
+	int (* refresh)(struct git_odb_backend *);
+
+	int (* foreach)(
+			struct git_odb_backend *,
+			git_odb_foreach_cb cb,
+			void *payload);
+
+	int (* writepack)(
+			struct git_odb_writepack **,
+			struct git_odb_backend *,
+			git_transfer_progress_callback progress_cb,
+			void *progress_payload);
+
 	void (* free)(struct git_odb_backend *);
 };
 
+#define GIT_ODB_BACKEND_VERSION 1
+#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
+
 /** Streaming mode */
 enum {
 	GIT_STREAM_RDONLY = (1 << 1),
@@ -84,7 +118,7 @@
 /** A stream to read/write from a backend */
 struct git_odb_stream {
 	struct git_odb_backend *backend;
-	int mode;
+	unsigned int mode;
 
 	int (*read)(struct git_odb_stream *stream, char *buffer, size_t len);
 	int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len);
@@ -92,8 +126,23 @@
 	void (*free)(struct git_odb_stream *stream);
 };
 
-GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir);
-GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir, int compression_level, int do_fsync);
+/** A stream to write a pack file to the ODB */
+struct git_odb_writepack {
+	struct git_odb_backend *backend;
+
+	int (*add)(struct git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
+	int (*commit)(struct git_odb_writepack *writepack, git_transfer_progress *stats);
+	void (*free)(struct git_odb_writepack *writepack);
+};
+
+GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
+
+/**
+ * Constructors for in-box ODB backends.
+ */
+GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
+GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
+GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
 
 GIT_END_DECL
 
diff --git a/libgit2/include/git2/oid.h b/libgit2/include/git2/oid.h
--- a/libgit2/include/git2/oid.h
+++ b/libgit2/include/git2/oid.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -30,11 +30,10 @@
 #define GIT_OID_MINPREFIXLEN 4
 
 /** Unique identity of any object (commit, tree, blob, tag). */
-typedef struct _git_oid git_oid;
-struct _git_oid {
+typedef struct git_oid {
 	/** raw binary formatted id */
 	unsigned char id[GIT_OID_RAWSZ];
-};
+} git_oid;
 
 /**
  * Parse a hex formatted object id into a git_oid.
@@ -48,6 +47,16 @@
 GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
 
 /**
+ * Parse a hex formatted null-terminated string into a git_oid.
+ *
+ * @param out oid structure the result is written into.
+ * @param str input hex string; must be at least 4 characters
+ *      long and null-terminated.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str);
+
+/**
  * Parse N characters of a hex formatted object id into a git_oid
  *
  * If N is odd, N-1 characters will be parsed instead.
@@ -71,29 +80,29 @@
 /**
  * Format a git_oid into a hex string.
  *
- * @param str output hex string; must be pointing at the start of
+ * @param out output hex string; must be pointing at the start of
  *		the hex sequence and have at least the number of bytes
  *		needed for an oid encoded in hex (40 bytes). Only the
  *		oid digits are written; a '\\0' terminator must be added
  *		by the caller if it is required.
  * @param oid oid structure to format.
  */
-GIT_EXTERN(void) git_oid_fmt(char *str, const git_oid *oid);
+GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
 
 /**
  * Format a git_oid into a loose-object path string.
  *
  * The resulting string is "aa/...", where "aa" is the first two
- * hex digitis of the oid and "..." is the remaining 38 digits.
+ * hex digits of the oid and "..." is the remaining 38 digits.
  *
- * @param str output hex string; must be pointing at the start of
+ * @param out output hex string; must be pointing at the start of
  *		the hex sequence and have at least the number of bytes
  *		needed for an oid encoded in hex (41 bytes). Only the
  *		oid digits are written; a '\\0' terminator must be added
  *		by the caller if it is required.
- * @param oid oid structure to format.
+ * @param id oid structure to format.
  */
-GIT_EXTERN(void) git_oid_pathfmt(char *str, const git_oid *oid);
+GIT_EXTERN(void) git_oid_pathfmt(char *out, const git_oid *id);
 
 /**
  * Format a git_oid into a newly allocated c-string.
@@ -102,7 +111,7 @@
  * @return the c-string; NULL if memory is exhausted. Caller must
  *			deallocate the string with git__free().
  */
-GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *oid);
+GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id);
 
 /**
  * Format a git_oid into a buffer as a hex format c-string.
@@ -115,11 +124,11 @@
  *
  * @param out the buffer into which the oid string is output.
  * @param n the size of the out buffer.
- * @param oid the oid structure to format.
+ * @param id the oid structure to format.
  * @return the out buffer pointer, assuming no input parameter
  *			errors, otherwise a pointer to an empty string.
  */
-GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *oid);
+GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id);
 
 /**
  * Copy an oid from one structure to another.
@@ -136,9 +145,33 @@
  * @param b second oid structure.
  * @return <0, 0, >0 if a < b, a == b, a > b.
  */
-GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
+GIT_INLINE(int) git_oid_cmp(const git_oid *a, const git_oid *b)
+{
+	const unsigned char *sha1 = a->id;
+	const unsigned char *sha2 = b->id;
+	int i;
 
+	for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
+		if (*sha1 != *sha2)
+			return *sha1 - *sha2;
+	}
+
+	return 0;
+}
+
 /**
+ * Compare two oid structures for equality
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return true if equal, false otherwise
+ */
+GIT_INLINE(int) git_oid_equal(const git_oid *a, const git_oid *b)
+{
+	return !git_oid_cmp(a, b);
+}
+
+/**
  * Compare the first 'len' hexadecimal characters (packets of 4 bits)
  * of two oid structures.
  *
@@ -147,22 +180,24 @@
  * @param len the number of hex chars to compare
  * @return 0 in case of a match
  */
-GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int len);
+GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
 
 /**
  * Check if an oid equals an hex formatted object id.
  *
- * @param a oid structure.
+ * @param id oid structure.
  * @param str input hex string of an object id.
  * @return GIT_ENOTOID if str is not a valid hex string,
  * 0 in case of a match, GIT_ERROR otherwise.
  */
-GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
+GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
 
 /**
  * Check is an oid is all zeros.
+ *
+ * @return 1 if all zeros, 0 otherwise.
  */
-GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
+GIT_EXTERN(int) git_oid_iszero(const git_oid *id);
 
 /**
  * OID Shortener object
@@ -204,12 +239,12 @@
  * GIT_ENOMEM error
  *
  * @param os a `git_oid_shorten` instance
- * @param text_oid an OID in text form
+ * @param text_id an OID in text form
  * @return the minimal length to uniquely identify all OIDs
  *		added so far to the set; or an error code (<0) if an
  *		error occurs.
  */
-GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
+GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_id);
 
 /**
  * Free an OID shortener instance
diff --git a/libgit2/include/git2/pack.h b/libgit2/include/git2/pack.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/pack.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_pack_h__
+#define INCLUDE_git_pack_h__
+
+#include "common.h"
+#include "oid.h"
+
+/**
+ * @file git2/pack.h
+ * @brief Git pack management routines
+ *
+ * Packing objects
+ * ---------------
+ *
+ * Creation of packfiles requires two steps:
+ *
+ * - First, insert all the objects you want to put into the packfile
+ *   using `git_packbuilder_insert` and `git_packbuilder_insert_tree`.
+ *   It's important to add the objects in recency order ("in the order
+ *   that they are 'reachable' from head").
+ *
+ *   "ANY order will give you a working pack, ... [but it is] the thing
+ *   that gives packs good locality. It keeps the objects close to the
+ *   head (whether they are old or new, but they are _reachable_ from the
+ *   head) at the head of the pack. So packs actually have absolutely
+ *   _wonderful_ IO patterns." - Linus Torvalds
+ *   git.git/Documentation/technical/pack-heuristics.txt
+ *
+ * - Second, use `git_packbuilder_write` or `git_packbuilder_foreach` to
+ *   write the resulting packfile.
+ *
+ *   libgit2 will take care of the delta ordering and generation.
+ *   `git_packbuilder_set_threads` can be used to adjust the number of
+ *   threads used for the process.
+ *
+ * See tests-clar/pack/packbuilder.c for an example.
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Initialize a new packbuilder
+ *
+ * @param out The new packbuilder object
+ * @param repo The repository
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_new(git_packbuilder **out, git_repository *repo);
+
+/**
+ * Set number of threads to spawn
+ *
+ * By default, libgit2 won't spawn any threads at all;
+ * when set to 0, libgit2 will autodetect the number of
+ * CPUs.
+ *
+ * @param pb The packbuilder
+ * @param n Number of threads to spawn
+ * @return number of actual threads to be used
+ */
+GIT_EXTERN(unsigned int) git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n);
+
+/**
+ * Insert a single object
+ *
+ * For an optimal pack it's mandatory to insert objects in recency order,
+ * commits followed by trees and blobs.
+ *
+ * @param pb The packbuilder
+ * @param id The oid of the commit
+ * @param name The name; might be NULL
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, const char *name);
+
+/**
+ * Insert a root tree object
+ *
+ * This will add the tree as well as all referenced trees and blobs.
+ *
+ * @param pb The packbuilder
+ * @param id The oid of the root tree
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
+
+/**
+ * Write the new pack and the corresponding index to path
+ *
+ * @param pb The packbuilder
+ * @param path Directory to store the new pack and index
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_write(git_packbuilder *pb, const char *file);
+
+typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
+/**
+ * Create the new pack and pass each object to the callback
+ *
+ * @param pb the packbuilder
+ * @param cb the callback to call with each packed object's buffer
+ * @param payload the callback's data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_foreach_cb cb, void *payload);
+
+/**
+ * Get the total number of objects the packbuilder will write out
+ *
+ * @param pb the packbuilder
+ * @return
+ */
+GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
+
+/**
+ * Get the number of objects the packbuilder has already written out
+ *
+ * @param pb the packbuilder
+ * @return
+ */
+GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
+
+/**
+ * Free the packbuilder and all associated data
+ *
+ * @param pb The packbuilder
+ */
+GIT_EXTERN(void) git_packbuilder_free(git_packbuilder *pb);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/push.h b/libgit2/include/git2/push.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/push.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_push_h__
+#define INCLUDE_git_push_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/push.h
+ * @brief Git push management functions
+ * @defgroup git_push push management functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Controls the behavior of a git_push object.
+ */
+typedef struct {
+	unsigned int version;
+
+	/**
+	 * If the transport being used to push to the remote requires the creation
+	 * of a pack file, this controls the number of worker threads used by
+	 * the packbuilder when creating that pack file to be sent to the remote.
+	 *
+	 * If set to 0, the packbuilder will auto-detect the number of threads
+	 * to create. The default value is 1.
+	 */
+	unsigned int pb_parallelism;
+} git_push_options;
+
+#define GIT_PUSH_OPTIONS_VERSION 1
+#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
+
+/**
+ * Create a new push object
+ *
+ * @param out New push object
+ * @param remote Remote instance
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_new(git_push **out, git_remote *remote);
+
+/**
+ * Set options on a push object
+ *
+ * @param push The push object
+ * @param opts The options to set on the push object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_set_options(
+	git_push *push,
+	const git_push_options *opts);
+
+/**
+ * Add a refspec to be pushed
+ *
+ * @param push The push object
+ * @param refspec Refspec string
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_add_refspec(git_push *push, const char *refspec);
+
+/**
+ * Update remote tips after a push
+ *
+ * @param push The push object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_update_tips(git_push *push);
+
+/**
+ * Actually push all given refspecs
+ *
+ * Note: To check if the push was successful (i.e. all remote references
+ * have been updated as requested), you need to call both
+ * `git_push_unpack_ok` and `git_push_status_foreach`. The remote
+ * repository might have refused to update some or all of the references.
+ *
+ * @param push The push object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_finish(git_push *push);
+
+/**
+ * Check if remote side successfully unpacked
+ *
+ * @param push The push object
+ *
+ * @return true if equal, false otherwise
+ */
+GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
+
+/**
+ * Call callback `cb' on each status
+ *
+ * For each of the updated references, we receive a status report in the
+ * form of `ok refs/heads/master` or `ng refs/heads/master <msg>`.
+ * `msg != NULL` means the reference has not been updated for the given
+ * reason.
+ *
+ * @param push The push object
+ * @param cb The callback to call on each object
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_push_status_foreach(git_push *push,
+			int (*cb)(const char *ref, const char *msg, void *data),
+			void *data);
+
+/**
+ * Free the given push object
+ *
+ * @param push The push object
+ */
+GIT_EXTERN(void) git_push_free(git_push *push);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/refdb.h b/libgit2/include/git2/refdb.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/refdb.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_refdb_h__
+#define INCLUDE_git_refdb_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "refs.h"
+
+/**
+ * @file git2/refdb.h
+ * @brief Git custom refs backend functions
+ * @defgroup git_refdb Git custom refs backend API
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new reference.  Either an oid or a symbolic target must be
+ * specified.
+ *
+ * @param refdb the reference database to associate with this reference
+ * @param name the reference name
+ * @param oid the object id for a direct reference
+ * @param symbolic the target for a symbolic reference
+ * @return the created git_reference or NULL on error
+ */
+GIT_EXTERN(git_reference *) git_reference__alloc(
+	git_refdb *refdb,
+	const char *name,
+	const git_oid *oid,
+	const char *symbolic);
+
+/**
+ * Create a new reference database with no backends.
+ *
+ * Before the Ref DB can be used for read/writing, a custom database
+ * backend must be manually set using `git_refdb_set_backend()`
+ *
+ * @param out location to store the database pointer, if opened.
+ *			Set to NULL if the open failed.
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_refdb_new(git_refdb **out, git_repository *repo);
+
+/**
+ * Create a new reference database and automatically add
+ * the default backends:
+ *
+ *  - git_refdb_dir: read and write loose and packed refs
+ *      from disk, assuming the repository dir as the folder
+ *
+ * @param out location to store the database pointer, if opened.
+ *			Set to NULL if the open failed.
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_refdb_open(git_refdb **out, git_repository *repo);
+
+/**
+ * Suggests that the given refdb compress or optimize its references.
+ * This mechanism is implementation specific.  For on-disk reference
+ * databases, for example, this may pack all loose references.
+ */
+GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb);
+
+/**
+ * Close an open reference database.
+ *
+ * @param refdb reference database pointer or NULL
+ */
+GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
+
+/**
+ * Sets the custom backend to an existing reference DB
+ *
+ * Read <refdb_backends.h> for more information.
+ *
+ * @param refdb database to add the backend to
+ * @param backend pointer to a git_refdb_backend instance
+ * @param priority Value for ordering the backends queue
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_refdb_set_backend(
+	git_refdb *refdb,
+	git_refdb_backend *backend);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/libgit2/include/git2/refdb_backend.h b/libgit2/include/git2/refdb_backend.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/refdb_backend.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_refdb_backend_h__
+#define INCLUDE_git_refdb_backend_h__
+
+#include "common.h"
+#include "types.h"
+#include "refs.h"
+#include "oid.h"
+
+/**
+ * @file git2/refdb_backend.h
+ * @brief Git custom refs backend functions
+ * @defgroup git_refdb_backend Git custom refs backend API
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** An instance for a custom backend */
+struct git_refdb_backend {
+    unsigned int version;
+
+	/**
+	 * Queries the refdb backend to determine if the given ref_name
+	 * exists.  A refdb implementation must provide this function.
+	 */
+	int (*exists)(
+		int *exists,
+		struct git_refdb_backend *backend,
+		const char *ref_name);
+
+	/**
+	 * Queries the refdb backend for a given reference.  A refdb
+	 * implementation must provide this function.
+	 */
+	int (*lookup)(
+		git_reference **out,
+		struct git_refdb_backend *backend,
+		const char *ref_name);
+
+	/**
+	 * Enumerates each reference in the refdb.  A refdb implementation must
+	 * provide this function.
+	 */
+	int (*foreach)(
+		struct git_refdb_backend *backend,
+		unsigned int list_flags,
+		git_reference_foreach_cb callback,
+		void *payload);
+
+	/**
+	 * Enumerates each reference in the refdb that matches the given
+	 * glob string.  A refdb implementation may provide this function;
+	 * if it is not provided, foreach will be used and the results filtered
+	 * against the glob.
+	 */
+	int (*foreach_glob)(
+		struct git_refdb_backend *backend,
+		const char *glob,
+		unsigned int list_flags,
+		git_reference_foreach_cb callback,
+		void *payload);
+
+	/**
+	 * Writes the given reference to the refdb.  A refdb implementation
+	 * must provide this function.
+	 */
+	int (*write)(struct git_refdb_backend *backend, const git_reference *ref);
+
+	/**
+	 * Deletes the given reference from the refdb.  A refdb implementation
+	 * must provide this function.
+	 */
+	int (*delete)(struct git_refdb_backend *backend, const git_reference *ref);
+
+	/**
+	 * Suggests that the given refdb compress or optimize its references.
+	 * This mechanism is implementation specific.  (For on-disk reference
+	 * databases, this may pack all loose references.)    A refdb
+	 * implementation may provide this function; if it is not provided,
+	 * nothing will be done.
+	 */
+	int (*compress)(struct git_refdb_backend *backend);
+
+	/**
+	 * Frees any resources held by the refdb.  A refdb implementation may
+	 * provide this function; if it is not provided, nothing will be done.
+	 */
+	void (*free)(struct git_refdb_backend *backend);
+};
+
+#define GIT_ODB_BACKEND_VERSION 1
+#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
+
+/**
+ * Constructors for default refdb backends.
+ */
+GIT_EXTERN(int) git_refdb_backend_fs(
+	struct git_refdb_backend **backend_out,
+	git_repository *repo,
+	git_refdb *refdb);
+
+GIT_END_DECL
+
+#endif
diff --git a/libgit2/include/git2/reflog.h b/libgit2/include/git2/reflog.h
--- a/libgit2/include/git2/reflog.h
+++ b/libgit2/include/git2/reflog.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -23,41 +23,54 @@
 /**
  * Read the reflog for the given reference
  *
+ * If there is no reflog file for the given
+ * reference yet, an empty reflog object will
+ * be returned.
+ *
  * The reflog must be freed manually by using
  * git_reflog_free().
  *
- * @param reflog pointer to reflog
+ * @param out pointer to reflog
  * @param ref reference to read the reflog for
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
+GIT_EXTERN(int) git_reflog_read(git_reflog **out, const git_reference *ref);
 
 /**
- * Write a new reflog for the given reference
- *
- * If there is no reflog file for the given
- * reference yet, it will be created.
+ * Write an existing in-memory reflog object back to disk
+ * using an atomic file lock.
  *
- * `oid_old` may be NULL in case it's a new reference.
+ * @param reflog an existing reflog object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
+
+/**
+ * Add a new entry to the reflog.
  *
  * `msg` is optional and can be NULL.
  *
- * @param ref the changed reference
- * @param oid_old the OID the reference was pointing to
+ * @param reflog an existing reflog object
+ * @param id the OID the reference is now pointing to
  * @param committer the signature of the committer
  * @param msg the reflog message
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_signature *committer, const char *msg);
+GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
 
 /**
  * Rename the reflog for the given reference
  *
+ * The reflog to be renamed is expected to already exist
+ *
+ * The new name will be checked for validity.
+ * See `git_reference_create_symbolic()` for rules about valid names.
+ *
  * @param ref the reference
- * @param new_name the new name of the reference
- * @return 0 or an error code
+ * @param name the new name of the reference
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *new_name);
+GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *name);
 
 /**
  * Delete the reflog for the given reference
@@ -73,24 +86,50 @@
  * @param reflog the previously loaded reflog
  * @return the number of log entries
  */
-GIT_EXTERN(unsigned int) git_reflog_entrycount(git_reflog *reflog);
+GIT_EXTERN(size_t) git_reflog_entrycount(git_reflog *reflog);
 
 /**
  * Lookup an entry by its index
  *
+ * Requesting the reflog entry with an index of 0 (zero) will
+ * return the most recently created entry.
+ *
  * @param reflog a previously loaded reflog
- * @param idx the position to lookup
+ * @param idx the position of the entry to lookup. Should be greater than or
+ * equal to 0 (zero) and less than `git_reflog_entrycount()`.
  * @return the entry; NULL if not found
  */
-GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx);
+GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, size_t idx);
 
 /**
+ * Remove an entry from the reflog by its index
+ *
+ * To ensure there's no gap in the log history, set `rewrite_previous_entry`
+ * param value to 1. When deleting entry `n`, member old_oid of entry `n-1`
+ * (if any) will be updated with the value of member new_oid of entry `n+1`.
+ *
+ * @param reflog a previously loaded reflog.
+ *
+ * @param idx the position of the entry to remove. Should be greater than or
+ * equal to 0 (zero) and less than `git_reflog_entrycount()`.
+ *
+ * @param rewrite_previous_entry 1 to rewrite the history; 0 otherwise.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if the entry doesn't exist
+ * or an error code.
+ */
+GIT_EXTERN(int) git_reflog_drop(
+	git_reflog *reflog,
+	size_t idx,
+	int rewrite_previous_entry);
+
+/**
  * Get the old oid
  *
  * @param entry a reflog entry
  * @return the old oid
  */
-GIT_EXTERN(const git_oid *) git_reflog_entry_oidold(const git_reflog_entry *entry);
+GIT_EXTERN(const git_oid *) git_reflog_entry_id_old(const git_reflog_entry *entry);
 
 /**
  * Get the new oid
@@ -98,7 +137,7 @@
  * @param entry a reflog entry
  * @return the new oid at this time
  */
-GIT_EXTERN(const git_oid *) git_reflog_entry_oidnew(const git_reflog_entry *entry);
+GIT_EXTERN(const git_oid *) git_reflog_entry_id_new(const git_reflog_entry *entry);
 
 /**
  * Get the committer of this entry
@@ -106,15 +145,15 @@
  * @param entry a reflog entry
  * @return the committer
  */
-GIT_EXTERN(git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry);
+GIT_EXTERN(const git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry);
 
 /**
- * Get the log msg
+ * Get the log message
  *
  * @param entry a reflog entry
  * @return the log msg
  */
-GIT_EXTERN(char *) git_reflog_entry_msg(const git_reflog_entry *entry);
+GIT_EXTERN(const char *) git_reflog_entry_message(const git_reflog_entry *entry);
 
 /**
  * Free the reflog
diff --git a/libgit2/include/git2/refs.h b/libgit2/include/git2/refs.h
--- a/libgit2/include/git2/refs.h
+++ b/libgit2/include/git2/refs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
 #include "common.h"
 #include "types.h"
 #include "oid.h"
+#include "strarray.h"
 
 /**
  * @file git2/refs.h
@@ -21,176 +22,222 @@
 GIT_BEGIN_DECL
 
 /**
- * Lookup a reference by its name in a repository.
+ * Lookup a reference by name in a repository.
  *
- * The generated reference must be freed by the user.
+ * The returned reference must be freed by the user.
  *
- * @param reference_out pointer to the looked-up reference
+ * The name will be checked for validity.
+ * See `git_reference_create_symbolic()` for rules about valid names.
+ *
+ * @param out pointer to the looked-up reference
  * @param repo the repository to look up the reference
- * @param name the long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
- * @return 0 or an error code
+ * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
+ * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
  */
-GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name);
+GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
 
 /**
  * Lookup a reference by name and resolve immediately to OID.
  *
- * @param oid Pointer to oid to be filled in
+ * This function provides a quick way to resolve a reference name straight
+ * through to the object id that it refers to.  This avoids having to
+ * allocate or free any `git_reference` objects for simple situations.
+ *
+ * The name will be checked for validity.
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * @param out Pointer to oid to be filled in
  * @param repo The repository in which to look up the reference
  * @param name The long name for the reference
- * @return 0 on success, -1 if name could not be resolved
+ * @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
  */
-GIT_EXTERN(int) git_reference_name_to_oid(
+GIT_EXTERN(int) git_reference_name_to_id(
 	git_oid *out, git_repository *repo, const char *name);
 
 /**
  * Create a new symbolic reference.
  *
- * The reference will be created in the repository and written
- * to the disk.
+ * A symbolic reference is a reference name that refers to another
+ * reference name.  If the other name moves, the symbolic name will move,
+ * too.  As a simple example, the "HEAD" reference might refer to
+ * "refs/heads/master" while on the "master" branch of a repository.
  *
- * The generated reference must be freed by the user.
+ * The symbolic reference will be created in the repository and written to
+ * the disk.  The generated reference object must be freed by the user.
  *
- * If `force` is true and there already exists a reference
- * with the same name, it will be overwritten.
+ * Valid reference names must follow one of two patterns:
  *
- * @param ref_out Pointer to the newly created reference
+ * 1. Top-level names must contain only capital letters and underscores,
+ *    and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything.  You must avoid
+ *    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ *    sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * @param out Pointer to the newly created reference
  * @param repo Repository where that reference will live
  * @param name The name of the reference
  * @param target The target of the reference
  * @param force Overwrite existing references
- * @return 0 or an error code
+ * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force);
+GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
 
 /**
- * Create a new object id reference.
+ * Create a new direct reference.
  *
- * The reference will be created in the repository and written
- * to the disk.
+ * A direct reference (also called an object id reference) refers directly
+ * to a specific object id (a.k.a. OID or SHA) in the repository.  The id
+ * permanently refers to the object (although the reference itself can be
+ * moved).  For example, in libgit2 the direct ref "refs/tags/v0.17.0"
+ * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
  *
- * The generated reference must be freed by the user.
+ * The direct reference will be created in the repository and written to
+ * the disk.  The generated reference object must be freed by the user.
  *
- * If `force` is true and there already exists a reference
- * with the same name, it will be overwritten.
+ * Valid reference names must follow one of two patterns:
  *
- * @param ref_out Pointer to the newly created reference
+ * 1. Top-level names must contain only capital letters and underscores,
+ *    and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything.  You must avoid
+ *    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ *    sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * @param out Pointer to the newly created reference
  * @param repo Repository where that reference will live
  * @param name The name of the reference
  * @param id The object id pointed to by the reference.
  * @param force Overwrite existing references
- * @return 0 or an error code
+ * @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force);
+GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
 
 /**
- * Get the OID pointed to by a reference.
+ * Get the OID pointed to by a direct reference.
  *
- * Only available if the reference is direct (i.e. not symbolic)
+ * Only available if the reference is direct (i.e. an object id reference,
+ * not a symbolic one).
  *
+ * To find the OID of a symbolic ref, call `git_reference_resolve()` and
+ * then this function (or maybe use `git_reference_name_to_id()` to
+ * directly resolve a reference name all the way through to an OID).
+ *
  * @param ref The reference
  * @return a pointer to the oid if available, NULL otherwise
  */
-GIT_EXTERN(const git_oid *) git_reference_oid(git_reference *ref);
+GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
 
 /**
- * Get full name to the reference pointed by this reference
+ * Get full name to the reference pointed to by a symbolic reference.
  *
- * Only available if the reference is symbolic
+ * Only available if the reference is symbolic.
  *
  * @param ref The reference
  * @return a pointer to the name if available, NULL otherwise
  */
-GIT_EXTERN(const char *) git_reference_target(git_reference *ref);
+GIT_EXTERN(const char *) git_reference_symbolic_target(const git_reference *ref);
 
 /**
- * Get the type of a reference
+ * Get the type of a reference.
  *
  * Either direct (GIT_REF_OID) or symbolic (GIT_REF_SYMBOLIC)
  *
  * @param ref The reference
  * @return the type
  */
-GIT_EXTERN(git_ref_t) git_reference_type(git_reference *ref);
+GIT_EXTERN(git_ref_t) git_reference_type(const git_reference *ref);
 
 /**
- * Get the full name of a reference
+ * Get the full name of a reference.
  *
+ * See `git_reference_create_symbolic()` for rules about valid names.
+ *
  * @param ref The reference
  * @return the full name for the ref
  */
-GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
+GIT_EXTERN(const char *) git_reference_name(const git_reference *ref);
 
 /**
- * Resolve a symbolic reference
+ * Resolve a symbolic reference to a direct reference.
  *
- * Thie method iteratively peels a symbolic reference
- * until it resolves to a direct reference to an OID.
+ * This method iteratively peels a symbolic reference until it resolves to
+ * a direct reference to an OID.
  *
- * The peeled reference is returned in the `resolved_ref`
- * argument, and must be freed manually once it's no longer
- * needed.
+ * The peeled reference is returned in the `resolved_ref` argument, and
+ * must be freed manually once it's no longer needed.
  *
- * If a direct reference is passed as an argument,
- * a copy of that reference is returned. This copy must
- * be manually freed too.
+ * If a direct reference is passed as an argument, a copy of that
+ * reference is returned. This copy must be manually freed too.
  *
  * @param resolved_ref Pointer to the peeled reference
  * @param ref The reference
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref);
+GIT_EXTERN(int) git_reference_resolve(git_reference **out, const git_reference *ref);
 
 /**
- * Get the repository where a reference resides
+ * Get the repository where a reference resides.
  *
  * @param ref The reference
  * @return a pointer to the repo
  */
-GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref);
+GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
 
 /**
- * Set the symbolic target of a reference.
+ * Create a new reference with the same name as the given reference but a
+ * different symbolic target. The reference must be a symbolic reference,
+ * otherwise this will fail.
  *
- * The reference must be a symbolic reference, otherwise
- * this method will fail.
+ * The new reference will be written to disk, overwriting the given reference.
  *
- * The reference will be automatically updated in
- * memory and on disk.
+ * The target name will be checked for validity.
+ * See `git_reference_create_symbolic()` for rules about valid names.
  *
+ * @param out Pointer to the newly created reference
  * @param ref The reference
  * @param target The new target for the reference
- * @return 0 or an error code
+ * @return 0 on success, EINVALIDSPEC or an error code
  */
-GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target);
+GIT_EXTERN(int) git_reference_symbolic_set_target(
+	git_reference **out,
+	git_reference *ref,
+	const char *target);
 
 /**
- * Set the OID target of a reference.
- *
- * The reference must be a direct reference, otherwise
- * this method will fail.
+ * Create a new reference with the same name as the given reference but a
+ * different OID target. The reference must be a direct reference, otherwise
+ * this will fail.
  *
- * The reference will be automatically updated in
- * memory and on disk.
+ * The new reference will be written to disk, overwriting the given reference.
  *
+ * @param out Pointer to the newly created reference
  * @param ref The reference
  * @param id The new target OID for the reference
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
+GIT_EXTERN(int) git_reference_set_target(
+	git_reference **out,
+	git_reference *ref,
+	const git_oid *id);
 
 /**
- * Rename an existing reference
+ * Rename an existing reference.
  *
  * This method works for both direct and symbolic references.
- * The new name will be checked for validity and may be
- * modified into a normalized form.
  *
- * The given git_reference will be updated in place.
+ * The new name will be checked for validity.
+ * See `git_reference_create_symbolic()` for rules about valid names.
  *
- * The reference will be immediately renamed in-memory
- * and on disk.
+ * On success, the given git_reference will be deleted from disk and a
+ * new `git_reference` will be returned.
  *
+ * The reference will be immediately renamed in-memory and on disk.
+ *
  * If the `force` flag is not enabled, and there's already
  * a reference with the given name, the renaming will fail.
  *
@@ -200,20 +247,23 @@
  * the reflog if it exists.
  *
  * @param ref The reference to rename
- * @param new_name The new name for the reference
+ * @param name The new name for the reference
  * @param force Overwrite an existing reference
- * @return 0 or an error code
+ * @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
  *
  */
-GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
+GIT_EXTERN(int) git_reference_rename(
+	git_reference **out,
+	git_reference *ref,
+	const char *new_name,
+	int force);
 
 /**
- * Delete an existing reference
- *
- * This method works for both direct and symbolic references.
+ * Delete an existing reference.
  *
- * The reference will be immediately removed on disk and from
- * memory. The given reference pointer will no longer be valid.
+ * This method works for both direct and symbolic references.  The reference
+ * will be immediately removed on disk but the memory will not be freed.
+ * Callers must call `git_reference_free`.
  *
  * @param ref The reference to remove
  * @return 0 or an error code
@@ -221,108 +271,209 @@
 GIT_EXTERN(int) git_reference_delete(git_reference *ref);
 
 /**
- * Pack all the loose references in the repository
+ * Fill a list with all the references that can be found in a repository.
  *
- * This method will load into the cache all the loose
- * references on the repository and update the
- * `packed-refs` file with them.
+ * Using the `list_flags` parameter, the listed references may be filtered
+ * by type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of
+ * `git_ref_t` values.  To include packed refs, include `GIT_REF_PACKED`.
+ * For convenience, use the value `GIT_REF_LISTALL` to obtain all
+ * references, including packed ones.
  *
- * Once the `packed-refs` file has been written properly,
- * the loose references will be removed from disk.
+ * The string array will be filled with the names of all references; these
+ * values are owned by the user and should be free'd manually when no
+ * longer needed, using `git_strarray_free()`.
  *
- * @param repo Repository where the loose refs will be packed
+ * @param array Pointer to a git_strarray structure where
+ *		the reference names will be stored
+ * @param repo Repository where to find the refs
+ * @param list_flags Filtering flags for the reference listing
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_reference_packall(git_repository *repo);
+GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
 
+typedef int (*git_reference_foreach_cb)(const char *refname, void *payload);
+
 /**
- * Fill a list with all the references that can be found
- * in a repository.
+ * Perform a callback on each reference in the repository.
  *
- * The listed references may be filtered by type, or using
- * a bitwise OR of several types. Use the magic value
- * `GIT_REF_LISTALL` to obtain all references, including
- * packed ones.
+ * Using the `list_flags` parameter, the references may be filtered by
+ * type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of
+ * `git_ref_t` values.  To include packed refs, include `GIT_REF_PACKED`.
+ * For convenience, use the value `GIT_REF_LISTALL` to obtain all
+ * references, including packed ones.
  *
- * The string array will be filled with the names of all
- * references; these values are owned by the user and
- * should be free'd manually when no longer needed, using
- * `git_strarray_free`.
+ * The `callback` function will be called for each reference in the
+ * repository, receiving the name of the reference and the `payload` value
+ * passed to this method.  Returning a non-zero value from the callback
+ * will terminate the iteration.
  *
- * @param array Pointer to a git_strarray structure where
- *		the reference names will be stored
  * @param repo Repository where to find the refs
- * @param list_flags Filtering flags for the reference
- *		listing.
- * @return 0 or an error code
+ * @param list_flags Filtering flags for the reference listing.
+ * @param callback Function which will be called for every listed ref
+ * @param payload Additional data to pass to the callback
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
-GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
+GIT_EXTERN(int) git_reference_foreach(
+	git_repository *repo,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload);
 
+/**
+ * Free the given reference.
+ *
+ * @param ref git_reference
+ */
+GIT_EXTERN(void) git_reference_free(git_reference *ref);
 
 /**
- * Perform an operation on each reference in the repository
+ * Compare two references.
  *
- * The processed references may be filtered by type, or using
- * a bitwise OR of several types. Use the magic value
- * `GIT_REF_LISTALL` to obtain all references, including
- * packed ones.
+ * @param ref1 The first git_reference
+ * @param ref2 The second git_reference
+ * @return 0 if the same, else a stable but meaningless ordering.
+ */
+GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
+
+/**
+ * Perform a callback on each reference in the repository whose name
+ * matches the given pattern.
  *
- * The `callback` function will be called for each of the references
- * in the repository, and will receive the name of the reference and
- * the `payload` value passed to this method.
+ * This function acts like `git_reference_foreach()` with an additional
+ * pattern match being applied to the reference name before issuing the
+ * callback function.  See that function for more information.
  *
+ * The pattern is matched using fnmatch or "glob" style where a '*' matches
+ * any sequence of letters, a '?' matches any letter, and square brackets
+ * can be used to define character ranges (such as "[0-9]" for digits).
+ *
  * @param repo Repository where to find the refs
- * @param list_flags Filtering flags for the reference
- *		listing.
+ * @param glob Pattern to match (fnmatch-style) against reference name.
+ * @param list_flags Filtering flags for the reference listing.
  * @param callback Function which will be called for every listed ref
  * @param payload Additional data to pass to the callback
- * @return 0 or an error code
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
-GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
+GIT_EXTERN(int) git_reference_foreach_glob(
+	git_repository *repo,
+	const char *glob,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload);
 
 /**
- * Check if a reference has been loaded from a packfile
+ * Check if a reflog exists for the specified reference.
  *
  * @param ref A git reference
- * @return 0 in case it's not packed; 1 otherwise
+ *
+ * @return 0 when no reflog can be found, 1 when it exists;
+ * otherwise an error code.
  */
-GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
+GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
 
 /**
- * Reload a reference from disk
+ * Check if a reference is a local branch.
  *
- * Reference pointers may become outdated if the Git
- * repository is accessed simultaneously by other clients
- * whilt the library is open.
+ * @param ref A git reference
  *
- * This method forces a reload of the reference from disk,
- * to ensure that the provided information is still
- * reliable.
+ * @return 1 when the reference lives in the refs/heads
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
+
+/**
+ * Check if a reference is a remote tracking branch
  *
- * If the reload fails (e.g. the reference no longer exists
- * on disk, or has become corrupted), an error code will be
- * returned and the reference pointer will be invalidated.
+ * @param ref A git reference
  *
- * @param ref The reference to reload
- * @return 0 on success, or an error code
+ * @return 1 when the reference lives in the refs/remotes
+ * namespace; 0 otherwise.
  */
-GIT_EXTERN(int) git_reference_reload(git_reference *ref);
+GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
 
+
+typedef enum {
+	GIT_REF_FORMAT_NORMAL = 0,
+
+	/**
+	 * Control whether one-level refnames are accepted
+	 * (i.e., refnames that do not contain multiple /-separated
+	 * components). Those are expected to be written only using
+	 * uppercase letters and underscore (FETCH_HEAD, ...)
+	 */
+	GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0),
+
+	/**
+	 * Interpret the provided name as a reference pattern for a
+	 * refspec (as used with remote repositories). If this option
+	 * is enabled, the name is allowed to contain a single * (<star>)
+	 * in place of a one full pathname component
+	 * (e.g., foo/<star>/bar but not foo/bar<star>).
+	 */
+	GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
+} git_reference_normalize_t;
+
 /**
- * Free the given reference
+ * Normalize reference name and check validity.
  *
- * @param ref git_reference
+ * This will normalize the reference name by removing any leading slash
+ * '/' characters and collapsing runs of adjacent slashes between name
+ * components into a single slash.
+ *
+ * Once normalized, if the reference name is valid, it will be returned in
+ * the user allocated buffer.
+ *
+ * See `git_reference_create_symbolic()` for rules about valid names.
+ *
+ * @param buffer_out User allocated buffer to store normalized name
+ * @param buffer_size Size of buffer_out
+ * @param name Reference name to be checked.
+ * @param flags Flags to constrain name validation rules - see the
+ *              GIT_REF_FORMAT constants above.
+ * @return 0 on success, GIT_EBUFS if buffer is too small, EINVALIDSPEC
+ * or an error code.
  */
-GIT_EXTERN(void) git_reference_free(git_reference *ref);
+GIT_EXTERN(int) git_reference_normalize_name(
+	char *buffer_out,
+	size_t buffer_size,
+	const char *name,
+	unsigned int flags);
 
 /**
- * Compare two references.
+ * Recursively peel reference until object of the specified type is found.
  *
- * @param ref1 The first git_reference
- * @param ref2 The second git_reference
- * @return 0 if the same, else a stable but meaningless ordering.
+ * The retrieved `peeled` object is owned by the repository
+ * and should be closed with the `git_object_free` method.
+ *
+ * If you pass `GIT_OBJ_ANY` as the target type, then the object
+ * will be peeled until a non-tag object is met.
+ *
+ * @param peeled Pointer to the peeled git_object
+ * @param ref The reference to be processed
+ * @param target_type The type of the requested object (GIT_OBJ_COMMIT,
+ * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY).
+ * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code
  */
-GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
+GIT_EXTERN(int) git_reference_peel(
+	git_object **out,
+	git_reference *ref,
+	git_otype type);
+
+/**
+ * Ensure the reference name is well-formed.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ *    and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything.  You must avoid
+ *    the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ *    sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * @param refname name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/refspec.h b/libgit2/include/git2/refspec.h
--- a/libgit2/include/git2/refspec.h
+++ b/libgit2/include/git2/refspec.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -36,6 +36,14 @@
 GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
 
 /**
+ * Get the force update setting
+ *
+ * @param refspec the refspec
+ * @return 1 if force update has been set, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
+
+/**
  * Check if a refspec's source descriptor matches a reference 
  *
  * @param refspec the refspec
@@ -45,15 +53,35 @@
 GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
 
 /**
+ * Check if a refspec's destination descriptor matches a reference
+ *
+ * @param refspec the refspec
+ * @param refname the name of the reference to check
+ * @return 1 if the refspec matches, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
+
+/**
  * Transform a reference to its target following the refspec's rules
  *
  * @param out where to store the target name
- * @param outlen the size ouf the `out` buffer
+ * @param outlen the size of the `out` buffer
  * @param spec the refspec
  * @param name the name of the reference to transform
  * @return 0, GIT_EBUFS or another error
  */
 GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
+
+/**
+ * Transform a target reference to its source reference following the refspec's rules
+ *
+ * @param out where to store the source reference name
+ * @param outlen the size of the `out` buffer
+ * @param spec the refspec
+ * @param name the name of the reference to transform
+ * @return 0, GIT_EBUFS or another error
+ */
+GIT_EXTERN(int) git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name);
 
 GIT_END_DECL
 
diff --git a/libgit2/include/git2/remote.h b/libgit2/include/git2/remote.h
--- a/libgit2/include/git2/remote.h
+++ b/libgit2/include/git2/remote.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,6 +12,8 @@
 #include "refspec.h"
 #include "net.h"
 #include "indexer.h"
+#include "strarray.h"
+#include "transport.h"
 
 /**
  * @file git2/remote.h
@@ -22,6 +24,7 @@
  */
 GIT_BEGIN_DECL
 
+typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
 /*
  * TODO: This functions still need to be implemented:
  * - _listcb/_foreach
@@ -31,35 +34,64 @@
  */
 
 /**
+ * Add a remote with the default fetch refspec to the repository's configuration.  This
+ * calls git_remote_save before returning.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create(
+		git_remote **out,
+		git_repository *repo,
+		const char *name,
+		const char *url);
+
+/**
  * Create a remote in memory
  *
- * Create a remote with the default refspecs in memory. You can use
- * this when you have a URL instead of a remote's name.
+ * Create a remote with the given refspec in memory. You can use
+ * this when you have a URL instead of a remote's name.  Note that in-memory
+ * remotes cannot be converted to persisted remotes.
  *
+ * The name, when provided, will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
  * @param out pointer to the new remote object
- * @param repo the associtated repository
- * @param name the remote's name
+ * @param repo the associated repository
+ * @param fetch the fetch refspec to use for this remote. May be NULL for defaults.
  * @param url the remote repository's URL
- * @param fetch the fetch refspec to use for this remote
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch);
+GIT_EXTERN(int) git_remote_create_inmemory(
+		git_remote **out,
+		git_repository *repo,
+		const char *fetch,
+		const char *url);
 
 /**
  * Get the information for a particular remote
  *
+ * The name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
  * @param out pointer to the new remote object
- * @param cfg the repository's configuration
+ * @param repo the associated repository
  * @param name the remote's name
- * @return 0 or an error code
+ * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const char *name);
 
 /**
  * Save a remote to its repository's configuration
  *
+ * One can't save a in-memory remote. Doing so will
+ * result in a GIT_EINVALIDSPEC being returned.
+ *
  * @param remote the remote to save to config
- * @return 0 or an error code
+ * @return 0, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_remote_save(const git_remote *remote);
 
@@ -67,9 +99,9 @@
  * Get the remote's name
  *
  * @param remote the remote
- * @return a pointer to the name
+ * @return a pointer to the name or NULL for in-memory remotes
  */
-GIT_EXTERN(const char *) git_remote_name(git_remote *remote);
+GIT_EXTERN(const char *) git_remote_name(const git_remote *remote);
 
 /**
  * Get the remote's url
@@ -77,9 +109,39 @@
  * @param remote the remote
  * @return a pointer to the url
  */
-GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
+GIT_EXTERN(const char *) git_remote_url(const git_remote *remote);
 
 /**
+ * Get the remote's url for pushing
+ *
+ * @param remote the remote
+ * @return a pointer to the url or NULL if no special url for pushing is set
+ */
+GIT_EXTERN(const char *) git_remote_pushurl(const git_remote *remote);
+
+/**
+ * Set the remote's url
+ *
+ * Existing connections will not be updated.
+ *
+ * @param remote the remote
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url);
+
+/**
+ * Set the remote's url for pushing
+ *
+ * Existing connections will not be updated.
+ *
+ * @param remote the remote
+ * @param url the url to set or NULL to clear the pushurl
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
+
+/**
  * Set the remote's fetch refspec
  *
  * @param remote the remote
@@ -94,13 +156,13 @@
  * @param remote the remote
  * @return a pointer to the fetch refspec or NULL if it doesn't exist
  */
-GIT_EXTERN(const git_refspec *) git_remote_fetchspec(git_remote *remote);
+GIT_EXTERN(const git_refspec *) git_remote_fetchspec(const git_remote *remote);
 
 /**
  * Set the remote's push refspec
  *
  * @param remote the remote
- * @apram spec the new push refspec
+ * @param spec the new push refspec
  * @return 0 or an error value
  */
 GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec);
@@ -112,7 +174,7 @@
  * @return a pointer to the push refspec or NULL if it doesn't exist
  */
 
-GIT_EXTERN(const git_refspec *) git_remote_pushspec(git_remote *remote);
+GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote);
 
 /**
  * Open a connection to a remote
@@ -125,7 +187,7 @@
  * @param direction whether you want to receive or send data
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
+GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
 
 /**
  * Get a list of refs at the remote
@@ -133,9 +195,13 @@
  * The remote (or more exactly its transport) must be connected. The
  * memory belongs to the remote.
  *
- * @param refs where to store the refs
+ * If you a return a non-zero value from the callback, this will stop
+ * looping over the refs.
+ *
  * @param remote the remote
- * @return 0 or an error code
+ * @param list_cb function to call with each ref discovered at the remote
+ * @param payload additional data to pass to the callback
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
 
@@ -149,10 +215,16 @@
  * filename will be NULL and the function will return success.
  *
  * @param remote the remote to download from
- * @param filename where to store the temproray filename
+ * @param progress_cb function to call with progress information.  Be aware that
+ * this is called inline with network and indexing operations, so performance
+ * may be affected.
+ * @param progress_payload payload for the progress callback
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
+GIT_EXTERN(int) git_remote_download(
+		git_remote *remote,
+		git_transfer_progress_callback progress_cb,
+		void *payload);
 
 /**
  * Check whether the remote is connected
@@ -160,11 +232,22 @@
  * Check whether the remote's underlying transport is connected to the
  * remote host.
  *
+ * @param remote the remote
  * @return 1 if it's connected, 0 otherwise.
  */
 GIT_EXTERN(int) git_remote_connected(git_remote *remote);
 
 /**
+ * Cancel the operation
+ *
+ * At certain points in its operation, the network code checks whether
+ * the operation has been cancelled and if so stops the operation.
+ *
+ * @param remote the remote
+ */
+GIT_EXTERN(void) git_remote_stop(git_remote *remote);
+
+/**
  * Disconnect from the remote
  *
  * Close the connection to the remote and free the underlying
@@ -188,14 +271,14 @@
  * Update the tips to the new state
  *
  * @param remote the remote to update
- * @param cb callback to run on each ref update. 'a' is the old value, 'b' is then new value
+ * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b));
+GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
 
 /**
  * Return whether a string is a valid remote URL
  *
- * @param tranport the url to check
+ * @param url the url to check
  * @param 1 if the url is valid, 0 otherwise
  */
 GIT_EXTERN(int) git_remote_valid_url(const char *url);
@@ -213,21 +296,167 @@
  *
  * The string array must be freed by the user.
  *
- * @param remotes_list a string array with the names of the remotes
+ * @param out a string array which receives the names of the remotes
  * @param repo the repository to query
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_remote_list(git_strarray *remotes_list, git_repository *repo);
+GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo);
 
 /**
- * Add a remote with the default fetch refspec to the repository's configuration
+ * Choose whether to check the server's certificate (applies to HTTPS only)
  *
- * @param out the resulting remote
- * @param repo the repository in which to create the remote
- * @param name the remote's name
- * @param url the remote's url
+ * @param remote the remote to configure
+ * @param check whether to check the server's certificate (defaults to yes)
  */
-GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url);
+GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check);
+
+/**
+ * Set a credentials acquisition callback for this remote. If the remote is
+ * not available for anonymous access, then you must set this callback in order
+ * to provide credentials to the transport at the time of authentication
+ * failure so that retry can be performed.
+ *
+ * @param remote the remote to configure
+ * @param cred_acquire_cb The credentials acquisition callback to use (defaults
+ * to NULL)
+ */
+GIT_EXTERN(void) git_remote_set_cred_acquire_cb(
+	git_remote *remote,
+	git_cred_acquire_cb cred_acquire_cb,
+	void *payload);
+
+/**
+ * Sets a custom transport for the remote. The caller can use this function
+ * to bypass the automatic discovery of a transport by URL scheme (i.e.
+ * http://, https://, git://) and supply their own transport to be used
+ * instead. After providing the transport to a remote using this function,
+ * the transport object belongs exclusively to that remote, and the remote will
+ * free it when it is freed with git_remote_free.
+ *
+ * @param remote the remote to configure
+ * @param transport the transport object for the remote to use
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_set_transport(
+	git_remote *remote,
+	git_transport *transport);
+
+/**
+ * Argument to the completion callback which tells it which operation
+ * finished.
+ */
+typedef enum git_remote_completion_type {
+	GIT_REMOTE_COMPLETION_DOWNLOAD,
+	GIT_REMOTE_COMPLETION_INDEXING,
+	GIT_REMOTE_COMPLETION_ERROR,
+} git_remote_completion_type;
+
+/**
+ * The callback settings structure
+ *
+ * Set the calbacks to be called by the remote.
+ */
+struct git_remote_callbacks {
+	unsigned int version;
+	void (*progress)(const char *str, int len, void *data);
+	int (*completion)(git_remote_completion_type type, void *data);
+	int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+	void *payload;
+};
+
+#define GIT_REMOTE_CALLBACKS_VERSION 1
+#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION}
+
+/**
+ * Set the callbacks for a remote
+ *
+ * Note that the remote keeps its own copy of the data and you need to
+ * call this function again if you want to change the callbacks.
+ *
+ * @param remote the remote to configure
+ * @param callbacks a pointer to the user's callback settings
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks);
+
+/**
+ * Get the statistics structure that is filled in by the fetch operation.
+ */
+GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
+
+typedef enum {
+	GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
+	GIT_REMOTE_DOWNLOAD_TAGS_NONE,
+	GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
+	GIT_REMOTE_DOWNLOAD_TAGS_ALL
+} git_remote_autotag_option_t;
+
+/**
+ * Retrieve the tag auto-follow setting
+ *
+ * @param remote the remote to query
+ * @return the auto-follow setting
+ */
+GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(git_remote *remote);
+
+/**
+ * Set the tag auto-follow setting
+ *
+ * @param remote the remote to configure
+ * @param value a GIT_REMOTE_DOWNLOAD_TAGS value
+ */
+GIT_EXTERN(void) git_remote_set_autotag(
+	git_remote *remote,
+	git_remote_autotag_option_t value);
+
+/**
+ * Give the remote a new name
+ *
+ * All remote-tracking branches and configuration settings
+ * for the remote are updated.
+ *
+ * The new name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * A temporary in-memory remote cannot be given a name with this method.
+ *
+ * @param remote the remote to rename
+ * @param new_name the new name the remote should bear
+ * @param callback Optional callback to notify the consumer of fetch refspecs
+ * that haven't been automatically updated and need potential manual tweaking.
+ * @param payload Additional data to pass to the callback
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_rename(
+	git_remote *remote,
+	const char *new_name,
+	git_remote_rename_problem_cb callback,
+	void *payload);
+
+/**
+ * Retrieve the update FETCH_HEAD setting.
+ *
+ * @param remote the remote to query
+ * @return the update FETCH_HEAD setting
+ */
+GIT_EXTERN(int) git_remote_update_fetchhead(git_remote *remote);
+
+/**
+ * Sets the update FETCH_HEAD setting.  By default, FETCH_HEAD will be
+ * updated on every fetch.  Set to 0 to disable.
+ *
+ * @param remote the remote to configure
+ * @param value 0 to disable updating FETCH_HEAD
+ */
+GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
+
+/**
+ * Ensure the remote name is well-formed.
+ *
+ * @param remote_name name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/repository.h b/libgit2/include/git2/repository.h
--- a/libgit2/include/git2/repository.h
+++ b/libgit2/include/git2/repository.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -29,13 +29,26 @@
  * The method will automatically detect if 'path' is a normal
  * or bare repository or fail is 'path' is neither.
  *
- * @param repository pointer to the repo which will be opened
+ * @param out pointer to the repo which will be opened
  * @param path the path to the repository
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *path);
+GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path);
 
 /**
+ * Create a "fake" repository to wrap an object database
+ *
+ * Create a repository object to wrap an object database to be used
+ * with the API when all you have is an object database. This doesn't
+ * have any paths associated with it, so use with care.
+ *
+ * @param out pointer to the repo
+ * @param odb the object database to wrap
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb);
+
+/**
  * Look for a git repository and copy its path in the given buffer.
  * The lookup start from base_path and walk across parent directories
  * if nothing has been found. The lookup ends when the first repository
@@ -45,10 +58,10 @@
  * The method will automatically detect if the repository is bare
  * (if there is a repository).
  *
- * @param repository_path The user allocated buffer which will
+ * @param path_out The user allocated buffer which will
  * contain the found path.
  *
- * @param size repository_path size
+ * @param path_size repository_path size
  *
  * @param start_path The base path where the lookup starts.
  *
@@ -64,24 +77,50 @@
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_repository_discover(
-		char *repository_path,
-		size_t size,
+		char *path_out,
+		size_t path_size,
 		const char *start_path,
 		int across_fs,
 		const char *ceiling_dirs);
 
-enum {
+/**
+ * Option flags for `git_repository_open_ext`.
+ *
+ * * GIT_REPOSITORY_OPEN_NO_SEARCH - Only open the repository if it can be
+ *   immediately found in the start_path.  Do not walk up from the
+ *   start_path looking at parent directories.
+ * * GIT_REPOSITORY_OPEN_CROSS_FS - Unless this flag is set, open will not
+ *   continue searching across filesystem boundaries (i.e. when `st_dev`
+ *   changes from the `stat` system call).  (E.g. Searching in a user's home
+ *   directory "/home/user/source/" will not return "/.git/" as the found
+ *   repo if "/" is a different filesystem than "/home".)
+ */
+typedef enum {
 	GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
 	GIT_REPOSITORY_OPEN_CROSS_FS  = (1 << 1),
-};
+} git_repository_open_flag_t;
 
 /**
  * Find and open a repository with extended controls.
+ *
+ * @param out Pointer to the repo which will be opened.  This can
+ *        actually be NULL if you only want to use the error code to
+ *        see if a repo at this path could be opened.
+ * @param path Path to open as git repository.  If the flags
+ *        permit "searching", then this can be a path to a subdirectory
+ *        inside the working directory of the repository.
+ * @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
+ * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
+ *        prefixes at which the search for a containing repository should
+ *        terminate.
+ * @return 0 on success, GIT_ENOTFOUND if no repository could be found,
+ *        or -1 if there was a repository but open failed for some reason
+ *        (such as repo corruption or system errors).
  */
 GIT_EXTERN(int) git_repository_open_ext(
-	git_repository **repo,
-	const char *start_path,
-	uint32_t flags,
+	git_repository **out,
+	const char *path,
+	unsigned int flags,
 	const char *ceiling_dirs);
 
 /**
@@ -111,25 +150,148 @@
  * TODO:
  *	- Reinit the repository
  *
- * @param repo_out pointer to the repo which will be created or reinitialized
+ * @param out pointer to the repo which will be created or reinitialized
  * @param path the path to the repository
- * @param is_bare if true, a Git repository without a working directory is created
- *		at the pointed path. If false, provided path will be considered as the working
- *		directory into which the .git directory will be created.
+ * @param is_bare if true, a Git repository without a working directory is
+ *		created at the pointed path. If false, provided path will be
+ *		considered as the working directory into which the .git directory
+ *		will be created.
  *
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
+GIT_EXTERN(int) git_repository_init(
+	git_repository **out,
+	const char *path,
+	unsigned is_bare);
 
 /**
+ * Option flags for `git_repository_init_ext`.
+ *
+ * These flags configure extra behaviors to `git_repository_init_ext`.
+ * In every case, the default behavior is the zero value (i.e. flag is
+ * not set).  Just OR the flag values together for the `flags` parameter
+ * when initializing a new repo.  Details of individual values are:
+ *
+ * * BARE   - Create a bare repository with no working directory.
+ * * NO_REINIT - Return an EEXISTS error if the repo_path appears to
+ *        already be an git repository.
+ * * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
+ *        path for non-bare repos (if it is not already there), but
+ *        passing this flag prevents that behavior.
+ * * MKDIR  - Make the repo_path (and workdir_path) as needed.  Init is
+ *        always willing to create the ".git" directory even without this
+ *        flag.  This flag tells init to create the trailing component of
+ *        the repo and workdir paths as needed.
+ * * MKPATH - Recursively make all components of the repo and workdir
+ *        paths as necessary.
+ * * EXTERNAL_TEMPLATE - libgit2 normally uses internal templates to
+ *        initialize a new repo.  This flags enables external templates,
+ *        looking the "template_path" from the options if set, or the
+ *        `init.templatedir` global config if not, or falling back on
+ *        "/usr/share/git-core/templates" if it exists.
+ */
+typedef enum {
+	GIT_REPOSITORY_INIT_BARE              = (1u << 0),
+	GIT_REPOSITORY_INIT_NO_REINIT         = (1u << 1),
+	GIT_REPOSITORY_INIT_NO_DOTGIT_DIR     = (1u << 2),
+	GIT_REPOSITORY_INIT_MKDIR             = (1u << 3),
+	GIT_REPOSITORY_INIT_MKPATH            = (1u << 4),
+	GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+} git_repository_init_flag_t;
+
+/**
+ * Mode options for `git_repository_init_ext`.
+ *
+ * Set the mode field of the `git_repository_init_options` structure
+ * either to the custom mode that you would like, or to one of the
+ * following modes:
+ *
+ * * SHARED_UMASK - Use permissions configured by umask - the default.
+ * * SHARED_GROUP - Use "--shared=group" behavior, chmod'ing the new repo
+ *        to be group writable and "g+sx" for sticky group assignment.
+ * * SHARED_ALL - Use "--shared=all" behavior, adding world readability.
+ * * Anything else - Set to custom value.
+ */
+typedef enum {
+	GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
+	GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
+	GIT_REPOSITORY_INIT_SHARED_ALL   = 0002777,
+} git_repository_init_mode_t;
+
+/**
+ * Extended options structure for `git_repository_init_ext`.
+ *
+ * This contains extra options for `git_repository_init_ext` that enable
+ * additional initialization features.  The fields are:
+ *
+ * * flags - Combination of GIT_REPOSITORY_INIT flags above.
+ * * mode  - Set to one of the standard GIT_REPOSITORY_INIT_SHARED_...
+ *        constants above, or to a custom value that you would like.
+ * * workdir_path - The path to the working dir or NULL for default (i.e.
+ *        repo_path parent on non-bare repos).  IF THIS IS RELATIVE PATH,
+ *        IT WILL BE EVALUATED RELATIVE TO THE REPO_PATH.  If this is not
+ *        the "natural" working directory, a .git gitlink file will be
+ *        created here linking to the repo_path.
+ * * description - If set, this will be used to initialize the "description"
+ *        file in the repository, instead of using the template content.
+ * * template_path - When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set,
+ *        this contains the path to use for the template directory.  If
+ *        this is NULL, the config or default directory options will be
+ *        used instead.
+ * * initial_head - The name of the head to point HEAD at.  If NULL, then
+ *        this will be treated as "master" and the HEAD ref will be set
+ *        to "refs/heads/master".  If this begins with "refs/" it will be
+ *        used verbatim; otherwise "refs/heads/" will be prefixed.
+ * * origin_url - If this is non-NULL, then after the rest of the
+ *        repository initialization is completed, an "origin" remote
+ *        will be added pointing to this URL.
+ */
+typedef struct {
+	unsigned int version;
+	uint32_t    flags;
+	uint32_t    mode;
+	const char *workdir_path;
+	const char *description;
+	const char *template_path;
+	const char *initial_head;
+	const char *origin_url;
+} git_repository_init_options;
+
+#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1
+#define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION}
+
+/**
+ * Create a new Git repository in the given folder with extended controls.
+ *
+ * This will initialize a new git repository (creating the repo_path
+ * if requested by flags) and working directory as needed.  It will
+ * auto-detect the case sensitivity of the file system and if the
+ * file system supports file mode bits correctly.
+ *
+ * @param out Pointer to the repo which will be created or reinitialized.
+ * @param repo_path The path to the repository.
+ * @param opts Pointer to git_repository_init_options struct.
+ * @return 0 or an error code on failure.
+ */
+GIT_EXTERN(int) git_repository_init_ext(
+	git_repository **out,
+	const char *repo_path,
+	git_repository_init_options *opts);
+
+/**
  * Retrieve and resolve the reference pointed at by HEAD.
  *
- * @param head_out pointer to the reference which will be retrieved
+ * The returned `git_reference` will be owned by caller and
+ * `git_reference_free()` must be called when done with it to release the
+ * allocated memory and prevent a leak.
+ *
+ * @param out pointer to the reference which will be retrieved
  * @param repo a repository object
  *
- * @return 0 on success; error code otherwise
+ * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
  */
-GIT_EXTERN(int) git_repository_head(git_reference **head_out, git_repository *repo);
+GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
 
 /**
  * Check if a repository's HEAD is detached
@@ -138,7 +300,7 @@
  * instead of a branch.
  *
  * @param repo Repo to test
- * @return 1 if HEAD is detached, 0 if i'ts not; error code if there
+ * @return 1 if HEAD is detached, 0 if it's not; error code if there
  * was an error.
  */
 GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
@@ -151,7 +313,7 @@
  *
  * @param repo Repo to test
  * @return 1 if the current branch is an orphan, 0 if it's not; error
- * code if therewas an error
+ * code if there was an error
  */
 GIT_EXTERN(int) git_repository_head_orphan(git_repository *repo);
 
@@ -159,7 +321,7 @@
  * Check if a repository is empty
  *
  * An empty repository has just been initialized and contains
- * no commits.
+ * no references.
  *
  * @param repo Repo to test
  * @return 1 if the repository is empty, 0 if it isn't, error code
@@ -202,9 +364,12 @@
  *
  * @param repo A repository object
  * @param workdir The path to a working directory
+ * @param update_gitlink Create/update gitlink in workdir and set config
+ *        "core.worktree" (if workdir is not the parent of the .git directory)
  * @return 0, or an error code
  */
-GIT_EXTERN(int) git_repository_set_workdir(git_repository *repo, const char *workdir);
+GIT_EXTERN(int) git_repository_set_workdir(
+	git_repository *repo, const char *workdir, int update_gitlink);
 
 /**
  * Check if a repository is bare
@@ -277,6 +442,39 @@
 GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
 
 /**
+ * Get the Reference Database Backend for this repository.
+ *
+ * If a custom refsdb has not been set, the default database for
+ * the repository will be returned (the one that manipulates loose
+ * and packed references in the `.git` directory).
+ *
+ * The refdb must be freed once it's no longer being used by
+ * the user.
+ *
+ * @param out Pointer to store the loaded refdb
+ * @param repo A repository object
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
+
+/**
+ * Set the Reference Database Backend for this repository
+ *
+ * The refdb will be used for all reference related operations
+ * involving this repository.
+ *
+ * The repository will keep a reference to the refdb; the user
+ * must still free the refdb object after setting it to the
+ * repository, or it will leak.
+ *
+ * @param repo A repository object
+ * @param refdb An refdb object
+ */
+GIT_EXTERN(void) git_repository_set_refdb(
+	git_repository *repo,
+	git_refdb *refdb);
+
+/**
  * Get the Index file for this repository.
  *
  * If a custom index has not been set, the default
@@ -306,6 +504,184 @@
  * @param index An index object
  */
 GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
+
+/**
+ * Retrieve git's prepared message
+ *
+ * Operations such as git revert/cherry-pick/merge with the -n option
+ * stop just short of creating a commit with the changes and save
+ * their prepared message in .git/MERGE_MSG so the next git-commit
+ * execution can present it to the user for them to amend if they
+ * wish.
+ *
+ * Use this function to get the contents of this file. Don't forget to
+ * remove the file after you create the commit.
+ *
+ * @param out Buffer to write data into or NULL to just read required size
+ * @param len Length of buffer in bytes
+ * @param repo Repository to read prepared message from
+ * @return Bytes written to buffer, GIT_ENOTFOUND if no message, or -1 on error
+ */
+GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
+
+/**
+ * Remove git's prepared message.
+ *
+ * Remove the message that `git_repository_message` retrieves.
+ */
+GIT_EXTERN(int) git_repository_message_remove(git_repository *repo);
+
+/**
+ * Remove all the metadata associated with an ongoing git merge, including
+ * MERGE_HEAD, MERGE_MSG, etc.
+ *
+ * @param repo A repository object
+ * @return 0 on success, or error
+ */
+GIT_EXTERN(int) git_repository_merge_cleanup(git_repository *repo);
+
+typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name,
+	const char *remote_url,
+	const git_oid *oid,
+	unsigned int is_merge,
+	void *payload);
+
+/**
+ * Call callback 'callback' for each entry in the given FETCH_HEAD file.
+ *
+ * @param repo A repository object
+ * @param callback Callback function
+ * @param payload Pointer to callback data (optional)
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
+ */
+GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo,
+	git_repository_fetchhead_foreach_cb callback,
+	void *payload);
+
+typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
+	void *payload);
+
+/**
+ * If a merge is in progress, call callback 'cb' for each commit ID in the
+ * MERGE_HEAD file.
+ *
+ * @param repo A repository object
+ * @param callback Callback function
+ * @param apyload Pointer to callback data (optional)
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
+ */
+GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
+	git_repository_mergehead_foreach_cb callback,
+	void *payload);
+
+/**
+ * Calculate hash of file using repository filtering rules.
+ *
+ * If you simply want to calculate the hash of a file on disk with no filters,
+ * you can just use the `git_odb_hashfile()` API.  However, if you want to
+ * hash a file in the repository and you want to apply filtering rules (e.g.
+ * crlf filters) before generating the SHA, then use this function.
+ *
+ * @param out Output value of calculated SHA
+ * @param repo Repository pointer
+ * @param path Path to file on disk whose contents should be hashed. If the
+ *             repository is not NULL, this can be a relative path.
+ * @param type The object type to hash as (e.g. GIT_OBJ_BLOB)
+ * @param as_path The path to use to look up filtering rules. If this is
+ *             NULL, then the `path` parameter will be used instead. If
+ *             this is passed as the empty string, then no filters will be
+ *             applied when calculating the hash.
+ */
+GIT_EXTERN(int) git_repository_hashfile(
+    git_oid *out,
+    git_repository *repo,
+    const char *path,
+    git_otype type,
+    const char *as_path);
+
+/**
+ * Make the repository HEAD point to the specified reference.
+ *
+ * If the provided reference points to a Tree or a Blob, the HEAD is
+ * unaltered and -1 is returned.
+ *
+ * If the provided reference points to a branch, the HEAD will point
+ * to that branch, staying attached, or become attached if it isn't yet.
+ * If the branch doesn't exist yet, no error will be return. The HEAD
+ * will then be attached to an unborn branch.
+ *
+ * Otherwise, the HEAD will be detached and will directly point to
+ * the Commit.
+ *
+ * @param repo Repository pointer
+ * @param refname Canonical name of the reference the HEAD should point at
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_head(
+	git_repository* repo,
+	const char* refname);
+
+/**
+ * Make the repository HEAD directly point to the Commit.
+ *
+ * If the provided committish cannot be found in the repository, the HEAD
+ * is unaltered and GIT_ENOTFOUND is returned.
+ *
+ * If the provided commitish cannot be peeled into a commit, the HEAD
+ * is unaltered and -1 is returned.
+ *
+ * Otherwise, the HEAD will eventually be detached and will directly point to
+ * the peeled Commit.
+ *
+ * @param repo Repository pointer
+ * @param commitish Object id of the Commit the HEAD should point to
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_head_detached(
+	git_repository* repo,
+	const git_oid* commitish);
+
+/**
+ * Detach the HEAD.
+ *
+ * If the HEAD is already detached and points to a Commit, 0 is returned.
+ *
+ * If the HEAD is already detached and points to a Tag, the HEAD is
+ * updated into making it point to the peeled Commit, and 0 is returned.
+ *
+ * If the HEAD is already detached and points to a non commitish, the HEAD is 
+ * unaltered, and -1 is returned.
+ *
+ * Otherwise, the HEAD will be detached and point to the peeled Commit.
+ *
+ * @param repo Repository pointer
+ * @return 0 on success, GIT_EORPHANEDHEAD when HEAD points to a non existing
+ * branch or an error code
+ */
+GIT_EXTERN(int) git_repository_detach_head(
+	git_repository* repo);
+
+typedef enum {
+	GIT_REPOSITORY_STATE_NONE,
+	GIT_REPOSITORY_STATE_MERGE,
+	GIT_REPOSITORY_STATE_REVERT,
+	GIT_REPOSITORY_STATE_CHERRY_PICK,
+	GIT_REPOSITORY_STATE_BISECT,
+	GIT_REPOSITORY_STATE_REBASE,
+	GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
+	GIT_REPOSITORY_STATE_REBASE_MERGE,
+	GIT_REPOSITORY_STATE_APPLY_MAILBOX,
+	GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE,
+} git_repository_state_t;
+
+/**
+ * Determines the status of a git repository - ie, whether an operation
+ * (merge, cherry-pick, etc) is in progress.
+ *
+ * @param repo Repository pointer
+ * @return The state of the repository
+ */
+GIT_EXTERN(int) git_repository_state(git_repository *repo);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/reset.h b/libgit2/include/git2/reset.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/reset.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_reset_h__
+#define INCLUDE_git_reset_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+
+/**
+ * @file git2/reset.h
+ * @brief Git reset management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Kinds of reset operation
+ */
+typedef enum {
+	GIT_RESET_SOFT  = 1, /** Move the head to the given commit */
+	GIT_RESET_MIXED = 2, /** SOFT plus reset index to the commit */
+	GIT_RESET_HARD  = 3, /** MIXED plus changes in working tree discarded */
+} git_reset_t;
+
+/**
+ * Sets the current head to the specified commit oid and optionally
+ * resets the index and working tree to match.
+ *
+ * SOFT reset means the Head will be moved to the commit.
+ *
+ * MIXED reset will trigger a SOFT reset, plus the index will be replaced
+ * with the content of the commit tree.
+ *
+ * HARD reset will trigger a MIXED reset and the working directory will be
+ * replaced with the content of the index.  (Untracked and ignored files
+ * will be left alone, however.)
+ *
+ * TODO: Implement remaining kinds of resets.
+ *
+ * @param repo Repository where to perform the reset operation.
+ *
+ * @param target Committish to which the Head should be moved to. This object
+ * must belong to the given `repo` and can either be a git_commit or a
+ * git_tag. When a git_tag is being passed, it should be dereferencable
+ * to a git_commit which oid will be used as the target of the branch.
+ *
+ * @param reset_type Kind of reset operation to perform.
+ *
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_reset(
+	git_repository *repo, git_object *target, git_reset_t reset_type);
+
+/**
+ * Updates some entries in the index from the target commit tree.
+ *
+ * The scope of the updated entries is determined by the paths
+ * being passed in the `pathspec` parameters.
+ *
+ * Passing a NULL `target` will result in removing
+ * entries in the index matching the provided pathspecs.
+ *
+ * @param repo Repository where to perform the reset operation.
+ *
+ * @param target The committish which content will be used to reset the content
+ * of the index.
+ *
+ * @param pathspecs List of pathspecs to operate on.
+ *
+ * @return 0 on success or an error code < 0
+ */
+GIT_EXTERN(int) git_reset_default(
+    git_repository *repo,
+    git_object *target,
+    git_strarray* pathspecs);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/revparse.h b/libgit2/include/git2/revparse.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/revparse.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_revparse_h__
+#define INCLUDE_git_revparse_h__
+
+#include "common.h"
+#include "types.h"
+
+
+/**
+ * @file git2/revparse.h
+ * @brief Git revision parsing routines
+ * @defgroup git_revparse Git revision parsing routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Find a single object, as specified by a revision string. See `man gitrevisions`,
+ * or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * @param out pointer to output object
+ * @param repo the repository to search in
+ * @param spec the textual specification for an object
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
+
+
+/**
+ * Revparse flags.  These indicate the intended behavior of the spec passed to
+ * git_revparse.
+ */
+typedef enum {
+	/** The spec targeted a single object. */
+	GIT_REVPARSE_SINGLE         = 1 << 0,
+	/** The spec targeted a range of commits. */
+	GIT_REVPARSE_RANGE          = 1 << 1,
+	/** The spec used the '...' operator, which invokes special semantics. */
+	GIT_REVPARSE_MERGE_BASE     = 1 << 2,
+} git_revparse_mode_t;
+
+/**
+ * Git Revision Spec: output of a `git_revparse` operation
+ */
+typedef struct {
+	/** The left element of the revspec; must be freed by the user */
+	git_object *from;
+	/** The right element of the revspec; must be freed by the user */
+	git_object *to;
+	/** The intent of the revspec */
+	unsigned int flags;
+} git_revspec;
+
+/**
+ * Parse a revision string for `from`, `to`, and intent. See `man gitrevisions` or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for information
+ * on the syntax accepted.
+ *
+ * @param revspec Pointer to an user-allocated git_revspec struct where the result
+ *	of the rev-parse will be stored
+ * @param repo the repository to search in
+ * @param spec the rev-parse spec to parse
+ * @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
+ */
+GIT_EXTERN(int) git_revparse(
+		git_revspec *revspec,
+		git_repository *repo,
+		const char *spec);
+
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/revwalk.h b/libgit2/include/git2/revwalk.h
--- a/libgit2/include/git2/revwalk.h
+++ b/libgit2/include/git2/revwalk.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -63,11 +63,11 @@
  * it is possible to have several revision walkers in
  * several different threads walking the same repository.
  *
- * @param walker pointer to the new revision walker
+ * @param out pointer to the new revision walker
  * @param repo the repo to walk through
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_revwalk_new(git_revwalk **walker, git_repository *repo);
+GIT_EXTERN(int) git_revwalk_new(git_revwalk **out, git_repository *repo);
 
 /**
  * Reset the revision walker for reuse.
@@ -92,22 +92,22 @@
  *
  * The given commit will be used as one of the roots
  * when starting the revision walk. At least one commit
- * must be pushed the repository before a walk can
+ * must be pushed onto the walker before a walk can
  * be started.
  *
  * @param walk the walker being used for the traversal.
- * @param oid the oid of the commit to start from.
+ * @param id the oid of the commit to start from.
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid);
+GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *id);
 
 /**
  * Push matching references
  *
- * The OIDs pinted to by the references that match the given glob
+ * The OIDs pointed to by the references that match the given glob
  * pattern will be pushed to the revision walker.
  *
- * A leading 'refs/' is implied it not present as well as a trailing
+ * A leading 'refs/' is implied if not present as well as a trailing
  * '/ *' if the glob lacks '?', '*' or '['.
  *
  * @param walk the walker being used for the traversal
@@ -134,19 +134,19 @@
  * output on the revision walk.
  *
  * @param walk the walker being used for the traversal.
- * @param oid the oid of commit that will be ignored during the traversal
+ * @param commit_id the oid of commit that will be ignored during the traversal
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid);
+GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *commit_id);
 
 /**
  * Hide matching references.
  *
- * The OIDs pinted to by the references that match the given glob
+ * The OIDs pointed to by the references that match the given glob
  * pattern and their ancestors will be hidden from the output on the
  * revision walk.
  *
- * A leading 'refs/' is implied it not present as well as a trailing
+ * A leading 'refs/' is implied if not present as well as a trailing
  * '/ *' if the glob lacks '?', '*' or '['.
  *
  * @param walk the walker being used for the traversal
@@ -169,7 +169,7 @@
  * The reference must point to a commit.
  *
  * @param walk the walker being used for the traversal
- * @param refname the referece to push
+ * @param refname the reference to push
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
@@ -180,7 +180,7 @@
  * The reference must point to a commit.
  *
  * @param walk the walker being used for the traversal
- * @param refname the referece to hide
+ * @param refname the reference to hide
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
@@ -198,12 +198,12 @@
  *
  * The revision walker is reset when the walk is over.
  *
- * @param oid Pointer where to store the oid of the next commit
+ * @param out Pointer where to store the oid of the next commit
  * @param walk the walker to pop the commit from.
  * @return 0 if the next commit was found;
- *	GIT_REVWALKOVER if there are no commits left to iterate
+ *	GIT_ITEROVER if there are no commits left to iterate
  */
-GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk);
+GIT_EXTERN(int) git_revwalk_next(git_oid *out, git_revwalk *walk);
 
 /**
  * Change the sorting mode when iterating through the
@@ -215,6 +215,21 @@
  * @param sort_mode combination of GIT_SORT_XXX flags
  */
 GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
+
+/**
+ * Push and hide the respective endpoints of the given range.
+ *
+ * The range should be of the form
+ *   <commit>..<commit>
+ * where each <commit> is in the form accepted by 'git_revparse_single'.
+ * The left-hand commit will be hidden and the right-hand commit pushed.
+ *
+ * @param walk the walker being used for the traversal
+ * @param range the range
+ * @return 0 or an error code
+ *
+ */
+GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
 
 /**
  * Free a revision walker previously allocated.
diff --git a/libgit2/include/git2/signature.h b/libgit2/include/git2/signature.h
--- a/libgit2/include/git2/signature.h
+++ b/libgit2/include/git2/signature.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -20,41 +20,52 @@
 GIT_BEGIN_DECL
 
 /**
- * Create a new action signature. The signature must be freed
- * manually or using git_signature_free
+ * Create a new action signature.
  *
- * @param sig_out new signature, in case of error NULL
+ * Call `git_signature_free()` to free the data.
+ *
+ * Note: angle brackets ('<' and '>') characters are not allowed
+ * to be used in either the `name` or the `email` parameter.
+ *
+ * @param out new signature, in case of error NULL
  * @param name name of the person
  * @param email email of the person
  * @param time time when the action happened
  * @param offset timezone offset in minutes for the time
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset);
+GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const char *email, git_time_t time, int offset);
 
 /**
- * Create a new action signature with a timestamp of 'now'. The
- * signature must be freed manually or using git_signature_free
+ * Create a new action signature with a timestamp of 'now'.
  *
- * @param sig_out new signature, in case of error NULL
+ * Call `git_signature_free()` to free the data.
+ *
+ * @param out new signature, in case of error NULL
  * @param name name of the person
  * @param email email of the person
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_signature_now(git_signature **sig_out, const char *name, const char *email);
+GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
 
 
 /**
- * Create a copy of an existing signature.
+ * Create a copy of an existing signature.  All internal strings are also
+ * duplicated.
  *
- * All internal strings are also duplicated.
+ * Call `git_signature_free()` to free the data.
+ *
  * @param sig signature to duplicated
  * @return a copy of sig, NULL on out of memory
  */
 GIT_EXTERN(git_signature *) git_signature_dup(const git_signature *sig);
 
 /**
- * Free an existing signature
+ * Free an existing signature.
+ *
+ * Because the signature is not an opaque structure, it is legal to free it
+ * manually, but be sure to free the "name" and "email" strings in addition
+ * to the structure itself.
  *
  * @param sig signature to free
  */
diff --git a/libgit2/include/git2/stash.h b/libgit2/include/git2/stash.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/stash.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_stash_h__
+#define INCLUDE_git_stash_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/stash.h
+ * @brief Git stash management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+typedef enum {
+	GIT_STASH_DEFAULT = 0,
+
+	/* All changes already added to the index
+	 * are left intact in the working directory
+	 */
+	GIT_STASH_KEEP_INDEX = (1 << 0),
+
+	/* All untracked files are also stashed and then
+	 * cleaned up from the working directory
+	 */
+	GIT_STASH_INCLUDE_UNTRACKED = (1 << 1),
+
+	/* All ignored files are also stashed and then
+	 * cleaned up from the working directory
+	 */
+	GIT_STASH_INCLUDE_IGNORED = (1 << 2),
+} git_stash_flags;
+
+/**
+ * Save the local modifications to a new stash.
+ *
+ * @param out Object id of the commit containing the stashed state.
+ * This commit is also the target of the direct reference refs/stash.
+ *
+ * @param repo The owning repository.
+ *
+ * @param stasher The identity of the person performing the stashing.
+ *
+ * @param message Optional description along with the stashed state.
+ *
+ * @param flags Flags to control the stashing process. (see GIT_STASH_* above)
+ *
+ * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
+ * or error code.
+ */
+GIT_EXTERN(int) git_stash_save(
+	git_oid *out,
+	git_repository *repo,
+	git_signature *stasher,
+	const char *message,
+	unsigned int flags);
+
+/**
+ * When iterating over all the stashed states, callback that will be
+ * issued per entry.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @param message The stash message.
+ *
+ * @param stash_id The commit oid of the stashed state.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+typedef int (*git_stash_cb)(
+	size_t index,
+	const char* message,
+	const git_oid *stash_id,
+	void *payload);
+
+/**
+ * Loop over all the stashed states and issue a callback for each one.
+ *
+ * If the callback returns a non-zero value, this will stop looping.
+ *
+ * @param repo Repository where to find the stash.
+ *
+ * @param callabck Callback to invoke per found stashed state. The most recent
+ * stash state will be enumerated first.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_stash_foreach(
+	git_repository *repo,
+	git_stash_cb callback,
+	void *payload);
+
+/**
+ * Remove a single stashed state from the stash list.
+ *
+ * @param repo The owning repository.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @return 0 on success, or error code
+ */
+
+GIT_EXTERN(int) git_stash_drop(
+	git_repository *repo,
+	size_t index);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/status.h b/libgit2/include/git2/status.h
--- a/libgit2/include/git2/status.h
+++ b/libgit2/include/git2/status.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,6 +9,7 @@
 
 #include "common.h"
 #include "types.h"
+#include "strarray.h"
 
 /**
  * @file git2/status.h
@@ -19,38 +20,67 @@
  */
 GIT_BEGIN_DECL
 
-enum {
-	GIT_STATUS_CURRENT	= 0,
+/**
+ * Status flags for a single file.
+ *
+ * A combination of these values will be returned to indicate the status of
+ * a file.  Status compares the working directory, the index, and the
+ * current HEAD of the repository.  The `GIT_STATUS_INDEX` set of flags
+ * represents the status of file in the index relative to the HEAD, and the
+ * `GIT_STATUS_WT` set of flags represent the status of the file in the
+ * working directory relative to the index.
+ */
+typedef enum {
+	GIT_STATUS_CURRENT = 0,
 
-	GIT_STATUS_INDEX_NEW = (1 << 0),
-	GIT_STATUS_INDEX_MODIFIED = (1 << 1),
-	GIT_STATUS_INDEX_DELETED = (1 << 2),
+	GIT_STATUS_INDEX_NEW        = (1u << 0),
+	GIT_STATUS_INDEX_MODIFIED   = (1u << 1),
+	GIT_STATUS_INDEX_DELETED    = (1u << 2),
+	GIT_STATUS_INDEX_RENAMED    = (1u << 3),
+	GIT_STATUS_INDEX_TYPECHANGE = (1u << 4),
 
-	GIT_STATUS_WT_NEW = (1 << 3),
-	GIT_STATUS_WT_MODIFIED = (1 << 4),
-	GIT_STATUS_WT_DELETED = (1 << 5),
+	GIT_STATUS_WT_NEW           = (1u << 7),
+	GIT_STATUS_WT_MODIFIED      = (1u << 8),
+	GIT_STATUS_WT_DELETED       = (1u << 9),
+	GIT_STATUS_WT_TYPECHANGE    = (1u << 10),
 
-	GIT_STATUS_IGNORED = (1 << 6),
-};
+	GIT_STATUS_IGNORED          = (1u << 14),
+} git_status_t;
 
 /**
+ * Function pointer to receive status on individual files
+ *
+ * `path` is the relative path to the file from the root of the repository.
+ *
+ * `status_flags` is a combination of `git_status_t` values that apply.
+ *
+ * `payload` is the value you passed to the foreach function as payload.
+ */
+typedef int (*git_status_cb)(
+	const char *path, unsigned int status_flags, void *payload);
+
+/**
  * Gather file statuses and run a callback for each one.
  *
- * The callback is passed the path of the file, the status and the data
- * pointer passed to this function. If the callback returns something other
- * than 0, this function will return that value.
+ * The callback is passed the path of the file, the status (a combination of
+ * the `git_status_t` values above) and the `payload` data pointer passed
+ * into this function.
  *
- * @param repo a repository object
- * @param callback the function to call on each file
- * @return 0 on success or the return value of the callback that was non-zero
+ * If the callback returns a non-zero value, this function will stop looping
+ * and return GIT_EUSER.
+ *
+ * @param repo A repository object
+ * @param callback The function to call on each file
+ * @param payload Pointer to pass through to callback function
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_status_foreach(
 	git_repository *repo,
-	int (*callback)(const char *, unsigned int, void *),
+	git_status_cb callback,
 	void *payload);
 
 /**
- * Select the files on which to report status.
+ * For extended status, select the files on which to report status.
  *
  * - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default.  This is the
  *   rough equivalent of `git status --porcelain` where each file
@@ -78,62 +108,108 @@
 /**
  * Flags to control status callbacks
  *
- * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should
- *   be made on untracked files.  These will only be made if the
- *   workdir files are included in the status "show" option.
- * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should
- *   get callbacks.  Again, these callbacks will only be made if
- *   the workdir files are included in the status "show" option.
- *   Right now, there is no option to include all files in
- *   directories that are ignored completely.
- * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
- *   should be made even on unmodified files.
- * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
- *   which appear to be submodules should just be skipped over.
- * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the
- *   contents of untracked directories should be included in the
- *   status.  Normally if an entire directory is new, then just
- *   the top-level directory will be included (with a trailing
- *   slash on the entry name).  Given this flag, the directory
- *   itself will not be included, but all the files in it will.
+ * - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
+ *   on untracked files.  These will only be made if the workdir files are
+ *   included in the status "show" option.
+ * - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get
+ *   callbacks.  Again, these callbacks will only be made if the workdir
+ *   files are included in the status "show" option.  Right now, there is
+ *   no option to include all files in directories that are ignored
+ *   completely.
+ * - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
+ *   made even on unmodified files.
+ * - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which
+ *   appear to be submodules should just be skipped over.
+ * - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of
+ *   untracked directories should be included in the status.  Normally if
+ *   an entire directory is new, then just the top-level directory will be
+ *   included (with a trailing slash on the entry name).  Given this flag,
+ *   the directory itself will not be included, but all the files in it
+ *   will.
+ * - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
+ *   will be treated as a literal path, and not as a pathspec.
+ * - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
+ *   ignored directories should be included in the status.  This is like
+ *   doing `git ls-files -o -i --exclude-standard` with core git.
+ *
+ * Calling `git_status_foreach()` is like calling the extended version
+ * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
+ * and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS.  Those options are bundled
+ * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
  */
+typedef enum {
+	GIT_STATUS_OPT_INCLUDE_UNTRACKED      = (1u << 0),
+	GIT_STATUS_OPT_INCLUDE_IGNORED        = (1u << 1),
+	GIT_STATUS_OPT_INCLUDE_UNMODIFIED     = (1u << 2),
+	GIT_STATUS_OPT_EXCLUDE_SUBMODULES     = (1u << 3),
+	GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
+	GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
+	GIT_STATUS_OPT_RECURSE_IGNORED_DIRS   = (1u << 6),
+} git_status_opt_t;
 
-enum {
-	GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0),
-	GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1),
-	GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
-	GIT_STATUS_OPT_EXCLUDE_SUBMODULED = (1 << 3),
-	GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
-};
+#define GIT_STATUS_OPT_DEFAULTS \
+	(GIT_STATUS_OPT_INCLUDE_IGNORED | \
+	GIT_STATUS_OPT_INCLUDE_UNTRACKED | \
+	GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS)
 
 /**
- * Options to control how callbacks will be made by
- * `git_status_foreach_ext()`.
+ * Options to control how `git_status_foreach_ext()` will issue callbacks.
+ *
+ * This structure is set so that zeroing it out will give you relatively
+ * sane defaults.
+ *
+ * The `show` value is one of the `git_status_show_t` constants that
+ * control which files to scan and in what order.
+ *
+ * The `flags` value is an OR'ed combination of the `git_status_opt_t`
+ * values above.
+ *
+ * The `pathspec` is an array of path patterns to match (using
+ * fnmatch-style matching), or just an array of paths to match exactly if
+ * `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified in the flags.
  */
 typedef struct {
+	unsigned int      version;
 	git_status_show_t show;
-	unsigned int flags;
-	git_strarray pathspec;
+	unsigned int      flags;
+	git_strarray      pathspec;
 } git_status_options;
 
+#define GIT_STATUS_OPTIONS_VERSION 1
+#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
+
 /**
  * Gather file status information and run callbacks as requested.
+ *
+ * This is an extended version of the `git_status_foreach()` API that
+ * allows for more granular control over which paths will be processed and
+ * in what order.  See the `git_status_options` structure for details
+ * about the additional controls that this makes available.
+ *
+ * @param repo Repository object
+ * @param opts Status options structure
+ * @param callback The function to call on each file
+ * @param payload Pointer to pass through to callback function
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 GIT_EXTERN(int) git_status_foreach_ext(
 	git_repository *repo,
 	const git_status_options *opts,
-	int (*callback)(const char *, unsigned int, void *),
+	git_status_cb callback,
 	void *payload);
 
 /**
- * Get file status for a single file
+ * Get file status for a single file.
  *
- * @param status_flags the status value
- * @param repo a repository object
- * @param path the file to retrieve status for, rooted at the repo's workdir
- * @return GIT_EINVALIDPATH when `path` points at a folder, GIT_ENOTFOUND when
- *		the file doesn't exist in any of HEAD, the index or the worktree,
- *		0 otherwise
+ * This is not quite the same as calling `git_status_foreach_ext()` with
+ * the pathspec set to the specified path.
+ *
+ * @param status_flags The status value for the file
+ * @param repo A repository object
+ * @param path The file to retrieve status for, rooted at the repo's workdir
+ * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
+ *      index, and work tree, GIT_EINVALIDPATH if `path` points at a folder,
+ *      GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error.
  */
 GIT_EXTERN(int) git_status_file(
 	unsigned int *status_flags,
@@ -143,14 +219,16 @@
 /**
  * Test if the ignore rules apply to a given file.
  *
- * This function simply checks the ignore rules to see if they would apply
- * to the given file.  Unlike git_status_file(), this indicates if the file
- * would be ignored regardless of whether the file is already in the index
- * or in the repository.
+ * This function checks the ignore rules to see if they would apply to the
+ * given file.  This indicates if the file would be ignored regardless of
+ * whether the file is already in the index or committed to the repository.
  *
- * @param ignored boolean returning 0 if the file is not ignored, 1 if it is
- * @param repo a repository object
- * @param path the file to check ignores for, rooted at the repo's workdir.
+ * One way to think of this is if you were to do "git add ." on the
+ * directory containing the file, would it be added or not?
+ *
+ * @param ignored Boolean returning 0 if the file is not ignored, 1 if it is
+ * @param repo A repository object
+ * @param path The file to check ignores for, rooted at the repo's workdir.
  * @return 0 if ignore rules could be processed for the file (regardless
  *         of whether it exists or not), or an error < 0 if they could not.
  */
diff --git a/libgit2/include/git2/strarray.h b/libgit2/include/git2/strarray.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/strarray.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_strarray_h__
+#define INCLUDE_git_strarray_h__
+
+#include "common.h"
+
+/**
+ * @file git2/strarray.h
+ * @brief Git string array routines
+ * @defgroup git_strarray Git string array routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Array of strings */
+typedef struct git_strarray {
+    char **strings;
+    size_t count;
+} git_strarray;
+
+/**
+ * Close a string array object
+ *
+ * This method should be called on `git_strarray` objects where the strings
+ * array is allocated and contains allocated strings, such as what you
+ * would get from `git_strarray_copy()`.  Not doing so, will result in a
+ * memory leak.
+ *
+ * This does not free the `git_strarray` itself, since the library will
+ * never allocate that object directly itself (it is more commonly embedded
+ * inside another struct or created on the stack).
+ *
+ * @param array git_strarray from which to free string data
+ */
+GIT_EXTERN(void) git_strarray_free(git_strarray *array);
+
+/**
+ * Copy a string array object from source to target.
+ *
+ * Note: target is overwritten and hence should be empty, otherwise its
+ * contents are leaked.  Call git_strarray_free() if necessary.
+ *
+ * @param tgt target
+ * @param src source
+ * @return 0 on success, < 0 on allocation failure
+ */
+GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/libgit2/include/git2/submodule.h b/libgit2/include/git2/submodule.h
--- a/libgit2/include/git2/submodule.h
+++ b/libgit2/include/git2/submodule.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -20,55 +20,178 @@
  */
 GIT_BEGIN_DECL
 
+/**
+ * Opaque structure representing a submodule.
+ *
+ * Submodule support in libgit2 builds a list of known submodules and keeps
+ * it in the repository.  The list is built from the .gitmodules file, the
+ * .git/config file, the index, and the HEAD tree.  Items in the working
+ * directory that look like submodules (i.e. a git repo) but are not
+ * mentioned in those places won't be tracked.
+ */
+typedef struct git_submodule git_submodule;
+
+/**
+ * Values that could be specified for the update rule of a submodule.
+ *
+ * Use the DEFAULT value if you have altered the update value via
+ * `git_submodule_set_update()` and wish to reset to the original default.
+ */
 typedef enum {
+	GIT_SUBMODULE_UPDATE_DEFAULT = -1,
 	GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
 	GIT_SUBMODULE_UPDATE_REBASE = 1,
-	GIT_SUBMODULE_UPDATE_MERGE = 2
+	GIT_SUBMODULE_UPDATE_MERGE = 2,
+	GIT_SUBMODULE_UPDATE_NONE = 3
 } git_submodule_update_t;
 
+/**
+ * Values that could be specified for how closely to examine the
+ * working directory when getting submodule status.
+ *
+ * Use the DEFUALT value if you have altered the ignore value via
+ * `git_submodule_set_ignore()` and wish to reset to the original value.
+ */
 typedef enum {
-	GIT_SUBMODULE_IGNORE_ALL = 0,       /* never dirty */
-	GIT_SUBMODULE_IGNORE_DIRTY = 1,     /* only dirty if HEAD moved */
-	GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
-	GIT_SUBMODULE_IGNORE_NONE = 3       /* any change or untracked == dirty */
+	GIT_SUBMODULE_IGNORE_DEFAULT = -1,  /* reset to default */
+	GIT_SUBMODULE_IGNORE_NONE = 0,      /* any change or untracked == dirty */
+	GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
+	GIT_SUBMODULE_IGNORE_DIRTY = 2,     /* only dirty if HEAD moved */
+	GIT_SUBMODULE_IGNORE_ALL = 3        /* never dirty */
 } git_submodule_ignore_t;
 
 /**
- * Description of submodule
+ * Return codes for submodule status.
  *
- * This record describes a submodule found in a repository.  There
- * should be an entry for every submodule found in the HEAD and for
- * every submodule described in .gitmodules.  The fields are as follows:
+ * A combination of these flags will be returned to describe the status of a
+ * submodule.  Depending on the "ignore" property of the submodule, some of
+ * the flags may never be returned because they indicate changes that are
+ * supposed to be ignored.
  *
- * - `name` is the name of the submodule from .gitmodules.
- * - `path` is the path to the submodule from the repo working directory.
- *   It is almost always the same as `name`.
- * - `url` is the url for the submodule.
- * - `oid` is the HEAD SHA1 for the submodule.
- * - `update` is a value from above - see gitmodules(5) update.
- * - `ignore` is a value from above - see gitmodules(5) ignore.
- * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
- * - `refcount` is for internal use.
+ * Submodule info is contained in 4 places: the HEAD tree, the index, config
+ * files (both .git/config and .gitmodules), and the working directory.  Any
+ * or all of those places might be missing information about the submodule
+ * depending on what state the repo is in.  We consider all four places to
+ * build the combination of status flags.
  *
- * If the submodule has been added to .gitmodules but not yet git added,
- * then the `oid` will be zero.  If the submodule has been deleted, but
- * the delete has not been committed yet, then the `oid` will be set, but
- * the `url` will be NULL.
+ * There are four values that are not really status, but give basic info
+ * about what sources of submodule data are available.  These will be
+ * returned even if ignore is set to "ALL".
+ *
+ * * IN_HEAD   - superproject head contains submodule
+ * * IN_INDEX  - superproject index contains submodule
+ * * IN_CONFIG - superproject gitmodules has submodule
+ * * IN_WD     - superproject workdir has submodule
+ *
+ * The following values will be returned so long as ignore is not "ALL".
+ *
+ * * INDEX_ADDED       - in index, not in head
+ * * INDEX_DELETED     - in head, not in index
+ * * INDEX_MODIFIED    - index and head don't match
+ * * WD_UNINITIALIZED  - workdir contains empty directory
+ * * WD_ADDED          - in workdir, not index
+ * * WD_DELETED        - in index, not workdir
+ * * WD_MODIFIED       - index and workdir head don't match
+ *
+ * The following can only be returned if ignore is "NONE" or "UNTRACKED".
+ *
+ * * WD_INDEX_MODIFIED - submodule workdir index is dirty
+ * * WD_WD_MODIFIED    - submodule workdir has modified files
+ *
+ * Lastly, the following will only be returned for ignore "NONE".
+ *
+ * * WD_UNTRACKED      - wd contains untracked files
  */
-typedef struct {
-	char *name;
-	char *path;
-	char *url;
-	git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
-	git_submodule_update_t update;
-	git_submodule_ignore_t ignore;
-	int fetch_recurse;
-	int refcount;
-} git_submodule;
+typedef enum {
+	 GIT_SUBMODULE_STATUS_IN_HEAD           = (1u << 0),
+	 GIT_SUBMODULE_STATUS_IN_INDEX          = (1u << 1),
+	 GIT_SUBMODULE_STATUS_IN_CONFIG         = (1u << 2),
+	 GIT_SUBMODULE_STATUS_IN_WD             = (1u << 3),
+	 GIT_SUBMODULE_STATUS_INDEX_ADDED       = (1u << 4),
+	 GIT_SUBMODULE_STATUS_INDEX_DELETED     = (1u << 5),
+	 GIT_SUBMODULE_STATUS_INDEX_MODIFIED    = (1u << 6),
+	 GIT_SUBMODULE_STATUS_WD_UNINITIALIZED  = (1u << 7),
+	 GIT_SUBMODULE_STATUS_WD_ADDED          = (1u << 8),
+	 GIT_SUBMODULE_STATUS_WD_DELETED        = (1u << 9),
+	 GIT_SUBMODULE_STATUS_WD_MODIFIED       = (1u << 10),
+	 GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
+	 GIT_SUBMODULE_STATUS_WD_WD_MODIFIED    = (1u << 12),
+	 GIT_SUBMODULE_STATUS_WD_UNTRACKED      = (1u << 13),
+} git_submodule_status_t;
 
+#define GIT_SUBMODULE_STATUS__IN_FLAGS \
+	(GIT_SUBMODULE_STATUS_IN_HEAD | \
+	GIT_SUBMODULE_STATUS_IN_INDEX | \
+	GIT_SUBMODULE_STATUS_IN_CONFIG | \
+	GIT_SUBMODULE_STATUS_IN_WD)
+
+#define GIT_SUBMODULE_STATUS__INDEX_FLAGS \
+	(GIT_SUBMODULE_STATUS_INDEX_ADDED | \
+	GIT_SUBMODULE_STATUS_INDEX_DELETED | \
+	GIT_SUBMODULE_STATUS_INDEX_MODIFIED)
+
+#define GIT_SUBMODULE_STATUS__WD_FLAGS \
+	~(GIT_SUBMODULE_STATUS__IN_FLAGS | GIT_SUBMODULE_STATUS__INDEX_FLAGS)
+
+#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
+	(((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_INDEX_UNMODIFIED(S) \
+	(((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \
+	(((S) & GIT_SUBMODULE_STATUS__WD_FLAGS) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \
+	(((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \
+	GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \
+	GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0)
+
 /**
- * Iterate over all submodules of a repository.
+ * Lookup submodule information by name or path.
  *
+ * Given either the submodule name or path (they are usually the same), this
+ * returns a structure describing the submodule.
+ *
+ * There are two expected error scenarios:
+ *
+ * - The submodule is not mentioned in the HEAD, the index, and the config,
+ *   but does "exist" in the working directory (i.e. there is a subdirectory
+ *   that is a valid self-contained git repo).  In this case, this function
+ *   returns GIT_EEXISTS to indicate the the submodule exists but not in a
+ *   state where a git_submodule can be instantiated.
+ * - The submodule is not mentioned in the HEAD, index, or config and the
+ *   working directory doesn't contain a value git repo at that path.
+ *   There may or may not be anything else at that path, but nothing that
+ *   looks like a submodule.  In this case, this returns GIT_ENOTFOUND.
+ *
+ * The submodule object is owned by the containing repo and will be freed
+ * when the repo is freed.  The caller need not free the submodule.
+ *
+ * @param submodule Pointer to submodule description object pointer..
+ * @param repo The repository.
+ * @param name The name of the submodule.  Trailing slashes will be ignored.
+ * @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
+ *         GIT_EEXISTS if submodule exists in working directory only, -1 on
+ *         other errors.
+ */
+GIT_EXTERN(int) git_submodule_lookup(
+	git_submodule **submodule,
+	git_repository *repo,
+	const char *name);
+
+/**
+ * Iterate over all tracked submodules of a repository.
+ *
+ * See the note on `git_submodule` above.  This iterates over the tracked
+ * submodules as decribed therein.
+ *
+ * If you are concerned about items in the working directory that look like
+ * submodules but are not tracked, the diff API will generate a diff record
+ * for workdir items that look like submodules but are not tracked, showing
+ * them as added in the workdir.  Also, the status API will treat the entire
+ * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item.
+ *
  * @param repo The repository
  * @param callback Function to be called with the name of each submodule.
  *        Return a non-zero value to terminate the iteration.
@@ -77,26 +200,344 @@
  */
 GIT_EXTERN(int) git_submodule_foreach(
 	git_repository *repo,
-	int (*callback)(const char *name, void *payload),
+	int (*callback)(git_submodule *sm, const char *name, void *payload),
 	void *payload);
 
 /**
- * Lookup submodule information by name or path.
+ * Set up a new git submodule for checkout.
  *
- * Given either the submodule name or path (they are ususally the same),
- * this returns a structure describing the submodule.  If the submodule
- * does not exist, this will return GIT_ENOTFOUND and set the submodule
- * pointer to NULL.
+ * This does "git submodule add" up to the fetch and checkout of the
+ * submodule contents.  It preps a new submodule, creates an entry in
+ * .gitmodules and creates an empty initialized repository either at the
+ * given path in the working directory or in .git/modules with a gitlink
+ * from the working directory to the new repo.
  *
- * @param submodule Pointer to submodule description object pointer..
- * @param repo The repository.
- * @param name The name of the submodule.  Trailing slashes will be ignored.
- * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
+ * To fully emulate "git submodule add" call this function, then open the
+ * submodule repo and perform the clone step as needed.  Lastly, call
+ * `git_submodule_add_finalize()` to wrap up adding the new submodule and
+ * .gitmodules to the index to be ready to commit.
+ *
+ * @param submodule The newly created submodule ready to open for clone
+ * @param repo Superproject repository to contain the new submodule
+ * @param url URL for the submodules remote
+ * @param path Path at which the submodule should be created
+ * @param use_gitlink Should workdir contain a gitlink to the repo in
+ *        .git/modules vs. repo directly in workdir.
+ * @return 0 on success, GIT_EEXISTS if submodule already exists,
+ *         -1 on other errors.
  */
-GIT_EXTERN(int) git_submodule_lookup(
+GIT_EXTERN(int) git_submodule_add_setup(
 	git_submodule **submodule,
 	git_repository *repo,
-	const char *name);
+	const char *url,
+	const char *path,
+	int use_gitlink);
+
+/**
+ * Resolve the setup of a new git submodule.
+ *
+ * This should be called on a submodule once you have called add setup
+ * and done the clone of the submodule.  This adds the .gitmodules file
+ * and the newly cloned submodule to the index to be ready to be committed
+ * (but doesn't actually do the commit).
+ *
+ * @param submodule The submodule to finish adding.
+ */
+GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule);
+
+/**
+ * Add current submodule HEAD commit to index of superproject.
+ *
+ * @param submodule The submodule to add to the index
+ * @param write_index Boolean if this should immediately write the index
+ *            file.  If you pass this as false, you will have to get the
+ *            git_index and explicitly call `git_index_write()` on it to
+ *            save the change.
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_add_to_index(
+	git_submodule *submodule,
+	int write_index);
+
+/**
+ * Write submodule settings to .gitmodules file.
+ *
+ * This commits any in-memory changes to the submodule to the gitmodules
+ * file on disk.  You may also be interested in `git_submodule_init()` which
+ * writes submodule info to ".git/config" (which is better for local changes
+ * to submodule settings) and/or `git_submodule_sync()` which writes
+ * settings about remotes to the actual submodule repository.
+ *
+ * @param submodule The submodule to write.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_save(git_submodule *submodule);
+
+/**
+ * Get the containing repository for a submodule.
+ *
+ * This returns a pointer to the repository that contains the submodule.
+ * This is a just a reference to the repository that was passed to the
+ * original `git_submodule_lookup()` call, so if that repository has been
+ * freed, then this may be a dangling reference.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to `git_repository`
+ */
+GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule);
+
+/**
+ * Get the name of submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule name
+ */
+GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule);
+
+/**
+ * Get the path to the submodule.
+ *
+ * The path is almost always the same as the submodule name, but the
+ * two are actually not required to match.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule path
+ */
+GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule);
+
+/**
+ * Get the URL for the submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule url
+ */
+GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
+
+/**
+ * Set the URL for the submodule.
+ *
+ * This sets the URL in memory for the submodule. This will be used for
+ * any following submodule actions while this submodule data is in memory.
+ *
+ * After calling this, you may wish to call `git_submodule_save()` to write
+ * the changes back to the ".gitmodules" file and `git_submodule_sync()` to
+ * write the changes to the checked out submodule repository.
+ *
+ * @param submodule Pointer to the submodule object
+ * @param url URL that should be used for the submodule
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_set_url(git_submodule *submodule, const char *url);
+
+/**
+ * Get the OID for the submodule in the index.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in index.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_index_id(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current HEAD tree.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in the HEAD.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current working directory.
+ *
+ * This returns the OID that corresponds to looking up 'HEAD' in the checked
+ * out submodule.  If there are pending changes in the index or anything
+ * else, this won't notice that.  You should call `git_submodule_status()`
+ * for a more complete picture about the state of the working directory.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not checked out.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
+
+/**
+ * Get the ignore rule for the submodule.
+ *
+ * There are four ignore values:
+ *
+ *  - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
+ *    of the submodule from a clean checkout to be dirty, including the
+ *    addition of untracked files.  This is the default if unspecified.
+ *  - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the
+ *    working tree (i.e. call `git_status_foreach()` on the submodule) but
+ *    UNTRACKED files will not count as making the submodule dirty.
+ *  - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the
+ *    submodule has moved for status.  This is fast since it does not need to
+ *    scan the working tree of the submodule at all.
+ *  - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
+ *    The working directory will be consider clean so long as there is a
+ *    checked out version present.
+ */
+GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
+	git_submodule *submodule);
+
+/**
+ * Set the ignore rule for the submodule.
+ *
+ * This sets the ignore rule in memory for the submodule.  This will be used
+ * for any following actions (such as `git_submodule_status()`) while the
+ * submodule is in memory.  You should call `git_submodule_save()` if you
+ * want to persist the new ignore role.
+ *
+ * Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling
+ * `git_submodule_reload()` will revert the rule to the value that was in the
+ * original config.
+ *
+ * @return old value for ignore
+ */
+GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
+	git_submodule *submodule,
+	git_submodule_ignore_t ignore);
+
+/**
+ * Get the update rule for the submodule.
+ */
+GIT_EXTERN(git_submodule_update_t) git_submodule_update(
+	git_submodule *submodule);
+
+/**
+ * Set the update rule for the submodule.
+ *
+ * This sets the update rule in memory for the submodule.  You should call
+ * `git_submodule_save()` if you want to persist the new update rule.
+ *
+ * Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling
+ * `git_submodule_reload()` will revert the rule to the value that was in the
+ * original config.
+ *
+ * @return old value for update
+ */
+GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
+	git_submodule *submodule,
+	git_submodule_update_t update);
+
+/**
+ * Read the fetchRecurseSubmodules rule for a submodule.
+ *
+ * This accesses the submodule.<name>.fetchRecurseSubmodules value for
+ * the submodule that controls fetching behavior for the submodule.
+ *
+ * Note that at this time, libgit2 does not honor this setting and the
+ * fetch functionality current ignores submodules.
+ *
+ * @return 0 if fetchRecurseSubmodules is false, 1 if true
+ */
+GIT_EXTERN(int) git_submodule_fetch_recurse_submodules(
+	git_submodule *submodule);
+
+/**
+ * Set the fetchRecurseSubmodules rule for a submodule.
+ *
+ * This sets the submodule.<name>.fetchRecurseSubmodules value for
+ * the submodule.  You should call `git_submodule_save()` if you want
+ * to persist the new value.
+ *
+ * @param submodule The submodule to modify
+ * @param fetch_recurse_submodules Boolean value
+ * @return old value for fetchRecurseSubmodules
+ */
+GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
+	git_submodule *submodule,
+	int fetch_recurse_submodules);
+
+/**
+ * Copy submodule info into ".git/config" file.
+ *
+ * Just like "git submodule init", this copies information about the
+ * submodule into ".git/config".  You can use the accessor functions
+ * above to alter the in-memory git_submodule object and control what
+ * is written to the config, overriding what is in .gitmodules.
+ *
+ * @param submodule The submodule to write into the superproject config
+ * @param overwrite By default, existing entries will not be overwritten,
+ *                  but setting this to true forces them to be updated.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
+
+/**
+ * Copy submodule remote info into submodule repo.
+ *
+ * This copies the information about the submodules URL into the checked out
+ * submodule config, acting like "git submodule sync".  This is useful if
+ * you have altered the URL for the submodule (or it has been altered by a
+ * fetch of upstream changes) and you need to update your local repo.
+ */
+GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule);
+
+/**
+ * Open the repository for a submodule.
+ *
+ * This is a newly opened repository object.  The caller is responsible for
+ * calling `git_repository_free()` on it when done.  Multiple calls to this
+ * function will return distinct `git_repository` objects.  This will only
+ * work if the submodule is checked out into the working directory.
+ *
+ * @param subrepo Pointer to the submodule repo which was opened
+ * @param submodule Submodule to be opened
+ * @return 0 on success, <0 if submodule repo could not be opened.
+ */
+GIT_EXTERN(int) git_submodule_open(
+	git_repository **repo,
+	git_submodule *submodule);
+
+/**
+ * Reread submodule info from config, index, and HEAD.
+ *
+ * Call this to reread cached submodule information for this submodule if
+ * you have reason to believe that it has changed.
+ */
+GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule);
+
+/**
+ * Reread all submodule info.
+ *
+ * Call this to reload all cached submodule information for the repo.
+ */
+GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo);
+
+/**
+ * Get the status for a submodule.
+ *
+ * This looks at a submodule and tries to determine the status.  It
+ * will return a combination of the `GIT_SUBMODULE_STATUS` values above.
+ * How deeply it examines the working directory to do this will depend
+ * on the `git_submodule_ignore_t` value for the submodule - which can be
+ * set either temporarily or permanently with `git_submodule_set_ignore()`.
+ *
+ * @param status Combination of `GIT_SUBMODULE_STATUS` flags
+ * @param submodule Submodule for which to get status
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_status(
+	unsigned int *status,
+	git_submodule *submodule);
+
+/**
+ * Get the locations of submodule information.
+ *
+ * This is a bit like a very lightweight version of `git_submodule_status`.
+ * It just returns a made of the first four submodule status values (i.e.
+ * the ones like GIT_SUBMODULE_STATUS_IN_HEAD, etc) that tell you where the
+ * submodule data comes from (i.e. the HEAD commit, gitmodules file, etc.).
+ * This can be useful if you want to know if the submodule is present in the
+ * working directory at this point in time, etc.
+ *
+ * @param status Combination of first four `GIT_SUBMODULE_STATUS` flags
+ * @param submodule Submodule for which to get status
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_location(
+	unsigned int *location_status,
+	git_submodule *submodule);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/tag.h b/libgit2/include/git2/tag.h
--- a/libgit2/include/git2/tag.h
+++ b/libgit2/include/git2/tag.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
 #include "types.h"
 #include "oid.h"
 #include "object.h"
+#include "strarray.h"
 
 /**
  * @file git2/tag.h
@@ -24,14 +25,16 @@
 /**
  * Lookup a tag object from the repository.
  *
- * @param tag pointer to the looked up tag
+ * @param out pointer to the looked up tag
  * @param repo the repo to use when locating the tag.
  * @param id identity of the tag to locate.
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oid *id)
+GIT_INLINE(int) git_tag_lookup(
+	git_tag **out, git_repository *repo, const git_oid *id)
 {
-	return git_object_lookup((git_object **)tag, repo, id, (git_otype)GIT_OBJ_TAG);
+	return git_object_lookup(
+		(git_object **)out, repo, id, (git_otype)GIT_OBJ_TAG);
 }
 
 /**
@@ -40,32 +43,33 @@
  *
  * @see git_object_lookup_prefix
  *
- * @param tag pointer to the looked up tag
+ * @param out pointer to the looked up tag
  * @param repo the repo to use when locating the tag.
  * @param id identity of the tag to locate.
  * @param len the length of the short identifier
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len)
+GIT_INLINE(int) git_tag_lookup_prefix(
+	git_tag **out, git_repository *repo, const git_oid *id, size_t len)
 {
-	return git_object_lookup_prefix((git_object **)tag, repo, id, len, (git_otype)GIT_OBJ_TAG);
+	return git_object_lookup_prefix(
+		(git_object **)out, repo, id, len, (git_otype)GIT_OBJ_TAG);
 }
 
 /**
  * Close an open tag
  *
- * This is a wrapper around git_object_free()
+ * You can no longer use the git_tag pointer after this call.
  *
- * IMPORTANT:
- * It *is* necessary to call this method when you stop
- * using a tag. Failure to do so will cause a memory leak.
+ * IMPORTANT: You MUST call this method when you are through with a tag to
+ * release memory. Failure to do so will cause a memory leak.
  *
  * @param tag the tag to close
  */
 
 GIT_INLINE(void) git_tag_free(git_tag *tag)
 {
-	git_object_free((git_object *) tag);
+	git_object_free((git_object *)tag);
 }
 
 
@@ -75,7 +79,7 @@
  * @param tag a previously loaded tag.
  * @return object identity for the tag.
  */
-GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag);
+GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
 
 /**
  * Get the tagged object of a tag
@@ -83,11 +87,11 @@
  * This method performs a repository lookup for the
  * given object and returns it
  *
- * @param target pointer where to store the target
+ * @param target_out pointer where to store the target
  * @param tag a previously loaded tag.
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *tag);
+GIT_EXTERN(int) git_tag_target(git_object **target_out, const git_tag *tag);
 
 /**
  * Get the OID of the tagged object of a tag
@@ -95,7 +99,7 @@
  * @param tag a previously loaded tag.
  * @return pointer to the OID
  */
-GIT_EXTERN(const git_oid *) git_tag_target_oid(git_tag *tag);
+GIT_EXTERN(const git_oid *) git_tag_target_id(const git_tag *tag);
 
 /**
  * Get the type of a tag's tagged object
@@ -103,7 +107,7 @@
  * @param tag a previously loaded tag.
  * @return type of the tagged object
  */
-GIT_EXTERN(git_otype) git_tag_type(git_tag *tag);
+GIT_EXTERN(git_otype) git_tag_target_type(const git_tag *tag);
 
 /**
  * Get the name of a tag
@@ -111,23 +115,23 @@
  * @param tag a previously loaded tag.
  * @return name of the tag
  */
-GIT_EXTERN(const char *) git_tag_name(git_tag *tag);
+GIT_EXTERN(const char *) git_tag_name(const git_tag *tag);
 
 /**
  * Get the tagger (author) of a tag
  *
  * @param tag a previously loaded tag.
- * @return reference to the tag's author
+ * @return reference to the tag's author or NULL when unspecified
  */
-GIT_EXTERN(const git_signature *) git_tag_tagger(git_tag *tag);
+GIT_EXTERN(const git_signature *) git_tag_tagger(const git_tag *tag);
 
 /**
  * Get the message of a tag
  *
  * @param tag a previously loaded tag.
- * @return message of the tag
+ * @return message of the tag or NULL when unspecified
  */
-GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
+GIT_EXTERN(const char *) git_tag_message(const git_tag *tag);
 
 
 /**
@@ -137,9 +141,13 @@
  * this tag object. If `force` is true and a reference
  * already exists with the given name, it'll be replaced.
  *
- * The message will be cleaned up from excess whitespace
- * it will be made sure that the last line ends with a '\n'.
+ * The message will not be cleaned up. This can be achieved
+ * through `git_message_prettify()`.
  *
+ * The tag name will be checked for validity. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
  * @param oid Pointer where to store the OID of the
  * newly created tag. If the tag already exists, this parameter
  * will be the oid of the existing tag, and the function will
@@ -161,18 +169,18 @@
  *
  * @param force Overwrite existing references
  *
- * @return 0 or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
  *	A tag object is written to the ODB, and a proper reference
  *	is written in the /refs/tags folder, pointing to it
  */
 GIT_EXTERN(int) git_tag_create(
-		git_oid *oid,
-		git_repository *repo,
-		const char *tag_name,
-		const git_object *target,
-		const git_signature *tagger,
-		const char *message,
-		int force);
+	git_oid *oid,
+	git_repository *repo,
+	const char *tag_name,
+	const git_object *target,
+	const git_signature *tagger,
+	const char *message,
+	int force);
 
 /**
  * Create a new tag in the repository from a buffer
@@ -181,13 +189,13 @@
  * @param repo Repository where to store the tag
  * @param buffer Raw tag data
  * @param force Overwrite existing tags
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
 GIT_EXTERN(int) git_tag_create_frombuffer(
-		git_oid *oid,
-		git_repository *repo,
-		const char *buffer,
-		int force);
+	git_oid *oid,
+	git_repository *repo,
+	const char *buffer,
+	int force);
 
 /**
  * Create a new lightweight tag pointing at a target object
@@ -196,6 +204,9 @@
  * this target object. If `force` is true and a reference
  * already exists with the given name, it'll be replaced.
  *
+ * The tag name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
  * @param oid Pointer where to store the OID of the provided
  * target object. If the tag already exists, this parameter
  * will be filled with the oid of the existing pointed object
@@ -212,30 +223,33 @@
  *
  * @param force Overwrite existing references
  *
- * @return 0 or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
  *	A proper reference is written in the /refs/tags folder,
  * pointing to the provided target object
  */
 GIT_EXTERN(int) git_tag_create_lightweight(
-		git_oid *oid,
-		git_repository *repo,
-		const char *tag_name,
-		const git_object *target,
-		int force);
+	git_oid *oid,
+	git_repository *repo,
+	const char *tag_name,
+	const git_object *target,
+	int force);
 
 /**
  * Delete an existing tag reference.
  *
+ * The tag name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
  * @param repo Repository where lives the tag
  *
  * @param tag_name Name of the tag to be deleted;
  * this name is validated for consistency.
  *
- * @return 0 or an error code
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
  */
 GIT_EXTERN(int) git_tag_delete(
-		git_repository *repo,
-		const char *tag_name);
+	git_repository *repo,
+	const char *tag_name);
 
 /**
  * Fill a list with all the tags in the Repository
@@ -251,8 +265,8 @@
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_tag_list(
-		git_strarray *tag_names,
-		git_repository *repo);
+	git_strarray *tag_names,
+	git_repository *repo);
 
 /**
  * Fill a list with all the tags in the Repository
@@ -273,24 +287,39 @@
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_tag_list_match(
-		git_strarray *tag_names,
-		const char *pattern,
-		git_repository *repo);
+	git_strarray *tag_names,
+	const char *pattern,
+	git_repository *repo);
 
+
+typedef int (*git_tag_foreach_cb)(const char *name, git_oid *oid, void *payload);
+
 /**
- * Recursively peel a tag until a non tag git_object
- * is met
+ * Call callback `cb' for each tag in the repository
  *
+ * @param repo Repository
+ * @param callback Callback function
+ * @param payload Pointer to callback data (optional)
+ */
+GIT_EXTERN(int) git_tag_foreach(
+	git_repository *repo,
+	git_tag_foreach_cb callback,
+	void *payload);
+
+
+/**
+ * Recursively peel a tag until a non tag git_object is found
+ *
  * The retrieved `tag_target` object is owned by the repository
  * and should be closed with the `git_object_free` method.
  *
- * @param tag_target Pointer to the peeled git_object
+ * @param tag_target_out Pointer to the peeled git_object
  * @param tag The tag to be processed
  * @return 0 or an error code
  */
 GIT_EXTERN(int) git_tag_peel(
-		git_object **tag_target,
-		git_tag *tag);
+	git_object **tag_target_out,
+	const git_tag *tag);
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/threads.h b/libgit2/include/git2/threads.h
--- a/libgit2/include/git2/threads.h
+++ b/libgit2/include/git2/threads.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -27,8 +27,10 @@
  *
  * If libgit2 has been built without GIT_THREADS
  * support, this function is a no-op.
+ *
+ * @return 0 or an error code
  */
-GIT_EXTERN(void) git_threads_init(void);
+GIT_EXTERN(int) git_threads_init(void);
 
 /**
  * Shutdown the threading system.
diff --git a/libgit2/include/git2/trace.h b/libgit2/include/git2/trace.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/trace.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_trace_h__
+#define INCLUDE_git_trace_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/trace.h
+ * @brief Git tracing configuration routines
+ * @defgroup git_trace Git tracing configuration routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Available tracing levels.  When tracing is set to a particular level,
+ * callers will be provided tracing at the given level and all lower levels.
+ */
+typedef enum {
+	/** No tracing will be performed. */
+	GIT_TRACE_NONE = 0,
+
+	/** Severe errors that may impact the program's execution */
+	GIT_TRACE_FATAL = 1,
+
+	/** Errors that do not impact the program's execution */
+	GIT_TRACE_ERROR = 2,
+	
+	/** Warnings that suggest abnormal data */
+	GIT_TRACE_WARN = 3,
+
+	/** Informational messages about program execution */
+	GIT_TRACE_INFO = 4,
+
+	/** Detailed data that allows for debugging */
+	GIT_TRACE_DEBUG = 5,
+
+	/** Exceptionally detailed debugging data */
+	GIT_TRACE_TRACE = 6
+} git_trace_level_t;
+
+/**
+ * An instance for a tracing function
+ */
+typedef void (*git_trace_callback)(git_trace_level_t level, const char *msg);
+
+/**
+ * Sets the system tracing configuration to the specified level with the
+ * specified callback.  When system events occur at a level equal to, or
+ * lower than, the given level they will be reported to the given callback.
+ *
+ * @param level Level to set tracing to
+ * @param cb Function to call with trace data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_callback cb);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/libgit2/include/git2/transport.h b/libgit2/include/git2/transport.h
new file mode 100644
--- /dev/null
+++ b/libgit2/include/git2/transport.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_transport_h__
+#define INCLUDE_git_transport_h__
+
+#include "indexer.h"
+#include "net.h"
+#include "types.h"
+
+/**
+ * @file git2/transport.h
+ * @brief Git transport interfaces and functions
+ * @defgroup git_transport interfaces and functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/*
+ *** Begin interface for credentials acquisition ***
+ */
+
+typedef enum {
+	/* git_cred_userpass_plaintext */
+	GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
+} git_credtype_t;
+
+/* The base structure for all credential types */
+typedef struct git_cred {
+	git_credtype_t credtype;
+	void (*free)(
+		struct git_cred *cred);
+} git_cred;
+
+/* A plaintext username and password */
+typedef struct git_cred_userpass_plaintext {
+	git_cred parent;
+	char *username;
+	char *password;
+} git_cred_userpass_plaintext;
+
+/**
+ * Creates a new plain-text username and password credential object.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username The username of the credential.
+ * @param password The password of the credential.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_cred_userpass_plaintext_new(
+	git_cred **out,
+	const char *username,
+	const char *password);
+
+/**
+ * Signature of a function which acquires a credential object.
+ *
+ * @param cred The newly created credential object.
+ * @param url The resource for which we are demanding a credential.
+ * @param username_from_url The username that was embedded in a "user@host"
+ *                          remote url, or NULL if not included.
+ * @param allowed_types A bitmask stating which cred types are OK to return.
+ * @param payload The payload provided when specifying this callback.
+ * @return 0 for success or an error code for failure
+ */
+typedef int (*git_cred_acquire_cb)(
+	git_cred **cred,
+	const char *url,
+	const char *username_from_url,
+	unsigned int allowed_types,
+	void *payload);
+
+/*
+ *** End interface for credentials acquisition ***
+ *** Begin base transport interface ***
+ */
+
+typedef enum {
+	GIT_TRANSPORTFLAGS_NONE = 0,
+	/* If the connection is secured with SSL/TLS, the authenticity
+	 * of the server certificate should not be verified. */
+	GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
+} git_transport_flags_t;
+
+typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
+
+typedef struct git_transport {
+	unsigned int version;
+	/* Set progress and error callbacks */
+	int (*set_callbacks)(struct git_transport *transport,
+		git_transport_message_cb progress_cb,
+		git_transport_message_cb error_cb,
+		void *payload);
+
+	/* Connect the transport to the remote repository, using the given
+	 * direction. */
+	int (*connect)(struct git_transport *transport,
+		const char *url,
+		git_cred_acquire_cb cred_acquire_cb,
+		void *cred_acquire_payload,
+		int direction,
+		int flags);
+
+	/* This function may be called after a successful call to connect(). The
+	 * provided callback is invoked for each ref discovered on the remote
+	 * end. */
+	int (*ls)(struct git_transport *transport,
+		git_headlist_cb list_cb,
+		void *payload);
+
+	/* Executes the push whose context is in the git_push object. */
+	int (*push)(struct git_transport *transport, git_push *push);
+
+	/* This function may be called after a successful call to connect(), when
+	 * the direction is FETCH. The function performs a negotiation to calculate
+	 * the wants list for the fetch. */
+	int (*negotiate_fetch)(struct git_transport *transport,
+		git_repository *repo,
+		const git_remote_head * const *refs,
+		size_t count);
+
+	/* This function may be called after a successful call to negotiate_fetch(),
+	 * when the direction is FETCH. This function retrieves the pack file for
+	 * the fetch from the remote end. */
+	int (*download_pack)(struct git_transport *transport,
+		git_repository *repo,
+		git_transfer_progress *stats,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload);
+
+	/* Checks to see if the transport is connected */
+	int (*is_connected)(struct git_transport *transport);
+
+	/* Reads the flags value previously passed into connect() */
+	int (*read_flags)(struct git_transport *transport, int *flags);
+
+	/* Cancels any outstanding transport operation */
+	void (*cancel)(struct git_transport *transport);
+
+	/* This function is the reverse of connect() -- it terminates the
+	 * connection to the remote end. */
+	int (*close)(struct git_transport *transport);
+
+	/* Frees/destructs the git_transport object. */
+	void (*free)(struct git_transport *transport);
+} git_transport;
+
+#define GIT_TRANSPORT_VERSION 1
+#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
+
+/**
+ * Function to use to create a transport from a URL. The transport database
+ * is scanned to find a transport that implements the scheme of the URI (i.e.
+ * git:// or http://) and a transport object is returned to the caller.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param url The URL to connect to
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url);
+
+/* Signature of a function which creates a transport */
+typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param);
+
+/* Transports which come with libgit2 (match git_transport_cb). The expected
+ * value for "param" is listed in-line below. */
+
+/**
+ * Create an instance of the dummy transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_dummy(
+	git_transport **out,
+	git_remote *owner,
+	/* NULL */ void *payload);
+
+/**
+ * Create an instance of the local transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_local(
+	git_transport **out,
+	git_remote *owner,
+	/* NULL */ void *payload);
+
+/**
+ * Create an instance of the smart transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload A pointer to a git_smart_subtransport_definition
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_smart(
+	git_transport **out,
+	git_remote *owner,
+	/* (git_smart_subtransport_definition *) */ void *payload);
+
+/*
+ *** End of base transport interface ***
+ *** Begin interface for subtransports for the smart transport ***
+ */
+
+/* The smart transport knows how to speak the git protocol, but it has no
+ * knowledge of how to establish a connection between it and another endpoint,
+ * or how to move data back and forth. For this, a subtransport interface is
+ * declared, and the smart transport delegates this work to the subtransports.
+ * Three subtransports are implemented: git, http, and winhttp. (The http and
+ * winhttp transports each implement both http and https.) */
+
+/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
+ * (request/response). The smart transport handles the differences in its own
+ * logic. The git subtransport is RPC = 0, while http and winhttp are both
+ * RPC = 1. */
+
+/* Actions that the smart transport can ask
+ * a subtransport to perform */
+typedef enum {
+	GIT_SERVICE_UPLOADPACK_LS = 1,
+	GIT_SERVICE_UPLOADPACK = 2,
+	GIT_SERVICE_RECEIVEPACK_LS = 3,
+	GIT_SERVICE_RECEIVEPACK = 4,
+} git_smart_service_t;
+
+struct git_smart_subtransport;
+
+/* A stream used by the smart transport to read and write data
+ * from a subtransport */
+typedef struct git_smart_subtransport_stream {
+	/* The owning subtransport */
+	struct git_smart_subtransport *subtransport;
+
+	int (*read)(
+			struct git_smart_subtransport_stream *stream,
+			char *buffer,
+			size_t buf_size,
+			size_t *bytes_read);
+
+	int (*write)(
+			struct git_smart_subtransport_stream *stream,
+			const char *buffer,
+			size_t len);
+
+	void (*free)(
+			struct git_smart_subtransport_stream *stream);
+} git_smart_subtransport_stream;
+
+/* An implementation of a subtransport which carries data for the
+ * smart transport */
+typedef struct git_smart_subtransport {
+	int (* action)(
+			git_smart_subtransport_stream **out,
+			struct git_smart_subtransport *transport,
+			const char *url,
+			git_smart_service_t action);
+
+	/* Subtransports are guaranteed a call to close() between
+	 * calls to action(), except for the following two "natural" progressions
+	 * of actions against a constant URL.
+	 *
+	 * 1. UPLOADPACK_LS -> UPLOADPACK
+	 * 2. RECEIVEPACK_LS -> RECEIVEPACK */
+	int (* close)(struct git_smart_subtransport *transport);
+
+	void (* free)(struct git_smart_subtransport *transport);
+} git_smart_subtransport;
+
+/* A function which creates a new subtransport for the smart transport */
+typedef int (*git_smart_subtransport_cb)(
+	git_smart_subtransport **out,
+	git_transport* owner);
+
+typedef struct git_smart_subtransport_definition {
+	/* The function to use to create the git_smart_subtransport */
+	git_smart_subtransport_cb callback;
+
+	/* True if the protocol is stateless; false otherwise. For example,
+	 * http:// is stateless, but git:// is not. */
+	unsigned rpc;
+} git_smart_subtransport_definition;
+
+/* Smart transport subtransports that come with libgit2 */
+
+/**
+ * Create an instance of the http subtransport. This subtransport
+ * also supports https. On Win32, this subtransport may be implemented
+ * using the WinHTTP library.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_http(
+	git_smart_subtransport **out,
+	git_transport* owner);
+
+/**
+ * Create an instance of the git subtransport.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_git(
+	git_smart_subtransport **out,
+	git_transport* owner);
+
+/*
+ *** End interface for subtransports for the smart transport ***
+ */
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/libgit2/include/git2/tree.h b/libgit2/include/git2/tree.h
--- a/libgit2/include/git2/tree.h
+++ b/libgit2/include/git2/tree.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -24,14 +24,15 @@
 /**
  * Lookup a tree object from the repository.
  *
- * @param tree pointer to the looked up tree
- * @param repo the repo to use when locating the tree.
- * @param id identity of the tree to locate.
+ * @param out Pointer to the looked up tree
+ * @param repo The repo to use when locating the tree.
+ * @param id Identity of the tree to locate.
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git_oid *id)
+GIT_INLINE(int) git_tree_lookup(
+	git_tree **out, git_repository *repo, const git_oid *id)
 {
-	return git_object_lookup((git_object **)tree, repo, id, GIT_OBJ_TREE);
+	return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
 }
 
 /**
@@ -46,72 +47,136 @@
  * @param len the length of the short identifier
  * @return 0 or an error code
  */
-GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len)
+GIT_INLINE(int) git_tree_lookup_prefix(
+	git_tree **out,
+	git_repository *repo,
+	const git_oid *id,
+	size_t len)
 {
-	return git_object_lookup_prefix((git_object **)tree, repo, id, len, GIT_OBJ_TREE);
+	return git_object_lookup_prefix(
+		(git_object **)out, repo, id, len, GIT_OBJ_TREE);
 }
 
 /**
  * Close an open tree
  *
- * This is a wrapper around git_object_free()
+ * You can no longer use the git_tree pointer after this call.
  *
- * IMPORTANT:
- * It *is* necessary to call this method when you stop
- * using a tree. Failure to do so will cause a memory leak.
+ * IMPORTANT: You MUST call this method when you stop using a tree to
+ * release memory. Failure to do so will cause a memory leak.
  *
- * @param tree the tree to close
+ * @param tree The tree to close
  */
-
 GIT_INLINE(void) git_tree_free(git_tree *tree)
 {
-	git_object_free((git_object *) tree);
+	git_object_free((git_object *)tree);
 }
 
-
 /**
  * Get the id of a tree.
  *
  * @param tree a previously loaded tree.
  * @return object identity for the tree.
  */
-GIT_EXTERN(const git_oid *) git_tree_id(git_tree *tree);
+GIT_EXTERN(const git_oid *) git_tree_id(const git_tree *tree);
 
 /**
+ * Get the repository that contains the tree.
+ *
+ * @param tree A previously loaded tree.
+ * @return Repository that contains this tree.
+ */
+GIT_EXTERN(git_repository *) git_tree_owner(const git_tree *tree);
+
+/**
  * Get the number of entries listed in a tree
  *
  * @param tree a previously loaded tree.
  * @return the number of entries in the tree
  */
-GIT_EXTERN(unsigned int) git_tree_entrycount(git_tree *tree);
+GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree);
 
 /**
  * Lookup a tree entry by its filename
  *
+ * This returns a git_tree_entry that is owned by the git_tree.  You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
  * @param tree a previously loaded tree.
  * @param filename the filename of the desired entry
  * @return the tree entry; NULL if not found
  */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(git_tree *tree, const char *filename);
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
+	git_tree *tree, const char *filename);
 
 /**
  * Lookup a tree entry by its position in the tree
  *
+ * This returns a git_tree_entry that is owned by the git_tree.  You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
  * @param tree a previously loaded tree.
  * @param idx the position in the entry list
  * @return the tree entry; NULL if not found
  */
-GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(git_tree *tree, unsigned int idx);
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
+	git_tree *tree, size_t idx);
 
 /**
- * Get the UNIX file attributes of a tree entry
+ * Lookup a tree entry by SHA value.
  *
- * @param entry a tree entry
- * @return attributes as an integer
+ * This returns a git_tree_entry that is owned by the git_tree.  You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
+ * Warning: this must examine every entry in the tree, so it is not fast.
+ *
+ * @param tree a previously loaded tree.
+ * @param oid the sha being looked for
+ * @return the tree entry; NULL if not found
  */
-GIT_EXTERN(unsigned int) git_tree_entry_attributes(const git_tree_entry *entry);
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid(
+	const git_tree *tree, const git_oid *oid);
 
 /**
+ * Retrieve a tree entry contained in a tree or in any of its subtrees,
+ * given its relative path.
+ *
+ * Unlike the other lookup functions, the returned tree entry is owned by
+ * the user and must be freed explicitly with `git_tree_entry_free()`.
+ *
+ * @param out Pointer where to store the tree entry
+ * @param root Previously loaded tree which is the root of the relative path
+ * @param subtree_path Path to the contained entry
+ * @return 0 on success; GIT_ENOTFOUND if the path does not exist
+ */
+GIT_EXTERN(int) git_tree_entry_bypath(
+	git_tree_entry **out,
+	git_tree *root,
+	const char *path);
+
+/**
+ * Duplicate a tree entry
+ *
+ * Create a copy of a tree entry. The returned copy is owned by the user,
+ * and must be freed explicitly with `git_tree_entry_free()`.
+ *
+ * @param entry A tree entry to duplicate
+ * @return a copy of the original entry or NULL on error (alloc failure)
+ */
+GIT_EXTERN(git_tree_entry *) git_tree_entry_dup(const git_tree_entry *entry);
+
+/**
+ * Free a user-owned tree entry
+ *
+ * IMPORTANT: This function is only needed for tree entries owned by the
+ * user, such as the ones returned by `git_tree_entry_dup()` or
+ * `git_tree_entry_bypath()`.
+ *
+ * @param entry The entry to free
+ */
+GIT_EXTERN(void) git_tree_entry_free(git_tree_entry *entry);
+
+/**
  * Get the filename of a tree entry
  *
  * @param entry a tree entry
@@ -136,51 +201,55 @@
 GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
 
 /**
- * Convert a tree entry to the git_object it points too.
+ * Get the UNIX file attributes of a tree entry
  *
- * @param object pointer to the converted object
- * @param repo repository where to lookup the pointed object
  * @param entry a tree entry
- * @return 0 or an error code
+ * @return filemode as an integer
  */
-GIT_EXTERN(int) git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
+GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
 
 /**
- * Write a tree to the ODB from the index file
+ * Compare two tree entries
  *
- * This method will scan the index and write a representation
- * of its current state back to disk; it recursively creates
- * tree objects for each of the subtrees stored in the index,
- * but only returns the OID of the root tree. This is the OID
- * that can be used e.g. to create a commit.
+ * @param e1 first tree entry
+ * @param e2 second tree entry
+ * @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2
+ */
+GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
+
+/**
+ * Convert a tree entry to the git_object it points too.
  *
- * The index instance cannot be bare, and needs to be associated
- * to an existing repository.
+ * You must call `git_object_free()` on the object when you are done with it.
  *
- * @param oid Pointer where to store the written tree
- * @param index Index to write
+ * @param object pointer to the converted object
+ * @param repo repository where to lookup the pointed object
+ * @param entry a tree entry
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index);
+GIT_EXTERN(int) git_tree_entry_to_object(
+	git_object **object_out,
+	git_repository *repo,
+	const git_tree_entry *entry);
 
 /**
  * Create a new tree builder.
  *
- * The tree builder can be used to create or modify
- * trees in memory and write them as tree objects to the
- * database.
+ * The tree builder can be used to create or modify trees in memory and
+ * write them as tree objects to the database.
  *
- * If the `source` parameter is not NULL, the tree builder
- * will be initialized with the entries of the given tree.
+ * If the `source` parameter is not NULL, the tree builder will be
+ * initialized with the entries of the given tree.
  *
- * If the `source` parameter is NULL, the tree builder will
- * have no entries and will have to be filled manually.
+ * If the `source` parameter is NULL, the tree builder will start with no
+ * entries and will have to be filled manually.
  *
- * @param builder_p Pointer where to store the tree builder
+ * @param out Pointer where to store the tree builder
  * @param source Source tree to initialize the builder (optional)
- * @return 0 on sucess; error code otherwise
+ * @return 0 on success; error code otherwise
  */
-GIT_EXTERN(int) git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source);
+GIT_EXTERN(int) git_treebuilder_create(
+	git_treebuilder **out, const git_tree *source);
 
 /**
  * Clear all the entires in the builder
@@ -218,7 +287,8 @@
  * @param filename Name of the entry
  * @return pointer to the entry; NULL if not found
  */
-GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(git_treebuilder *bld, const char *filename);
+GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
+	git_treebuilder *bld, const char *filename);
 
 /**
  * Add or update an entry to the builder
@@ -226,20 +296,31 @@
  * Insert a new entry for `filename` in the builder with the
  * given attributes.
  *
- * if an entry named `filename` already exists, its attributes
+ * If an entry named `filename` already exists, its attributes
  * will be updated with the given ones.
  *
- * The optional pointer `entry_out` can be used to retrieve a
- * pointer to the newly created/updated entry.
+ * The optional pointer `out` can be used to retrieve a pointer to
+ * the newly created/updated entry.  Pass NULL if you do not need it.
  *
- * @param entry_out Pointer to store the entry (optional)
+ * No attempt is being made to ensure that the provided oid points
+ * to an existing git object in the object database, nor that the
+ * attributes make sense regarding the type of the pointed at object.
+ *
+ * @param out Pointer to store the entry (optional)
  * @param bld Tree builder
  * @param filename Filename of the entry
  * @param id SHA1 oid of the entry
- * @param attributes Folder attributes of the entry
+ * @param filemode Folder attributes of the entry. This parameter must
+ *			be valued with one of the following entries: 0040000, 0100644,
+ *			0100755, 0120000 or 0160000.
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes);
+GIT_EXTERN(int) git_treebuilder_insert(
+	const git_tree_entry **out,
+	git_treebuilder *bld,
+	const char *filename,
+	const git_oid *id,
+	git_filemode_t filemode);
 
 /**
  * Remove an entry from the builder by its filename
@@ -247,78 +328,75 @@
  * @param bld Tree builder
  * @param filename Filename of the entry to remove
  */
-GIT_EXTERN(int) git_treebuilder_remove(git_treebuilder *bld, const char *filename);
+GIT_EXTERN(int) git_treebuilder_remove(
+	git_treebuilder *bld, const char *filename);
 
+typedef int (*git_treebuilder_filter_cb)(
+	const git_tree_entry *entry, void *payload);
+
 /**
  * Filter the entries in the tree
  *
- * The `filter` callback will be called for each entry
- * in the tree with a pointer to the entry and the
- * provided `payload`: if the callback returns 1, the
- * entry will be filtered (removed from the builder).
+ * The `filter` callback will be called for each entry in the tree with a
+ * pointer to the entry and the provided `payload`; if the callback returns
+ * non-zero, the entry will be filtered (removed from the builder).
  *
  * @param bld Tree builder
  * @param filter Callback to filter entries
+ * @param payload Extra data to pass to filter
  */
-GIT_EXTERN(void) git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload);
+GIT_EXTERN(void) git_treebuilder_filter(
+	git_treebuilder *bld,
+	git_treebuilder_filter_cb filter,
+	void *payload);
 
 /**
  * Write the contents of the tree builder as a tree object
  *
- * The tree builder will be written to the given `repo`, and
- * it's identifying SHA1 hash will be stored in the `oid`
- * pointer.
+ * The tree builder will be written to the given `repo`, and its
+ * identifying SHA1 hash will be stored in the `id` pointer.
  *
- * @param oid Pointer where to store the written OID
- * @param repo Repository where to store the object
+ * @param id Pointer to store the OID of the newly written tree
+ * @param repo Repository in which to store the object
  * @param bld Tree builder to write
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld);
+GIT_EXTERN(int) git_treebuilder_write(
+	git_oid *id, git_repository *repo, git_treebuilder *bld);
 
-/**
- * Retrieve a subtree contained in a tree, given its
- * relative path.
- *
- * The returned tree is owned by the repository and
- * should be closed with the `git_object_free` method.
- *
- * @param subtree Pointer where to store the subtree
- * @param root A previously loaded tree which will be the root of the relative path
- * @param subtree_path Path to the contained subtree
- * @return 0 on success; GIT_ENOTFOUND if the path does not lead to a subtree
- */
-GIT_EXTERN(int) git_tree_get_subtree(git_tree **subtree, git_tree *root, const char *subtree_path);
 
 /** Callback for the tree traversal method */
-typedef int (*git_treewalk_cb)(const char *root, git_tree_entry *entry, void *payload);
+typedef int (*git_treewalk_cb)(
+	const char *root, const git_tree_entry *entry, void *payload);
 
 /** Tree traversal modes */
-enum git_treewalk_mode {
+typedef enum {
 	GIT_TREEWALK_PRE = 0, /* Pre-order */
 	GIT_TREEWALK_POST = 1, /* Post-order */
-};
+} git_treewalk_mode;
 
 /**
- * Traverse the entries in a tree and its subtrees in
- * post or pre order
+ * Traverse the entries in a tree and its subtrees in post or pre order.
  *
- * The entries will be traversed in the specified order,
- * children subtrees will be automatically loaded as required,
- * and the `callback` will be called once per entry with
- * the current (relative) root for the entry and the entry
- * data itself.
+ * The entries will be traversed in the specified order, children subtrees
+ * will be automatically loaded as required, and the `callback` will be
+ * called once per entry with the current (relative) root for the entry and
+ * the entry data itself.
  *
- * If the callback returns a negative value, the passed entry
- * will be skiped on the traversal.
+ * If the callback returns a positive value, the passed entry will be
+ * skipped on the traversal (in pre mode). A negative value stops the walk.
  *
  * @param tree The tree to walk
- * @param callback Function to call on each tree entry
  * @param mode Traversal mode (pre or post-order)
+ * @param callback Function to call on each tree entry
  * @param payload Opaque pointer to be passed on each callback
  * @return 0 or an error code
  */
-GIT_EXTERN(int) git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload);
+GIT_EXTERN(int) git_tree_walk(
+	const git_tree *tree,
+	git_treewalk_mode mode,
+	git_treewalk_cb callback,
+	void *payload);
 
 /** @} */
 
diff --git a/libgit2/include/git2/types.h b/libgit2/include/git2/types.h
--- a/libgit2/include/git2/types.h
+++ b/libgit2/include/git2/types.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -32,6 +32,9 @@
  * stat() functions, for all platforms.
  */
 #include <sys/types.h>
+#ifdef __amigaos4__
+#include <stdint.h>
+#endif
 
 #if defined(_MSC_VER)
 
@@ -86,6 +89,15 @@
 /** A stream to read/write from the ODB */
 typedef struct git_odb_stream git_odb_stream;
 
+/** A stream to write a packfile to the ODB */
+typedef struct git_odb_writepack git_odb_writepack;
+
+/** An open refs database handle. */
+typedef struct git_refdb git_refdb;
+
+/** A custom backend for refs */
+typedef struct git_refdb_backend git_refdb_backend;
+
 /**
  * Representation of an existing git repository,
  * including all its object contents
@@ -123,7 +135,7 @@
 typedef struct git_config git_config;
 
 /** Interface to access a configuration file */
-typedef struct git_config_file git_config_file;
+typedef struct git_config_backend git_config_backend;
 
 /** Representation of a reference log entry */
 typedef struct git_reflog_entry git_reflog_entry;
@@ -134,6 +146,9 @@
 /** Representation of a git note */
 typedef struct git_note git_note;
 
+/** Representation of a git packbuilder */
+typedef struct git_packbuilder git_packbuilder;
+
 /** Time in a signature */
 typedef struct git_time {
 	git_time_t time; /** time in seconds from epoch */
@@ -155,9 +170,7 @@
 	GIT_REF_INVALID = 0, /** Invalid reference */
 	GIT_REF_OID = 1, /** A reference which points at an object id */
 	GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */
-	GIT_REF_PACKED = 4,
-	GIT_REF_HAS_PEEL = 8,
-	GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
+	GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
 } git_ref_t;
 
 /** Basic type of any Git branch. */
@@ -166,10 +179,22 @@
 	GIT_BRANCH_REMOTE = 2,
 } git_branch_t;
 
+/** Valid modes for index and tree entries. */
+typedef enum {
+	GIT_FILEMODE_NEW					= 0000000,
+	GIT_FILEMODE_TREE					= 0040000,
+	GIT_FILEMODE_BLOB					= 0100644,
+	GIT_FILEMODE_BLOB_EXECUTABLE		= 0100755,
+	GIT_FILEMODE_LINK					= 0120000,
+	GIT_FILEMODE_COMMIT					= 0160000,
+} git_filemode_t;
+
 typedef struct git_refspec git_refspec;
 typedef struct git_remote git_remote;
+typedef struct git_push git_push;
 
 typedef struct git_remote_head git_remote_head;
+typedef struct git_remote_callbacks git_remote_callbacks;
 
 /** @} */
 GIT_END_DECL
diff --git a/libgit2/include/git2/version.h b/libgit2/include/git2/version.h
--- a/libgit2/include/git2/version.h
+++ b/libgit2/include/git2/version.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,9 +7,9 @@
 #ifndef INCLUDE_git_version_h__
 #define INCLUDE_git_version_h__
 
-#define LIBGIT2_VERSION "0.17.0"
+#define LIBGIT2_VERSION "0.18.0"
 #define LIBGIT2_VER_MAJOR 0
-#define LIBGIT2_VER_MINOR 17
+#define LIBGIT2_VER_MINOR 18
 #define LIBGIT2_VER_REVISION 0
 
 #endif
diff --git a/libgit2/include/git2/windows.h b/libgit2/include/git2/windows.h
deleted file mode 100644
--- a/libgit2/include/git2/windows.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_git_windows_h__
-#define INCLUDE_git_windows_h__
-
-#include "common.h"
-
-/**
- * @file git2/windows.h
- * @brief Windows-specific functions
- * @ingroup Git
- * @{
- */
-GIT_BEGIN_DECL
-
-/**
- * Set the active codepage for Windows syscalls
- *
- * All syscalls performed by the library will assume
- * this codepage when converting paths and strings
- * to use by the Windows kernel.
- *
- * The default value of UTF-8 will work automatically
- * with most Git repositories created on Unix systems.
- *
- * This settings needs only be changed when working
- * with repositories that contain paths in specific,
- * non-UTF codepages.
- *
- * A full list of all available codepage identifiers may
- * be found at:
- *
- * http://msdn.microsoft.com/en-us/library/windows/desktop/dd317756(v=vs.85).aspx
- *
- * @param codepage numeric codepage identifier
- */
-GIT_EXTERN(void) gitwin_set_codepage(unsigned int codepage);
-
-/**
- * Return the active codepage for Windows syscalls
- *
- * @return numeric codepage identifier
- */
-GIT_EXTERN(unsigned int) gitwin_get_codepage(void);
-
-/**
- * Set the active Windows codepage to UTF-8 (this is
- * the default value)
- */
-GIT_EXTERN(void) gitwin_set_utf8(void);
-
-/** @} */
-GIT_END_DECL
-#endif
-
diff --git a/libgit2/src/attr.c b/libgit2/src/attr.c
--- a/libgit2/src/attr.c
+++ b/libgit2/src/attr.c
@@ -1,10 +1,32 @@
 #include "repository.h"
 #include "fileops.h"
 #include "config.h"
+#include "attr.h"
+#include "ignore.h"
+#include "git2/oid.h"
 #include <ctype.h>
 
 GIT__USE_STRMAP;
 
+const char *git_attr__true  = "[internal]__TRUE__";
+const char *git_attr__false = "[internal]__FALSE__";
+const char *git_attr__unset = "[internal]__UNSET__";
+
+git_attr_t git_attr_value(const char *attr)
+{
+	if (attr == NULL || attr == git_attr__unset)
+		return GIT_ATTR_UNSPECIFIED_T;
+
+	if (attr == git_attr__true)
+		return GIT_ATTR_TRUE_T;
+
+	if (attr == git_attr__false)
+		return GIT_ATTR_FALSE_T;
+
+	return GIT_ATTR_VALUE_T;
+}
+
+
 static int collect_attr_files(
 	git_repository *repo,
 	uint32_t flags,
@@ -22,7 +44,7 @@
 	int error;
 	git_attr_path path;
 	git_vector files = GIT_VECTOR_INIT;
-	unsigned int i, j;
+	size_t i, j;
 	git_attr_file *file;
 	git_attr_name attr;
 	git_attr_rule *rule;
@@ -41,8 +63,9 @@
 	git_vector_foreach(&files, i, file) {
 
 		git_attr_file__foreach_matching_rule(file, &path, j, rule) {
-			int pos = git_vector_bsearch(&rule->assigns, &attr);
-			if (pos >= 0) {
+			size_t pos;
+
+			if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) {
 				*value = ((git_attr_assignment *)git_vector_get(
 							  &rule->assigns, pos))->value;
 				goto cleanup;
@@ -74,7 +97,7 @@
 	int error;
 	git_attr_path path;
 	git_vector files = GIT_VECTOR_INIT;
-	unsigned int i, j, k;
+	size_t i, j, k;
 	git_attr_file *file;
 	git_attr_rule *rule;
 	attr_get_many_info *info = NULL;
@@ -96,7 +119,7 @@
 		git_attr_file__foreach_matching_rule(file, &path, j, rule) {
 
 			for (k = 0; k < num_attr; k++) {
-				int pos;
+				size_t pos;
 
 				if (info[k].found != NULL) /* already found assignment */
 					continue;
@@ -106,8 +129,7 @@
 					info[k].name.name_hash = git_attr_file__name_hash(names[k]);
 				}
 
-				pos = git_vector_bsearch(&rule->assigns, &info[k].name);
-				if (pos >= 0) {
+				if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) {
 					info[k].found = (git_attr_assignment *)
 						git_vector_get(&rule->assigns, pos);
 					values[k] = info[k].found->value;
@@ -138,7 +160,7 @@
 	int error;
 	git_attr_path path;
 	git_vector files = GIT_VECTOR_INIT;
-	unsigned int i, j, k;
+	size_t i, j, k;
 	git_attr_file *file;
 	git_attr_rule *rule;
 	git_attr_assignment *assign;
@@ -163,11 +185,15 @@
 					continue;
 
 				git_strmap_insert(seen, assign->name, assign, error);
-				if (error >= 0)
-					error = callback(assign->name, assign->value, payload);
+				if (error < 0)
+					goto cleanup;
 
-				if (error != 0)
+				error = callback(assign->name, assign->value, payload);
+				if (error) {
+					giterr_clear();
+					error = GIT_EUSER;
 					goto cleanup;
+				}
 			}
 		}
 	}
@@ -237,30 +263,30 @@
 
 static int load_attr_file(
 	const char **data,
-	git_attr_file_stat_sig *sig,
+	git_futils_filestamp *stamp,
 	const char *filename)
 {
 	int error;
 	git_buf content = GIT_BUF_INIT;
-	struct stat st;
 
-	if (p_stat(filename, &st) < 0)
-		return GIT_ENOTFOUND;
+	error = git_futils_filestamp_check(stamp, filename);
+	if (error < 0)
+		return error;
 
-	if (sig != NULL &&
-		(git_time_t)st.st_mtime == sig->seconds &&
-		(git_off_t)st.st_size == sig->size &&
-		(unsigned int)st.st_ino == sig->ino)
+	/* if error == 0, then file is up to date. By returning GIT_ENOTFOUND,
+	 * we tell the caller not to reparse this file...
+	 */
+	if (!error)
 		return GIT_ENOTFOUND;
 
-	error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
-	if (error < 0)
-		return error;
+	error = git_futils_readbuffer(&content, filename);
+	if (error < 0) {
+		/* convert error into ENOTFOUND so failed permissions / invalid
+		 * file type don't actually stop the operation in progress.
+		 */
+		return GIT_ENOTFOUND;
 
-	if (sig != NULL) {
-		sig->seconds = (git_time_t)st.st_mtime;
-		sig->size    = (git_off_t)st.st_size;
-		sig->ino     = (unsigned int)st.st_ino;
+		/* TODO: once warnings are available, issue a warning callback */
 	}
 
 	*data = git_buf_detach(&content);
@@ -276,14 +302,15 @@
 	const char *relfile)
 {
 	int error;
+	size_t pos;
 	git_index *index;
-	git_index_entry *entry;
+	const git_index_entry *entry;
 
 	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
-		(error = git_index_find(index, relfile)) < 0)
+		(error = git_index_find(&pos, index, relfile)) < 0)
 		return error;
 
-	entry = git_index_get(index, error);
+	entry = git_index_get_byindex(index, pos);
 
 	if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
 		return GIT_ENOTFOUND;
@@ -352,6 +379,7 @@
 	const char *filename,
 	git_attr_file_source source,
 	git_attr_file_parser parse,
+	void* parsedata,
 	git_vector *stack)
 {
 	int error = 0;
@@ -361,7 +389,7 @@
 	git_attr_cache *cache = git_repository_attr_cache(repo);
 	git_attr_file *file = NULL;
 	git_blob *blob = NULL;
-	git_attr_file_stat_sig st;
+	git_futils_filestamp stamp;
 
 	assert(filename && stack);
 
@@ -383,12 +411,10 @@
 	/* if not in cache, load data, parse, and cache */
 
 	if (source == GIT_ATTR_FILE_FROM_FILE) {
-		if (file)
-			memcpy(&st, &file->cache_data.st, sizeof(st));
-		else
-			memset(&st, 0, sizeof(st));
+		git_futils_filestamp_set(
+			&stamp, file ? &file->cache_data.stamp : NULL);
 
-		error = load_attr_file(&content, &st, filename);
+		error = load_attr_file(&content, &stamp, filename);
 	} else {
 		error = load_attr_blob_from_index(&content, &blob,
 			repo, file ? &file->cache_data.oid : NULL, relfile);
@@ -403,14 +429,19 @@
 		goto finish;
 	}
 
-	if (!file &&
-		(error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
-		goto finish;
+	/* if we got here, we have to parse and/or reparse the file */
+	if (file)
+		git_attr_file__clear_rules(file);
+	else {
+		error = git_attr_file__new(&file, source, relfile, &cache->pool);
+		if (error < 0)
+			goto finish;
+	}
 
-	if (parse && (error = parse(repo, content, file)) < 0)
+	if (parse && (error = parse(repo, parsedata, content, file)) < 0)
 		goto finish;
 
-	git_strmap_insert(cache->files, file->key, file, error);
+	git_strmap_insert(cache->files, file->key, file, error); //-V595
 	if (error > 0)
 		error = 0;
 
@@ -418,7 +449,7 @@
 	if (blob)
 		git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
 	else
-		memcpy(&file->cache_data.st, &st, sizeof(st));
+		git_futils_filestamp_set(&file->cache_data.stamp, &stamp);
 
 finish:
 	/* push file onto vector if we found one*/
@@ -439,7 +470,7 @@
 }
 
 #define push_attr_file(R,S,B,F) \
-	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,(S))
+	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S))
 
 typedef struct {
 	git_repository *repo;
@@ -488,7 +519,7 @@
 	for (i = 0; !error && i < n_src; ++i)
 		error = git_attr_cache__push_file(
 			info->repo, path->ptr, GIT_ATTR_FILE, src[i],
-			git_attr_file__parse_buffer, info->files);
+			git_attr_file__parse_buffer, NULL, info->files);
 
 	return error;
 }
@@ -550,8 +581,10 @@
 		error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
 		if (!error)
 			error = push_attr_file(repo, files, NULL, dir.ptr);
-		else if (error == GIT_ENOTFOUND)
+		else if (error == GIT_ENOTFOUND) {
+			giterr_clear();
 			error = 0;
+		}
 	}
 
  cleanup:
@@ -562,7 +595,30 @@
 	return error;
 }
 
+static int attr_cache__lookup_path(
+	const char **out, git_config *cfg, const char *key, const char *fallback)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int error;
 
+	if (!(error = git_config_get_string(out, cfg, key)))
+		return 0;
+
+	if (error == GIT_ENOTFOUND) {
+		giterr_clear();
+		error = 0;
+
+		if (!git_futils_find_xdg_file(&buf, fallback))
+			*out = git_buf_detach(&buf);
+		else
+			*out = NULL;
+
+		git_buf_free(&buf);
+	}
+
+	return error;
+}
+
 int git_attr_cache__init(git_repository *repo)
 {
 	int ret;
@@ -576,15 +632,15 @@
 	if (git_repository_config__weakptr(&cfg, repo) < 0)
 		return -1;
 
-	ret = git_config_get_string(&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG);
-	if (ret < 0 && ret != GIT_ENOTFOUND)
+	ret = attr_cache__lookup_path(
+		&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
+	if (ret < 0)
 		return ret;
 
-	ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);
-	if (ret < 0 && ret != GIT_ENOTFOUND)
+	ret = attr_cache__lookup_path(
+		&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
+	if (ret < 0)
 		return ret;
-
-	giterr_clear();
 
 	/* allocate hashtable for attribute and ignore file contents */
 	if (cache->files == NULL) {
diff --git a/libgit2/src/attr.h b/libgit2/src/attr.h
--- a/libgit2/src/attr.h
+++ b/libgit2/src/attr.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,24 +8,12 @@
 #define INCLUDE_attr_h__
 
 #include "attr_file.h"
-#include "strmap.h"
 
-#define GIT_ATTR_CONFIG   "core.attributesfile"
-#define GIT_IGNORE_CONFIG "core.excludesfile"
-
-typedef struct {
-	int initialized;
-	git_pool pool;
-	git_strmap *files;	/* hash path to git_attr_file of rules */
-	git_strmap *macros;	/* hash name to vector<git_attr_assignment> */
-	const char *cfg_attr_file; /* cached value of core.attributesfile */
-	const char *cfg_excl_file; /* cached value of core.excludesfile */
-} git_attr_cache;
+#define GIT_ATTR_CONFIG       "core.attributesfile"
+#define GIT_IGNORE_CONFIG     "core.excludesfile"
 
 typedef int (*git_attr_file_parser)(
-	git_repository *, const char *, git_attr_file *);
-
-extern int git_attr_cache__init(git_repository *repo);
+	git_repository *, void *, const char *, git_attr_file *);
 
 extern int git_attr_cache__insert_macro(
 	git_repository *repo, git_attr_rule *macro);
@@ -39,6 +27,7 @@
 	const char *filename,
 	git_attr_file_source source,
 	git_attr_file_parser parse,
+	void *parsedata, /* passed through to parse function */
 	git_vector *stack);
 
 extern int git_attr_cache__internal_file(
diff --git a/libgit2/src/attr_file.c b/libgit2/src/attr_file.c
--- a/libgit2/src/attr_file.c
+++ b/libgit2/src/attr_file.c
@@ -1,16 +1,17 @@
 #include "common.h"
 #include "repository.h"
 #include "filebuf.h"
+#include "attr.h"
 #include "git2/blob.h"
 #include "git2/tree.h"
 #include <ctype.h>
 
-const char *git_attr__true  = "[internal]__TRUE__";
-const char *git_attr__false = "[internal]__FALSE__";
-const char *git_attr__unset = "[internal]__UNSET__";
-
 static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
 static void git_attr_rule__clear(git_attr_rule *rule);
+static bool parse_optimized_patterns(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *pattern);
 
 int git_attr_file__new(
 	git_attr_file **attrs_ptr,
@@ -57,13 +58,15 @@
 }
 
 int git_attr_file__parse_buffer(
-	git_repository *repo, const char *buffer, git_attr_file *attrs)
+	git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
 {
 	int error = 0;
 	const char *scan = NULL;
 	char *context = NULL;
 	git_attr_rule *rule = NULL;
 
+	GIT_UNUSED(parsedata);
+
 	assert(buffer && attrs);
 
 	scan = buffer;
@@ -127,7 +130,7 @@
 
 	if (!(error = git_futils_readbuffer(&content, path)))
 		error = git_attr_file__parse_buffer(
-			NULL, git_buf_cstr(&content), *attrs_ptr);
+			NULL, NULL, git_buf_cstr(&content), *attrs_ptr);
 
 	git_buf_free(&content);
 
@@ -139,19 +142,24 @@
 	return error;
 }
 
-void git_attr_file__free(git_attr_file *file)
+void git_attr_file__clear_rules(git_attr_file *file)
 {
 	unsigned int i;
 	git_attr_rule *rule;
 
-	if (!file)
-		return;
-
 	git_vector_foreach(&file->rules, i, rule)
 		git_attr_rule__free(rule);
 
 	git_vector_free(&file->rules);
+}
 
+void git_attr_file__free(git_attr_file *file)
+{
+	if (!file)
+		return;
+
+	git_attr_file__clear_rules(file);
+
 	if (file->pool_is_allocated) {
 		git_pool_clear(file->pool);
 		git__free(file->pool);
@@ -178,7 +186,7 @@
 	const char *attr,
 	const char **value)
 {
-	unsigned int i;
+	size_t i;
 	git_attr_name name;
 	git_attr_rule *rule;
 
@@ -188,9 +196,9 @@
 	name.name_hash = git_attr_file__name_hash(attr);
 
 	git_attr_file__foreach_matching_rule(file, path, i, rule) {
-		int pos = git_vector_bsearch(&rule->assigns, &name);
+		size_t pos;
 
-		if (pos >= 0) {
+		if (!git_vector_bsearch(&pos, &rule->assigns, &name)) {
 			*value = ((git_attr_assignment *)
 					  git_vector_get(&rule->assigns, pos))->value;
 			break;
@@ -206,16 +214,17 @@
 	const git_attr_path *path)
 {
 	int fnm;
+	int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0;
 
 	if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
 		return false;
 
 	if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
-		fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
+		fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags);
 	else if (path->is_dir)
-		fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
+		fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags);
 	else
-		fnm = p_fnmatch(match->pattern, path->basename, 0);
+		fnm = p_fnmatch(match->pattern, path->basename, icase_flags);
 
 	return (fnm == FNM_NOMATCH) ? false : true;
 }
@@ -236,32 +245,30 @@
 git_attr_assignment *git_attr_rule__lookup_assignment(
 	git_attr_rule *rule, const char *name)
 {
-	int pos;
+	size_t pos;
 	git_attr_name key;
 	key.name = name;
 	key.name_hash = git_attr_file__name_hash(name);
 
-	pos = git_vector_bsearch(&rule->assigns, &key);
+	if (git_vector_bsearch(&pos, &rule->assigns, &key))
+		return NULL;
 
-	return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;
+	return git_vector_get(&rule->assigns, pos);
 }
 
 int git_attr_path__init(
 	git_attr_path *info, const char *path, const char *base)
 {
+	ssize_t root;
+
 	/* build full path as best we can */
 	git_buf_init(&info->full, 0);
 
-	if (base != NULL && git_path_root(path) < 0) {
-		if (git_buf_joinpath(&info->full, base, path) < 0)
-			return -1;
-		info->path = info->full.ptr + strlen(base);
-	} else {
-		if (git_buf_sets(&info->full, path) < 0)
-			return -1;
-		info->path = info->full.ptr;
-	}
+	if (git_path_join_unrooted(&info->full, path, base, &root) < 0)
+		return -1;
 
+	info->path = info->full.ptr + root;
+
 	/* remove trailing slashes */
 	while (info->full.size > 0) {
 		if (info->full.ptr[info->full.size - 1] != '/')
@@ -293,7 +300,6 @@
 	info->basename = NULL;
 }
 
-
 /*
  * From gitattributes(5):
  *
@@ -338,10 +344,16 @@
 	const char **base)
 {
 	const char *pattern, *scan;
-	int slash_count;
+	int slash_count, allow_space;
 
 	assert(spec && base && *base);
 
+	if (parse_optimized_patterns(spec, pool, *base))
+		return 0;
+
+	spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
+	allow_space = (spec->flags != 0);
+
 	pattern = *base;
 
 	while (git__isspace(*pattern)) pattern++;
@@ -350,8 +362,6 @@
 		return GIT_ENOTFOUND;
 	}
 
-	spec->flags = 0;
-
 	if (*pattern == '[') {
 		if (strncmp(pattern, "[attr]", 6) == 0) {
 			spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
@@ -368,8 +378,10 @@
 	slash_count = 0;
 	for (scan = pattern; *scan != '\0'; ++scan) {
 		/* scan until (non-escaped) white space */
-		if (git__isspace(*scan) && *(scan - 1) != '\\')
-			break;
+		if (git__isspace(*scan) && *(scan - 1) != '\\') {
+			if (!allow_space || (*scan != ' ' && *scan != '\t'))
+				break;
+		}
 
 		if (*scan == '/') {
 			spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
@@ -418,20 +430,26 @@
 		return -1;
 	} else {
 		/* strip '\' that might have be used for internal whitespace */
-		char *to = spec->pattern;
-		for (scan = spec->pattern; *scan; to++, scan++) {
-			if (*scan == '\\')
-				scan++; /* skip '\' but include next char */
-			if (to != scan)
-				*to = *scan;
-		}
-		if (to != scan) {
-			*to = '\0';
-			spec->length = (to - spec->pattern);
-		}
+		spec->length = git__unescape(spec->pattern);
 	}
 
 	return 0;
+}
+
+static bool parse_optimized_patterns(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *pattern)
+{
+	if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) {
+		spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL;
+		spec->pattern = git_pool_strndup(pool, pattern, 1);
+		spec->length = 1;
+
+		return true;
+	}
+
+	return false;
 }
 
 static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
diff --git a/libgit2/src/attr_file.h b/libgit2/src/attr_file.h
--- a/libgit2/src/attr_file.h
+++ b/libgit2/src/attr_file.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,14 +7,17 @@
 #ifndef INCLUDE_attr_file_h__
 #define INCLUDE_attr_file_h__
 
+#include "git2/oid.h"
 #include "git2/attr.h"
 #include "vector.h"
 #include "pool.h"
 #include "buffer.h"
+#include "fileops.h"
 
 #define GIT_ATTR_FILE			".gitattributes"
 #define GIT_ATTR_FILE_INREPO	"info/attributes"
 #define GIT_ATTR_FILE_SYSTEM	"gitattributes"
+#define GIT_ATTR_FILE_XDG		"attributes"
 
 #define GIT_ATTR_FNMATCH_NEGATIVE	(1U << 0)
 #define GIT_ATTR_FNMATCH_DIRECTORY	(1U << 1)
@@ -22,7 +25,14 @@
 #define GIT_ATTR_FNMATCH_MACRO		(1U << 3)
 #define GIT_ATTR_FNMATCH_IGNORE		(1U << 4)
 #define GIT_ATTR_FNMATCH_HASWILD	(1U << 5)
+#define GIT_ATTR_FNMATCH_ALLOWSPACE	(1U << 6)
+#define GIT_ATTR_FNMATCH_ICASE		(1U << 7)
+#define GIT_ATTR_FNMATCH_MATCH_ALL	(1U << 8)
 
+extern const char *git_attr__true;
+extern const char *git_attr__false;
+extern const char *git_attr__unset;
+
 typedef struct {
 	char *pattern;
 	size_t length;
@@ -48,27 +58,21 @@
 } git_attr_assignment;
 
 typedef struct {
-	git_time_t seconds;
-	git_off_t  size;
-	unsigned int ino;
-} git_attr_file_stat_sig;
-
-typedef struct {
 	char *key;				/* cache "source#path" this was loaded from */
 	git_vector rules;		/* vector of <rule*> or <fnmatch*> */
 	git_pool *pool;
 	bool pool_is_allocated;
 	union {
 		git_oid oid;
-		git_attr_file_stat_sig st;
+		git_futils_filestamp stamp;
 	} cache_data;
 } git_attr_file;
 
 typedef struct {
-	git_buf     full;
-	const char *path;
-	const char *basename;
-	int         is_dir;
+	git_buf  full;
+	char    *path;
+	char    *basename;
+	int      is_dir;
 } git_attr_path;
 
 typedef enum {
@@ -88,8 +92,10 @@
 
 extern void git_attr_file__free(git_attr_file *file);
 
+extern void git_attr_file__clear_rules(git_attr_file *file);
+
 extern int git_attr_file__parse_buffer(
-	git_repository *repo, const char *buf, git_attr_file *file);
+	git_repository *repo, void *parsedata, const char *buf, git_attr_file *file);
 
 extern int git_attr_file__lookup_one(
 	git_attr_file *file,
diff --git a/libgit2/src/attrcache.h b/libgit2/src/attrcache.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/attrcache.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_attrcache_h__
+#define INCLUDE_attrcache_h__
+
+#include "pool.h"
+#include "strmap.h"
+
+typedef struct {
+	int initialized;
+	git_pool pool;
+	git_strmap *files;	/* hash path to git_attr_file of rules */
+	git_strmap *macros;	/* hash name to vector<git_attr_assignment> */
+	const char *cfg_attr_file; /* cached value of core.attributesfile */
+	const char *cfg_excl_file; /* cached value of core.excludesfile */
+} git_attr_cache;
+
+extern int git_attr_cache__init(git_repository *repo);
+
+#endif
diff --git a/libgit2/src/blob.c b/libgit2/src/blob.c
--- a/libgit2/src/blob.c
+++ b/libgit2/src/blob.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,17 +12,18 @@
 #include "common.h"
 #include "blob.h"
 #include "filter.h"
+#include "buf_text.h"
 
-const void *git_blob_rawcontent(git_blob *blob)
+const void *git_blob_rawcontent(const git_blob *blob)
 {
 	assert(blob);
 	return blob->odb_object->raw.data;
 }
 
-size_t git_blob_rawsize(git_blob *blob)
+git_off_t git_blob_rawsize(const git_blob *blob)
 {
 	assert(blob);
-	return blob->odb_object->raw.len;
+	return (git_off_t)blob->odb_object->raw.len;
 }
 
 int git_blob__getbuf(git_buf *buffer, git_blob *blob)
@@ -68,6 +69,7 @@
 	int fd, error;
 	char buffer[4096];
 	git_odb_stream *stream = NULL;
+	ssize_t read_len = -1, written = 0;
 
 	if ((error = git_odb_open_wstream(
 			&stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
@@ -78,20 +80,18 @@
 		return -1;
 	}
 
-	while (!error && file_size > 0) {
-		ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
-
-		if (read_len < 0) {
-			giterr_set(
-				GITERR_OS, "Failed to create blob. Can't read whole file");
-			error = -1;
-		}
-		else if (!(error = stream->write(stream, buffer, read_len)))
-			file_size -= read_len;
+	while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
+		error = stream->write(stream, buffer, read_len);
+		written += read_len;
 	}
 
 	p_close(fd);
 
+	if (written != file_size || read_len < 0) {
+		giterr_set(GITERR_OS, "Failed to read file into stream");
+		error = -1;
+	}
+
 	if (!error)
 		error = stream->finalize_write(oid, stream);
 
@@ -148,27 +148,31 @@
 	return error;
 }
 
-static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
+static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
 {
 	int error;
 	struct stat st;
 	git_odb *odb = NULL;
 	git_off_t size;
 
-	if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
+	assert(hint_path || !try_load_filters);
+
+	if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
 		return error;
 
 	size = st.st_size;
 
 	if (S_ISLNK(st.st_mode)) {
-		error = write_symlink(oid, odb, path, (size_t)size);
+		error = write_symlink(oid, odb, content_path, (size_t)size);
 	} else {
 		git_vector write_filters = GIT_VECTOR_INIT;
-		int filter_count;
+		int filter_count = 0;
 
-		/* Load the filters for writing this file to the ODB */
-		filter_count = git_filters_load(
-			&write_filters, repo, path, GIT_FILTER_TO_ODB);
+		if (try_load_filters) {
+			/* Load the filters for writing this file to the ODB */
+			filter_count = git_filters_load(
+				&write_filters, repo, hint_path, GIT_FILTER_TO_ODB);
+		}
 
 		if (filter_count < 0) {
 			/* Negative value means there was a critical error */
@@ -176,10 +180,10 @@
 		} else if (filter_count == 0) {
 			/* No filters need to be applied to the document: we can stream
 			 * directly from disk */
-			error = write_file_stream(oid, odb, path, size);
+			error = write_file_stream(oid, odb, content_path, size);
 		} else {
 			/* We need to apply one or more filters */
-			error = write_file_filtered(oid, odb, path, &write_filters);
+			error = write_file_filtered(oid, odb, content_path, &write_filters);
 		}
 
 		git_filters_free(&write_filters);
@@ -202,21 +206,25 @@
 	return error;
 }
 
-int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
 {
 	git_buf full_path = GIT_BUF_INIT;
 	const char *workdir;
 	int error;
 
+	if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
+		return error;
+
 	workdir = git_repository_workdir(repo);
-	assert(workdir); /* error to call this on bare repo */
 
 	if (git_buf_joinpath(&full_path, workdir, path) < 0) {
 		git_buf_free(&full_path);
 		return -1;
 	}
 
-	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+	error = blob_create_internal(
+		oid, repo, git_buf_cstr(&full_path),
+		git_buf_cstr(&full_path) + strlen(workdir), true);
 
 	git_buf_free(&full_path);
 	return error;
@@ -226,14 +234,88 @@
 {
 	int error;
 	git_buf full_path = GIT_BUF_INIT;
+	const char *workdir, *hintpath;
 
 	if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
 		git_buf_free(&full_path);
 		return error;
 	}
 
-	error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
+	hintpath = git_buf_cstr(&full_path);
+	workdir  = git_repository_workdir(repo);
 
+	if (workdir && !git__prefixcmp(hintpath, workdir))
+		hintpath += strlen(workdir);
+
+	error = blob_create_internal(
+		oid, repo, git_buf_cstr(&full_path), hintpath, true);
+
 	git_buf_free(&full_path);
 	return error;
+}
+
+#define BUFFER_SIZE 4096
+
+int git_blob_create_fromchunks(
+	git_oid *oid,
+	git_repository *repo,
+	const char *hintpath,
+	int (*source_cb)(char *content, size_t max_length, void *payload),
+	void *payload)
+{
+	int error = -1, read_bytes;
+	char *content = NULL;
+	git_filebuf file = GIT_FILEBUF_INIT;
+	git_buf path = GIT_BUF_INIT;
+
+	if (git_buf_join_n(
+		&path, '/', 3, 
+		git_repository_path(repo),
+		GIT_OBJECTS_DIR, 
+		"streamed") < 0)
+			goto cleanup;
+
+	content = git__malloc(BUFFER_SIZE);
+	GITERR_CHECK_ALLOC(content);
+
+	if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY) < 0)
+		goto cleanup;
+
+	while (1) {
+		read_bytes = source_cb(content, BUFFER_SIZE, payload);
+
+		assert(read_bytes <= BUFFER_SIZE);
+
+		if (read_bytes <= 0)
+			break;
+
+		if (git_filebuf_write(&file, content, read_bytes) < 0)
+			goto cleanup;
+	}
+
+	if (read_bytes < 0)
+		goto cleanup;
+
+	if (git_filebuf_flush(&file) < 0)
+		goto cleanup;
+
+	error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
+
+cleanup:
+	git_buf_free(&path);
+	git_filebuf_cleanup(&file);
+	git__free(content);
+	return error;
+}
+
+int git_blob_is_binary(git_blob *blob)
+{
+	git_buf content;
+
+	assert(blob);
+
+	content.ptr = blob->odb_object->raw.data;
+	content.size = min(blob->odb_object->raw.len, 4000);
+
+	return git_buf_text_is_binary(&content);
 }
diff --git a/libgit2/src/blob.h b/libgit2/src/blob.h
--- a/libgit2/src/blob.h
+++ b/libgit2/src/blob.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/branch.c b/libgit2/src/branch.c
--- a/libgit2/src/branch.c
+++ b/libgit2/src/branch.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,9 +7,13 @@
 
 #include "common.h"
 #include "commit.h"
-#include "branch.h"
 #include "tag.h"
+#include "config.h"
+#include "refspec.h"
+#include "refs.h"
 
+#include "git2/branch.h"
+
 static int retrieve_branch_reference(
 	git_reference **branch_reference_out,
 	git_repository *repo,
@@ -41,168 +45,539 @@
 	return error;
 }
 
-static int create_error_invalid(const char *msg)
+static int not_a_local_branch(const char *reference_name)
 {
-	giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
+	giterr_set(
+		GITERR_INVALID,
+		"Reference '%s' is not a local branch.", reference_name);
 	return -1;
 }
 
 int git_branch_create(
-		git_oid *oid_out,
-		git_repository *repo,
-		const char *branch_name,
-		const git_object *target,
-		int force)
+	git_reference **ref_out,
+	git_repository *repository,
+	const char *branch_name,
+	const git_commit *commit,
+	int force)
 {
-	git_otype target_type = GIT_OBJ_BAD;
-	git_object *commit = NULL;
 	git_reference *branch = NULL;
 	git_buf canonical_branch_name = GIT_BUF_INIT;
 	int error = -1;
 
-	assert(repo && branch_name && target && oid_out);
-
-	if (git_object_owner(target) != repo)
-		return create_error_invalid("The given target does not belong to this repository");
-
-	target_type = git_object_type(target);
-
-	switch (target_type)
-	{
-	case GIT_OBJ_TAG:
-		if (git_tag_peel(&commit, (git_tag *)target) < 0)
-			goto cleanup;
-
-		if (git_object_type(commit) != GIT_OBJ_COMMIT) {
-			create_error_invalid("The given target does not resolve to a commit");
-			goto cleanup;
-		}
-		break;
-
-	case GIT_OBJ_COMMIT:
-		commit = (git_object *)target;
-		break;
-
-	default:
-		return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
-	}
+	assert(branch_name && commit && ref_out);
+	assert(git_object_owner((const git_object *)commit) == repository);
 
 	if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
 		goto cleanup;
 
-	if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
-		goto cleanup;
+	error = git_reference_create(&branch, repository,
+		git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force);
 
-	git_oid_cpy(oid_out, git_reference_oid(branch));
-	error = 0;
+	if (!error)
+		*ref_out = branch;
 
 cleanup:
-	if (target_type == GIT_OBJ_TAG)
-		git_object_free(commit);
-
-	git_reference_free(branch);
 	git_buf_free(&canonical_branch_name);
 	return error;
 }
 
-int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type)
+int git_branch_delete(git_reference *branch)
 {
-	git_reference *branch = NULL;
-	git_reference *head = NULL;
-	int error;
+	int is_head;
+	git_buf config_section = GIT_BUF_INIT;
+	int error = -1;
 
-	assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
+	assert(branch);
 
-	if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
-		return error;
+	if (!git_reference_is_branch(branch) &&
+		!git_reference_is_remote(branch)) {
+		giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch));
+		return -1;
+	}
 
-	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
-		giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
-		goto on_error;
+	if ((is_head = git_branch_is_head(branch)) < 0)
+		return is_head;
+
+	if (is_head) {
+		giterr_set(GITERR_REFERENCE,
+				"Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch));
+		return -1;
 	}
 
-	if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
-		&& (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
-			giterr_set(GITERR_REFERENCE,
-					"Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
+	if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
+		goto on_error;
+
+	if (git_config_rename_section(
+		git_reference_owner(branch), 
+		git_buf_cstr(&config_section),
+		NULL) < 0)
 			goto on_error;
-	}
 
 	if (git_reference_delete(branch) < 0)
 		goto on_error;
 
-	git_reference_free(head);
-	return 0;
+	error = 0;
 
 on_error:
-	git_reference_free(head);
-	git_reference_free(branch);
-	return -1;
+	git_buf_free(&config_section);
+	return error;
 }
 
 typedef struct {
-	git_vector *branchlist;
+	git_branch_foreach_cb branch_cb;
+	void *callback_payload;
 	unsigned int branch_type;
-} branch_filter_data;
+} branch_foreach_filter;
 
-static int branch_list_cb(const char *branch_name, void *payload)
+static int branch_foreach_cb(const char *branch_name, void *payload)
 {
-	branch_filter_data *filter = (branch_filter_data *)payload;
+	branch_foreach_filter *filter = (branch_foreach_filter *)payload;
 
-	if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
-		|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
-		return git_vector_insert(filter->branchlist, git__strdup(branch_name));
+	if (filter->branch_type & GIT_BRANCH_LOCAL &&
+		git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
+		return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload);
 
+	if (filter->branch_type & GIT_BRANCH_REMOTE &&
+		git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0)
+		return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload);
+
 	return 0;
 }
 
-int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
+int git_branch_foreach(
+	git_repository *repo,
+	unsigned int list_flags,
+	git_branch_foreach_cb branch_cb,
+	void *payload)
 {
+	branch_foreach_filter filter;
+
+	filter.branch_cb = branch_cb;
+	filter.branch_type = list_flags;
+	filter.callback_payload = payload;
+
+	return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter);
+}
+
+int git_branch_move(
+	git_reference **out,
+	git_reference *branch,
+	const char *new_branch_name,
+	int force)
+{
+	git_buf new_reference_name = GIT_BUF_INIT,
+		old_config_section = GIT_BUF_INIT,
+		new_config_section = GIT_BUF_INIT;
 	int error;
-	branch_filter_data filter;
-	git_vector branchlist;
 
-	assert(branch_names && repo);
+	assert(branch && new_branch_name);
 
-	if (git_vector_init(&branchlist, 8, NULL) < 0)
+	if (!git_reference_is_branch(branch))
+		return not_a_local_branch(git_reference_name(branch));
+
+	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0 ||
+		(error = git_buf_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 ||
+		(error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0)
+		goto done;
+
+	if ((error = git_config_rename_section(git_reference_owner(branch),
+		git_buf_cstr(&old_config_section),
+		git_buf_cstr(&new_config_section))) < 0)
+		goto done;
+	
+	if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0)
+		goto done;
+
+done:
+	git_buf_free(&new_reference_name);
+	git_buf_free(&old_config_section);
+	git_buf_free(&new_config_section);
+
+	return error;
+}
+
+int git_branch_lookup(
+	git_reference **ref_out,
+	git_repository *repo,
+	const char *branch_name,
+	git_branch_t branch_type)
+{
+	assert(ref_out && repo && branch_name);
+
+	return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
+}
+
+int git_branch_name(const char **out, git_reference *ref)
+{
+	const char *branch_name;
+
+	assert(out && ref);
+
+	branch_name = ref->name;
+
+	if (git_reference_is_branch(ref)) {
+		branch_name += strlen(GIT_REFS_HEADS_DIR);
+	} else if (git_reference_is_remote(ref)) {
+		branch_name += strlen(GIT_REFS_REMOTES_DIR);
+	} else {
+		giterr_set(GITERR_INVALID,
+				"Reference '%s' is neither a local nor a remote branch.", ref->name);
 		return -1;
+	}
+	*out = branch_name;
+	return 0;
+}
 
-	filter.branchlist = &branchlist;
-	filter.branch_type = list_flags;
+static int retrieve_upstream_configuration(
+	const char **out,
+	git_repository *repo,
+	const char *canonical_branch_name,
+	const char *format)
+{
+	git_config *config;
+	git_buf buf = GIT_BUF_INIT;
+	int error;
 
-	error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter);
-	if (error < 0) {
-		git_vector_free(&branchlist);
+	if (git_repository_config__weakptr(&config, repo) < 0)
 		return -1;
+
+	if (git_buf_printf(&buf, format,
+		canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
+			return -1;
+
+	error = git_config_get_string(out, config, git_buf_cstr(&buf));
+	git_buf_free(&buf);
+	return error;
+}
+
+int git_branch_upstream__name(
+	git_buf *tracking_name,
+	git_repository *repo,
+	const char *canonical_branch_name)
+{
+	const char *remote_name, *merge_name;
+	git_buf buf = GIT_BUF_INIT;
+	int error = -1;
+	git_remote *remote = NULL;
+	const git_refspec *refspec;
+
+	assert(tracking_name && canonical_branch_name);
+
+	if (!git_reference__is_branch(canonical_branch_name))
+		return not_a_local_branch(canonical_branch_name);
+
+	if ((error = retrieve_upstream_configuration(
+		&remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0)
+			goto cleanup;
+
+	if ((error = retrieve_upstream_configuration(
+		&merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0)
+			goto cleanup;
+
+	if (!*remote_name || !*merge_name) {
+		error = GIT_ENOTFOUND;
+		goto cleanup;
 	}
 
-	branch_names->strings = (char **)branchlist.contents;
-	branch_names->count = branchlist.length;
-	return 0;
+	if (strcmp(".", remote_name) != 0) {
+		if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
+			goto cleanup;
+
+		refspec = git_remote_fetchspec(remote);
+		if (refspec == NULL
+			|| refspec->src == NULL
+			|| refspec->dst == NULL) {
+				error = GIT_ENOTFOUND;
+				goto cleanup;
+		}
+
+		if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
+			goto cleanup;
+	} else
+		if (git_buf_sets(&buf, merge_name) < 0)
+			goto cleanup;
+
+	error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf));
+
+cleanup:
+	git_remote_free(remote);
+	git_buf_free(&buf);
+	return error;
 }
 
-int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
+static int remote_name(git_buf *buf, git_repository *repo, const char *canonical_branch_name)
 {
-	git_reference *reference = NULL;
-	git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
+	git_strarray remote_list = {0};
+	size_t i;
+	git_remote *remote;
+	const git_refspec *fetchspec;
 	int error = 0;
+	char *remote_name = NULL;
 
-	if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
+	assert(buf && repo && canonical_branch_name);
+
+	/* Verify that this is a remote branch */
+	if (!git_reference__is_remote(canonical_branch_name)) {
+		giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
+			canonical_branch_name);
+		error = GIT_ERROR;
 		goto cleanup;
+	}
 
-	/* We need to be able to return GIT_ENOTFOUND */
-	if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
+	/* Get the remotes */
+	if ((error = git_remote_list(&remote_list, repo)) < 0)
 		goto cleanup;
 
-	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
+	/* Find matching remotes */
+	for (i = 0; i < remote_list.count; i++) {
+		if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
+			continue;
+
+		fetchspec = git_remote_fetchspec(remote);
+
+		/* Defensivly check that we have a fetchspec */
+		if (fetchspec &&
+			git_refspec_dst_matches(fetchspec, canonical_branch_name)) {
+			/* If we have not already set out yet, then set
+			 * it to the matching remote name. Otherwise
+			 * multiple remotes match this reference, and it
+			 * is ambiguous. */
+			if (!remote_name) {
+				remote_name = remote_list.strings[i];
+			} else {
+				git_remote_free(remote);
+				error = GIT_EAMBIGUOUS;
+				goto cleanup;
+			}
+		}
+
+		git_remote_free(remote);
+	}
+
+	if (remote_name) {
+		git_buf_clear(buf);
+		error = git_buf_puts(buf, remote_name);
+	} else {
+		error = GIT_ENOTFOUND;
+	}
+
+cleanup:
+	git_strarray_free(&remote_list);
+	return error;
+}
+
+int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo, const char *refname)
+{
+	int ret;
+	git_buf buf = GIT_BUF_INIT;
+
+	if ((ret = remote_name(&buf, repo, refname)) < 0)
+		return ret;
+
+	if (buffer)
+		git_buf_copy_cstr(buffer, buffer_len, &buf);
+
+	ret = git_buf_len(&buf) + 1;
+	git_buf_free(&buf);
+
+	return ret;
+}
+
+int git_branch_upstream_name(
+	char *tracking_branch_name_out,
+	size_t buffer_size,
+	git_repository *repo,
+	const char *canonical_branch_name)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int error;
+
+	assert(canonical_branch_name);
+
+	if (tracking_branch_name_out && buffer_size)
+		*tracking_branch_name_out = '\0';
+
+	if ((error = git_branch_upstream__name(
+		&buf, repo, canonical_branch_name)) < 0)
+			goto cleanup;
+
+	if (tracking_branch_name_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */
+		giterr_set(
+			GITERR_INVALID,
+			"Buffer too short to hold the tracked reference name.");
+		error = -1;
 		goto cleanup;
+	}
 
-	error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
+	if (tracking_branch_name_out)
+		git_buf_copy_cstr(tracking_branch_name_out, buffer_size, &buf);
 
+	error = (int)buf.size + 1;
+
 cleanup:
-	git_reference_free(reference);
-	git_buf_free(&old_reference_name);
-	git_buf_free(&new_reference_name);
+	git_buf_free(&buf);
+	return (int)error;
+}
 
+int git_branch_upstream(
+		git_reference **tracking_out,
+		git_reference *branch)
+{
+	int error;
+	git_buf tracking_name = GIT_BUF_INIT;
+
+	if ((error = git_branch_upstream__name(&tracking_name,
+		git_reference_owner(branch), git_reference_name(branch))) < 0)
+			return error;
+
+	error = git_reference_lookup(
+		tracking_out,
+		git_reference_owner(branch),
+		git_buf_cstr(&tracking_name));
+
+	git_buf_free(&tracking_name);
 	return error;
+}
+
+static int unset_upstream(git_config *config, const char *shortname)
+{
+	git_buf buf = GIT_BUF_INIT;
+
+	if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
+		return -1;
+
+	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
+		goto on_error;
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
+		goto on_error;
+
+	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
+		goto on_error;
+
+	git_buf_free(&buf);
+	return 0;
+
+on_error:
+	git_buf_free(&buf);
+	return -1;
+}
+
+int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
+{
+	git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
+	git_reference *upstream;
+	git_repository *repo;
+	git_remote *remote = NULL;
+	git_config *config;
+	const char *name, *shortname;
+	int local;
+	const git_refspec *fetchspec;
+
+	name = git_reference_name(branch);
+	if (!git_reference__is_branch(name))
+		return not_a_local_branch(name);
+
+	if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
+		return -1;
+
+	shortname = name + strlen(GIT_REFS_HEADS_DIR);
+
+	if (upstream_name == NULL)
+		return unset_upstream(config, shortname);
+
+	repo = git_reference_owner(branch);
+
+	/* First we need to figure out whether it's a branch or remote-tracking */
+	if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
+		local = 1;
+	else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
+		local = 0;
+	else
+		return GIT_ENOTFOUND;
+
+	/*
+	 * If it's local, the remote is "." and the branch name is
+	 * simply the refname. Otherwise we need to figure out what
+	 * the remote-tracking branch's name on the remote is and use
+	 * that.
+	 */
+	if (local)
+		git_buf_puts(&value, ".");
+	else
+		remote_name(&value, repo, git_reference_name(upstream));
+
+	if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
+		goto on_error;
+
+	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+		goto on_error;
+
+	if (local) {
+		if (git_buf_puts(&value, git_reference_name(branch)) < 0)
+			goto on_error;
+	} else {
+		/* Get the remoe-tracking branch's refname in its repo */
+		if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
+			goto on_error;
+
+		fetchspec = git_remote_fetchspec(remote);
+		git_buf_clear(&value);
+		if (git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
+			goto on_error;
+
+		git_remote_free(remote);
+		remote = NULL;
+	}
+
+	git_buf_clear(&key);
+	if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
+		goto on_error;
+
+	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
+		goto on_error;
+
+	git_reference_free(upstream);
+	git_buf_free(&key);
+	git_buf_free(&value);
+
+	return 0;
+
+on_error:
+	git_reference_free(upstream);
+	git_buf_free(&key);
+	git_buf_free(&value);
+	git_remote_free(remote);
+
+	return -1;
+}
+
+int git_branch_is_head(
+		git_reference *branch)
+{
+	git_reference *head;
+	bool is_same = false;
+	int error;
+
+	assert(branch);
+
+	if (!git_reference_is_branch(branch))
+		return false;
+
+	error = git_repository_head(&head, git_reference_owner(branch));
+
+	if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
+		return false;
+
+	if (error < 0)
+		return -1;
+
+	is_same = strcmp(
+		git_reference_name(branch),
+		git_reference_name(head)) == 0;
+
+	git_reference_free(head);
+
+	return is_same;
 }
diff --git a/libgit2/src/branch.h b/libgit2/src/branch.h
--- a/libgit2/src/branch.h
+++ b/libgit2/src/branch.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,11 +7,11 @@
 #ifndef INCLUDE_branch_h__
 #define INCLUDE_branch_h__
 
-#include "git2/branch.h"
+#include "buffer.h"
 
-struct git_branch {
-	char *remote; /* TODO: Make this a git_remote */
-	char *merge;
-};
+int git_branch_upstream__name(
+	git_buf *tracking_name,
+	git_repository *repo,
+	const char *canonical_branch_name);
 
 #endif
diff --git a/libgit2/src/bswap.h b/libgit2/src/bswap.h
--- a/libgit2/src/bswap.h
+++ b/libgit2/src/bswap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/buf_text.c b/libgit2/src/buf_text.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/buf_text.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "buf_text.h"
+
+int git_buf_text_puts_escaped(
+	git_buf *buf,
+	const char *string,
+	const char *esc_chars,
+	const char *esc_with)
+{
+	const char *scan;
+	size_t total = 0, esc_len = strlen(esc_with), count;
+
+	if (!string)
+		return 0;
+
+	for (scan = string; *scan; ) {
+		/* count run of non-escaped characters */
+		count = strcspn(scan, esc_chars);
+		total += count;
+		scan += count;
+		/* count run of escaped characters */
+		count = strspn(scan, esc_chars);
+		total += count * (esc_len + 1);
+		scan += count;
+	}
+
+	if (git_buf_grow(buf, buf->size + total + 1) < 0)
+		return -1;
+
+	for (scan = string; *scan; ) {
+		count = strcspn(scan, esc_chars);
+
+		memmove(buf->ptr + buf->size, scan, count);
+		scan += count;
+		buf->size += count;
+
+		for (count = strspn(scan, esc_chars); count > 0; --count) {
+			/* copy escape sequence */
+			memmove(buf->ptr + buf->size, esc_with, esc_len);
+			buf->size += esc_len;
+			/* copy character to be escaped */
+			buf->ptr[buf->size] = *scan;
+			buf->size++;
+			scan++;
+		}
+	}
+
+	buf->ptr[buf->size] = '\0';
+
+	return 0;
+}
+
+void git_buf_text_unescape(git_buf *buf)
+{
+	buf->size = git__unescape(buf->ptr);
+}
+
+int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src)
+{
+	const char *scan = src->ptr;
+	const char *scan_end = src->ptr + src->size;
+	const char *next = memchr(scan, '\r', src->size);
+	char *out;
+
+	assert(tgt != src);
+
+	if (!next)
+		return GIT_ENOTFOUND;
+
+	/* reduce reallocs while in the loop */
+	if (git_buf_grow(tgt, src->size) < 0)
+		return -1;
+	out = tgt->ptr;
+	tgt->size = 0;
+
+	/* Find the next \r and copy whole chunk up to there to tgt */
+	for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
+		if (next > scan) {
+			size_t copylen = next - scan;
+			memcpy(out, scan, copylen);
+			out += copylen;
+		}
+
+		/* Do not drop \r unless it is followed by \n */
+		if (next[1] != '\n')
+			*out++ = '\r';
+	}
+
+	/* Copy remaining input into dest */
+	memcpy(out, scan, scan_end - scan + 1); /* +1 for NUL byte */
+	out += (scan_end - scan);
+	tgt->size = out - tgt->ptr;
+
+	return 0;
+}
+
+int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src)
+{
+	const char *start = src->ptr;
+	const char *end = start + src->size;
+	const char *scan = start;
+	const char *next = memchr(scan, '\n', src->size);
+
+	assert(tgt != src);
+
+	if (!next)
+		return GIT_ENOTFOUND;
+
+	/* attempt to reduce reallocs while in the loop */
+	if (git_buf_grow(tgt, src->size + (src->size >> 4) + 1) < 0)
+		return -1;
+	tgt->size = 0;
+
+	for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
+		size_t copylen = next - scan;
+		/* don't convert existing \r\n to \r\r\n */
+		size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2;
+		size_t needsize = tgt->size + copylen + extralen + 1;
+
+		if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0)
+			return -1;
+
+		if (next > scan) {
+			memcpy(tgt->ptr + tgt->size, scan, copylen);
+			tgt->size += copylen;
+		}
+		if (extralen == 2)
+			tgt->ptr[tgt->size++] = '\r';
+		tgt->ptr[tgt->size++] = '\n';
+	}
+
+	return git_buf_put(tgt, scan, end - scan);
+}
+
+int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
+{
+	size_t i;
+	const char *str, *pfx;
+
+	git_buf_clear(buf);
+
+	if (!strings || !strings->count)
+		return 0;
+
+	/* initialize common prefix to first string */
+	if (git_buf_sets(buf, strings->strings[0]) < 0)
+		return -1;
+
+	/* go through the rest of the strings, truncating to shared prefix */
+	for (i = 1; i < strings->count; ++i) {
+
+		for (str = strings->strings[i], pfx = buf->ptr;
+			 *str && *str == *pfx; str++, pfx++)
+			/* scanning */;
+
+		git_buf_truncate(buf, pfx - buf->ptr);
+
+		if (!buf->size)
+			break;
+	}
+
+	return 0;
+}
+
+bool git_buf_text_is_binary(const git_buf *buf)
+{
+	const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+	int printable = 0, nonprintable = 0;
+
+	while (scan < end) {
+		unsigned char c = *scan++;
+
+		if (c > 0x1F && c < 0x7F)
+			printable++;
+		else if (c == '\0')
+			return true;
+		else if (!git__isspace(c))
+			nonprintable++;
+	}
+
+	return ((printable >> 7) < nonprintable);
+}
+
+bool git_buf_text_contains_nul(const git_buf *buf)
+{
+	return (memchr(buf->ptr, '\0', buf->size) != NULL);
+}
+
+int git_buf_text_detect_bom(git_bom_t *bom, const git_buf *buf, size_t offset)
+{
+	const char *ptr;
+	size_t len;
+
+	*bom = GIT_BOM_NONE;
+	/* need at least 2 bytes after offset to look for any BOM */
+	if (buf->size < offset + 2)
+		return 0;
+
+	ptr = buf->ptr + offset;
+	len = buf->size - offset;
+
+	switch (*ptr++) {
+	case 0:
+		if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
+			*bom = GIT_BOM_UTF32_BE;
+			return 4;
+		}
+		break;
+	case '\xEF':
+		if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
+			*bom = GIT_BOM_UTF8;
+			return 3;
+		}
+		break;
+	case '\xFE':
+		if (*ptr == '\xFF') {
+			*bom = GIT_BOM_UTF16_BE;
+			return 2;
+		}
+		break;
+	case '\xFF':
+		if (*ptr != '\xFE')
+			break;
+		if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
+			*bom = GIT_BOM_UTF32_LE;
+			return 4;
+		} else {
+			*bom = GIT_BOM_UTF16_LE;
+			return 2;
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+bool git_buf_text_gather_stats(
+	git_buf_text_stats *stats, const git_buf *buf, bool skip_bom)
+{
+	const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+	int skip;
+
+	memset(stats, 0, sizeof(*stats));
+
+	/* BOM detection */
+	skip = git_buf_text_detect_bom(&stats->bom, buf, 0);
+	if (skip_bom)
+		scan += skip;
+
+	/* Ignore EOF character */
+	if (buf->size > 0 && end[-1] == '\032')
+		end--;
+
+	/* Counting loop */
+	while (scan < end) {
+		unsigned char c = *scan++;
+
+		if ((c > 0x1F && c < 0x7F) || c > 0x9f)
+			stats->printable++;
+		else switch (c) {
+			case '\0':
+				stats->nul++;
+				stats->nonprintable++;
+				break;
+			case '\n':
+				stats->lf++;
+				break;
+			case '\r':
+				stats->cr++;
+				if (scan < end && *scan == '\n')
+					stats->crlf++;
+				break;
+			case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
+				stats->printable++;
+				break;
+			default:
+				stats->nonprintable++;
+				break;
+			}
+	}
+
+	return (stats->nul > 0 ||
+		((stats->printable >> 7) < stats->nonprintable));
+}
diff --git a/libgit2/src/buf_text.h b/libgit2/src/buf_text.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/buf_text.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_buf_text_h__
+#define INCLUDE_buf_text_h__
+
+#include "buffer.h"
+
+typedef enum {
+	GIT_BOM_NONE = 0,
+	GIT_BOM_UTF8 = 1,
+	GIT_BOM_UTF16_LE = 2,
+	GIT_BOM_UTF16_BE = 3,
+	GIT_BOM_UTF32_LE = 4,
+	GIT_BOM_UTF32_BE = 5
+} git_bom_t;
+
+typedef struct {
+	git_bom_t bom; /* BOM found at head of text */
+	unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */
+	unsigned int printable, nonprintable; /* These are just approximations! */
+} git_buf_text_stats;
+
+/**
+ * Append string to buffer, prefixing each character from `esc_chars` with
+ * `esc_with` string.
+ *
+ * @param buf Buffer to append data to
+ * @param string String to escape and append
+ * @param esc_chars Characters to be escaped
+ * @param esc_with String to insert in from of each found character
+ * @return 0 on success, <0 on failure (probably allocation problem)
+ */
+extern int git_buf_text_puts_escaped(
+	git_buf *buf,
+	const char *string,
+	const char *esc_chars,
+	const char *esc_with);
+
+/**
+ * Append string escaping characters that are regex special
+ */
+GIT_INLINE(int) git_buf_text_puts_escape_regex(git_buf *buf, const char *string)
+{
+	return git_buf_text_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
+}
+
+/**
+ * Unescape all characters in a buffer in place
+ *
+ * I.e. remove backslashes
+ */
+extern void git_buf_text_unescape(git_buf *buf);
+
+/**
+ * Replace all \r\n with \n (or do nothing if no \r\n are found)
+ *
+ * @return 0 on success, GIT_ENOTFOUND if no \r\n, -1 on memory error
+ */
+extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src);
+
+/**
+ * Replace all \n with \r\n (or do nothing if no \n are found)
+ *
+ * @return 0 on success, GIT_ENOTFOUND if no \n, -1 on memory error
+ */
+extern int git_buf_text_lf_to_crlf(git_buf *tgt, const git_buf *src);
+
+/**
+ * Fill buffer with the common prefix of a array of strings
+ *
+ * Buffer will be set to empty if there is no common prefix
+ */
+extern int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strs);
+
+/**
+ * Check quickly if buffer looks like it contains binary data
+ *
+ * @param buf Buffer to check
+ * @return true if buffer looks like non-text data
+ */
+extern bool git_buf_text_is_binary(const git_buf *buf);
+
+/**
+ * Check quickly if buffer contains a NUL byte
+ *
+ * @param buf Buffer to check
+ * @return true if buffer contains a NUL byte
+ */
+extern bool git_buf_text_contains_nul(const git_buf *buf);
+
+/**
+ * Check if a buffer begins with a UTF BOM
+ *
+ * @param bom Set to the type of BOM detected or GIT_BOM_NONE
+ * @param buf Buffer in which to check the first bytes for a BOM
+ * @param offset Offset into buffer to look for BOM
+ * @return Number of bytes of BOM data (or 0 if no BOM found)
+ */
+extern int git_buf_text_detect_bom(
+	git_bom_t *bom, const git_buf *buf, size_t offset);
+
+/**
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with counts of unreadable characters, carriage
+ * returns, etc, so it can be used in heuristics.  This automatically skips
+ * a trailing EOF (\032 character).  Also it will look for a BOM at the
+ * start of the text and can be told to skip that as well.
+ *
+ * @param stats Structure to be filled in
+ * @param buf Text to process
+ * @param skip_bom Exclude leading BOM from stats if true
+ * @return Does the buffer heuristically look like binary data
+ */
+extern bool git_buf_text_gather_stats(
+	git_buf_text_stats *stats, const git_buf *buf, bool skip_bom);
+
+#endif
diff --git a/libgit2/src/buffer.c b/libgit2/src/buffer.c
--- a/libgit2/src/buffer.c
+++ b/libgit2/src/buffer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -31,15 +31,7 @@
 		git_buf_grow(buf, initial_size);
 }
 
-int git_buf_grow(git_buf *buf, size_t target_size)
-{
-	int error = git_buf_try_grow(buf, target_size);
-	if (error != 0)
-		buf->ptr = git_buf__oom;
-	return error;
-}
-
-int git_buf_try_grow(git_buf *buf, size_t target_size)
+int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
 {
 	char *new_ptr;
 	size_t new_size;
@@ -67,8 +59,12 @@
 	new_size = (new_size + 7) & ~7;
 
 	new_ptr = git__realloc(new_ptr, new_size);
-	if (!new_ptr)
+
+	if (!new_ptr) {
+		if (mark_oom)
+			buf->ptr = git_buf__oom;
 		return -1;
+	}
 
 	buf->asize = new_size;
 	buf->ptr   = new_ptr;
@@ -141,11 +137,52 @@
 	return git_buf_put(buf, string, strlen(string));
 }
 
+static const char b64str[64] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
+{
+	size_t extra = len % 3;
+	uint8_t *write, a, b, c;
+	const uint8_t *read = (const uint8_t *)data;
+
+	ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
+	write = (uint8_t *)&buf->ptr[buf->size];
+
+	/* convert each run of 3 bytes into 4 output bytes */
+	for (len -= extra; len > 0; len -= 3) {
+		a = *read++;
+		b = *read++;
+		c = *read++;
+
+		*write++ = b64str[a >> 2];
+		*write++ = b64str[(a & 0x03) << 4 | b >> 4];
+		*write++ = b64str[(b & 0x0f) << 2 | c >> 6];
+		*write++ = b64str[c & 0x3f];
+	}
+
+	if (extra > 0) {
+		a = *read++;
+		b = (extra > 1) ? *read++ : 0;
+
+		*write++ = b64str[a >> 2];
+		*write++ = b64str[(a & 0x03) << 4 | b >> 4];
+		*write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
+		*write++ = '=';
+	}
+
+	buf->size = ((char *)write) - buf->ptr;
+	buf->ptr[buf->size] = '\0';
+
+	return 0;
+}
+
 int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
 {
 	int len;
+	const size_t expected_size = buf->size + (strlen(format) * 2);
 
-	ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
+	ENSURE_SIZE(buf, expected_size);
 
 	while (1) {
 		va_list args;
@@ -411,51 +448,30 @@
 		(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
 }
 
-int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
+int git_buf_splice(
+	git_buf *buf,
+	size_t where,
+	size_t nb_to_remove,
+	const char *data,
+	size_t nb_to_insert)
 {
-	size_t i;
-	const char *str, *pfx;
-
-	git_buf_clear(buf);
-
-	if (!strings || !strings->count)
-		return 0;
+	assert(buf &&
+		where <= git_buf_len(buf) &&
+		where + nb_to_remove <= git_buf_len(buf));
 
-	/* initialize common prefix to first string */
-	if (git_buf_sets(buf, strings->strings[0]) < 0)
+	/* Ported from git.git
+	 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
+	 */
+	if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0)
 		return -1;
 
-	/* go through the rest of the strings, truncating to shared prefix */
-	for (i = 1; i < strings->count; ++i) {
-
-		for (str = strings->strings[i], pfx = buf->ptr;
-			 *str && *str == *pfx; str++, pfx++)
-			/* scanning */;
-
-		git_buf_truncate(buf, pfx - buf->ptr);
+	memmove(buf->ptr + where + nb_to_insert,
+			buf->ptr + where + nb_to_remove,
+			buf->size - where - nb_to_remove);
 
-		if (!buf->size)
-			break;
-	}
+	memcpy(buf->ptr + where, data, nb_to_insert);
 
+	buf->size = buf->size + nb_to_insert - nb_to_remove;
+	buf->ptr[buf->size] = '\0';
 	return 0;
 }
-
-bool git_buf_is_binary(const git_buf *buf)
-{
-	size_t i;
-	int printable = 0, nonprintable = 0;
-
-	for (i = 0; i < buf->size; i++) {
-		unsigned char c = buf->ptr[i];
-		if (c > 0x1F && c < 0x7F)
-			printable++;
-		else if (c == '\0')
-			return true;
-		else if (!git__isspace(c))
-			nonprintable++;
-	}
-
-	return ((printable >> 7) < nonprintable);
-}
-
diff --git a/libgit2/src/buffer.h b/libgit2/src/buffer.h
--- a/libgit2/src/buffer.h
+++ b/libgit2/src/buffer.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,6 +8,7 @@
 #define INCLUDE_buffer_h__
 
 #include "common.h"
+#include "git2/strarray.h"
 #include <stdarg.h>
 
 typedef struct {
@@ -26,30 +27,35 @@
  * For the cases where GIT_BUF_INIT cannot be used to do static
  * initialization.
  */
-void git_buf_init(git_buf *buf, size_t initial_size);
+extern void git_buf_init(git_buf *buf, size_t initial_size);
 
 /**
- * Grow the buffer to hold at least `target_size` bytes.
+ * Attempt to grow the buffer to hold at least `target_size` bytes.
  *
- * If the allocation fails, this will return an error and the buffer
- * will be marked as invalid for future operations.  The existing
- * contents of the buffer will be preserved however.
- * @return 0 on success or -1 on failure
+ * If the allocation fails, this will return an error.  If mark_oom is true,
+ * this will mark the buffer as invalid for future operations; if false,
+ * existing buffer content will be preserved, but calling code must handle
+ * that buffer was not expanded.
  */
-int git_buf_grow(git_buf *buf, size_t target_size);
+extern int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom);
 
 /**
- * Attempt to grow the buffer to hold at least `target_size` bytes.
+ * Grow the buffer to hold at least `target_size` bytes.
  *
- * This is just like `git_buf_grow` except that even if the allocation
- * fails, the git_buf will still be left in a valid state.
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding contents.
+ *
+ * @return 0 on success or -1 on failure
  */
-int git_buf_try_grow(git_buf *buf, size_t target_size);
+GIT_INLINE(int) git_buf_grow(git_buf *buf, size_t target_size)
+{
+	return git_buf_try_grow(buf, target_size, true);
+}
 
-void git_buf_free(git_buf *buf);
-void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
-char *git_buf_detach(git_buf *buf);
-void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
+extern void git_buf_free(git_buf *buf);
+extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
+extern char *git_buf_detach(git_buf *buf);
+extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
 
 /**
  * Test if there have been any reallocation failures with this git_buf.
@@ -113,7 +119,7 @@
 
 #define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
 
-GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
+GIT_INLINE(ssize_t) git_buf_rfind_next(const git_buf *buf, char ch)
 {
 	ssize_t idx = (ssize_t)buf->size - 1;
 	while (idx >= 0 && buf->ptr[idx] == ch) idx--;
@@ -121,15 +127,50 @@
 	return idx;
 }
 
+GIT_INLINE(ssize_t) git_buf_rfind(const git_buf *buf, char ch)
+{
+	ssize_t idx = (ssize_t)buf->size - 1;
+	while (idx >= 0 && buf->ptr[idx] != ch) idx--;
+	return idx;
+}
+
+GIT_INLINE(ssize_t) git_buf_find(const git_buf *buf, char ch)
+{
+	void *found = memchr(buf->ptr, ch, buf->size);
+	return found ? (ssize_t)((const char *)found - buf->ptr) : -1;
+}
+
 /* Remove whitespace from the end of the buffer */
 void git_buf_rtrim(git_buf *buf);
 
 int git_buf_cmp(const git_buf *a, const git_buf *b);
 
-/* Fill buf with the common prefix of a array of strings */
-int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
+/* Write data as base64 encoded in buffer */
+int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
 
-/* Check if buffer looks like it contains binary data */
-bool git_buf_is_binary(const git_buf *buf);
+/*
+ * Insert, remove or replace a portion of the buffer.
+ *
+ * @param buf The buffer to work with
+ *
+ * @param where The location in the buffer where the transformation
+ * should be applied.
+ *
+ * @param nb_to_remove The number of chars to be removed. 0 to not
+ * remove any character in the buffer.
+ *
+ * @param data A pointer to the data which should be inserted.
+ *
+ * @param nb_to_insert The number of chars to be inserted. 0 to not
+ * insert any character from the buffer.
+ *
+ * @return 0 or an error code.
+ */
+int git_buf_splice(
+	git_buf *buf,
+	size_t where,
+	size_t nb_to_remove,
+	const char *data,
+	size_t nb_to_insert);
 
 #endif
diff --git a/libgit2/src/cache.c b/libgit2/src/cache.c
--- a/libgit2/src/cache.c
+++ b/libgit2/src/cache.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,6 +11,7 @@
 #include "thread-utils.h"
 #include "util.h"
 #include "cache.h"
+#include "git2/oid.h"
 
 int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
 {
@@ -40,6 +41,7 @@
 			git_cached_obj_decref(cache->nodes[i], cache->free_obj);
 	}
 
+	git_mutex_free(&cache->lock);
 	git__free(cache->nodes);
 }
 
@@ -50,7 +52,11 @@
 
 	memcpy(&hash, oid->id, sizeof(hash));
 
-	git_mutex_lock(&cache->lock);
+	if (git_mutex_lock(&cache->lock)) {
+		giterr_set(GITERR_THREAD, "unable to lock cache mutex");
+		return NULL;
+	}
+
 	{
 		node = cache->nodes[hash & cache->size_mask];
 
@@ -69,16 +75,20 @@
 	git_cached_obj *entry = _entry;
 	uint32_t hash;
 
-	memcpy(&hash, &entry->oid, sizeof(hash));
+	memcpy(&hash, &entry->oid, sizeof(uint32_t));
 
-	/* increase the refcount on this object, because
-	 * the cache now owns it */
-	git_cached_obj_incref(entry);
+	if (git_mutex_lock(&cache->lock)) {
+		giterr_set(GITERR_THREAD, "unable to lock cache mutex");
+		return NULL;
+	}
 
-	git_mutex_lock(&cache->lock);
 	{
 		git_cached_obj *node = cache->nodes[hash & cache->size_mask];
 
+		/* increase the refcount on this object, because
+		 * the cache now owns it */
+		git_cached_obj_incref(entry);
+
 		if (node == NULL) {
 			cache->nodes[hash & cache->size_mask] = entry;
 		} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
@@ -88,12 +98,13 @@
 			git_cached_obj_decref(node, cache->free_obj);
 			cache->nodes[hash & cache->size_mask] = entry;
 		}
+
+		/* increase the refcount again, because we are
+		 * returning it to the user */
+		git_cached_obj_incref(entry);
+
 	}
 	git_mutex_unlock(&cache->lock);
-
-	/* increase the refcount again, because we are
-	 * returning it to the user */
-	git_cached_obj_incref(entry);
 
 	return entry;
 }
diff --git a/libgit2/src/cache.h b/libgit2/src/cache.h
--- a/libgit2/src/cache.h
+++ b/libgit2/src/cache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/cc-compat.h b/libgit2/src/cc-compat.h
--- a/libgit2/src/cc-compat.h
+++ b/libgit2/src/cc-compat.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -38,8 +38,10 @@
 /* Define the printf format specifer to use for size_t output */
 #if defined(_MSC_VER) || defined(__MINGW32__)
 #	define PRIuZ "Iu"
+#	define PRIxZ "Ix"
 #else
 #	define PRIuZ "zu"
+#	define PRIxZ "zx"
 #endif
 
 /* Micosoft Visual C/C++ */
diff --git a/libgit2/src/checkout.c b/libgit2/src/checkout.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/checkout.c
@@ -0,0 +1,1383 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <assert.h>
+
+#include "checkout.h"
+
+#include "git2/repository.h"
+#include "git2/refs.h"
+#include "git2/tree.h"
+#include "git2/blob.h"
+#include "git2/config.h"
+#include "git2/diff.h"
+#include "git2/submodule.h"
+
+#include "refs.h"
+#include "repository.h"
+#include "filter.h"
+#include "blob.h"
+#include "diff.h"
+#include "pathspec.h"
+#include "buf_text.h"
+
+/* See docs/checkout-internals.md for more information */
+
+enum {
+	CHECKOUT_ACTION__NONE = 0,
+	CHECKOUT_ACTION__REMOVE = 1,
+	CHECKOUT_ACTION__UPDATE_BLOB = 2,
+	CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
+	CHECKOUT_ACTION__CONFLICT = 8,
+	CHECKOUT_ACTION__MAX = 8,
+	CHECKOUT_ACTION__DEFER_REMOVE = 16,
+	CHECKOUT_ACTION__REMOVE_AND_UPDATE =
+		(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
+};
+
+typedef struct {
+	git_repository *repo;
+	git_diff_list *diff;
+	git_checkout_opts opts;
+	bool opts_free_baseline;
+	char *pfx;
+	git_index *index;
+	git_pool pool;
+	git_vector removes;
+	git_buf path;
+	size_t workdir_len;
+	unsigned int strategy;
+	int can_symlink;
+	bool reload_submodules;
+	size_t total_steps;
+	size_t completed_steps;
+} checkout_data;
+
+static int checkout_notify(
+	checkout_data *data,
+	git_checkout_notify_t why,
+	const git_diff_delta *delta,
+	const git_index_entry *wditem)
+{
+	git_diff_file wdfile;
+	const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL;
+	const char *path = NULL;
+
+	if (!data->opts.notify_cb)
+		return 0;
+
+	if ((why & data->opts.notify_flags) == 0)
+		return 0;
+
+	if (wditem) {
+		memset(&wdfile, 0, sizeof(wdfile));
+
+		git_oid_cpy(&wdfile.oid, &wditem->oid);
+		wdfile.path = wditem->path;
+		wdfile.size = wditem->file_size;
+		wdfile.flags = GIT_DIFF_FLAG_VALID_OID;
+		wdfile.mode = wditem->mode;
+
+		workdir = &wdfile;
+
+		path = wditem->path;
+	}
+
+	if (delta) {
+		switch (delta->status) {
+		case GIT_DELTA_UNMODIFIED:
+		case GIT_DELTA_MODIFIED:
+		case GIT_DELTA_TYPECHANGE:
+		default:
+			baseline = &delta->old_file;
+			target = &delta->new_file;
+			break;
+		case GIT_DELTA_ADDED:
+		case GIT_DELTA_IGNORED:
+		case GIT_DELTA_UNTRACKED:
+			target = &delta->new_file;
+			break;
+		case GIT_DELTA_DELETED:
+			baseline = &delta->old_file;
+			break;
+		}
+
+		path = delta->old_file.path;
+	}
+
+	return data->opts.notify_cb(
+		why, path, baseline, target, workdir, data->opts.notify_payload);
+}
+
+static bool checkout_is_workdir_modified(
+	checkout_data *data,
+	const git_diff_file *baseitem,
+	const git_index_entry *wditem)
+{
+	git_oid oid;
+
+	/* handle "modified" submodule */
+	if (wditem->mode == GIT_FILEMODE_COMMIT) {
+		git_submodule *sm;
+		unsigned int sm_status = 0;
+		const git_oid *sm_oid = NULL;
+
+		if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 ||
+			git_submodule_status(&sm_status, sm) < 0)
+			return true;
+
+		if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
+			return true;
+
+		sm_oid = git_submodule_wd_id(sm);
+		if (!sm_oid)
+			return false;
+
+		return (git_oid_cmp(&baseitem->oid, sm_oid) != 0);
+	}
+
+	/* depending on where base is coming from, we may or may not know
+	 * the actual size of the data, so we can't rely on this shortcut.
+	 */
+	if (baseitem->size && wditem->file_size != baseitem->size)
+		return true;
+
+	if (git_diff__oid_for_file(
+			data->repo, wditem->path, wditem->mode,
+			wditem->file_size, &oid) < 0)
+		return false;
+
+	return (git_oid_cmp(&baseitem->oid, &oid) != 0);
+}
+
+#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
+	((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
+
+static int checkout_action_common(
+	checkout_data *data,
+	int action,
+	const git_diff_delta *delta,
+	const git_index_entry *wd)
+{
+	git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
+
+	if (action <= 0)
+		return action;
+
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+		action = (action & ~CHECKOUT_ACTION__REMOVE);
+
+	if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
+		if (S_ISGITLINK(delta->new_file.mode))
+			action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
+				CHECKOUT_ACTION__UPDATE_SUBMODULE;
+
+		notify = GIT_CHECKOUT_NOTIFY_UPDATED;
+	}
+
+	if ((action & CHECKOUT_ACTION__CONFLICT) != 0)
+		notify = GIT_CHECKOUT_NOTIFY_CONFLICT;
+
+	if (notify != GIT_CHECKOUT_NOTIFY_NONE &&
+		checkout_notify(data, notify, delta, wd) != 0)
+		return GIT_EUSER;
+
+	return action;
+}
+
+static int checkout_action_no_wd(
+	checkout_data *data,
+	const git_diff_delta *delta)
+{
+	int action = CHECKOUT_ACTION__NONE;
+
+	switch (delta->status) {
+	case GIT_DELTA_UNMODIFIED: /* case 12 */
+		if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL))
+			return GIT_EUSER;
+		action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
+		break;
+	case GIT_DELTA_ADDED:    /* case 2 or 28 (and 5 but not really) */
+	case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
+		action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+		break;
+	case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
+		if (delta->new_file.mode == GIT_FILEMODE_TREE)
+			action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+		break;
+	case GIT_DELTA_DELETED: /* case 8 or 25 */
+	default: /* impossible */
+		break;
+	}
+
+	return checkout_action_common(data, action, delta, NULL);
+}
+
+static int checkout_action_wd_only(
+	checkout_data *data,
+	git_iterator *workdir,
+	const git_index_entry *wd,
+	git_vector *pathspec)
+{
+	bool remove = false;
+	git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
+
+	if (!git_pathspec_match_path(
+			pathspec, wd->path,
+			(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+			git_iterator_ignore_case(workdir), NULL))
+		return 0;
+
+	/* check if item is tracked in the index but not in the checkout diff */
+	if (data->index != NULL) {
+		if (wd->mode != GIT_FILEMODE_TREE) {
+			int error;
+
+			if ((error = git_index_find(NULL, data->index, wd->path)) == 0) {
+				notify = GIT_CHECKOUT_NOTIFY_DIRTY;
+				remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
+			} else if (error != GIT_ENOTFOUND)
+				return error;
+		} else {
+			/* for tree entries, we have to see if there are any index
+			 * entries that are contained inside that tree
+			 */
+			size_t pos = git_index__prefix_position(data->index, wd->path);
+			const git_index_entry *e = git_index_get_byindex(data->index, pos);
+
+			if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) {
+				notify = GIT_CHECKOUT_NOTIFY_DIRTY;
+				remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
+			}
+		}
+	}
+
+	if (notify != GIT_CHECKOUT_NOTIFY_NONE)
+		/* found in index */;
+	else if (git_iterator_current_is_ignored(workdir)) {
+		notify = GIT_CHECKOUT_NOTIFY_IGNORED;
+		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
+	}
+	else {
+		notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
+		remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
+	}
+
+	if (checkout_notify(data, notify, NULL, wd))
+		return GIT_EUSER;
+
+	if (remove) {
+		char *path = git_pool_strdup(&data->pool, wd->path);
+		GITERR_CHECK_ALLOC(path);
+
+		if (git_vector_insert(&data->removes, path) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static bool submodule_is_config_only(
+	checkout_data *data,
+	const char *path)
+{
+	git_submodule *sm = NULL;
+	unsigned int sm_loc = 0;
+
+	if (git_submodule_lookup(&sm, data->repo, path) < 0 ||
+		git_submodule_location(&sm_loc, sm) < 0 ||
+		sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
+		return true;
+
+	return false;
+}
+
+static int checkout_action_with_wd(
+	checkout_data *data,
+	const git_diff_delta *delta,
+	const git_index_entry *wd)
+{
+	int action = CHECKOUT_ACTION__NONE;
+
+	switch (delta->status) {
+	case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
+		if (checkout_is_workdir_modified(data, &delta->old_file, wd)) {
+			if (checkout_notify(
+					data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
+				return GIT_EUSER;
+			action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
+		}
+		break;
+	case GIT_DELTA_ADDED: /* case 3, 4 or 6 */
+		action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
+		break;
+	case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
+		if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+			action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+		else
+			action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
+		break;
+	case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
+		if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+			action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
+		else
+			action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+		break;
+	case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
+		if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+			if (wd->mode == GIT_FILEMODE_TREE)
+				/* either deleting items in old tree will delete the wd dir,
+				 * or we'll get a conflict when we attempt blob update...
+				 */
+				action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+			else if (wd->mode == GIT_FILEMODE_COMMIT) {
+				/* workdir is possibly a "phantom" submodule - treat as a
+				 * tree if the only submodule info came from the config
+				 */
+				if (submodule_is_config_only(data, wd->path))
+					action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+				else
+					action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+			} else
+				action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+		}
+		else if (checkout_is_workdir_modified(data, &delta->old_file, wd))
+			action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+		else
+			action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
+
+		/* don't update if the typechange is to a tree */
+		if (delta->new_file.mode == GIT_FILEMODE_TREE)
+			action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB);
+		break;
+	default: /* impossible */
+		break;
+	}
+
+	return checkout_action_common(data, action, delta, wd);
+}
+
+static int checkout_action_with_wd_blocker(
+	checkout_data *data,
+	const git_diff_delta *delta,
+	const git_index_entry *wd)
+{
+	int action = CHECKOUT_ACTION__NONE;
+
+	switch (delta->status) {
+	case GIT_DELTA_UNMODIFIED:
+		/* should show delta as dirty / deleted */
+		if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd))
+			return GIT_EUSER;
+		action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
+		break;
+	case GIT_DELTA_ADDED:
+	case GIT_DELTA_MODIFIED:
+		action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+		break;
+	case GIT_DELTA_DELETED:
+		action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+		break;
+	case GIT_DELTA_TYPECHANGE:
+		/* not 100% certain about this... */
+		action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+		break;
+	default: /* impossible */
+		break;
+	}
+
+	return checkout_action_common(data, action, delta, wd);
+}
+
+static int checkout_action_with_wd_dir(
+	checkout_data *data,
+	const git_diff_delta *delta,
+	const git_index_entry *wd)
+{
+	int action = CHECKOUT_ACTION__NONE;
+
+	switch (delta->status) {
+	case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
+		if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) ||
+			checkout_notify(
+				data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
+			return GIT_EUSER;
+		break;
+	case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */
+	case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */
+		if (delta->old_file.mode == GIT_FILEMODE_COMMIT)
+			/* expected submodule (and maybe found one) */;
+		else if (delta->new_file.mode != GIT_FILEMODE_TREE)
+			action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+		break;
+	case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */
+		if (delta->old_file.mode != GIT_FILEMODE_TREE &&
+			checkout_notify(
+				data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd))
+			return GIT_EUSER;
+		break;
+	case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */
+		if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+			/* For typechange from dir, remove dir and add blob, but it is
+			 * not safe to remove dir if it contains modified files.
+			 * However, safely removing child files will remove the parent
+			 * directory if is it left empty, so we can defer removing the
+			 * dir and it will succeed if no children are left.
+			 */
+			action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+			if (action != CHECKOUT_ACTION__NONE)
+				action |= CHECKOUT_ACTION__DEFER_REMOVE;
+		}
+		else if (delta->new_file.mode != GIT_FILEMODE_TREE)
+			/* For typechange to dir, dir is already created so no action */
+			action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+		break;
+	default: /* impossible */
+		break;
+	}
+
+	return checkout_action_common(data, action, delta, wd);
+}
+
+static int checkout_action(
+	checkout_data *data,
+	git_diff_delta *delta,
+	git_iterator *workdir,
+	const git_index_entry **wditem_ptr,
+	git_vector *pathspec)
+{
+	const git_index_entry *wd = *wditem_ptr;
+	int cmp = -1, act;
+	int (*strcomp)(const char *, const char *) = data->diff->strcomp;
+	int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
+
+	/* move workdir iterator to follow along with deltas */
+
+	while (1) {
+		if (!wd)
+			return checkout_action_no_wd(data, delta);
+
+		cmp = strcomp(wd->path, delta->old_file.path);
+
+		/* 1. wd before delta ("a/a" before "a/b")
+		 * 2. wd prefixes delta & should expand ("a/" before "a/b")
+		 * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c")
+		 * 4. wd equals delta ("a/b" and "a/b")
+		 * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b")
+		 * 6. wd after delta ("a/c" after "a/b")
+		 */
+
+		if (cmp < 0) {
+			cmp = pfxcomp(delta->old_file.path, wd->path);
+
+			if (cmp == 0) {
+				if (wd->mode == GIT_FILEMODE_TREE) {
+					/* case 2 - entry prefixed by workdir tree */
+					if (git_iterator_advance_into(&wd, workdir) < 0)
+						goto fail;
+
+					*wditem_ptr = wd;
+					continue;
+				}
+
+				/* case 3 maybe - wd contains non-dir where dir expected */
+				if (delta->old_file.path[strlen(wd->path)] == '/') {
+					act = checkout_action_with_wd_blocker(data, delta, wd);
+					*wditem_ptr =
+						git_iterator_advance(&wd, workdir) ? NULL : wd;
+					return act;
+				}
+			}
+
+			/* case 1 - handle wd item (if it matches pathspec) */
+			if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
+				git_iterator_advance(&wd, workdir) < 0)
+				goto fail;
+
+			*wditem_ptr = wd;
+			continue;
+		}
+
+		if (cmp == 0) {
+			/* case 4 */
+			act = checkout_action_with_wd(data, delta, wd);
+			*wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd;
+			return act;
+		}
+
+		cmp = pfxcomp(wd->path, delta->old_file.path);
+
+		if (cmp == 0) { /* case 5 */
+			if (wd->path[strlen(delta->old_file.path)] != '/')
+				return checkout_action_no_wd(data, delta);
+
+			if (delta->status == GIT_DELTA_TYPECHANGE) {
+				if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+					act = checkout_action_with_wd(data, delta, wd);
+					if (git_iterator_advance_into(&wd, workdir) < 0)
+						wd = NULL;
+					*wditem_ptr = wd;
+					return act;
+				}
+
+				if (delta->new_file.mode == GIT_FILEMODE_TREE ||
+					delta->new_file.mode == GIT_FILEMODE_COMMIT ||
+					delta->old_file.mode == GIT_FILEMODE_COMMIT)
+				{
+					act = checkout_action_with_wd(data, delta, wd);
+					if (git_iterator_advance(&wd, workdir) < 0)
+						wd = NULL;
+					*wditem_ptr = wd;
+					return act;
+				}
+			}
+
+			return checkout_action_with_wd_dir(data, delta, wd);
+		}
+
+		/* case 6 - wd is after delta */
+		return checkout_action_no_wd(data, delta);
+	}
+
+fail:
+	*wditem_ptr = NULL;
+	return -1;
+}
+
+static int checkout_remaining_wd_items(
+	checkout_data *data,
+	git_iterator *workdir,
+	const git_index_entry *wd,
+	git_vector *spec)
+{
+	int error = 0;
+
+	while (wd && !error) {
+		if (!(error = checkout_action_wd_only(data, workdir, wd, spec)))
+			error = git_iterator_advance(&wd, workdir);
+	}
+
+	return error;
+}
+
+static int checkout_get_actions(
+	uint32_t **actions_ptr,
+	size_t **counts_ptr,
+	checkout_data *data,
+	git_iterator *workdir)
+{
+	int error = 0;
+	const git_index_entry *wditem;
+	git_vector pathspec = GIT_VECTOR_INIT, *deltas;
+	git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
+	git_diff_delta *delta;
+	size_t i, *counts = NULL;
+	uint32_t *actions = NULL;
+
+	if (data->opts.paths.count > 0 &&
+		git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
+		return -1;
+
+	if ((error = git_iterator_current(&wditem, workdir)) < 0)
+		goto fail;
+
+	deltas = &data->diff->deltas;
+
+	*counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
+	*actions_ptr = actions = git__calloc(
+		deltas->length ? deltas->length : 1, sizeof(uint32_t));
+	if (!counts || !actions) {
+		error = -1;
+		goto fail;
+	}
+
+	git_vector_foreach(deltas, i, delta) {
+		int act = checkout_action(data, delta, workdir, &wditem, &pathspec);
+
+		if (act < 0) {
+			error = act;
+			goto fail;
+		}
+
+		actions[i] = act;
+
+		if (act & CHECKOUT_ACTION__REMOVE)
+			counts[CHECKOUT_ACTION__REMOVE]++;
+		if (act & CHECKOUT_ACTION__UPDATE_BLOB)
+			counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
+		if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
+			counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
+		if (act & CHECKOUT_ACTION__CONFLICT)
+			counts[CHECKOUT_ACTION__CONFLICT]++;
+	}
+
+	error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec);
+	if (error < 0)
+		goto fail;
+
+	counts[CHECKOUT_ACTION__REMOVE] += data->removes.length;
+
+	if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
+		(data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0)
+	{
+		giterr_set(GITERR_CHECKOUT, "%d conflicts prevent checkout",
+			(int)counts[CHECKOUT_ACTION__CONFLICT]);
+		error = GIT_EMERGECONFLICT;
+		goto fail;
+	}
+
+	git_pathspec_free(&pathspec);
+	git_pool_clear(&pathpool);
+
+	return 0;
+
+fail:
+	*counts_ptr = NULL;
+	git__free(counts);
+	*actions_ptr = NULL;
+	git__free(actions);
+
+	git_pathspec_free(&pathspec);
+	git_pool_clear(&pathpool);
+
+	return error;
+}
+
+static int buffer_to_file(
+	struct stat *st,
+	git_buf *buffer,
+	const char *path,
+	mode_t dir_mode,
+	int file_open_flags,
+	mode_t file_mode)
+{
+	int fd, error;
+
+	if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
+		return error;
+
+	if ((fd = p_open(path, file_open_flags, file_mode)) < 0) {
+		giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
+		return fd;
+	}
+
+	if ((error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer))) < 0) {
+		giterr_set(GITERR_OS, "Could not write to '%s'", path);
+		(void)p_close(fd);
+	} else {
+		if ((error = p_close(fd)) < 0)
+			giterr_set(GITERR_OS, "Error while closing '%s'", path);
+
+		if ((error = p_stat(path, st)) < 0)
+			giterr_set(GITERR_OS, "Error while statting '%s'", path);
+	}
+
+	if (!error &&
+		(file_mode & 0100) != 0 &&
+		(error = p_chmod(path, file_mode)) < 0)
+		giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
+
+	return error;
+}
+
+static int blob_content_to_file(
+	struct stat *st,
+	git_blob *blob,
+	const char *path,
+	mode_t entry_filemode,
+	git_checkout_opts *opts)
+{
+	int error = -1, nb_filters = 0;
+	mode_t file_mode = opts->file_mode;
+	bool dont_free_filtered;
+	git_buf unfiltered = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+	git_vector filters = GIT_VECTOR_INIT;
+
+	/* Create a fake git_buf from the blob raw data... */
+	filtered.ptr = blob->odb_object->raw.data;
+	filtered.size = blob->odb_object->raw.len;
+	/* ... and make sure it doesn't get unexpectedly freed */
+	dont_free_filtered = true;
+
+	if (!opts->disable_filters &&
+		!git_buf_text_is_binary(&filtered) &&
+		(nb_filters = git_filters_load(
+			&filters,
+			git_object_owner((git_object *)blob),
+			path,
+			GIT_FILTER_TO_WORKTREE)) > 0)
+	{
+		/* reset 'filtered' so it can be a filter target */
+		git_buf_init(&filtered, 0);
+		dont_free_filtered = false;
+	}
+
+	if (nb_filters < 0)
+		return nb_filters;
+
+	if (nb_filters > 0)	 {
+		if ((error = git_blob__getbuf(&unfiltered, blob)) < 0)
+			goto cleanup;
+
+		if ((error = git_filters_apply(&filtered, &unfiltered, &filters)) < 0)
+			goto cleanup;
+	}
+
+	/* Allow overriding of file mode */
+	if (!file_mode)
+		file_mode = entry_filemode;
+
+	error = buffer_to_file(
+		st, &filtered, path, opts->dir_mode, opts->file_open_flags, file_mode);
+
+	if (!error)
+		st->st_mode = entry_filemode;
+
+cleanup:
+	git_filters_free(&filters);
+	git_buf_free(&unfiltered);
+	if (!dont_free_filtered)
+		git_buf_free(&filtered);
+
+	return error;
+}
+
+static int blob_content_to_link(
+	struct stat *st, git_blob *blob, const char *path, int can_symlink)
+{
+	git_buf linktarget = GIT_BUF_INIT;
+	int error;
+
+	if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
+		return error;
+
+	if (can_symlink) {
+		if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
+			giterr_set(GITERR_CHECKOUT, "Could not create symlink %s\n", path);
+	} else {
+		error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
+	}
+
+	if (!error) {
+		if ((error = p_lstat(path, st)) < 0)
+			giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path);
+
+		st->st_mode = GIT_FILEMODE_LINK;
+	}
+
+	git_buf_free(&linktarget);
+
+	return error;
+}
+
+static int checkout_update_index(
+	checkout_data *data,
+	const git_diff_file *file,
+	struct stat *st)
+{
+	git_index_entry entry;
+
+	if (!data->index)
+		return 0;
+
+	memset(&entry, 0, sizeof(entry));
+	entry.path = (char *)file->path; /* cast to prevent warning */
+	git_index_entry__init_from_stat(&entry, st);
+	git_oid_cpy(&entry.oid, &file->oid);
+
+	return git_index_add(data->index, &entry);
+}
+
+static int checkout_submodule(
+	checkout_data *data,
+	const git_diff_file *file)
+{
+	int error = 0;
+	git_submodule *sm;
+
+	/* Until submodules are supported, UPDATE_ONLY means do nothing here */
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+		return 0;
+
+	if ((error = git_futils_mkdir(
+			file->path, git_repository_workdir(data->repo),
+			data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
+		return error;
+
+	if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0)
+		return error;
+
+	/* TODO: Support checkout_strategy options.  Two circumstances:
+	 * 1 - submodule already checked out, but we need to move the HEAD
+	 *     to the new OID, or
+	 * 2 - submodule not checked out and we should recursively check it out
+	 *
+	 * Checkout will not execute a pull on the submodule, but a clone
+	 * command should probably be able to.  Do we need a submodule callback?
+	 */
+
+	/* update the index unless prevented */
+	if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) {
+		struct stat st;
+
+		git_buf_truncate(&data->path, data->workdir_len);
+		if (git_buf_puts(&data->path, file->path) < 0)
+			return -1;
+
+		if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) {
+			giterr_set(
+				GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
+			return error;
+		}
+
+		st.st_mode = GIT_FILEMODE_COMMIT;
+
+		error = checkout_update_index(data, file, &st);
+	}
+
+	return error;
+}
+
+static void report_progress(
+	checkout_data *data,
+	const char *path)
+{
+	if (data->opts.progress_cb)
+		data->opts.progress_cb(
+			path, data->completed_steps, data->total_steps,
+			data->opts.progress_payload);
+}
+
+static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
+{
+	struct stat st;
+
+	if (p_lstat(path, &st) < 0) {
+		/* if doesn't exist, then no error and no update */
+		if (errno == ENOENT || errno == ENOTDIR)
+			return 0;
+
+		/* otherwise, stat error and no update */
+		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+		return -1;
+	}
+
+	/* only safe for update if this is the same type of file */
+	if ((st.st_mode & ~0777) == (expected_mode & ~0777))
+		return 1;
+
+	return 0;
+}
+
+static int checkout_blob(
+	checkout_data *data,
+	const git_diff_file *file)
+{
+	int error = 0;
+	git_blob *blob;
+	struct stat st;
+
+	git_buf_truncate(&data->path, data->workdir_len);
+	if (git_buf_puts(&data->path, file->path) < 0)
+		return -1;
+
+	if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
+		int rval = checkout_safe_for_update_only(
+			git_buf_cstr(&data->path), file->mode);
+		if (rval <= 0)
+			return rval;
+	}
+
+	if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
+		return error;
+
+	if (S_ISLNK(file->mode))
+		error = blob_content_to_link(
+			&st, blob, git_buf_cstr(&data->path), data->can_symlink);
+	else
+		error = blob_content_to_file(
+			&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
+
+	git_blob_free(blob);
+
+	/* if we try to create the blob and an existing directory blocks it from
+	 * being written, then there must have been a typechange conflict in a
+	 * parent directory - suppress the error and try to continue.
+	 */
+	if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 &&
+		(error == GIT_ENOTFOUND || error == GIT_EEXISTS))
+	{
+		giterr_clear();
+		error = 0;
+	}
+
+	/* update the index unless prevented */
+	if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+		error = checkout_update_index(data, file, &st);
+
+	/* update the submodule data if this was a new .gitmodules file */
+	if (!error && strcmp(file->path, ".gitmodules") == 0)
+		data->reload_submodules = true;
+
+	return error;
+}
+
+static int checkout_remove_the_old(
+	unsigned int *actions,
+	checkout_data *data)
+{
+	int error = 0;
+	git_diff_delta *delta;
+	const char *str;
+	size_t i;
+	const char *workdir = git_buf_cstr(&data->path);
+	uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
+		GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
+
+	git_buf_truncate(&data->path, data->workdir_len);
+
+	git_vector_foreach(&data->diff->deltas, i, delta) {
+		if (actions[i] & CHECKOUT_ACTION__REMOVE) {
+			error = git_futils_rmdir_r(delta->old_file.path, workdir, flg);
+			if (error < 0)
+				return error;
+
+			data->completed_steps++;
+			report_progress(data, delta->old_file.path);
+
+			if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 &&
+				(data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
+				data->index != NULL)
+			{
+				(void)git_index_remove(data->index, delta->old_file.path, 0);
+			}
+		}
+	}
+
+	git_vector_foreach(&data->removes, i, str) {
+		error = git_futils_rmdir_r(str, workdir, flg);
+		if (error < 0)
+			return error;
+
+		data->completed_steps++;
+		report_progress(data, str);
+
+		if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
+			data->index != NULL)
+		{
+			if (str[strlen(str) - 1] == '/')
+				(void)git_index_remove_directory(data->index, str, 0);
+			else
+				(void)git_index_remove(data->index, str, 0);
+		}
+	}
+
+	return 0;
+}
+
+static int checkout_deferred_remove(git_repository *repo, const char *path)
+{
+#if 0
+	int error = git_futils_rmdir_r(
+		path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS);
+
+	if (error == GIT_ENOTFOUND) {
+		error = 0;
+		giterr_clear();
+	}
+
+	return error;
+#else
+	GIT_UNUSED(repo);
+	GIT_UNUSED(path);
+	assert(false);
+	return 0;
+#endif
+}
+
+static int checkout_create_the_new(
+	unsigned int *actions,
+	checkout_data *data)
+{
+	int error = 0;
+	git_diff_delta *delta;
+	size_t i;
+
+	git_vector_foreach(&data->diff->deltas, i, delta) {
+		if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
+			/* this had a blocker directory that should only be removed iff
+			 * all of the contents of the directory were safely removed
+			 */
+			if ((error = checkout_deferred_remove(
+					data->repo, delta->old_file.path)) < 0)
+				return error;
+		}
+
+		if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
+			error = checkout_blob(data, &delta->new_file);
+			if (error < 0)
+				return error;
+
+			data->completed_steps++;
+			report_progress(data, delta->new_file.path);
+		}
+	}
+
+	return 0;
+}
+
+static int checkout_create_submodules(
+	unsigned int *actions,
+	checkout_data *data)
+{
+	int error = 0;
+	git_diff_delta *delta;
+	size_t i;
+
+	/* initial reload of submodules if .gitmodules was changed */
+	if (data->reload_submodules &&
+		(error = git_submodule_reload_all(data->repo)) < 0)
+		return error;
+
+	git_vector_foreach(&data->diff->deltas, i, delta) {
+		if (actions[i] & CHECKOUT_ACTION__DEFER_REMOVE) {
+			/* this has a blocker directory that should only be removed iff
+			 * all of the contents of the directory were safely removed
+			 */
+			if ((error = checkout_deferred_remove(
+					data->repo, delta->old_file.path)) < 0)
+				return error;
+		}
+
+		if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
+			int error = checkout_submodule(data, &delta->new_file);
+			if (error < 0)
+				return error;
+
+			data->completed_steps++;
+			report_progress(data, delta->new_file.path);
+		}
+	}
+
+	/* final reload once submodules have been updated */
+	return git_submodule_reload_all(data->repo);
+}
+
+static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
+{
+	int error = 0;
+	git_reference *ref = NULL;
+	git_object *head;
+
+	if (!(error = git_repository_head(&ref, repo)) &&
+		!(error = git_reference_peel(&head, ref, GIT_OBJ_TREE)))
+		*out = (git_tree *)head;
+
+	git_reference_free(ref);
+
+	return error;
+}
+
+static void checkout_data_clear(checkout_data *data)
+{
+	if (data->opts_free_baseline) {
+		git_tree_free(data->opts.baseline);
+		data->opts.baseline = NULL;
+	}
+
+	git_vector_free(&data->removes);
+	git_pool_clear(&data->pool);
+
+	git__free(data->pfx);
+	data->pfx = NULL;
+
+	git_buf_free(&data->path);
+
+	git_index_free(data->index);
+	data->index = NULL;
+}
+
+static int checkout_data_init(
+	checkout_data *data,
+	git_iterator *target,
+	git_checkout_opts *proposed)
+{
+	int error = 0;
+	git_config *cfg;
+	git_repository *repo = git_iterator_owner(target);
+
+	memset(data, 0, sizeof(*data));
+
+	if (!repo) {
+		giterr_set(GITERR_CHECKOUT, "Cannot checkout nothing");
+		return -1;
+	}
+
+	if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
+		return error;
+
+	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+		return error;
+
+	data->repo = repo;
+
+	GITERR_CHECK_VERSION(
+		proposed, GIT_CHECKOUT_OPTS_VERSION, "git_checkout_opts");
+
+	if (!proposed)
+		GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTS_VERSION);
+	else
+		memmove(&data->opts, proposed, sizeof(git_checkout_opts));
+
+	/* refresh config and index content unless NO_REFRESH is given */
+	if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
+		if ((error = git_config_refresh(cfg)) < 0)
+			goto cleanup;
+
+		/* if we are checking out the index, don't reload,
+		 * otherwise get index and force reload
+		 */
+		if ((data->index = git_iterator_get_index(target)) != NULL) {
+			GIT_REFCOUNT_INC(data->index);
+		} else {
+			/* otherwise, grab and reload the index */
+			if ((error = git_repository_index(&data->index, data->repo)) < 0 ||
+				(error = git_index_read(data->index)) < 0)
+				goto cleanup;
+
+			/* clear the REUC when doing a tree or commit checkout */
+			git_index_reuc_clear(data->index);
+		}
+	}
+
+	/* if you are forcing, definitely allow safe updates */
+	if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
+		data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE_CREATE;
+	if ((data->opts.checkout_strategy & GIT_CHECKOUT_SAFE_CREATE) != 0)
+		data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+	data->strategy = data->opts.checkout_strategy;
+
+	/* opts->disable_filters is false by default */
+
+	if (!data->opts.dir_mode)
+		data->opts.dir_mode = GIT_DIR_MODE;
+
+	if (!data->opts.file_open_flags)
+		data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
+
+	data->pfx = git_pathspec_prefix(&data->opts.paths);
+
+	error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
+	if (error < 0) {
+		if (error != GIT_ENOTFOUND)
+			goto cleanup;
+
+		/* If "core.symlinks" is not found anywhere, default to true. */
+		data->can_symlink = true;
+		giterr_clear();
+		error = 0;
+	}
+
+	if (!data->opts.baseline) {
+		data->opts_free_baseline = true;
+		error = checkout_lookup_head_tree(&data->opts.baseline, repo);
+
+		if (error == GIT_EORPHANEDHEAD) {
+			error = 0;
+			giterr_clear();
+		}
+
+		if (error < 0)
+			goto cleanup;
+	}
+
+	if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
+		(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
+		(error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0)
+		goto cleanup;
+
+	data->workdir_len = git_buf_len(&data->path);
+
+cleanup:
+	if (error < 0)
+		checkout_data_clear(data);
+
+	return error;
+}
+
+int git_checkout_iterator(
+	git_iterator *target,
+	git_checkout_opts *opts)
+{
+	int error = 0;
+	git_iterator *baseline = NULL, *workdir = NULL;
+	checkout_data data = {0};
+	git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+	uint32_t *actions = NULL;
+	size_t *counts = NULL;
+	git_iterator_flag_t iterflags = 0;
+
+	/* initialize structures and options */
+	error = checkout_data_init(&data, target, opts);
+	if (error < 0)
+		return error;
+
+	diff_opts.flags =
+		GIT_DIFF_INCLUDE_UNMODIFIED |
+		GIT_DIFF_INCLUDE_UNTRACKED |
+		GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
+		GIT_DIFF_INCLUDE_IGNORED |
+		GIT_DIFF_INCLUDE_TYPECHANGE |
+		GIT_DIFF_INCLUDE_TYPECHANGE_TREES |
+		GIT_DIFF_SKIP_BINARY_CHECK;
+	if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)
+		diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+	if (data.opts.paths.count > 0)
+		diff_opts.pathspec = data.opts.paths;
+
+	/* set up iterators */
+
+	iterflags = git_iterator_ignore_case(target) ?
+		GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
+
+	if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
+		(error = git_iterator_for_workdir(
+			&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
+			data.pfx, data.pfx)) < 0 ||
+		(error = git_iterator_for_tree(
+			&baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
+		goto cleanup;
+
+	/* Should not have case insensitivity mismatch */
+	assert(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
+
+	/* Generate baseline-to-target diff which will include an entry for
+	 * every possible update that might need to be made.
+	 */
+	if ((error = git_diff__from_iterators(
+			&data.diff, data.repo, baseline, target, &diff_opts)) < 0)
+		goto cleanup;
+
+	/* Loop through diff (and working directory iterator) building a list of
+	 * actions to be taken, plus look for conflicts and send notifications.
+	 */
+	if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
+		goto cleanup;
+
+	data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
+		counts[CHECKOUT_ACTION__UPDATE_BLOB] +
+		counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
+
+	report_progress(&data, NULL); /* establish 0 baseline */
+
+	/* To deal with some order dependencies, perform remaining checkout
+	 * in three passes: removes, then update blobs, then update submodules.
+	 */
+	if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
+		(error = checkout_remove_the_old(actions, &data)) < 0)
+		goto cleanup;
+
+	if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
+		(error = checkout_create_the_new(actions, &data)) < 0)
+		goto cleanup;
+
+	if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
+		(error = checkout_create_submodules(actions, &data)) < 0)
+		goto cleanup;
+
+	assert(data.completed_steps == data.total_steps);
+
+cleanup:
+	if (error == GIT_EUSER)
+		giterr_clear();
+
+	if (!error && data.index != NULL &&
+		(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+		error = git_index_write(data.index);
+
+	git_diff_list_free(data.diff);
+	git_iterator_free(workdir);
+	git_iterator_free(baseline);
+	git__free(actions);
+	git__free(counts);
+	checkout_data_clear(&data);
+
+	return error;
+}
+
+int git_checkout_index(
+	git_repository *repo,
+	git_index *index,
+	git_checkout_opts *opts)
+{
+	int error;
+	git_iterator *index_i;
+
+	if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0)
+		return error;
+
+	if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+		return error;
+	GIT_REFCOUNT_INC(index);
+
+	if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL)))
+		error = git_checkout_iterator(index_i, opts);
+
+	git_iterator_free(index_i);
+	git_index_free(index);
+
+	return error;
+}
+
+int git_checkout_tree(
+	git_repository *repo,
+	const git_object *treeish,
+	git_checkout_opts *opts)
+{
+	int error;
+	git_tree *tree = NULL;
+	git_iterator *tree_i = NULL;
+
+	if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0)
+		return error;
+
+	if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
+		giterr_set(
+			GITERR_CHECKOUT, "Provided object cannot be peeled to a tree");
+		return -1;
+	}
+
+	if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL)))
+		error = git_checkout_iterator(tree_i, opts);
+
+	git_iterator_free(tree_i);
+	git_tree_free(tree);
+
+	return error;
+}
+
+int git_checkout_head(
+	git_repository *repo,
+	git_checkout_opts *opts)
+{
+	int error;
+	git_tree *head = NULL;
+	git_iterator *head_i = NULL;
+
+	if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0)
+		return error;
+
+	if (!(error = checkout_lookup_head_tree(&head, repo)) &&
+		!(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
+		error = git_checkout_iterator(head_i, opts);
+
+	git_iterator_free(head_i);
+	git_tree_free(head);
+
+	return error;
+}
diff --git a/libgit2/src/checkout.h b/libgit2/src/checkout.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/checkout.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_checkout_h__
+#define INCLUDE_checkout_h__
+
+#include "git2/checkout.h"
+#include "iterator.h"
+
+#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
+
+/**
+ * Update the working directory to match the target iterator.  The
+ * expected baseline value can be passed in via the checkout options
+ * or else will default to the HEAD commit.
+ */
+extern int git_checkout_iterator(
+	git_iterator *target,
+	git_checkout_opts *opts);
+
+#endif
diff --git a/libgit2/src/clone.c b/libgit2/src/clone.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/clone.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <assert.h>
+
+#include "git2/clone.h"
+#include "git2/remote.h"
+#include "git2/revparse.h"
+#include "git2/branch.h"
+#include "git2/config.h"
+#include "git2/checkout.h"
+#include "git2/commit.h"
+#include "git2/tree.h"
+
+#include "common.h"
+#include "remote.h"
+#include "fileops.h"
+#include "refs.h"
+#include "path.h"
+
+static int create_branch(
+	git_reference **branch,
+	git_repository *repo,
+	const git_oid *target,
+	const char *name)
+{
+	git_commit *head_obj = NULL;
+	git_reference *branch_ref = NULL;
+	int error;
+
+	/* Find the target commit */
+	if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
+		return error;
+
+	/* Create the new branch */
+	error = git_branch_create(&branch_ref, repo, name, head_obj, 0);
+
+	git_commit_free(head_obj);
+
+	if (!error)
+		*branch = branch_ref;
+	else
+		git_reference_free(branch_ref);
+
+	return error;
+}
+
+static int setup_tracking_config(
+	git_repository *repo,
+	const char *branch_name,
+	const char *remote_name,
+	const char *merge_target)
+{
+	git_config *cfg;
+	git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
+	int error = -1;
+
+	if (git_repository_config__weakptr(&cfg, repo) < 0)
+		return -1;
+
+	if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
+		goto cleanup;
+
+	if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
+		goto cleanup;
+
+	if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
+		goto cleanup;
+
+	if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
+		goto cleanup;
+
+	error = 0;
+
+cleanup:
+	git_buf_free(&remote_key);
+	git_buf_free(&merge_key);
+	return error;
+}
+
+static int create_tracking_branch(
+	git_reference **branch,
+	git_repository *repo,
+	const git_oid *target,
+	const char *branch_name)
+{
+	int error;
+
+	if ((error = create_branch(branch, repo, target, branch_name)) < 0)
+		return error;
+
+	return setup_tracking_config(
+		repo,
+		branch_name,
+		GIT_REMOTE_ORIGIN,
+		git_reference_name(*branch));
+}
+
+struct head_info {
+	git_repository *repo;
+	git_oid remote_head_oid;
+	git_buf branchname;
+	const git_refspec *refspec;
+	bool found;
+};
+
+static int reference_matches_remote_head(
+	const char *reference_name,
+	void *payload)
+{
+	struct head_info *head_info = (struct head_info *)payload;
+	git_oid oid;
+
+	/* TODO: Should we guard against references
+	 * which name doesn't start with refs/heads/ ?
+	 */
+
+	/* Stop looking if we've already found a match */
+	if (head_info->found)
+		return 0;
+
+	if (git_reference_name_to_id(
+		&oid,
+		head_info->repo,
+		reference_name) < 0) {
+			/* If the reference doesn't exists, it obviously cannot match the expected oid. */
+			giterr_clear();
+			return 0;
+	}
+
+	if (git_oid_cmp(&head_info->remote_head_oid, &oid) == 0) {
+		/* Determine the local reference name from the remote tracking one */
+		if (git_refspec_transform_l(
+			&head_info->branchname, 
+			head_info->refspec,
+			reference_name) < 0)
+				return -1;
+		
+		if (git_buf_len(&head_info->branchname) > 0) {
+			if (git_buf_sets(
+				&head_info->branchname,
+				git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0)
+					return -1;
+
+			head_info->found = 1;
+		}
+	}
+
+	return 0;
+}
+
+static int update_head_to_new_branch(
+	git_repository *repo,
+	const git_oid *target,
+	const char *name)
+{
+	git_reference *tracking_branch = NULL;
+	int error;
+
+	if ((error = create_tracking_branch(
+		&tracking_branch,
+		repo,
+		target,
+		name)) < 0)
+			return error;
+
+	error = git_repository_set_head(repo, git_reference_name(tracking_branch));
+
+	git_reference_free(tracking_branch);
+
+	return error;
+}
+
+static int get_head_callback(git_remote_head *head, void *payload)
+{
+	git_remote_head **destination = (git_remote_head **)payload;
+
+	/* Save the first entry, and terminate the enumeration */
+	*destination = head;
+	return 1;
+}
+
+static int update_head_to_remote(git_repository *repo, git_remote *remote)
+{
+	int retcode = -1;
+	git_remote_head *remote_head;
+	struct head_info head_info;
+	git_buf remote_master_name = GIT_BUF_INIT;
+
+	/* Did we just clone an empty repository? */
+	if (remote->refs.length == 0) {
+		return setup_tracking_config(
+			repo,
+			"master",
+			GIT_REMOTE_ORIGIN,
+			GIT_REFS_HEADS_MASTER_FILE);
+	}
+
+	/* Get the remote's HEAD. This is always the first ref in remote->refs. */
+	remote_head = NULL;
+	
+	if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
+		return -1;
+
+	assert(remote_head);
+
+	git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
+	git_buf_init(&head_info.branchname, 16);
+	head_info.repo = repo;
+	head_info.refspec = git_remote_fetchspec(remote);
+	head_info.found = 0;
+	
+	/* Determine the remote tracking reference name from the local master */
+	if (git_refspec_transform_r(
+		&remote_master_name,
+		head_info.refspec,
+		GIT_REFS_HEADS_MASTER_FILE) < 0)
+			return -1;
+
+	/* Check to see if the remote HEAD points to the remote master */
+	if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0)
+		goto cleanup;
+
+	if (head_info.found) {
+		retcode = update_head_to_new_branch(
+			repo,
+			&head_info.remote_head_oid,
+			git_buf_cstr(&head_info.branchname));
+
+		goto cleanup;
+	}
+
+	/* Not master. Check all the other refs. */
+	if (git_reference_foreach(
+		repo,
+		GIT_REF_LISTALL,
+		reference_matches_remote_head,
+		&head_info) < 0)
+			goto cleanup;
+
+	if (head_info.found) {
+		retcode = update_head_to_new_branch(
+			repo,
+			&head_info.remote_head_oid,
+			git_buf_cstr(&head_info.branchname));
+
+		goto cleanup;
+	} else {
+		retcode = git_repository_set_head_detached(
+			repo,
+			&head_info.remote_head_oid);
+		goto cleanup;
+	}
+
+cleanup:
+	git_buf_free(&remote_master_name);
+	git_buf_free(&head_info.branchname);
+	return retcode;
+}
+
+static int update_head_to_branch(
+		git_repository *repo,
+		const git_clone_options *options)
+{
+	int retcode;
+	git_buf remote_branch_name = GIT_BUF_INIT;
+	git_reference* remote_ref = NULL;
+	
+	assert(options->checkout_branch);
+
+	if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
+		options->remote_name, options->checkout_branch)) < 0 )
+		goto cleanup;
+
+	if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
+		goto cleanup;
+
+	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref),
+		options->checkout_branch);
+
+cleanup:
+	git_reference_free(remote_ref);
+	git_buf_free(&remote_branch_name);
+	return retcode;
+}
+
+/*
+ * submodules?
+ */
+
+static int create_and_configure_origin(
+		git_remote **out,
+		git_repository *repo,
+		const char *url,
+		const git_clone_options *options)
+{
+	int error;
+	git_remote *origin = NULL;
+
+	if ((error = git_remote_create(&origin, repo, options->remote_name, url)) < 0)
+		goto on_error;
+
+	git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
+			options->cred_acquire_payload);
+	git_remote_set_autotag(origin, options->remote_autotag);
+	/*
+	 * Don't write FETCH_HEAD, we'll check out the remote tracking
+	 * branch ourselves based on the server's default.
+	 */
+	git_remote_set_update_fetchhead(origin, 0);
+
+	if (options->remote_callbacks &&
+	    (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
+		goto on_error;
+
+	if (options->fetch_spec &&
+	    (error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0)
+		goto on_error;
+
+	if (options->push_spec &&
+	    (error = git_remote_set_pushspec(origin, options->push_spec)) < 0)
+		goto on_error;
+
+	if (options->pushurl &&
+	    (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
+		goto on_error;
+
+	if ((error = git_remote_save(origin)) < 0)
+		goto on_error;
+
+	*out = origin;
+	return 0;
+
+on_error:
+	git_remote_free(origin);
+	return error;
+}
+
+
+static int setup_remotes_and_fetch(
+		git_repository *repo,
+		const char *url,
+		const git_clone_options *options)
+{
+	int retcode = GIT_ERROR;
+	git_remote *origin;
+
+	/* Construct an origin remote */
+	if (!create_and_configure_origin(&origin, repo, url, options)) {
+		git_remote_set_update_fetchhead(origin, 0);
+
+		/* Connect and download everything */
+		if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) {
+			if (!(retcode = git_remote_download(origin, options->fetch_progress_cb,
+						options->fetch_progress_payload))) {
+				/* Create "origin/foo" branches for all remote branches */
+				if (!git_remote_update_tips(origin)) {
+					/* Point HEAD to the requested branch */
+					if (options->checkout_branch) {
+						if (!update_head_to_branch(repo, options))
+							retcode = 0;
+					}
+					/* Point HEAD to the same ref as the remote's head */
+					else if (!update_head_to_remote(repo, origin)) {
+						retcode = 0;
+					}
+				}
+			}
+			git_remote_disconnect(origin);
+		}
+		git_remote_free(origin);
+	}
+
+	return retcode;
+}
+
+
+static bool path_is_okay(const char *path)
+{
+	/* The path must either not exist, or be an empty directory */
+	if (!git_path_exists(path)) return true;
+	if (!git_path_is_empty_dir(path)) {
+		giterr_set(GITERR_INVALID,
+					  "'%s' exists and is not an empty directory", path);
+		return false;
+	}
+	return true;
+}
+
+static bool should_checkout(
+	git_repository *repo,
+	bool is_bare,
+	git_checkout_opts *opts)
+{
+	if (is_bare)
+		return false;
+
+	if (!opts)
+		return false;
+
+	if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
+		return false;
+
+	return !git_repository_head_orphan(repo);
+}
+
+static void normalize_options(git_clone_options *dst, const git_clone_options *src)
+{
+	git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
+	if (!src) src = &default_options;
+
+	*dst = *src;
+
+	/* Provide defaults for null pointers */
+	if (!dst->remote_name) dst->remote_name = "origin";
+	if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+}
+
+int git_clone(
+	git_repository **out,
+	const char *url,
+	const char *local_path,
+	const git_clone_options *options)
+{
+	int retcode = GIT_ERROR;
+	git_repository *repo = NULL;
+	git_clone_options normOptions;
+	int remove_directory_on_failure = 0;
+
+	assert(out && url && local_path);
+
+	normalize_options(&normOptions, options);
+	GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
+
+	if (!path_is_okay(local_path)) {
+		return GIT_ERROR;
+	}
+
+	/* Only remove the directory on failure if we create it */
+	remove_directory_on_failure = !git_path_exists(local_path);
+
+	if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
+		if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
+			/* Failed to fetch; clean up */
+			git_repository_free(repo);
+
+			if (remove_directory_on_failure)
+				git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
+			else
+				git_futils_cleanupdir_r(local_path);
+
+		} else {
+			*out = repo;
+			retcode = 0;
+		}
+	}
+
+	if (!retcode && should_checkout(repo, normOptions.bare, &normOptions.checkout_opts))
+		retcode = git_checkout_head(*out, &normOptions.checkout_opts);
+
+	return retcode;
+}
diff --git a/libgit2/src/commit.c b/libgit2/src/commit.c
--- a/libgit2/src/commit.c
+++ b/libgit2/src/commit.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -18,31 +18,22 @@
 
 #include <stdarg.h>
 
-#define COMMIT_BASIC_PARSE 0x0
-#define COMMIT_FULL_PARSE 0x1
-
-#define COMMIT_PRINT(commit) {\
-	char oid[41]; oid[40] = 0;\
-	git_oid_fmt(oid, &commit->object.id);\
-	printf("Oid: %s | In degree: %d | Time: %u\n", oid, commit->in_degree, commit->commit_time);\
-}
-
 static void clear_parents(git_commit *commit)
 {
-	unsigned int i;
+	size_t i;
 
-	for (i = 0; i < commit->parent_oids.length; ++i) {
-		git_oid *parent = git_vector_get(&commit->parent_oids, i);
+	for (i = 0; i < commit->parent_ids.length; ++i) {
+		git_oid *parent = git_vector_get(&commit->parent_ids, i);
 		git__free(parent);
 	}
 
-	git_vector_clear(&commit->parent_oids);
+	git_vector_clear(&commit->parent_ids);
 }
 
 void git_commit__free(git_commit *commit)
 {
 	clear_parents(commit);
-	git_vector_free(&commit->parent_oids);
+	git_vector_free(&commit->parent_ids);
 
 	git_signature_free(commit->author);
 	git_signature_free(commit->committer);
@@ -52,11 +43,6 @@
 	git__free(commit);
 }
 
-const git_oid *git_commit_id(git_commit *c)
-{
-	return git_object_id((git_object *)c);
-}
-
 int git_commit_create_v(
 		git_oid *oid,
 		git_repository *repo,
@@ -90,66 +76,6 @@
 	return res;
 }
 
-/* Update the reference named `ref_name` so it points to `oid` */
-static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
-{
-	git_reference *ref;
-	int res;
-
-	res = git_reference_lookup(&ref, repo, ref_name);
-
-	/* If we haven't found the reference at all, we assume we need to create
-	 * a new reference and that's it */
-	if (res == GIT_ENOTFOUND) {
-		giterr_clear();
-		return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
-	}
-
-	if (res < 0)
-		return -1;
-
-	/* If we have found a reference, but it's symbolic, we need to update
-	 * the direct reference it points to */
-	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
-		git_reference *aux;
-		const char *sym_target;
-
-		/* The target pointed at by this reference */
-		sym_target = git_reference_target(ref);
-
-		/* resolve the reference to the target it points to */
-		res = git_reference_resolve(&aux, ref);
-
-		/*
-		 * if the symbolic reference pointed to an inexisting ref,
-		 * this is means we're creating a new branch, for example.
-		 * We need to create a new direct reference with that name
-		 */
-		if (res == GIT_ENOTFOUND) {
-			giterr_clear();
-			res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
-			git_reference_free(ref);
-			return res;
-		}
-
-		/* free the original symbolic reference now; not before because
-		 * we're using the `sym_target` pointer */
-		git_reference_free(ref);
-
-		if (res < 0)
-			return -1;
-
-		/* store the newly found direct reference in its place */
-		ref = aux;
-	}
-
-	/* ref is made to point to `oid`: ref is either the original reference,
-	 * or the target of the symbolic reference we've looked up */
-	res = git_reference_set_oid(ref, oid);
-	git_reference_free(ref);
-	return res;
-}
-
 int git_commit_create_oid(
 		git_oid *oid,
 		git_repository *repo,
@@ -162,7 +88,7 @@
 		int parent_count,
 		const git_oid *parents[])
 {
-	git_buf commit = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
+	git_buf commit = GIT_BUF_INIT;
 	int i;
 	git_odb *odb;
 
@@ -179,19 +105,8 @@
 
 	git_buf_putc(&commit, '\n');
 
-#if 0
-	/* Remove comments by default */
-	if (git_message_prettify(&cleaned_message, message, 1) < 0)
-		goto on_error;
-
-	if (git_buf_puts(&commit, git_buf_cstr(&cleaned_message)) < 0)
-		goto on_error;
-
-	git_buf_free(&cleaned_message);
-#else
 	if (git_buf_puts(&commit, message) < 0)
 		goto on_error;
-#endif
 
 	if (git_repository_odb__weakptr(&odb, repo) < 0)
 		goto on_error;
@@ -202,13 +117,12 @@
 	git_buf_free(&commit);
 
 	if (update_ref != NULL)
-		return update_reference(repo, oid, update_ref);
+		return git_reference__update_terminal(repo, update_ref, oid);
 
 	return 0;
 
 on_error:
 	git_buf_free(&commit);
-	git_buf_free(&cleaned_message);
 	giterr_set(GITERR_OBJECT, "Failed to create commit.");
 	return -1;
 }
@@ -251,27 +165,25 @@
 {
 	const char *buffer = data;
 	const char *buffer_end = (const char *)data + len;
-
-	git_oid parent_oid;
+	git_oid parent_id;
 
-	git_vector_init(&commit->parent_oids, 4, NULL);
+	if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
+		return -1;
 
-	if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)
+	if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
 		goto bad_buffer;
 
 	/*
 	 * TODO: commit grafts!
 	 */
 
-	while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {
-		git_oid *new_oid;
-
-		new_oid = git__malloc(sizeof(git_oid));
-		GITERR_CHECK_ALLOC(new_oid);
+	while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
+		git_oid *new_id = git__malloc(sizeof(git_oid));
+		GITERR_CHECK_ALLOC(new_id);
 
-		git_oid_cpy(new_oid, &parent_oid);
+		git_oid_cpy(new_id, &parent_id);
 
-		if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
+		if (git_vector_insert(&commit->parent_ids, new_id) < 0)
 			return -1;
 	}
 
@@ -288,24 +200,30 @@
 	if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
 		return -1;
 
-	if (git__prefixcmp(buffer, "encoding ") == 0) {
-		const char *encoding_end;
-		buffer += strlen("encoding ");
+	/* Parse add'l header entries until blank line found */
+	while (buffer < buffer_end && *buffer != '\n') {
+		const char *eoln = buffer;
+		while (eoln < buffer_end && *eoln != '\n')
+			++eoln;
 
-		encoding_end = buffer;
-		while (encoding_end < buffer_end && *encoding_end != '\n')
-			encoding_end++;
+		if (git__prefixcmp(buffer, "encoding ") == 0) {
+			buffer += strlen("encoding ");
 
-		commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
-		GITERR_CHECK_ALLOC(commit->message_encoding);
+			commit->message_encoding = git__strndup(buffer, eoln - buffer);
+			GITERR_CHECK_ALLOC(commit->message_encoding);
+		}
 
-		buffer = encoding_end;
+		if (eoln < buffer_end && *eoln == '\n')
+			++eoln;
+
+		buffer = eoln;
 	}
 
-	/* parse commit message */
-	while (buffer < buffer_end - 1 && *buffer == '\n')
+	/* buffer is now at the end of the header, double-check and move forward into the message */
+	if (buffer < buffer_end && *buffer == '\n')
 		buffer++;
 
+	/* parse commit message */
 	if (buffer <= buffer_end) {
 		commit->message = git__strndup(buffer, buffer_end - buffer);
 		GITERR_CHECK_ALLOC(commit->message);
@@ -325,7 +243,7 @@
 }
 
 #define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
-	_rvalue git_commit_##_name(git_commit *commit) \
+	_rvalue git_commit_##_name(const git_commit *commit) \
 	{\
 		assert(commit); \
 		return _return; \
@@ -337,33 +255,66 @@
 GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
 GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
 GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
-GIT_COMMIT_GETTER(unsigned int, parentcount, commit->parent_oids.length)
-GIT_COMMIT_GETTER(const git_oid *, tree_oid, &commit->tree_oid);
+GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length)
+GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
 
+int git_commit_tree(git_tree **tree_out, const git_commit *commit)
+{
+	assert(commit);
+	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
+}
 
-int git_commit_tree(git_tree **tree_out, git_commit *commit)
+const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n)
 {
 	assert(commit);
-	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid);
+
+	return git_vector_get(&commit->parent_ids, n);
 }
 
 int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
 {
-	git_oid *parent_oid;
+	const git_oid *parent_id;
 	assert(commit);
 
-	parent_oid = git_vector_get(&commit->parent_oids, n);
-	if (parent_oid == NULL) {
+	parent_id = git_commit_parent_id(commit, n);
+	if (parent_id == NULL) {
 		giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
 		return GIT_ENOTFOUND;
 	}
 
-	return git_commit_lookup(parent, commit->object.repo, parent_oid);
+	return git_commit_lookup(parent, commit->object.repo, parent_id);
 }
 
-const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)
+int git_commit_nth_gen_ancestor(
+	git_commit **ancestor,
+	const git_commit *commit,
+	unsigned int n)
 {
-	assert(commit);
+	git_commit *current, *parent;
+	int error;
 
-	return git_vector_get(&commit->parent_oids, n);
+	assert(ancestor && commit);
+
+	current = (git_commit *)commit;
+
+	if (n == 0)
+		return git_commit_lookup(
+			ancestor,
+			commit->object.repo,
+			git_object_id((const git_object *)commit));
+
+	while (n--) {
+		error = git_commit_parent(&parent, (git_commit *)current, 0);
+
+		if (current != commit)
+			git_commit_free(current);
+
+		if (error < 0)
+			return error;
+
+		current = parent;
+	}
+
+	*ancestor = parent;
+	return 0;
 }
diff --git a/libgit2/src/commit.h b/libgit2/src/commit.h
--- a/libgit2/src/commit.h
+++ b/libgit2/src/commit.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,8 +17,8 @@
 struct git_commit {
 	git_object object;
 
-	git_vector parent_oids;
-	git_oid tree_oid;
+	git_vector parent_ids;
+	git_oid tree_id;
 
 	git_signature *author;
 	git_signature *committer;
diff --git a/libgit2/src/commit_list.c b/libgit2/src/commit_list.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/commit_list.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "commit_list.h"
+#include "common.h"
+#include "revwalk.h"
+#include "pool.h"
+#include "odb.h"
+
+int git_commit_list_time_cmp(void *a, void *b)
+{
+	git_commit_list_node *commit_a = (git_commit_list_node *)a;
+	git_commit_list_node *commit_b = (git_commit_list_node *)b;
+
+	return (commit_a->time < commit_b->time);
+}
+
+git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
+{
+	git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
+	if (new_list != NULL) {
+		new_list->item = item;
+		new_list->next = *list_p;
+	}
+	*list_p = new_list;
+	return new_list;
+}
+
+git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p)
+{
+	git_commit_list **pp = list_p;
+	git_commit_list *p;
+
+	while ((p = *pp) != NULL) {
+		if (git_commit_list_time_cmp(p->item, item) < 0)
+			break;
+
+		pp = &p->next;
+	}
+
+	return git_commit_list_insert(item, pp);
+}
+
+git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
+{
+	return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
+}
+
+static int commit_error(git_commit_list_node *commit, const char *msg)
+{
+	char commit_oid[GIT_OID_HEXSZ + 1];
+	git_oid_fmt(commit_oid, &commit->oid);
+	commit_oid[GIT_OID_HEXSZ] = '\0';
+
+	giterr_set(GITERR_ODB, "Failed to parse commit %s - %s", commit_oid, msg);
+
+	return -1;
+}
+
+static git_commit_list_node **alloc_parents(
+	git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
+{
+	if (n_parents <= PARENTS_PER_COMMIT)
+		return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
+
+	return (git_commit_list_node **)git_pool_malloc(
+		&walk->commit_pool, (uint32_t)(n_parents * sizeof(git_commit_list_node *)));
+}
+
+
+void git_commit_list_free(git_commit_list **list_p)
+{
+	git_commit_list *list = *list_p;
+
+	if (list == NULL)
+		return;
+
+	while (list) {
+		git_commit_list *temp = list;
+		list = temp->next;
+		git__free(temp);
+	}
+
+	*list_p = NULL;
+}
+
+git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
+{
+	git_commit_list *top = *stack;
+	git_commit_list_node *item = top ? top->item : NULL;
+
+	if (top) {
+		*stack = top->next;
+		git__free(top);
+	}
+	return item;
+}
+
+static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, git_rawobj *raw)
+{
+	const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
+	unsigned char *buffer = raw->data;
+	unsigned char *buffer_end = buffer + raw->len;
+	unsigned char *parents_start, *committer_start;
+	int i, parents = 0;
+	int commit_time;
+
+	buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
+
+	parents_start = buffer;
+	while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) {
+		parents++;
+		buffer += parent_len;
+	}
+
+	commit->parents = alloc_parents(walk, commit, parents);
+	GITERR_CHECK_ALLOC(commit->parents);
+
+	buffer = parents_start;
+	for (i = 0; i < parents; ++i) {
+		git_oid oid;
+
+		if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
+			return -1;
+
+		commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
+		if (commit->parents[i] == NULL)
+			return -1;
+
+		buffer += parent_len;
+	}
+
+	commit->out_degree = (unsigned short)parents;
+
+	if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
+		return commit_error(commit, "object is corrupted");
+
+	buffer++;
+
+	if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
+		return commit_error(commit, "object is corrupted");
+
+	/* Skip trailing spaces */
+	while (buffer > committer_start && git__isspace(*buffer))
+		buffer--;
+
+	/* Seek for the begining of the pack of digits */
+	while (buffer > committer_start && git__isdigit(*buffer))
+		buffer--;
+
+	/* Skip potential timezone offset */
+	if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) {
+		buffer--;
+
+		while (buffer > committer_start && git__isspace(*buffer))
+			buffer--;
+
+		while (buffer > committer_start && git__isdigit(*buffer))
+			buffer--;
+	}
+
+	if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
+		return commit_error(commit, "cannot parse commit time");
+
+	commit->time = (time_t)commit_time;
+	commit->parsed = 1;
+	return 0;
+}
+
+int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
+{
+	git_odb_object *obj;
+	int error;
+
+	if (commit->parsed)
+		return 0;
+
+	if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
+		return error;
+
+	if (obj->raw.type != GIT_OBJ_COMMIT) {
+		giterr_set(GITERR_INVALID, "Object is no commit object");
+		error = -1;
+	} else
+		error = commit_quick_parse(walk, commit, &obj->raw);
+
+	git_odb_object_free(obj);
+	return error;
+}
+
diff --git a/libgit2/src/commit_list.h b/libgit2/src/commit_list.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/commit_list.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_commit_list_h__
+#define INCLUDE_commit_list_h__
+
+#include "git2/oid.h"
+
+#define PARENT1  (1 << 0)
+#define PARENT2  (1 << 1)
+#define RESULT   (1 << 2)
+#define STALE    (1 << 3)
+
+#define PARENTS_PER_COMMIT	2
+#define COMMIT_ALLOC \
+	(sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *))
+
+typedef struct git_commit_list_node {
+	git_oid oid;
+	uint32_t time;
+	unsigned int seen:1,
+			 uninteresting:1,
+			 topo_delay:1,
+			 parsed:1,
+			 flags : 4;
+
+	unsigned short in_degree;
+	unsigned short out_degree;
+
+	struct git_commit_list_node **parents;
+} git_commit_list_node;
+
+typedef struct git_commit_list {
+	git_commit_list_node *item;
+	struct git_commit_list *next;
+} git_commit_list;
+
+git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
+int git_commit_list_time_cmp(void *a, void *b);
+void git_commit_list_free(git_commit_list **list_p);
+git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
+git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
+int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit);
+git_commit_list_node *git_commit_list_pop(git_commit_list **stack);
+
+#endif
diff --git a/libgit2/src/common.h b/libgit2/src/common.h
--- a/libgit2/src/common.h
+++ b/libgit2/src/common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -24,21 +24,24 @@
 
 # include <io.h>
 # include <direct.h>
+# include <winsock2.h>
 # include <windows.h>
 # include "win32/msvc-compat.h"
 # include "win32/mingw-compat.h"
+# include "win32/error.h"
+# include "win32/version.h"
 # ifdef GIT_THREADS
 #	include "win32/pthread.h"
-#endif
-
-# define snprintf _snprintf
+# endif
 
 #else
-# include <unistd.h>
 
+# include <unistd.h>
 # ifdef GIT_THREADS
 #	include <pthread.h>
 # endif
+#define GIT_STDLIB_CALL
+
 #endif
 
 #include "git2/types.h"
@@ -48,25 +51,59 @@
 
 #include <regex.h>
 
-extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
-#define git__throw(error, ...) \
-	(git___throw(__VA_ARGS__), error)
+/**
+ * Check a pointer allocation result, returning -1 if it failed.
+ */
+#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
 
-extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
-#define git__rethrow(error, ...) \
-	(git___rethrow(__VA_ARGS__), error)
+/**
+ * Check a return value and propogate result if non-zero.
+ */
+#define GITERR_CHECK_ERROR(code) \
+	do { int _err = (code); if (_err < 0) return _err; } while (0)
 
+/**
+ * Set the error message for this thread, formatting as needed.
+ */
+void giterr_set(int error_class, const char *string, ...);
 
-#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
+/**
+ * Set the error message for a regex failure, using the internal regex
+ * error code lookup and return a libgit error code.
+ */
+int giterr_set_regex(const regex_t *regex, int error_code);
 
-void giterr_set_oom(void);
-void giterr_set(int error_class, const char *string, ...);
-void giterr_clear(void);
-void giterr_set_str(int error_class, const char *string);
-void giterr_set_regex(const regex_t *regex, int error_code);
+/**
+ * Check a versioned structure for validity
+ */
+GIT_INLINE(int) giterr__check_version(const void *structure, unsigned int expected_max, const char *name)
+{
+	unsigned int actual;
 
+	if (!structure)
+		return 0;
 
-#include "util.h"
+	actual = *(const unsigned int*)structure;
+	if (actual > 0 && actual <= expected_max)
+		return 0;
 
+	giterr_set(GITERR_INVALID, "Invalid version %d on %s", actual, name);
+	return -1;
+}
+#define GITERR_CHECK_VERSION(S,V,N) if (giterr__check_version(S,V,N) < 0) return -1
+
+/**
+ * Initialize a structure with a version.
+ */
+GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
+{
+	memset(structure, 0, len);
+	*((int*)structure) = version;
+}
+#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
+
+/* NOTE: other giterr functions are in the public errors.h header file */
+
+#include "util.h"
 
 #endif /* INCLUDE_common_h__ */
diff --git a/libgit2/src/compat/fnmatch.h b/libgit2/src/compat/fnmatch.h
deleted file mode 100644
--- a/libgit2/src/compat/fnmatch.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_fnmatch__compat_h__
-#define INCLUDE_fnmatch__compat_h__
-
-#include "common.h"
-
-#define FNM_NOMATCH		1		/* Match failed. */
-#define FNM_NOSYS		2		/* Function not supported (unused). */
-
-#define FNM_NOESCAPE		0x01		/* Disable backslash escaping. */
-#define FNM_PATHNAME		0x02		/* Slash must be matched by slash. */
-#define FNM_PERIOD		0x04		/* Period must be matched by period. */
-#define FNM_LEADING_DIR 0x08		/* Ignore /<tail> after Imatch. */
-#define FNM_CASEFOLD		0x10		/* Case insensitive search. */
-
-#define FNM_IGNORECASE	FNM_CASEFOLD
-#define FNM_FILE_NAME	FNM_PATHNAME
-
-extern int p_fnmatch(const char *pattern, const char *string, int flags);
-
-#endif /* _FNMATCH_H */
-
diff --git a/libgit2/src/compress.c b/libgit2/src/compress.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/compress.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "compress.h"
+
+#include <zlib.h>
+
+#define BUFFER_SIZE (1024 * 1024)
+
+int git__compress(git_buf *buf, const void *buff, size_t len)
+{
+	z_stream zs;
+	char *zb;
+	size_t have;
+
+	memset(&zs, 0, sizeof(zs));
+	if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK)
+		return -1;
+
+	zb = git__malloc(BUFFER_SIZE);
+	GITERR_CHECK_ALLOC(zb);
+
+	zs.next_in = (void *)buff;
+	zs.avail_in = (uInt)len;
+
+	do {
+		zs.next_out = (unsigned char *)zb;
+		zs.avail_out = BUFFER_SIZE;
+
+		if (deflate(&zs, Z_FINISH) == Z_STREAM_ERROR) {
+			git__free(zb);
+			return -1;
+		}
+
+		have = BUFFER_SIZE - (size_t)zs.avail_out;
+
+		if (git_buf_put(buf, zb, have) < 0) {
+			git__free(zb);
+			return -1;
+		}
+
+	} while (zs.avail_out == 0);
+
+	assert(zs.avail_in == 0);
+
+	deflateEnd(&zs);
+	git__free(zb);
+	return 0;
+}
diff --git a/libgit2/src/compress.h b/libgit2/src/compress.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/compress.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_compress_h__
+#define INCLUDE_compress_h__
+
+#include "common.h"
+
+#include "buffer.h"
+
+int git__compress(git_buf *buf, const void *buff, size_t len);
+
+#endif /* INCLUDE_compress_h__ */
diff --git a/libgit2/src/config.c b/libgit2/src/config.c
--- a/libgit2/src/config.c
+++ b/libgit2/src/config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,8 @@
 #include "config.h"
 #include "git2/config.h"
 #include "vector.h"
+#include "buf_text.h"
+#include "config_file.h"
 #if GIT_WIN32
 # include <windows.h>
 #endif
@@ -17,21 +19,29 @@
 #include <ctype.h>
 
 typedef struct {
-	git_config_file *file;
-	int priority;
+	git_refcount rc;
+
+	git_config_backend *file;
+	unsigned int level;
 } file_internal;
 
+static void file_internal_free(file_internal *internal)
+{
+	git_config_backend *file;
+
+	file = internal->file;
+	file->free(file);
+	git__free(internal);
+}
+
 static void config_free(git_config *cfg)
 {
-	unsigned int i;
-	git_config_file *file;
+	size_t i;
 	file_internal *internal;
 
 	for(i = 0; i < cfg->files.length; ++i){
 		internal = git_vector_get(&cfg->files, i);
-		file = internal->file;
-		file->free(file);
-		git__free(internal);
+		GIT_REFCOUNT_DEC(internal, file_internal_free);
 	}
 
 	git_vector_free(&cfg->files);
@@ -51,7 +61,7 @@
 	const file_internal *bk_a = (const file_internal *)(a);
 	const file_internal *bk_b = (const file_internal *)(b);
 
-	return bk_b->priority - bk_a->priority;
+	return bk_b->level - bk_a->level;
 }
 
 int git_config_new(git_config **out)
@@ -73,92 +83,253 @@
 	return 0;
 }
 
-int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
+int git_config_add_file_ondisk(
+	git_config *cfg,
+	const char *path,
+	unsigned int level,
+	int force)
 {
-	git_config_file *file = NULL;
+	git_config_backend *file = NULL;
+	int res;
 
+	assert(cfg && path);
+
+	if (!git_path_isfile(path)) {
+		giterr_set(GITERR_CONFIG, "Cannot find config file '%s'", path);
+		return GIT_ENOTFOUND;
+	}
+
 	if (git_config_file__ondisk(&file, path) < 0)
 		return -1;
 
-	if (git_config_add_file(cfg, file, priority) < 0) {
+	if ((res = git_config_add_backend(cfg, file, level, force)) < 0) {
 		/*
 		 * free manually; the file is not owned by the config
 		 * instance yet and will not be freed on cleanup
 		 */
 		file->free(file);
-		return -1;
+		return res;
 	}
 
 	return 0;
 }
 
-int git_config_open_ondisk(git_config **cfg, const char *path)
+int git_config_open_ondisk(git_config **out, const char *path)
 {
-	if (git_config_new(cfg) < 0)
-		return -1;
+	int error;
+	git_config *config;
 
-	if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
-		git_config_free(*cfg);
+	*out = NULL;
+
+	if (git_config_new(&config) < 0)
 		return -1;
+
+	if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0)
+		git_config_free(config);
+	else
+		*out = config;
+
+	return error;
+}
+
+static int find_internal_file_by_level(
+	file_internal **internal_out,
+	const git_config *cfg,
+	int level)
+{
+	int pos = -1;
+	file_internal *internal;
+	unsigned int i;
+
+	/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
+	 * which has the highest level. As config files are stored in a vector
+	 * sorted by decreasing order of level, getting the file at position 0
+	 * will do the job.
+	 */
+	if (level == GIT_CONFIG_HIGHEST_LEVEL) {
+		pos = 0;
+	} else {
+		git_vector_foreach(&cfg->files, i, internal) {
+			if (internal->level == (unsigned int)level)
+				pos = i;
+		}
 	}
 
+	if (pos == -1) {
+		giterr_set(GITERR_CONFIG,
+			"No config file exists for the given level '%i'", level);
+		return GIT_ENOTFOUND;
+	}
+
+	*internal_out = git_vector_get(&cfg->files, pos);
+
 	return 0;
 }
 
-int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
+static int duplicate_level(void **old_raw, void *new_raw)
 {
+	file_internal **old = (file_internal **)old_raw;
+
+	GIT_UNUSED(new_raw);
+
+	giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (*old)->level);
+	return GIT_EEXISTS;
+}
+
+static void try_remove_existing_file_internal(
+	git_config *cfg,
+	unsigned int level)
+{
+	int pos = -1;
 	file_internal *internal;
+	unsigned int i;
+
+	git_vector_foreach(&cfg->files, i, internal) {
+		if (internal->level == level)
+			pos = i;
+	}
+
+	if (pos == -1)
+		return;
+
+	internal = git_vector_get(&cfg->files, pos);
+
+	if (git_vector_remove(&cfg->files, pos) < 0)
+		return;
+
+	GIT_REFCOUNT_DEC(internal, file_internal_free);
+}
+
+static int git_config__add_internal(
+	git_config *cfg,
+	file_internal *internal,
+	unsigned int level,
+	int force)
+{
 	int result;
 
+	/* delete existing config file for level if it exists */
+	if (force)
+		try_remove_existing_file_internal(cfg, level);
+
+	if ((result = git_vector_insert_sorted(&cfg->files,
+			internal, &duplicate_level)) < 0)
+		return result;
+
+	git_vector_sort(&cfg->files);
+	internal->file->cfg = cfg;
+
+	GIT_REFCOUNT_INC(internal);
+
+	return 0;
+}
+
+int git_config_open_level(
+    git_config **cfg_out,
+    const git_config *cfg_parent,
+    unsigned int level)
+{
+	git_config *cfg;
+	file_internal *internal;
+	int res;
+
+	if ((res = find_internal_file_by_level(&internal, cfg_parent, level)) < 0)
+		return res;
+
+	if ((res = git_config_new(&cfg)) < 0)
+		return res;
+
+	if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
+		git_config_free(cfg);
+		return res;
+	}
+
+	*cfg_out = cfg;
+
+	return 0;
+}
+
+int git_config_add_backend(
+	git_config *cfg,
+	git_config_backend *file,
+	unsigned int level,
+	int force)
+{
+	file_internal *internal;
+	int result;
+
 	assert(cfg && file);
 
-	if ((result = file->open(file)) < 0)
+	GITERR_CHECK_VERSION(file, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
+
+	if ((result = file->open(file, level)) < 0)
 		return result;
 
 	internal = git__malloc(sizeof(file_internal));
 	GITERR_CHECK_ALLOC(internal);
 
+	memset(internal, 0x0, sizeof(file_internal));
+
 	internal->file = file;
-	internal->priority = priority;
+	internal->level = level;
 
-	if (git_vector_insert(&cfg->files, internal) < 0) {
+	if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
 		git__free(internal);
-		return -1;
+		return result;
 	}
 
-	git_vector_sort(&cfg->files);
-	internal->file->cfg = cfg;
-
 	return 0;
 }
 
+int git_config_refresh(git_config *cfg)
+{
+	int error = 0;
+	size_t i;
+
+	for (i = 0; i < cfg->files.length && !error; ++i) {
+		file_internal *internal = git_vector_get(&cfg->files, i);
+		git_config_backend *file = internal->file;
+		error = file->refresh(file);
+	}
+
+	return error;
+}
+
 /*
  * Loop over all the variables
  */
 
-int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
+int git_config_foreach(
+	const git_config *cfg, git_config_foreach_cb cb, void *payload)
 {
+	return git_config_foreach_match(cfg, NULL, cb, payload);
+}
+
+int git_config_foreach_match(
+	const git_config *cfg,
+	const char *regexp,
+	git_config_foreach_cb cb,
+	void *payload)
+{
 	int ret = 0;
-	unsigned int i;
+	size_t i;
 	file_internal *internal;
-	git_config_file *file;
+	git_config_backend *file;
 
-	for(i = 0; i < cfg->files.length && ret == 0; ++i) {
+	for (i = 0; i < cfg->files.length && ret == 0; ++i) {
 		internal = git_vector_get(&cfg->files, i);
 		file = internal->file;
-		ret = file->foreach(file, fn, data);
+		ret = file->foreach(file, regexp, cb, payload);
 	}
 
 	return ret;
 }
 
-int git_config_delete(git_config *cfg, const char *name)
+int git_config_delete_entry(git_config *cfg, const char *name)
 {
+	git_config_backend *file;
 	file_internal *internal;
-	git_config_file *file;
 
-	assert(cfg->files.length);
-
 	internal = git_vector_get(&cfg->files, 0);
 	file = internal->file;
 
@@ -188,10 +359,13 @@
 
 int git_config_set_string(git_config *cfg, const char *name, const char *value)
 {
+	git_config_backend *file;
 	file_internal *internal;
-	git_config_file *file;
 
-	assert(cfg->files.length);
+	if (!value) {
+		giterr_set(GITERR_CONFIG, "The value to set cannot be NULL");
+		return -1;
+	}
 
 	internal = git_vector_get(&cfg->files, 0);
 	file = internal->file;
@@ -199,211 +373,121 @@
 	return file->set(file, name, value);
 }
 
-static int parse_int64(int64_t *out, const char *value)
-{
-	const char *num_end;
-	int64_t num;
-
-	if (git__strtol64(&num, value, &num_end, 0) < 0)
-		return -1;
-
-	switch (*num_end) {
-	case 'g':
-	case 'G':
-		num *= 1024;
-		/* fallthrough */
-
-	case 'm':
-	case 'M':
-		num *= 1024;
-		/* fallthrough */
-
-	case 'k':
-	case 'K':
-		num *= 1024;
-
-		/* check that that there are no more characters after the
-		 * given modifier suffix */
-		if (num_end[1] != '\0')
-			return -1;
-
-		/* fallthrough */
-
-	case '\0':
-		*out = num;
-		return 0;
-
-	default:
-		return -1;
-	}
-}
-
-static int parse_int32(int32_t *out, const char *value)
-{
-	int64_t tmp;
-	int32_t truncate;
-
-	if (parse_int64(&tmp, value) < 0)
-		return -1;
-
-	truncate = tmp & 0xFFFFFFFF;
-	if (truncate != tmp)
-		return -1;
-
-	*out = truncate;
-	return 0;
-}
-
 /***********
  * Getters
  ***********/
-int git_config_lookup_map_value(
-	git_cvar_map *maps, size_t map_n, const char *value, int *out)
-{
-	size_t i;
-
-	if (!value)
-		return GIT_ENOTFOUND;
-
-	for (i = 0; i < map_n; ++i) {
-		git_cvar_map *m = maps + i;
-
-		switch (m->cvar_type) {
-		case GIT_CVAR_FALSE:
-		case GIT_CVAR_TRUE: {
-			int bool_val;
-
-			if (git__parse_bool(&bool_val, value) == 0 &&
-				bool_val == (int)m->cvar_type) {
-				*out = m->map_value;
-				return 0;
-			}
-			break;
-		}
-
-		case GIT_CVAR_INT32:
-			if (parse_int32(out, value) == 0)
-				return 0;
-			break;
-
-		case GIT_CVAR_STRING:
-			if (strcasecmp(value, m->str_match) == 0) {
-				*out = m->map_value;
-				return 0;
-			}
-			break;
-		}
-	}
-
-	return GIT_ENOTFOUND;
-}
-
 int git_config_get_mapped(
 	int *out,
-	git_config *cfg,
+	const git_config *cfg,
 	const char *name,
-	git_cvar_map *maps,
+	const git_cvar_map *maps,
 	size_t map_n)
 {
 	const char *value;
 	int ret;
 
-	ret = git_config_get_string(&value, cfg, name);
-	if (ret < 0)
+	if ((ret = git_config_get_string(&value, cfg, name)) < 0)
 		return ret;
 
-	if (!git_config_lookup_map_value(maps, map_n, value, out))
-		return 0;
-
-	giterr_set(GITERR_CONFIG,
-		"Failed to map the '%s' config variable with a valid value", name);
-	return -1;
+	return git_config_lookup_map_value(out, maps, map_n, value);
 }
 
-int git_config_get_int64(int64_t *out, git_config *cfg, const char *name)
+int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
 {
 	const char *value;
 	int ret;
 
-	ret = git_config_get_string(&value, cfg, name);
-	if (ret < 0)
+	if ((ret = git_config_get_string(&value, cfg, name)) < 0)
 		return ret;
 
-	if (parse_int64(out, value) < 0) {
-		giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
-		return -1;
-	}
-
-	return 0;
+	return git_config_parse_int64(out, value);
 }
 
-int git_config_get_int32(int32_t *out, git_config *cfg, const char *name)
+int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
 {
 	const char *value;
 	int ret;
 
-	ret = git_config_get_string(&value, cfg, name);
-	if (ret < 0)
+	if ((ret = git_config_get_string(&value, cfg, name)) < 0)
 		return ret;
 
-	if (parse_int32(out, value) < 0) {
-		giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
-		return -1;
+	return git_config_parse_int32(out, value);
+}
+
+static int get_string_at_file(const char **out, const git_config_backend *file, const char *name)
+{
+	const git_config_entry *entry;
+	int res;
+
+	res = file->get(file, name, &entry);
+	if (!res)
+		*out = entry->value;
+
+	return res;
+}
+
+static int get_string(const char **out, const git_config *cfg, const char *name)
+{
+	file_internal *internal;
+	unsigned int i;
+
+	git_vector_foreach(&cfg->files, i, internal) {
+		int res = get_string_at_file(out, internal->file, name);
+
+		if (res != GIT_ENOTFOUND)
+			return res;
 	}
 
-	return 0;
+	return GIT_ENOTFOUND;
 }
 
-int git_config_get_bool(int *out, git_config *cfg, const char *name)
+int git_config_get_bool(int *out, const git_config *cfg, const char *name)
 {
-	const char *value;
+	const char *value = NULL;
 	int ret;
 
-	ret = git_config_get_string(&value, cfg, name);
-	if (ret < 0)
+	if ((ret = get_string(&value, cfg, name)) < 0)
 		return ret;
 
-	if (git__parse_bool(out, value) == 0)
-		return 0;
+	return git_config_parse_bool(out, value);
+}
 
-	if (parse_int32(out, value) == 0) {
-		*out = !!(*out);
-		return 0;
-	}
+int git_config_get_string(const char **out, const git_config *cfg, const char *name)
+{
+	int ret;
+	const char *str = NULL;
 
-	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
-	return -1;
+	if ((ret = get_string(&str, cfg, name)) < 0)
+		return ret;
+
+	*out = str == NULL ? "" : str;
+	return 0;
 }
 
-int git_config_get_string(const char **out, git_config *cfg, const char *name)
+int git_config_get_entry(const git_config_entry **out, const git_config *cfg, const char *name)
 {
 	file_internal *internal;
 	unsigned int i;
 
-	assert(cfg->files.length);
-
 	*out = NULL;
 
 	git_vector_foreach(&cfg->files, i, internal) {
-		git_config_file *file = internal->file;
+		git_config_backend *file = internal->file;
 		int ret = file->get(file, name, out);
 		if (ret != GIT_ENOTFOUND)
 			return ret;
 	}
 
-	giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
 	return GIT_ENOTFOUND;
 }
 
-int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
-			    int (*fn)(const char *value, void *data), void *data)
+int git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp,
+		git_config_foreach_cb cb, void *payload)
 {
 	file_internal *internal;
-	git_config_file *file;
+	git_config_backend *file;
 	int ret = GIT_ENOTFOUND;
-	unsigned int i;
-
-	assert(cfg->files.length);
+	size_t i;
 
 	/*
 	 * This loop runs the "wrong" way 'round because we need to
@@ -412,7 +496,7 @@
 	for (i = cfg->files.length; i > 0; --i) {
 		internal = git_vector_get(&cfg->files, i - 1);
 		file = internal->file;
-		ret = file->get_multivar(file, name, regexp, fn, data);
+		ret = file->get_multivar(file, name, regexp, cb, payload);
 		if (ret < 0 && ret != GIT_ENOTFOUND)
 			return ret;
 	}
@@ -422,47 +506,57 @@
 
 int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
 {
+	git_config_backend *file;
 	file_internal *internal;
-	git_config_file *file;
-	int ret = GIT_ENOTFOUND;
-	unsigned int i;
 
-	for (i = cfg->files.length; i > 0; --i) {
-		internal = git_vector_get(&cfg->files, i - 1);
-		file = internal->file;
-		ret = file->set_multivar(file, name, regexp, value);
-		if (ret < 0 && ret != GIT_ENOTFOUND)
-			return ret;
+	internal = git_vector_get(&cfg->files, 0);
+	file = internal->file;
+
+	return file->set_multivar(file, name, regexp, value);
+}
+
+static int git_config__find_file_to_path(
+	char *out, size_t outlen, int (*find)(git_buf *buf))
+{
+	int error = 0;
+	git_buf path = GIT_BUF_INIT;
+
+	if ((error = find(&path)) < 0)
+		goto done;
+
+	if (path.size >= outlen) {
+		giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
+		error = GIT_EBUFS;
+		goto done;
 	}
 
-	return 0;
+	git_buf_copy_cstr(out, outlen, &path);
+
+done:
+	git_buf_free(&path);
+	return error;
 }
 
 int git_config_find_global_r(git_buf *path)
 {
-	return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+	return git_futils_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
 }
 
 int git_config_find_global(char *global_config_path, size_t length)
 {
-	git_buf path  = GIT_BUF_INIT;
-	int     ret = git_config_find_global_r(&path);
-
-	if (ret < 0) {
-		git_buf_free(&path);
-		return ret;
-	}
+	return git_config__find_file_to_path(
+		global_config_path, length, git_config_find_global_r);
+}
 
-	if (path.size >= length) {
-		git_buf_free(&path);
-		giterr_set(GITERR_NOMEMORY,
-			"Path is to long to fit on the given buffer");
-		return -1;
-	}
+int git_config_find_xdg_r(git_buf *path)
+{
+	return git_futils_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
+}
 
-	git_buf_copy_cstr(global_config_path, length, &path);
-	git_buf_free(&path);
-	return 0;
+int git_config_find_xdg(char *xdg_config_path, size_t length)
+{
+	return git_config__find_file_to_path(
+		xdg_config_path, length, git_config_find_xdg_r);
 }
 
 int git_config_find_system_r(git_buf *path)
@@ -472,37 +566,244 @@
 
 int git_config_find_system(char *system_config_path, size_t length)
 {
-	git_buf path  = GIT_BUF_INIT;
-	int     ret = git_config_find_system_r(&path);
+	return git_config__find_file_to_path(
+		system_config_path, length, git_config_find_system_r);
+}
 
-	if (ret < 0) {
-		git_buf_free(&path);
-		return ret;
+int git_config_open_default(git_config **out)
+{
+	int error;
+	git_config *cfg = NULL;
+	git_buf buf = GIT_BUF_INIT;
+
+	error = git_config_new(&cfg);
+
+	if (!error && !git_config_find_global_r(&buf))
+		error = git_config_add_file_ondisk(cfg, buf.ptr,
+			GIT_CONFIG_LEVEL_GLOBAL, 0);
+
+	if (!error && !git_config_find_xdg_r(&buf))
+		error = git_config_add_file_ondisk(cfg, buf.ptr,
+			GIT_CONFIG_LEVEL_XDG, 0);
+
+	if (!error && !git_config_find_system_r(&buf))
+		error = git_config_add_file_ondisk(cfg, buf.ptr,
+			GIT_CONFIG_LEVEL_SYSTEM, 0);
+
+	git_buf_free(&buf);
+
+	if (error && cfg) {
+		git_config_free(cfg);
+		cfg = NULL;
 	}
 
-	if (path.size >= length) {
-		git_buf_free(&path);
-		giterr_set(GITERR_NOMEMORY,
-			"Path is to long to fit on the given buffer");
-		return -1;
+	*out = cfg;
+
+	return error;
+}
+
+/***********
+ * Parsers
+ ***********/
+int git_config_lookup_map_value(
+	int *out,
+	const git_cvar_map *maps,
+	size_t map_n,
+	const char *value)
+{
+	size_t i;
+
+	if (!value)
+		goto fail_parse;
+
+	for (i = 0; i < map_n; ++i) {
+		const git_cvar_map *m = maps + i;
+
+		switch (m->cvar_type) {
+		case GIT_CVAR_FALSE:
+		case GIT_CVAR_TRUE: {
+			int bool_val;
+
+			if (git__parse_bool(&bool_val, value) == 0 &&
+				bool_val == (int)m->cvar_type) {
+				*out = m->map_value;
+				return 0;
+			}
+			break;
+		}
+
+		case GIT_CVAR_INT32:
+			if (git_config_parse_int32(out, value) == 0)
+				return 0;
+			break;
+
+		case GIT_CVAR_STRING:
+			if (strcasecmp(value, m->str_match) == 0) {
+				*out = m->map_value;
+				return 0;
+			}
+			break;
+		}
 	}
 
-	git_buf_copy_cstr(system_config_path, length, &path);
-	git_buf_free(&path);
+fail_parse:
+	giterr_set(GITERR_CONFIG, "Failed to map '%s'", value);
+	return -1;
+}
+
+int git_config_parse_bool(int *out, const char *value)
+{
+	if (git__parse_bool(out, value) == 0)
+		return 0;
+
+	if (git_config_parse_int32(out, value) == 0) {
+		*out = !!(*out);
+		return 0;
+	}
+
+	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
+	return -1;
+}
+
+int git_config_parse_int64(int64_t *out, const char *value)
+{
+	const char *num_end;
+	int64_t num;
+
+	if (git__strtol64(&num, value, &num_end, 0) < 0)
+		goto fail_parse;
+
+	switch (*num_end) {
+	case 'g':
+	case 'G':
+		num *= 1024;
+		/* fallthrough */
+
+	case 'm':
+	case 'M':
+		num *= 1024;
+		/* fallthrough */
+
+	case 'k':
+	case 'K':
+		num *= 1024;
+
+		/* check that that there are no more characters after the
+		 * given modifier suffix */
+		if (num_end[1] != '\0')
+			return -1;
+
+		/* fallthrough */
+
+	case '\0':
+		*out = num;
+		return 0;
+
+	default:
+		goto fail_parse;
+	}
+
+fail_parse:
+	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
+	return -1;
+}
+
+int git_config_parse_int32(int32_t *out, const char *value)
+{
+	int64_t tmp;
+	int32_t truncate;
+
+	if (git_config_parse_int64(&tmp, value) < 0)
+		goto fail_parse;
+
+	truncate = tmp & 0xFFFFFFFF;
+	if (truncate != tmp)
+		goto fail_parse;
+
+	*out = truncate;
 	return 0;
+
+fail_parse:
+	giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
+	return -1;
 }
 
-int git_config_open_global(git_config **out)
+struct rename_data {
+	git_config *config;
+	git_buf *name;
+	size_t old_len;
+	int actual_error;
+};
+
+static int rename_config_entries_cb(
+	const git_config_entry *entry,
+	void *payload)
 {
-	int error;
-	git_buf path = GIT_BUF_INIT;
+	int error = 0;
+	struct rename_data *data = (struct rename_data *)payload;
+	size_t base_len = git_buf_len(data->name);
 
-	if ((error = git_config_find_global_r(&path)) < 0)
-		return error;
+	if (base_len > 0 &&
+		!(error = git_buf_puts(data->name, entry->name + data->old_len)))
+	{
+		error = git_config_set_string(
+			data->config, git_buf_cstr(data->name), entry->value);
 
-	error = git_config_open_ondisk(out, git_buf_cstr(&path));
-	git_buf_free(&path);
+		git_buf_truncate(data->name, base_len);
+	}
 
+	if (!error)
+		error = git_config_delete_entry(data->config, entry->name);
+
+	data->actual_error = error; /* preserve actual error code */
+
 	return error;
 }
 
+int git_config_rename_section(
+	git_repository *repo,
+	const char *old_section_name,
+	const char *new_section_name)
+{
+	git_config *config;
+	git_buf pattern = GIT_BUF_INIT, replace = GIT_BUF_INIT;
+	int error = 0;
+	struct rename_data data;
+
+	git_buf_text_puts_escape_regex(&pattern, old_section_name);
+
+	if ((error = git_buf_puts(&pattern, "\\..+")) < 0)
+		goto cleanup;
+
+	if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+		goto cleanup;
+
+	data.config  = config;
+	data.name    = &replace;
+	data.old_len = strlen(old_section_name) + 1;
+	data.actual_error = 0;
+
+	if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0)
+		goto cleanup;
+
+	if (new_section_name != NULL &&
+		(error = git_config_file_normalize_section(
+			replace.ptr, strchr(replace.ptr, '.'))) < 0)
+	{
+		giterr_set(
+			GITERR_CONFIG, "Invalid config section '%s'", new_section_name);
+		goto cleanup;
+	}
+
+	error = git_config_foreach_match(
+		config, git_buf_cstr(&pattern), rename_config_entries_cb, &data);
+
+	if (error == GIT_EUSER)
+		error = data.actual_error;
+
+cleanup:
+	git_buf_free(&pattern);
+	git_buf_free(&replace);
+
+	return error;
+}
diff --git a/libgit2/src/config.h b/libgit2/src/config.h
--- a/libgit2/src/config.h
+++ b/libgit2/src/config.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,9 +12,11 @@
 #include "vector.h"
 #include "repository.h"
 
-#define GIT_CONFIG_FILENAME ".gitconfig"
-#define GIT_CONFIG_FILENAME_INREPO "config"
 #define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
+#define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig"
+#define GIT_CONFIG_FILENAME_XDG    "config"
+
+#define GIT_CONFIG_FILENAME_INREPO "config"
 #define GIT_CONFIG_FILE_MODE 0666
 
 struct git_config {
@@ -23,11 +25,25 @@
 };
 
 extern int git_config_find_global_r(git_buf *global_config_path);
+extern int git_config_find_xdg_r(git_buf *system_config_path);
 extern int git_config_find_system_r(git_buf *system_config_path);
 
-extern int git_config_parse_bool(int *out, const char *bool_string);
+extern int git_config_rename_section(
+	git_repository *repo,
+	const char *old_section_name,	/* eg "branch.dummy" */
+	const char *new_section_name);	/* NULL to drop the old section */
 
-extern int git_config_lookup_map_value(
-	git_cvar_map *maps, size_t map_n, const char *value, int *out);
+/**
+ * Create a configuration file backend for ondisk files
+ *
+ * These are the normal `.gitconfig` files that Core Git
+ * processes. Note that you first have to add this file to a
+ * configuration object before you can query it for configuration
+ * variables.
+ *
+ * @param out the new backend
+ * @param path where the config file is located
+ */
+extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
 
 #endif
diff --git a/libgit2/src/config_cache.c b/libgit2/src/config_cache.c
--- a/libgit2/src/config_cache.c
+++ b/libgit2/src/config_cache.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -24,7 +24,7 @@
  *	core.eol
  *		Sets the line ending type to use in the working directory for
  *	files that have the text property set. Alternatives are lf, crlf
- *	and native, which uses the platform’s native line ending. The default
+ *	and native, which uses the platform's native line ending. The default
  *	value is native. See gitattributes(5) for more information on
  *	end-of-line conversion. 
  */
diff --git a/libgit2/src/config_file.c b/libgit2/src/config_file.c
--- a/libgit2/src/config_file.c
+++ b/libgit2/src/config_file.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
 #include "fileops.h"
 #include "filebuf.h"
 #include "buffer.h"
+#include "buf_text.h"
 #include "git2/config.h"
 #include "git2/types.h"
 #include "strmap.h"
@@ -22,15 +23,9 @@
 
 typedef struct cvar_t {
 	struct cvar_t *next;
-	char *key; /* TODO: we might be able to get rid of this */
-	char *value;
+	git_config_entry *entry;
 } cvar_t;
 
-typedef struct {
-	struct cvar_t *head;
-	struct cvar_t *tail;
-} cvar_t_list;
-
 #define CVAR_LIST_HEAD(list) ((list)->head)
 
 #define CVAR_LIST_TAIL(list) ((list)->tail)
@@ -70,7 +65,7 @@
 		 (iter) = (tmp))
 
 typedef struct {
-	git_config_file parent;
+	git_config_backend parent;
 
 	git_strmap *values;
 
@@ -81,12 +76,17 @@
 		int eof;
 	} reader;
 
-	char *file_path;
+	char  *file_path;
+	time_t file_mtime;
+	size_t file_size;
+
+	unsigned int level;
 } diskfile_backend;
 
-static int config_parse(diskfile_backend *cfg_file);
+static int config_parse(diskfile_backend *cfg_file, unsigned int level);
 static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
 static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
+static char *escape_value(const char *ptr);
 
 static void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
 {
@@ -99,11 +99,35 @@
 	if (var == NULL)
 		return;
 
-	git__free(var->key);
-	git__free(var->value);
+	git__free((char*)var->entry->name);
+	git__free((char *)var->entry->value);
+	git__free(var->entry);
 	git__free(var);
 }
 
+int git_config_file_normalize_section(char *start, char *end)
+{
+	char *scan;
+
+	if (start == end)
+		return GIT_EINVALIDSPEC;
+
+	/* Validate and downcase range */
+	for (scan = start; *scan; ++scan) {
+		if (end && scan >= end)
+			break;
+		if (isalnum(*scan))
+			*scan = tolower(*scan);
+		else if (*scan != '-' || scan == start)
+			return GIT_EINVALIDSPEC;
+	}
+
+	if (scan == start)
+		return GIT_EINVALIDSPEC;
+
+	return 0;
+}
+
 /* Take something the user gave us and make it nice for our hash function */
 static int normalize_name(const char *in, char **out)
 {
@@ -117,19 +141,26 @@
 	fdot = strchr(name, '.');
 	ldot = strrchr(name, '.');
 
-	if (fdot == NULL || ldot == NULL) {
-		git__free(name);
-		giterr_set(GITERR_CONFIG,
-			"Invalid variable name: '%s'", in);
-		return -1;
-	}
+	if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
+		goto invalid;
 
-	/* Downcase up to the first dot and after the last one */
-	git__strntolower(name, fdot - name);
-	git__strtolower(ldot);
+	/* Validate and downcase up to first dot and after last dot */
+	if (git_config_file_normalize_section(name, fdot) < 0 ||
+		git_config_file_normalize_section(ldot + 1, NULL) < 0)
+		goto invalid;
 
+	/* If there is a middle range, make sure it doesn't have newlines */
+	while (fdot < ldot)
+		if (*fdot++ == '\n')
+			goto invalid;
+
 	*out = name;
 	return 0;
+
+invalid:
+	git__free(name);
+	giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
+	return GIT_EINVALIDSPEC;
 }
 
 static void free_vars(git_strmap *values)
@@ -149,34 +180,62 @@
 	git_strmap_free(values);
 }
 
-static int config_open(git_config_file *cfg)
+static int config_open(git_config_backend *cfg, unsigned int level)
 {
 	int res;
 	diskfile_backend *b = (diskfile_backend *)cfg;
 
+	b->level = level;
+
 	b->values = git_strmap_alloc();
 	GITERR_CHECK_ALLOC(b->values);
 
 	git_buf_init(&b->reader.buffer, 0);
-	res = git_futils_readbuffer(&b->reader.buffer, b->file_path);
+	res = git_futils_readbuffer_updated(
+		&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, NULL);
 
 	/* It's fine if the file doesn't exist */
 	if (res == GIT_ENOTFOUND)
 		return 0;
 
-	if (res < 0 || config_parse(b) <  0) {
+	if (res < 0 || (res = config_parse(b, level)) < 0) {
 		free_vars(b->values);
 		b->values = NULL;
-		git_buf_free(&b->reader.buffer);
-		return -1;
 	}
 
 	git_buf_free(&b->reader.buffer);
-	return 0;
+	return res;
 }
 
-static void backend_free(git_config_file *_backend)
+static int config_refresh(git_config_backend *cfg)
 {
+	int res, updated = 0;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	git_strmap *old_values;
+
+	res = git_futils_readbuffer_updated(
+		&b->reader.buffer, b->file_path, &b->file_mtime, &b->file_size, &updated);
+	if (res < 0 || !updated)
+		return (res == GIT_ENOTFOUND) ? 0 : res;
+
+	/* need to reload - store old values and prep for reload */
+	old_values = b->values;
+	b->values = git_strmap_alloc();
+	GITERR_CHECK_ALLOC(b->values);
+
+	if ((res = config_parse(b, b->level)) < 0) {
+		free_vars(b->values);
+		b->values = old_values;
+	} else {
+		free_vars(old_values);
+	}
+
+	git_buf_free(&b->reader.buffer);
+	return res;
+}
+
+static void backend_free(git_config_backend *_backend)
+{
 	diskfile_backend *backend = (diskfile_backend *)_backend;
 
 	if (backend == NULL)
@@ -187,37 +246,63 @@
 	git__free(backend);
 }
 
-static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
+static int file_foreach(
+	git_config_backend *backend,
+	const char *regexp,
+	int (*fn)(const git_config_entry *, void *),
+	void *data)
 {
 	diskfile_backend *b = (diskfile_backend *)backend;
-	cvar_t *var;
+	cvar_t *var, *next_var;
 	const char *key;
+	regex_t regex;
+	int result = 0;
 
 	if (!b->values)
 		return 0;
 
+	if (regexp != NULL) {
+		if ((result = regcomp(&regex, regexp, REG_EXTENDED)) < 0) {
+			giterr_set_regex(&regex, result);
+			regfree(&regex);
+			return -1;
+		}
+	}
+
 	git_strmap_foreach(b->values, key, var,
-		do {
-			if (fn(key, var->value, data) < 0)
-				break;
+		for (; var != NULL; var = next_var) {
+			next_var = CVAR_LIST_NEXT(var);
 
-			var = CVAR_LIST_NEXT(var);
-		} while (var != NULL);
+			/* skip non-matching keys if regexp was provided */
+			if (regexp && regexec(&regex, key, 0, NULL, 0) != 0)
+				continue;
+
+			/* abort iterator on non-zero return value */
+			if (fn(var->entry, data)) {
+				giterr_clear();
+				result = GIT_EUSER;
+				goto cleanup;
+			}
+		}
 	);
 
-	return 0;
+cleanup:
+	if (regexp != NULL)
+		regfree(&regex);
+
+	return result;
 }
 
-static int config_set(git_config_file *cfg, const char *name, const char *value)
+static int config_set(git_config_backend *cfg, const char *name, const char *value)
 {
 	cvar_t *var = NULL, *old_var;
 	diskfile_backend *b = (diskfile_backend *)cfg;
-	char *key;
+	char *key, *esc_value = NULL;
 	khiter_t pos;
-	int rval;
+	int rval, ret;
 
-	if (normalize_name(name, &key) < 0)
-		return -1;
+	if ((rval = normalize_name(name, &key)) < 0)
+		return rval;
 
 	/*
 	 * Try to find it in the existing values and update it if it
@@ -229,40 +314,57 @@
 		char *tmp = NULL;
 
 		git__free(key);
+
 		if (existing->next != NULL) {
 			giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
 			return -1;
 		}
 
+		/* don't update if old and new values already match */
+		if ((!existing->entry->value && !value) ||
+			(existing->entry->value && value && !strcmp(existing->entry->value, value)))
+			return 0;
+
 		if (value) {
 			tmp = git__strdup(value);
 			GITERR_CHECK_ALLOC(tmp);
+			esc_value = escape_value(value);
+			GITERR_CHECK_ALLOC(esc_value);
 		}
 
-		git__free(existing->value);
-		existing->value = tmp;
+		git__free((void *)existing->entry->value);
+		existing->entry->value = tmp;
 
-		return config_write(b, existing->key, NULL, value);
+		ret = config_write(b, existing->entry->name, NULL, esc_value);
+
+		git__free(esc_value);
+		return ret;
 	}
 
 	var = git__malloc(sizeof(cvar_t));
 	GITERR_CHECK_ALLOC(var);
-
 	memset(var, 0x0, sizeof(cvar_t));
+	var->entry = git__malloc(sizeof(git_config_entry));
+	GITERR_CHECK_ALLOC(var->entry);
+	memset(var->entry, 0x0, sizeof(git_config_entry));
 
-	var->key = key;
-	var->value = NULL;
+	var->entry->name = key;
+	var->entry->value = NULL;
 
 	if (value) {
-		var->value = git__strdup(value);
-		GITERR_CHECK_ALLOC(var->value);
+		var->entry->value = git__strdup(value);
+		GITERR_CHECK_ALLOC(var->entry->value);
+		esc_value = escape_value(value);
+		GITERR_CHECK_ALLOC(esc_value);
 	}
 
-	if (config_write(b, key, NULL, value) < 0) {
+	if (config_write(b, key, NULL, esc_value) < 0) {
+		git__free(esc_value);
 		cvar_free(var);
 		return -1;
 	}
 
+	git__free(esc_value);
 	git_strmap_insert2(b->values, key, var, old_var, rval);
 	if (rval < 0)
 		return -1;
@@ -275,14 +377,15 @@
 /*
  * Internal function that actually gets the value in string form
  */
-static int config_get(git_config_file *cfg, const char *name, const char **out)
+static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out)
 {
 	diskfile_backend *b = (diskfile_backend *)cfg;
 	char *key;
 	khiter_t pos;
+	int error;
 
-	if (normalize_name(name, &key) < 0)
-		return -1;
+	if ((error = normalize_name(name, &key)) < 0)
+		return error;
 
 	pos = git_strmap_lookup_index(b->values, key);
 	git__free(key);
@@ -291,25 +394,26 @@
 	if (!git_strmap_valid_index(b->values, pos))
 		return GIT_ENOTFOUND;
 
-	*out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
+	*out = ((cvar_t *)git_strmap_value_at(b->values, pos))->entry;
 
 	return 0;
 }
 
 static int config_get_multivar(
-	git_config_file *cfg,
+	git_config_backend *cfg,
 	const char *name,
 	const char *regex_str,
-	int (*fn)(const char *, void *),
+	int (*fn)(const git_config_entry *, void *),
 	void *data)
 {
 	cvar_t *var;
 	diskfile_backend *b = (diskfile_backend *)cfg;
 	char *key;
 	khiter_t pos;
+	int error;
 
-	if (normalize_name(name, &key) < 0)
-		return -1;
+	if ((error = normalize_name(name, &key)) < 0)
+		return error;
 
 	pos = git_strmap_lookup_index(b->values, key);
 	git__free(key);
@@ -327,16 +431,17 @@
 		result = regcomp(&regex, regex_str, REG_EXTENDED);
 		if (result < 0) {
 			giterr_set_regex(&regex, result);
+			regfree(&regex);
 			return -1;
 		}
 
 		/* and throw the callback only on the variables that
 		 * match the regex */
 		do {
-			if (regexec(&regex, var->value, 0, NULL, 0) == 0) {
+			if (regexec(&regex, var->entry->value, 0, NULL, 0) == 0) {
 				/* early termination by the user is not an error;
 				 * just break and return successfully */
-				if (fn(var->value, data) < 0)
+				if (fn(var->entry, data) < 0)
 					break;
 			}
 
@@ -348,7 +453,7 @@
 		do {
 			/* early termination by the user is not an error;
 			 * just break and return successfully */
-			if (fn(var->value, data) < 0)
+			if (fn(var->entry, data) < 0)
 				break;
 
 			var = var->next;
@@ -359,7 +464,7 @@
 }
 
 static int config_set_multivar(
-	git_config_file *cfg, const char *name, const char *regexp, const char *value)
+	git_config_backend *cfg, const char *name, const char *regexp, const char *value)
 {
 	int replaced = 0;
 	cvar_t *var, *newvar;
@@ -371,8 +476,8 @@
 
 	assert(regexp);
 
-	if (normalize_name(name, &key) < 0)
-		return -1;
+	if ((result = normalize_name(name, &key)) < 0)
+		return result;
 
 	pos = git_strmap_lookup_index(b->values, key);
 	if (!git_strmap_valid_index(b->values, pos)) {
@@ -386,16 +491,17 @@
 	if (result < 0) {
 		git__free(key);
 		giterr_set_regex(&preg, result);
+		regfree(&preg);
 		return -1;
 	}
 
 	for (;;) {
-		if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
+		if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) {
 			char *tmp = git__strdup(value);
 			GITERR_CHECK_ALLOC(tmp);
 
-			git__free(var->value);
-			var->value = tmp;
+			git__free((void *)var->entry->value);
+			var->entry->value = tmp;
 			replaced = 1;
 		}
 
@@ -409,15 +515,19 @@
 	if (!replaced) {
 		newvar = git__malloc(sizeof(cvar_t));
 		GITERR_CHECK_ALLOC(newvar);
-
 		memset(newvar, 0x0, sizeof(cvar_t));
+		newvar->entry = git__malloc(sizeof(git_config_entry));
+		GITERR_CHECK_ALLOC(newvar->entry);
+		memset(newvar->entry, 0x0, sizeof(git_config_entry));
 
-		newvar->key = git__strdup(var->key);
-		GITERR_CHECK_ALLOC(newvar->key);
+		newvar->entry->name = git__strdup(var->entry->name);
+		GITERR_CHECK_ALLOC(newvar->entry->name);
 
-		newvar->value = git__strdup(value);
-		GITERR_CHECK_ALLOC(newvar->value);
+		newvar->entry->value = git__strdup(value);
+		GITERR_CHECK_ALLOC(newvar->entry->value);
 
+		newvar->entry->level = var->entry->level;
+
 		var->next = newvar;
 	}
 
@@ -429,7 +539,7 @@
 	return result;
 }
 
-static int config_delete(git_config_file *cfg, const char *name)
+static int config_delete(git_config_backend *cfg, const char *name)
 {
 	cvar_t *var;
 	diskfile_backend *b = (diskfile_backend *)cfg;
@@ -437,14 +547,16 @@
 	int result;
 	khiter_t pos;
 
-	if (normalize_name(name, &key) < 0)
-		return -1;
+	if ((result = normalize_name(name, &key)) < 0)
+		return result;
 
 	pos = git_strmap_lookup_index(b->values, key);
 	git__free(key);
 
-	if (!git_strmap_valid_index(b->values, pos))
+	if (!git_strmap_valid_index(b->values, pos)) {
+		giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name);
 		return GIT_ENOTFOUND;
+	}
 
 	var = git_strmap_value_at(b->values, pos);
 
@@ -455,20 +567,20 @@
 
 	git_strmap_delete_at(b->values, pos);
 
-	result = config_write(b, var->key, NULL, NULL);
+	result = config_write(b, var->entry->name, NULL, NULL);
 
 	cvar_free(var);
 	return result;
 }
 
-int git_config_file__ondisk(git_config_file **out, const char *path)
+int git_config_file__ondisk(git_config_backend **out, const char *path)
 {
 	diskfile_backend *backend;
 
-	backend = git__malloc(sizeof(diskfile_backend));
+	backend = git__calloc(1, sizeof(diskfile_backend));
 	GITERR_CHECK_ALLOC(backend);
 
-	memset(backend, 0x0, sizeof(diskfile_backend));
+	backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
 
 	backend->file_path = git__strdup(path);
 	GITERR_CHECK_ALLOC(backend->file_path);
@@ -480,9 +592,10 @@
 	backend->parent.set_multivar = config_set_multivar;
 	backend->parent.del = config_delete;
 	backend->parent.foreach = file_foreach;
+	backend->parent.refresh = config_refresh;
 	backend->parent.free = backend_free;
 
-	*out = (git_config_file *)backend;
+	*out = (git_config_backend *)backend;
 
 	return 0;
 }
@@ -774,17 +887,14 @@
 
 static int skip_bom(diskfile_backend *cfg)
 {
-	static const char utf8_bom[] = "\xef\xbb\xbf";
-
-	if (cfg->reader.buffer.size < sizeof(utf8_bom))
-		return 0;
+	git_bom_t bom;
+	int bom_offset = git_buf_text_detect_bom(&bom,
+		&cfg->reader.buffer, cfg->reader.read_ptr - cfg->reader.buffer.ptr);
 
-	if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
-		cfg->reader.read_ptr += sizeof(utf8_bom);
+	if (bom == GIT_BOM_UTF8)
+		cfg->reader.read_ptr += bom_offset;
 
-	/* TODO: the reference implementation does pretty stupid
-		shit with the BoM
-	*/
+	/* TODO: reference implementation is pretty stupid with BoM */
 
 	return 0;
 }
@@ -844,7 +954,7 @@
 	}
 
 	/* skip any space at the end */
-	if (git__isspace(ptr[-1])) {
+	if (ptr > line && git__isspace(ptr[-1])) {
 		ptr--;
 	}
 	ptr[0] = '\0';
@@ -852,7 +962,7 @@
 	return quote_count;
 }
 
-static int config_parse(diskfile_backend *cfg_file)
+static int config_parse(diskfile_backend *cfg_file, unsigned int level)
 {
 	int c;
 	char *current_section = NULL;
@@ -900,8 +1010,10 @@
 
 			var = git__malloc(sizeof(cvar_t));
 			GITERR_CHECK_ALLOC(var);
-
 			memset(var, 0x0, sizeof(cvar_t));
+			var->entry = git__malloc(sizeof(git_config_entry));
+			GITERR_CHECK_ALLOC(var->entry);
+			memset(var->entry, 0x0, sizeof(git_config_entry));
 
 			git__strtolower(var_name);
 			git_buf_printf(&buf, "%s.%s", current_section, var_name);
@@ -910,13 +1022,14 @@
 			if (git_buf_oom(&buf))
 				return -1;
 
-			var->key = git_buf_detach(&buf);
-			var->value = var_value;
+			var->entry->name = git_buf_detach(&buf);
+			var->entry->value = var_value;
+			var->entry->level = level;
 
 			/* Add or append the new config option */
-			pos = git_strmap_lookup_index(cfg_file->values, var->key);
+			pos = git_strmap_lookup_index(cfg_file->values, var->entry->name);
 			if (!git_strmap_valid_index(cfg_file->values, pos)) {
-				git_strmap_insert(cfg_file->values, var->key, var, result);
+				git_strmap_insert(cfg_file->values, var->entry->name, var, result);
 				if (result < 0)
 					break;
 				result = 0;
@@ -948,9 +1061,12 @@
 	if (dot == NULL) {
 		git_buf_puts(&buf, key);
 	} else {
+		char *escaped;
 		git_buf_put(&buf, key, dot - key);
-		/* TODO: escape  */
-		git_buf_printf(&buf, " \"%s\"", dot + 1);
+		escaped = escape_value(dot + 1);
+		GITERR_CHECK_ALLOC(escaped);
+		git_buf_printf(&buf, " \"%s\"", escaped);
+		git__free(escaped);
 	}
 	git_buf_puts(&buf, "]\n");
 
@@ -1133,6 +1249,10 @@
 				goto rewrite_fail;
 			}
 
+			/* If we are here, there is at least a section line */
+			if (cfg->reader.buffer.size > 0 && *(cfg->reader.buffer.ptr + cfg->reader.buffer.size - 1) != '\n')
+				git_filebuf_write(&file, "\n", 1);
+
 			git_filebuf_printf(&file, "\t%s = %s\n", name, value);
 		}
 	}
@@ -1140,8 +1260,12 @@
 	git__free(section);
 	git__free(current_section);
 
+	/* refresh stats - if this errors, then commit will error too */
+	(void)git_filebuf_stats(&cfg->file_mtime, &cfg->file_size, &file);
+
 	result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
 	git_buf_free(&cfg->reader.buffer);
+
 	return result;
 
 rewrite_fail:
@@ -1153,13 +1277,44 @@
 	return -1;
 }
 
+static const char *escapes = "ntb\"\\";
+static const char *escaped = "\n\t\b\"\\";
+
+/* Escape the values to write them to the file */
+static char *escape_value(const char *ptr)
+{
+	git_buf buf = GIT_BUF_INIT;
+	size_t len;
+	const char *esc;
+
+	assert(ptr);
+
+	len = strlen(ptr);
+	git_buf_grow(&buf, len);
+
+	while (*ptr != '\0') {
+		if ((esc = strchr(escaped, *ptr)) != NULL) {
+			git_buf_putc(&buf, '\\');
+			git_buf_putc(&buf, escapes[esc - escaped]);
+		} else {
+			git_buf_putc(&buf, *ptr);
+		}
+		ptr++;
+	}
+
+	if (git_buf_oom(&buf)) {
+		git_buf_free(&buf);
+		return NULL;
+	}
+
+	return git_buf_detach(&buf);
+}
+
 /* '\"' -> '"' etc */
 static char *fixup_line(const char *ptr, int quote_count)
 {
 	char *str = git__malloc(strlen(ptr) + 1);
 	char *out = str, *esc;
-	const char *escapes = "ntb\"\\";
-	const char *escaped = "\n\t\b\"\\";
 
 	if (str == NULL)
 		return NULL;
@@ -1196,8 +1351,15 @@
 
 static int is_multiline_var(const char *str)
 {
+	int count = 0;
 	const char *end = str + strlen(str);
-	return (end > str) && (end[-1] == '\\');
+	while (end > str && end[-1] == '\\') {
+		count++;
+		end--;
+	}
+
+	/* An odd number means last backslash wasn't escaped, so it's multiline */
+	return (end > str) && (count & 1);
 }
 
 static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
@@ -1272,10 +1434,8 @@
 	else
 		value_start = var_end + 1;
 
-	if (git__isspace(var_end[-1])) {
-		do var_end--;
-		while (git__isspace(var_end[0]));
-	}
+	do var_end--;
+	while (var_end>line && git__isspace(*var_end));
 
 	*var_name = git__strndup(line, var_end - line + 1);
 	GITERR_CHECK_ALLOC(*var_name);
@@ -1309,8 +1469,10 @@
 		else if (value_start[0] != '\0') {
 			*var_value = fixup_line(value_start, 0);
 			GITERR_CHECK_ALLOC(*var_value);
+		} else { /* equals sign but missing rhs */
+			*var_value = git__strdup("");
+			GITERR_CHECK_ALLOC(*var_value);
 		}
-
 	}
 
 	git__free(line);
diff --git a/libgit2/src/config_file.h b/libgit2/src/config_file.h
--- a/libgit2/src/config_file.h
+++ b/libgit2/src/config_file.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,23 +9,52 @@
 
 #include "git2/config.h"
 
-GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
+GIT_INLINE(int) git_config_file_open(git_config_backend *cfg, unsigned int level)
 {
-	return cfg->open(cfg);
+	return cfg->open(cfg, level);
 }
 
-GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
+GIT_INLINE(void) git_config_file_free(git_config_backend *cfg)
 {
 	cfg->free(cfg);
 }
 
+GIT_INLINE(int) git_config_file_get_string(
+	const git_config_entry **out, git_config_backend *cfg, const char *name)
+{
+	return cfg->get(cfg, name, out);
+}
+
+GIT_INLINE(int) git_config_file_set_string(
+	git_config_backend *cfg, const char *name, const char *value)
+{
+	return cfg->set(cfg, name, value);
+}
+
+GIT_INLINE(int) git_config_file_delete(
+	git_config_backend *cfg, const char *name)
+{
+	return cfg->del(cfg, name);
+}
+
 GIT_INLINE(int) git_config_file_foreach(
-	git_config_file *cfg,
-	int (*fn)(const char *key, const char *value, void *data),
+	git_config_backend *cfg,
+	int (*fn)(const git_config_entry *entry, void *data),
 	void *data)
 {
-	return cfg->foreach(cfg, fn, data);
+	return cfg->foreach(cfg, NULL, fn, data);
 }
+
+GIT_INLINE(int) git_config_file_foreach_match(
+	git_config_backend *cfg,
+	const char *regexp,
+	int (*fn)(const git_config_entry *entry, void *data),
+	void *data)
+{
+	return cfg->foreach(cfg, regexp, fn, data);
+}
+
+extern int git_config_file_normalize_section(char *start, char *end);
 
 #endif
 
diff --git a/libgit2/src/crlf.c b/libgit2/src/crlf.c
--- a/libgit2/src/crlf.c
+++ b/libgit2/src/crlf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,9 +9,10 @@
 #include "fileops.h"
 #include "hash.h"
 #include "filter.h"
+#include "buf_text.h"
 #include "repository.h"
-
 #include "git2/attr.h"
+#include "git2/blob.h"
 
 struct crlf_attrs {
 	int crlf_action;
@@ -21,6 +22,8 @@
 struct crlf_filter {
 	git_filter f;
 	struct crlf_attrs attrs;
+	git_repository *repo;
+	char path[GIT_FLEX_ARRAY];
 };
 
 static int check_crlf(const char *value)
@@ -103,36 +106,46 @@
 	return -1;
 }
 
-static int drop_crlf(git_buf *dest, const git_buf *source)
+static int has_cr_in_index(git_filter *self)
 {
-	const char *scan = source->ptr, *next;
-	const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
+	struct crlf_filter *filter = (struct crlf_filter *)self;
+	git_index *index;
+	const git_index_entry *entry;
+	git_blob *blob;
+	const void *blobcontent;
+	git_off_t blobsize;
+	bool found_cr;
 
-	/* Main scan loop.  Find the next carriage return and copy the
-	 * whole chunk up to that point to the destination buffer.
-	 */
-	while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
-		/* copy input up to \r */
-		if (next > scan)
-			git_buf_put(dest, scan, next - scan);
+	if (git_repository_index__weakptr(&index, filter->repo) < 0) {
+		giterr_clear();
+		return false;
+	}
 
-		/* Do not drop \r unless it is followed by \n */
-		if (*(next + 1) != '\n')
-			git_buf_putc(dest, '\r');
+	if (!(entry = git_index_get_bypath(index, filter->path, 0)) &&
+		!(entry = git_index_get_bypath(index, filter->path, 1)))
+		return false;
 
-		scan = next + 1;
-	}
+	if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
+		return true;
 
-	/* If there was no \r, then tell the library to skip this filter */
-	if (scan == source->ptr)
-		return -1;
+	if (git_blob_lookup(&blob, filter->repo, &entry->oid) < 0)
+		return false;
 
-	/* Copy remaining input into dest */
-	git_buf_put(dest, scan, scan_end - scan);
-	return 0;
+	blobcontent = git_blob_rawcontent(blob);
+	blobsize    = git_blob_rawsize(blob);
+	if (!git__is_sizet(blobsize))
+		blobsize = (size_t)-1;
+
+	found_cr = (blobcontent != NULL &&
+		blobsize > 0 &&
+		memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
+
+	git_blob_free(blob);
+	return found_cr;
 }
 
-static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+static int crlf_apply_to_odb(
+	git_filter *self, git_buf *dest, const git_buf *source)
 {
 	struct crlf_filter *filter = (struct crlf_filter *)self;
 
@@ -148,9 +161,12 @@
 	if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
 		filter->attrs.crlf_action == GIT_CRLF_GUESS) {
 
-		git_text_stats stats;
-		git_text_gather_stats(&stats, source);
+		git_buf_text_stats stats;
 
+		/* Check heuristics for binary vs text... */
+		if (git_buf_text_gather_stats(&stats, source, false))
+			return -1;
+
 		/*
 		 * We're currently not going to even try to convert stuff
 		 * that has bare CR characters. Does anybody do that crazy
@@ -159,35 +175,94 @@
 		if (stats.cr != stats.crlf)
 			return -1;
 
-		/*
-		 * And add some heuristics for binary vs text, of course...
-		 */
-		if (git_text_is_binary(&stats))
-			return -1;
-
-#if 0
-		if (crlf_action == CRLF_GUESS) {
+		if (filter->attrs.crlf_action == GIT_CRLF_GUESS) {
 			/*
 			 * If the file in the index has any CR in it, do not convert.
 			 * This is the new safer autocrlf handling.
 			 */
-			if (has_cr_in_index(path))
-				return 0;
+			if (has_cr_in_index(self))
+				return -1;
 		}
-#endif
 
 		if (!stats.cr)
 			return -1;
 	}
 
 	/* Actually drop the carriage returns */
-	return drop_crlf(dest, source);
+	return git_buf_text_crlf_to_lf(dest, source);
 }
 
-int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+static const char *line_ending(struct crlf_filter *filter)
 {
+	switch (filter->attrs.crlf_action) {
+	case GIT_CRLF_BINARY:
+	case GIT_CRLF_INPUT:
+		return "\n";
+
+	case GIT_CRLF_CRLF:
+		return "\r\n";
+
+	case GIT_CRLF_AUTO:
+	case GIT_CRLF_TEXT:
+	case GIT_CRLF_GUESS:
+		break;
+
+	default:
+		goto line_ending_error;
+	}
+
+	switch (filter->attrs.eol) {
+	case GIT_EOL_UNSET:
+		return GIT_EOL_NATIVE == GIT_EOL_CRLF
+			? "\r\n"
+			: "\n";
+
+	case GIT_EOL_CRLF:
+		return "\r\n";
+
+	case GIT_EOL_LF:
+		return "\n";
+
+	default:
+		goto line_ending_error;
+	}
+
+line_ending_error:
+	giterr_set(GITERR_INVALID, "Invalid input to line ending filter");
+	return NULL;
+}
+
+static int crlf_apply_to_workdir(
+	git_filter *self, git_buf *dest, const git_buf *source)
+{
+	struct crlf_filter *filter = (struct crlf_filter *)self;
+	const char *workdir_ending = NULL;
+
+	assert(self && dest && source);
+
+	/* Empty file? Nothing to do. */
+	if (git_buf_len(source) == 0)
+		return -1;
+
+	/* Determine proper line ending */
+	workdir_ending = line_ending(filter);
+	if (!workdir_ending)
+		return -1;
+	if (!strcmp("\n", workdir_ending)) /* do nothing for \n ending */
+		return -1;
+
+	/* for now, only lf->crlf conversion is supported here */
+	assert(!strcmp("\r\n", workdir_ending));
+	return git_buf_text_lf_to_crlf(dest, source);
+}
+
+static int find_and_add_filter(
+	git_vector *filters, git_repository *repo, const char *path,
+	int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source))
+{
 	struct crlf_attrs ca;
 	struct crlf_filter *filter;
+	size_t pathlen;
 	int error;
 
 	/* Load gitattributes for the path */
@@ -196,7 +271,7 @@
 
 	/*
 	 * Use the core Git logic to see if we should perform CRLF for this file
-	 * based on its attributes & the value of `core.auto_crlf`
+	 * based on its attributes & the value of `core.autocrlf`
 	 */
 	ca.crlf_action = crlf_input_action(&ca);
 
@@ -206,8 +281,7 @@
 	if (ca.crlf_action == GIT_CRLF_GUESS) {
 		int auto_crlf;
 
-		if ((error = git_repository__cvar(
-			&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+		if ((error = git_repository__cvar(&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
 			return error;
 
 		if (auto_crlf == GIT_AUTO_CRLF_FALSE)
@@ -216,13 +290,27 @@
 
 	/* If we're good, we create a new filter object and push it
 	 * into the filters array */
-	filter = git__malloc(sizeof(struct crlf_filter));
+	pathlen = strlen(path);
+	filter = git__malloc(sizeof(struct crlf_filter) + pathlen + 1);
 	GITERR_CHECK_ALLOC(filter);
 
-	filter->f.apply = &crlf_apply_to_odb;
+	filter->f.apply = apply;
 	filter->f.do_free = NULL;
 	memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+	filter->repo = repo;
+	memcpy(filter->path, path, pathlen + 1);
 
 	return git_vector_insert(filters, filter);
 }
 
+int git_filter_add__crlf_to_odb(
+	git_vector *filters, git_repository *repo, const char *path)
+{
+	return find_and_add_filter(filters, repo, path, &crlf_apply_to_odb);
+}
+
+int git_filter_add__crlf_to_workdir(
+	git_vector *filters, git_repository *repo, const char *path)
+{
+	return find_and_add_filter(filters, repo, path, &crlf_apply_to_workdir);
+}
diff --git a/libgit2/src/date.c b/libgit2/src/date.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/date.c
@@ -0,0 +1,876 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+
+#include "common.h"
+
+#ifndef GIT_WIN32
+#include <sys/time.h>
+#endif
+
+#include "util.h"
+#include "cache.h"
+#include "posix.h"
+
+#include <ctype.h>
+#include <time.h>
+
+typedef enum {
+	DATE_NORMAL = 0,
+	DATE_RELATIVE,
+	DATE_SHORT,
+	DATE_LOCAL,
+	DATE_ISO8601,
+	DATE_RFC2822,
+	DATE_RAW
+} date_mode;
+
+/*
+ * This is like mktime, but without normalization of tm_wday and tm_yday.
+ */
+static git_time_t tm_to_time_t(const struct tm *tm)
+{
+	static const int mdays[] = {
+	    0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+	};
+	int year = tm->tm_year - 70;
+	int month = tm->tm_mon;
+	int day = tm->tm_mday;
+
+	if (year < 0 || year > 129) /* algo only works for 1970-2099 */
+		return -1;
+	if (month < 0 || month > 11) /* array bounds */
+		return -1;
+	if (month < 2 || (year + 2) % 4)
+		day--;
+	if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+		return -1;
+	return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
+		tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
+}
+
+static const char *month_names[] = {
+	"January", "February", "March", "April", "May", "June",
+	"July", "August", "September", "October", "November", "December"
+};
+
+static const char *weekday_names[] = {
+	"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
+};
+
+
+
+/*
+ * Check these. And note how it doesn't do the summer-time conversion.
+ *
+ * In my world, it's always summer, and things are probably a bit off
+ * in other ways too.
+ */
+static const struct {
+	const char *name;
+	int offset;
+	int dst;
+} timezone_names[] = {
+	{ "IDLW", -12, 0, },	/* International Date Line West */
+	{ "NT",   -11, 0, },	/* Nome */
+	{ "CAT",  -10, 0, },	/* Central Alaska */
+	{ "HST",  -10, 0, },	/* Hawaii Standard */
+	{ "HDT",  -10, 1, },	/* Hawaii Daylight */
+	{ "YST",   -9, 0, },	/* Yukon Standard */
+	{ "YDT",   -9, 1, },	/* Yukon Daylight */
+	{ "PST",   -8, 0, },	/* Pacific Standard */
+	{ "PDT",   -8, 1, },	/* Pacific Daylight */
+	{ "MST",   -7, 0, },	/* Mountain Standard */
+	{ "MDT",   -7, 1, },	/* Mountain Daylight */
+	{ "CST",   -6, 0, },	/* Central Standard */
+	{ "CDT",   -6, 1, },	/* Central Daylight */
+	{ "EST",   -5, 0, },	/* Eastern Standard */
+	{ "EDT",   -5, 1, },	/* Eastern Daylight */
+	{ "AST",   -3, 0, },	/* Atlantic Standard */
+	{ "ADT",   -3, 1, },	/* Atlantic Daylight */
+	{ "WAT",   -1, 0, },	/* West Africa */
+
+	{ "GMT",    0, 0, },	/* Greenwich Mean */
+	{ "UTC",    0, 0, },	/* Universal (Coordinated) */
+	{ "Z",      0, 0, },    /* Zulu, alias for UTC */
+
+	{ "WET",    0, 0, },	/* Western European */
+	{ "BST",    0, 1, },	/* British Summer */
+	{ "CET",   +1, 0, },	/* Central European */
+	{ "MET",   +1, 0, },	/* Middle European */
+	{ "MEWT",  +1, 0, },	/* Middle European Winter */
+	{ "MEST",  +1, 1, },	/* Middle European Summer */
+	{ "CEST",  +1, 1, },	/* Central European Summer */
+	{ "MESZ",  +1, 1, },	/* Middle European Summer */
+	{ "FWT",   +1, 0, },	/* French Winter */
+	{ "FST",   +1, 1, },	/* French Summer */
+	{ "EET",   +2, 0, },	/* Eastern Europe */
+	{ "EEST",  +2, 1, },	/* Eastern European Daylight */
+	{ "WAST",  +7, 0, },	/* West Australian Standard */
+	{ "WADT",  +7, 1, },	/* West Australian Daylight */
+	{ "CCT",   +8, 0, },	/* China Coast */
+	{ "JST",   +9, 0, },	/* Japan Standard */
+	{ "EAST", +10, 0, },	/* Eastern Australian Standard */
+	{ "EADT", +10, 1, },	/* Eastern Australian Daylight */
+	{ "GST",  +10, 0, },	/* Guam Standard */
+	{ "NZT",  +12, 0, },	/* New Zealand */
+	{ "NZST", +12, 0, },	/* New Zealand Standard */
+	{ "NZDT", +12, 1, },	/* New Zealand Daylight */
+	{ "IDLE", +12, 0, },	/* International Date Line East */
+};
+
+static size_t match_string(const char *date, const char *str)
+{
+	size_t i = 0;
+
+	for (i = 0; *date; date++, str++, i++) {
+		if (*date == *str)
+			continue;
+		if (toupper(*date) == toupper(*str))
+			continue;
+		if (!isalnum(*date))
+			break;
+		return 0;
+	}
+	return i;
+}
+
+static int skip_alpha(const char *date)
+{
+	int i = 0;
+	do {
+		i++;
+	} while (isalpha(date[i]));
+	return i;
+}
+
+/*
+* Parse month, weekday, or timezone name
+*/
+static size_t match_alpha(const char *date, struct tm *tm, int *offset)
+{
+	unsigned int i;
+
+	for (i = 0; i < 12; i++) {
+		size_t match = match_string(date, month_names[i]);
+		if (match >= 3) {
+			tm->tm_mon = i;
+			return match;
+		}
+	}
+
+	for (i = 0; i < 7; i++) {
+		size_t match = match_string(date, weekday_names[i]);
+		if (match >= 3) {
+			tm->tm_wday = i;
+			return match;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
+		size_t match = match_string(date, timezone_names[i].name);
+		if (match >= 3 || match == strlen(timezone_names[i].name)) {
+			int off = timezone_names[i].offset;
+
+			/* This is bogus, but we like summer */
+			off += timezone_names[i].dst;
+
+			/* Only use the tz name offset if we don't have anything better */
+			if (*offset == -1)
+				*offset = 60*off;
+
+			return match;
+		}
+	}
+
+	if (match_string(date, "PM") == 2) {
+		tm->tm_hour = (tm->tm_hour % 12) + 12;
+		return 2;
+	}
+
+	if (match_string(date, "AM") == 2) {
+		tm->tm_hour = (tm->tm_hour % 12) + 0;
+		return 2;
+	}
+
+	/* BAD */
+	return skip_alpha(date);
+}
+
+static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
+{
+	if (month > 0 && month < 13 && day > 0 && day < 32) {
+		struct tm check = *tm;
+		struct tm *r = (now_tm ? &check : tm);
+		time_t specified;
+
+		r->tm_mon = month - 1;
+		r->tm_mday = day;
+		if (year == -1) {
+			if (!now_tm)
+				return 1;
+			r->tm_year = now_tm->tm_year;
+		}
+		else if (year >= 1970 && year < 2100)
+			r->tm_year = year - 1900;
+		else if (year > 70 && year < 100)
+			r->tm_year = year;
+		else if (year < 38)
+			r->tm_year = year + 100;
+		else
+			return 0;
+		if (!now_tm)
+			return 1;
+
+		specified = tm_to_time_t(r);
+
+		/* Be it commit time or author time, it does not make
+		 * sense to specify timestamp way into the future.  Make
+		 * sure it is not later than ten days from now...
+		 */
+		if (now + 10*24*3600 < specified)
+			return 0;
+		tm->tm_mon = r->tm_mon;
+		tm->tm_mday = r->tm_mday;
+		if (year != -1)
+			tm->tm_year = r->tm_year;
+		return 1;
+	}
+	return 0;
+}
+
+static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
+{
+	time_t now;
+	struct tm now_tm;
+	struct tm *refuse_future;
+	long num2, num3;
+
+	num2 = strtol(end+1, &end, 10);
+	num3 = -1;
+	if (*end == c && isdigit(end[1]))
+		num3 = strtol(end+1, &end, 10);
+
+	/* Time? Date? */
+	switch (c) {
+	case ':':
+		if (num3 < 0)
+			num3 = 0;
+		if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
+			tm->tm_hour = num;
+			tm->tm_min = num2;
+			tm->tm_sec = num3;
+			break;
+		}
+		return 0;
+
+	case '-':
+	case '/':
+	case '.':
+		now = time(NULL);
+		refuse_future = NULL;
+		if (p_gmtime_r(&now, &now_tm))
+			refuse_future = &now_tm;
+
+		if (num > 70) {
+			/* yyyy-mm-dd? */
+			if (is_date(num, num2, num3, refuse_future, now, tm))
+				break;
+			/* yyyy-dd-mm? */
+			if (is_date(num, num3, num2, refuse_future, now, tm))
+				break;
+		}
+		/* Our eastern European friends say dd.mm.yy[yy]
+		 * is the norm there, so giving precedence to
+		 * mm/dd/yy[yy] form only when separator is not '.'
+		 */
+		if (c != '.' &&
+		    is_date(num3, num, num2, refuse_future, now, tm))
+			break;
+		/* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
+		if (is_date(num3, num2, num, refuse_future, now, tm))
+			break;
+		/* Funny European mm.dd.yy */
+		if (c == '.' &&
+		    is_date(num3, num, num2, refuse_future, now, tm))
+			break;
+		return 0;
+	}
+	return end - date;
+}
+
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
+static int nodate(struct tm *tm)
+{
+	return (tm->tm_year &
+		tm->tm_mon &
+		tm->tm_mday &
+		tm->tm_hour &
+		tm->tm_min &
+		tm->tm_sec) < 0;
+}
+
+/*
+ * We've seen a digit. Time? Year? Date?
+ */
+static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
+{
+	size_t n;
+	char *end;
+	unsigned long num;
+
+	num = strtoul(date, &end, 10);
+
+	/*
+	 * Seconds since 1970? We trigger on that for any numbers with
+	 * more than 8 digits. This is because we don't want to rule out
+	 * numbers like 20070606 as a YYYYMMDD date.
+	 */
+	if (num >= 100000000 && nodate(tm)) {
+		time_t time = num;
+		if (p_gmtime_r(&time, tm)) {
+			*tm_gmt = 1;
+			return end - date;
+		}
+	}
+
+	/*
+	 * Check for special formats: num[-.:/]num[same]num
+	 */
+	switch (*end) {
+	case ':':
+	case '.':
+	case '/':
+	case '-':
+		if (isdigit(end[1])) {
+			size_t match = match_multi_number(num, *end, date, end, tm);
+			if (match)
+				return match;
+		}
+	}
+
+	/*
+	 * None of the special formats? Try to guess what
+	 * the number meant. We use the number of digits
+	 * to make a more educated guess..
+	 */
+	n = 0;
+	do {
+		n++;
+	} while (isdigit(date[n]));
+
+	/* Four-digit year or a timezone? */
+	if (n == 4) {
+		if (num <= 1400 && *offset == -1) {
+			unsigned int minutes = num % 100;
+			unsigned int hours = num / 100;
+			*offset = hours*60 + minutes;
+		} else if (num > 1900 && num < 2100)
+			tm->tm_year = num - 1900;
+		return n;
+	}
+
+	/*
+	 * Ignore lots of numerals. We took care of 4-digit years above.
+	 * Days or months must be one or two digits.
+	 */
+	if (n > 2)
+		return n;
+
+	/*
+	 * NOTE! We will give precedence to day-of-month over month or
+	 * year numbers in the 1-12 range. So 05 is always "mday 5",
+	 * unless we already have a mday..
+	 *
+	 * IOW, 01 Apr 05 parses as "April 1st, 2005".
+	 */
+	if (num > 0 && num < 32 && tm->tm_mday < 0) {
+		tm->tm_mday = num;
+		return n;
+	}
+
+	/* Two-digit year? */
+	if (n == 2 && tm->tm_year < 0) {
+		if (num < 10 && tm->tm_mday >= 0) {
+			tm->tm_year = num + 100;
+			return n;
+		}
+		if (num >= 70) {
+			tm->tm_year = num;
+			return n;
+		}
+	}
+
+	if (num > 0 && num < 13 && tm->tm_mon < 0)
+		tm->tm_mon = num-1;
+
+	return n;
+}
+
+static size_t match_tz(const char *date, int *offp)
+{
+	char *end;
+	int hour = strtoul(date + 1, &end, 10);
+	size_t n = end - (date + 1);
+	int min = 0;
+
+	if (n == 4) {
+		/* hhmm */
+		min = hour % 100;
+		hour = hour / 100;
+	} else if (n != 2) {
+		min = 99; /* random stuff */
+	} else if (*end == ':') {
+		/* hh:mm? */
+		min = strtoul(end + 1, &end, 10);
+		if (end - (date + 1) != 5)
+			min = 99; /* random stuff */
+	} /* otherwise we parsed "hh" */
+
+	/*
+	 * Don't accept any random stuff. Even though some places have
+	 * offset larger than 12 hours (e.g. Pacific/Kiritimati is at
+	 * UTC+14), there is something wrong if hour part is much
+	 * larger than that. We might also want to check that the
+	 * minutes are divisible by 15 or something too. (Offset of
+	 * Kathmandu, Nepal is UTC+5:45)
+	 */
+	if (min < 60 && hour < 24) {
+		int offset = hour * 60 + min;
+		if (*date == '-')
+			offset = -offset;
+		*offp = offset;
+	}
+	return end - date;
+}
+
+/*
+ * Parse a string like "0 +0000" as ancient timestamp near epoch, but
+ * only when it appears not as part of any other string.
+ */
+static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset)
+{
+	char *end;
+	unsigned long stamp;
+	int ofs;
+
+	if (*date < '0' || '9' <= *date)
+		return -1;
+	stamp = strtoul(date, &end, 10);
+	if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
+		return -1;
+	date = end + 2;
+	ofs = strtol(date, &end, 10);
+	if ((*end != '\0' && (*end != '\n')) || end != date + 4)
+		return -1;
+	ofs = (ofs / 100) * 60 + (ofs % 100);
+	if (date[-1] == '-')
+		ofs = -ofs;
+	*timestamp = stamp;
+	*offset = ofs;
+	return 0;
+}
+
+/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
+   (i.e. English) day/month names, and it doesn't work correctly with %z. */
+static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset)
+{
+	struct tm tm;
+	int tm_gmt;
+	git_time_t dummy_timestamp;
+	int dummy_offset;
+
+	if (!timestamp)
+		timestamp = &dummy_timestamp;
+	if (!offset)
+		offset = &dummy_offset;
+
+	memset(&tm, 0, sizeof(tm));
+	tm.tm_year = -1;
+	tm.tm_mon = -1;
+	tm.tm_mday = -1;
+	tm.tm_isdst = -1;
+	tm.tm_hour = -1;
+	tm.tm_min = -1;
+	tm.tm_sec = -1;
+	*offset = -1;
+	tm_gmt = 0;
+
+	if (*date == '@' &&
+	    !match_object_header_date(date + 1, timestamp, offset))
+		return 0; /* success */
+	for (;;) {
+		size_t match = 0;
+		unsigned char c = *date;
+
+		/* Stop at end of string or newline */
+		if (!c || c == '\n')
+			break;
+
+		if (isalpha(c))
+			match = match_alpha(date, &tm, offset);
+		else if (isdigit(c))
+			match = match_digit(date, &tm, offset, &tm_gmt);
+		else if ((c == '-' || c == '+') && isdigit(date[1]))
+			match = match_tz(date, offset);
+
+		if (!match) {
+			/* BAD */
+			match = 1;
+		}
+
+		date += match;
+	}
+
+	/* mktime uses local timezone */
+	*timestamp = tm_to_time_t(&tm);
+	if (*offset == -1)
+		*offset = (int)((time_t)*timestamp - mktime(&tm)) / 60;
+
+	if (*timestamp == (git_time_t)-1)
+		return -1;
+
+	if (!tm_gmt)
+		*timestamp -= *offset * 60;
+	return 0; /* success */
+}
+
+
+/*
+ * Relative time update (eg "2 days ago").  If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec)
+{
+	time_t n;
+
+	if (tm->tm_mday < 0)
+		tm->tm_mday = now->tm_mday;
+	if (tm->tm_mon < 0)
+		tm->tm_mon = now->tm_mon;
+	if (tm->tm_year < 0) {
+		tm->tm_year = now->tm_year;
+		if (tm->tm_mon > now->tm_mon)
+			tm->tm_year--;
+	}
+
+	n = mktime(tm) - sec;
+	p_localtime_r(&n, tm);
+	return n;
+}
+
+static void date_now(struct tm *tm, struct tm *now, int *num)
+{
+   GIT_UNUSED(num);
+	update_tm(tm, now, 0);
+}
+
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
+{
+   GIT_UNUSED(num);
+	update_tm(tm, now, 24*60*60);
+}
+
+static void date_time(struct tm *tm, struct tm *now, int hour)
+{
+	if (tm->tm_hour < hour)
+		date_yesterday(tm, now, NULL);
+	tm->tm_hour = hour;
+	tm->tm_min = 0;
+	tm->tm_sec = 0;
+}
+
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
+{
+   GIT_UNUSED(num);
+	date_time(tm, now, 0);
+}
+
+static void date_noon(struct tm *tm, struct tm *now, int *num)
+{
+   GIT_UNUSED(num);
+	date_time(tm, now, 12);
+}
+
+static void date_tea(struct tm *tm, struct tm *now, int *num)
+{
+   GIT_UNUSED(num);
+	date_time(tm, now, 17);
+}
+
+static void date_pm(struct tm *tm, struct tm *now, int *num)
+{
+	int hour, n = *num;
+	*num = 0;
+   GIT_UNUSED(now);
+
+	hour = tm->tm_hour;
+	if (n) {
+		hour = n;
+		tm->tm_min = 0;
+		tm->tm_sec = 0;
+	}
+	tm->tm_hour = (hour % 12) + 12;
+}
+
+static void date_am(struct tm *tm, struct tm *now, int *num)
+{
+	int hour, n = *num;
+	*num = 0;
+   GIT_UNUSED(now);
+
+	hour = tm->tm_hour;
+	if (n) {
+		hour = n;
+		tm->tm_min = 0;
+		tm->tm_sec = 0;
+	}
+	tm->tm_hour = (hour % 12);
+}
+
+static void date_never(struct tm *tm, struct tm *now, int *num)
+{
+	time_t n = 0;
+   GIT_UNUSED(now);
+   GIT_UNUSED(num);
+	p_localtime_r(&n, tm);
+}
+
+static const struct special {
+	const char *name;
+	void (*fn)(struct tm *, struct tm *, int *);
+} special[] = {
+	{ "yesterday", date_yesterday },
+	{ "noon", date_noon },
+	{ "midnight", date_midnight },
+	{ "tea", date_tea },
+	{ "PM", date_pm },
+	{ "AM", date_am },
+	{ "never", date_never },
+	{ "now", date_now },
+	{ NULL }
+};
+
+static const char *number_name[] = {
+	"zero", "one", "two", "three", "four",
+	"five", "six", "seven", "eight", "nine", "ten",
+};
+
+static const struct typelen {
+	const char *type;
+	int length;
+} typelen[] = {
+	{ "seconds", 1 },
+	{ "minutes", 60 },
+	{ "hours", 60*60 },
+	{ "days", 24*60*60 },
+	{ "weeks", 7*24*60*60 },
+	{ NULL }
+};
+
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
+{
+	const struct typelen *tl;
+	const struct special *s;
+	const char *end = date;
+	int i;
+
+	while (isalpha(*++end))
+		/* scan to non-alpha */;
+
+	for (i = 0; i < 12; i++) {
+		size_t match = match_string(date, month_names[i]);
+		if (match >= 3) {
+			tm->tm_mon = i;
+			*touched = 1;
+			return end;
+		}
+	}
+
+	for (s = special; s->name; s++) {
+		size_t len = strlen(s->name);
+		if (match_string(date, s->name) == len) {
+			s->fn(tm, now, num);
+			*touched = 1;
+			return end;
+		}
+	}
+
+	if (!*num) {
+		for (i = 1; i < 11; i++) {
+			size_t len = strlen(number_name[i]);
+			if (match_string(date, number_name[i]) == len) {
+				*num = i;
+				*touched = 1;
+				return end;
+			}
+		}
+		if (match_string(date, "last") == 4) {
+			*num = 1;
+			*touched = 1;
+		}
+		return end;
+	}
+
+	tl = typelen;
+	while (tl->type) {
+		size_t len = strlen(tl->type);
+		if (match_string(date, tl->type) >= len-1) {
+			update_tm(tm, now, tl->length * *num);
+			*num = 0;
+			*touched = 1;
+			return end;
+		}
+		tl++;
+	}
+
+	for (i = 0; i < 7; i++) {
+		size_t match = match_string(date, weekday_names[i]);
+		if (match >= 3) {
+			int diff, n = *num -1;
+			*num = 0;
+
+			diff = tm->tm_wday - i;
+			if (diff <= 0)
+				n++;
+			diff += 7*n;
+
+			update_tm(tm, now, diff * 24 * 60 * 60);
+			*touched = 1;
+			return end;
+		}
+	}
+
+	if (match_string(date, "months") >= 5) {
+		int n;
+		update_tm(tm, now, 0); /* fill in date fields if needed */
+		n = tm->tm_mon - *num;
+		*num = 0;
+		while (n < 0) {
+			n += 12;
+			tm->tm_year--;
+		}
+		tm->tm_mon = n;
+		*touched = 1;
+		return end;
+	}
+
+	if (match_string(date, "years") >= 4) {
+		update_tm(tm, now, 0); /* fill in date fields if needed */
+		tm->tm_year -= *num;
+		*num = 0;
+		*touched = 1;
+		return end;
+	}
+
+	return end;
+}
+
+static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
+{
+	char *end;
+	unsigned long number = strtoul(date, &end, 10);
+
+	switch (*end) {
+	case ':':
+	case '.':
+	case '/':
+	case '-':
+		if (isdigit(end[1])) {
+			size_t match = match_multi_number(number, *end, date, end, tm);
+			if (match)
+				return date + match;
+		}
+	}
+
+	/* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
+	if (date[0] != '0' || end - date <= 2)
+		*num = number;
+	return end;
+}
+
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+	int number = *num;
+
+	if (number) {
+		*num = 0;
+		if (tm->tm_mday < 0 && number < 32)
+			tm->tm_mday = number;
+		else if (tm->tm_mon < 0 && number < 13)
+			tm->tm_mon = number-1;
+		else if (tm->tm_year < 0) {
+			if (number > 1969 && number < 2100)
+				tm->tm_year = number - 1900;
+			else if (number > 69 && number < 100)
+				tm->tm_year = number;
+			else if (number < 38)
+				tm->tm_year = 100 + number;
+			/* We mess up for number = 00 ? */
+		}
+	}
+}
+
+static git_time_t approxidate_str(const char *date,
+                                  const struct timeval *tv,
+                                  int *error_ret)
+{
+	int number = 0;
+	int touched = 0;
+	struct tm tm = {0}, now;
+	time_t time_sec;
+
+	time_sec = tv->tv_sec;
+	p_localtime_r(&time_sec, &tm);
+	now = tm;
+
+	tm.tm_year = -1;
+	tm.tm_mon = -1;
+	tm.tm_mday = -1;
+
+	for (;;) {
+		unsigned char c = *date;
+		if (!c)
+			break;
+		date++;
+		if (isdigit(c)) {
+			pending_number(&tm, &number);
+			date = approxidate_digit(date-1, &tm, &number);
+			touched = 1;
+			continue;
+		}
+		if (isalpha(c))
+			date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
+	}
+	pending_number(&tm, &number);
+	if (!touched)
+		*error_ret = 1;
+	return update_tm(&tm, &now, 0);
+}
+
+int git__date_parse(git_time_t *out, const char *date)
+{
+	struct timeval tv;
+	git_time_t timestamp;
+	int offset, error_ret=0;
+
+	if (!parse_date_basic(date, &timestamp, &offset)) {
+      *out = timestamp;
+		return 0;
+	}
+
+	p_gettimeofday(&tv, NULL);
+	*out = approxidate_str(date, &tv, &error_ret);
+   return error_ret;
+}
diff --git a/libgit2/src/delta-apply.c b/libgit2/src/delta-apply.c
--- a/libgit2/src/delta-apply.c
+++ b/libgit2/src/delta-apply.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -33,6 +33,19 @@
 	} while (c & 0x80);
 	*delta = d;
 	*size = r;
+	return 0;
+}
+
+int git__delta_read_header(
+	const unsigned char *delta,
+	size_t delta_len,
+	size_t *base_sz,
+	size_t *res_sz)
+{
+	const unsigned char *delta_end = delta + delta_len;
+	if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
+	    (hdr_sz(res_sz, &delta, delta_end) < 0))
+		return -1;
 	return 0;
 }
 
diff --git a/libgit2/src/delta-apply.h b/libgit2/src/delta-apply.h
--- a/libgit2/src/delta-apply.h
+++ b/libgit2/src/delta-apply.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -29,5 +29,22 @@
 	size_t base_len,
 	const unsigned char *delta,
 	size_t delta_len);
+
+/**
+ * Read the header of a git binary delta.
+ *
+ * @param delta the delta to execute copy/insert instructions from.
+ * @param delta_len total number of bytes in the delta.
+ * @param base_sz pointer to store the base size field.
+ * @param res_sz pointer to store the result size field.
+ * @return
+ * - 0 on a successful decoding the header.
+ * - GIT_ERROR if the delta is corrupt.
+ */
+extern int git__delta_read_header(
+	const unsigned char *delta,
+	size_t delta_len,
+	size_t *base_sz,
+	size_t *res_sz);
 
 #endif
diff --git a/libgit2/src/delta.c b/libgit2/src/delta.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/delta.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "delta.h"
+
+/* maximum hash entry list for the same hash bucket */
+#define HASH_LIMIT 64
+
+#define RABIN_SHIFT 23
+#define RABIN_WINDOW 16
+
+static const unsigned int T[256] = {
+	0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
+	0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
+	0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
+	0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
+	0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
+	0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
+	0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
+	0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
+	0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
+	0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
+	0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
+	0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
+	0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
+	0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
+	0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
+	0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
+	0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
+	0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
+	0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
+	0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
+	0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
+	0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
+	0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
+	0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
+	0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
+	0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
+	0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
+	0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
+	0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
+	0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
+	0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
+	0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
+	0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
+	0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
+	0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
+	0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
+	0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
+	0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
+	0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
+	0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
+	0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
+	0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
+	0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
+};
+
+static const unsigned int U[256] = {
+	0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
+	0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
+	0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
+	0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
+	0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
+	0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
+	0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
+	0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
+	0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
+	0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
+	0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
+	0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
+	0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
+	0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
+	0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
+	0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
+	0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
+	0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
+	0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
+	0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
+	0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
+	0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
+	0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
+	0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
+	0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
+	0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
+	0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
+	0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
+	0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
+	0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
+	0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
+	0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
+	0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
+	0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
+	0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
+	0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
+	0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
+	0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
+	0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
+	0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
+	0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
+	0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
+	0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
+};
+
+struct index_entry {
+	const unsigned char *ptr;
+	unsigned int val;
+	struct index_entry *next;
+};
+
+struct git_delta_index {
+	unsigned long memsize;
+	const void *src_buf;
+	unsigned long src_size;
+	unsigned int hash_mask;
+	struct index_entry *hash[GIT_FLEX_ARRAY];
+};
+
+struct git_delta_index *
+git_delta_create_index(const void *buf, unsigned long bufsize)
+{
+	unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
+	const unsigned char *data, *buffer = buf;
+	struct git_delta_index *index;
+	struct index_entry *entry, **hash;
+	void *mem;
+	unsigned long memsize;
+
+	if (!buf || !bufsize)
+		return NULL;
+
+	/* Determine index hash size.  Note that indexing skips the
+	   first byte to allow for optimizing the rabin polynomial
+	   initialization in create_delta(). */
+	entries = (unsigned int)(bufsize - 1) / RABIN_WINDOW;
+	if (bufsize >= 0xffffffffUL) {
+		/*
+		 * Current delta format can't encode offsets into
+		 * reference buffer with more than 32 bits.
+		 */
+		entries = 0xfffffffeU / RABIN_WINDOW;
+	}
+	hsize = entries / 4;
+	for (i = 4; (1u << i) < hsize && i < 31; i++);
+	hsize = 1 << i;
+	hmask = hsize - 1;
+
+	/* allocate lookup index */
+	memsize = sizeof(*index) +
+		  sizeof(*hash) * hsize +
+		  sizeof(*entry) * entries;
+	mem = git__malloc(memsize);
+	if (!mem)
+		return NULL;
+	index = mem;
+	mem = index->hash;
+	hash = mem;
+	mem = hash + hsize;
+	entry = mem;
+
+	index->memsize = memsize;
+	index->src_buf = buf;
+	index->src_size = bufsize;
+	index->hash_mask = hmask;
+	memset(hash, 0, hsize * sizeof(*hash));
+
+	/* allocate an array to count hash entries */
+	hash_count = calloc(hsize, sizeof(*hash_count));
+	if (!hash_count) {
+		git__free(index);
+		return NULL;
+	}
+
+	/* then populate the index */
+	prev_val = ~0;
+	for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
+	     data >= buffer;
+	     data -= RABIN_WINDOW) {
+		unsigned int val = 0;
+		for (i = 1; i <= RABIN_WINDOW; i++)
+			val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
+		if (val == prev_val) {
+			/* keep the lowest of consecutive identical blocks */
+			entry[-1].ptr = data + RABIN_WINDOW;
+		} else {
+			prev_val = val;
+			i = val & hmask;
+			entry->ptr = data + RABIN_WINDOW;
+			entry->val = val;
+			entry->next = hash[i];
+			hash[i] = entry++;
+			hash_count[i]++;
+		}
+	}
+
+	/*
+	 * Determine a limit on the number of entries in the same hash
+	 * bucket.  This guard us against patological data sets causing
+	 * really bad hash distribution with most entries in the same hash
+	 * bucket that would bring us to O(m*n) computing costs (m and n
+	 * corresponding to reference and target buffer sizes).
+	 *
+	 * Make sure none of the hash buckets has more entries than
+	 * we're willing to test.  Otherwise we cull the entry list
+	 * uniformly to still preserve a good repartition across
+	 * the reference buffer.
+	 */
+	for (i = 0; i < hsize; i++) {
+		if (hash_count[i] < HASH_LIMIT)
+			continue;
+
+		entry = hash[i];
+		do {
+			struct index_entry *keep = entry;
+			int skip = hash_count[i] / HASH_LIMIT / 2;
+			do {
+				entry = entry->next;
+			} while(--skip && entry);
+			keep->next = entry;
+		} while (entry);
+	}
+	git__free(hash_count);
+
+	return index;
+}
+
+void git_delta_free_index(struct git_delta_index *index)
+{
+	git__free(index);
+}
+
+unsigned long git_delta_sizeof_index(struct git_delta_index *index)
+{
+	if (index)
+		return index->memsize;
+	else
+		return 0;
+}
+
+/*
+ * The maximum size for any opcode sequence, including the initial header
+ * plus rabin window plus biggest copy.
+ */
+#define MAX_OP_SIZE	(5 + 5 + 1 + RABIN_WINDOW + 7)
+
+void *
+git_delta_create(
+	const struct git_delta_index *index,
+	const void *trg_buf,
+	unsigned long trg_size,
+	unsigned long *delta_size,
+	unsigned long max_size)
+{
+	unsigned int i, outpos, outsize, moff, msize, val;
+	int inscnt;
+	const unsigned char *ref_data, *ref_top, *data, *top;
+	unsigned char *out;
+
+	if (!trg_buf || !trg_size)
+		return NULL;
+
+	outpos = 0;
+	outsize = 8192;
+	if (max_size && outsize >= max_size)
+		outsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
+	out = git__malloc(outsize);
+	if (!out)
+		return NULL;
+
+	/* store reference buffer size */
+	i = index->src_size;
+	while (i >= 0x80) {
+		out[outpos++] = i | 0x80;
+		i >>= 7;
+	}
+	out[outpos++] = i;
+
+	/* store target buffer size */
+	i = trg_size;
+	while (i >= 0x80) {
+		out[outpos++] = i | 0x80;
+		i >>= 7;
+	}
+	out[outpos++] = i;
+
+	ref_data = index->src_buf;
+	ref_top = ref_data + index->src_size;
+	data = trg_buf;
+	top = (const unsigned char *) trg_buf + trg_size;
+
+	outpos++;
+	val = 0;
+	for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
+		out[outpos++] = *data;
+		val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+	}
+	inscnt = i;
+
+	moff = 0;
+	msize = 0;
+	while (data < top) {
+		if (msize < 4096) {
+			struct index_entry *entry;
+			val ^= U[data[-RABIN_WINDOW]];
+			val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+			i = val & index->hash_mask;
+			for (entry = index->hash[i]; entry; entry = entry->next) {
+				const unsigned char *ref = entry->ptr;
+				const unsigned char *src = data;
+				unsigned int ref_size = (unsigned int)(ref_top - ref);
+				if (entry->val != val)
+					continue;
+				if (ref_size > (unsigned int)(top - src))
+					ref_size = (unsigned int)(top - src);
+				if (ref_size <= msize)
+					break;
+				while (ref_size-- && *src++ == *ref)
+					ref++;
+				if (msize < (unsigned int)(ref - entry->ptr)) {
+					/* this is our best match so far */
+					msize = (unsigned int)(ref - entry->ptr);
+					moff = (unsigned int)(entry->ptr - ref_data);
+					if (msize >= 4096) /* good enough */
+						break;
+				}
+			}
+		}
+
+		if (msize < 4) {
+			if (!inscnt)
+				outpos++;
+			out[outpos++] = *data++;
+			inscnt++;
+			if (inscnt == 0x7f) {
+				out[outpos - inscnt - 1] = inscnt;
+				inscnt = 0;
+			}
+			msize = 0;
+		} else {
+			unsigned int left;
+			unsigned char *op;
+
+			if (inscnt) {
+				while (moff && ref_data[moff-1] == data[-1]) {
+					/* we can match one byte back */
+					msize++;
+					moff--;
+					data--;
+					outpos--;
+					if (--inscnt)
+						continue;
+					outpos--;  /* remove count slot */
+					inscnt--;  /* make it -1 */
+					break;
+				}
+				out[outpos - inscnt - 1] = inscnt;
+				inscnt = 0;
+			}
+
+			/* A copy op is currently limited to 64KB (pack v2) */
+			left = (msize < 0x10000) ? 0 : (msize - 0x10000);
+			msize -= left;
+
+			op = out + outpos++;
+			i = 0x80;
+
+			if (moff & 0x000000ff)
+				out[outpos++] = moff >> 0,  i |= 0x01;
+			if (moff & 0x0000ff00)
+				out[outpos++] = moff >> 8,  i |= 0x02;
+			if (moff & 0x00ff0000)
+				out[outpos++] = moff >> 16, i |= 0x04;
+			if (moff & 0xff000000)
+				out[outpos++] = moff >> 24, i |= 0x08;
+
+			if (msize & 0x00ff)
+				out[outpos++] = msize >> 0, i |= 0x10;
+			if (msize & 0xff00)
+				out[outpos++] = msize >> 8, i |= 0x20;
+
+			*op = i;
+
+			data += msize;
+			moff += msize;
+			msize = left;
+
+			if (msize < 4096) {
+				int j;
+				val = 0;
+				for (j = -RABIN_WINDOW; j < 0; j++)
+					val = ((val << 8) | data[j])
+					      ^ T[val >> RABIN_SHIFT];
+			}
+		}
+
+		if (outpos >= outsize - MAX_OP_SIZE) {
+			void *tmp = out;
+			outsize = outsize * 3 / 2;
+			if (max_size && outsize >= max_size)
+				outsize = max_size + MAX_OP_SIZE + 1;
+			if (max_size && outpos > max_size)
+				break;
+			out = git__realloc(out, outsize);
+			if (!out) {
+				git__free(tmp);
+				return NULL;
+			}
+		}
+	}
+
+	if (inscnt)
+		out[outpos - inscnt - 1] = inscnt;
+
+	if (max_size && outpos > max_size) {
+		git__free(out);
+		return NULL;
+	}
+
+	*delta_size = outpos;
+	return out;
+}
diff --git a/libgit2/src/delta.h b/libgit2/src/delta.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/delta.h
@@ -0,0 +1,114 @@
+/*
+ * diff-delta code taken from git.git. See diff-delta.c for details.
+ *
+ */
+#ifndef INCLUDE_git_delta_h__
+#define INCLUDE_git_delta_h__
+
+#include "common.h"
+
+/* opaque object for delta index */
+struct git_delta_index;
+
+/*
+ * create_delta_index: compute index data from given buffer
+ *
+ * This returns a pointer to a struct delta_index that should be passed to
+ * subsequent create_delta() calls, or to free_delta_index().  A NULL pointer
+ * is returned on failure.  The given buffer must not be freed nor altered
+ * before free_delta_index() is called.  The returned pointer must be freed
+ * using free_delta_index().
+ */
+extern struct git_delta_index *
+git_delta_create_index(const void *buf, unsigned long bufsize);
+
+/*
+ * free_delta_index: free the index created by create_delta_index()
+ *
+ * Given pointer must be what create_delta_index() returned, or NULL.
+ */
+extern void git_delta_free_index(struct git_delta_index *index);
+
+/*
+ * sizeof_delta_index: returns memory usage of delta index
+ *
+ * Given pointer must be what create_delta_index() returned, or NULL.
+ */
+extern unsigned long git_delta_sizeof_index(struct git_delta_index *index);
+
+/*
+ * create_delta: create a delta from given index for the given buffer
+ *
+ * This function may be called multiple times with different buffers using
+ * the same delta_index pointer.  If max_delta_size is non-zero and the
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
+ * On success, a non-NULL pointer to the buffer with the delta data is
+ * returned and *delta_size is updated with its size.  The returned buffer
+ * must be freed by the caller.
+ */
+extern void *git_delta_create(
+	const struct git_delta_index *index,
+	const void *buf,
+	unsigned long bufsize,
+	unsigned long *delta_size,
+	unsigned long max_delta_size);
+
+/*
+ * diff_delta: create a delta from source buffer to target buffer
+ *
+ * If max_delta_size is non-zero and the resulting delta is to be larger
+ * than max_delta_size then NULL is returned.  On success, a non-NULL
+ * pointer to the buffer with the delta data is returned and *delta_size is
+ * updated with its size.  The returned buffer must be freed by the caller.
+ */
+GIT_INLINE(void *) git_delta(
+	const void *src_buf, unsigned long src_bufsize,
+	const void *trg_buf, unsigned long trg_bufsize,
+	unsigned long *delta_size,
+	unsigned long max_delta_size)
+{
+	struct git_delta_index *index = git_delta_create_index(src_buf, src_bufsize);
+	if (index) {
+		void *delta = git_delta_create(
+			index, trg_buf, trg_bufsize, delta_size, max_delta_size);
+		git_delta_free_index(index);
+		return delta;
+	}
+	return NULL;
+}
+
+/*
+ * patch_delta: recreate target buffer given source buffer and delta data
+ *
+ * On success, a non-NULL pointer to the target buffer is returned and
+ * *trg_bufsize is updated with its size.  On failure a NULL pointer is
+ * returned.  The returned buffer must be freed by the caller.
+ */
+extern void *git_delta_patch(
+	const void *src_buf, unsigned long src_size,
+	const void *delta_buf, unsigned long delta_size,
+	unsigned long *dst_size);
+
+/* the smallest possible delta size is 4 bytes */
+#define GIT_DELTA_SIZE_MIN	4
+
+/*
+ * This must be called twice on the delta data buffer, first to get the
+ * expected source buffer size, and again to get the target buffer size.
+ */
+GIT_INLINE(unsigned long) git_delta_get_hdr_size(
+	const unsigned char **datap, const unsigned char *top)
+{
+	const unsigned char *data = *datap;
+	unsigned long cmd, size = 0;
+	int i = 0;
+	do {
+		cmd = *data++;
+		size |= (cmd & 0x7f) << i;
+		i += 7;
+	} while (cmd & 0x80 && data < top);
+	*datap = data;
+	return size;
+}
+
+#endif
diff --git a/libgit2/src/diff.c b/libgit2/src/diff.c
--- a/libgit2/src/diff.c
+++ b/libgit2/src/diff.c
@@ -1,74 +1,19 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
 #include "common.h"
-#include "git2/diff.h"
 #include "diff.h"
 #include "fileops.h"
 #include "config.h"
 #include "attr_file.h"
-
-static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
-{
-	git_buf prefix = GIT_BUF_INIT;
-	const char *scan;
-
-	if (git_buf_common_prefix(&prefix, pathspec) < 0)
-		return NULL;
-
-	/* diff prefix will only be leading non-wildcards */
-	for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
-	git_buf_truncate(&prefix, scan - prefix.ptr);
-
-	if (prefix.size > 0)
-		return git_buf_detach(&prefix);
-
-	git_buf_free(&prefix);
-	return NULL;
-}
-
-static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
-{
-	const char *str;
-
-	if (pathspec == NULL || pathspec->count == 0)
-		return false;
-	if (pathspec->count > 1)
-		return true;
-
-	str = pathspec->strings[0];
-	if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
-		return false;
-	return true;
-}
-
-static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
-{
-	unsigned int i;
-	git_attr_fnmatch *match;
-
-	if (!diff->pathspec.length)
-		return true;
-
-	git_vector_foreach(&diff->pathspec, i, match) {
-		int result = p_fnmatch(match->pattern, path, 0);
-
-		/* if we didn't match, look for exact dirname prefix match */
-		if (result == FNM_NOMATCH &&
-			(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
-			strncmp(path, match->pattern, match->length) == 0 &&
-			path[match->length] == '/')
-			result = 0;
-
-		if (result == 0)
-			return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
-	}
+#include "filter.h"
+#include "pathspec.h"
 
-	return false;
-}
+#define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
+#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
 
 static git_diff_delta *diff_delta__alloc(
 	git_diff_list *diff,
@@ -87,7 +32,7 @@
 
 	delta->new_file.path = delta->old_file.path;
 
-	if (diff->opts.flags & GIT_DIFF_REVERSE) {
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
 		switch (status) {
 		case GIT_DELTA_ADDED:   status = GIT_DELTA_DELETED; break;
 		case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
@@ -99,70 +44,16 @@
 	return delta;
 }
 
-static git_diff_delta *diff_delta__dup(
-	const git_diff_delta *d, git_pool *pool)
-{
-	git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
-	if (!delta)
-		return NULL;
-
-	memcpy(delta, d, sizeof(git_diff_delta));
-
-	delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
-	if (delta->old_file.path == NULL)
-		goto fail;
-
-	if (d->new_file.path != d->old_file.path) {
-		delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
-		if (delta->new_file.path == NULL)
-			goto fail;
-	} else {
-		delta->new_file.path = delta->old_file.path;
-	}
-
-	return delta;
-
-fail:
-	git__free(delta);
-	return NULL;
-}
-
-static git_diff_delta *diff_delta__merge_like_cgit(
-	const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+static int diff_notify(
+	const git_diff_list *diff,
+	const git_diff_delta *delta,
+	const char *matched_pathspec)
 {
-	git_diff_delta *dup = diff_delta__dup(a, pool);
-	if (!dup)
-		return NULL;
-
-	if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
-		return dup;
-
-	git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
-
-	dup->new_file.mode = b->new_file.mode;
-	dup->new_file.size = b->new_file.size;
-	dup->new_file.flags = b->new_file.flags;
-
-	/* Emulate C git for merging two diffs (a la 'git diff <sha>').
-	 *
-	 * When C git does a diff between the work dir and a tree, it actually
-	 * diffs with the index but uses the workdir contents.  This emulates
-	 * those choices so we can emulate the type of diff.
-	 */
-	if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
-		if (dup->status == GIT_DELTA_DELETED)
-			/* preserve pending delete info */;
-		else if (b->status == GIT_DELTA_UNTRACKED ||
-				 b->status == GIT_DELTA_IGNORED)
-			dup->status = b->status;
-		else
-			dup->status = GIT_DELTA_UNMODIFIED;
-	}
-	else if (dup->status == GIT_DELTA_UNMODIFIED ||
-			 b->status == GIT_DELTA_DELETED)
-		dup->status = b->status;
+	if (!diff->opts.notify_cb)
+		return 0;
 
-	return dup;
+	return diff->opts.notify_cb(
+		diff, delta, matched_pathspec, diff->opts.notify_payload);
 }
 
 static int diff_delta__from_one(
@@ -171,18 +62,28 @@
 	const git_index_entry *entry)
 {
 	git_diff_delta *delta;
+	const char *matched_pathspec;
+	int notify_res;
 
 	if (status == GIT_DELTA_IGNORED &&
-		(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
 		return 0;
 
 	if (status == GIT_DELTA_UNTRACKED &&
-		(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
 		return 0;
 
-	if (!diff_path_matches_pathspec(diff, entry->path))
+	if (entry->mode == GIT_FILEMODE_COMMIT &&
+		DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
 		return 0;
 
+	if (!git_pathspec_match_path(
+			&diff->pathspec, entry->path,
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
+			&matched_pathspec))
+		return 0;
+
 	delta = diff_delta__alloc(diff, status, entry->path);
 	GITERR_CHECK_ALLOC(delta);
 
@@ -199,56 +100,124 @@
 		git_oid_cpy(&delta->new_file.oid, &entry->oid);
 	}
 
-	delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
-	delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+	delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
 
-	if (git_vector_insert(&diff->deltas, delta) < 0) {
+	if (delta->status == GIT_DELTA_DELETED ||
+		!git_oid_iszero(&delta->new_file.oid))
+		delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+
+	notify_res = diff_notify(diff, delta, matched_pathspec);
+
+	if (notify_res)
 		git__free(delta);
+	else if (git_vector_insert(&diff->deltas, delta) < 0) {
+		git__free(delta);
 		return -1;
 	}
 
-	return 0;
+	return notify_res < 0 ? GIT_EUSER : 0;
 }
 
 static int diff_delta__from_two(
 	git_diff_list *diff,
 	git_delta_t   status,
 	const git_index_entry *old_entry,
+	uint32_t old_mode,
 	const git_index_entry *new_entry,
-	git_oid *new_oid)
+	uint32_t new_mode,
+	git_oid *new_oid,
+	const char *matched_pathspec)
 {
 	git_diff_delta *delta;
+	int notify_res;
 
 	if (status == GIT_DELTA_UNMODIFIED &&
-		(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+		DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
 		return 0;
 
-	if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
-		const git_index_entry *temp = old_entry;
+	if (old_entry->mode == GIT_FILEMODE_COMMIT &&
+		new_entry->mode == GIT_FILEMODE_COMMIT &&
+		DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
+		return 0;
+
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+		uint32_t temp_mode = old_mode;
+		const git_index_entry *temp_entry = old_entry;
 		old_entry = new_entry;
-		new_entry = temp;
+		new_entry = temp_entry;
+		old_mode = new_mode;
+		new_mode = temp_mode;
 	}
 
 	delta = diff_delta__alloc(diff, status, old_entry->path);
 	GITERR_CHECK_ALLOC(delta);
 
-	delta->old_file.mode = old_entry->mode;
 	git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
-	delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
+	delta->old_file.size = old_entry->file_size;
+	delta->old_file.mode = old_mode;
+	delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
 
-	delta->new_file.mode = new_entry->mode;
-	git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
+	git_oid_cpy(&delta->new_file.oid, &new_entry->oid);
+	delta->new_file.size = new_entry->file_size;
+	delta->new_file.mode = new_mode;
+
+	if (new_oid) {
+		if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
+			git_oid_cpy(&delta->old_file.oid, new_oid);
+		else
+			git_oid_cpy(&delta->new_file.oid, new_oid);
+	}
+
 	if (new_oid || !git_oid_iszero(&new_entry->oid))
-		delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+		delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
 
-	if (git_vector_insert(&diff->deltas, delta) < 0) {
+	notify_res = diff_notify(diff, delta, matched_pathspec);
+
+	if (notify_res)
 		git__free(delta);
+	else if (git_vector_insert(&diff->deltas, delta) < 0) {
+		git__free(delta);
 		return -1;
 	}
 
-	return 0;
+	return notify_res < 0 ? GIT_EUSER : 0;
 }
 
+static git_diff_delta *diff_delta__last_for_item(
+	git_diff_list *diff,
+	const git_index_entry *item)
+{
+	git_diff_delta *delta = git_vector_last(&diff->deltas);
+	if (!delta)
+		return NULL;
+
+	switch (delta->status) {
+	case GIT_DELTA_UNMODIFIED:
+	case GIT_DELTA_DELETED:
+		if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0)
+			return delta;
+		break;
+	case GIT_DELTA_ADDED:
+		if (git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
+			return delta;
+		break;
+	case GIT_DELTA_UNTRACKED:
+		if (diff->strcomp(delta->new_file.path, item->path) == 0 &&
+			git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
+			return delta;
+		break;
+	case GIT_DELTA_MODIFIED:
+		if (git_oid_cmp(&delta->old_file.oid, &item->oid) == 0 ||
+			git_oid_cmp(&delta->new_file.oid, &item->oid) == 0)
+			return delta;
+		break;
+	default:
+		break;
+	}
+
+	return NULL;
+}
+
 static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
 {
 	size_t len = strlen(prefix);
@@ -260,13 +229,34 @@
 		return git_pool_strndup(pool, prefix, len + 1);
 }
 
-static int diff_delta__cmp(const void *a, const void *b)
+int git_diff_delta__cmp(const void *a, const void *b)
 {
 	const git_diff_delta *da = a, *db = b;
 	int val = strcmp(da->old_file.path, db->old_file.path);
 	return val ? val : ((int)da->status - (int)db->status);
 }
 
+bool git_diff_delta__should_skip(
+	const git_diff_options *opts, const git_diff_delta *delta)
+{
+	uint32_t flags = opts ? opts->flags : 0;
+
+	if (delta->status == GIT_DELTA_UNMODIFIED &&
+		(flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+		return true;
+
+	if (delta->status == GIT_DELTA_IGNORED &&
+		(flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+		return true;
+
+	if (delta->status == GIT_DELTA_UNTRACKED &&
+		(flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+		return true;
+
+	return false;
+}
+
+
 static int config_bool(git_config *cfg, const char *name, int defvalue)
 {
 	int val = defvalue;
@@ -281,14 +271,14 @@
 	git_repository *repo, const git_diff_options *opts)
 {
 	git_config *cfg;
-	size_t i;
 	git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
 	if (diff == NULL)
 		return NULL;
 
+	GIT_REFCOUNT_INC(diff);
 	diff->repo = repo;
 
-	if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+	if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
 		git_pool_init(&diff->pool, 1, 0) < 0)
 		goto fail;
 
@@ -300,17 +290,37 @@
 	if (config_bool(cfg, "core.ignorestat", 0))
 		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
 	if (config_bool(cfg, "core.filemode", 1))
-		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS;
 	if (config_bool(cfg, "core.trustctime", 1))
 		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
 	/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
 
-	if (opts == NULL)
+	/* TODO: there are certain config settings where even if we were
+	 * not given an options structure, we need the diff list to have one
+	 * so that we can store the altered default values.
+	 *
+	 * - diff.ignoreSubmodules
+	 * - diff.mnemonicprefix
+	 * - diff.noprefix
+	 */
+
+	if (opts == NULL) {
+		/* Make sure we default to 3 lines */
+		diff->opts.context_lines = 3;
 		return diff;
+	}
 
 	memcpy(&diff->opts, opts, sizeof(git_diff_options));
-	memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
 
+	if(opts->flags & GIT_DIFF_IGNORE_FILEMODE)
+		diff->diffcaps = diff->diffcaps & ~GIT_DIFFCAPS_TRUST_MODE_BITS;
+
+	/* pathspec init will do nothing for empty pathspec */
+	if (git_pathspec_init(&diff->pathspec, &opts->pathspec, &diff->pool) < 0)
+		goto fail;
+
+	/* TODO: handle config diff.mnemonicprefix, diff.noprefix */
+
 	diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
 		opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
 	diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
@@ -319,39 +329,15 @@
 	if (!diff->opts.old_prefix || !diff->opts.new_prefix)
 		goto fail;
 
-	if (diff->opts.flags & GIT_DIFF_REVERSE) {
-		char *swap = diff->opts.old_prefix;
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+		const char *swap = diff->opts.old_prefix;
 		diff->opts.old_prefix = diff->opts.new_prefix;
 		diff->opts.new_prefix = swap;
 	}
 
-	/* only copy pathspec if it is "interesting" so we can test
-	 * diff->pathspec.length > 0 to know if it is worth calling
-	 * fnmatch as we iterate.
-	 */
-	if (!diff_pathspec_is_interesting(&opts->pathspec))
-		return diff;
-
-	if (git_vector_init(
-		&diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
-		goto fail;
-
-	for (i = 0; i < opts->pathspec.count; ++i) {
-		int ret;
-		const char *pattern = opts->pathspec.strings[i];
-		git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
-		if (!match)
-			goto fail;
-		ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
-		if (ret == GIT_ENOTFOUND) {
-			git__free(match);
-			continue;
-		} else if (ret < 0)
-			goto fail;
-
-		if (git_vector_insert(&diff->pathspec, match) < 0)
-			goto fail;
-	}
+	/* INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
+		diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
 
 	return diff;
 
@@ -360,65 +346,106 @@
 	return NULL;
 }
 
-void git_diff_list_free(git_diff_list *diff)
+static void diff_list_free(git_diff_list *diff)
 {
 	git_diff_delta *delta;
-	git_attr_fnmatch *match;
 	unsigned int i;
 
-	if (!diff)
-		return;
-
 	git_vector_foreach(&diff->deltas, i, delta) {
 		git__free(delta);
 		diff->deltas.contents[i] = NULL;
 	}
 	git_vector_free(&diff->deltas);
 
-	git_vector_foreach(&diff->pathspec, i, match) {
-		git__free(match);
-		diff->pathspec.contents[i] = NULL;
-	}
-	git_vector_free(&diff->pathspec);
-
+	git_pathspec_free(&diff->pathspec);
 	git_pool_clear(&diff->pool);
 	git__free(diff);
 }
 
-static int oid_for_workdir_item(
+void git_diff_list_free(git_diff_list *diff)
+{
+	if (!diff)
+		return;
+
+	GIT_REFCOUNT_DEC(diff, diff_list_free);
+}
+
+void git_diff_list_addref(git_diff_list *diff)
+{
+	GIT_REFCOUNT_INC(diff);
+}
+
+int git_diff__oid_for_file(
 	git_repository *repo,
-	const git_index_entry *item,
+	const char *path,
+	uint16_t  mode,
+	git_off_t size,
 	git_oid *oid)
 {
-	int result;
+	int result = 0;
 	git_buf full_path = GIT_BUF_INIT;
 
-	if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
+	if (git_buf_joinpath(
+		&full_path, git_repository_workdir(repo), path) < 0)
 		return -1;
 
-	/* calculate OID for file if possible*/
-	if (S_ISLNK(item->mode))
+	if (!mode) {
+		struct stat st;
+
+		if (p_stat(path, &st) < 0) {
+			giterr_set(GITERR_OS, "Could not stat '%s'", path);
+			result = -1;
+			goto cleanup;
+		}
+
+		mode = st.st_mode;
+		size = st.st_size;
+	}
+
+	/* calculate OID for file if possible */
+	if (S_ISGITLINK(mode)) {
+		git_submodule *sm;
+		const git_oid *sm_oid;
+
+		if (!git_submodule_lookup(&sm, repo, path) &&
+			(sm_oid = git_submodule_wd_id(sm)) != NULL)
+			git_oid_cpy(oid, sm_oid);
+		else {
+			/* if submodule lookup failed probably just in an intermediate
+			 * state where some init hasn't happened, so ignore the error
+			 */
+			giterr_clear();
+			memset(oid, 0, sizeof(*oid));
+		}
+	} else if (S_ISLNK(mode)) {
 		result = git_odb__hashlink(oid, full_path.ptr);
-	else if (!git__is_sizet(item->file_size)) {
-		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+	} else if (!git__is_sizet(size)) {
+		giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path);
 		result = -1;
 	} else {
-		int fd = git_futils_open_ro(full_path.ptr);
-		if (fd < 0)
-			result = fd;
-		else {
-			result = git_odb__hashfd(
-				oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
-			p_close(fd);
+		git_vector filters = GIT_VECTOR_INIT;
+
+		result = git_filters_load(&filters, repo, path, GIT_FILTER_TO_ODB);
+		if (result >= 0) {
+			int fd = git_futils_open_ro(full_path.ptr);
+			if (fd < 0)
+				result = fd;
+			else {
+				result = git_odb__hashfd_filtered(
+					oid, fd, (size_t)size, GIT_OBJ_BLOB, &filters);
+				p_close(fd);
+			}
 		}
+
+		git_filters_free(&filters);
 	}
 
+cleanup:
 	git_buf_free(&full_path);
-
 	return result;
 }
 
-#define EXEC_BIT_MASK 0000111
+#define MODE_BITS_MASK 0000777
 
 static int maybe_modified(
 	git_iterator *old_iter,
@@ -431,24 +458,30 @@
 	git_delta_t status = GIT_DELTA_MODIFIED;
 	unsigned int omode = oitem->mode;
 	unsigned int nmode = nitem->mode;
+	bool new_is_workdir = (new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
+	const char *matched_pathspec;
 
 	GIT_UNUSED(old_iter);
 
-	if (!diff_path_matches_pathspec(diff, oitem->path))
+	if (!git_pathspec_match_path(
+			&diff->pathspec, oitem->path,
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
+			DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
+			&matched_pathspec))
 		return 0;
 
-	/* on platforms with no symlinks, promote plain files to symlinks */
-	if (S_ISLNK(omode) && S_ISREG(nmode) &&
+	/* on platforms with no symlinks, preserve mode of existing symlinks */
+	if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
 		!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
-		nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
+		nmode = omode;
 
-	/* on platforms with no execmode, clear exec bit from comparisons */
-	if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
-		omode = omode & ~EXEC_BIT_MASK;
-		nmode = nmode & ~EXEC_BIT_MASK;
-	}
+	/* on platforms with no execmode, just preserve old mode */
+	if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
+		(nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
+		new_is_workdir)
+		nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
 
-	/* support "assume unchanged" (badly, b/c we still stat everything) */
+	/* support "assume unchanged" (poorly, b/c we still stat everything) */
 	if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
 		status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
 			GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
@@ -459,23 +492,29 @@
 
 	/* if basic type of file changed, then split into delete and add */
 	else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
-		if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
-			diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
-			return -1;
-		return 0;
+		if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE))
+			status = GIT_DELTA_TYPECHANGE;
+		else {
+			if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
+				diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
+				return -1;
+			return 0;
+		}
 	}
 
 	/* if oids and modes match, then file is unmodified */
-	else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
-			 omode == nmode)
+	else if (git_oid_equal(&oitem->oid, &nitem->oid) && omode == nmode)
 		status = GIT_DELTA_UNMODIFIED;
 
-	/* if we have a workdir item with an unknown oid, check deeper */
-	else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
+	/* if we have an unknown OID and a workdir iterator, then check some
+	 * circumstances that can accelerate things or need special handling
+	 */
+	else if (git_oid_iszero(&nitem->oid) && new_is_workdir) {
 		/* TODO: add check against index file st_mtime to avoid racy-git */
 
-		/* if they files look exactly alike, then we'll assume the same */
-		if (oitem->file_size == nitem->file_size &&
+		/* if the stat data looks exactly alike, then assume the same */
+		if (omode == nmode &&
+			oitem->file_size == nitem->file_size &&
 			(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
 			 (oitem->ctime.seconds == nitem->ctime.seconds)) &&
 			oitem->mtime.seconds == nitem->mtime.seconds &&
@@ -487,77 +526,193 @@
 			status = GIT_DELTA_UNMODIFIED;
 
 		else if (S_ISGITLINK(nmode)) {
+			int err;
 			git_submodule *sub;
 
-			if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+			if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
 				status = GIT_DELTA_UNMODIFIED;
-			else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
-				return -1;
-			else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
+			else if ((err = git_submodule_lookup(&sub, diff->repo, nitem->path)) < 0) {
+				if (err == GIT_EEXISTS)
+					status = GIT_DELTA_UNMODIFIED;
+				else
+					return err;
+			} else if (git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
 				status = GIT_DELTA_UNMODIFIED;
 			else {
-				/* TODO: support other GIT_SUBMODULE_IGNORE values */
-				status = GIT_DELTA_UNMODIFIED;
+				unsigned int sm_status = 0;
+				if (git_submodule_status(&sm_status, sub) < 0)
+					return -1;
+
+				/* check IS_WD_UNMODIFIED because this case is only used
+				 * when the new side of the diff is the working directory
+				 */
+				status = GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)
+						 ? GIT_DELTA_UNMODIFIED : GIT_DELTA_MODIFIED;
+
+				/* grab OID while we are here */
+				if (git_oid_iszero(&nitem->oid)) {
+					const git_oid *sm_oid = git_submodule_wd_id(sub);
+					if (sm_oid != NULL) {
+						git_oid_cpy(&noid, sm_oid);
+						use_noid = &noid;
+					}
+				}
 			}
 		}
+	}
 
-		/* TODO: check git attributes so we will not have to read the file
-		 * in if it is marked binary.
-		 */
+	/* if mode is GITLINK and submodules are ignored, then skip */
+	else if (S_ISGITLINK(nmode) &&
+			 DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
+		status = GIT_DELTA_UNMODIFIED;
 
-		else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
-			return -1;
+	/* if we got here and decided that the files are modified, but we
+	 * haven't calculated the OID of the new item, then calculate it now
+	 */
+	if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
+		if (!use_noid) {
+			if (git_diff__oid_for_file(diff->repo,
+					nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
+				return -1;
+			use_noid = &noid;
+		}
 
-		else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
-				 omode == nmode)
+		/* if oid matches, then mark unmodified (except submodules, where
+		 * the filesystem content may be modified even if the oid still
+		 * matches between the index and the workdir HEAD)
+		 */
+		if (omode == nmode && !S_ISGITLINK(omode) &&
+			git_oid_equal(&oitem->oid, use_noid))
 			status = GIT_DELTA_UNMODIFIED;
+	}
 
-		/* store calculated oid so we don't have to recalc later */
-		use_noid = &noid;
+	return diff_delta__from_two(
+		diff, status, oitem, omode, nitem, nmode, use_noid, matched_pathspec);
+}
+
+static bool entry_is_prefixed(
+	git_diff_list *diff,
+	const git_index_entry *item,
+	const git_index_entry *prefix_item)
+{
+	size_t pathlen;
+
+	if (!item || diff->pfxcomp(item->path, prefix_item->path) != 0)
+		return false;
+
+	pathlen = strlen(prefix_item->path);
+
+	return (prefix_item->path[pathlen - 1] == '/' ||
+			item->path[pathlen] == '\0' ||
+			item->path[pathlen] == '/');
+}
+
+static int diff_list_init_from_iterators(
+	git_diff_list *diff,
+	git_iterator *old_iter,
+	git_iterator *new_iter)
+{
+	diff->old_src = old_iter->type;
+	diff->new_src = new_iter->type;
+
+	/* Use case-insensitive compare if either iterator has
+	 * the ignore_case bit set */
+	if (!git_iterator_ignore_case(old_iter) &&
+		!git_iterator_ignore_case(new_iter))
+	{
+		diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
+
+		diff->strcomp    = git__strcmp;
+		diff->strncomp   = git__strncmp;
+		diff->pfxcomp    = git__prefixcmp;
+		diff->entrycomp  = git_index_entry__cmp;
+	} else {
+		diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
+
+		diff->strcomp    = git__strcasecmp;
+		diff->strncomp   = git__strncasecmp;
+		diff->pfxcomp    = git__prefixcmp_icase;
+		diff->entrycomp  = git_index_entry__cmp_icase;
 	}
 
-	return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
+	return 0;
 }
 
-static int diff_from_iterators(
+int git_diff__from_iterators(
+	git_diff_list **diff_ptr,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
 	git_iterator *old_iter,
 	git_iterator *new_iter,
-	git_diff_list **diff_ptr)
+	const git_diff_options *opts)
 {
+	int error = 0;
 	const git_index_entry *oitem, *nitem;
 	git_buf ignore_prefix = GIT_BUF_INIT;
 	git_diff_list *diff = git_diff_list_alloc(repo, opts);
-	if (!diff)
+
+	*diff_ptr = NULL;
+
+	if (!diff || diff_list_init_from_iterators(diff, old_iter, new_iter) < 0)
 		goto fail;
 
-	diff->old_src = old_iter->type;
-	diff->new_src = new_iter->type;
+	if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
+		if (git_iterator_set_ignore_case(old_iter, true) < 0 ||
+			git_iterator_set_ignore_case(new_iter, true) < 0)
+			goto fail;
+	}
 
-	if (git_iterator_current(old_iter, &oitem) < 0 ||
-		git_iterator_current(new_iter, &nitem) < 0)
+	if (git_iterator_current(&oitem, old_iter) < 0 ||
+		git_iterator_current(&nitem, new_iter) < 0)
 		goto fail;
 
 	/* run iterators building diffs */
 	while (oitem || nitem) {
+		int cmp = oitem ? (nitem ? diff->entrycomp(oitem, nitem) : -1) : 1;
 
 		/* create DELETED records for old items not matched in new */
-		if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
-			if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
-				git_iterator_advance(old_iter, &oitem) < 0)
+		if (cmp < 0) {
+			if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0)
 				goto fail;
+
+			/* if we are generating TYPECHANGE records then check for that
+			 * instead of just generating a DELETE record
+			 */
+			if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
+				entry_is_prefixed(diff, nitem, oitem))
+			{
+				/* this entry has become a tree! convert to TYPECHANGE */
+				git_diff_delta *last = diff_delta__last_for_item(diff, oitem);
+				if (last) {
+					last->status = GIT_DELTA_TYPECHANGE;
+					last->new_file.mode = GIT_FILEMODE_TREE;
+				}
+
+				/* If new_iter is a workdir iterator, then this situation
+				 * will certainly be followed by a series of untracked items.
+				 * Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
+				 */
+				if (S_ISDIR(nitem->mode) &&
+					DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
+				{
+					if (git_iterator_advance(&nitem, new_iter) < 0)
+						goto fail;
+				}
+			}
+
+			if (git_iterator_advance(&oitem, old_iter) < 0)
+				goto fail;
 		}
 
 		/* create ADDED, TRACKED, or IGNORED records for new items not
 		 * matched in old (and/or descend into directories as needed)
 		 */
-		else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
+		else if (cmp > 0) {
 			git_delta_t delta_type = GIT_DELTA_UNTRACKED;
+			bool contains_oitem = entry_is_prefixed(diff, oitem, nitem);
 
 			/* check if contained in ignored parent directory */
 			if (git_buf_len(&ignore_prefix) &&
-				git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
+				diff->pfxcomp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
 				delta_type = GIT_DELTA_IGNORED;
 
 			if (S_ISDIR(nitem->mode)) {
@@ -565,20 +720,48 @@
 				 * it or if the user requested the contents of untracked
 				 * directories and it is not under an ignored directory.
 				 */
-				if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
+				bool recurse_into_dir =
 					(delta_type == GIT_DELTA_UNTRACKED &&
-					 (diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
-				{
-					/* if this directory is ignored, remember it as the
-					 * "ignore_prefix" for processing contained items
-					 */
-					if (delta_type == GIT_DELTA_UNTRACKED &&
-						git_iterator_current_is_ignored(new_iter))
-						git_buf_sets(&ignore_prefix, nitem->path);
+					 DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
+					(delta_type == GIT_DELTA_IGNORED &&
+					 DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
 
-					if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
+				/* do not advance into directories that contain a .git file */
+				if (!contains_oitem && recurse_into_dir) {
+					git_buf *full = NULL;
+					if (git_iterator_current_workdir_path(&full, new_iter) < 0)
 						goto fail;
+					if (git_path_contains_dir(full, DOT_GIT))
+						recurse_into_dir = false;
+				}
 
+				/* if directory is ignored, remember ignore_prefix */
+				if ((contains_oitem || recurse_into_dir) &&
+					delta_type == GIT_DELTA_UNTRACKED &&
+					git_iterator_current_is_ignored(new_iter))
+				{
+					git_buf_sets(&ignore_prefix, nitem->path);
+					delta_type = GIT_DELTA_IGNORED;
+
+					/* skip recursion if we've just learned this is ignored */
+					if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS))
+						recurse_into_dir = false;
+				}
+
+				if (contains_oitem || recurse_into_dir) {
+					/* advance into directory */
+					error = git_iterator_advance_into(&nitem, new_iter);
+
+					/* if directory is empty, can't advance into it, so skip */
+					if (error == GIT_ENOTFOUND) {
+						giterr_clear();
+						error = git_iterator_advance(&nitem, new_iter);
+
+						git_buf_clear(&ignore_prefix);
+					}
+
+					if (error < 0)
+						goto fail;
 					continue;
 				}
 			}
@@ -598,8 +781,9 @@
 			 * checked before container directory exclusions are used to
 			 * skip the file.
 			 */
-			else if (delta_type == GIT_DELTA_IGNORED) {
-				if (git_iterator_advance(new_iter, &nitem) < 0)
+			else if (delta_type == GIT_DELTA_IGNORED &&
+					 DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) {
+				if (git_iterator_advance(&nitem, new_iter) < 0)
 					goto fail;
 				continue; /* ignored parent directory, so skip completely */
 			}
@@ -607,177 +791,145 @@
 			else if (git_iterator_current_is_ignored(new_iter))
 				delta_type = GIT_DELTA_IGNORED;
 
-			else if (new_iter->type != GIT_ITERATOR_WORKDIR)
+			else if (new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
 				delta_type = GIT_DELTA_ADDED;
 
-			if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
-				git_iterator_advance(new_iter, &nitem) < 0)
+			if (diff_delta__from_one(diff, delta_type, nitem) < 0)
 				goto fail;
+
+			/* if we are generating TYPECHANGE records then check for that
+			 * instead of just generating an ADDED/UNTRACKED record
+			 */
+			if (delta_type != GIT_DELTA_IGNORED &&
+				DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
+				contains_oitem)
+			{
+				/* this entry was prefixed with a tree - make TYPECHANGE */
+				git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
+				if (last) {
+					last->status = GIT_DELTA_TYPECHANGE;
+					last->old_file.mode = GIT_FILEMODE_TREE;
+				}
+			}
+
+			if (git_iterator_advance(&nitem, new_iter) < 0)
+				goto fail;
 		}
 
 		/* otherwise item paths match, so create MODIFIED record
 		 * (or ADDED and DELETED pair if type changed)
 		 */
 		else {
-			assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
+			assert(oitem && nitem && cmp == 0);
 
 			if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
-				git_iterator_advance(old_iter, &oitem) < 0 ||
-				git_iterator_advance(new_iter, &nitem) < 0)
+				git_iterator_advance(&oitem, old_iter) < 0 ||
+				git_iterator_advance(&nitem, new_iter) < 0)
 				goto fail;
 		}
 	}
 
-	git_iterator_free(old_iter);
-	git_iterator_free(new_iter);
-	git_buf_free(&ignore_prefix);
-
 	*diff_ptr = diff;
-	return 0;
 
 fail:
-	git_iterator_free(old_iter);
-	git_iterator_free(new_iter);
+	if (!*diff_ptr) {
+		git_diff_list_free(diff);
+		error = -1;
+	}
+
 	git_buf_free(&ignore_prefix);
 
-	git_diff_list_free(diff);
-	*diff_ptr = NULL;
-	return -1;
+	return error;
 }
 
+#define DIFF_FROM_ITERATORS(MAKE_FIRST, MAKE_SECOND) do { \
+	git_iterator *a = NULL, *b = NULL; \
+	char *pfx = opts ? git_pathspec_prefix(&opts->pathspec) : NULL; \
+	GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); \
+	if (!(error = MAKE_FIRST) && !(error = MAKE_SECOND)) \
+		error = git_diff__from_iterators(diff, repo, a, b, opts); \
+	git__free(pfx); git_iterator_free(a); git_iterator_free(b); \
+} while (0)
 
 int git_diff_tree_to_tree(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts, /**< can be NULL for defaults */
 	git_tree *old_tree,
 	git_tree *new_tree,
-	git_diff_list **diff)
+	const git_diff_options *opts)
 {
-	git_iterator *a = NULL, *b = NULL;
-	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
-
-	assert(repo && old_tree && new_tree && diff);
+	int error = 0;
 
-	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
-		git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
-		return -1;
+	assert(diff && repo);
 
-	git__free(prefix);
+	DIFF_FROM_ITERATORS(
+		git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
+		git_iterator_for_tree(&b, new_tree, 0, pfx, pfx)
+	);
 
-	return diff_from_iterators(repo, opts, a, b, diff);
+	return error;
 }
 
-int git_diff_index_to_tree(
+int git_diff_tree_to_index(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts,
 	git_tree *old_tree,
-	git_diff_list **diff)
+	git_index *index,
+	const git_diff_options *opts)
 {
-	git_iterator *a = NULL, *b = NULL;
-	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+	int error = 0;
 
-	assert(repo && diff);
+	assert(diff && repo);
 
-	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
-		git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
-		return -1;
+	if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+		return error;
 
-	git__free(prefix);
+	DIFF_FROM_ITERATORS(
+		git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
+		git_iterator_for_index(&b, index, 0, pfx, pfx)
+	);
 
-	return diff_from_iterators(repo, opts, a, b, diff);
+	return error;
 }
 
-int git_diff_workdir_to_index(
+int git_diff_index_to_workdir(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts,
-	git_diff_list **diff)
+	git_index *index,
+	const git_diff_options *opts)
 {
-	git_iterator *a = NULL, *b = NULL;
-	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+	int error = 0;
 
-	assert(repo && diff);
+	assert(diff && repo);
 
-	if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
-		git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
-		return -1;
+	if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+		return error;
 
-	git__free(prefix);
+	DIFF_FROM_ITERATORS(
+		git_iterator_for_index(&a, index, 0, pfx, pfx),
+		git_iterator_for_workdir(
+			&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
+	);
 
-	return diff_from_iterators(repo, opts, a, b, diff);
+	return error;
 }
 
 
-int git_diff_workdir_to_tree(
+int git_diff_tree_to_workdir(
+	git_diff_list **diff,
 	git_repository *repo,
-	const git_diff_options *opts,
 	git_tree *old_tree,
-	git_diff_list **diff)
-{
-	git_iterator *a = NULL, *b = NULL;
-	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
-
-	assert(repo && old_tree && diff);
-
-	if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
-		git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
-		return -1;
-
-	git__free(prefix);
-
-	return diff_from_iterators(repo, opts, a, b, diff);
-}
-
-int git_diff_merge(
-	git_diff_list *onto,
-	const git_diff_list *from)
+	const git_diff_options *opts)
 {
 	int error = 0;
-	git_pool onto_pool;
-	git_vector onto_new;
-	git_diff_delta *delta;
-	unsigned int i, j;
 
-	assert(onto && from);
-
-	if (!from->deltas.length)
-		return 0;
-
-	if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
-		git_pool_init(&onto_pool, 1, 0) < 0)
-		return -1;
-
-	for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
-		git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
-		const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
-		int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
-
-		if (cmp < 0) {
-			delta = diff_delta__dup(o, &onto_pool);
-			i++;
-		} else if (cmp > 0) {
-			delta = diff_delta__dup(f, &onto_pool);
-			j++;
-		} else {
-			delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
-			i++;
-			j++;
-		}
-
-		if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
-			break;
-	}
-
-	if (!error) {
-		git_vector_swap(&onto->deltas, &onto_new);
-		git_pool_swap(&onto->pool, &onto_pool);
-		onto->new_src = from->new_src;
-	}
+	assert(diff && repo);
 
-	git_vector_foreach(&onto_new, i, delta)
-		git__free(delta);
-	git_vector_free(&onto_new);
-	git_pool_clear(&onto_pool);
+	DIFF_FROM_ITERATORS(
+		git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
+		git_iterator_for_workdir(
+			&b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx)
+	);
 
 	return error;
 }
-
diff --git a/libgit2/src/diff.h b/libgit2/src/diff.h
--- a/libgit2/src/diff.h
+++ b/libgit2/src/diff.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,6 +7,9 @@
 #ifndef INCLUDE_diff_h__
 #define INCLUDE_diff_h__
 
+#include "git2/diff.h"
+#include "git2/oid.h"
+
 #include <stdio.h>
 #include "vector.h"
 #include "buffer.h"
@@ -20,21 +23,56 @@
 enum {
 	GIT_DIFFCAPS_HAS_SYMLINKS     = (1 << 0), /* symlinks on platform? */
 	GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
-	GIT_DIFFCAPS_TRUST_EXEC_BIT   = (1 << 2), /* use st_mode exec bit? */
+	GIT_DIFFCAPS_TRUST_MODE_BITS  = (1 << 2), /* use st_mode? */
 	GIT_DIFFCAPS_TRUST_CTIME      = (1 << 3), /* use st_ctime? */
 	GIT_DIFFCAPS_USE_DEV          = (1 << 4), /* use st_dev? */
 };
 
+enum {
+	GIT_DIFF_FLAG__FREE_PATH  = (1 << 7),  /* `path` is allocated memory */
+	GIT_DIFF_FLAG__FREE_DATA  = (1 << 8),  /* internal file data is allocated */
+	GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9),  /* internal file data is mmap'ed */
+	GIT_DIFF_FLAG__NO_DATA    = (1 << 10), /* file data should not be loaded */
+	GIT_DIFF_FLAG__TO_DELETE  = (1 << 11), /* delete entry during rename det. */
+	GIT_DIFF_FLAG__TO_SPLIT   = (1 << 12), /* split entry during rename det. */
+};
+
 struct git_diff_list {
+	git_refcount     rc;
 	git_repository   *repo;
 	git_diff_options opts;
 	git_vector       pathspec;
-	git_vector       deltas;    /* vector of git_diff_file_delta */
+	git_vector       deltas;    /* vector of git_diff_delta */
 	git_pool pool;
 	git_iterator_type_t old_src;
 	git_iterator_type_t new_src;
 	uint32_t diffcaps;
+
+	int (*strcomp)(const char *, const char *);
+	int (*strncomp)(const char *, const char *, size_t);
+	int (*pfxcomp)(const char *str, const char *pfx);
+	int (*entrycomp)(const void *a, const void *b);
 };
+
+extern void git_diff__cleanup_modes(
+	uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
+
+extern void git_diff_list_addref(git_diff_list *diff);
+
+extern int git_diff_delta__cmp(const void *a, const void *b);
+
+extern bool git_diff_delta__should_skip(
+	const git_diff_options *opts, const git_diff_delta *delta);
+
+extern int git_diff__oid_for_file(
+	git_repository *, const char *, uint16_t, git_off_t, git_oid *);
+
+extern int git_diff__from_iterators(
+	git_diff_list **diff_ptr,
+	git_repository *repo,
+	git_iterator *old_iter,
+	git_iterator *new_iter,
+	const git_diff_options *opts);
 
 #endif
 
diff --git a/libgit2/src/diff_output.c b/libgit2/src/diff_output.c
--- a/libgit2/src/diff_output.c
+++ b/libgit2/src/diff_output.c
@@ -1,786 +1,1819 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "git2/diff.h"
-#include "git2/attr.h"
-#include "git2/blob.h"
-#include "xdiff/xdiff.h"
-#include <ctype.h>
-#include "diff.h"
-#include "map.h"
-#include "fileops.h"
-#include "filter.h"
-
-typedef struct {
-	git_diff_list *diff;
-	void *cb_data;
-	git_diff_hunk_fn hunk_cb;
-	git_diff_data_fn line_cb;
-	unsigned int index;
-	git_diff_delta *delta;
-	git_diff_range range;
-} diff_output_info;
-
-static int read_next_int(const char **str, int *value)
-{
-	const char *scan = *str;
-	int v = 0, digits = 0;
-	/* find next digit */
-	for (scan = *str; *scan && !isdigit(*scan); scan++);
-	/* parse next number */
-	for (; isdigit(*scan); scan++, digits++)
-		v = (v * 10) + (*scan - '0');
-	*str = scan;
-	*value = v;
-	return (digits > 0) ? 0 : -1;
-}
-
-static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
-{
-	diff_output_info *info = priv;
-
-	if (len == 1 && info->hunk_cb) {
-		git_diff_range range = { -1, 0, -1, 0 };
-		const char *scan = bufs[0].ptr;
-
-		/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
-		if (*scan != '@')
-			return -1;
-
-		if (read_next_int(&scan, &range.old_start) < 0)
-			return -1;
-		if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
-			return -1;
-
-		if (read_next_int(&scan, &range.new_start) < 0)
-			return -1;
-		if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
-			return -1;
-
-		if (range.old_start < 0 || range.new_start < 0)
-			return -1;
-
-		memcpy(&info->range, &range, sizeof(git_diff_range));
-
-		return info->hunk_cb(
-			info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
-	}
-
-	if ((len == 2 || len == 3) && info->line_cb) {
-		int origin;
-
-		/* expect " "/"-"/"+", then data, then maybe newline */
-		origin =
-			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
-			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
-			GIT_DIFF_LINE_CONTEXT;
-
-		if (info->line_cb(
-			info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
-			return -1;
-
-		/* deal with adding and removing newline at EOF */
-		if (len == 3) {
-			if (origin == GIT_DIFF_LINE_ADDITION)
-				origin = GIT_DIFF_LINE_ADD_EOFNL;
-			else
-				origin = GIT_DIFF_LINE_DEL_EOFNL;
-
-			return info->line_cb(
-				info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
-		}
-	}
-
-	return 0;
-}
-
-#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
-
-static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
-{
-	const char *value;
-	if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
-		return -1;
-
-	if (GIT_ATTR_FALSE(value))
-		file->flags |= GIT_DIFF_FILE_BINARY;
-	else if (GIT_ATTR_TRUE(value))
-		file->flags |= GIT_DIFF_FILE_NOT_BINARY;
-	/* otherwise leave file->flags alone */
-
-	return 0;
-}
-
-static void update_delta_is_binary(git_diff_delta *delta)
-{
-	if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
-		(delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
-		delta->binary = 1;
-	else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||
-			 (delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)
-		delta->binary = 0;
-	/* otherwise leave delta->binary value untouched */
-}
-
-static int file_is_binary_by_attr(
-	git_diff_list *diff,
-	git_diff_delta *delta)
-{
-	int error = 0, mirror_new;
-
-	delta->binary = -1;
-
-	/* make sure files are conceivably mmap-able */
-	if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
-		(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
-	{
-		delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
-		delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
-		delta->binary = 1;
-		return 0;
-	}
-
-	/* check if user is forcing us to text diff these files */
-	if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
-		delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
-		delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
-		delta->binary = 0;
-		return 0;
-	}
-
-	/* check diff attribute +, -, or 0 */
-	if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
-		return -1;
-
-	mirror_new = (delta->new_file.path == delta->old_file.path ||
-				  strcmp(delta->new_file.path, delta->old_file.path) == 0);
-	if (mirror_new)
-		delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);
-	else
-		error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
-
-	update_delta_is_binary(delta);
-
-	return error;
-}
-
-static int file_is_binary_by_content(
-	git_diff_delta *delta,
-	git_map *old_data,
-	git_map *new_data)
-{
-	git_buf search;
-
-	if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
-		search.ptr  = old_data->data;
-		search.size = min(old_data->len, 4000);
-
-		if (git_buf_is_binary(&search))
-			delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
-		else
-			delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
-	}
-
-	if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
-		search.ptr  = new_data->data;
-		search.size = min(new_data->len, 4000);
-
-		if (git_buf_is_binary(&search))
-			delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
-		else
-			delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
-	}
-
-	update_delta_is_binary(delta);
-
-	/* TODO: if value != NULL, implement diff drivers */
-
-	return 0;
-}
-
-static void setup_xdiff_options(
-	git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
-{
-	memset(cfg, 0, sizeof(xdemitconf_t));
-	memset(param, 0, sizeof(xpparam_t));
-
-	cfg->ctxlen =
-		(!opts || !opts->context_lines) ? 3 : opts->context_lines;
-	cfg->interhunkctxlen =
-		(!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines;
-
-	if (!opts)
-		return;
-
-	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
-		param->flags |= XDF_WHITESPACE_FLAGS;
-	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
-		param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
-	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
-		param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
-}
-
-static int get_blob_content(
-	git_repository *repo,
-	const git_oid *oid,
-	git_map *map,
-	git_blob **blob)
-{
-	if (git_oid_iszero(oid))
-		return 0;
-
-	if (git_blob_lookup(blob, repo, oid) < 0)
-		return -1;
-
-	map->data = (void *)git_blob_rawcontent(*blob);
-	map->len  = git_blob_rawsize(*blob);
-	return 0;
-}
-
-static int get_workdir_content(
-	git_repository *repo,
-	git_diff_file *file,
-	git_map *map)
-{
-	int error = 0;
-	git_buf path = GIT_BUF_INIT;
-
-	if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)
-		return -1;
-
-	if (S_ISLNK(file->mode)) {
-		ssize_t read_len;
-
-		file->flags |= GIT_DIFF_FILE_FREE_DATA;
-		file->flags |= GIT_DIFF_FILE_BINARY;
-
-		map->data = git__malloc((size_t)file->size + 1);
-		GITERR_CHECK_ALLOC(map->data);
-
-		read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
-		if (read_len != (ssize_t)file->size) {
-			giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
-			error = -1;
-		} else
-			map->len = read_len;
-	}
-	else {
-		error = git_futils_mmap_ro_file(map, path.ptr);
-		file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
-	}
-	git_buf_free(&path);
-	return error;
-}
-
-static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
-{
-	if (blob != NULL)
-		git_blob_free(blob);
-
-	if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
-		git__free(map->data);
-		map->data = NULL;
-		file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
-	}
-	else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
-		git_futils_mmap_free(map);
-		map->data = NULL;
-		file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
-	}
-}
-
-static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {
-	assert(dst && src);
-
-	dst->data = src->ptr;
-	dst->len = src->size;
-#ifdef GIT_WIN32
-	dst->fmh = NULL;
-#endif
-}
-
-int git_diff_foreach(
-	git_diff_list *diff,
-	void *data,
-	git_diff_file_fn file_cb,
-	git_diff_hunk_fn hunk_cb,
-	git_diff_data_fn line_cb)
-{
-	int error = 0;
-	diff_output_info info;
-	git_diff_delta *delta;
-	xpparam_t    xdiff_params;
-	xdemitconf_t xdiff_config;
-	xdemitcb_t   xdiff_callback;
-
-	info.diff    = diff;
-	info.cb_data = data;
-	info.hunk_cb = hunk_cb;
-	info.line_cb = line_cb;
-
-	setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
-	memset(&xdiff_callback, 0, sizeof(xdiff_callback));
-	xdiff_callback.outf = diff_output_cb;
-	xdiff_callback.priv = &info;
-
-	git_vector_foreach(&diff->deltas, info.index, delta) {
-		git_blob *old_blob = NULL, *new_blob = NULL;
-		git_map old_data, new_data;
-		mmfile_t old_xdiff_data, new_xdiff_data;
-
-		if (delta->status == GIT_DELTA_UNMODIFIED &&
-			(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
-			continue;
-
-		if (delta->status == GIT_DELTA_IGNORED &&
-			(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
-			continue;
-
-		if (delta->status == GIT_DELTA_UNTRACKED &&
-			(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
-			continue;
-
-		if ((error = file_is_binary_by_attr(diff, delta)) < 0)
-			goto cleanup;
-
-		old_data.data = "";
-		old_data.len = 0;
-		new_data.data = "";
-		new_data.len  = 0;
-
-		/* TODO: Partial blob reading to defer loading whole blob.
-		 * I.e. I want a blob with just the first 4kb loaded, then
-		 * later on I will read the rest of the blob if needed.
-		 */
-
-		/* map files */
-		if (delta->binary != 1 &&
-			(hunk_cb || line_cb) &&
-			(delta->status == GIT_DELTA_DELETED ||
-			 delta->status == GIT_DELTA_MODIFIED))
-		{
-			if (diff->old_src == GIT_ITERATOR_WORKDIR)
-				error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
-			else
-				error = get_blob_content(
-					diff->repo, &delta->old_file.oid, &old_data, &old_blob);
-
-			if (error < 0)
-				goto cleanup;
-		}
-
-		if (delta->binary != 1 &&
-			(hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
-			(delta->status == GIT_DELTA_ADDED ||
-			 delta->status == GIT_DELTA_MODIFIED))
-		{
-			if (diff->new_src == GIT_ITERATOR_WORKDIR)
-				error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
-			else
-				error = get_blob_content(
-					diff->repo, &delta->new_file.oid, &new_data, &new_blob);
-
-			if (error < 0)
-				goto cleanup;
-
-			if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {
-				error = git_odb_hash(
-					&delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
-
-				if (error < 0)
-					goto cleanup;
-
-				/* since we did not have the definitive oid, we may have
-				 * incorrect status and need to skip this item.
-				 */
-				if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
-					delta->status = GIT_DELTA_UNMODIFIED;
-					if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
-						goto cleanup;
-				}
-			}
-		}
-
-		/* if we have not already decided whether file is binary,
-		 * check the first 4K for nul bytes to decide...
-		 */
-		if (delta->binary == -1) {
-			error = file_is_binary_by_content(
-				delta, &old_data, &new_data);
-			if (error < 0)
-				goto cleanup;
-		}
-
-		/* TODO: if ignore_whitespace is set, then we *must* do text
-		 * diffs to tell if a file has really been changed.
-		 */
-
-		if (file_cb != NULL) {
-			error = file_cb(data, delta, (float)info.index / diff->deltas.length);
-			if (error < 0)
-				goto cleanup;
-		}
-
-		/* don't do hunk and line diffs if file is binary */
-		if (delta->binary == 1)
-			goto cleanup;
-
-		/* nothing to do if we did not get data */
-		if (!old_data.len && !new_data.len)
-			goto cleanup;
-
-		assert(hunk_cb || line_cb);
-
-		info.delta = delta;
-		old_xdiff_data.ptr = old_data.data;
-		old_xdiff_data.size = old_data.len;
-		new_xdiff_data.ptr = new_data.data;
-		new_xdiff_data.size = new_data.len;
-
-		xdl_diff(&old_xdiff_data, &new_xdiff_data,
-			&xdiff_params, &xdiff_config, &xdiff_callback);
-
-cleanup:
-		release_content(&delta->old_file, &old_data, old_blob);
-		release_content(&delta->new_file, &new_data, new_blob);
-
-		if (error < 0)
-			break;
-	}
-
-	return error;
-}
-
-
-typedef struct {
-	git_diff_list *diff;
-	git_diff_data_fn print_cb;
-	void *cb_data;
-	git_buf *buf;
-} diff_print_info;
-
-static char pick_suffix(int mode)
-{
-	if (S_ISDIR(mode))
-		return '/';
-	else if (mode & 0100)
-		/* in git, modes are very regular, so we must have 0100755 mode */
-		return '*';
-	else
-		return ' ';
-}
-
-static int print_compact(void *data, git_diff_delta *delta, float progress)
-{
-	diff_print_info *pi = data;
-	char code, old_suffix, new_suffix;
-
-	GIT_UNUSED(progress);
-
-	switch (delta->status) {
-	case GIT_DELTA_ADDED: code = 'A'; break;
-	case GIT_DELTA_DELETED: code = 'D'; break;
-	case GIT_DELTA_MODIFIED: code = 'M'; break;
-	case GIT_DELTA_RENAMED: code = 'R'; break;
-	case GIT_DELTA_COPIED: code = 'C'; break;
-	case GIT_DELTA_IGNORED: code = 'I'; break;
-	case GIT_DELTA_UNTRACKED: code = '?'; break;
-	default: code = 0;
-	}
-
-	if (!code)
-		return 0;
-
-	old_suffix = pick_suffix(delta->old_file.mode);
-	new_suffix = pick_suffix(delta->new_file.mode);
-
-	git_buf_clear(pi->buf);
-
-	if (delta->old_file.path != delta->new_file.path &&
-		strcmp(delta->old_file.path,delta->new_file.path) != 0)
-		git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
-			delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
-	else if (delta->old_file.mode != delta->new_file.mode &&
-		delta->old_file.mode != 0 && delta->new_file.mode != 0)
-		git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
-			delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
-	else if (old_suffix != ' ')
-		git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
-	else
-		git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
-
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-	return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
-}
-
-int git_diff_print_compact(
-	git_diff_list *diff,
-	void *cb_data,
-	git_diff_data_fn print_cb)
-{
-	int error;
-	git_buf buf = GIT_BUF_INIT;
-	diff_print_info pi;
-
-	pi.diff     = diff;
-	pi.print_cb = print_cb;
-	pi.cb_data  = cb_data;
-	pi.buf      = &buf;
-
-	error = git_diff_foreach(diff, &pi, print_compact, NULL, NULL);
-
-	git_buf_free(&buf);
-
-	return error;
-}
-
-
-static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
-{
-	char start_oid[8], end_oid[8];
-
-	/* TODO: Determine a good actual OID range to print */
-	git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);
-	git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);
-
-	/* TODO: Match git diff more closely */
-	if (delta->old_file.mode == delta->new_file.mode) {
-		git_buf_printf(pi->buf, "index %s..%s %o\n",
-			start_oid, end_oid, delta->old_file.mode);
-	} else {
-		if (delta->old_file.mode == 0) {
-			git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
-		} else if (delta->new_file.mode == 0) {
-			git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
-		} else {
-			git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
-			git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
-		}
-		git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
-	}
-
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-	return 0;
-}
-
-static int print_patch_file(void *data, git_diff_delta *delta, float progress)
-{
-	diff_print_info *pi = data;
-	const char *oldpfx = pi->diff->opts.old_prefix;
-	const char *oldpath = delta->old_file.path;
-	const char *newpfx = pi->diff->opts.new_prefix;
-	const char *newpath = delta->new_file.path;
-	int result;
-
-	GIT_UNUSED(progress);
-
-	if (!oldpfx)
-		oldpfx = DIFF_OLD_PREFIX_DEFAULT;
-
-	if (!newpfx)
-		newpfx = DIFF_NEW_PREFIX_DEFAULT;
-
-	git_buf_clear(pi->buf);
-	git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
-
-	if (print_oid_range(pi, delta) < 0)
-		return -1;
-
-	if (git_oid_iszero(&delta->old_file.oid)) {
-		oldpfx = "";
-		oldpath = "/dev/null";
-	}
-	if (git_oid_iszero(&delta->new_file.oid)) {
-		newpfx = "";
-		newpath = "/dev/null";
-	}
-
-	if (delta->binary != 1) {
-		git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
-		git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
-	}
-
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-    result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
-    if (result < 0)
-        return result;
-
-    if (delta->binary != 1)
-        return 0;
-
-	git_buf_clear(pi->buf);
-	git_buf_printf(
-		pi->buf, "Binary files %s%s and %s%s differ\n",
-		oldpfx, oldpath, newpfx, newpath);
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-	return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
-}
-
-static int print_patch_hunk(
-	void *data,
-	git_diff_delta *d,
-	git_diff_range *r,
-	const char *header,
-	size_t header_len)
-{
-	diff_print_info *pi = data;
-
-	git_buf_clear(pi->buf);
-	if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
-		return -1;
-
-	return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
-}
-
-static int print_patch_line(
-	void *data,
-	git_diff_delta *delta,
-	git_diff_range *range,
-	char line_origin, /* GIT_DIFF_LINE value from above */
-	const char *content,
-	size_t content_len)
-{
-	diff_print_info *pi = data;
-
-	git_buf_clear(pi->buf);
-
-	if (line_origin == GIT_DIFF_LINE_ADDITION ||
-		line_origin == GIT_DIFF_LINE_DELETION ||
-		line_origin == GIT_DIFF_LINE_CONTEXT)
-		git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
-	else if (content_len > 0)
-		git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
-
-	if (git_buf_oom(pi->buf))
-		return -1;
-
-	return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
-}
-
-int git_diff_print_patch(
-	git_diff_list *diff,
-	void *cb_data,
-	git_diff_data_fn print_cb)
-{
-	int error;
-	git_buf buf = GIT_BUF_INIT;
-	diff_print_info pi;
-
-	pi.diff     = diff;
-	pi.print_cb = print_cb;
-	pi.cb_data  = cb_data;
-	pi.buf      = &buf;
-
-	error = git_diff_foreach(
-		diff, &pi, print_patch_file, print_patch_hunk, print_patch_line);
-
-	git_buf_free(&buf);
-
-	return error;
-}
-
-int git_diff_blobs(
-	git_blob *old_blob,
-	git_blob *new_blob,
-	git_diff_options *options,
-	void *cb_data,
-	git_diff_file_fn file_cb,
-	git_diff_hunk_fn hunk_cb,
-	git_diff_data_fn line_cb)
-{
-	diff_output_info info;
-	git_diff_delta delta;
-	mmfile_t old_data, new_data;
-	git_map old_map, new_map;
-	xpparam_t xdiff_params;
-	xdemitconf_t xdiff_config;
-	xdemitcb_t xdiff_callback;
-	git_blob *new, *old;
-
-	memset(&delta, 0, sizeof(delta));
-
-	new = new_blob;
-	old = old_blob;
-
-	if (options && (options->flags & GIT_DIFF_REVERSE)) {
-		git_blob *swap = old;
-		old = new;
-		new = swap;
-	}
-
-	if (old) {
-		old_data.ptr  = (char *)git_blob_rawcontent(old);
-		old_data.size = git_blob_rawsize(old);
-		git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));
-	} else {
-		old_data.ptr  = "";
-		old_data.size = 0;
-	}
-
-	if (new) {
-		new_data.ptr  = (char *)git_blob_rawcontent(new);
-		new_data.size = git_blob_rawsize(new);
-		git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));
-	} else {
-		new_data.ptr  = "";
-		new_data.size = 0;
-	}
-
-	/* populate a "fake" delta record */
-	delta.status = new ?
-		(old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
-		(old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
-
-	if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)
-		delta.status = GIT_DELTA_UNMODIFIED;
-
-	delta.old_file.size = old_data.size;
-	delta.new_file.size = new_data.size;
-
-	fill_map_from_mmfile(&old_map, &old_data);
-	fill_map_from_mmfile(&new_map, &new_data);
-
-	if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
-		return -1;
-
-	if (file_cb != NULL) {
-		int error = file_cb(cb_data, &delta, 1);
-		if (error < 0)
-			return error;
-	}
-
-	/* don't do hunk and line diffs if the two blobs are identical */
-	if (delta.status == GIT_DELTA_UNMODIFIED)
-		return 0;
-
-	/* don't do hunk and line diffs if file is binary */
-	if (delta.binary == 1)
-		return 0;
-
-	info.diff    = NULL;
-	info.delta   = &delta;
-	info.cb_data = cb_data;
-	info.hunk_cb = hunk_cb;
-	info.line_cb = line_cb;
-
-	setup_xdiff_options(options, &xdiff_config, &xdiff_params);
-	memset(&xdiff_callback, 0, sizeof(xdiff_callback));
-	xdiff_callback.outf = diff_output_cb;
-	xdiff_callback.priv = &info;
-
-	xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "git2/attr.h"
+#include "git2/oid.h"
+#include "git2/submodule.h"
+#include "diff_output.h"
+#include <ctype.h>
+#include "fileops.h"
+#include "filter.h"
+#include "buf_text.h"
+
+static int read_next_int(const char **str, int *value)
+{
+	const char *scan = *str;
+	int v = 0, digits = 0;
+	/* find next digit */
+	for (scan = *str; *scan && !isdigit(*scan); scan++);
+	/* parse next number */
+	for (; isdigit(*scan); scan++, digits++)
+		v = (v * 10) + (*scan - '0');
+	*str = scan;
+	*value = v;
+	return (digits > 0) ? 0 : -1;
+}
+
+static int parse_hunk_header(git_diff_range *range, const char *header)
+{
+	/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+	if (*header != '@')
+		return -1;
+	if (read_next_int(&header, &range->old_start) < 0)
+		return -1;
+	if (*header == ',') {
+		if (read_next_int(&header, &range->old_lines) < 0)
+			return -1;
+	} else
+		range->old_lines = 1;
+	if (read_next_int(&header, &range->new_start) < 0)
+		return -1;
+	if (*header == ',') {
+		if (read_next_int(&header, &range->new_lines) < 0)
+			return -1;
+	} else
+		range->new_lines = 1;
+	if (range->old_start < 0 || range->new_start < 0)
+		return -1;
+
+	return 0;
+}
+
+#define KNOWN_BINARY_FLAGS (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
+#define NOT_BINARY_FLAGS   (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
+
+static int update_file_is_binary_by_attr(
+	git_repository *repo, git_diff_file *file)
+{
+	const char *value;
+
+	/* because of blob diffs, cannot assume path is set */
+	if (!file->path || !strlen(file->path))
+		return 0;
+
+	if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
+		return -1;
+
+	if (GIT_ATTR_FALSE(value))
+		file->flags |= GIT_DIFF_FLAG_BINARY;
+	else if (GIT_ATTR_TRUE(value))
+		file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+	/* otherwise leave file->flags alone */
+
+	return 0;
+}
+
+static void update_delta_is_binary(git_diff_delta *delta)
+{
+	if ((delta->old_file.flags & GIT_DIFF_FLAG_BINARY) != 0 ||
+		(delta->new_file.flags & GIT_DIFF_FLAG_BINARY) != 0)
+		delta->flags |= GIT_DIFF_FLAG_BINARY;
+
+	else if ((delta->old_file.flags & NOT_BINARY_FLAGS) != 0 &&
+			 (delta->new_file.flags & NOT_BINARY_FLAGS) != 0)
+		delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+
+	/* otherwise leave delta->flags binary value untouched */
+}
+
+/* returns if we forced binary setting (and no further checks needed) */
+static bool diff_delta_is_binary_forced(
+	diff_context *ctxt,
+	git_diff_delta *delta)
+{
+	/* return true if binary-ness has already been settled */
+	if ((delta->flags & KNOWN_BINARY_FLAGS) != 0)
+		return true;
+
+	/* make sure files are conceivably mmap-able */
+	if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
+		(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
+	{
+		delta->old_file.flags |= GIT_DIFF_FLAG_BINARY;
+		delta->new_file.flags |= GIT_DIFF_FLAG_BINARY;
+		delta->flags |= GIT_DIFF_FLAG_BINARY;
+		return true;
+	}
+
+	/* check if user is forcing us to text diff these files */
+	if (ctxt->opts && (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) != 0) {
+		delta->old_file.flags |= GIT_DIFF_FLAG_NOT_BINARY;
+		delta->new_file.flags |= GIT_DIFF_FLAG_NOT_BINARY;
+		delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+		return true;
+	}
+
+	return false;
+}
+
+static int diff_delta_is_binary_by_attr(
+	diff_context *ctxt, git_diff_patch *patch)
+{
+	int error = 0, mirror_new;
+	git_diff_delta *delta = patch->delta;
+
+	if (diff_delta_is_binary_forced(ctxt, delta))
+		return 0;
+
+	/* check diff attribute +, -, or 0 */
+	if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0)
+		return -1;
+
+	mirror_new = (delta->new_file.path == delta->old_file.path ||
+		ctxt->diff->strcomp(delta->new_file.path, delta->old_file.path) == 0);
+	if (mirror_new)
+		delta->new_file.flags |= (delta->old_file.flags & KNOWN_BINARY_FLAGS);
+	else
+		error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file);
+
+	update_delta_is_binary(delta);
+
+	return error;
+}
+
+static int diff_delta_is_binary_by_content(
+	diff_context *ctxt,
+	git_diff_delta *delta,
+	git_diff_file *file,
+	const git_map *map)
+{
+	const git_buf search = { map->data, 0, min(map->len, 4000) };
+
+	if (diff_delta_is_binary_forced(ctxt, delta))
+		return 0;
+
+	/* TODO: provide encoding / binary detection callbacks that can
+	 * be UTF-8 aware, etc.  For now, instead of trying to be smart,
+	 * let's just use the simple NUL-byte detection that core git uses.
+	 */
+
+	/* previously was: if (git_buf_text_is_binary(&search)) */
+	if (git_buf_text_contains_nul(&search))
+		file->flags |= GIT_DIFF_FLAG_BINARY;
+	else
+		file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+
+	update_delta_is_binary(delta);
+
+	return 0;
+}
+
+static int diff_delta_is_binary_by_size(
+	diff_context *ctxt, git_diff_delta *delta, git_diff_file *file)
+{
+	git_off_t threshold = MAX_DIFF_FILESIZE;
+
+	if ((file->flags & KNOWN_BINARY_FLAGS) != 0)
+		return 0;
+
+	if (ctxt && ctxt->opts) {
+		if (ctxt->opts->max_size < 0)
+			return 0;
+
+		if (ctxt->opts->max_size > 0)
+			threshold = ctxt->opts->max_size;
+	}
+
+	if (file->size > threshold)
+		file->flags |= GIT_DIFF_FLAG_BINARY;
+
+	update_delta_is_binary(delta);
+
+	return 0;
+}
+
+static void setup_xdiff_options(
+	const git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
+{
+	memset(cfg, 0, sizeof(xdemitconf_t));
+	memset(param, 0, sizeof(xpparam_t));
+
+	cfg->ctxlen =
+		(!opts) ? 3 : opts->context_lines;
+	cfg->interhunkctxlen =
+		(!opts) ? 0 : opts->interhunk_lines;
+
+	if (!opts)
+		return;
+
+	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
+		param->flags |= XDF_WHITESPACE_FLAGS;
+	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
+		param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+	if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
+		param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+}
+
+
+static int get_blob_content(
+	diff_context *ctxt,
+	git_diff_delta *delta,
+	git_diff_file *file,
+	git_map *map,
+	git_blob **blob)
+{
+	int error;
+	git_odb_object *odb_obj = NULL;
+
+	if (git_oid_iszero(&file->oid))
+		return 0;
+
+	if (file->mode == GIT_FILEMODE_COMMIT)
+	{
+		char oidstr[GIT_OID_HEXSZ+1];
+		git_buf content = GIT_BUF_INIT;
+
+		git_oid_fmt(oidstr, &file->oid);
+		oidstr[GIT_OID_HEXSZ] = 0;
+		git_buf_printf(&content, "Subproject commit %s\n", oidstr );
+
+		map->data = git_buf_detach(&content);
+		map->len = strlen(map->data);
+
+		file->flags |= GIT_DIFF_FLAG__FREE_DATA;
+		return 0;
+	}
+
+	if (!file->size) {
+		git_odb *odb;
+		size_t len;
+		git_otype type;
+
+		/* peek at object header to avoid loading if too large */
+		if ((error = git_repository_odb__weakptr(&odb, ctxt->repo)) < 0 ||
+			(error = git_odb__read_header_or_object(
+				&odb_obj, &len, &type, odb, &file->oid)) < 0)
+			return error;
+
+		assert(type == GIT_OBJ_BLOB);
+
+		file->size = len;
+	}
+
+	/* if blob is too large to diff, mark as binary */
+	if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0)
+		return error;
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+		return 0;
+
+	if (odb_obj != NULL) {
+		error = git_object__from_odb_object(
+			(git_object **)blob, ctxt->repo, odb_obj, GIT_OBJ_BLOB);
+		git_odb_object_free(odb_obj);
+	} else
+		error = git_blob_lookup(blob, ctxt->repo, &file->oid);
+
+	if (error)
+		return error;
+
+	map->data = (void *)git_blob_rawcontent(*blob);
+	map->len  = (size_t)git_blob_rawsize(*blob);
+
+	return diff_delta_is_binary_by_content(ctxt, delta, file, map);
+}
+
+static int get_workdir_sm_content(
+	diff_context *ctxt,
+	git_diff_file *file,
+	git_map *map)
+{
+	int error = 0;
+	git_buf content = GIT_BUF_INIT;
+	git_submodule* sm = NULL;
+	unsigned int sm_status = 0;
+	const char* sm_status_text = "";
+	char oidstr[GIT_OID_HEXSZ+1];
+
+	if ((error = git_submodule_lookup(&sm, ctxt->repo, file->path)) < 0 ||
+		(error = git_submodule_status(&sm_status, sm)) < 0)
+	{
+		/* GIT_EEXISTS means a "submodule" that has not been git added */
+		if (error == GIT_EEXISTS)
+			error = 0;
+		return error;
+	}
+
+	/* update OID if we didn't have it previously */
+	if ((file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
+		const git_oid* sm_head;
+
+		if ((sm_head = git_submodule_wd_id(sm)) != NULL ||
+			(sm_head = git_submodule_head_id(sm)) != NULL)
+		{
+			git_oid_cpy(&file->oid, sm_head);
+			file->flags |= GIT_DIFF_FLAG_VALID_OID;
+		}
+	}
+
+	git_oid_fmt(oidstr, &file->oid);
+	oidstr[GIT_OID_HEXSZ] = '\0';
+
+	if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
+		sm_status_text = "-dirty";
+
+	git_buf_printf(&content, "Subproject commit %s%s\n",
+				   oidstr, sm_status_text);
+
+	map->data = git_buf_detach(&content);
+	map->len = strlen(map->data);
+
+	file->flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+	return 0;
+}
+
+static int get_filtered(
+	git_map *map, git_file fd, git_diff_file *file, git_vector *filters)
+{
+	int error;
+	git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
+
+	if ((error = git_futils_readbuffer_fd(&raw, fd, (size_t)file->size)) < 0)
+		return error;
+
+	if (!filters->length)
+		git_buf_swap(&filtered, &raw);
+	else
+		error = git_filters_apply(&filtered, &raw, filters);
+
+	if (!error) {
+		map->len  = git_buf_len(&filtered);
+		map->data = git_buf_detach(&filtered);
+
+		file->flags |= GIT_DIFF_FLAG__FREE_DATA;
+	}
+
+	git_buf_free(&raw);
+	git_buf_free(&filtered);
+
+	return error;
+}
+
+static int get_workdir_content(
+	diff_context *ctxt,
+	git_diff_delta *delta,
+	git_diff_file *file,
+	git_map *map)
+{
+	int error = 0;
+	git_buf path = GIT_BUF_INIT;
+	const char *wd = git_repository_workdir(ctxt->repo);
+
+	if (S_ISGITLINK(file->mode))
+		return get_workdir_sm_content(ctxt, file, map);
+
+	if (S_ISDIR(file->mode))
+		return 0;
+
+	if (git_buf_joinpath(&path, wd, file->path) < 0)
+		return -1;
+
+	if (S_ISLNK(file->mode)) {
+		ssize_t alloc_len, read_len;
+
+		file->flags |= GIT_DIFF_FLAG__FREE_DATA;
+		file->flags |= GIT_DIFF_FLAG_BINARY;
+
+		/* link path on disk could be UTF-16, so prepare a buffer that is
+		 * big enough to handle some UTF-8 data expansion
+		 */
+		alloc_len = (ssize_t)(file->size * 2) + 1;
+
+		map->data = git__malloc(alloc_len);
+		GITERR_CHECK_ALLOC(map->data);
+
+		read_len = p_readlink(path.ptr, map->data, alloc_len);
+		if (read_len < 0) {
+			giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
+			error = -1;
+			goto cleanup;
+		}
+
+		map->len = read_len;
+	}
+	else {
+		git_file fd = git_futils_open_ro(path.ptr);
+		git_vector filters = GIT_VECTOR_INIT;
+
+		if (fd < 0) {
+			error = fd;
+			goto cleanup;
+		}
+
+		if (!file->size && !(file->size = git_futils_filesize(fd)))
+			goto close_and_cleanup;
+
+		if ((error = diff_delta_is_binary_by_size(ctxt, delta, file)) < 0 ||
+			(delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+			goto close_and_cleanup;
+
+		error = git_filters_load(
+			&filters, ctxt->repo, file->path, GIT_FILTER_TO_ODB);
+		if (error < 0)
+			goto close_and_cleanup;
+
+		if (error == 0) { /* note: git_filters_load returns filter count */
+			error = git_futils_mmap_ro(map, fd, 0, (size_t)file->size);
+			if (!error)
+				file->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
+		}
+		if (error != 0)
+			error = get_filtered(map, fd, file, &filters);
+
+close_and_cleanup:
+		git_filters_free(&filters);
+		p_close(fd);
+	}
+
+	/* once data is loaded, update OID if we didn't have it previously */
+	if (!error && (file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
+		error = git_odb_hash(
+			&file->oid, map->data, map->len, GIT_OBJ_BLOB);
+		if (!error)
+			file->flags |= GIT_DIFF_FLAG_VALID_OID;
+	}
+
+	if (!error)
+		error = diff_delta_is_binary_by_content(ctxt, delta, file, map);
+
+cleanup:
+	git_buf_free(&path);
+	return error;
+}
+
+static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
+{
+	if (blob != NULL)
+		git_blob_free(blob);
+
+	if (file->flags & GIT_DIFF_FLAG__FREE_DATA) {
+		git__free(map->data);
+		map->data = "";
+		map->len  = 0;
+		file->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
+	}
+	else if (file->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
+		git_futils_mmap_free(map);
+		map->data = "";
+		map->len  = 0;
+		file->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
+	}
+}
+
+
+static int diff_context_init(
+	diff_context *ctxt,
+	git_diff_list *diff,
+	git_repository *repo,
+	const git_diff_options *opts,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload)
+{
+	memset(ctxt, 0, sizeof(diff_context));
+
+	if (!repo && diff)
+		repo = diff->repo;
+
+	if (!opts && diff)
+		opts = &diff->opts;
+
+	ctxt->repo = repo;
+	ctxt->diff = diff;
+	ctxt->opts = opts;
+	ctxt->file_cb = file_cb;
+	ctxt->hunk_cb = hunk_cb;
+	ctxt->data_cb = data_cb;
+	ctxt->payload = payload;
+	ctxt->error = 0;
+
+	setup_xdiff_options(ctxt->opts, &ctxt->xdiff_config, &ctxt->xdiff_params);
+
+	return 0;
+}
+
+static int diff_delta_file_callback(
+	diff_context *ctxt, git_diff_delta *delta, size_t idx)
+{
+	float progress;
+
+	if (!ctxt->file_cb)
+		return 0;
+
+	progress = ctxt->diff ? ((float)idx / ctxt->diff->deltas.length) : 1.0f;
+
+	if (ctxt->file_cb(delta, progress, ctxt->payload) != 0)
+		ctxt->error = GIT_EUSER;
+
+	return ctxt->error;
+}
+
+static void diff_patch_init(
+	diff_context *ctxt, git_diff_patch *patch)
+{
+	memset(patch, 0, sizeof(git_diff_patch));
+
+	patch->diff = ctxt->diff;
+	patch->ctxt = ctxt;
+
+	if (patch->diff) {
+		patch->old_src = patch->diff->old_src;
+		patch->new_src = patch->diff->new_src;
+	} else {
+		patch->old_src = patch->new_src = GIT_ITERATOR_TYPE_TREE;
+	}
+}
+
+static git_diff_patch *diff_patch_alloc(
+	diff_context *ctxt, git_diff_delta *delta)
+{
+	git_diff_patch *patch = git__malloc(sizeof(git_diff_patch));
+	if (!patch)
+		return NULL;
+
+	diff_patch_init(ctxt, patch);
+
+	git_diff_list_addref(patch->diff);
+
+	GIT_REFCOUNT_INC(patch);
+
+	patch->delta = delta;
+	patch->flags = GIT_DIFF_PATCH_ALLOCATED;
+
+	return patch;
+}
+
+static int diff_patch_load(
+	diff_context *ctxt, git_diff_patch *patch)
+{
+	int error = 0;
+	git_diff_delta *delta = patch->delta;
+	bool check_if_unmodified = false;
+
+	if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
+		return 0;
+
+	error = diff_delta_is_binary_by_attr(ctxt, patch);
+
+	patch->old_data.data = "";
+	patch->old_data.len  = 0;
+	patch->old_blob      = NULL;
+
+	patch->new_data.data = "";
+	patch->new_data.len  = 0;
+	patch->new_blob      = NULL;
+
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+		goto cleanup;
+
+	if (!ctxt->hunk_cb &&
+		!ctxt->data_cb &&
+		(ctxt->opts->flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0)
+		goto cleanup;
+
+	switch (delta->status) {
+	case GIT_DELTA_ADDED:
+		delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		break;
+	case GIT_DELTA_DELETED:
+		delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		break;
+	case GIT_DELTA_MODIFIED:
+		break;
+	case GIT_DELTA_UNTRACKED:
+		delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)
+			delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		break;
+	default:
+		delta->new_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		delta->old_file.flags |= GIT_DIFF_FLAG__NO_DATA;
+		break;
+	}
+
+#define CHECK_UNMODIFIED (GIT_DIFF_FLAG__NO_DATA | GIT_DIFF_FLAG_VALID_OID)
+
+	check_if_unmodified =
+		(delta->old_file.flags & CHECK_UNMODIFIED) == 0 &&
+		(delta->new_file.flags & CHECK_UNMODIFIED) == 0;
+
+	/* Always try to load workdir content first, since it may need to be
+	 * filtered (and hence use 2x memory) and we want to minimize the max
+	 * memory footprint during diff.
+	 */
+
+	if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
+		patch->old_src == GIT_ITERATOR_TYPE_WORKDIR) {
+		if ((error = get_workdir_content(
+				ctxt, delta, &delta->old_file, &patch->old_data)) < 0)
+			goto cleanup;
+		if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+			goto cleanup;
+	}
+
+	if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
+		patch->new_src == GIT_ITERATOR_TYPE_WORKDIR) {
+		if ((error = get_workdir_content(
+				ctxt, delta, &delta->new_file, &patch->new_data)) < 0)
+			goto cleanup;
+		if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+			goto cleanup;
+	}
+
+	if ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
+		patch->old_src != GIT_ITERATOR_TYPE_WORKDIR) {
+		if ((error = get_blob_content(
+				ctxt, delta, &delta->old_file,
+				&patch->old_data, &patch->old_blob)) < 0)
+			goto cleanup;
+		if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+			goto cleanup;
+	}
+
+	if ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0 &&
+		patch->new_src != GIT_ITERATOR_TYPE_WORKDIR) {
+		if ((error = get_blob_content(
+				ctxt, delta, &delta->new_file,
+				&patch->new_data, &patch->new_blob)) < 0)
+			goto cleanup;
+		if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+			goto cleanup;
+	}
+
+	/* if we did not previously have the definitive oid, we may have
+	 * incorrect status and need to switch this to UNMODIFIED.
+	 */
+	if (check_if_unmodified &&
+		delta->old_file.mode == delta->new_file.mode &&
+		!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
+	{
+		delta->status = GIT_DELTA_UNMODIFIED;
+
+		if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+			goto cleanup;
+	}
+
+cleanup:
+	if ((delta->flags & KNOWN_BINARY_FLAGS) == 0)
+		update_delta_is_binary(delta);
+
+	if (!error) {
+		patch->flags |= GIT_DIFF_PATCH_LOADED;
+
+		/* patch is diffable only for non-binary, modified files where at
+		 * least one side has data and there is actual change in the data
+		 */
+		if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
+			delta->status != GIT_DELTA_UNMODIFIED &&
+			(patch->old_data.len || patch->new_data.len) &&
+			(patch->old_data.len != patch->new_data.len ||
+			 !git_oid_equal(&delta->old_file.oid, &delta->new_file.oid)))
+			patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
+	}
+
+	return error;
+}
+
+static int diff_patch_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+	git_diff_patch *patch = priv;
+	diff_context   *ctxt  = patch->ctxt;
+
+	if (len == 1) {
+		ctxt->error = parse_hunk_header(&ctxt->range, bufs[0].ptr);
+		if (ctxt->error < 0)
+			return ctxt->error;
+
+		if (ctxt->hunk_cb != NULL &&
+			ctxt->hunk_cb(patch->delta, &ctxt->range,
+				bufs[0].ptr, bufs[0].size, ctxt->payload))
+			ctxt->error = GIT_EUSER;
+	}
+
+	if (len == 2 || len == 3) {
+		/* expect " "/"-"/"+", then data */
+		char origin =
+			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+			GIT_DIFF_LINE_CONTEXT;
+
+		if (ctxt->data_cb != NULL &&
+			ctxt->data_cb(patch->delta, &ctxt->range,
+				origin, bufs[1].ptr, bufs[1].size, ctxt->payload))
+			ctxt->error = GIT_EUSER;
+	}
+
+	if (len == 3 && !ctxt->error) {
+		/* If we have a '+' and a third buf, then we have added a line
+		 * without a newline and the old code had one, so DEL_EOFNL.
+		 * If we have a '-' and a third buf, then we have removed a line
+		 * with out a newline but added a blank line, so ADD_EOFNL.
+		 */
+		char origin =
+			(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
+			(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
+			GIT_DIFF_LINE_CONTEXT;
+
+		if (ctxt->data_cb != NULL &&
+			ctxt->data_cb(patch->delta, &ctxt->range,
+				origin, bufs[2].ptr, bufs[2].size, ctxt->payload))
+			ctxt->error = GIT_EUSER;
+	}
+
+	return ctxt->error;
+}
+
+static int diff_patch_generate(
+	diff_context *ctxt, git_diff_patch *patch)
+{
+	int error = 0;
+	xdemitcb_t xdiff_callback;
+	mmfile_t old_xdiff_data, new_xdiff_data;
+
+	if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
+		return 0;
+
+	if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0)
+		if ((error = diff_patch_load(ctxt, patch)) < 0)
+			return error;
+
+	if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
+		return 0;
+
+	if (!ctxt->file_cb && !ctxt->hunk_cb)
+		return 0;
+
+	patch->ctxt = ctxt;
+
+	memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+	xdiff_callback.outf = diff_patch_cb;
+	xdiff_callback.priv = patch;
+
+	old_xdiff_data.ptr  = patch->old_data.data;
+	old_xdiff_data.size = patch->old_data.len;
+	new_xdiff_data.ptr  = patch->new_data.data;
+	new_xdiff_data.size = patch->new_data.len;
+
+	xdl_diff(&old_xdiff_data, &new_xdiff_data,
+		&ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback);
+
+	error = ctxt->error;
+
+	if (!error)
+		patch->flags |= GIT_DIFF_PATCH_DIFFED;
+
+	return error;
+}
+
+static void diff_patch_unload(git_diff_patch *patch)
+{
+	if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0) {
+		patch->flags = (patch->flags & ~GIT_DIFF_PATCH_DIFFED);
+
+		patch->hunks_size = 0;
+		patch->lines_size = 0;
+	}
+
+	if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0) {
+		patch->flags = (patch->flags & ~GIT_DIFF_PATCH_LOADED);
+
+		release_content(
+			&patch->delta->old_file, &patch->old_data, patch->old_blob);
+		release_content(
+			&patch->delta->new_file, &patch->new_data, patch->new_blob);
+	}
+}
+
+static void diff_patch_free(git_diff_patch *patch)
+{
+	diff_patch_unload(patch);
+
+	git__free(patch->lines);
+	patch->lines = NULL;
+	patch->lines_asize = 0;
+
+	git__free(patch->hunks);
+	patch->hunks = NULL;
+	patch->hunks_asize = 0;
+
+	if (!(patch->flags & GIT_DIFF_PATCH_ALLOCATED))
+		return;
+
+	patch->flags = 0;
+
+	git_diff_list_free(patch->diff); /* decrements refcount */
+
+	git__free(patch);
+}
+
+#define MAX_HUNK_STEP 128
+#define MIN_HUNK_STEP 8
+#define MAX_LINE_STEP 256
+#define MIN_LINE_STEP 8
+
+static int diff_patch_hunk_cb(
+	const git_diff_delta *delta,
+	const git_diff_range *range,
+	const char *header,
+	size_t header_len,
+	void *payload)
+{
+	git_diff_patch *patch = payload;
+	diff_patch_hunk *hunk;
+
+	GIT_UNUSED(delta);
+
+	if (patch->hunks_size >= patch->hunks_asize) {
+		size_t new_size;
+		diff_patch_hunk *new_hunks;
+
+		if (patch->hunks_asize > MAX_HUNK_STEP)
+			new_size = patch->hunks_asize + MAX_HUNK_STEP;
+		else
+			new_size = patch->hunks_asize * 2;
+		if (new_size < MIN_HUNK_STEP)
+			new_size = MIN_HUNK_STEP;
+
+		new_hunks = git__realloc(
+			patch->hunks, new_size * sizeof(diff_patch_hunk));
+		if (!new_hunks)
+			return -1;
+
+		patch->hunks = new_hunks;
+		patch->hunks_asize = new_size;
+	}
+
+	hunk = &patch->hunks[patch->hunks_size++];
+
+	memcpy(&hunk->range, range, sizeof(hunk->range));
+
+	assert(header_len + 1 < sizeof(hunk->header));
+	memcpy(&hunk->header, header, header_len);
+	hunk->header[header_len] = '\0';
+	hunk->header_len = header_len;
+
+	hunk->line_start = patch->lines_size;
+	hunk->line_count = 0;
+
+	patch->oldno = range->old_start;
+	patch->newno = range->new_start;
+
+	return 0;
+}
+
+static int diff_patch_line_cb(
+	const git_diff_delta *delta,
+	const git_diff_range *range,
+	char line_origin,
+	const char *content,
+	size_t content_len,
+	void *payload)
+{
+	git_diff_patch *patch = payload;
+	diff_patch_hunk *hunk;
+	diff_patch_line *line;
+
+	GIT_UNUSED(delta);
+	GIT_UNUSED(range);
+
+	assert(patch->hunks_size > 0);
+	assert(patch->hunks != NULL);
+
+	hunk = &patch->hunks[patch->hunks_size - 1];
+
+	if (patch->lines_size >= patch->lines_asize) {
+		size_t new_size;
+		diff_patch_line *new_lines;
+
+		if (patch->lines_asize > MAX_LINE_STEP)
+			new_size = patch->lines_asize + MAX_LINE_STEP;
+		else
+			new_size = patch->lines_asize * 2;
+		if (new_size < MIN_LINE_STEP)
+			new_size = MIN_LINE_STEP;
+
+		new_lines = git__realloc(
+			patch->lines, new_size * sizeof(diff_patch_line));
+		if (!new_lines)
+			return -1;
+
+		patch->lines = new_lines;
+		patch->lines_asize = new_size;
+	}
+
+	line = &patch->lines[patch->lines_size++];
+
+	line->ptr = content;
+	line->len = content_len;
+	line->origin = line_origin;
+
+	/* do some bookkeeping so we can provide old/new line numbers */
+
+	for (line->lines = 0; content_len > 0; --content_len) {
+		if (*content++ == '\n')
+			++line->lines;
+	}
+
+	switch (line_origin) {
+	case GIT_DIFF_LINE_ADDITION:
+		line->oldno = -1;
+		line->newno = patch->newno;
+		patch->newno += line->lines;
+		break;
+	case GIT_DIFF_LINE_DELETION:
+		line->oldno = patch->oldno;
+		line->newno = -1;
+		patch->oldno += line->lines;
+		break;
+	default:
+		line->oldno = patch->oldno;
+		line->newno = patch->newno;
+		patch->oldno += line->lines;
+		patch->newno += line->lines;
+		break;
+	}
+
+	hunk->line_count++;
+
+	return 0;
+}
+
+static int diff_required(git_diff_list *diff, const char *action)
+{
+	if (!diff) {
+		giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_diff_foreach(
+	git_diff_list *diff,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload)
+{
+	int error = 0;
+	diff_context ctxt;
+	size_t idx;
+	git_diff_patch patch;
+
+	if (diff_required(diff, "git_diff_foreach") < 0)
+		return -1;
+
+	if (diff_context_init(
+			&ctxt, diff, NULL, NULL, file_cb, hunk_cb, data_cb, payload) < 0)
+		return -1;
+
+	diff_patch_init(&ctxt, &patch);
+
+	git_vector_foreach(&diff->deltas, idx, patch.delta) {
+
+		/* check flags against patch status */
+		if (git_diff_delta__should_skip(ctxt.opts, patch.delta))
+			continue;
+
+		if (!(error = diff_patch_load(&ctxt, &patch))) {
+
+			/* invoke file callback */
+			error = diff_delta_file_callback(&ctxt, patch.delta, idx);
+
+			/* generate diffs and invoke hunk and line callbacks */
+			if (!error)
+				error = diff_patch_generate(&ctxt, &patch);
+
+			diff_patch_unload(&patch);
+		}
+
+		if (error < 0)
+			break;
+	}
+
+	if (error == GIT_EUSER)
+		giterr_clear(); /* don't let error message leak */
+
+	return error;
+}
+
+
+typedef struct {
+	git_diff_list *diff;
+	git_diff_data_cb print_cb;
+	void *payload;
+	git_buf *buf;
+} diff_print_info;
+
+static char pick_suffix(int mode)
+{
+	if (S_ISDIR(mode))
+		return '/';
+	else if (mode & 0100) //-V536
+		/* in git, modes are very regular, so we must have 0100755 mode */
+		return '*';
+	else
+		return ' ';
+}
+
+char git_diff_status_char(git_delta_t status)
+{
+	char code;
+
+	switch (status) {
+	case GIT_DELTA_ADDED:     code = 'A'; break;
+	case GIT_DELTA_DELETED:   code = 'D'; break;
+	case GIT_DELTA_MODIFIED:  code = 'M'; break;
+	case GIT_DELTA_RENAMED:   code = 'R'; break;
+	case GIT_DELTA_COPIED:    code = 'C'; break;
+	case GIT_DELTA_IGNORED:   code = 'I'; break;
+	case GIT_DELTA_UNTRACKED: code = '?'; break;
+	default:                  code = ' '; break;
+	}
+
+	return code;
+}
+
+static int print_compact(
+	const git_diff_delta *delta, float progress, void *data)
+{
+	diff_print_info *pi = data;
+	char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
+
+	GIT_UNUSED(progress);
+
+	if (code == ' ')
+		return 0;
+
+	old_suffix = pick_suffix(delta->old_file.mode);
+	new_suffix = pick_suffix(delta->new_file.mode);
+
+	git_buf_clear(pi->buf);
+
+	if (delta->old_file.path != delta->new_file.path &&
+		pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
+		git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
+			delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+	else if (delta->old_file.mode != delta->new_file.mode &&
+		delta->old_file.mode != 0 && delta->new_file.mode != 0)
+		git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
+			delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+	else if (old_suffix != ' ')
+		git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
+	else
+		git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
+
+	if (git_buf_oom(pi->buf))
+		return -1;
+
+	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
+			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	{
+		giterr_clear();
+		return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+int git_diff_print_compact(
+	git_diff_list *diff,
+	git_diff_data_cb print_cb,
+	void *payload)
+{
+	int error;
+	git_buf buf = GIT_BUF_INIT;
+	diff_print_info pi;
+
+	pi.diff     = diff;
+	pi.print_cb = print_cb;
+	pi.payload  = payload;
+	pi.buf      = &buf;
+
+	error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
+
+	git_buf_free(&buf);
+
+	return error;
+}
+
+static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
+{
+	char start_oid[8], end_oid[8];
+
+	/* TODO: Determine a good actual OID range to print */
+	git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);
+	git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);
+
+	/* TODO: Match git diff more closely */
+	if (delta->old_file.mode == delta->new_file.mode) {
+		git_buf_printf(pi->buf, "index %s..%s %o\n",
+			start_oid, end_oid, delta->old_file.mode);
+	} else {
+		if (delta->old_file.mode == 0) {
+			git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
+		} else if (delta->new_file.mode == 0) {
+			git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
+		} else {
+			git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
+			git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
+		}
+		git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
+	}
+
+	if (git_buf_oom(pi->buf))
+		return -1;
+
+	return 0;
+}
+
+static int print_patch_file(
+	const git_diff_delta *delta, float progress, void *data)
+{
+	diff_print_info *pi = data;
+	const char *oldpfx = pi->diff->opts.old_prefix;
+	const char *oldpath = delta->old_file.path;
+	const char *newpfx = pi->diff->opts.new_prefix;
+	const char *newpath = delta->new_file.path;
+
+	GIT_UNUSED(progress);
+
+	if (S_ISDIR(delta->new_file.mode) ||
+		delta->status == GIT_DELTA_UNMODIFIED ||
+		delta->status == GIT_DELTA_IGNORED ||
+		(delta->status == GIT_DELTA_UNTRACKED &&
+		 (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
+		return 0;
+
+	if (!oldpfx)
+		oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+
+	if (!newpfx)
+		newpfx = DIFF_NEW_PREFIX_DEFAULT;
+
+	git_buf_clear(pi->buf);
+	git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+
+	if (print_oid_range(pi, delta) < 0)
+		return -1;
+
+	if (git_oid_iszero(&delta->old_file.oid)) {
+		oldpfx = "";
+		oldpath = "/dev/null";
+	}
+	if (git_oid_iszero(&delta->new_file.oid)) {
+		newpfx = "";
+		newpath = "/dev/null";
+	}
+
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
+		git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
+		git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
+	}
+
+	if (git_buf_oom(pi->buf))
+		return -1;
+
+	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
+			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	{
+		giterr_clear();
+		return GIT_EUSER;
+	}
+
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+		return 0;
+
+	git_buf_clear(pi->buf);
+	git_buf_printf(
+		pi->buf, "Binary files %s%s and %s%s differ\n",
+		oldpfx, oldpath, newpfx, newpath);
+	if (git_buf_oom(pi->buf))
+		return -1;
+
+	if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
+			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	{
+		giterr_clear();
+		return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+static int print_patch_hunk(
+	const git_diff_delta *d,
+	const git_diff_range *r,
+	const char *header,
+	size_t header_len,
+	void *data)
+{
+	diff_print_info *pi = data;
+
+	if (S_ISDIR(d->new_file.mode))
+		return 0;
+
+	git_buf_clear(pi->buf);
+	if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
+		return -1;
+
+	if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
+			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	{
+		giterr_clear();
+		return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+static int print_patch_line(
+	const git_diff_delta *delta,
+	const git_diff_range *range,
+	char line_origin, /* GIT_DIFF_LINE value from above */
+	const char *content,
+	size_t content_len,
+	void *data)
+{
+	diff_print_info *pi = data;
+
+	if (S_ISDIR(delta->new_file.mode))
+		return 0;
+
+	git_buf_clear(pi->buf);
+
+	if (line_origin == GIT_DIFF_LINE_ADDITION ||
+		line_origin == GIT_DIFF_LINE_DELETION ||
+		line_origin == GIT_DIFF_LINE_CONTEXT)
+		git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
+	else if (content_len > 0)
+		git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
+
+	if (git_buf_oom(pi->buf))
+		return -1;
+
+	if (pi->print_cb(delta, range, line_origin,
+			git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+	{
+		giterr_clear();
+		return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+int git_diff_print_patch(
+	git_diff_list *diff,
+	git_diff_data_cb print_cb,
+	void *payload)
+{
+	int error;
+	git_buf buf = GIT_BUF_INIT;
+	diff_print_info pi;
+
+	pi.diff     = diff;
+	pi.print_cb = print_cb;
+	pi.payload  = payload;
+	pi.buf      = &buf;
+
+	error = git_diff_foreach(
+		diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
+
+	git_buf_free(&buf);
+
+	return error;
+}
+
+static void set_data_from_blob(
+	const git_blob *blob, git_map *map, git_diff_file *file)
+{
+	if (blob) {
+		file->size = git_blob_rawsize(blob);
+		git_oid_cpy(&file->oid, git_object_id((const git_object *)blob));
+		file->mode = 0644;
+
+		map->len   = (size_t)file->size;
+		map->data  = (char *)git_blob_rawcontent(blob);
+	} else {
+		file->size = 0;
+		file->flags |= GIT_DIFF_FLAG__NO_DATA;
+
+		map->len   = 0;
+		map->data  = "";
+	}
+}
+
+static void set_data_from_buffer(
+	const char *buffer, size_t buffer_len, git_map *map, git_diff_file *file)
+{
+	file->size = (git_off_t)buffer_len;
+	file->mode = 0644;
+	map->len   = buffer_len;
+
+	if (!buffer) {
+		file->flags |= GIT_DIFF_FLAG__NO_DATA;
+		map->data = NULL;
+	} else {
+		map->data = (char *)buffer;
+		git_odb_hash(&file->oid, buffer, buffer_len, GIT_OBJ_BLOB);
+	}
+}
+
+typedef struct {
+	diff_context   ctxt;
+	git_diff_delta delta;
+	git_diff_patch patch;
+} diff_single_data;
+
+static int diff_single_init(
+	diff_single_data *data,
+	git_repository *repo,
+	const git_diff_options *opts,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload)
+{
+	GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+
+	memset(data, 0, sizeof(*data));
+
+	if (diff_context_init(
+			&data->ctxt, NULL, repo, opts,
+			file_cb, hunk_cb, data_cb, payload) < 0)
+		return -1;
+
+	diff_patch_init(&data->ctxt, &data->patch);
+
+	return 0;
+}
+
+static int diff_single_apply(diff_single_data *data)
+{
+	int error;
+	git_diff_delta *delta = &data->delta;
+	bool has_old = ((delta->old_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+	bool has_new = ((delta->new_file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+
+	/* finish setting up fake git_diff_delta record and loaded data */
+
+	data->patch.delta = delta;
+	delta->flags = delta->flags & ~KNOWN_BINARY_FLAGS;
+
+	delta->status = has_new ?
+		(has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
+		(has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
+
+	if (git_oid_cmp(&delta->new_file.oid, &delta->old_file.oid) == 0)
+		delta->status = GIT_DELTA_UNMODIFIED;
+
+	if ((error = diff_delta_is_binary_by_content(
+			&data->ctxt, delta, &delta->old_file, &data->patch.old_data)) < 0 ||
+		(error = diff_delta_is_binary_by_content(
+			&data->ctxt, delta, &delta->new_file, &data->patch.new_data)) < 0)
+		goto cleanup;
+
+	data->patch.flags |= GIT_DIFF_PATCH_LOADED;
+
+	if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
+		delta->status != GIT_DELTA_UNMODIFIED)
+		data->patch.flags |= GIT_DIFF_PATCH_DIFFABLE;
+
+	/* do diffs */
+
+	if (!(error = diff_delta_file_callback(&data->ctxt, delta, 1)))
+		error = diff_patch_generate(&data->ctxt, &data->patch);
+
+cleanup:
+	if (error == GIT_EUSER)
+		giterr_clear();
+
+	diff_patch_unload(&data->patch);
+
+	return error;
+}
+
+int git_diff_blobs(
+	const git_blob *old_blob,
+	const git_blob *new_blob,
+	const git_diff_options *options,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload)
+{
+	int error;
+	diff_single_data d;
+	git_repository *repo =
+		new_blob ? git_object_owner((const git_object *)new_blob) :
+		old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
+
+	if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */
+		return 0;
+
+	if ((error = diff_single_init(
+			&d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0)
+		return error;
+
+	if (options && (options->flags & GIT_DIFF_REVERSE) != 0) {
+		const git_blob *swap = old_blob;
+		old_blob = new_blob;
+		new_blob = swap;
+	}
+
+	set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file);
+	set_data_from_blob(new_blob, &d.patch.new_data, &d.delta.new_file);
+
+	return diff_single_apply(&d);
+}
+
+int git_diff_blob_to_buffer(
+	const git_blob *old_blob,
+	const char *buf,
+	size_t buflen,
+	const git_diff_options *options,
+	git_diff_file_cb file_cb,
+	git_diff_hunk_cb hunk_cb,
+	git_diff_data_cb data_cb,
+	void *payload)
+{
+	int error;
+	diff_single_data d;
+	git_repository *repo =
+		old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
+
+	if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */
+		return 0;
+
+	if ((error = diff_single_init(
+			&d, repo, options, file_cb, hunk_cb, data_cb, payload)) < 0)
+		return error;
+
+	if (options && (options->flags & GIT_DIFF_REVERSE) != 0) {
+		set_data_from_buffer(buf, buflen, &d.patch.old_data, &d.delta.old_file);
+		set_data_from_blob(old_blob, &d.patch.new_data, &d.delta.new_file);
+	} else {
+		set_data_from_blob(old_blob, &d.patch.old_data, &d.delta.old_file);
+		set_data_from_buffer(buf, buflen, &d.patch.new_data, &d.delta.new_file);
+	}
+
+	return diff_single_apply(&d);
+}
+
+size_t git_diff_num_deltas(git_diff_list *diff)
+{
+	assert(diff);
+	return (size_t)diff->deltas.length;
+}
+
+size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
+{
+	size_t i, count = 0;
+	git_diff_delta *delta;
+
+	assert(diff);
+
+	git_vector_foreach(&diff->deltas, i, delta) {
+		count += (delta->status == type);
+	}
+
+	return count;
+}
+
+int git_diff_get_patch(
+	git_diff_patch **patch_ptr,
+	const git_diff_delta **delta_ptr,
+	git_diff_list *diff,
+	size_t idx)
+{
+	int error;
+	diff_context ctxt;
+	git_diff_delta *delta;
+	git_diff_patch *patch;
+
+	if (patch_ptr)
+		*patch_ptr = NULL;
+	if (delta_ptr)
+		*delta_ptr = NULL;
+
+	if (diff_required(diff, "git_diff_get_patch") < 0)
+		return -1;
+
+	if (diff_context_init(
+			&ctxt, diff, NULL, NULL,
+			NULL, diff_patch_hunk_cb, diff_patch_line_cb, NULL) < 0)
+		return -1;
+
+	delta = git_vector_get(&diff->deltas, idx);
+	if (!delta) {
+		giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
+		return GIT_ENOTFOUND;
+	}
+
+	if (delta_ptr)
+		*delta_ptr = delta;
+
+	if (!patch_ptr &&
+		((delta->flags & KNOWN_BINARY_FLAGS) != 0 ||
+		 (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
+		return 0;
+
+	if (git_diff_delta__should_skip(ctxt.opts, delta))
+		return 0;
+
+	/* Don't load the patch if the user doesn't want it */
+	if (!patch_ptr)
+		return 0;
+
+	patch = diff_patch_alloc(&ctxt, delta);
+	if (!patch)
+		return -1;
+
+	if (!(error = diff_patch_load(&ctxt, patch))) {
+		ctxt.payload = patch;
+
+		error = diff_patch_generate(&ctxt, patch);
+
+		if (error == GIT_EUSER)
+			error = ctxt.error;
+	}
+
+	if (error)
+		git_diff_patch_free(patch);
+	else if (patch_ptr)
+		*patch_ptr = patch;
+
+	return error;
+}
+
+void git_diff_patch_free(git_diff_patch *patch)
+{
+	if (patch)
+		GIT_REFCOUNT_DEC(patch, diff_patch_free);
+}
+
+const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
+{
+	assert(patch);
+	return patch->delta;
+}
+
+size_t git_diff_patch_num_hunks(git_diff_patch *patch)
+{
+	assert(patch);
+	return patch->hunks_size;
+}
+
+int git_diff_patch_line_stats(
+	size_t *total_ctxt,
+	size_t *total_adds,
+	size_t *total_dels,
+	const git_diff_patch *patch)
+{
+	size_t totals[3], idx;
+
+	memset(totals, 0, sizeof(totals));
+
+	for (idx = 0; idx < patch->lines_size; ++idx) {
+		switch (patch->lines[idx].origin) {
+		case GIT_DIFF_LINE_CONTEXT:  totals[0]++; break;
+		case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
+		case GIT_DIFF_LINE_DELETION: totals[2]++; break;
+		default:
+			/* diff --stat and --numstat don't count EOFNL marks because
+			 * they will always be paired with a ADDITION or DELETION line.
+			 */
+			break;
+		}
+	}
+
+	if (total_ctxt)
+		*total_ctxt = totals[0];
+	if (total_adds)
+		*total_adds = totals[1];
+	if (total_dels)
+		*total_dels = totals[2];
+
+	return 0;
+}
+
+int git_diff_patch_get_hunk(
+	const git_diff_range **range,
+	const char **header,
+	size_t *header_len,
+	size_t *lines_in_hunk,
+	git_diff_patch *patch,
+	size_t hunk_idx)
+{
+	diff_patch_hunk *hunk;
+
+	assert(patch);
+
+	if (hunk_idx >= patch->hunks_size) {
+		if (range) *range = NULL;
+		if (header) *header = NULL;
+		if (header_len) *header_len = 0;
+		if (lines_in_hunk) *lines_in_hunk = 0;
+		return GIT_ENOTFOUND;
+	}
+
+	hunk = &patch->hunks[hunk_idx];
+
+	if (range) *range = &hunk->range;
+	if (header) *header = hunk->header;
+	if (header_len) *header_len = hunk->header_len;
+	if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
+
+	return 0;
+}
+
+int git_diff_patch_num_lines_in_hunk(
+	git_diff_patch *patch,
+	size_t hunk_idx)
+{
+	assert(patch);
+
+	if (hunk_idx >= patch->hunks_size)
+		return GIT_ENOTFOUND;
+	else
+		return (int)patch->hunks[hunk_idx].line_count;
+}
+
+int git_diff_patch_get_line_in_hunk(
+	char *line_origin,
+	const char **content,
+	size_t *content_len,
+	int *old_lineno,
+	int *new_lineno,
+	git_diff_patch *patch,
+	size_t hunk_idx,
+	size_t line_of_hunk)
+{
+	diff_patch_hunk *hunk;
+	diff_patch_line *line;
+
+	assert(patch);
+
+	if (hunk_idx >= patch->hunks_size)
+		goto notfound;
+	hunk = &patch->hunks[hunk_idx];
+
+	if (line_of_hunk >= hunk->line_count)
+		goto notfound;
+
+	line = &patch->lines[hunk->line_start + line_of_hunk];
+
+	if (line_origin) *line_origin = line->origin;
+	if (content) *content = line->ptr;
+	if (content_len) *content_len = line->len;
+	if (old_lineno) *old_lineno = (int)line->oldno;
+	if (new_lineno) *new_lineno = (int)line->newno;
+
+	return 0;
+
+notfound:
+	if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
+	if (content) *content = NULL;
+	if (content_len) *content_len = 0;
+	if (old_lineno) *old_lineno = -1;
+	if (new_lineno) *new_lineno = -1;
+
+	return GIT_ENOTFOUND;
+}
+
+static int print_to_buffer_cb(
+    const git_diff_delta *delta,
+    const git_diff_range *range,
+    char line_origin,
+    const char *content,
+    size_t content_len,
+    void *payload)
+{
+	git_buf *output = payload;
+	GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
+	return git_buf_put(output, content, content_len);
+}
+
+int git_diff_patch_print(
+	git_diff_patch *patch,
+	git_diff_data_cb print_cb,
+	void *payload)
+{
+	int error;
+	git_buf temp = GIT_BUF_INIT;
+	diff_print_info pi;
+	size_t h, l;
+
+	assert(patch && print_cb);
+
+	pi.diff     = patch->diff;
+	pi.print_cb = print_cb;
+	pi.payload  = payload;
+	pi.buf      = &temp;
+
+	error = print_patch_file(patch->delta, 0, &pi);
+
+	for (h = 0; h < patch->hunks_size && !error; ++h) {
+		diff_patch_hunk *hunk = &patch->hunks[h];
+
+		error = print_patch_hunk(
+			patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi);
+
+		for (l = 0; l < hunk->line_count && !error; ++l) {
+			diff_patch_line *line = &patch->lines[hunk->line_start + l];
+
+			error = print_patch_line(
+				patch->delta, &hunk->range,
+				line->origin, line->ptr, line->len, &pi);
+		}
+	}
+
+	git_buf_free(&temp);
+
+	return error;
+}
+
+int git_diff_patch_to_str(
+	char **string,
+	git_diff_patch *patch)
+{
+	int error;
+	git_buf output = GIT_BUF_INIT;
+
+	error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
+
+	/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
+	 * meaning a memory allocation failure, so just map to -1...
+	 */
+	if (error == GIT_EUSER)
+		error = -1;
+
+	*string = git_buf_detach(&output);
+
+	return error;
+}
+
+int git_diff__paired_foreach(
+	git_diff_list *idx2head,
+	git_diff_list *wd2idx,
+	int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
+	void *payload)
+{
+	int cmp;
+	git_diff_delta *i2h, *w2i;
+	size_t i, j, i_max, j_max;
+	int (*strcomp)(const char *, const char *);
+
+	i_max = idx2head ? idx2head->deltas.length : 0;
+	j_max = wd2idx   ? wd2idx->deltas.length   : 0;
+
+   /* Get appropriate strcmp function */
+   strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
+
+   /* Assert both iterators use matching ignore-case. If this function ever
+    * supports merging diffs that are not sorted by the same function, then
+    * it will need to spool and sort on one of the results before merging
+    */
+   if (idx2head && wd2idx) {
+       assert(idx2head->strcomp == wd2idx->strcomp);
+   }
+
+	for (i = 0, j = 0; i < i_max || j < j_max; ) {
+		i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
+		w2i = wd2idx   ? GIT_VECTOR_GET(&wd2idx->deltas,j)   : NULL;
+
+		cmp = !w2i ? -1 : !i2h ? 1 :
+			strcomp(i2h->old_file.path, w2i->old_file.path);
+
+		if (cmp < 0) {
+			if (cb(i2h, NULL, payload))
+				return GIT_EUSER;
+			i++;
+		} else if (cmp > 0) {
+			if (cb(NULL, w2i, payload))
+				return GIT_EUSER;
+			j++;
+		} else {
+			if (cb(i2h, w2i, payload))
+				return GIT_EUSER;
+			i++; j++;
+		}
+	}
 
 	return 0;
 }
diff --git a/libgit2/src/diff_output.h b/libgit2/src/diff_output.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/diff_output.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_output_h__
+#define INCLUDE_diff_output_h__
+
+#include "git2/blob.h"
+#include "diff.h"
+#include "map.h"
+#include "xdiff/xdiff.h"
+
+#define MAX_DIFF_FILESIZE 0x20000000
+
+enum {
+	GIT_DIFF_PATCH_ALLOCATED  = (1 << 0),
+	GIT_DIFF_PATCH_PREPPED    = (1 << 1),
+	GIT_DIFF_PATCH_LOADED     = (1 << 2),
+	GIT_DIFF_PATCH_DIFFABLE   = (1 << 3),
+	GIT_DIFF_PATCH_DIFFED     = (1 << 4),
+};
+
+/* context for performing diffs */
+typedef struct {
+	git_repository   *repo;
+	git_diff_list    *diff;
+	const git_diff_options *opts;
+	git_diff_file_cb  file_cb;
+	git_diff_hunk_cb  hunk_cb;
+	git_diff_data_cb  data_cb;
+	void *payload;
+	int   error;
+	git_diff_range range;
+	xdemitconf_t xdiff_config;
+	xpparam_t    xdiff_params;
+} diff_context;
+
+/* cached information about a single span in a diff */
+typedef struct diff_patch_line diff_patch_line;
+struct diff_patch_line {
+	const char *ptr;
+	size_t len;
+	size_t lines, oldno, newno;
+	char origin;
+};
+
+/* cached information about a hunk in a diff */
+typedef struct diff_patch_hunk diff_patch_hunk;
+struct diff_patch_hunk {
+	git_diff_range range;
+	char   header[128];
+	size_t header_len;
+	size_t line_start;
+	size_t line_count;
+};
+
+struct git_diff_patch {
+	git_refcount rc;
+	git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
+	git_diff_delta *delta;
+	diff_context *ctxt; /* only valid while generating patch */
+	git_iterator_type_t old_src;
+	git_iterator_type_t new_src;
+	git_blob *old_blob;
+	git_blob *new_blob;
+	git_map  old_data;
+	git_map  new_data;
+	uint32_t flags;
+	diff_patch_hunk *hunks;
+	size_t hunks_asize, hunks_size;
+	diff_patch_line *lines;
+	size_t lines_asize, lines_size;
+	size_t oldno, newno;
+};
+
+/* context for performing diff on a single delta */
+typedef struct {
+	git_diff_patch *patch;
+	uint32_t prepped  : 1;
+	uint32_t loaded   : 1;
+	uint32_t diffable : 1;
+	uint32_t diffed   : 1;
+} diff_delta_context;
+
+extern int git_diff__paired_foreach(
+	git_diff_list *idx2head,
+	git_diff_list *wd2idx,
+	int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
+	void *payload);
+
+#endif
diff --git a/libgit2/src/diff_tform.c b/libgit2/src/diff_tform.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/diff_tform.c
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "diff.h"
+#include "git2/config.h"
+#include "git2/blob.h"
+#include "hashsig.h"
+
+static git_diff_delta *diff_delta__dup(
+	const git_diff_delta *d, git_pool *pool)
+{
+	git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
+	if (!delta)
+		return NULL;
+
+	memcpy(delta, d, sizeof(git_diff_delta));
+
+	delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
+	if (delta->old_file.path == NULL)
+		goto fail;
+
+	if (d->new_file.path != d->old_file.path) {
+		delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
+		if (delta->new_file.path == NULL)
+			goto fail;
+	} else {
+		delta->new_file.path = delta->old_file.path;
+	}
+
+	return delta;
+
+fail:
+	git__free(delta);
+	return NULL;
+}
+
+static git_diff_delta *diff_delta__merge_like_cgit(
+	const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
+{
+	git_diff_delta *dup;
+
+	/* Emulate C git for merging two diffs (a la 'git diff <sha>').
+	 *
+	 * When C git does a diff between the work dir and a tree, it actually
+	 * diffs with the index but uses the workdir contents.  This emulates
+	 * those choices so we can emulate the type of diff.
+	 *
+	 * We have three file descriptions here, let's call them:
+	 *  f1 = a->old_file
+	 *  f2 = a->new_file AND b->old_file
+	 *  f3 = b->new_file
+	 */
+
+	/* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
+	if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
+		return diff_delta__dup(a, pool);
+
+	/* otherwise, base this diff on the 'b' diff */
+	if ((dup = diff_delta__dup(b, pool)) == NULL)
+		return NULL;
+
+	/* If 'a' status is uninteresting, then we're done */
+	if (a->status == GIT_DELTA_UNMODIFIED)
+		return dup;
+
+	assert(a->status != GIT_DELTA_UNMODIFIED);
+	assert(b->status != GIT_DELTA_UNMODIFIED);
+
+	/* A cgit exception is that the diff of a file that is only in the
+	 * index (i.e. not in HEAD nor workdir) is given as empty.
+	 */
+	if (dup->status == GIT_DELTA_DELETED) {
+		if (a->status == GIT_DELTA_ADDED)
+			dup->status = GIT_DELTA_UNMODIFIED;
+		/* else don't overwrite DELETE status */
+	} else {
+		dup->status = a->status;
+	}
+
+	git_oid_cpy(&dup->old_file.oid, &a->old_file.oid);
+	dup->old_file.mode  = a->old_file.mode;
+	dup->old_file.size  = a->old_file.size;
+	dup->old_file.flags = a->old_file.flags;
+
+	return dup;
+}
+
+int git_diff_merge(
+	git_diff_list *onto,
+	const git_diff_list *from)
+{
+	int error = 0;
+	git_pool onto_pool;
+	git_vector onto_new;
+	git_diff_delta *delta;
+	bool ignore_case = false;
+	unsigned int i, j;
+
+	assert(onto && from);
+
+	if (!from->deltas.length)
+		return 0;
+
+	if (git_vector_init(
+			&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
+		git_pool_init(&onto_pool, 1, 0) < 0)
+		return -1;
+
+	if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
+		(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
+	{
+		ignore_case = true;
+
+		/* This function currently only supports merging diff lists that
+		 * are sorted identically. */
+		assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
+				(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
+	}
+
+	for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+		git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+		const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+		int cmp = !f ? -1 : !o ? 1 : STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
+
+		if (cmp < 0) {
+			delta = diff_delta__dup(o, &onto_pool);
+			i++;
+		} else if (cmp > 0) {
+			delta = diff_delta__dup(f, &onto_pool);
+			j++;
+		} else {
+			delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
+			i++;
+			j++;
+		}
+
+		/* the ignore rules for the target may not match the source
+		 * or the result of a merged delta could be skippable...
+		 */
+		if (git_diff_delta__should_skip(&onto->opts, delta)) {
+			git__free(delta);
+			continue;
+		}
+
+		if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
+			break;
+	}
+
+	if (!error) {
+		git_vector_swap(&onto->deltas, &onto_new);
+		git_pool_swap(&onto->pool, &onto_pool);
+		onto->new_src = from->new_src;
+
+		/* prefix strings also come from old pool, so recreate those.*/
+		onto->opts.old_prefix =
+			git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix);
+		onto->opts.new_prefix =
+			git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix);
+	}
+
+	git_vector_foreach(&onto_new, i, delta)
+		git__free(delta);
+	git_vector_free(&onto_new);
+	git_pool_clear(&onto_pool);
+
+	return error;
+}
+
+static int find_similar__hashsig_for_file(
+	void **out, const git_diff_file *f, const char *path, void *p)
+{
+	git_hashsig_option_t opt = (git_hashsig_option_t)p;
+	int error = 0;
+
+	GIT_UNUSED(f);
+	error = git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
+	
+	if (error == GIT_EBUFS) {
+		error = 0;
+		giterr_clear();
+	}
+
+	return error;
+}
+
+static int find_similar__hashsig_for_buf(
+	void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
+{
+	git_hashsig_option_t opt = (git_hashsig_option_t)p;
+	int error = 0;
+	
+	GIT_UNUSED(f);
+	error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
+	
+	if (error == GIT_EBUFS) {
+		error = 0;
+		giterr_clear();
+	}
+
+	return error;
+}
+
+static void find_similar__hashsig_free(void *sig, void *payload)
+{
+	GIT_UNUSED(payload);
+	git_hashsig_free(sig);
+}
+
+static int find_similar__calc_similarity(
+	int *score, void *siga, void *sigb, void *payload)
+{
+	GIT_UNUSED(payload);
+	*score = git_hashsig_compare(siga, sigb);
+	return 0;
+}
+
+#define DEFAULT_THRESHOLD 50
+#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
+#define DEFAULT_TARGET_LIMIT 200
+
+static int normalize_find_opts(
+	git_diff_list *diff,
+	git_diff_find_options *opts,
+	git_diff_find_options *given)
+{
+	git_config *cfg = NULL;
+
+	if (diff->repo != NULL &&
+		git_repository_config__weakptr(&cfg, diff->repo) < 0)
+		return -1;
+
+	if (given != NULL)
+		memcpy(opts, given, sizeof(*opts));
+	else {
+		const char *val = NULL;
+
+		GIT_INIT_STRUCTURE(opts, GIT_DIFF_FIND_OPTIONS_VERSION);
+
+		opts->flags = GIT_DIFF_FIND_RENAMES;
+
+		if (git_config_get_string(&val, cfg, "diff.renames") < 0)
+			giterr_clear();
+		else if (val &&
+			(!strcasecmp(val, "copies") || !strcasecmp(val, "copy")))
+			opts->flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+	}
+
+	GITERR_CHECK_VERSION(opts, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options");
+
+	/* some flags imply others */
+
+	if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES)
+		opts->flags |= GIT_DIFF_FIND_RENAMES;
+
+	if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
+		opts->flags |= GIT_DIFF_FIND_COPIES;
+
+#define USE_DEFAULT(X) ((X) == 0 || (X) > 100)
+
+	if (USE_DEFAULT(opts->rename_threshold))
+		opts->rename_threshold = DEFAULT_THRESHOLD;
+
+	if (USE_DEFAULT(opts->rename_from_rewrite_threshold))
+		opts->rename_from_rewrite_threshold = DEFAULT_THRESHOLD;
+
+	if (USE_DEFAULT(opts->copy_threshold))
+		opts->copy_threshold = DEFAULT_THRESHOLD;
+
+	if (USE_DEFAULT(opts->break_rewrite_threshold))
+		opts->break_rewrite_threshold = DEFAULT_BREAK_REWRITE_THRESHOLD;
+
+#undef USE_DEFAULT
+
+	if (!opts->target_limit) {
+		int32_t limit = 0;
+
+		opts->target_limit = DEFAULT_TARGET_LIMIT;
+
+		if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0)
+			giterr_clear();
+		else if (limit > 0)
+			opts->target_limit = limit;
+	}
+
+	/* assign the internal metric with whitespace flag as payload */
+	if (!opts->metric) {
+		opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
+		GITERR_CHECK_ALLOC(opts->metric);
+
+		opts->metric->file_signature = find_similar__hashsig_for_file;
+		opts->metric->buffer_signature = find_similar__hashsig_for_buf;
+		opts->metric->free_signature = find_similar__hashsig_free;
+		opts->metric->similarity = find_similar__calc_similarity;
+
+		if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
+			opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
+		else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE)
+			opts->metric->payload = (void *)GIT_HASHSIG_NORMAL;
+		else
+			opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
+	}
+
+	return 0;
+}
+
+static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size)
+{
+	git_vector onto = GIT_VECTOR_INIT;
+	size_t i;
+	git_diff_delta *delta;
+
+	if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
+		return -1;
+
+	/* build new delta list without TO_DELETE and splitting TO_SPLIT */
+	git_vector_foreach(&diff->deltas, i, delta) {
+		if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
+			continue;
+
+		if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
+			git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
+			if (!deleted)
+				goto on_error;
+
+			deleted->status = GIT_DELTA_DELETED;
+			memset(&deleted->new_file, 0, sizeof(deleted->new_file));
+			deleted->new_file.path = deleted->old_file.path;
+			deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+
+			if (git_vector_insert(&onto, deleted) < 0)
+				goto on_error;
+
+			delta->status = GIT_DELTA_ADDED;
+			memset(&delta->old_file, 0, sizeof(delta->old_file));
+			delta->old_file.path = delta->new_file.path;
+			delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+		}
+
+		if (git_vector_insert(&onto, delta) < 0)
+			goto on_error;
+	}
+
+	/* cannot return an error past this point */
+	git_vector_foreach(&diff->deltas, i, delta)
+		if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
+			git__free(delta);
+
+	/* swap new delta list into place */
+	git_vector_sort(&onto);
+	git_vector_swap(&diff->deltas, &onto);
+	git_vector_free(&onto);
+
+	return 0;
+
+on_error:
+	git_vector_foreach(&onto, i, delta)
+		git__free(delta);
+
+	git_vector_free(&onto);
+
+	return -1;
+}
+
+GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
+{
+	git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
+	return (idx & 1) ? &delta->new_file : &delta->old_file;
+}
+
+static int similarity_calc(
+	git_diff_list *diff,
+	git_diff_find_options *opts,
+	size_t file_idx,
+	void **cache)
+{
+	int error = 0;
+	git_diff_file *file = similarity_get_file(diff, file_idx);
+	git_iterator_type_t src = (file_idx & 1) ? diff->old_src : diff->new_src;
+
+	if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
+		git_buf path = GIT_BUF_INIT;
+
+		/* TODO: apply wd-to-odb filters to file data if necessary */
+
+		if (!(error = git_buf_joinpath(
+				&path, git_repository_workdir(diff->repo), file->path)))
+			error = opts->metric->file_signature(
+				&cache[file_idx], file, path.ptr, opts->metric->payload);
+
+		git_buf_free(&path);
+	} else { /* compute hashsig from blob buffer */
+		git_blob *blob = NULL;
+		git_off_t blobsize;
+
+		/* TODO: add max size threshold a la diff? */
+
+		if ((error = git_blob_lookup(&blob, diff->repo, &file->oid)) < 0)
+			return error;
+
+		blobsize = git_blob_rawsize(blob);
+		if (!git__is_sizet(blobsize)) /* ? what to do ? */
+			blobsize = (size_t)-1;
+
+		error = opts->metric->buffer_signature(
+			&cache[file_idx], file, git_blob_rawcontent(blob),
+			(size_t)blobsize, opts->metric->payload);
+
+		git_blob_free(blob);
+	}
+
+	return error;
+}
+
+static int similarity_measure(
+	git_diff_list *diff,
+	git_diff_find_options *opts,
+	void **cache,
+	size_t a_idx,
+	size_t b_idx)
+{
+	int score = 0;
+	git_diff_file *a_file = similarity_get_file(diff, a_idx);
+	git_diff_file *b_file = similarity_get_file(diff, b_idx);
+
+	if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
+		return 0;
+
+	if (git_oid_cmp(&a_file->oid, &b_file->oid) == 0)
+		return 100;
+
+	/* update signature cache if needed */
+	if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
+		return -1;
+	if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
+		return -1;
+	
+	/* some metrics may not wish to process this file (too big / too small) */
+	if (!cache[a_idx] || !cache[b_idx])
+		return 0;
+
+	/* compare signatures */
+	if (opts->metric->similarity(
+			&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
+		return -1;
+
+	/* clip score */
+	if (score < 0)
+		score = 0;
+	else if (score > 100)
+		score = 100;
+
+	return score;
+}
+
+#define FLAG_SET(opts,flag_name) ((opts.flags & flag_name) != 0)
+
+int git_diff_find_similar(
+	git_diff_list *diff,
+	git_diff_find_options *given_opts)
+{
+	size_t i, j, cache_size, *matches;
+	int error = 0, similarity;
+	git_diff_delta *from, *to;
+	git_diff_find_options opts;
+	size_t tried_targets, num_rewrites = 0;
+	void **cache;
+
+	if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
+		return error;
+
+	/* TODO: maybe abort if deltas.length > target_limit ??? */
+
+	cache_size = diff->deltas.length * 2; /* must store b/c length may change */
+	cache = git__calloc(cache_size, sizeof(void *));
+	GITERR_CHECK_ALLOC(cache);
+
+	matches = git__calloc(diff->deltas.length, sizeof(size_t));
+	GITERR_CHECK_ALLOC(matches);
+
+	/* first break MODIFIED records that are too different (if requested) */
+
+	if (FLAG_SET(opts, GIT_DIFF_FIND_AND_BREAK_REWRITES)) {
+		git_vector_foreach(&diff->deltas, i, from) {
+			if (from->status != GIT_DELTA_MODIFIED)
+				continue;
+
+			similarity = similarity_measure(
+				diff, &opts, cache, 2 * i, 2 * i + 1);
+
+			if (similarity < 0) {
+				error = similarity;
+				goto cleanup;
+			}
+
+			if ((unsigned int)similarity < opts.break_rewrite_threshold) {
+				from->flags |= GIT_DIFF_FLAG__TO_SPLIT;
+				num_rewrites++;
+			}
+		}
+	}
+
+	/* next find the most similar delta for each rename / copy candidate */
+
+	git_vector_foreach(&diff->deltas, i, from) {
+		tried_targets = 0;
+
+		/* skip things that aren't blobs */
+		if (GIT_MODE_TYPE(from->old_file.mode) !=
+			GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
+			continue;
+
+		/* don't check UNMODIFIED files as source unless given option */
+		if (from->status == GIT_DELTA_UNMODIFIED &&
+			!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
+			continue;
+
+		/* skip all but DELETED files unless copy detection is on */
+		if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES) &&
+			from->status != GIT_DELTA_DELETED &&
+			(from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0)
+			continue;
+
+		git_vector_foreach(&diff->deltas, j, to) {
+			if (i == j)
+				continue;
+
+			/* skip things that aren't blobs */
+			if (GIT_MODE_TYPE(to->new_file.mode) !=
+				GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
+				continue;
+
+			switch (to->status) {
+			case GIT_DELTA_ADDED:
+			case GIT_DELTA_UNTRACKED:
+			case GIT_DELTA_RENAMED:
+			case GIT_DELTA_COPIED:
+				break;
+			case GIT_DELTA_MODIFIED:
+				if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0)
+					continue;
+				break;
+			default:
+				/* only the above status values should be checked */
+				continue;
+			}
+
+			/* cap on maximum files we'll examine (per "from" file) */
+			if (++tried_targets > opts.target_limit)
+				break;
+
+			/* calculate similarity and see if this pair beats the
+			 * similarity score of the current best pair.
+			 */
+			similarity = similarity_measure(
+				diff, &opts, cache, 2 * i, 2 * j + 1);
+
+			if (similarity < 0) {
+				error = similarity;
+				goto cleanup;
+			}
+
+			if (to->similarity < (unsigned int)similarity) {
+				to->similarity = (unsigned int)similarity;
+				matches[j] = i + 1;
+			}
+		}
+	}
+
+	/* next rewrite the diffs with renames / copies */
+
+	git_vector_foreach(&diff->deltas, j, to) {
+		if (!matches[j]) {
+			assert(to->similarity == 0);
+			continue;
+		}
+
+		i = matches[j] - 1;
+		from = GIT_VECTOR_GET(&diff->deltas, i);
+		assert(from);
+
+		/* four possible outcomes here:
+		 * 1. old DELETED and if over rename threshold,
+		 *    new becomes RENAMED and old goes away
+		 * 2. old SPLIT and if over rename threshold,
+		 *    new becomes RENAMED and old becomes ADDED (clear SPLIT)
+		 * 3. old was MODIFIED but FIND_RENAMES_FROM_REWRITES is on and
+		 *    old is more similar to new than it is to itself, in which
+		 *    case, new becomes RENAMED and old becomed ADDED
+		 * 4. otherwise if over copy threshold, new becomes COPIED
+		 */
+
+		if (from->status == GIT_DELTA_DELETED) {
+			if (to->similarity < opts.rename_threshold) {
+				to->similarity = 0;
+				continue;
+			}
+
+			to->status = GIT_DELTA_RENAMED;
+			memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+
+			from->flags |= GIT_DIFF_FLAG__TO_DELETE;
+			num_rewrites++;
+
+			continue;
+		}
+
+		if (from->status == GIT_DELTA_MODIFIED &&
+			(from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
+		{
+			if (to->similarity < opts.rename_threshold) {
+				to->similarity = 0;
+				continue;
+			}
+
+			to->status = GIT_DELTA_RENAMED;
+			memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+
+			from->status = GIT_DELTA_ADDED;
+			from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+			memset(&from->old_file, 0, sizeof(from->old_file));
+			num_rewrites--;
+
+			continue;
+		}
+
+		if (from->status == GIT_DELTA_MODIFIED &&
+			FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
+			to->similarity > opts.rename_threshold)
+		{
+			similarity = similarity_measure(
+				diff, &opts, cache, 2 * i, 2 * i + 1);
+
+			if (similarity < 0) {
+				error = similarity;
+				goto cleanup;
+			}
+
+			if ((unsigned int)similarity < opts.rename_from_rewrite_threshold) {
+				to->status = GIT_DELTA_RENAMED;
+				memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+
+				from->status = GIT_DELTA_ADDED;
+				memset(&from->old_file, 0, sizeof(from->old_file));
+				from->old_file.path = to->old_file.path;
+				from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
+
+				continue;
+			}
+		}
+
+		if (to->similarity < opts.copy_threshold) {
+			to->similarity = 0;
+			continue;
+		}
+
+		/* convert "to" to a COPIED record */
+		to->status = GIT_DELTA_COPIED;
+		memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+	}
+
+	if (num_rewrites > 0) {
+		assert(num_rewrites < diff->deltas.length);
+
+		error = apply_splits_and_deletes(
+			diff, diff->deltas.length - num_rewrites);
+	}
+
+cleanup:
+	git__free(matches);
+
+	for (i = 0; i < cache_size; ++i) {
+		if (cache[i] != NULL)
+			opts.metric->free_signature(cache[i], opts.metric->payload);
+	}
+	git__free(cache);
+
+	if (!given_opts || !given_opts->metric)
+		git__free(opts.metric);
+
+	return error;
+}
+
+#undef FLAG_SET
diff --git a/libgit2/src/errors.c b/libgit2/src/errors.c
--- a/libgit2/src/errors.c
+++ b/libgit2/src/errors.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -40,52 +40,34 @@
 {
 	git_buf buf = GIT_BUF_INIT;
 	va_list arglist;
-
-	int unix_error_code = 0;
-
 #ifdef GIT_WIN32
-	DWORD win32_error_code = 0;
-#endif
-
-	if (error_class == GITERR_OS) {
-		unix_error_code = errno;
-		errno = 0;
-
-#ifdef GIT_WIN32
-		win32_error_code = GetLastError();
-		SetLastError(0);
+	DWORD win32_error_code = (error_class == GITERR_OS) ? GetLastError() : 0;
 #endif
-	}
+	int error_code = (error_class == GITERR_OS) ? errno : 0;
 
 	va_start(arglist, string);
 	git_buf_vprintf(&buf, string, arglist);
 	va_end(arglist);
 
-	/* automatically suffix strerror(errno) for GITERR_OS errors */
 	if (error_class == GITERR_OS) {
-
-		if (unix_error_code != 0) {
-			git_buf_PUTS(&buf, ": ");
-			git_buf_puts(&buf, strerror(unix_error_code));
-		}
-
 #ifdef GIT_WIN32
-		else if (win32_error_code != 0) {
-			LPVOID lpMsgBuf = NULL;
-
-			FormatMessage(
-				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
-				FORMAT_MESSAGE_FROM_SYSTEM |
-				FORMAT_MESSAGE_IGNORE_INSERTS,
-				NULL, win32_error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
+		char * win32_error = git_win32_get_error_message(win32_error_code);
+		if (win32_error) {
+			git_buf_PUTS(&buf, ": ");
+			git_buf_puts(&buf, win32_error);
+			git__free(win32_error);
 
-			if (lpMsgBuf) {
-				git_buf_PUTS(&buf, ": ");
-				git_buf_puts(&buf, lpMsgBuf);
-				LocalFree(lpMsgBuf);
-			}
+			SetLastError(0);
 		}
+		else
 #endif
+		if (error_code) {
+			git_buf_PUTS(&buf, ": ");
+			git_buf_puts(&buf, strerror(error_code));
+		}
+
+		if (error_code)
+			errno = 0;
 	}
 
 	if (!git_buf_oom(&buf))
@@ -94,22 +76,40 @@
 
 void giterr_set_str(int error_class, const char *string)
 {
-	char *message = git__strdup(string);
+	char *message;
 
+	assert(string);
+
+	message = git__strdup(string);
+
 	if (message)
 		set_error(error_class, message);
 }
 
-void giterr_set_regex(const regex_t *regex, int error_code)
+int giterr_set_regex(const regex_t *regex, int error_code)
 {
 	char error_buf[1024];
+
+	assert(error_code);
+
 	regerror(error_code, regex, error_buf, sizeof(error_buf));
 	giterr_set_str(GITERR_REGEX, error_buf);
+
+	if (error_code == REG_NOMATCH)
+		return GIT_ENOTFOUND;
+
+	return GIT_EINVALIDSPEC;
 }
 
 void giterr_clear(void)
 {
+	set_error(0, NULL);
 	GIT_GLOBAL->last_error = NULL;
+
+	errno = 0;
+#ifdef GIT_WIN32
+	SetLastError(0);
+#endif
 }
 
 const git_error *giterr_last(void)
diff --git a/libgit2/src/fetch.c b/libgit2/src/fetch.c
--- a/libgit2/src/fetch.c
+++ b/libgit2/src/fetch.c
@@ -1,18 +1,16 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "git2/remote.h"
 #include "git2/oid.h"
 #include "git2/refs.h"
 #include "git2/revwalk.h"
-#include "git2/indexer.h"
+#include "git2/transport.h"
 
 #include "common.h"
-#include "transport.h"
 #include "remote.h"
 #include "refspec.h"
 #include "pack.h"
@@ -21,7 +19,7 @@
 
 struct filter_payload {
 	git_remote *remote;
-	const git_refspec *spec;
+	const git_refspec *spec, *tagspec;
 	git_odb *odb;
 	int found_head;
 };
@@ -29,18 +27,21 @@
 static int filter_ref__cb(git_remote_head *head, void *payload)
 {
 	struct filter_payload *p = payload;
+	int match = 0;
 
-	if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
+	if (!git_reference_is_valid_name(head->name))
+		return 0;
+
+	if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
 		p->found_head = 1;
-	} else {
-		/* If it doesn't match the refpec, we don't want it */
-		if (!git_refspec_src_matches(p->spec, head->name))
-			return 0;
+	else if (git_refspec_src_matches(p->spec, head->name))
+			match = 1;
+	else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
+		 git_refspec_src_matches(p->tagspec, head->name))
+			match = 1;
 
-		/* Don't even try to ask for the annotation target */
-		if (!git__suffixcmp(head->name, "^{}"))
-			return 0;
-	}
+	if (!match)
+		return 0;
 
 	/* If we have the object, mark it so we don't ask for it */
 	if (git_odb_exists(p->odb, &head->oid))
@@ -54,8 +55,12 @@
 static int filter_wants(git_remote *remote)
 {
 	struct filter_payload p;
+	git_refspec tagspec;
+	int error = -1;
 
 	git_vector_clear(&remote->refs);
+	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+		return error;
 
 	/*
 	 * The fetch refspec can be NULL, and what this means is that the
@@ -64,13 +69,19 @@
 	 * HEAD, which will be stored in FETCH_HEAD after the fetch.
 	 */
 	p.spec = git_remote_fetchspec(remote);
+	p.tagspec = &tagspec;
 	p.found_head = 0;
 	p.remote = remote;
 
 	if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
-		return -1;
+		goto cleanup;
 
-	return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
+	error = git_remote_ls(remote, filter_ref__cb, &p);
+
+cleanup:
+	git_refspec__free(&tagspec);
+
+	return error;
 }
 
 /*
@@ -81,7 +92,7 @@
 int git_fetch_negotiate(git_remote *remote)
 {
 	git_transport *t = remote->transport;
-
+	
 	if (filter_wants(remote) < 0) {
 		giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
 		return -1;
@@ -92,109 +103,24 @@
 		return 0;
 
 	/*
-	 * Now we have everything set up so we can start tell the server
-	 * what we want and what we have.
+	 * Now we have everything set up so we can start tell the
+	 * server what we want and what we have.
 	 */
-	return t->negotiate_fetch(t, remote->repo, &remote->refs);
+	return t->negotiate_fetch(t,
+		remote->repo,
+		(const git_remote_head * const *)remote->refs.contents,
+		remote->refs.length);
 }
 
-int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
+int git_fetch_download_pack(
+	git_remote *remote,
+	git_transfer_progress_callback progress_cb,
+	void *progress_payload)
 {
+	git_transport *t = remote->transport;
+
 	if(!remote->need_pack)
 		return 0;
 
-	return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
-}
-
-/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
-int git_fetch__download_pack(
-	const char *buffered,
-	size_t buffered_size,
-	GIT_SOCKET fd,
-	git_repository *repo,
-	git_off_t *bytes,
-	git_indexer_stats *stats)
-{
-	int recvd;
-	char buff[1024];
-	gitno_buffer buf;
-	git_indexer_stream *idx;
-
-	gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
-
-	if (memcmp(buffered, "PACK", strlen("PACK"))) {
-		giterr_set(GITERR_NET, "The pack doesn't start with the signature");
-		return -1;
-	}
-
-	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
-		return -1;
-
-	memset(stats, 0, sizeof(git_indexer_stats));
-	if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
-		goto on_error;
-
-	*bytes = buffered_size;
-
-	do {
-		if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
-			goto on_error;
-
-		gitno_consume_n(&buf, buf.offset);
-		if ((recvd = gitno_recv(&buf)) < 0)
-			goto on_error;
-
-		*bytes += recvd;
-	} while(recvd > 0);
-
-	if (git_indexer_stream_finalize(idx, stats))
-		goto on_error;
-
-	git_indexer_stream_free(idx);
-	return 0;
-
-on_error:
-	git_indexer_stream_free(idx);
-	return -1;
-}
-
-int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
-{
-	git_revwalk *walk;
-	git_strarray refs;
-	unsigned int i;
-	git_reference *ref;
-
-	if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)
-		return -1;
-
-	if (git_revwalk_new(&walk, repo) < 0)
-		return -1;
-
-	git_revwalk_sorting(walk, GIT_SORT_TIME);
-
-	for (i = 0; i < refs.count; ++i) {
-		/* No tags */
-		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
-			continue;
-
-		if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
-			goto on_error;
-
-		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
-			continue;
-		if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)
-			goto on_error;
-
-		git_reference_free(ref);
-	}
-
-	git_strarray_free(&refs);
-	*out = walk;
-	return 0;
-
-on_error:
-	git_reference_free(ref);
-	git_strarray_free(&refs);
-	return -1;
+	return t->download_pack(t, remote->repo, &remote->stats, progress_cb, progress_payload);
 }
diff --git a/libgit2/src/fetch.h b/libgit2/src/fetch.h
--- a/libgit2/src/fetch.h
+++ b/libgit2/src/fetch.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,10 +10,19 @@
 #include "netops.h"
 
 int git_fetch_negotiate(git_remote *remote);
-int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
 
-int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
-			     git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
+int git_fetch_download_pack(
+		git_remote *remote,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload);
+
+int git_fetch__download_pack(
+		git_transport *t,
+		git_repository *repo,
+		git_transfer_progress *stats,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload);
+
 int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
 
 #endif
diff --git a/libgit2/src/fetchhead.c b/libgit2/src/fetchhead.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fetchhead.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/types.h"
+#include "git2/oid.h"
+
+#include "fetchhead.h"
+#include "common.h"
+#include "buffer.h"
+#include "fileops.h"
+#include "filebuf.h"
+#include "refs.h"
+#include "repository.h"
+
+int git_fetchhead_ref_cmp(const void *a, const void *b)
+{
+	const git_fetchhead_ref *one = (const git_fetchhead_ref *)a;
+	const git_fetchhead_ref *two = (const git_fetchhead_ref *)b;
+
+	if (one->is_merge && !two->is_merge)
+		return -1;
+	if (two->is_merge && !one->is_merge)
+		return 1;
+
+	if (one->ref_name && two->ref_name)
+		return strcmp(one->ref_name, two->ref_name);
+	else if (one->ref_name)
+		return -1;
+	else if (two->ref_name)
+		return 1;
+
+	return 0;
+}
+
+int git_fetchhead_ref_create(
+	git_fetchhead_ref **out,
+	git_oid *oid,
+	unsigned int is_merge,
+	const char *ref_name,
+	const char *remote_url)
+{
+	git_fetchhead_ref *fetchhead_ref;
+
+	assert(out && oid);
+
+	*out = NULL;
+
+	fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref));
+	GITERR_CHECK_ALLOC(fetchhead_ref);
+
+	memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref));
+
+	git_oid_cpy(&fetchhead_ref->oid, oid);
+	fetchhead_ref->is_merge = is_merge;
+
+	if (ref_name)
+		fetchhead_ref->ref_name = git__strdup(ref_name);
+
+	if (remote_url)
+		fetchhead_ref->remote_url = git__strdup(remote_url);
+
+	*out = fetchhead_ref;
+
+	return 0;
+}
+
+static int fetchhead_ref_write(
+	git_filebuf *file,
+	git_fetchhead_ref *fetchhead_ref)
+{
+	char oid[GIT_OID_HEXSZ + 1];
+	const char *type, *name;
+
+	assert(file && fetchhead_ref);
+
+	git_oid_fmt(oid, &fetchhead_ref->oid);
+	oid[GIT_OID_HEXSZ] = '\0';
+
+	if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
+		type = "branch ";
+		name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR);
+	} else if(git__prefixcmp(fetchhead_ref->ref_name,
+		GIT_REFS_TAGS_DIR) == 0) {
+		type = "tag ";
+		name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
+	} else {
+		type = "";
+		name = fetchhead_ref->ref_name;
+	}
+
+	return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
+		oid,
+		(fetchhead_ref->is_merge) ? "" : "not-for-merge",
+		type,
+		name,
+		fetchhead_ref->remote_url);
+}
+
+int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	git_buf path = GIT_BUF_INIT;
+	unsigned int i;
+	git_fetchhead_ref *fetchhead_ref;
+
+	assert(repo && fetchhead_refs);
+
+	if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
+		return -1;
+
+	if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_FORCE) < 0) {
+		git_buf_free(&path);
+		return -1;
+	}
+
+	git_buf_free(&path);
+
+	git_vector_sort(fetchhead_refs);
+
+	git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
+		fetchhead_ref_write(&file, fetchhead_ref);
+
+	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
+}
+
+static int fetchhead_ref_parse(
+	git_oid *oid,
+	unsigned int *is_merge,
+	git_buf *ref_name,
+	const char **remote_url,
+	char *line,
+	size_t line_num)
+{
+	char *oid_str, *is_merge_str, *desc, *name = NULL;
+	const char *type = NULL;
+	int error = 0;
+
+	*remote_url = NULL;
+
+	if (!*line) {
+		giterr_set(GITERR_FETCHHEAD,
+			"Empty line in FETCH_HEAD line %d", line_num);
+		return -1;
+	}
+
+	/* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
+	if ((oid_str = git__strsep(&line, "\t")) == NULL) {
+		oid_str = line;
+		line += strlen(line);
+
+		*is_merge = 1;
+	}
+
+	if (strlen(oid_str) != GIT_OID_HEXSZ) {
+		giterr_set(GITERR_FETCHHEAD,
+			"Invalid object ID in FETCH_HEAD line %d", line_num);
+		return -1;
+	}
+
+	if (git_oid_fromstr(oid, oid_str) < 0) {
+		const git_error *oid_err = giterr_last();
+		const char *err_msg = oid_err ? oid_err->message : "Invalid object ID";
+
+		giterr_set(GITERR_FETCHHEAD, "%s in FETCH_HEAD line %d",
+			err_msg, line_num);
+		return -1;
+	}
+
+	/* Parse new data from newer git clients */
+	if (*line) {
+		if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
+			giterr_set(GITERR_FETCHHEAD,
+				"Invalid description data in FETCH_HEAD line %d", line_num);
+			return -1;
+		}
+
+		if (*is_merge_str == '\0')
+			*is_merge = 1;
+		else if (strcmp(is_merge_str, "not-for-merge") == 0)
+			*is_merge = 0;
+		else {
+			giterr_set(GITERR_FETCHHEAD,
+				"Invalid for-merge entry in FETCH_HEAD line %d", line_num);
+			return -1;
+		}
+
+		if ((desc = line) == NULL) {
+			giterr_set(GITERR_FETCHHEAD,
+				"Invalid description in FETCH_HEAD line %d", line_num);
+			return -1;
+		}
+
+		if (git__prefixcmp(desc, "branch '") == 0) {
+			type = GIT_REFS_HEADS_DIR;
+			name = desc + 8;
+		} else if (git__prefixcmp(desc, "tag '") == 0) {
+			type = GIT_REFS_TAGS_DIR;
+			name = desc + 5;
+		} else if (git__prefixcmp(desc, "'") == 0)
+			name = desc + 1;
+
+		if (name) {
+			if ((desc = strchr(name, '\'')) == NULL ||
+				git__prefixcmp(desc, "' of ") != 0) {
+				giterr_set(GITERR_FETCHHEAD,
+					"Invalid description in FETCH_HEAD line %d", line_num);
+				return -1;
+			}
+
+			*desc = '\0';
+			desc += 5;
+		}
+
+		*remote_url = desc;
+	}
+
+	git_buf_clear(ref_name);
+
+	if (type)
+		git_buf_join(ref_name, '/', type, name);
+	else if(name)
+		git_buf_puts(ref_name, name);
+
+	return error;
+}
+
+int git_repository_fetchhead_foreach(git_repository *repo,
+	git_repository_fetchhead_foreach_cb cb,
+	void *payload)
+{
+	git_buf path = GIT_BUF_INIT, file = GIT_BUF_INIT, name = GIT_BUF_INIT;
+	const char *ref_name;
+	git_oid oid;
+	const char *remote_url;
+	unsigned int is_merge = 0;
+	char *buffer, *line;
+	size_t line_num = 0;
+	int error = 0;
+
+	assert(repo && cb);
+
+	if (git_buf_joinpath(&path, repo->path_repository, GIT_FETCH_HEAD_FILE) < 0)
+		return -1;
+
+	if ((error = git_futils_readbuffer(&file, git_buf_cstr(&path))) < 0)
+		goto done;
+
+	buffer = file.ptr;
+
+	while ((line = git__strsep(&buffer, "\n")) != NULL) {
+		++line_num;
+
+		if ((error = fetchhead_ref_parse(&oid, &is_merge, &name, &remote_url,
+			line, line_num)) < 0)
+			goto done;
+
+		if (git_buf_len(&name) > 0)
+			ref_name = git_buf_cstr(&name);
+		else
+			ref_name = NULL;
+
+		if ((cb(ref_name, remote_url, &oid, is_merge, payload)) != 0) {
+			error = GIT_EUSER;
+			goto done;
+		}
+	}
+
+	if (*buffer) {
+		giterr_set(GITERR_FETCHHEAD, "No EOL at line %d", line_num+1);
+		error = -1;
+		goto done;
+	}
+
+done:
+	git_buf_free(&file);
+	git_buf_free(&path);
+	git_buf_free(&name);
+
+	return error;
+}
+
+void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref)
+{
+	if (fetchhead_ref == NULL)
+		return;
+
+	git__free(fetchhead_ref->remote_url);
+	git__free(fetchhead_ref->ref_name);
+	git__free(fetchhead_ref);
+}
+
diff --git a/libgit2/src/fetchhead.h b/libgit2/src/fetchhead.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fetchhead.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_fetchhead_h__
+#define INCLUDE_fetchhead_h__
+
+#include "vector.h"
+
+struct git_fetchhead_ref {
+	git_oid oid;
+	unsigned int is_merge;
+	char *ref_name;
+	char *remote_url;
+};
+
+typedef struct git_fetchhead_ref git_fetchhead_ref;
+
+int git_fetchhead_ref_create(
+	git_fetchhead_ref **fetchhead_ref_out,
+	git_oid *oid,
+	unsigned int is_merge,
+	const char *ref_name,
+	const char *remote_url);
+
+int git_fetchhead_ref_cmp(const void *a, const void *b);
+
+int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs);
+
+void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref);
+
+#endif
diff --git a/libgit2/src/filebuf.c b/libgit2/src/filebuf.c
--- a/libgit2/src/filebuf.c
+++ b/libgit2/src/filebuf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -50,6 +50,7 @@
 		if (flags & GIT_FILEBUF_FORCE)
 			p_unlink(file->path_lock);
 		else {
+			giterr_clear(); /* actual OS error code just confuses */
 			giterr_set(GITERR_OS,
 				"Failed to lock file '%s' for writing", file->path_lock);
 			return -1;
@@ -72,7 +73,7 @@
 	if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
 		git_file source;
 		char buffer[2048];
-		size_t read_bytes;
+		ssize_t read_bytes;
 
 		source = p_open(file->path_original, O_RDONLY);
 		if (source < 0) {
@@ -82,13 +83,18 @@
 			return -1;
 		}
 
-		while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
+		while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
 			p_write(file->fd, buffer, read_bytes);
-			if (file->digest)
-				git_hash_update(file->digest, buffer, read_bytes);
+			if (file->compute_digest)
+				git_hash_update(&file->digest, buffer, read_bytes);
 		}
 
 		p_close(source);
+
+		if (read_bytes < 0) {
+			giterr_set(GITERR_OS, "Failed to read file '%s'", file->path_original);
+			return -1;
+		}
 	}
 
 	return 0;
@@ -102,8 +108,10 @@
 	if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))
 		p_unlink(file->path_lock);
 
-	if (file->digest)
-		git_hash_free_ctx(file->digest);
+	if (file->compute_digest) {
+		git_hash_ctx_cleanup(&file->digest);
+		file->compute_digest = 0;
+	}
 
 	if (file->buffer)
 		git__free(file->buffer);
@@ -130,6 +138,11 @@
 	return result;
 }
 
+int git_filebuf_flush(git_filebuf *file)
+{
+	return flush_buffer(file);
+}
+
 static int write_normal(git_filebuf *file, void *source, size_t len)
 {
 	if (len > 0) {
@@ -138,8 +151,8 @@
 			return -1;
 		}
 
-		if (file->digest)
-			git_hash_update(file->digest, source, len);
+		if (file->compute_digest)
+			git_hash_update(&file->digest, source, len);
 	}
 
 	return 0;
@@ -175,8 +188,8 @@
 
 		assert(zs->avail_in == 0);
 
-		if (file->digest)
-			git_hash_update(file->digest, source, len);
+		if (file->compute_digest)
+			git_hash_update(&file->digest, source, len);
 	}
 
 	return 0;
@@ -210,8 +223,10 @@
 
 	/* If we are hashing on-write, allocate a new hash context */
 	if (flags & GIT_FILEBUF_HASH_CONTENTS) {
-		file->digest = git_hash_new_ctx();
-		GITERR_CHECK_ALLOC(file->digest);
+		file->compute_digest = 1;
+
+		if (git_hash_ctx_init(&file->digest) < 0)
+			goto cleanup;
 	}
 
 	compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
@@ -280,16 +295,16 @@
 
 int git_filebuf_hash(git_oid *oid, git_filebuf *file)
 {
-	assert(oid && file && file->digest);
+	assert(oid && file && file->compute_digest);
 
 	flush_buffer(file);
 
 	if (verify_last_error(file) < 0)
 		return -1;
 
-	git_hash_final(oid, file->digest);
-	git_hash_free_ctx(file->digest);
-	file->digest = NULL;
+	git_hash_final(oid, &file->digest);
+	git_hash_ctx_cleanup(&file->digest);
+	file->compute_digest = 0;
 
 	return 0;
 }
@@ -314,10 +329,15 @@
 	if (verify_last_error(file) < 0)
 		goto on_error;
 
-	p_close(file->fd);
-	file->fd = -1;
 	file->fd_is_open = false;
 
+	if (p_close(file->fd) < 0) {
+		giterr_set(GITERR_OS, "Failed to close file at '%s'", file->path_lock);
+		goto on_error;
+	}
+
+	file->fd = -1;
+
 	if (p_chmod(file->path_lock, mode)) {
 		giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
 		goto on_error;
@@ -450,3 +470,26 @@
 	return res;
 }
 
+int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file)
+{
+	int res;
+	struct stat st;
+
+	if (file->fd_is_open)
+		res = p_fstat(file->fd, &st);
+	else
+		res = p_stat(file->path_original, &st);
+
+	if (res < 0) {
+		giterr_set(GITERR_OS, "Could not get stat info for '%s'",
+			file->path_original);
+		return res;
+	}
+
+	if (mtime)
+		*mtime = st.st_mtime;
+	if (size)
+		*size = (size_t)st.st_size;
+
+	return 0;
+}
diff --git a/libgit2/src/filebuf.h b/libgit2/src/filebuf.h
--- a/libgit2/src/filebuf.h
+++ b/libgit2/src/filebuf.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -31,7 +31,8 @@
 
 	int (*write)(struct git_filebuf *file, void *source, size_t len);
 
-	git_hash_ctx *digest;
+	bool compute_digest;
+	git_hash_ctx digest;
 
 	unsigned char *buffer;
 	unsigned char *z_buf;
@@ -81,5 +82,7 @@
 int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
 void git_filebuf_cleanup(git_filebuf *lock);
 int git_filebuf_hash(git_oid *oid, git_filebuf *file);
+int git_filebuf_flush(git_filebuf *file);
+int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
 
 #endif
diff --git a/libgit2/src/fileops.c b/libgit2/src/fileops.c
--- a/libgit2/src/fileops.c
+++ b/libgit2/src/fileops.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,22 +7,15 @@
 #include "common.h"
 #include "fileops.h"
 #include <ctype.h>
+#if GIT_WIN32
+#include "win32/findfile.h"
+#endif
 
 int git_futils_mkpath2file(const char *file_path, const mode_t mode)
 {
-	int result = 0;
-	git_buf target_folder = GIT_BUF_INIT;
-
-	if (git_path_dirname_r(&target_folder, file_path) < 0)
-		return -1;
-
-	/* Does the containing folder exist? */
-	if (git_path_isdir(target_folder.ptr) == false)
-		/* Let's create the tree structure */
-		result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
-
-	git_buf_free(&target_folder);
-	return result;
+	return git_futils_mkdir(
+		file_path, NULL, mode,
+		GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
 }
 
 int git_futils_mktmp(git_buf *path_out, const char *filename)
@@ -65,11 +58,10 @@
 	int fd;
 
 #ifdef GIT_WIN32
-	wchar_t* buf;
+	wchar_t buf[GIT_WIN_PATH];
 
-	buf = gitwin_to_utf16(path);
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
 	fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
-	git__free(buf);
 #else
 	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
 #endif
@@ -94,7 +86,7 @@
 {
 	int fd = p_open(path, O_RDONLY);
 	if (fd < 0) {
-		if (errno == ENOENT)
+		if (errno == ENOENT || errno == ENOTDIR)
 			fd = GIT_ENOTFOUND;
 		giterr_set(GITERR_OS, "Failed to open '%s'", path);
 	}
@@ -127,11 +119,35 @@
 		return 0;
 }
 
-int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
+int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
 {
+	ssize_t read_size = 0;
+
+	git_buf_clear(buf);
+
+	if (git_buf_grow(buf, len + 1) < 0)
+		return -1;
+
+	/* p_read loops internally to read len bytes */
+	read_size = p_read(fd, buf->ptr, len);
+
+	if (read_size != (ssize_t)len) {
+		giterr_set(GITERR_OS, "Failed to read descriptor");
+		return -1;
+	}
+
+	buf->ptr[read_size] = '\0';
+	buf->size = read_size;
+
+	return 0;
+}
+
+int git_futils_readbuffer_updated(
+	git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
+{
 	git_file fd;
-	size_t len;
 	struct stat st;
+	bool changed = false;
 
 	assert(buf && path && *path);
 
@@ -148,41 +164,31 @@
 	}
 
 	/*
-	 * If we were given a time, we only want to read the file if it
-	 * has been modified.
+	 * If we were given a time and/or a size, we only want to read the file
+	 * if it has been modified.
 	 */
-	if (mtime != NULL && *mtime >= st.st_mtime) {
+	if (size && *size != (size_t)st.st_size)
+		changed = true;
+	if (mtime && *mtime != st.st_mtime)
+		changed = true;
+	if (!size && !mtime)
+		changed = true;
+
+	if (!changed) {
 		p_close(fd);
 		return 0;
 	}
 
 	if (mtime != NULL)
 		*mtime = st.st_mtime;
-
-	len = (size_t) st.st_size;
-
-	git_buf_clear(buf);
+	if (size != NULL)
+		*size = (size_t)st.st_size;
 
-	if (git_buf_grow(buf, len + 1) < 0) {
+	if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
 		p_close(fd);
 		return -1;
 	}
 
-	buf->ptr[len] = '\0';
-
-	while (len > 0) {
-		ssize_t read_size = p_read(fd, buf->ptr, len);
-
-		if (read_size < 0) {
-			p_close(fd);
-			giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
-			return -1;
-		}
-
-		len -= read_size;
-		buf->size += read_size;
-	}
-
 	p_close(fd);
 
 	if (updated != NULL)
@@ -193,7 +199,7 @@
 
 int git_futils_readbuffer(git_buf *buf, const char *path)
 {
-	return git_futils_readbuffer_updated(buf, path, NULL, NULL);
+	return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
 }
 
 int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
@@ -239,237 +245,771 @@
 	p_munmap(out);
 }
 
-int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
+int git_futils_mkdir(
+	const char *path,
+	const char *base,
+	mode_t mode,
+	uint32_t flags)
 {
-	int root_path_offset;
+	int error = -1;
 	git_buf make_path = GIT_BUF_INIT;
-	size_t start;
-	char *pp, *sp;
-	bool failed = false;
+	ssize_t root = 0;
+	char lastch, *tail;
 
-	if (base != NULL) {
-		start = strlen(base);
-		if (git_buf_joinpath(&make_path, base, path) < 0)
-			return -1;
-	} else {
-		start = 0;
-		if (git_buf_puts(&make_path, path) < 0)
-			return -1;
+	/* build path and find "root" where we should start calling mkdir */
+	if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
+		return -1;
+
+	if (make_path.size == 0) {
+		giterr_set(GITERR_OS, "Attempt to create empty path");
+		goto fail;
 	}
 
-	pp = make_path.ptr + start;
+	/* remove trailing slashes on path */
+	while (make_path.ptr[make_path.size - 1] == '/') {
+		make_path.size--;
+		make_path.ptr[make_path.size] = '\0';
+	}
 
-	root_path_offset = git_path_root(make_path.ptr);
-	if (root_path_offset > 0)
-		pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
+	/* if we are not supposed to made the last element, truncate it */
+	if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
+		git_buf_rtruncate_at_char(&make_path, '/');
+		flags |= GIT_MKDIR_SKIP_LAST;
+	}
+	if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
+		git_buf_rtruncate_at_char(&make_path, '/');
 
-	while (!failed && (sp = strchr(pp, '/')) != NULL) {
-		if (sp != pp && git_path_isdir(make_path.ptr) == false) {
-			*sp = 0;
+	/* if we are not supposed to make the whole path, reset root */
+	if ((flags & GIT_MKDIR_PATH) == 0)
+		root = git_buf_rfind(&make_path, '/');
 
-			/* Do not choke while trying to recreate an existing directory */
-			if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
-				failed = true;
+	/* clip root to make_path length */
+	if (root >= (ssize_t)make_path.size)
+		root = (ssize_t)make_path.size - 1;
+	if (root < 0)
+		root = 0;
 
-			*sp = '/';
+	tail = & make_path.ptr[root];
+
+	while (*tail) {
+		/* advance tail to include next path component */
+		while (*tail == '/')
+			tail++;
+		while (*tail && *tail != '/')
+			tail++;
+
+		/* truncate path at next component */
+		lastch = *tail;
+		*tail = '\0';
+
+		/* make directory */
+		if (p_mkdir(make_path.ptr, mode) < 0) {
+			int already_exists = 0;
+
+			switch (errno) {
+			case EEXIST:
+				if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
+					!git_path_isdir(make_path.ptr)) {
+					giterr_set(
+						GITERR_OS, "Existing path is not a directory '%s'",
+						make_path.ptr);
+					error = GIT_ENOTFOUND;
+					goto fail;
+				}
+
+				already_exists = 1;
+				break;
+			case ENOSYS:
+				/* Solaris can generate this error if you try to mkdir
+				 * a path which is already a mount point. In that case,
+				 * the path does already exist; but it's not implied by
+				 * the definition of the error, so let's recheck */
+				if (git_path_isdir(make_path.ptr)) {
+					already_exists = 1;
+					break;
+				}
+
+				/* Fall through */
+				errno = ENOSYS;
+			default:
+				giterr_set(GITERR_OS, "Failed to make directory '%s'",
+					make_path.ptr);
+				goto fail;
+			}
+
+			if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
+				giterr_set(GITERR_OS, "Directory already exists '%s'",
+					make_path.ptr);
+				error = GIT_EEXISTS;
+				goto fail;
+			}
 		}
 
-		pp = sp + 1;
-	}
+		/* chmod if requested */
+		if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
+			((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
+		{
+			if (p_chmod(make_path.ptr, mode) < 0) {
+				giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
+					make_path.ptr);
+				goto fail;
+			}
+		}
 
-	if (*pp != '\0' && !failed) {
-		if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
-			failed = true;
+		*tail = lastch;
 	}
 
 	git_buf_free(&make_path);
+	return 0;
 
-	if (failed) {
-		giterr_set(GITERR_OS,
-			"Failed to create directory structure at '%s'", path);
-		return -1;
+fail:
+	git_buf_free(&make_path);
+	return error;
+}
+
+int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
+{
+	return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH);
+}
+
+typedef struct {
+	const char *base;
+	size_t baselen;
+	uint32_t flags;
+	int error;
+} futils__rmdir_data;
+
+static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
+{
+	if (filemsg)
+		giterr_set(GITERR_OS, "Could not remove directory. File '%s' %s",
+				   path, filemsg);
+	else
+		giterr_set(GITERR_OS, "Could not remove directory '%s'", path);
+
+	return -1;
+}
+
+static int futils__rm_first_parent(git_buf *path, const char *ceiling)
+{
+	int error = GIT_ENOTFOUND;
+	struct stat st;
+
+	while (error == GIT_ENOTFOUND) {
+		git_buf_rtruncate_at_char(path, '/');
+
+		if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0)
+			error = 0;
+		else if (p_lstat_posixly(path->ptr, &st) == 0) {
+			if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+				error = p_unlink(path->ptr);
+			else if (!S_ISDIR(st.st_mode))
+				error = -1; /* fail to remove non-regular file */
+		} else if (errno != ENOTDIR)
+			error = -1;
 	}
 
-	return 0;
+	if (error)
+		futils__error_cannot_rmdir(path->ptr, "cannot remove parent");
+
+	return error;
 }
 
-static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
+static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
 {
-	git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
+	struct stat st;
+	futils__rmdir_data *data = opaque;
 
-	assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
-		|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
-		|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
+	if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
+		if (errno == ENOENT)
+			data->error = 0;
+		else if (errno == ENOTDIR) {
+			/* asked to remove a/b/c/d/e and a/b is a normal file */
+			if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
+				data->error = futils__rm_first_parent(path, data->base);
+			else
+				futils__error_cannot_rmdir(
+					path->ptr, "parent is not directory");
+		}
+		else
+			futils__error_cannot_rmdir(path->ptr, "cannot access");
+	}
 
-	if (git_path_isdir(path->ptr) == true) {
-		if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
-			return -1;
+	else if (S_ISDIR(st.st_mode)) {
+		int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
+		if (error < 0)
+			return (error == GIT_EUSER) ? data->error : error;
 
-		if (p_rmdir(path->ptr) < 0) {
-			if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
-				return 0;
+		data->error = p_rmdir(path->ptr);
 
-			giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
-			return -1;
+		if (data->error < 0) {
+			if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
+				(errno == ENOTEMPTY || errno == EEXIST))
+				data->error = 0;
+			else
+				futils__error_cannot_rmdir(path->ptr, NULL);
 		}
+	}
 
-		return 0;
+	else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
+		data->error = p_unlink(path->ptr);
+
+		if (data->error < 0)
+			futils__error_cannot_rmdir(path->ptr, "cannot be removed");
 	}
 
-	if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
-		if (p_unlink(path->ptr) < 0) {
-			giterr_set(GITERR_OS, "Could not remove directory.  File '%s' cannot be removed", path->ptr);
-			return -1;
-		}
+	else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
+		data->error = futils__error_cannot_rmdir(path->ptr, "still present");
 
-		return 0;
+	return data->error;
+}
+
+static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
+{
+	futils__rmdir_data *data = opaque;
+	int error;
+
+	if (git_buf_len(path) <= data->baselen)
+		return GIT_ITEROVER;
+
+	error = p_rmdir(git_buf_cstr(path));
+
+	if (error) {
+		int en = errno;
+
+		if (en == ENOENT || en == ENOTDIR) {
+			giterr_clear();
+			error = 0;
+		} else if (en == ENOTEMPTY || en == EEXIST) {
+			giterr_clear();
+			error = GIT_ITEROVER;
+		} else {
+			futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
+		}
 	}
 
-	if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
-		giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
+	return error;
+}
+
+int git_futils_rmdir_r(
+	const char *path, const char *base, uint32_t flags)
+{
+	int error;
+	git_buf fullpath = GIT_BUF_INIT;
+	futils__rmdir_data data;
+
+	/* build path and find "root" where we should start calling mkdir */
+	if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
 		return -1;
+
+	data.base    = base ? base : "";
+	data.baselen = base ? strlen(base) : 0;
+	data.flags   = flags;
+	data.error   = 0;
+
+	error = futils__rmdir_recurs_foreach(&data, &fullpath);
+
+	/* remove now-empty parents if requested */
+	if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) {
+		error = git_path_walk_up(
+			&fullpath, base, futils__rmdir_empty_parent, &data);
+
+		if (error == GIT_ITEROVER)
+			error = 0;
 	}
 
-	return 0;
+	git_buf_free(&fullpath);
+
+	return error;
 }
 
-int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
+int git_futils_cleanupdir_r(const char *path)
 {
 	int error;
-	git_buf p = GIT_BUF_INIT;
+	git_buf fullpath = GIT_BUF_INIT;
+	futils__rmdir_data data;
 
-	error = git_buf_sets(&p, path);
-	if (!error)
-		error = _rmdir_recurs_foreach(&removal_type, &p);
-	git_buf_free(&p);
+	if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
+		goto clean_up;
+
+	data.base    = "";
+	data.baselen = 0;
+	data.flags   = GIT_RMDIR_REMOVE_FILES;
+	data.error   = 0;
+
+	if (!git_path_exists(path)) {
+		giterr_set(GITERR_OS, "Path does not exist: %s" , path);
+		error = GIT_ERROR;
+		goto clean_up;
+	}
+
+	if (!git_path_isdir(path)) {
+		giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
+		error = GIT_ERROR;
+		goto clean_up;
+	}
+
+	error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
+	if (error == GIT_EUSER)
+		error = data.error;
+
+clean_up:
+	git_buf_free(&fullpath);
 	return error;
 }
 
-int git_futils_find_global_file(git_buf *path, const char *filename)
+
+static int git_futils_guess_system_dirs(git_buf *out)
 {
-	const char *home = getenv("HOME");
+#ifdef GIT_WIN32
+	return git_win32__find_system_dirs(out);
+#else
+	return git_buf_sets(out, "/etc");
+#endif
+}
 
+static int git_futils_guess_global_dirs(git_buf *out)
+{
 #ifdef GIT_WIN32
-	if (home == NULL)
-		home = getenv("USERPROFILE");
+	return git_win32__find_global_dirs(out);
+#else
+	return git_buf_sets(out, getenv("HOME"));
 #endif
+}
 
-	if (home == NULL) {
-		giterr_set(GITERR_OS, "Global file lookup failed. "
-			"Cannot locate the user's home directory");
-		return -1;
-	}
+static int git_futils_guess_xdg_dirs(git_buf *out)
+{
+#ifdef GIT_WIN32
+	return git_win32__find_xdg_dirs(out);
+#else
+	const char *env = NULL;
 
-	if (git_buf_joinpath(path, home, filename) < 0)
-		return -1;
+	if ((env = getenv("XDG_CONFIG_HOME")) != NULL)
+		return git_buf_joinpath(out, env, "git");
+	else if ((env = getenv("HOME")) != NULL)
+		return git_buf_joinpath(out, env, ".config/git");
 
-	if (git_path_exists(path->ptr) == false) {
-		git_buf_clear(path);
-		return GIT_ENOTFOUND;
+	git_buf_clear(out);
+	return 0;
+#endif
+}
+
+typedef int (*git_futils_dirs_guess_cb)(git_buf *out);
+
+static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] =
+	{ GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
+
+static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
+	git_futils_guess_system_dirs,
+	git_futils_guess_global_dirs,
+	git_futils_guess_xdg_dirs,
+};
+
+static int git_futils_check_selector(git_futils_dir_t which)
+{
+	if (which < GIT_FUTILS_DIR__MAX)
+		return 0;
+	giterr_set(GITERR_INVALID, "config directory selector out of range");
+	return -1;
+}
+
+int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which)
+{
+	assert(out);
+
+	*out = NULL;
+
+	GITERR_CHECK_ERROR(git_futils_check_selector(which));
+
+	if (!git_buf_len(&git_futils__dirs[which]))
+		GITERR_CHECK_ERROR(
+			git_futils__dir_guess[which](&git_futils__dirs[which]));
+
+	*out = &git_futils__dirs[which];
+	return 0;
+}
+
+int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which)
+{
+	const git_buf *path = NULL;
+
+	GITERR_CHECK_ERROR(git_futils_check_selector(which));
+	GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which));
+
+	if (!out || path->size >= outlen) {
+		giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path");
+		return GIT_EBUFS;
 	}
 
+	git_buf_copy_cstr(out, outlen, path);
 	return 0;
 }
 
-#ifdef GIT_WIN32
-typedef struct {
-	wchar_t *path;
-	DWORD len;
-} win32_path;
+#define PATH_MAGIC "$PATH"
 
-static const win32_path *win32_system_root(void)
+int git_futils_dirs_set(git_futils_dir_t which, const char *search_path)
 {
-	static win32_path s_root = { 0, 0 };
+	const char *expand_path = NULL;
+	git_buf merge = GIT_BUF_INIT;
 
-	if (s_root.path == NULL) {
-		const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
+	GITERR_CHECK_ERROR(git_futils_check_selector(which));
 
-		s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
-		if (s_root.len <= 0) {
-			giterr_set(GITERR_OS, "Failed to expand environment strings");
-			return NULL;
-		}
+	if (search_path != NULL)
+		expand_path = strstr(search_path, PATH_MAGIC);
 
-		s_root.path = git__calloc(s_root.len, sizeof(wchar_t));
-		if (s_root.path == NULL)
-			return NULL;
+	/* init with default if not yet done and needed (ignoring error) */
+	if ((!search_path || expand_path) &&
+		!git_buf_len(&git_futils__dirs[which]))
+		git_futils__dir_guess[which](&git_futils__dirs[which]);
 
-		if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
-			giterr_set(GITERR_OS, "Failed to expand environment strings");
-			git__free(s_root.path);
-			s_root.path = NULL;
-			return NULL;
-		}
-	}
+	/* if $PATH is not referenced, then just set the path */
+	if (!expand_path)
+		return git_buf_sets(&git_futils__dirs[which], search_path);
 
-	return &s_root;
+	/* otherwise set to join(before $PATH, old value, after $PATH) */
+	if (expand_path > search_path)
+		git_buf_set(&merge, search_path, expand_path - search_path);
+
+	if (git_buf_len(&git_futils__dirs[which]))
+		git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR,
+			merge.ptr, git_futils__dirs[which].ptr);
+
+	expand_path += strlen(PATH_MAGIC);
+	if (*expand_path)
+		git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
+
+	git_buf_swap(&git_futils__dirs[which], &merge);
+	git_buf_free(&merge);
+
+	return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0;
 }
 
-static int win32_find_system_file(git_buf *path, const char *filename)
+void git_futils_dirs_free(void)
 {
-	int error = 0;
-	const win32_path *root = win32_system_root();
+	int i;
+	for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i)
+		git_buf_free(&git_futils__dirs[i]);
+}
+
+static int git_futils_find_in_dirlist(
+	git_buf *path, const char *name, git_futils_dir_t which, const char *label)
+{
 	size_t len;
-	wchar_t *file_utf16 = NULL, *scan;
-	char *file_utf8 = NULL;
+	const char *scan, *next = NULL;
+	const git_buf *syspath;
 
-	if (!root || !filename || (len = strlen(filename)) == 0)
-		return GIT_ENOTFOUND;
+	GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which));
 
-	/* allocate space for wchar_t path to file */
-	file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
-	GITERR_CHECK_ALLOC(file_utf16);
+	for (scan = git_buf_cstr(syspath); scan; scan = next) {
+		for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR);
+			 next && next > scan && next[-1] == '\\';
+			 next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR))
+			/* find unescaped separator or end of string */;
 
-	/* append root + '\\' + filename as wchar_t */
-	memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
+		len = next ? (size_t)(next++ - scan) : strlen(scan);
+		if (!len)
+			continue;
 
-	if (*filename == '/' || *filename == '\\')
-		filename++;
+		GITERR_CHECK_ERROR(git_buf_set(path, scan, len));
+		GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name));
 
-	if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
-		(int)len + 1) {
-		error = -1;
-		goto cleanup;
+		if (git_path_exists(path->ptr))
+			return 0;
 	}
 
-	for (scan = file_utf16; *scan; scan++)
-		if (*scan == L'/')
-			*scan = L'\\';
+	git_buf_clear(path);
+	giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name);
+	return GIT_ENOTFOUND;
+}
 
-	/* check access */
-	if (_waccess(file_utf16, F_OK) < 0) {
-		error = GIT_ENOTFOUND;
-		goto cleanup;
+int git_futils_find_system_file(git_buf *path, const char *filename)
+{
+	return git_futils_find_in_dirlist(
+		path, filename, GIT_FUTILS_DIR_SYSTEM, "system");
+}
+
+int git_futils_find_global_file(git_buf *path, const char *filename)
+{
+	return git_futils_find_in_dirlist(
+		path, filename, GIT_FUTILS_DIR_GLOBAL, "global");
+}
+
+int git_futils_find_xdg_file(git_buf *path, const char *filename)
+{
+	return git_futils_find_in_dirlist(
+		path, filename, GIT_FUTILS_DIR_XDG, "global/xdg");
+}
+
+int git_futils_fake_symlink(const char *old, const char *new)
+{
+	int retcode = GIT_ERROR;
+	int fd = git_futils_creat_withpath(new, 0755, 0644);
+	if (fd >= 0) {
+		retcode = p_write(fd, old, strlen(old));
+		p_close(fd);
 	}
+	return retcode;
+}
 
-	/* convert to utf8 */
-	if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
+static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
+{
+	int error = 0;
+	char buffer[4096];
+	ssize_t len = 0;
+
+	while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
+		/* p_write() does not have the same semantics as write().  It loops
+		 * internally and will return 0 when it has completed writing.
+		 */
+		error = p_write(ofd, buffer, len);
+
+	if (len < 0) {
+		giterr_set(GITERR_OS, "Read error while copying file");
+		error = (int)len;
+	}
+
+	if (close_fd_when_done) {
+		p_close(ifd);
+		p_close(ofd);
+	}
+
+	return error;
+}
+
+int git_futils_cp(const char *from, const char *to, mode_t filemode)
+{
+	int ifd, ofd;
+
+	if ((ifd = git_futils_open_ro(from)) < 0)
+		return ifd;
+
+	if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
+		if (errno == ENOENT || errno == ENOTDIR)
+			ofd = GIT_ENOTFOUND;
+		giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
+		p_close(ifd);
+		return ofd;
+	}
+
+	return cp_by_fd(ifd, ofd, true);
+}
+
+static int cp_link(const char *from, const char *to, size_t link_size)
+{
+	int error = 0;
+	ssize_t read_len;
+	char *link_data = git__malloc(link_size + 1);
+	GITERR_CHECK_ALLOC(link_data);
+
+	read_len = p_readlink(from, link_data, link_size);
+	if (read_len != (ssize_t)link_size) {
+		giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", from);
 		error = -1;
+	}
 	else {
-		git_path_mkposix(file_utf8);
-		git_buf_attach(path, file_utf8, 0);
+		link_data[read_len] = '\0';
+
+		if (p_symlink(link_data, to) < 0) {
+			giterr_set(GITERR_OS, "Could not symlink '%s' as '%s'",
+				link_data, to);
+			error = -1;
+		}
 	}
 
-cleanup:
-	git__free(file_utf16);
+	git__free(link_data);
+	return error;
+}
 
+typedef struct {
+	const char *to_root;
+	git_buf to;
+	ssize_t from_prefix;
+	uint32_t flags;
+	uint32_t mkdir_flags;
+	mode_t dirmode;
+} cp_r_info;
+
+#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
+
+static int _cp_r_mkdir(cp_r_info *info, git_buf *from)
+{
+	int error = 0;
+
+	/* create root directory the first time we need to create a directory */
+	if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
+		error = git_futils_mkdir(
+			info->to_root, NULL, info->dirmode,
+			(info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
+
+		info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
+	}
+
+	/* create directory with root as base to prevent excess chmods */
+	if (!error)
+		error = git_futils_mkdir(
+			from->ptr + info->from_prefix, info->to_root,
+			info->dirmode, info->mkdir_flags);
+
 	return error;
 }
-#endif
 
-int git_futils_find_system_file(git_buf *path, const char *filename)
+static int _cp_r_callback(void *ref, git_buf *from)
 {
-	if (git_buf_joinpath(path, "/etc", filename) < 0)
+	int error = 0;
+	cp_r_info *info = ref;
+	struct stat from_st, to_st;
+	bool exists = false;
+
+	if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
+		from->ptr[git_path_basename_offset(from)] == '.')
+		return 0;
+
+	if (git_buf_joinpath(
+			&info->to, info->to_root, from->ptr + info->from_prefix) < 0)
 		return -1;
 
-	if (git_path_exists(path->ptr) == true)
+	if (p_lstat(info->to.ptr, &to_st) < 0) {
+		if (errno != ENOENT && errno != ENOTDIR) {
+			giterr_set(GITERR_OS,
+				"Could not access %s while copying files", info->to.ptr);
+			return -1;
+		}
+	} else
+		exists = true;
+
+	if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
+		return error;
+
+	if (S_ISDIR(from_st.st_mode)) {
+		mode_t oldmode = info->dirmode;
+
+		/* if we are not chmod'ing, then overwrite dirmode */
+		if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
+			info->dirmode = from_st.st_mode;
+
+		/* make directory now if CREATE_EMPTY_DIRS is requested and needed */
+		if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
+			error = _cp_r_mkdir(info, from);
+
+		/* recurse onto target directory */
+		if (!error && (!exists || S_ISDIR(to_st.st_mode)))
+			error = git_path_direach(from, _cp_r_callback, info);
+
+		if (oldmode != 0)
+			info->dirmode = oldmode;
+
+		return error;
+	}
+
+	if (exists) {
+		if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
+			return 0;
+
+		if (p_unlink(info->to.ptr) < 0) {
+			giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
+				info->to.ptr);
+			return -1;
+		}
+	}
+
+	/* Done if this isn't a regular file or a symlink */
+	if (!S_ISREG(from_st.st_mode) &&
+		(!S_ISLNK(from_st.st_mode) ||
+		 (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
 		return 0;
 
-	git_buf_clear(path);
+	/* Make container directory on demand if needed */
+	if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
+		(error = _cp_r_mkdir(info, from)) < 0)
+		return error;
 
-#ifdef GIT_WIN32
-	return win32_find_system_file(path, filename);
-#else
-	return GIT_ENOTFOUND;
-#endif
+	/* make symlink or regular file */
+	if (S_ISLNK(from_st.st_mode))
+		error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
+	else {
+		mode_t usemode = from_st.st_mode;
+
+		if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
+			usemode = (usemode & 0111) ? 0777 : 0666;
+
+		error = git_futils_cp(from->ptr, info->to.ptr, usemode);
+	}
+
+	return error;
+}
+
+int git_futils_cp_r(
+	const char *from,
+	const char *to,
+	uint32_t flags,
+	mode_t dirmode)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+	cp_r_info info;
+
+	if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */
+		return -1;
+
+	info.to_root = to;
+	info.flags   = flags;
+	info.dirmode = dirmode;
+	info.from_prefix = path.size;
+	git_buf_init(&info.to, 0);
+
+	/* precalculate mkdir flags */
+	if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
+		/* if not creating empty dirs, then use mkdir to create the path on
+		 * demand right before files are copied.
+		 */
+		info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
+		if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
+			info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
+	} else {
+		/* otherwise, we will do simple mkdir as directories are encountered */
+		info.mkdir_flags =
+			((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
+	}
+
+	error = _cp_r_callback(&info, &path);
+
+	git_buf_free(&path);
+	git_buf_free(&info.to);
+
+	return error;
+}
+
+int git_futils_filestamp_check(
+	git_futils_filestamp *stamp, const char *path)
+{
+	struct stat st;
+
+	/* if the stamp is NULL, then always reload */
+	if (stamp == NULL)
+		return 1;
+
+	if (p_stat(path, &st) < 0)
+		return GIT_ENOTFOUND;
+
+	if (stamp->mtime == (git_time_t)st.st_mtime &&
+		stamp->size  == (git_off_t)st.st_size   &&
+		stamp->ino   == (unsigned int)st.st_ino)
+		return 0;
+
+	stamp->mtime = (git_time_t)st.st_mtime;
+	stamp->size  = (git_off_t)st.st_size;
+	stamp->ino   = (unsigned int)st.st_ino;
+
+	return 1;
+}
+
+void git_futils_filestamp_set(
+	git_futils_filestamp *target, const git_futils_filestamp *source)
+{
+	assert(target);
+
+	if (source)
+		memcpy(target, source, sizeof(*target));
+	else
+		memset(target, 0, sizeof(*target));
 }
diff --git a/libgit2/src/fileops.h b/libgit2/src/fileops.h
--- a/libgit2/src/fileops.h
+++ b/libgit2/src/fileops.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -18,7 +18,9 @@
  * Read whole files into an in-memory buffer for processing
  */
 extern int git_futils_readbuffer(git_buf *obj, const char *path);
-extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
+extern int git_futils_readbuffer_updated(
+	git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
+extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
 
 /**
  * File utils
@@ -49,34 +51,99 @@
 
 /**
  * Create a path recursively
+ *
+ * If a base parameter is being passed, it's expected to be valued with a
+ * path pointing to an already existing directory.
  */
 extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
 
 /**
+ * Flags to pass to `git_futils_mkdir`.
+ *
+ * * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists.
+ * * GIT_MKDIR_PATH says to make all components in the path.
+ * * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
+ * * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
+ * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
+ * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path
+ * * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
+ *
+ * Note that the chmod options will be executed even if the directory already
+ * exists, unless GIT_MKDIR_EXCL is given.
+ */
+typedef enum {
+	GIT_MKDIR_EXCL = 1,
+	GIT_MKDIR_PATH = 2,
+	GIT_MKDIR_CHMOD = 4,
+	GIT_MKDIR_CHMOD_PATH = 8,
+	GIT_MKDIR_SKIP_LAST = 16,
+	GIT_MKDIR_SKIP_LAST2 = 32,
+	GIT_MKDIR_VERIFY_DIR = 64,
+} git_futils_mkdir_flags;
+
+/**
+ * Create a directory or entire path.
+ *
+ * This makes a directory (and the entire path leading up to it if requested),
+ * and optionally chmods the directory immediately after (or each part of the
+ * path if requested).
+ *
+ * @param path The path to create.
+ * @param base Root for relative path.  These directories will never be made.
+ * @param mode The mode to use for created directories.
+ * @param flags Combination of the mkdir flags above.
+ * @return 0 on success, else error code
+ */
+extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags);
+
+/**
  * Create all the folders required to contain
  * the full path of a file
  */
 extern int git_futils_mkpath2file(const char *path, const mode_t mode);
 
+/**
+ * Flags to pass to `git_futils_rmdir_r`.
+ *
+ * * GIT_RMDIR_EMPTY_HIERARCHY - the default; remove hierarchy of empty
+ *       dirs and generate error if any files are found.
+ * * GIT_RMDIR_REMOVE_FILES    - attempt to remove files in the hierarchy.
+ * * GIT_RMDIR_SKIP_NONEMPTY   - skip non-empty directories with no error.
+ * * GIT_RMDIR_EMPTY_PARENTS   - remove containing directories up to base
+ *       if removing this item leaves them empty
+ * * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
+ *
+ * The old values translate into the new as follows:
+ *
+ * * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
+ * * GIT_DIRREMOVAL_FILES_AND_DIRS  ~= GIT_RMDIR_REMOVE_FILES
+ * * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
+ */
 typedef enum {
-	GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
-	GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
-	GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
-} git_directory_removal_type;
+	GIT_RMDIR_EMPTY_HIERARCHY = 0,
+	GIT_RMDIR_REMOVE_FILES    = (1 << 0),
+	GIT_RMDIR_SKIP_NONEMPTY   = (1 << 1),
+	GIT_RMDIR_EMPTY_PARENTS   = (1 << 2),
+	GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
+} git_futils_rmdir_flags;
 
 /**
  * Remove path and any files and directories beneath it.
  *
- * @param path Path to to top level directory to process.
- *
- * @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
- * of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS
- * to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove
- * empty directories (no failure on file encounter).
+ * @param path Path to the top level directory to process.
+ * @param base Root for relative path.
+ * @param flags Combination of git_futils_rmdir_flags values
+ * @return 0 on success; -1 on error.
+ */
+extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
+
+/**
+ * Remove all files and directories beneath the specified path.
  *
+ * @param path Path to the top level directory to process.
  * @return 0 on success; -1 on error.
  */
-extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
+extern int git_futils_cleanupdir_r(const char *path);
 
 /**
  * Create and open a temporary file with a `_git2_` suffix.
@@ -92,6 +159,58 @@
 extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
 
 /**
+ * Copy a file
+ *
+ * The filemode will be used for the newly created file.
+ */
+extern int git_futils_cp(
+	const char *from,
+	const char *to,
+	mode_t filemode);
+
+/**
+ * Flags that can be passed to `git_futils_cp_r`.
+ *
+ * - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no
+ *   files under them (otherwise directories will only be created lazily
+ *   when a file inside them is copied).
+ * - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored.
+ * - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored.
+ * - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content,
+ *   otherwise they are silently skipped.
+ * - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode`
+ * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
+ *   source file to the target; with this flag, always use 0666 (or 0777 if
+ *   source has exec bits set) for target.
+ */
+typedef enum {
+	GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
+	GIT_CPDIR_COPY_SYMLINKS     = (1u << 1),
+	GIT_CPDIR_COPY_DOTFILES     = (1u << 2),
+	GIT_CPDIR_OVERWRITE         = (1u << 3),
+	GIT_CPDIR_CHMOD_DIRS        = (1u << 4),
+	GIT_CPDIR_SIMPLE_TO_MODE    = (1u << 5),
+} git_futils_cpdir_flags;
+
+/**
+ * Copy a directory tree.
+ *
+ * This copies directories and files from one root to another.  You can
+ * pass a combinationof GIT_CPDIR flags as defined above.
+ *
+ * If you pass the CHMOD flag, then the dirmode will be applied to all
+ * directories that are created during the copy, overiding the natural
+ * permissions.  If you do not pass the CHMOD flag, then the dirmode
+ * will actually be copied from the source files and the `dirmode` arg
+ * will be ignored.
+ */
+extern int git_futils_cp_r(
+	const char *from,
+	const char *to,
+	uint32_t flags,
+	mode_t dirmode);
+
+/**
  * Open a file readonly and set error if needed.
  */
 extern int git_futils_open_ro(const char *path);
@@ -157,23 +276,120 @@
  *
  * @param pathbuf buffer to write the full path into
  * @param filename name of file to find in the home directory
- * @return
- * - 0 if found;
- * - GIT_ENOTFOUND if not found;
- * - -1 on an unspecified OS related error.
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
  */
 extern int git_futils_find_global_file(git_buf *path, const char *filename);
 
 /**
+ * Find an "XDG" file (i.e. one in user's XDG config path).
+ *
+ * @param pathbuf buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
+
+/**
  * Find a "system" file (i.e. one shared for all users of the system).
  *
  * @param pathbuf buffer to write the full path into
  * @param filename name of file to find in the home directory
- * @return
- * - 0 if found;
- * - GIT_ENOTFOUND if not found;
- * - -1 on an unspecified OS related error.
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
  */
 extern int git_futils_find_system_file(git_buf *path, const char *filename);
+
+typedef enum {
+	GIT_FUTILS_DIR_SYSTEM = 0,
+	GIT_FUTILS_DIR_GLOBAL = 1,
+	GIT_FUTILS_DIR_XDG    = 2,
+	GIT_FUTILS_DIR__MAX   = 3,
+} git_futils_dir_t;
+
+/**
+ * Get the search path for global/system/xdg files
+ *
+ * @param out pointer to git_buf containing search path
+ * @param which which list of paths to return
+ * @return 0 on success, <0 on failure
+ */
+extern int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which);
+
+/**
+ * Get search path into a preallocated buffer
+ *
+ * @param out String buffer to write into
+ * @param outlen Size of string buffer
+ * @param which Which search path to return
+ * @return 0 on success, GIT_EBUFS if out is too small, <0 on other failure
+ */
+
+extern int git_futils_dirs_get_str(
+	char *out, size_t outlen, git_futils_dir_t which);
+
+/**
+ * Set search paths for global/system/xdg files
+ *
+ * The first occurrence of the magic string "$PATH" in the new value will
+ * be replaced with the old value of the search path.
+ *
+ * @param which Which search path to modify
+ * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR)
+ * @return 0 on success, <0 on failure (allocation error)
+ */
+extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths);
+
+/**
+ * Release / reset all search paths
+ */
+extern void git_futils_dirs_free(void);
+
+/**
+ * Create a "fake" symlink (text file containing the target path).
+ *
+ * @param new symlink file to be created
+ * @param old original symlink target
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fake_symlink(const char *new, const char *old);
+
+/**
+ * A file stamp represents a snapshot of information about a file that can
+ * be used to test if the file changes.  This portable implementation is
+ * based on stat data about that file, but it is possible that OS specific
+ * versions could be implemented in the future.
+ */
+typedef struct {
+	git_time_t mtime;
+	git_off_t  size;
+	unsigned int ino;
+} git_futils_filestamp;
+
+/**
+ * Compare stat information for file with reference info.
+ *
+ * This function updates the file stamp to current data for the given path
+ * and returns 0 if the file is up-to-date relative to the prior setting or
+ * 1 if the file has been changed.  (This also may return GIT_ENOTFOUND if
+ * the file doesn't exist.)
+ *
+ * @param stamp File stamp to be checked
+ * @param path Path to stat and check if changed
+ * @return 0 if up-to-date, 1 if out-of-date, <0 on error
+ */
+extern int git_futils_filestamp_check(
+	git_futils_filestamp *stamp, const char *path);
+
+/**
+ * Set or reset file stamp data
+ *
+ * This writes the target file stamp.  If the source is NULL, this will set
+ * the target stamp to values that will definitely be out of date.  If the
+ * source is not NULL, this copies the source values to the target.
+ *
+ * @param tgt File stamp to write to
+ * @param src File stamp to copy from or NULL to clear the target
+ */
+extern void git_futils_filestamp_set(
+	git_futils_filestamp *tgt, const git_futils_filestamp *src);
 
 #endif /* INCLUDE_fileops_h__ */
diff --git a/libgit2/src/filter.c b/libgit2/src/filter.c
--- a/libgit2/src/filter.c
+++ b/libgit2/src/filter.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,79 +11,7 @@
 #include "filter.h"
 #include "repository.h"
 #include "git2/config.h"
-
-/* Tweaked from Core Git. I wonder what we could use this for... */
-void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
-{
-	size_t i;
-
-	memset(stats, 0, sizeof(*stats));
-
-	for (i = 0; i < git_buf_len(text); i++) {
-		unsigned char c = text->ptr[i];
-
-		if (c == '\r') {
-			stats->cr++;
-
-			if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
-				stats->crlf++;
-		}
-
-		else if (c == '\n')
-			stats->lf++;
-
-		else if (c == 0x85)
-			/* Unicode CR+LF */
-			stats->crlf++;
-
-		else if (c == 127)
-			/* DEL */
-			stats->nonprintable++;
-
-		else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
-			switch (c) {
-				/* BS, HT, ESC and FF */
-			case '\b': case '\t': case '\033': case '\014':
-				stats->printable++;
-				break;
-			case 0:
-				stats->nul++;
-				/* fall through */
-			default:
-				stats->nonprintable++;
-			}
-		}
-
-		else
-			stats->printable++;
-	}
-
-	/* If file ends with EOF then don't count this EOF as non-printable. */
-	if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
-		stats->nonprintable--;
-}
-
-/*
- * Fresh from Core Git
- */
-int git_text_is_binary(git_text_stats *stats)
-{
-	if (stats->nul)
-		return 1;
-
-	if ((stats->printable >> 7) < stats->nonprintable)
-		return 1;
-	/*
-	 * Other heuristics? Average line length might be relevant,
-	 * as might LF vs CR vs CRLF counts..
-	 *
-	 * NOTE! It might be normal to have a low ratio of CRLF to LF
-	 * (somebody starts with a LF-only file and edits it with an editor
-	 * that adds CRLF only to lines that are added..). But do  we
-	 * want to support CR-only? Probably not.
-	 */
-	return 0;
-}
+#include "blob.h"
 
 int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
 {
@@ -95,8 +23,9 @@
 		if (error < 0)
 			return error;
 	} else {
-		giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
-		return -1;
+		error = git_filter_add__crlf_to_workdir(filters, repo, path);
+		if (error < 0)
+			return error;
 	}
 
 	return (int)filters->length;
@@ -119,7 +48,8 @@
 
 int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
 {
-	unsigned int i, src;
+	size_t i;
+	unsigned int src;
 	git_buf *dbuffer[2];
 
 	dbuffer[0] = source;
@@ -162,4 +92,3 @@
 
 	return 0;
 }
-
diff --git a/libgit2/src/filter.h b/libgit2/src/filter.h
--- a/libgit2/src/filter.h
+++ b/libgit2/src/filter.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -31,14 +31,6 @@
 	GIT_CRLF_AUTO,
 } git_crlf_t;
 
-typedef struct {
-	/* NUL, CR, LF and CRLF counts */
-	unsigned int nul, cr, lf, crlf;
-
-	/* These are just approximations! */
-	unsigned int printable, nonprintable;
-} git_text_stats;
-
 /*
  * FILTER API
  */
@@ -96,24 +88,7 @@
 /* Strip CRLF, from Worktree to ODB */
 extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
 
-
-/*
- * PLAINTEXT API
- */
-
-/*
- * Gather stats for a piece of text
- *
- * Fill the `stats` structure with information on the number of
- * unreadable characters, carriage returns, etc, so it can be
- * used in heuristics.
- */
-extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
-
-/*
- * Process `git_text_stats` data generated by `git_text_stat` to see
- * if it qualifies as a binary file
- */
-extern int git_text_is_binary(git_text_stats *stats);
+/* Add CRLF, from ODB to worktree */
+extern int git_filter_add__crlf_to_workdir(git_vector *filters, git_repository *repo, const char *path);
 
 #endif
diff --git a/libgit2/src/fnmatch.c b/libgit2/src/fnmatch.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fnmatch.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+/*
+ * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
+ * Compares a filename or pathname to a pattern.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "fnmatch.h"
+
+#define EOS		'\0'
+
+#define RANGE_MATCH		1
+#define RANGE_NOMATCH		0
+#define RANGE_ERROR		(-1)
+
+static int rangematch(const char *, char, int, char **);
+
+static int
+p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs)
+{
+		const char *stringstart;
+		char *newp;
+		char c, test;
+
+		if (recurs-- == 0)
+				return FNM_NORES;
+
+		for (stringstart = string;;)
+				switch (c = *pattern++) {
+				case EOS:
+						if ((flags & FNM_LEADING_DIR) && *string == '/')
+								return (0);
+						return (*string == EOS ? 0 : FNM_NOMATCH);
+				case '?':
+						if (*string == EOS)
+								return (FNM_NOMATCH);
+						if (*string == '/' && (flags & FNM_PATHNAME))
+								return (FNM_NOMATCH);
+						if (*string == '.' && (flags & FNM_PERIOD) &&
+							(string == stringstart ||
+							((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+								return (FNM_NOMATCH);
+						++string;
+						break;
+				case '*':
+						c = *pattern;
+						/* Collapse multiple stars. */
+						while (c == '*')
+								c = *++pattern;
+
+						if (*string == '.' && (flags & FNM_PERIOD) &&
+							(string == stringstart ||
+							((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+								return (FNM_NOMATCH);
+
+						/* Optimize for pattern with * at end or before /. */
+						if (c == EOS) {
+								if (flags & FNM_PATHNAME)
+										return ((flags & FNM_LEADING_DIR) ||
+											strchr(string, '/') == NULL ?
+											0 : FNM_NOMATCH);
+								else
+										return (0);
+						} else if (c == '/' && (flags & FNM_PATHNAME)) {
+								if ((string = strchr(string, '/')) == NULL)
+										return (FNM_NOMATCH);
+								break;
+						}
+
+						/* General case, use recursion. */
+						while ((test = *string) != EOS) {
+								int e;
+
+								e = p_fnmatchx(pattern, string, flags & ~FNM_PERIOD, recurs);
+								if (e != FNM_NOMATCH)
+										return e;
+								if (test == '/' && (flags & FNM_PATHNAME))
+										break;
+								++string;
+						}
+						return (FNM_NOMATCH);
+				case '[':
+						if (*string == EOS)
+								return (FNM_NOMATCH);
+						if (*string == '/' && (flags & FNM_PATHNAME))
+								return (FNM_NOMATCH);
+						if (*string == '.' && (flags & FNM_PERIOD) &&
+							(string == stringstart ||
+							((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+								return (FNM_NOMATCH);
+
+						switch (rangematch(pattern, *string, flags, &newp)) {
+						case RANGE_ERROR:
+								/* not a good range, treat as normal text */
+								goto normal;
+						case RANGE_MATCH:
+								pattern = newp;
+								break;
+						case RANGE_NOMATCH:
+								return (FNM_NOMATCH);
+						}
+						++string;
+						break;
+				case '\\':
+						if (!(flags & FNM_NOESCAPE)) {
+								if ((c = *pattern++) == EOS) {
+										c = '\\';
+										--pattern;
+								}
+						}
+						/* FALLTHROUGH */
+				default:
+				normal:
+						if (c != *string && !((flags & FNM_CASEFOLD) &&
+									(tolower((unsigned char)c) ==
+									tolower((unsigned char)*string))))
+								return (FNM_NOMATCH);
+						++string;
+						break;
+				}
+		/* NOTREACHED */
+}
+
+static int
+rangematch(const char *pattern, char test, int flags, char **newp)
+{
+		int negate, ok;
+		char c, c2;
+
+		/*
+			* A bracket expression starting with an unquoted circumflex
+			* character produces unspecified results (IEEE 1003.2-1992,
+			* 3.13.2). This implementation treats it like '!', for
+			* consistency with the regular expression syntax.
+			* J.T. Conklin (conklin@ngai.kaleida.com)
+			*/
+		if ((negate = (*pattern == '!' || *pattern == '^')) != 0)
+				++pattern;
+
+		if (flags & FNM_CASEFOLD)
+				test = (char)tolower((unsigned char)test);
+
+		/*
+			* A right bracket shall lose its special meaning and represent
+			* itself in a bracket expression if it occurs first in the list.
+			* -- POSIX.2 2.8.3.2
+			*/
+		ok = 0;
+		c = *pattern++;
+		do {
+				if (c == '\\' && !(flags & FNM_NOESCAPE))
+						c = *pattern++;
+				if (c == EOS)
+						return (RANGE_ERROR);
+				if (c == '/' && (flags & FNM_PATHNAME))
+						return (RANGE_NOMATCH);
+				if ((flags & FNM_CASEFOLD))
+						c = (char)tolower((unsigned char)c);
+				if (*pattern == '-'
+					&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
+						pattern += 2;
+						if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+								c2 = *pattern++;
+						if (c2 == EOS)
+								return (RANGE_ERROR);
+						if (flags & FNM_CASEFOLD)
+								c2 = (char)tolower((unsigned char)c2);
+						if (c <= test && test <= c2)
+								ok = 1;
+				} else if (c == test)
+						ok = 1;
+		} while ((c = *pattern++) != ']');
+
+		*newp = (char *)pattern;
+		return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
+
+int
+p_fnmatch(const char *pattern, const char *string, int flags)
+{
+		return p_fnmatchx(pattern, string, flags, 64);
+}
+
diff --git a/libgit2/src/fnmatch.h b/libgit2/src/fnmatch.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fnmatch.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_fnmatch__compat_h__
+#define INCLUDE_fnmatch__compat_h__
+
+#include "common.h"
+
+#define FNM_NOMATCH		1		/* Match failed. */
+#define FNM_NOSYS		2		/* Function not supported (unused). */
+#define	FNM_NORES		3		/* Out of resources */
+
+#define FNM_NOESCAPE	0x01		/* Disable backslash escaping. */
+#define FNM_PATHNAME	0x02		/* Slash must be matched by slash. */
+#define FNM_PERIOD		0x04		/* Period must be matched by period. */
+#define FNM_LEADING_DIR 0x08		/* Ignore /<tail> after Imatch. */
+#define FNM_CASEFOLD	0x10		/* Case insensitive search. */
+
+#define FNM_IGNORECASE	FNM_CASEFOLD
+#define FNM_FILE_NAME	FNM_PATHNAME
+
+extern int p_fnmatch(const char *pattern, const char *string, int flags);
+
+#endif /* _FNMATCH_H */
+
diff --git a/libgit2/src/global.c b/libgit2/src/global.c
--- a/libgit2/src/global.c
+++ b/libgit2/src/global.c
@@ -1,14 +1,19 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
 #include "common.h"
 #include "global.h"
-#include "git2/threads.h" 
+#include "hash.h"
+#include "fileops.h"
+#include "git2/threads.h"
 #include "thread-utils.h"
 
+
+git_mutex git__mwindow_mutex;
+
 /**
  * Handle the global state with TLS
  *
@@ -35,30 +40,58 @@
  * functions are not available in that case.
  */
 
+/*
+ * `git_threads_init()` allows subsystems to perform global setup,
+ * which may take place in the global scope.  An explicit memory
+ * fence exists at the exit of `git_threads_init()`.  Without this,
+ * CPU cores are free to reorder cache invalidation of `_tls_init`
+ * before cache invalidation of the subsystems' newly written global
+ * state.
+ */
 #if defined(GIT_THREADS) && defined(GIT_WIN32)
 
 static DWORD _tls_index;
 static int _tls_init = 0;
 
-void git_threads_init(void)
+int git_threads_init(void)
 {
+	int error;
+
 	if (_tls_init)
-		return;
+		return 0;
 
 	_tls_index = TlsAlloc();
-	_tls_init = 1;
+	git_mutex_init(&git__mwindow_mutex);
+
+	/* Initialize any other subsystems that have global state */
+	if ((error = git_hash_global_init()) >= 0)
+		_tls_init = 1;
+
+	if (error == 0)
+		_tls_init = 1;
+
+	GIT_MEMORY_BARRIER;
+
+	return error;
 }
 
 void git_threads_shutdown(void)
 {
 	TlsFree(_tls_index);
 	_tls_init = 0;
+	git_mutex_free(&git__mwindow_mutex);
+
+	/* Shut down any subsystems that have global state */
+	git_hash_global_shutdown();
+	git_futils_dirs_free();
 }
 
 git_global_st *git__global_state(void)
 {
 	void *ptr;
 
+	assert(_tls_init);
+
 	if ((ptr = TlsGetValue(_tls_index)) != NULL)
 		return ptr;
 
@@ -81,25 +114,42 @@
 	git__free(st);
 }
 
-void git_threads_init(void)
+int git_threads_init(void)
 {
+	int error = 0;
+
 	if (_tls_init)
-		return;
+		return 0;
 
+	git_mutex_init(&git__mwindow_mutex);
 	pthread_key_create(&_tls_key, &cb__free_status);
-	_tls_init = 1;
+
+	/* Initialize any other subsystems that have global state */
+	if ((error = git_hash_global_init()) >= 0)
+		_tls_init = 1;
+
+	GIT_MEMORY_BARRIER;
+
+	return error;
 }
 
 void git_threads_shutdown(void)
 {
 	pthread_key_delete(_tls_key);
 	_tls_init = 0;
+	git_mutex_free(&git__mwindow_mutex);
+
+	/* Shut down any subsystems that have global state */
+	git_hash_global_shutdown();
+	git_futils_dirs_free();
 }
 
 git_global_st *git__global_state(void)
 {
 	void *ptr;
 
+	assert(_tls_init);
+
 	if ((ptr = pthread_getspecific(_tls_key)) != NULL)
 		return ptr;
 
@@ -116,14 +166,17 @@
 
 static git_global_st __state;
 
-void git_threads_init(void)
+int git_threads_init(void)
 {
 	/* noop */ 
+	return 0;
 }
 
 void git_threads_shutdown(void)
 {
-	/* noop */
+	/* Shut down any subsystems that have global state */
+	git_hash_global_shutdown();
+	git_futils_dirs_free();
 }
 
 git_global_st *git__global_state(void)
diff --git a/libgit2/src/global.h b/libgit2/src/global.h
--- a/libgit2/src/global.h
+++ b/libgit2/src/global.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,19 +8,24 @@
 #define INCLUDE_global_h__
 
 #include "mwindow.h"
+#include "hash.h"
 
-typedef struct {
-	struct {
-		char last[1024];
-	} error;
+#if defined(GIT_THREADS) && defined(_MSC_VER)
+# define GIT_MEMORY_BARRIER MemoryBarrier()
+#elif defined(GIT_THREADS)
+# define GIT_MEMORY_BARRIER __sync_synchronize()
+#else
+# define GIT_MEMORY_BARRIER /* noop */
+#endif
 
+typedef struct {
 	git_error *last_error;
 	git_error error_t;
-
-	git_mwindow_ctl mem_ctl;
 } git_global_st;
 
 git_global_st *git__global_state(void);
+
+extern git_mutex git__mwindow_mutex;
 
 #define GIT_GLOBAL (git__global_state())
 
diff --git a/libgit2/src/graph.c b/libgit2/src/graph.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/graph.c
@@ -0,0 +1,178 @@
+
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "revwalk.h"
+#include "merge.h"
+#include "git2/graph.h"
+
+static int interesting(git_pqueue *list, git_commit_list *roots)
+{
+	unsigned int i;
+	/* element 0 isn't used - we need to start at 1 */
+	for (i = 1; i < list->size; i++) {
+		git_commit_list_node *commit = list->d[i];
+		if ((commit->flags & STALE) == 0)
+			return 1;
+	}
+
+	while(roots) {
+		if ((roots->item->flags & STALE) == 0)
+			return 1;
+		roots = roots->next;
+	}
+
+	return 0;
+}
+
+static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
+	git_commit_list_node *two)
+{
+	unsigned int i;
+	git_commit_list *roots = NULL;
+	git_pqueue list;
+
+	/* if the commit is repeated, we have a our merge base already */
+	if (one == two) {
+		one->flags |= PARENT1 | PARENT2 | RESULT;
+		return 0;
+	}
+
+	if (git_pqueue_init(&list, 2, git_commit_list_time_cmp) < 0)
+		return -1;
+
+	if (git_commit_list_parse(walk, one) < 0)
+		goto on_error;
+	one->flags |= PARENT1;
+	if (git_pqueue_insert(&list, one) < 0)
+		goto on_error;
+
+	if (git_commit_list_parse(walk, two) < 0)
+		goto on_error;
+	two->flags |= PARENT2;
+	if (git_pqueue_insert(&list, two) < 0)
+		goto on_error;
+
+	/* as long as there are non-STALE commits */
+	while (interesting(&list, roots)) {
+		git_commit_list_node *commit;
+		int flags;
+
+		commit = git_pqueue_pop(&list);
+		if (commit == NULL)
+			break;
+
+		flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+		if (flags == (PARENT1 | PARENT2)) {
+			if (!(commit->flags & RESULT))
+				commit->flags |= RESULT;
+			/* we mark the parents of a merge stale */
+			flags |= STALE;
+		}
+
+		for (i = 0; i < commit->out_degree; i++) {
+			git_commit_list_node *p = commit->parents[i];
+			if ((p->flags & flags) == flags)
+				continue;
+
+			if (git_commit_list_parse(walk, p) < 0)
+				goto on_error;
+
+			p->flags |= flags;
+			if (git_pqueue_insert(&list, p) < 0)
+				goto on_error;
+		}
+
+		/* Keep track of root commits, to make sure the path gets marked */
+		if (commit->out_degree == 0) {
+			if (git_commit_list_insert(commit, &roots) == NULL)
+				goto on_error;
+		}
+	}
+
+	git_commit_list_free(&roots);
+	git_pqueue_free(&list);
+	return 0;
+
+on_error:
+	git_commit_list_free(&roots);
+	git_pqueue_free(&list);
+	return -1;
+}
+
+
+static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
+	size_t *ahead, size_t *behind)
+{
+	git_commit_list_node *commit;
+	git_pqueue pq;
+	int i;
+	*ahead = 0;
+	*behind = 0;
+
+	if (git_pqueue_init(&pq, 2, git_commit_list_time_cmp) < 0)
+		return -1;
+	if (git_pqueue_insert(&pq, one) < 0)
+		goto on_error;
+	if (git_pqueue_insert(&pq, two) < 0)
+		goto on_error;
+
+	while ((commit = git_pqueue_pop(&pq)) != NULL) {
+		if (commit->flags & RESULT ||
+			(commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2))
+			continue;
+		else if (commit->flags & PARENT1)
+			(*behind)++;
+		else if (commit->flags & PARENT2)
+			(*ahead)++;
+
+		for (i = 0; i < commit->out_degree; i++) {
+			git_commit_list_node *p = commit->parents[i];
+			if (git_pqueue_insert(&pq, p) < 0)
+				return -1;
+		}
+		commit->flags |= RESULT;
+	}
+
+	git_pqueue_free(&pq);
+	return 0;
+
+on_error:
+	git_pqueue_free(&pq);
+	return -1;
+}
+
+int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo,
+	const git_oid *local, const git_oid *upstream)
+{
+	git_revwalk *walk;
+	git_commit_list_node *commit_u, *commit_l;
+
+	if (git_revwalk_new(&walk, repo) < 0)
+		return -1;
+
+	commit_u = git_revwalk__commit_lookup(walk, upstream);
+	if (commit_u == NULL)
+		goto on_error;
+
+	commit_l = git_revwalk__commit_lookup(walk, local);
+	if (commit_l == NULL)
+		goto on_error;
+
+	if (mark_parents(walk, commit_l, commit_u) < 0)
+		goto on_error;
+	if (ahead_behind(commit_l, commit_u, ahead, behind) < 0)
+		goto on_error;
+
+	git_revwalk_free(walk);
+
+	return 0;
+
+on_error:
+	git_revwalk_free(walk);
+	return -1;
+}
diff --git a/libgit2/src/hash.c b/libgit2/src/hash.c
--- a/libgit2/src/hash.c
+++ b/libgit2/src/hash.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,67 +8,40 @@
 #include "common.h"
 #include "hash.h"
 
-#if defined(PPC_SHA1)
-# include "ppc/sha1.h"
-#else
-# include "sha1.h"
-#endif
-
-struct git_hash_ctx {
-	SHA_CTX c;
-};
-
-git_hash_ctx *git_hash_new_ctx(void)
+int git_hash_buf(git_oid *out, const void *data, size_t len)
 {
-	git_hash_ctx *ctx = git__malloc(sizeof(*ctx));
-
-	if (!ctx)
-		return NULL;
-
-	SHA1_Init(&ctx->c);
+	git_hash_ctx ctx;
+	int error = 0;
 
-	return ctx;
-}
+	if (git_hash_ctx_init(&ctx) < 0)
+		return -1;
 
-void git_hash_free_ctx(git_hash_ctx *ctx)
-{
-	git__free(ctx);
-}
+	if ((error = git_hash_update(&ctx, data, len)) >= 0)
+		error = git_hash_final(out, &ctx);
 
-void git_hash_init(git_hash_ctx *ctx)
-{
-	assert(ctx);
-	SHA1_Init(&ctx->c);
+	git_hash_ctx_cleanup(&ctx);
+	
+	return error;
 }
 
-void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
 {
-	assert(ctx);
-	SHA1_Update(&ctx->c, data, len);
-}
+	git_hash_ctx ctx;
+	size_t i;
+	int error = 0;
 
-void git_hash_final(git_oid *out, git_hash_ctx *ctx)
-{
-	assert(ctx);
-	SHA1_Final(out->id, &ctx->c);
-}
+	if (git_hash_ctx_init(&ctx) < 0)
+		return -1;
 
-void git_hash_buf(git_oid *out, const void *data, size_t len)
-{
-	SHA_CTX c;
+	for (i = 0; i < n; i++) {
+		if ((error = git_hash_update(&ctx, vec[i].data, vec[i].len)) < 0)
+			goto done;
+	}
 
-	SHA1_Init(&c);
-	SHA1_Update(&c, data, len);
-	SHA1_Final(out->id, &c);
-}
+	error = git_hash_final(out, &ctx);
 
-void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
-{
-	SHA_CTX c;
-	size_t i;
+done:
+	git_hash_ctx_cleanup(&ctx);
 
-	SHA1_Init(&c);
-	for (i = 0; i < n; i++)
-		SHA1_Update(&c, vec[i].data, vec[i].len);
-	SHA1_Final(out->id, &c);
+	return error;
 }
diff --git a/libgit2/src/hash.h b/libgit2/src/hash.h
--- a/libgit2/src/hash.h
+++ b/libgit2/src/hash.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,21 +9,33 @@
 
 #include "git2/oid.h"
 
+typedef struct git_hash_prov git_hash_prov;
 typedef struct git_hash_ctx git_hash_ctx;
 
+int git_hash_global_init(void);
+void git_hash_global_shutdown(void);
+
+int git_hash_ctx_init(git_hash_ctx *ctx);
+void git_hash_ctx_cleanup(git_hash_ctx *ctx);
+
+#if defined(OPENSSL_SHA1)
+# include "hash/hash_openssl.h"
+#elif defined(WIN32_SHA1)
+# include "hash/hash_win32.h"
+#else
+# include "hash/hash_generic.h"
+#endif
+
 typedef struct {
 	void *data;
 	size_t len;
 } git_buf_vec;
 
-git_hash_ctx *git_hash_new_ctx(void);
-void git_hash_free_ctx(git_hash_ctx *ctx);
-
-void git_hash_init(git_hash_ctx *c);
-void git_hash_update(git_hash_ctx *c, const void *data, size_t len);
-void git_hash_final(git_oid *out, git_hash_ctx *c);
+int git_hash_init(git_hash_ctx *c);
+int git_hash_update(git_hash_ctx *c, const void *data, size_t len);
+int git_hash_final(git_oid *out, git_hash_ctx *c);
 
-void git_hash_buf(git_oid *out, const void *data, size_t len);
-void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);
+int git_hash_buf(git_oid *out, const void *data, size_t len);
+int git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n);
 
 #endif /* INCLUDE_hash_h__ */
diff --git a/libgit2/src/hash/hash_win32.c b/libgit2/src/hash/hash_win32.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/hash/hash_win32.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "global.h"
+#include "hash.h"
+#include "hash/hash_win32.h"
+
+#include <wincrypt.h>
+#include <strsafe.h>
+
+static struct git_hash_prov hash_prov = {0};
+
+/* Hash initialization */
+
+/* Initialize CNG, if available */
+GIT_INLINE(int) hash_cng_prov_init(void)
+{
+	OSVERSIONINFOEX version_test = {0};
+	DWORD version_test_mask;
+	DWORDLONG version_condition_mask = 0;
+	char dll_path[MAX_PATH];
+	DWORD dll_path_len, size_len;
+
+	return -1;
+
+	/* Only use CNG on Windows 2008 / Vista SP1  or better (Windows 6.0 SP1) */
+	version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+	version_test.dwMajorVersion = 6;
+	version_test.dwMinorVersion = 0;
+	version_test.wServicePackMajor = 1;
+	version_test.wServicePackMinor = 0;
+
+	version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
+
+	VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+	VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+	if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+		return -1;
+
+	/* Load bcrypt.dll explicitly from the system directory */
+	if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH ||
+		StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
+		StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
+		(hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
+		return -1;
+
+	/* Load the function addresses */
+	if ((hash_prov.prov.cng.open_algorithm_provider = (hash_win32_cng_open_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptOpenAlgorithmProvider")) == NULL ||
+		(hash_prov.prov.cng.get_property = (hash_win32_cng_get_property_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptGetProperty")) == NULL ||
+		(hash_prov.prov.cng.create_hash = (hash_win32_cng_create_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCreateHash")) == NULL ||
+		(hash_prov.prov.cng.finish_hash = (hash_win32_cng_finish_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptFinishHash")) == NULL ||
+		(hash_prov.prov.cng.hash_data = (hash_win32_cng_hash_data_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptHashData")) == NULL ||
+		(hash_prov.prov.cng.destroy_hash = (hash_win32_cng_destroy_hash_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptDestroyHash")) == NULL ||
+		(hash_prov.prov.cng.close_algorithm_provider = (hash_win32_cng_close_algorithm_provider_fn)GetProcAddress(hash_prov.prov.cng.dll, "BCryptCloseAlgorithmProvider")) == NULL) {
+		FreeLibrary(hash_prov.prov.cng.dll);
+		return -1;
+	}
+
+	/* Load the SHA1 algorithm */
+	if (hash_prov.prov.cng.open_algorithm_provider(&hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0) {
+		FreeLibrary(hash_prov.prov.cng.dll);
+		return -1;
+	}
+
+	/* Get storage space for the hash object */
+	if (hash_prov.prov.cng.get_property(hash_prov.prov.cng.handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_prov.prov.cng.hash_object_size, sizeof(DWORD), &size_len, 0) < 0) {
+		hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
+		FreeLibrary(hash_prov.prov.cng.dll);
+		return -1;
+	}
+
+	hash_prov.type = CNG;
+	return 0;
+}
+
+GIT_INLINE(void) hash_cng_prov_shutdown(void)
+{
+	hash_prov.prov.cng.close_algorithm_provider(hash_prov.prov.cng.handle, 0);
+	FreeLibrary(hash_prov.prov.cng.dll);
+
+	hash_prov.type = INVALID;
+}
+
+/* Initialize CryptoAPI */
+GIT_INLINE(int) hash_cryptoapi_prov_init()
+{
+	if (!CryptAcquireContext(&hash_prov.prov.cryptoapi.handle, NULL, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+		return -1;
+
+	hash_prov.type = CRYPTOAPI;
+	return 0;
+}
+
+GIT_INLINE(void) hash_cryptoapi_prov_shutdown(void)
+{
+	CryptReleaseContext(hash_prov.prov.cryptoapi.handle, 0);
+
+	hash_prov.type = INVALID;
+}
+
+int git_hash_global_init()
+{
+	int error = 0;
+
+	if (hash_prov.type != INVALID)
+		return 0;
+
+	if ((error = hash_cng_prov_init()) < 0)
+		error = hash_cryptoapi_prov_init();
+
+	return error;	
+}
+
+void git_hash_global_shutdown()
+{
+	if (hash_prov.type == CNG)
+		hash_cng_prov_shutdown();
+	else if(hash_prov.type == CRYPTOAPI)
+		hash_cryptoapi_prov_shutdown();
+}
+
+/* CryptoAPI: available in Windows XP and newer */
+
+GIT_INLINE(int) hash_ctx_cryptoapi_init(git_hash_ctx *ctx)
+{
+	ctx->type = CRYPTOAPI;
+	ctx->prov = &hash_prov;
+
+	return git_hash_init(ctx);
+}
+
+GIT_INLINE(int) hash_cryptoapi_init(git_hash_ctx *ctx)
+{
+	if (ctx->ctx.cryptoapi.valid)
+		CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+
+	if (!CryptCreateHash(ctx->prov->prov.cryptoapi.handle, CALG_SHA1, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
+		ctx->ctx.cryptoapi.valid = 0;
+		return -1;
+	}
+
+	ctx->ctx.cryptoapi.valid = 1;
+	return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+	assert(ctx->ctx.cryptoapi.valid);
+
+	if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, (const BYTE *)data, (DWORD)len, 0))
+		return -1;
+
+	return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_final(git_oid *out, git_hash_ctx *ctx)
+{
+	DWORD len = 20;
+	int error = 0;
+
+	assert(ctx->ctx.cryptoapi.valid);
+
+	if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out->id, &len, 0))
+		error = -1;
+
+	CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+	ctx->ctx.cryptoapi.valid = 0;
+
+	return error;
+}
+
+GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_ctx *ctx)
+{
+	if (ctx->ctx.cryptoapi.valid)
+		CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+}
+
+/* CNG: Available in Windows Server 2008 and newer */
+
+GIT_INLINE(int) hash_ctx_cng_init(git_hash_ctx *ctx)
+{
+	if ((ctx->ctx.cng.hash_object = git__malloc(hash_prov.prov.cng.hash_object_size)) == NULL)
+		return -1;
+
+	if (hash_prov.prov.cng.create_hash(hash_prov.prov.cng.handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_prov.prov.cng.hash_object_size, NULL, 0, 0) < 0) {
+		git__free(ctx->ctx.cng.hash_object);
+		return -1;
+	}
+
+	ctx->type = CNG;
+	ctx->prov = &hash_prov;
+
+	return 0;
+}
+
+GIT_INLINE(int) hash_cng_init(git_hash_ctx *ctx)
+{
+	BYTE hash[GIT_OID_RAWSZ];
+
+	if (!ctx->ctx.cng.updated)
+		return 0;
+
+	/* CNG needs to be finished to restart */
+	if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, GIT_OID_RAWSZ, 0) < 0)
+		return -1;
+
+	ctx->ctx.cng.updated = 0;
+
+	return 0;
+}
+
+GIT_INLINE(int) hash_cng_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+	if (ctx->prov->prov.cng.hash_data(ctx->ctx.cng.hash_handle, (PBYTE)data, (ULONG)len, 0) < 0)
+		return -1;
+
+	return 0;
+}
+
+GIT_INLINE(int) hash_cng_final(git_oid *out, git_hash_ctx *ctx)
+{
+	if (ctx->prov->prov.cng.finish_hash(ctx->ctx.cng.hash_handle, out->id, GIT_OID_RAWSZ, 0) < 0)
+		return -1;
+
+	ctx->ctx.cng.updated = 0;
+
+	return 0;
+}
+
+GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_ctx *ctx)
+{
+	ctx->prov->prov.cng.destroy_hash(ctx->ctx.cng.hash_handle);
+	git__free(ctx->ctx.cng.hash_object);
+}
+
+/* Indirection between CryptoAPI and CNG */
+
+int git_hash_ctx_init(git_hash_ctx *ctx)
+{
+	int error = 0;
+
+	assert(ctx);
+
+	/*
+	 * When compiled with GIT_THREADS, the global hash_prov data is
+	 * initialized with git_threads_init.  Otherwise, it must be initialized
+	 * at first use.
+	 */
+	if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0)
+		return error;
+
+	memset(ctx, 0x0, sizeof(git_hash_ctx));
+
+	return (hash_prov.type == CNG) ? hash_ctx_cng_init(ctx) : hash_ctx_cryptoapi_init(ctx);
+}
+
+int git_hash_init(git_hash_ctx *ctx)
+{
+	assert(ctx && ctx->type);
+	return (ctx->type == CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
+}
+
+int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+	assert(ctx && ctx->type);
+	return (ctx->type == CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
+}
+
+int git_hash_final(git_oid *out, git_hash_ctx *ctx)
+{
+	assert(ctx && ctx->type);
+	return (ctx->type == CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
+}
+
+void git_hash_ctx_cleanup(git_hash_ctx *ctx)
+{
+	assert(ctx);
+
+	if (ctx->type == CNG)
+		hash_ctx_cng_cleanup(ctx);
+	else if(ctx->type == CRYPTOAPI)
+		hash_ctx_cryptoapi_cleanup(ctx);
+}
diff --git a/libgit2/src/hashsig.c b/libgit2/src/hashsig.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/hashsig.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "hashsig.h"
+#include "fileops.h"
+#include "util.h"
+
+typedef uint32_t hashsig_t;
+typedef uint64_t hashsig_state;
+
+#define HASHSIG_SCALE 100
+
+#define HASHSIG_HASH_WINDOW 32
+#define HASHSIG_HASH_START	0
+#define HASHSIG_HASH_SHIFT  5
+#define HASHSIG_HASH_MASK   0x7FFFFFFF
+
+#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
+
+typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
+
+typedef struct {
+	int size, asize;
+	hashsig_cmp cmp;
+	hashsig_t values[HASHSIG_HEAP_SIZE];
+} hashsig_heap;
+
+typedef struct {
+	hashsig_state state, shift_n;
+	char window[HASHSIG_HASH_WINDOW];
+	int win_len, win_pos, saw_lf;
+} hashsig_in_progress;
+
+#define HASHSIG_IN_PROGRESS_INIT { HASHSIG_HASH_START, 1, {0}, 0, 0, 1 }
+
+struct git_hashsig {
+	hashsig_heap mins;
+	hashsig_heap maxs;
+	git_hashsig_option_t opt;
+	int considered;
+};
+
+#define HEAP_LCHILD_OF(I) (((I)*2)+1)
+#define HEAP_RCHILD_OF(I) (((I)*2)+2)
+#define HEAP_PARENT_OF(I) (((I)-1)>>1)
+
+static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
+{
+	h->size  = 0;
+	h->asize = HASHSIG_HEAP_SIZE;
+	h->cmp   = cmp;
+}
+
+static int hashsig_cmp_max(const void *a, const void *b, void *payload)
+{
+	hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
+	GIT_UNUSED(payload);
+	return (av < bv) ? -1 : (av > bv) ? 1 : 0;
+}
+
+static int hashsig_cmp_min(const void *a, const void *b, void *payload)
+{
+	hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
+	GIT_UNUSED(payload);
+	return (av > bv) ? -1 : (av < bv) ? 1 : 0;
+}
+
+static void hashsig_heap_up(hashsig_heap *h, int el)
+{
+	int parent_el = HEAP_PARENT_OF(el);
+
+	while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) {
+		hashsig_t t = h->values[el];
+		h->values[el] = h->values[parent_el];
+		h->values[parent_el] = t;
+
+		el = parent_el;
+		parent_el = HEAP_PARENT_OF(el);
+	}
+}
+
+static void hashsig_heap_down(hashsig_heap *h, int el)
+{
+	hashsig_t v, lv, rv;
+
+	/* 'el < h->size / 2' tests if el is bottom row of heap */
+
+	while (el < h->size / 2) {
+		int lel = HEAP_LCHILD_OF(el), rel = HEAP_RCHILD_OF(el), swapel;
+
+		v  = h->values[el];
+		lv = h->values[lel];
+		rv = h->values[rel];
+
+		if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0)
+			break;
+
+		swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel;
+
+		h->values[el] = h->values[swapel];
+		h->values[swapel] = v;
+
+		el = swapel;
+	}
+}
+
+static void hashsig_heap_sort(hashsig_heap *h)
+{
+	/* only need to do this at the end for signature comparison */
+	git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL);
+}
+
+static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
+{
+	/* if heap is full, pop top if new element should replace it */
+	if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
+		h->size--;
+		h->values[0] = h->values[h->size];
+		hashsig_heap_down(h, 0);
+	}
+
+	/* if heap is not full, insert new element */
+	if (h->size < h->asize) {
+		h->values[h->size++] = val;
+		hashsig_heap_up(h, h->size - 1);
+	}
+}
+
+GIT_INLINE(bool) hashsig_include_char(
+	char ch, git_hashsig_option_t opt, int *saw_lf)
+{
+	if ((opt & GIT_HASHSIG_IGNORE_WHITESPACE) && git__isspace(ch))
+		return false;
+
+	if (opt & GIT_HASHSIG_SMART_WHITESPACE) {
+		if (ch == '\r' || (*saw_lf && git__isspace(ch)))
+			return false;
+
+		*saw_lf = (ch == '\n');
+	}
+
+	return true;
+}
+
+static void hashsig_initial_window(
+	git_hashsig *sig,
+	const char **data,
+	size_t size,
+	hashsig_in_progress *prog)
+{
+	hashsig_state state, shift_n;
+	int win_len;
+	const char *scan, *end;
+
+	/* init until we have processed at least HASHSIG_HASH_WINDOW data */
+
+	if (prog->win_len >= HASHSIG_HASH_WINDOW)
+		return;
+
+	state   = prog->state;
+	win_len = prog->win_len;
+	shift_n = prog->shift_n;
+
+	scan = *data;
+	end  = scan + size;
+
+	while (scan < end && win_len < HASHSIG_HASH_WINDOW) {
+		char ch = *scan++;
+
+		if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
+			continue;
+
+		state = (state * HASHSIG_HASH_SHIFT + ch) & HASHSIG_HASH_MASK;
+
+		if (!win_len)
+			shift_n = 1;
+		else
+			shift_n = (shift_n * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
+
+		prog->window[win_len++] = ch;
+	}
+
+	/* insert initial hash if we just finished */
+
+	if (win_len == HASHSIG_HASH_WINDOW) {
+		hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+		hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
+		sig->considered = 1;
+	}
+
+	prog->state   = state;
+	prog->win_len = win_len;
+	prog->shift_n = shift_n;
+
+	*data = scan;
+}
+
+static int hashsig_add_hashes(
+	git_hashsig *sig,
+	const char *data,
+	size_t size,
+	hashsig_in_progress *prog)
+{
+	const char *scan = data, *end = data + size;
+	hashsig_state state, shift_n, rmv;
+
+	if (prog->win_len < HASHSIG_HASH_WINDOW)
+		hashsig_initial_window(sig, &scan, size, prog);
+
+	state   = prog->state;
+	shift_n = prog->shift_n;
+
+	/* advance window, adding new chars and removing old */
+
+	for (; scan < end; ++scan) {
+		char ch = *scan;
+
+		if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
+			continue;
+
+		rmv = shift_n * prog->window[prog->win_pos];
+
+		state = (state - rmv) & HASHSIG_HASH_MASK;
+		state = (state * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
+		state = (state + ch) & HASHSIG_HASH_MASK;
+
+		hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+		hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
+		sig->considered++;
+
+		prog->window[prog->win_pos] = ch;
+		prog->win_pos = (prog->win_pos + 1) % HASHSIG_HASH_WINDOW;
+	}
+
+	prog->state = state;
+
+	return 0;
+}
+
+static int hashsig_finalize_hashes(git_hashsig *sig)
+{
+	if (sig->mins.size < HASHSIG_HEAP_SIZE) {
+		giterr_set(GITERR_INVALID,
+			"File too small for similarity signature calculation");
+		return GIT_EBUFS;
+	}
+
+	hashsig_heap_sort(&sig->mins);
+	hashsig_heap_sort(&sig->maxs);
+
+	return 0;
+}
+
+static git_hashsig *hashsig_alloc(git_hashsig_option_t opts)
+{
+	git_hashsig *sig = git__calloc(1, sizeof(git_hashsig));
+	if (!sig)
+		return NULL;
+
+	hashsig_heap_init(&sig->mins, hashsig_cmp_min);
+	hashsig_heap_init(&sig->maxs, hashsig_cmp_max);
+	sig->opt = opts;
+
+	return sig;
+}
+
+int git_hashsig_create(
+	git_hashsig **out,
+	const char *buf,
+	size_t buflen,
+	git_hashsig_option_t opts)
+{
+	int error;
+	hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+	git_hashsig *sig = hashsig_alloc(opts);
+	GITERR_CHECK_ALLOC(sig);
+
+	error = hashsig_add_hashes(sig, buf, buflen, &prog);
+
+	if (!error)
+		error = hashsig_finalize_hashes(sig);
+
+	if (!error)
+		*out = sig;
+	else
+		git_hashsig_free(sig);
+
+	return error;
+}
+
+int git_hashsig_create_fromfile(
+	git_hashsig **out,
+	const char *path,
+	git_hashsig_option_t opts)
+{
+	char buf[4096];
+	ssize_t buflen = 0;
+	int error = 0, fd;
+	hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
+	git_hashsig *sig = hashsig_alloc(opts);
+	GITERR_CHECK_ALLOC(sig);
+
+	if ((fd = git_futils_open_ro(path)) < 0) {
+		git__free(sig);
+		return fd;
+	}
+
+	while (!error) {
+		if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
+			if ((error = (int)buflen) < 0)
+				giterr_set(GITERR_OS,
+					"Read error on '%s' calculating similarity hashes", path);
+			break;
+		}
+
+		error = hashsig_add_hashes(sig, buf, buflen, &prog);
+	}
+
+	p_close(fd);
+
+	if (!error)
+		error = hashsig_finalize_hashes(sig);
+
+	if (!error)
+		*out = sig;
+	else
+		git_hashsig_free(sig);
+
+	return error;
+}
+
+void git_hashsig_free(git_hashsig *sig)
+{
+	git__free(sig);
+}
+
+static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
+{
+	int matches = 0, i, j, cmp;
+
+	assert(a->cmp == b->cmp);
+
+	/* hash heaps are sorted - just look for overlap vs total */
+
+	for (i = 0, j = 0; i < a->size && j < b->size; ) {
+		cmp = a->cmp(&a->values[i], &b->values[j], NULL);
+
+		if (cmp < 0)
+			++i;
+		else if (cmp > 0)
+			++j;
+		else {
+			++i; ++j; ++matches;
+		}
+	}
+
+	return HASHSIG_SCALE * (matches * 2) / (a->size + b->size);
+}
+
+int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
+{
+	return (hashsig_heap_compare(&a->mins, &b->mins) +
+			hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
+}
+
diff --git a/libgit2/src/hashsig.h b/libgit2/src/hashsig.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/hashsig.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_hashsig_h__
+#define INCLUDE_hashsig_h__
+
+#include "common.h"
+
+/**
+ * Similarity signature of line hashes for a buffer
+ */
+typedef struct git_hashsig git_hashsig;
+
+typedef enum {
+	GIT_HASHSIG_NORMAL = 0, /* use all data */
+	GIT_HASHSIG_IGNORE_WHITESPACE = 1, /* ignore whitespace */
+	GIT_HASHSIG_SMART_WHITESPACE = 2, /* ignore \r and all space after \n */
+} git_hashsig_option_t;
+
+/**
+ * Build a similarity signature for a buffer
+ *
+ * If you have passed a whitespace-ignoring buffer, then the whitespace
+ * will be removed from the buffer while it is being processed, modifying
+ * the buffer in place.  Sorry about that!
+ *
+ * This will return an error if the buffer doesn't contain enough data to
+ * compute a valid signature.
+ *
+ * @param out The array of hashed runs representing the file content
+ * @param buf The contents of the file to hash
+ * @param buflen The length of the data at `buf`
+ * @param generate_pairwise_hashes Should pairwise runs be hashed
+ */
+extern int git_hashsig_create(
+	git_hashsig **out,
+	const char *buf,
+	size_t buflen,
+	git_hashsig_option_t opts);
+
+/**
+ * Build a similarity signature from a file
+ *
+ * This walks through the file, only loading a maximum of 4K of file data at
+ * a time.  Otherwise, it acts just like `git_hashsig_create`.
+ *
+ * This will return an error if the file doesn't contain enough data to
+ * compute a valid signature.
+ */
+extern int git_hashsig_create_fromfile(
+	git_hashsig **out,
+	const char *path,
+	git_hashsig_option_t opts);
+
+/**
+ * Release memory for a content similarity signature
+ */
+extern void git_hashsig_free(git_hashsig *sig);
+
+/**
+ * Measure similarity between two files
+ *
+ * @return <0 for error, [0 to 100] as similarity score
+ */
+extern int git_hashsig_compare(
+	const git_hashsig *a,
+	const git_hashsig *b);
+
+#endif
diff --git a/libgit2/src/ignore.c b/libgit2/src/ignore.c
--- a/libgit2/src/ignore.c
+++ b/libgit2/src/ignore.c
@@ -1,20 +1,39 @@
+#include "git2/ignore.h"
 #include "ignore.h"
+#include "attr.h"
 #include "path.h"
+#include "config.h"
 
 #define GIT_IGNORE_INTERNAL		"[internal]exclude"
-#define GIT_IGNORE_FILE_INREPO	"info/exclude"
-#define GIT_IGNORE_FILE			".gitignore"
 
+#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
+
 static int parse_ignore_file(
-	git_repository *repo, const char *buffer, git_attr_file *ignores)
+	git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores)
 {
 	int error = 0;
 	git_attr_fnmatch *match = NULL;
 	const char *scan = NULL;
 	char *context = NULL;
+	bool ignore_case = false;
+	git_config *cfg = NULL;
+	int val;
 
-	GIT_UNUSED(repo);
+	/* Prefer to have the caller pass in a git_ignores as the parsedata object.
+	 * If they did not, then we can (much more slowly) find the value of
+	 * ignore_case by using the repository object. */
+	if (parsedata != NULL) {
+		ignore_case = ((git_ignores *)parsedata)->ignore_case;
+	} else {
+		if ((error = git_repository_config(&cfg, repo)) < 0)
+			return error;
 
+		if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
+			ignore_case = (val != 0);
+
+		git_config_free(cfg);
+	}
+
 	if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
 		context = ignores->key + 2;
 		context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';
@@ -28,10 +47,16 @@
 			GITERR_CHECK_ALLOC(match);
 		}
 
+		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+
 		if (!(error = git_attr_fnmatch__parse(
 			match, ignores->pool, context, &scan)))
 		{
-			match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
+			match->flags |= GIT_ATTR_FNMATCH_IGNORE;
+
+			if (ignore_case)
+				match->flags |= GIT_ATTR_FNMATCH_ICASE;
+
 			scan = git__next_line(scan);
 			error = git_vector_insert(&ignores->rules, match);
 		}
@@ -55,15 +80,28 @@
 	return error;
 }
 
-#define push_ignore_file(R,S,B,F) \
-	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(S))
+#define push_ignore_file(R,IGN,S,B,F) \
+	git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S))
 
 static int push_one_ignore(void *ref, git_buf *path)
 {
 	git_ignores *ign = (git_ignores *)ref;
-	return push_ignore_file(ign->repo, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
+	return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE);
 }
 
+static int get_internal_ignores(git_attr_file **ign, git_repository *repo)
+{
+	int error;
+
+	if (!(error = git_attr_cache__init(repo)))
+		error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign);
+
+	if (!error && !(*ign)->rules.length)
+		error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign);
+
+	return error;
+}
+
 int git_ignore__for_path(
 	git_repository *repo,
 	const char *path,
@@ -71,6 +109,8 @@
 {
 	int error = 0;
 	const char *workdir = git_repository_workdir(repo);
+	git_config *cfg = NULL;
+	int val;
 
 	assert(ignores);
 
@@ -78,6 +118,17 @@
 	git_buf_init(&ignores->dir, 0);
 	ignores->ign_internal = NULL;
 
+	/* Set the ignore_case flag appropriately */
+	if ((error = git_repository_config(&cfg, repo)) < 0)
+		goto cleanup;
+
+	if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
+		ignores->ignore_case = (val != 0);
+	else
+		ignores->ignore_case = 0;
+
+	git_config_free(cfg);
+
 	if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 ||
 		(error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 ||
 		(error = git_attr_cache__init(repo)) < 0)
@@ -92,8 +143,7 @@
 		goto cleanup;
 
 	/* set up internals */
-	error = git_attr_cache__internal_file(
-		repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
+	error = get_internal_ignores(&ignores->ign_internal, repo);
 	if (error < 0)
 		goto cleanup;
 
@@ -106,14 +156,14 @@
 	}
 
 	/* load .git/info/exclude */
-	error = push_ignore_file(repo, &ignores->ign_global,
+	error = push_ignore_file(repo, ignores, &ignores->ign_global,
 		git_repository_path(repo), GIT_IGNORE_FILE_INREPO);
 	if (error < 0)
 		goto cleanup;
 
 	/* load core.excludesfile */
 	if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
-		error = push_ignore_file(repo, &ignores->ign_global, NULL,
+		error = push_ignore_file(repo, ignores, &ignores->ign_global, NULL,
 			git_repository_attr_cache(repo)->cfg_excl_file);
 
 cleanup:
@@ -129,7 +179,7 @@
 		return -1;
 	else
 		return push_ignore_file(
-			ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+			ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
 }
 
 int git_ignore__pop_dir(git_ignores *ign)
@@ -154,7 +204,7 @@
 static bool ignore_lookup_in_rules(
 	git_vector *rules, git_attr_path *path, int *ignored)
 {
-	unsigned int j;
+	size_t j;
 	git_attr_fnmatch *match;
 
 	git_vector_rforeach(rules, j, match) {
@@ -201,3 +251,110 @@
 	git_attr_path__free(&path);
 	return 0;
 }
+
+int git_ignore_add_rule(
+	git_repository *repo,
+	const char *rules)
+{
+	int error;
+	git_attr_file *ign_internal;
+
+	if (!(error = get_internal_ignores(&ign_internal, repo)))
+		error = parse_ignore_file(repo, NULL, rules, ign_internal);
+
+	return error;
+}
+
+int git_ignore_clear_internal_rules(
+	git_repository *repo)
+{
+	int error;
+	git_attr_file *ign_internal;
+
+	if (!(error = get_internal_ignores(&ign_internal, repo))) {
+		git_attr_file__clear_rules(ign_internal);
+
+		return parse_ignore_file(
+			repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal);
+	}
+
+	return error;
+}
+
+int git_ignore_path_is_ignored(
+	int *ignored,
+	git_repository *repo,
+	const char *pathname)
+{
+	int error;
+	const char *workdir;
+	git_attr_path path;
+	char *tail, *end;
+	bool full_is_dir;
+	git_ignores ignores;
+	unsigned int i;
+	git_attr_file *file;
+
+	assert(ignored && pathname);
+
+	workdir = repo ? git_repository_workdir(repo) : NULL;
+
+	if ((error = git_attr_path__init(&path, pathname, workdir)) < 0)
+		return error;
+
+	tail = path.path;
+	end  = &path.full.ptr[path.full.size];
+	full_is_dir = path.is_dir;
+
+	while (1) {
+		/* advance to next component of path */
+		path.basename = tail;
+
+		while (tail < end && *tail != '/') tail++;
+		*tail = '\0';
+
+		path.full.size = (tail - path.full.ptr);
+		path.is_dir = (tail == end) ? full_is_dir : true;
+
+		/* update ignores for new path fragment */
+		if (path.basename == path.path)
+			error = git_ignore__for_path(repo, path.path, &ignores);
+		else
+			error = git_ignore__push_dir(&ignores, path.basename);
+		if (error < 0)
+			break;
+
+		/* first process builtins - success means path was found */
+		if (ignore_lookup_in_rules(
+				&ignores.ign_internal->rules, &path, ignored))
+			goto cleanup;
+
+		/* next process files in the path */
+		git_vector_foreach(&ignores.ign_path, i, file) {
+			if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+				goto cleanup;
+		}
+
+		/* last process global ignores */
+		git_vector_foreach(&ignores.ign_global, i, file) {
+			if (ignore_lookup_in_rules(&file->rules, &path, ignored))
+				goto cleanup;
+		}
+
+		/* if we found no rules before reaching the end, we're done */
+		if (tail == end)
+			break;
+
+		/* reinstate divider in path */
+		*tail = '/';
+		while (*tail == '/') tail++;
+	}
+
+	*ignored = 0;
+
+cleanup:
+	git_attr_path__free(&path);
+	git_ignore__free(&ignores);
+	return error;
+}
+
diff --git a/libgit2/src/ignore.h b/libgit2/src/ignore.h
--- a/libgit2/src/ignore.h
+++ b/libgit2/src/ignore.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,7 +9,12 @@
 
 #include "repository.h"
 #include "vector.h"
+#include "attr_file.h"
 
+#define GIT_IGNORE_FILE			".gitignore"
+#define GIT_IGNORE_FILE_INREPO	"info/exclude"
+#define GIT_IGNORE_FILE_XDG		"ignore"
+
 /* The git_ignores structure maintains three sets of ignores:
  * - internal ignores
  * - per directory ignores
@@ -23,6 +28,7 @@
 	git_attr_file *ign_internal;
 	git_vector ign_path;
 	git_vector ign_global;
+	unsigned int ignore_case:1;
 } git_ignores;
 
 extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
diff --git a/libgit2/src/index.c b/libgit2/src/index.c
--- a/libgit2/src/index.c
+++ b/libgit2/src/index.c
@@ -1,969 +1,1681 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <stddef.h>
-
-#include "common.h"
-#include "repository.h"
-#include "index.h"
-#include "tree.h"
-#include "tree-cache.h"
-#include "hash.h"
-#include "git2/odb.h"
-#include "git2/blob.h"
-
-#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
-#define short_entry_size(len) entry_size(struct entry_short, len)
-#define long_entry_size(len) entry_size(struct entry_long, len)
-
-#define minimal_entry_size (offsetof(struct entry_short, path))
-
-static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
-static const size_t INDEX_HEADER_SIZE = 12;
-
-static const unsigned int INDEX_VERSION_NUMBER = 2;
-static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
-
-static const unsigned int INDEX_HEADER_SIG = 0x44495243;
-static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
-static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
-
-#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
-
-struct index_header {
-	uint32_t signature;
-	uint32_t version;
-	uint32_t entry_count;
-};
-
-struct index_extension {
-	char signature[4];
-	uint32_t extension_size;
-};
-
-struct entry_time {
-	uint32_t seconds;
-	uint32_t nanoseconds;
-};
-
-struct entry_short {
-	struct entry_time ctime;
-	struct entry_time mtime;
-	uint32_t dev;
-	uint32_t ino;
-	uint32_t mode;
-	uint32_t uid;
-	uint32_t gid;
-	uint32_t file_size;
-	git_oid oid;
-	uint16_t flags;
-	char path[1]; /* arbitrary length */
-};
-
-struct entry_long {
-	struct entry_time ctime;
-	struct entry_time mtime;
-	uint32_t dev;
-	uint32_t ino;
-	uint32_t mode;
-	uint32_t uid;
-	uint32_t gid;
-	uint32_t file_size;
-	git_oid oid;
-	uint16_t flags;
-	uint16_t flags_extended;
-	char path[1]; /* arbitrary length */
-};
-
-/* local declarations */
-static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
-static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
-static int read_header(struct index_header *dest, const void *buffer);
-
-static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
-static int is_index_extended(git_index *index);
-static int write_index(git_index *index, git_filebuf *file);
-
-static void index_entry_free(git_index_entry *entry);
-
-static int index_srch(const void *key, const void *array_member)
-{
-	const git_index_entry *entry = array_member;
-
-	return strcmp(key, entry->path);
-}
-
-static int index_cmp(const void *a, const void *b)
-{
-	const git_index_entry *entry_a = a;
-	const git_index_entry *entry_b = b;
-
-	return strcmp(entry_a->path, entry_b->path);
-}
-
-static int unmerged_srch(const void *key, const void *array_member)
-{
-	const git_index_entry_unmerged *entry = array_member;
-
-	return strcmp(key, entry->path);
-}
-
-static int unmerged_cmp(const void *a, const void *b)
-{
-	const git_index_entry_unmerged *info_a = a;
-	const git_index_entry_unmerged *info_b = b;
-
-	return strcmp(info_a->path, info_b->path);
-}
-
-static unsigned int index_create_mode(unsigned int mode)
-{
-	if (S_ISLNK(mode))
-		return S_IFLNK;
-	if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
-		return (S_IFLNK | S_IFDIR);
-	return S_IFREG | ((mode & 0100) ? 0755 : 0644);
-}
-
-int git_index_open(git_index **index_out, const char *index_path)
-{
-	git_index *index;
-
-	assert(index_out && index_path);
-
-	index = git__calloc(1, sizeof(git_index));
-	GITERR_CHECK_ALLOC(index);
-
-	index->index_file_path = git__strdup(index_path);
-	GITERR_CHECK_ALLOC(index->index_file_path);
-
-	if (git_vector_init(&index->entries, 32, index_cmp) < 0)
-		return -1;
-
-	/* Check if index file is stored on disk already */
-	if (git_path_exists(index->index_file_path) == true)
-		index->on_disk = 1;
-
-	*index_out = index;
-	GIT_REFCOUNT_INC(index);
-	return git_index_read(index);
-}
-
-static void index_free(git_index *index)
-{
-	git_index_entry *e;
-	unsigned int i;
-
-	git_index_clear(index);
-	git_vector_foreach(&index->entries, i, e) {
-		index_entry_free(e);
-	}
-	git_vector_free(&index->entries);
-	git_vector_foreach(&index->unmerged, i, e) {
-		index_entry_free(e);
-	}
-	git_vector_free(&index->unmerged);
-
-	git__free(index->index_file_path);
-	git__free(index);
-}
-
-void git_index_free(git_index *index)
-{
-	if (index == NULL)
-		return;
-
-	GIT_REFCOUNT_DEC(index, index_free);
-}
-
-void git_index_clear(git_index *index)
-{
-	unsigned int i;
-
-	assert(index);
-
-	for (i = 0; i < index->entries.length; ++i) {
-		git_index_entry *e;
-		e = git_vector_get(&index->entries, i);
-		git__free(e->path);
-		git__free(e);
-	}
-
-	for (i = 0; i < index->unmerged.length; ++i) {
-		git_index_entry_unmerged *e;
-		e = git_vector_get(&index->unmerged, i);
-		git__free(e->path);
-		git__free(e);
-	}
-
-	git_vector_clear(&index->entries);
-	git_vector_clear(&index->unmerged);
-	index->last_modified = 0;
-
-	git_tree_cache_free(index->tree);
-	index->tree = NULL;
-}
-
-int git_index_read(git_index *index)
-{
-	int error, updated;
-	git_buf buffer = GIT_BUF_INIT;
-	time_t mtime;
-
-	assert(index->index_file_path);
-
-	if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
-		git_index_clear(index);
-		index->on_disk = 0;
-		return 0;
-	}
-
-	/* We don't want to update the mtime if we fail to parse the index */
-	mtime = index->last_modified;
-	error = git_futils_readbuffer_updated(
-		&buffer, index->index_file_path, &mtime, &updated);
-	if (error < 0)
-		return error;
-
-	if (updated) {
-		git_index_clear(index);
-		error = parse_index(index, buffer.ptr, buffer.size);
-
-		if (!error)
-			index->last_modified = mtime;
-
-		git_buf_free(&buffer);
-	}
-
-	return error;
-}
-
-int git_index_write(git_index *index)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	struct stat indexst;
-	int error;
-
-	git_vector_sort(&index->entries);
-
-	if ((error = git_filebuf_open(
-			 &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
-		return error;
-
-	if ((error = write_index(index, &file)) < 0) {
-		git_filebuf_cleanup(&file);
-		return error;
-	}
-
-	if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
-		return error;
-
-	if (p_stat(index->index_file_path, &indexst) == 0) {
-		index->last_modified = indexst.st_mtime;
-		index->on_disk = 1;
-	}
-
-	return 0;
-}
-
-unsigned int git_index_entrycount(git_index *index)
-{
-	assert(index);
-	return index->entries.length;
-}
-
-unsigned int git_index_entrycount_unmerged(git_index *index)
-{
-	assert(index);
-	return index->unmerged.length;
-}
-
-git_index_entry *git_index_get(git_index *index, unsigned int n)
-{
-	git_vector_sort(&index->entries);
-	return git_vector_get(&index->entries, n);
-}
-
-void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry)
-{
-	entry->ctime.seconds = (git_time_t)st->st_ctime;
-	entry->mtime.seconds = (git_time_t)st->st_mtime;
-	/* entry->mtime.nanoseconds = st->st_mtimensec; */
-	/* entry->ctime.nanoseconds = st->st_ctimensec; */
-	entry->dev  = st->st_rdev;
-	entry->ino  = st->st_ino;
-	entry->mode = index_create_mode(st->st_mode);
-	entry->uid  = st->st_uid;
-	entry->gid  = st->st_gid;
-	entry->file_size = st->st_size;
-}
-
-static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path, int stage)
-{
-	git_index_entry *entry = NULL;
-	struct stat st;
-	git_oid oid;
-	const char *workdir;
-	git_buf full_path = GIT_BUF_INIT;
-	int error;
-
-	assert(stage >= 0 && stage <= 3);
-
-	if (INDEX_OWNER(index) == NULL ||
-		(workdir = git_repository_workdir(INDEX_OWNER(index))) == NULL)
-	{
-		giterr_set(GITERR_INDEX,
-			"Could not initialize index entry. Repository is bare");
-		return -1;
-	}
-
-	if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
-		return error;
-
-	if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
-		git_buf_free(&full_path);
-		return error;
-	}
-
-	git_buf_free(&full_path); /* done with full path */
-
-	/* There is no need to validate the rel_path here, since it will be
-	 * immediately validated by the call to git_blob_create_fromfile.
-	 */
-
-	/* write the blob to disk and get the oid */
-	if ((error = git_blob_create_fromfile(&oid, INDEX_OWNER(index), rel_path)) < 0)
-		return error;
-
-	entry = git__calloc(1, sizeof(git_index_entry));
-	GITERR_CHECK_ALLOC(entry);
-
-	git_index__init_entry_from_stat(&st, entry);
-
-	entry->oid = oid;
-	entry->flags |= (stage << GIT_IDXENTRY_STAGESHIFT);
-	entry->path = git__strdup(rel_path);
-	GITERR_CHECK_ALLOC(entry->path);
-
-	*entry_out = entry;
-	return 0;
-}
-
-static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
-{
-	git_index_entry *entry;
-
-	entry = git__malloc(sizeof(git_index_entry));
-	if (!entry)
-		return NULL;
-
-	memcpy(entry, source_entry, sizeof(git_index_entry));
-
-	/* duplicate the path string so we own it */
-	entry->path = git__strdup(entry->path);
-	if (!entry->path)
-		return NULL;
-
-	return entry;
-}
-
-static void index_entry_free(git_index_entry *entry)
-{
-	if (!entry)
-		return;
-	git__free(entry->path);
-	git__free(entry);
-}
-
-static int index_insert(git_index *index, git_index_entry *entry, int replace)
-{
-	size_t path_length;
-	int position;
-	git_index_entry **entry_array;
-
-	assert(index && entry && entry->path != NULL);
-
-	/* make sure that the path length flag is correct */
-	path_length = strlen(entry->path);
-
-	entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
-
-	if (path_length < GIT_IDXENTRY_NAMEMASK)
-		entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
-	else
-		entry->flags |= GIT_IDXENTRY_NAMEMASK;;
-
-	/*
-	 * replacing is not requested: just insert entry at the end;
-	 * the index is no longer sorted
-	 */
-	if (!replace)
-		return git_vector_insert(&index->entries, entry);
-
-	/* look if an entry with this path already exists */
-	position = git_index_find(index, entry->path);
-
-	/*
-	 * if no entry exists add the entry at the end;
-	 * the index is no longer sorted
-	 */
-	if (position == GIT_ENOTFOUND)
-		return git_vector_insert(&index->entries, entry);
-
-	/* exists, replace it */
-	entry_array = (git_index_entry **) index->entries.contents;
-	git__free(entry_array[position]->path);
-	git__free(entry_array[position]);
-	entry_array[position] = entry;
-
-	return 0;
-}
-
-static int index_add(git_index *index, const char *path, int stage, int replace)
-{
-	git_index_entry *entry = NULL;
-	int ret;
-
-	if ((ret = index_entry_init(&entry, index, path, stage)) < 0 ||
-		(ret = index_insert(index, entry, replace)) < 0)
-	{
-		index_entry_free(entry);
-		return ret;
-	}
-
-	git_tree_cache_invalidate_path(index->tree, entry->path);
-	return 0;
-}
-
-int git_index_add(git_index *index, const char *path, int stage)
-{
-	return index_add(index, path, stage, 1);
-}
-
-int git_index_append(git_index *index, const char *path, int stage)
-{
-	return index_add(index, path, stage, 0);
-}
-
-static int index_add2(
-	git_index *index, const git_index_entry *source_entry, int replace)
-{
-	git_index_entry *entry = NULL;
-	int ret;
-
-	entry = index_entry_dup(source_entry);
-	if (entry == NULL)
-		return -1;
-
-	if ((ret = index_insert(index, entry, replace)) < 0) {
-		index_entry_free(entry);
-		return ret;
-	}
-
-	git_tree_cache_invalidate_path(index->tree, entry->path);
-	return 0;
-}
-
-int git_index_add2(git_index *index, const git_index_entry *source_entry)
-{
-	return index_add2(index, source_entry, 1);
-}
-
-int git_index_append2(git_index *index, const git_index_entry *source_entry)
-{
-	return index_add2(index, source_entry, 1);
-}
-
-int git_index_remove(git_index *index, int position)
-{
-	int error;
-	git_index_entry *entry;
-
-	git_vector_sort(&index->entries);
-
-	entry = git_vector_get(&index->entries, position);
-	if (entry != NULL)
-		git_tree_cache_invalidate_path(index->tree, entry->path);
-
-	error = git_vector_remove(&index->entries, (unsigned int)position);
-
-	if (!error)
-		index_entry_free(entry);
-
-	return error;
-}
-
-int git_index_find(git_index *index, const char *path)
-{
-	return git_vector_bsearch2(&index->entries, index_srch, path);
-}
-
-unsigned int git_index__prefix_position(git_index *index, const char *path)
-{
-	unsigned int pos;
-
-	git_vector_bsearch3(&pos, &index->entries, index_srch, path);
-
-	return pos;
-}
-
-void git_index_uniq(git_index *index)
-{
-	git_vector_uniq(&index->entries);
-}
-
-const git_index_entry_unmerged *git_index_get_unmerged_bypath(
-	git_index *index, const char *path)
-{
-	int pos;
-	assert(index && path);
-
-	if (!index->unmerged.length)
-		return NULL;
-
-	if ((pos = git_vector_bsearch2(&index->unmerged, unmerged_srch, path)) < 0)
-		return NULL;
-
-	return git_vector_get(&index->unmerged, pos);
-}
-
-const git_index_entry_unmerged *git_index_get_unmerged_byindex(
-	git_index *index, unsigned int n)
-{
-	assert(index);
-	return git_vector_get(&index->unmerged, n);
-}
-
-static int index_error_invalid(const char *message)
-{
-	giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
-	return -1;
-}
-
-static int read_unmerged(git_index *index, const char *buffer, size_t size)
-{
-	const char *endptr;
-	size_t len;
-	int i;
-
-	if (git_vector_init(&index->unmerged, 16, unmerged_cmp) < 0)
-		return -1;
-
-	while (size) {
-		git_index_entry_unmerged *lost;
-
-		len = strlen(buffer) + 1;
-		if (size <= len)
-			return index_error_invalid("reading unmerged entries");
-
-		lost = git__malloc(sizeof(git_index_entry_unmerged));
-		GITERR_CHECK_ALLOC(lost);
-
-		if (git_vector_insert(&index->unmerged, lost) < 0)
-			return -1;
-
-		/* read NUL-terminated pathname for entry */
-		lost->path = git__strdup(buffer);
-		GITERR_CHECK_ALLOC(lost->path);
-
-		size -= len;
-		buffer += len;
-
-		/* read 3 ASCII octal numbers for stage entries */
-		for (i = 0; i < 3; i++) {
-			int tmp;
-
-			if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
-				!endptr || endptr == buffer || *endptr ||
-				(unsigned)tmp > UINT_MAX)
-				return index_error_invalid("reading unmerged entry stage");
-
-			lost->mode[i] = tmp;
-
-			len = (endptr + 1) - buffer;
-			if (size <= len)
-				return index_error_invalid("reading unmerged entry stage");
-
-			size -= len;
-			buffer += len;
-		}
-
-		/* read up to 3 OIDs for stage entries */
-		for (i = 0; i < 3; i++) {
-			if (!lost->mode[i])
-				continue;
-			if (size < 20)
-				return index_error_invalid("reading unmerged entry oid");
-
-			git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
-			size -= 20;
-			buffer += 20;
-		}
-	}
-
-	return 0;
-}
-
-static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
-{
-	size_t path_length, entry_size;
-	uint16_t flags_raw;
-	const char *path_ptr;
-	const struct entry_short *source = buffer;
-
-	if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
-		return 0;
-
-	memset(dest, 0x0, sizeof(git_index_entry));
-
-	dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
-	dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
-	dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
-	dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
-	dest->dev = ntohl(source->dev);
-	dest->ino = ntohl(source->ino);
-	dest->mode = ntohl(source->mode);
-	dest->uid = ntohl(source->uid);
-	dest->gid = ntohl(source->gid);
-	dest->file_size = ntohl(source->file_size);
-	git_oid_cpy(&dest->oid, &source->oid);
-	dest->flags = ntohs(source->flags);
-
-	if (dest->flags & GIT_IDXENTRY_EXTENDED) {
-		const struct entry_long *source_l = (const struct entry_long *)source;
-		path_ptr = source_l->path;
-
-		flags_raw = ntohs(source_l->flags_extended);
-		memcpy(&dest->flags_extended, &flags_raw, 2);
-	} else
-		path_ptr = source->path;
-
-	path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;
-
-	/* if this is a very long string, we must find its
-	 * real length without overflowing */
-	if (path_length == 0xFFF) {
-		const char *path_end;
-
-		path_end = memchr(path_ptr, '\0', buffer_size);
-		if (path_end == NULL)
-			return 0;
-
-		path_length = path_end - path_ptr;
-	}
-
-	if (dest->flags & GIT_IDXENTRY_EXTENDED)
-		entry_size = long_entry_size(path_length);
-	else
-		entry_size = short_entry_size(path_length);
-
-	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
-		return 0;
-
-	dest->path = git__strdup(path_ptr);
-	assert(dest->path);
-
-	return entry_size;
-}
-
-static int read_header(struct index_header *dest, const void *buffer)
-{
-	const struct index_header *source = buffer;
-
-	dest->signature = ntohl(source->signature);
-	if (dest->signature != INDEX_HEADER_SIG)
-		return index_error_invalid("incorrect header signature");
-
-	dest->version = ntohl(source->version);
-	if (dest->version != INDEX_VERSION_NUMBER_EXT &&
-		dest->version != INDEX_VERSION_NUMBER)
-		return index_error_invalid("incorrect header version");
-
-	dest->entry_count = ntohl(source->entry_count);
-	return 0;
-}
-
-static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
-{
-	const struct index_extension *source;
-	struct index_extension dest;
-	size_t total_size;
-
-	source = (const struct index_extension *)(buffer);
-
-	memcpy(dest.signature, source->signature, 4);
-	dest.extension_size = ntohl(source->extension_size);
-
-	total_size = dest.extension_size + sizeof(struct index_extension);
-
-	if (buffer_size - total_size < INDEX_FOOTER_SIZE)
-		return 0;
-
-	/* optional extension */
-	if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
-		/* tree cache */
-		if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
-			if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
-				return 0;
-		} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
-			if (read_unmerged(index, buffer + 8, dest.extension_size) < 0)
-				return 0;
-		}
-		/* else, unsupported extension. We cannot parse this, but we can skip
-		 * it by returning `total_size */
-	} else {
-		/* we cannot handle non-ignorable extensions;
-		 * in fact they aren't even defined in the standard */
-		return 0;
-	}
-
-	return total_size;
-}
-
-static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
-{
-	unsigned int i;
-	struct index_header header;
-	git_oid checksum_calculated, checksum_expected;
-
-#define seek_forward(_increase) { \
-	if (_increase >= buffer_size) \
-		return index_error_invalid("ran out of data while parsing"); \
-	buffer += _increase; \
-	buffer_size -= _increase;\
-}
-
-	if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
-		return index_error_invalid("insufficient buffer space");
-
-	/* Precalculate the SHA1 of the files's contents -- we'll match it to
-	 * the provided SHA1 in the footer */
-	git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
-
-	/* Parse header */
-	if (read_header(&header, buffer) < 0)
-		return -1;
-
-	seek_forward(INDEX_HEADER_SIZE);
-
-	git_vector_clear(&index->entries);
-
-	/* Parse all the entries */
-	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
-		size_t entry_size;
-		git_index_entry *entry;
-
-		entry = git__malloc(sizeof(git_index_entry));
-		GITERR_CHECK_ALLOC(entry);
-
-		entry_size = read_entry(entry, buffer, buffer_size);
-
-		/* 0 bytes read means an object corruption */
-		if (entry_size == 0)
-			return index_error_invalid("invalid entry");
-
-		if (git_vector_insert(&index->entries, entry) < 0)
-			return -1;
-
-		seek_forward(entry_size);
-	}
-
-	if (i != header.entry_count)
-		return index_error_invalid("header entries changed while parsing");
-
-	/* There's still space for some extensions! */
-	while (buffer_size > INDEX_FOOTER_SIZE) {
-		size_t extension_size;
-
-		extension_size = read_extension(index, buffer, buffer_size);
-
-		/* see if we have read any bytes from the extension */
-		if (extension_size == 0)
-			return index_error_invalid("extension size is zero");
-
-		seek_forward(extension_size);
-	}
-
-	if (buffer_size != INDEX_FOOTER_SIZE)
-		return index_error_invalid("buffer size does not match index footer size");
-
-	/* 160-bit SHA-1 over the content of the index file before this checksum. */
-	git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
-
-	if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
-		return index_error_invalid("calculated checksum does not match expected");
-
-#undef seek_forward
-
-	/* force sorting in the vector: the entries are
-	 * assured to be sorted on the index */
-	index->entries.sorted = 1;
-	return 0;
-}
-
-static int is_index_extended(git_index *index)
-{
-	unsigned int i, extended;
-	git_index_entry *entry;
-
-	extended = 0;
-
-	git_vector_foreach(&index->entries, i, entry) {
-		entry->flags &= ~GIT_IDXENTRY_EXTENDED;
-		if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
-			extended++;
-			entry->flags |= GIT_IDXENTRY_EXTENDED;
-		}
-	}
-
-	return extended;
-}
-
-static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
-{
-	void *mem = NULL;
-	struct entry_short *ondisk;
-	size_t path_len, disk_size;
-	char *path;
-
-	path_len = strlen(entry->path);
-
-	if (entry->flags & GIT_IDXENTRY_EXTENDED)
-		disk_size = long_entry_size(path_len);
-	else
-		disk_size = short_entry_size(path_len);
-
-	if (git_filebuf_reserve(file, &mem, disk_size) < 0)
-		return -1;
-
-	ondisk = (struct entry_short *)mem;
-
-	memset(ondisk, 0x0, disk_size);
-
-	/**
-	 * Yes, we have to truncate.
-	 *
-	 * The on-disk format for Index entries clearly defines
-	 * the time and size fields to be 4 bytes each -- so even if
-	 * we store these values with 8 bytes on-memory, they must
-	 * be truncated to 4 bytes before writing to disk.
-	 *
-	 * In 2038 I will be either too dead or too rich to care about this
-	 */
-	ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
-	ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
-	ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
-	ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
-	ondisk->dev = htonl(entry->dev);
-	ondisk->ino = htonl(entry->ino);
-	ondisk->mode = htonl(entry->mode);
-	ondisk->uid = htonl(entry->uid);
-	ondisk->gid = htonl(entry->gid);
-	ondisk->file_size = htonl((uint32_t)entry->file_size);
-
-	git_oid_cpy(&ondisk->oid, &entry->oid);
-
-	ondisk->flags = htons(entry->flags);
-
-	if (entry->flags & GIT_IDXENTRY_EXTENDED) {
-		struct entry_long *ondisk_ext;
-		ondisk_ext = (struct entry_long *)ondisk;
-		ondisk_ext->flags_extended = htons(entry->flags_extended);
-		path = ondisk_ext->path;
-	}
-	else
-		path = ondisk->path;
-
-	memcpy(path, entry->path, path_len);
-
-	return 0;
-}
-
-static int write_entries(git_index *index, git_filebuf *file)
-{
-	unsigned int i;
-
-	for (i = 0; i < index->entries.length; ++i) {
-		git_index_entry *entry;
-		entry = git_vector_get(&index->entries, i);
-		if (write_disk_entry(file, entry) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-static int write_index(git_index *index, git_filebuf *file)
-{
-	git_oid hash_final;
-
-	struct index_header header;
-
-	int is_extended;
-
-	assert(index && file);
-
-	is_extended = is_index_extended(index);
-
-	header.signature = htonl(INDEX_HEADER_SIG);
-	header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
-	header.entry_count = htonl(index->entries.length);
-
-	if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
-		return -1;
-
-	if (write_entries(index, file) < 0)
-		return -1;
-
-	/* TODO: write extensions (tree cache) */
-
-	/* get out the hash for all the contents we've appended to the file */
-	git_filebuf_hash(&hash_final, file);
-
-	/* write it at the end of the file */
-	return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
-}
-
-int git_index_entry_stage(const git_index_entry *entry)
-{
-	return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
-}
-
-static int read_tree_cb(const char *root, git_tree_entry *tentry, void *data)
-{
-	git_index *index = data;
-	git_index_entry *entry = NULL;
-	git_buf path = GIT_BUF_INIT;
-
-	if (git_tree_entry__is_tree(tentry))
-		return 0;
-
-	if (git_buf_joinpath(&path, root, tentry->filename) < 0)
-		return -1;
-
-	entry = git__calloc(1, sizeof(git_index_entry));
-	GITERR_CHECK_ALLOC(entry);
-
-	entry->mode = tentry->attr;
-	entry->oid = tentry->oid;
-	entry->path = git_buf_detach(&path);
-	git_buf_free(&path);
-
-	if (index_insert(index, entry, 0) < 0) {
-		index_entry_free(entry);
-		return -1;
-	}
-
-	return 0;
-}
-
-int git_index_read_tree(git_index *index, git_tree *tree)
-{
-	git_index_clear(index);
-
-	return git_tree_walk(tree, read_tree_cb, GIT_TREEWALK_POST, index);
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stddef.h>
+
+#include "common.h"
+#include "repository.h"
+#include "index.h"
+#include "tree.h"
+#include "tree-cache.h"
+#include "hash.h"
+#include "iterator.h"
+#include "pathspec.h"
+#include "git2/odb.h"
+#include "git2/oid.h"
+#include "git2/blob.h"
+#include "git2/config.h"
+
+#define entry_size(type,len) ((offsetof(type, path) + (len) + 8) & ~7)
+#define short_entry_size(len) entry_size(struct entry_short, len)
+#define long_entry_size(len) entry_size(struct entry_long, len)
+
+#define minimal_entry_size (offsetof(struct entry_short, path))
+
+static const size_t INDEX_FOOTER_SIZE = GIT_OID_RAWSZ;
+static const size_t INDEX_HEADER_SIZE = 12;
+
+static const unsigned int INDEX_VERSION_NUMBER = 2;
+static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
+
+static const unsigned int INDEX_HEADER_SIG = 0x44495243;
+static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
+static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
+
+#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
+
+struct index_header {
+	uint32_t signature;
+	uint32_t version;
+	uint32_t entry_count;
+};
+
+struct index_extension {
+	char signature[4];
+	uint32_t extension_size;
+};
+
+struct entry_time {
+	uint32_t seconds;
+	uint32_t nanoseconds;
+};
+
+struct entry_short {
+	struct entry_time ctime;
+	struct entry_time mtime;
+	uint32_t dev;
+	uint32_t ino;
+	uint32_t mode;
+	uint32_t uid;
+	uint32_t gid;
+	uint32_t file_size;
+	git_oid oid;
+	uint16_t flags;
+	char path[1]; /* arbitrary length */
+};
+
+struct entry_long {
+	struct entry_time ctime;
+	struct entry_time mtime;
+	uint32_t dev;
+	uint32_t ino;
+	uint32_t mode;
+	uint32_t uid;
+	uint32_t gid;
+	uint32_t file_size;
+	git_oid oid;
+	uint16_t flags;
+	uint16_t flags_extended;
+	char path[1]; /* arbitrary length */
+};
+
+struct entry_srch_key {
+	const char *path;
+	int stage;
+};
+
+/* local declarations */
+static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size);
+static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size);
+static int read_header(struct index_header *dest, const void *buffer);
+
+static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
+static bool is_index_extended(git_index *index);
+static int write_index(git_index *index, git_filebuf *file);
+
+static int index_find(size_t *at_pos, git_index *index, const char *path, int stage);
+
+static void index_entry_free(git_index_entry *entry);
+static void index_entry_reuc_free(git_index_reuc_entry *reuc);
+
+GIT_INLINE(int) index_entry_stage(const git_index_entry *entry)
+{
+	return (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT;
+}
+
+static int index_srch(const void *key, const void *array_member)
+{
+	const struct entry_srch_key *srch_key = key;
+	const git_index_entry *entry = array_member;
+	int ret;
+
+	ret = strcmp(srch_key->path, entry->path);
+
+	if (ret == 0)
+		ret = srch_key->stage - index_entry_stage(entry);
+
+	return ret;
+}
+
+static int index_isrch(const void *key, const void *array_member)
+{
+	const struct entry_srch_key *srch_key = key;
+	const git_index_entry *entry = array_member;
+	int ret;
+
+	ret = strcasecmp(srch_key->path, entry->path);
+
+	if (ret == 0)
+		ret = srch_key->stage - index_entry_stage(entry);
+
+	return ret;
+}
+
+static int index_cmp_path(const void *a, const void *b)
+{
+	return strcmp((const char *)a, (const char *)b);
+}
+
+static int index_icmp_path(const void *a, const void *b)
+{
+	return strcasecmp((const char *)a, (const char *)b);
+}
+
+static int index_srch_path(const void *path, const void *array_member)
+{
+	const git_index_entry *entry = array_member;
+
+	return strcmp((const char *)path, entry->path);
+}
+
+static int index_isrch_path(const void *path, const void *array_member)
+{
+	const git_index_entry *entry = array_member;
+
+	return strcasecmp((const char *)path, entry->path);
+}
+
+static int index_cmp(const void *a, const void *b)
+{
+	int diff;
+	const git_index_entry *entry_a = a;
+	const git_index_entry *entry_b = b;
+
+	diff = strcmp(entry_a->path, entry_b->path);
+
+	if (diff == 0)
+		diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));
+
+	return diff;
+}
+
+static int index_icmp(const void *a, const void *b)
+{
+	int diff;
+	const git_index_entry *entry_a = a;
+	const git_index_entry *entry_b = b;
+
+	diff = strcasecmp(entry_a->path, entry_b->path);
+
+	if (diff == 0)
+		diff = (index_entry_stage(entry_a) - index_entry_stage(entry_b));
+
+	return diff;
+}
+
+static int reuc_srch(const void *key, const void *array_member)
+{
+	const git_index_reuc_entry *reuc = array_member;
+
+	return strcmp(key, reuc->path);
+}
+
+static int reuc_isrch(const void *key, const void *array_member)
+{
+	const git_index_reuc_entry *reuc = array_member;
+
+	return strcasecmp(key, reuc->path);
+}
+
+static int reuc_cmp(const void *a, const void *b)
+{
+	const git_index_reuc_entry *info_a = a;
+	const git_index_reuc_entry *info_b = b;
+
+	return strcmp(info_a->path, info_b->path);
+}
+
+static int reuc_icmp(const void *a, const void *b)
+{
+	const git_index_reuc_entry *info_a = a;
+	const git_index_reuc_entry *info_b = b;
+
+	return strcasecmp(info_a->path, info_b->path);
+}
+
+static unsigned int index_create_mode(unsigned int mode)
+{
+	if (S_ISLNK(mode))
+		return S_IFLNK;
+
+	if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
+		return (S_IFLNK | S_IFDIR);
+
+	return S_IFREG | ((mode & 0100) ? 0755 : 0644);
+}
+
+static unsigned int index_merge_mode(
+	git_index *index, git_index_entry *existing, unsigned int mode)
+{
+	if (index->no_symlinks && S_ISREG(mode) &&
+		existing && S_ISLNK(existing->mode))
+		return existing->mode;
+
+	if (index->distrust_filemode && S_ISREG(mode))
+		return (existing && S_ISREG(existing->mode)) ?
+			existing->mode : index_create_mode(0666);
+
+	return index_create_mode(mode);
+}
+
+void git_index__set_ignore_case(git_index *index, bool ignore_case)
+{
+	index->ignore_case = ignore_case;
+
+	index->entries._cmp = ignore_case ? index_icmp : index_cmp;
+	index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path;
+	index->entries_search = ignore_case ? index_isrch : index_srch;
+	index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
+	index->entries.sorted = 0;
+	git_vector_sort(&index->entries);
+
+	index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp;
+	index->reuc_search = ignore_case ? reuc_isrch : reuc_srch;
+	index->reuc.sorted = 0;
+	git_vector_sort(&index->reuc);
+}
+
+int git_index_open(git_index **index_out, const char *index_path)
+{
+	git_index *index;
+
+	assert(index_out);
+
+	index = git__calloc(1, sizeof(git_index));
+	GITERR_CHECK_ALLOC(index);
+
+	if (index_path != NULL) {
+		index->index_file_path = git__strdup(index_path);
+		GITERR_CHECK_ALLOC(index->index_file_path);
+
+		/* Check if index file is stored on disk already */
+		if (git_path_exists(index->index_file_path) == true)
+			index->on_disk = 1;
+	}
+
+	if (git_vector_init(&index->entries, 32, index_cmp) < 0 ||
+		git_vector_init(&index->reuc, 32, reuc_cmp) < 0)
+		return -1;
+
+	index->entries_cmp_path = index_cmp_path;
+	index->entries_search = index_srch;
+	index->entries_search_path = index_srch_path;
+	index->reuc_search = reuc_srch;
+
+	*index_out = index;
+	GIT_REFCOUNT_INC(index);
+
+	return (index_path != NULL) ? git_index_read(index) : 0;
+}
+
+int git_index_new(git_index **out)
+{
+	return git_index_open(out, NULL);
+}
+
+static void index_free(git_index *index)
+{
+	git_index_clear(index);
+	git_vector_free(&index->entries);
+	git_vector_free(&index->reuc);
+
+	git__free(index->index_file_path);
+	git__free(index);
+}
+
+void git_index_free(git_index *index)
+{
+	if (index == NULL)
+		return;
+
+	GIT_REFCOUNT_DEC(index, index_free);
+}
+
+void git_index_clear(git_index *index)
+{
+	size_t i;
+
+	assert(index);
+
+	for (i = 0; i < index->entries.length; ++i) {
+		git_index_entry *e;
+		e = git_vector_get(&index->entries, i);
+		git__free(e->path);
+		git__free(e);
+	}
+	git_vector_clear(&index->entries);
+
+	git_index_reuc_clear(index);
+	
+	git_futils_filestamp_set(&index->stamp, NULL);
+
+	git_tree_cache_free(index->tree);
+	index->tree = NULL;
+}
+
+static int create_index_error(int error, const char *msg)
+{
+	giterr_set(GITERR_INDEX, msg);
+	return error;
+}
+
+int git_index_set_caps(git_index *index, unsigned int caps)
+{
+	int old_ignore_case;
+
+	assert(index);
+
+	old_ignore_case = index->ignore_case;
+
+	if (caps == GIT_INDEXCAP_FROM_OWNER) {
+		git_config *cfg;
+		int val;
+
+		if (INDEX_OWNER(index) == NULL ||
+			git_repository_config__weakptr(&cfg, INDEX_OWNER(index)) < 0)
+				return create_index_error(-1,
+					"Cannot get repository config to set index caps");
+
+		if (git_config_get_bool(&val, cfg, "core.ignorecase") == 0)
+			index->ignore_case = (val != 0);
+		if (git_config_get_bool(&val, cfg, "core.filemode") == 0)
+			index->distrust_filemode = (val == 0);
+		if (git_config_get_bool(&val, cfg, "core.symlinks") == 0)
+			index->no_symlinks = (val == 0);
+	}
+	else {
+		index->ignore_case = ((caps & GIT_INDEXCAP_IGNORE_CASE) != 0);
+		index->distrust_filemode = ((caps & GIT_INDEXCAP_NO_FILEMODE) != 0);
+		index->no_symlinks = ((caps & GIT_INDEXCAP_NO_SYMLINKS) != 0);
+	}
+
+	if (old_ignore_case != index->ignore_case) {
+		git_index__set_ignore_case(index, index->ignore_case);
+	}
+
+	return 0;
+}
+
+unsigned int git_index_caps(const git_index *index)
+{
+	return ((index->ignore_case ? GIT_INDEXCAP_IGNORE_CASE : 0) |
+			(index->distrust_filemode ? GIT_INDEXCAP_NO_FILEMODE : 0) |
+			(index->no_symlinks ? GIT_INDEXCAP_NO_SYMLINKS : 0));
+}
+
+int git_index_read(git_index *index)
+{
+	int error = 0, updated;
+	git_buf buffer = GIT_BUF_INIT;
+	git_futils_filestamp stamp = {0};
+
+	if (!index->index_file_path)
+		return create_index_error(-1,
+			"Failed to read index: The index is in-memory only");
+
+	if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
+		git_index_clear(index);
+		index->on_disk = 0;
+		return 0;
+	}
+
+	updated = git_futils_filestamp_check(&stamp, index->index_file_path);
+	if (updated <= 0)
+		return updated;
+
+	error = git_futils_readbuffer(&buffer, index->index_file_path);
+	if (error < 0)
+		return error;
+
+	git_index_clear(index);
+	error = parse_index(index, buffer.ptr, buffer.size);
+
+	if (!error)
+		git_futils_filestamp_set(&index->stamp, &stamp);
+
+	git_buf_free(&buffer);
+	return error;
+}
+
+int git_index_write(git_index *index)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	int error;
+
+	if (!index->index_file_path)
+		return create_index_error(-1,
+			"Failed to read index: The index is in-memory only");
+
+	git_vector_sort(&index->entries);
+	git_vector_sort(&index->reuc);
+
+	if ((error = git_filebuf_open(
+			 &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
+		return error;
+
+	if ((error = write_index(index, &file)) < 0) {
+		git_filebuf_cleanup(&file);
+		return error;
+	}
+
+	if ((error = git_filebuf_commit(&file, GIT_INDEX_FILE_MODE)) < 0)
+		return error;
+
+	error = git_futils_filestamp_check(&index->stamp, index->index_file_path);
+	if (error < 0)
+		return error;
+
+	index->on_disk = 1;
+	return 0;
+}
+
+int git_index_write_tree(git_oid *oid, git_index *index)
+{
+	git_repository *repo;
+
+	assert(oid && index);
+
+	repo = INDEX_OWNER(index);
+
+	if (repo == NULL)
+		return create_index_error(-1, "Failed to write tree. "
+		  "The index file is not backed up by an existing repository");
+
+	return git_tree__write_index(oid, index, repo);
+}
+
+int git_index_write_tree_to(git_oid *oid, git_index *index, git_repository *repo)
+{
+	assert(oid && index && repo);
+	return git_tree__write_index(oid, index, repo);
+}
+
+size_t git_index_entrycount(const git_index *index)
+{
+	assert(index);
+	return index->entries.length;
+}
+
+const git_index_entry *git_index_get_byindex(
+	git_index *index, size_t n)
+{
+	assert(index);
+	git_vector_sort(&index->entries);
+	return git_vector_get(&index->entries, n);
+}
+
+const git_index_entry *git_index_get_bypath(
+	git_index *index, const char *path, int stage)
+{
+	size_t pos;
+
+	assert(index);
+
+	git_vector_sort(&index->entries);
+
+	if (index_find(&pos, index, path, stage) < 0)
+		return NULL;
+
+	return git_index_get_byindex(index, pos);
+}
+
+void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
+{
+	entry->ctime.seconds = (git_time_t)st->st_ctime;
+	entry->mtime.seconds = (git_time_t)st->st_mtime;
+	/* entry->mtime.nanoseconds = st->st_mtimensec; */
+	/* entry->ctime.nanoseconds = st->st_ctimensec; */
+	entry->dev  = st->st_rdev;
+	entry->ino  = st->st_ino;
+	entry->mode = index_create_mode(st->st_mode);
+	entry->uid  = st->st_uid;
+	entry->gid  = st->st_gid;
+	entry->file_size = st->st_size;
+}
+
+int git_index_entry__cmp(const void *a, const void *b)
+{
+	const git_index_entry *entry_a = a;
+	const git_index_entry *entry_b = b;
+
+	return strcmp(entry_a->path, entry_b->path);
+}
+
+int git_index_entry__cmp_icase(const void *a, const void *b)
+{
+	const git_index_entry *entry_a = a;
+	const git_index_entry *entry_b = b;
+
+	return strcasecmp(entry_a->path, entry_b->path);
+}
+
+static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
+{
+	git_index_entry *entry = NULL;
+	struct stat st;
+	git_oid oid;
+	const char *workdir;
+	git_buf full_path = GIT_BUF_INIT;
+	int error;
+
+	if (INDEX_OWNER(index) == NULL)
+		return create_index_error(-1,
+			"Could not initialize index entry. "
+			"Index is not backed up by an existing repository.");
+
+	workdir = git_repository_workdir(INDEX_OWNER(index));
+
+	if (!workdir)
+		return create_index_error(GIT_EBAREREPO,
+			"Could not initialize index entry. Repository is bare");
+
+	if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
+		return error;
+
+	if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
+		git_buf_free(&full_path);
+		return error;
+	}
+
+	git_buf_free(&full_path); /* done with full path */
+
+	/* There is no need to validate the rel_path here, since it will be
+	 * immediately validated by the call to git_blob_create_fromfile.
+	 */
+
+	/* write the blob to disk and get the oid */
+	if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
+		return error;
+
+	entry = git__calloc(1, sizeof(git_index_entry));
+	GITERR_CHECK_ALLOC(entry);
+
+	git_index_entry__init_from_stat(entry, &st);
+
+	entry->oid = oid;
+	entry->path = git__strdup(rel_path);
+	GITERR_CHECK_ALLOC(entry->path);
+
+	*entry_out = entry;
+	return 0;
+}
+
+static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
+	const char *path,
+	int ancestor_mode, git_oid *ancestor_oid,
+	int our_mode, git_oid *our_oid, int their_mode, git_oid *their_oid)
+{
+	git_index_reuc_entry *reuc = NULL;
+
+	assert(reuc_out && path);
+
+	*reuc_out = NULL;
+
+	reuc = git__calloc(1, sizeof(git_index_reuc_entry));
+	GITERR_CHECK_ALLOC(reuc);
+
+	reuc->path = git__strdup(path);
+	if (reuc->path == NULL)
+		return -1;
+
+	if ((reuc->mode[0] = ancestor_mode) > 0)
+		git_oid_cpy(&reuc->oid[0], ancestor_oid);
+
+	if ((reuc->mode[1] = our_mode) > 0)
+		git_oid_cpy(&reuc->oid[1], our_oid);
+
+	if ((reuc->mode[2] = their_mode) > 0)
+		git_oid_cpy(&reuc->oid[2], their_oid);
+
+	*reuc_out = reuc;
+	return 0;
+}
+
+static void index_entry_reuc_free(git_index_reuc_entry *reuc)
+{
+	if (!reuc)
+		return;
+
+	git__free(reuc->path);
+	git__free(reuc);
+}
+
+static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
+{
+	git_index_entry *entry;
+
+	entry = git__malloc(sizeof(git_index_entry));
+	if (!entry)
+		return NULL;
+
+	memcpy(entry, source_entry, sizeof(git_index_entry));
+
+	/* duplicate the path string so we own it */
+	entry->path = git__strdup(entry->path);
+	if (!entry->path)
+		return NULL;
+
+	return entry;
+}
+
+static void index_entry_free(git_index_entry *entry)
+{
+	if (!entry)
+		return;
+	git__free(entry->path);
+	git__free(entry);
+}
+
+static int index_insert(git_index *index, git_index_entry *entry, int replace)
+{
+	size_t path_length, position;
+	git_index_entry **existing = NULL;
+
+	assert(index && entry && entry->path != NULL);
+
+	/* make sure that the path length flag is correct */
+	path_length = strlen(entry->path);
+
+	entry->flags &= ~GIT_IDXENTRY_NAMEMASK;
+
+	if (path_length < GIT_IDXENTRY_NAMEMASK)
+		entry->flags |= path_length & GIT_IDXENTRY_NAMEMASK;
+	else
+		entry->flags |= GIT_IDXENTRY_NAMEMASK;
+
+	/* look if an entry with this path already exists */
+	if (!index_find(&position, index, entry->path, index_entry_stage(entry))) {
+		existing = (git_index_entry **)&index->entries.contents[position];
+
+		/* update filemode to existing values if stat is not trusted */
+		entry->mode = index_merge_mode(index, *existing, entry->mode);
+	}
+
+	/* if replacing is not requested or no existing entry exists, just
+	 * insert entry at the end; the index is no longer sorted
+	 */
+	if (!replace || !existing)
+		return git_vector_insert(&index->entries, entry);
+
+	/* exists, replace it */
+	git__free((*existing)->path);
+	git__free(*existing);
+	*existing = entry;
+
+	return 0;
+}
+
+static int index_conflict_to_reuc(git_index *index, const char *path)
+{
+	git_index_entry *conflict_entries[3];
+	int ancestor_mode, our_mode, their_mode;
+	git_oid *ancestor_oid, *our_oid, *their_oid;
+	int ret;
+
+	if ((ret = git_index_conflict_get(&conflict_entries[0],
+		&conflict_entries[1], &conflict_entries[2], index, path)) < 0)
+		return ret;
+
+	ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode;
+	our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode;
+	their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode;
+
+	ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->oid;
+	our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->oid;
+	their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->oid;
+
+	if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid,
+		our_mode, our_oid, their_mode, their_oid)) >= 0)
+		ret = git_index_conflict_remove(index, path);
+
+	return ret;
+}
+
+int git_index_add_bypath(git_index *index, const char *path)
+{
+	git_index_entry *entry = NULL;
+	int ret;
+
+	assert(index && path);
+
+	if ((ret = index_entry_init(&entry, index, path)) < 0 ||
+		(ret = index_insert(index, entry, 1)) < 0)
+		goto on_error;
+
+	/* Adding implies conflict was resolved, move conflict entries to REUC */
+	if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
+		goto on_error;
+
+	git_tree_cache_invalidate_path(index->tree, entry->path);
+	return 0;
+
+on_error:
+	index_entry_free(entry);
+	return ret;
+}
+
+int git_index_remove_bypath(git_index *index, const char *path)
+{
+	int ret;
+
+	assert(index && path);
+
+	if (((ret = git_index_remove(index, path, 0)) < 0 &&
+		ret != GIT_ENOTFOUND) ||
+		((ret = index_conflict_to_reuc(index, path)) < 0 &&
+		ret != GIT_ENOTFOUND))
+		return ret;
+
+	return 0;
+}
+
+int git_index_add(git_index *index, const git_index_entry *source_entry)
+{
+	git_index_entry *entry = NULL;
+	int ret;
+
+	entry = index_entry_dup(source_entry);
+	if (entry == NULL)
+		return -1;
+
+	if ((ret = index_insert(index, entry, 1)) < 0) {
+		index_entry_free(entry);
+		return ret;
+	}
+
+	git_tree_cache_invalidate_path(index->tree, entry->path);
+	return 0;
+}
+
+int git_index_remove(git_index *index, const char *path, int stage)
+{
+	size_t position;
+	int error;
+	git_index_entry *entry;
+
+	git_vector_sort(&index->entries);
+
+	if (index_find(&position, index, path, stage) < 0)
+		return GIT_ENOTFOUND;
+
+	entry = git_vector_get(&index->entries, position);
+	if (entry != NULL)
+		git_tree_cache_invalidate_path(index->tree, entry->path);
+
+	error = git_vector_remove(&index->entries, position);
+
+	if (!error)
+		index_entry_free(entry);
+
+	return error;
+}
+
+int git_index_remove_directory(git_index *index, const char *dir, int stage)
+{
+	git_buf pfx = GIT_BUF_INIT;
+	int error = 0;
+	size_t pos;
+	git_index_entry *entry;
+
+	if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0)
+		return -1;
+
+	git_vector_sort(&index->entries);
+
+	pos = git_index__prefix_position(index, pfx.ptr);
+
+	while (1) {
+		entry = git_vector_get(&index->entries, pos);
+		if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
+			break;
+
+		if (index_entry_stage(entry) != stage) {
+			++pos;
+			continue;
+		}
+
+		git_tree_cache_invalidate_path(index->tree, entry->path);
+
+		if ((error = git_vector_remove(&index->entries, pos)) < 0)
+			break;
+		index_entry_free(entry);
+
+		/* removed entry at 'pos' so we don't need to increment it */
+	}
+
+	git_buf_free(&pfx);
+
+	return error;
+}
+
+static int index_find(size_t *at_pos, git_index *index, const char *path, int stage)
+{
+	struct entry_srch_key srch_key;
+
+	assert(path);
+
+	srch_key.path = path;
+	srch_key.stage = stage;
+
+	return git_vector_bsearch2(at_pos, &index->entries, index->entries_search, &srch_key);
+}
+
+int git_index_find(size_t *at_pos, git_index *index, const char *path)
+{
+	size_t pos;
+
+	assert(index && path);
+
+	if (git_vector_bsearch2(&pos, &index->entries, index->entries_search_path, path) < 0) {
+		giterr_set(GITERR_INDEX, "Index does not contain %s", path);
+		return GIT_ENOTFOUND;
+	}
+
+	/* Since our binary search only looked at path, we may be in the
+	 * middle of a list of stages.
+	 */
+	while (pos > 0) {
+		const git_index_entry *prev = git_vector_get(&index->entries, pos-1);
+
+		if (index->entries_cmp_path(prev->path, path) != 0)
+			break;
+
+		--pos;
+	}
+
+	if (at_pos)
+		*at_pos = pos;
+
+	return 0;
+}
+
+size_t git_index__prefix_position(git_index *index, const char *path)
+{
+	struct entry_srch_key srch_key;
+	size_t pos;
+
+	srch_key.path = path;
+	srch_key.stage = 0;
+
+	git_vector_sort(&index->entries);
+	git_vector_bsearch2(
+		&pos, &index->entries, index->entries_search, &srch_key);
+
+	return pos;
+}
+
+int git_index_conflict_add(git_index *index,
+	const git_index_entry *ancestor_entry,
+	const git_index_entry *our_entry,
+	const git_index_entry *their_entry)
+{
+	git_index_entry *entries[3] = { 0 };
+	unsigned short i;
+	int ret = 0;
+
+	assert (index);
+
+	if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) ||
+		(our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) ||
+		(their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL))
+		return -1;
+
+	for (i = 0; i < 3; i++) {
+		if (entries[i] == NULL)
+			continue;
+
+		/* Make sure stage is correct */
+		entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) |
+			((i+1) << GIT_IDXENTRY_STAGESHIFT);
+
+		if ((ret = index_insert(index, entries[i], 1)) < 0)
+			goto on_error;
+	}
+
+	return 0;
+
+on_error:
+	for (i = 0; i < 3; i++) {
+		if (entries[i] != NULL)
+			index_entry_free(entries[i]);
+	}
+
+	return ret;
+}
+
+int git_index_conflict_get(git_index_entry **ancestor_out,
+	git_index_entry **our_out,
+	git_index_entry **their_out,
+	git_index *index, const char *path)
+{
+	size_t pos, posmax;
+	int stage;
+	git_index_entry *conflict_entry;
+	int error = GIT_ENOTFOUND;
+
+	assert(ancestor_out && our_out && their_out && index && path);
+
+	*ancestor_out = NULL;
+	*our_out = NULL;
+	*their_out = NULL;
+
+	if (git_index_find(&pos, index, path) < 0)
+		return GIT_ENOTFOUND;
+
+	for (posmax = git_index_entrycount(index); pos < posmax; ++pos) {
+
+		conflict_entry = git_vector_get(&index->entries, pos);
+
+		if (index->entries_cmp_path(conflict_entry->path, path) != 0)
+			break;
+
+		stage = index_entry_stage(conflict_entry);
+
+		switch (stage) {
+		case 3:
+			*their_out = conflict_entry;
+			error = 0;
+			break;
+		case 2:
+			*our_out = conflict_entry;
+			error = 0;
+			break;
+		case 1:
+			*ancestor_out = conflict_entry;
+			error = 0;
+			break;
+		default:
+			break;
+		};
+	}
+
+	return error;
+}
+
+int git_index_conflict_remove(git_index *index, const char *path)
+{
+	size_t pos, posmax;
+	git_index_entry *conflict_entry;
+	int error = 0;
+
+	assert(index && path);
+
+	if (git_index_find(&pos, index, path) < 0)
+		return GIT_ENOTFOUND;
+
+	posmax = git_index_entrycount(index);
+
+	while (pos < posmax) {
+		conflict_entry = git_vector_get(&index->entries, pos);
+
+		if (index->entries_cmp_path(conflict_entry->path, path) != 0)
+			break;
+
+		if (index_entry_stage(conflict_entry) == 0) {
+			pos++;
+			continue;
+		}
+
+		if ((error = git_vector_remove(&index->entries, pos)) < 0)
+			return error;
+
+		index_entry_free(conflict_entry);
+		posmax--;
+	}
+
+	return 0;
+}
+
+static int index_conflicts_match(const git_vector *v, size_t idx)
+{
+	git_index_entry *entry = git_vector_get(v, idx);
+
+	if (index_entry_stage(entry) > 0) {
+		index_entry_free(entry);
+		return 1;
+	}
+
+	return 0;
+}
+
+void git_index_conflict_cleanup(git_index *index)
+{
+	assert(index);
+	git_vector_remove_matching(&index->entries, index_conflicts_match);
+}
+
+int git_index_has_conflicts(const git_index *index)
+{
+	size_t i;
+	git_index_entry *entry;
+
+	assert(index);
+
+	git_vector_foreach(&index->entries, i, entry) {
+		if (index_entry_stage(entry) > 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+unsigned int git_index_reuc_entrycount(git_index *index)
+{
+	assert(index);
+	return (unsigned int)index->reuc.length;
+}
+
+static int index_reuc_insert(git_index *index, git_index_reuc_entry *reuc, int replace)
+{
+	git_index_reuc_entry **existing = NULL;
+	size_t position;
+
+	assert(index && reuc && reuc->path != NULL);
+
+	if (!git_index_reuc_find(&position, index, reuc->path))
+		existing = (git_index_reuc_entry **)&index->reuc.contents[position];
+
+	if (!replace || !existing)
+		return git_vector_insert(&index->reuc, reuc);
+
+	/* exists, replace it */
+	git__free((*existing)->path);
+	git__free(*existing);
+	*existing = reuc;
+
+	return 0;
+}
+
+int git_index_reuc_add(git_index *index, const char *path,
+	int ancestor_mode, git_oid *ancestor_oid,
+	int our_mode, git_oid *our_oid,
+	int their_mode, git_oid *their_oid)
+{
+	git_index_reuc_entry *reuc = NULL;
+	int error = 0;
+
+	assert(index && path);
+
+	if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
+		(error = index_reuc_insert(index, reuc, 1)) < 0)
+	{
+		index_entry_reuc_free(reuc);
+		return error;
+	}
+
+	return error;
+} 
+
+int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path)
+{
+	return git_vector_bsearch2(at_pos, &index->reuc, index->reuc_search, path);
+}
+
+const git_index_reuc_entry *git_index_reuc_get_bypath(
+	git_index *index, const char *path)
+{
+	size_t pos;
+	assert(index && path);
+
+	if (!index->reuc.length)
+		return NULL;
+
+	git_vector_sort(&index->reuc);
+
+	if (git_index_reuc_find(&pos, index, path) < 0)
+		return NULL;
+
+	return git_vector_get(&index->reuc, pos);
+}
+
+const git_index_reuc_entry *git_index_reuc_get_byindex(
+	git_index *index, size_t n)
+{
+	assert(index);
+
+	git_vector_sort(&index->reuc);
+	return git_vector_get(&index->reuc, n);
+}
+
+int git_index_reuc_remove(git_index *index, size_t position)
+{
+	int error;
+	git_index_reuc_entry *reuc;
+
+	git_vector_sort(&index->reuc);
+
+	reuc = git_vector_get(&index->reuc, position);
+	error = git_vector_remove(&index->reuc, position);
+
+	if (!error)
+		index_entry_reuc_free(reuc);
+
+	return error;
+}
+
+void git_index_reuc_clear(git_index *index)
+{
+	size_t i;
+	git_index_reuc_entry *reuc;
+
+	assert(index);
+
+	git_vector_foreach(&index->reuc, i, reuc) {
+		git__free(reuc->path);
+		git__free(reuc);
+	}
+
+	git_vector_clear(&index->reuc);
+}
+
+static int index_error_invalid(const char *message)
+{
+	giterr_set(GITERR_INDEX, "Invalid data in index - %s", message);
+	return -1;
+}
+
+static int read_reuc(git_index *index, const char *buffer, size_t size)
+{
+	const char *endptr;
+	size_t len;
+	int i;
+
+	/* This gets called multiple times, the vector might already be initialized */
+	if (index->reuc._alloc_size == 0 && git_vector_init(&index->reuc, 16, reuc_cmp) < 0)
+		return -1;
+
+	while (size) {
+		git_index_reuc_entry *lost;
+
+		len = strlen(buffer) + 1;
+		if (size <= len)
+			return index_error_invalid("reading reuc entries");
+
+		lost = git__malloc(sizeof(git_index_reuc_entry));
+		GITERR_CHECK_ALLOC(lost);
+
+		if (git_vector_insert(&index->reuc, lost) < 0)
+			return -1;
+
+		/* read NUL-terminated pathname for entry */
+		lost->path = git__strdup(buffer);
+		GITERR_CHECK_ALLOC(lost->path);
+
+		size -= len;
+		buffer += len;
+
+		/* read 3 ASCII octal numbers for stage entries */
+		for (i = 0; i < 3; i++) {
+			int tmp;
+
+			if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
+				!endptr || endptr == buffer || *endptr ||
+				(unsigned)tmp > UINT_MAX)
+				return index_error_invalid("reading reuc entry stage");
+
+			lost->mode[i] = tmp;
+
+			len = (endptr + 1) - buffer;
+			if (size <= len)
+				return index_error_invalid("reading reuc entry stage");
+
+			size -= len;
+			buffer += len;
+		}
+
+		/* read up to 3 OIDs for stage entries */
+		for (i = 0; i < 3; i++) {
+			if (!lost->mode[i])
+				continue;
+			if (size < 20)
+				return index_error_invalid("reading reuc entry oid");
+
+			git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
+			size -= 20;
+			buffer += 20;
+		}
+	}
+
+	/* entries are guaranteed to be sorted on-disk */
+	index->reuc.sorted = 1;
+
+	return 0;
+}
+
+static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size)
+{
+	size_t path_length, entry_size;
+	uint16_t flags_raw;
+	const char *path_ptr;
+	const struct entry_short *source = buffer;
+
+	if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size)
+		return 0;
+
+	memset(dest, 0x0, sizeof(git_index_entry));
+
+	dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds);
+	dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds);
+	dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds);
+	dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds);
+	dest->dev = ntohl(source->dev);
+	dest->ino = ntohl(source->ino);
+	dest->mode = ntohl(source->mode);
+	dest->uid = ntohl(source->uid);
+	dest->gid = ntohl(source->gid);
+	dest->file_size = ntohl(source->file_size);
+	git_oid_cpy(&dest->oid, &source->oid);
+	dest->flags = ntohs(source->flags);
+
+	if (dest->flags & GIT_IDXENTRY_EXTENDED) {
+		const struct entry_long *source_l = (const struct entry_long *)source;
+		path_ptr = source_l->path;
+
+		flags_raw = ntohs(source_l->flags_extended);
+		memcpy(&dest->flags_extended, &flags_raw, 2);
+	} else
+		path_ptr = source->path;
+
+	path_length = dest->flags & GIT_IDXENTRY_NAMEMASK;
+
+	/* if this is a very long string, we must find its
+	 * real length without overflowing */
+	if (path_length == 0xFFF) {
+		const char *path_end;
+
+		path_end = memchr(path_ptr, '\0', buffer_size);
+		if (path_end == NULL)
+			return 0;
+
+		path_length = path_end - path_ptr;
+	}
+
+	if (dest->flags & GIT_IDXENTRY_EXTENDED)
+		entry_size = long_entry_size(path_length);
+	else
+		entry_size = short_entry_size(path_length);
+
+	if (INDEX_FOOTER_SIZE + entry_size > buffer_size)
+		return 0;
+
+	dest->path = git__strdup(path_ptr);
+	assert(dest->path);
+
+	return entry_size;
+}
+
+static int read_header(struct index_header *dest, const void *buffer)
+{
+	const struct index_header *source = buffer;
+
+	dest->signature = ntohl(source->signature);
+	if (dest->signature != INDEX_HEADER_SIG)
+		return index_error_invalid("incorrect header signature");
+
+	dest->version = ntohl(source->version);
+	if (dest->version != INDEX_VERSION_NUMBER_EXT &&
+		dest->version != INDEX_VERSION_NUMBER)
+		return index_error_invalid("incorrect header version");
+
+	dest->entry_count = ntohl(source->entry_count);
+	return 0;
+}
+
+static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size)
+{
+	const struct index_extension *source;
+	struct index_extension dest;
+	size_t total_size;
+
+	source = (const struct index_extension *)(buffer);
+
+	memcpy(dest.signature, source->signature, 4);
+	dest.extension_size = ntohl(source->extension_size);
+
+	total_size = dest.extension_size + sizeof(struct index_extension);
+
+	if (buffer_size - total_size < INDEX_FOOTER_SIZE)
+		return 0;
+
+	/* optional extension */
+	if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
+		/* tree cache */
+		if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
+			if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0)
+				return 0;
+		} else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
+			if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
+				return 0;
+		}
+		/* else, unsupported extension. We cannot parse this, but we can skip
+		 * it by returning `total_size */
+	} else {
+		/* we cannot handle non-ignorable extensions;
+		 * in fact they aren't even defined in the standard */
+		return 0;
+	}
+
+	return total_size;
+}
+
+static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
+{
+	unsigned int i;
+	struct index_header header;
+	git_oid checksum_calculated, checksum_expected;
+
+#define seek_forward(_increase) { \
+	if (_increase >= buffer_size) \
+		return index_error_invalid("ran out of data while parsing"); \
+	buffer += _increase; \
+	buffer_size -= _increase;\
+}
+
+	if (buffer_size < INDEX_HEADER_SIZE + INDEX_FOOTER_SIZE)
+		return index_error_invalid("insufficient buffer space");
+
+	/* Precalculate the SHA1 of the files's contents -- we'll match it to
+	 * the provided SHA1 in the footer */
+	git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE);
+
+	/* Parse header */
+	if (read_header(&header, buffer) < 0)
+		return -1;
+
+	seek_forward(INDEX_HEADER_SIZE);
+
+	git_vector_clear(&index->entries);
+
+	/* Parse all the entries */
+	for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) {
+		size_t entry_size;
+		git_index_entry *entry;
+
+		entry = git__malloc(sizeof(git_index_entry));
+		GITERR_CHECK_ALLOC(entry);
+
+		entry_size = read_entry(entry, buffer, buffer_size);
+
+		/* 0 bytes read means an object corruption */
+		if (entry_size == 0)
+			return index_error_invalid("invalid entry");
+
+		if (git_vector_insert(&index->entries, entry) < 0)
+			return -1;
+
+		seek_forward(entry_size);
+	}
+
+	if (i != header.entry_count)
+		return index_error_invalid("header entries changed while parsing");
+
+	/* There's still space for some extensions! */
+	while (buffer_size > INDEX_FOOTER_SIZE) {
+		size_t extension_size;
+
+		extension_size = read_extension(index, buffer, buffer_size);
+
+		/* see if we have read any bytes from the extension */
+		if (extension_size == 0)
+			return index_error_invalid("extension size is zero");
+
+		seek_forward(extension_size);
+	}
+
+	if (buffer_size != INDEX_FOOTER_SIZE)
+		return index_error_invalid("buffer size does not match index footer size");
+
+	/* 160-bit SHA-1 over the content of the index file before this checksum. */
+	git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer);
+
+	if (git_oid_cmp(&checksum_calculated, &checksum_expected) != 0)
+		return index_error_invalid("calculated checksum does not match expected");
+
+#undef seek_forward
+
+	/* Entries are stored case-sensitively on disk. */
+	index->entries.sorted = !index->ignore_case;
+	git_vector_sort(&index->entries);
+
+	return 0;
+}
+
+static bool is_index_extended(git_index *index)
+{
+	size_t i, extended;
+	git_index_entry *entry;
+
+	extended = 0;
+
+	git_vector_foreach(&index->entries, i, entry) {
+		entry->flags &= ~GIT_IDXENTRY_EXTENDED;
+		if (entry->flags_extended & GIT_IDXENTRY_EXTENDED_FLAGS) {
+			extended++;
+			entry->flags |= GIT_IDXENTRY_EXTENDED;
+		}
+	}
+
+	return (extended > 0);
+}
+
+static int write_disk_entry(git_filebuf *file, git_index_entry *entry)
+{
+	void *mem = NULL;
+	struct entry_short *ondisk;
+	size_t path_len, disk_size;
+	char *path;
+
+	path_len = strlen(entry->path);
+
+	if (entry->flags & GIT_IDXENTRY_EXTENDED)
+		disk_size = long_entry_size(path_len);
+	else
+		disk_size = short_entry_size(path_len);
+
+	if (git_filebuf_reserve(file, &mem, disk_size) < 0)
+		return -1;
+
+	ondisk = (struct entry_short *)mem;
+
+	memset(ondisk, 0x0, disk_size);
+
+	/**
+	 * Yes, we have to truncate.
+	 *
+	 * The on-disk format for Index entries clearly defines
+	 * the time and size fields to be 4 bytes each -- so even if
+	 * we store these values with 8 bytes on-memory, they must
+	 * be truncated to 4 bytes before writing to disk.
+	 *
+	 * In 2038 I will be either too dead or too rich to care about this
+	 */
+	ondisk->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
+	ondisk->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
+	ondisk->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
+	ondisk->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
+	ondisk->dev = htonl(entry->dev);
+	ondisk->ino = htonl(entry->ino);
+	ondisk->mode = htonl(entry->mode);
+	ondisk->uid = htonl(entry->uid);
+	ondisk->gid = htonl(entry->gid);
+	ondisk->file_size = htonl((uint32_t)entry->file_size);
+
+	git_oid_cpy(&ondisk->oid, &entry->oid);
+
+	ondisk->flags = htons(entry->flags);
+
+	if (entry->flags & GIT_IDXENTRY_EXTENDED) {
+		struct entry_long *ondisk_ext;
+		ondisk_ext = (struct entry_long *)ondisk;
+		ondisk_ext->flags_extended = htons(entry->flags_extended);
+		path = ondisk_ext->path;
+	}
+	else
+		path = ondisk->path;
+
+	memcpy(path, entry->path, path_len);
+
+	return 0;
+}
+
+static int write_entries(git_index *index, git_filebuf *file)
+{
+	int error = 0;
+	size_t i;
+	git_vector case_sorted;
+	git_index_entry *entry;
+	git_vector *out = &index->entries;
+
+	/* If index->entries is sorted case-insensitively, then we need
+	 * to re-sort it case-sensitively before writing */
+	if (index->ignore_case) {
+		git_vector_dup(&case_sorted, &index->entries, index_cmp);
+		git_vector_sort(&case_sorted);
+		out = &case_sorted;
+	}
+
+	git_vector_foreach(out, i, entry)
+		if ((error = write_disk_entry(file, entry)) < 0)
+			break;
+
+	if (index->ignore_case)
+		git_vector_free(&case_sorted);
+
+	return error;
+}
+
+static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data)
+{
+	struct index_extension ondisk;
+	int error = 0;
+
+	memset(&ondisk, 0x0, sizeof(struct index_extension));
+	memcpy(&ondisk, header, 4);
+	ondisk.extension_size = htonl(header->extension_size);
+
+	if ((error = git_filebuf_write(file, &ondisk, sizeof(struct index_extension))) == 0)
+		error = git_filebuf_write(file, data->ptr, data->size);
+
+	return error;
+}
+
+static int create_reuc_extension_data(git_buf *reuc_buf, git_index_reuc_entry *reuc)
+{
+	int i;
+	int error = 0;
+
+	if ((error = git_buf_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0)
+		return error;
+
+	for (i = 0; i < 3; i++) {
+		if ((error = git_buf_printf(reuc_buf, "%o", reuc->mode[i])) < 0 ||
+			(error = git_buf_put(reuc_buf, "\0", 1)) < 0)
+			return error;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (reuc->mode[i] && (error = git_buf_put(reuc_buf, (char *)&reuc->oid[i].id, GIT_OID_RAWSZ)) < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int write_reuc_extension(git_index *index, git_filebuf *file)
+{
+	git_buf reuc_buf = GIT_BUF_INIT;
+	git_vector *out = &index->reuc;
+	git_index_reuc_entry *reuc;
+	struct index_extension extension;
+	size_t i;
+	int error = 0;
+
+	git_vector_foreach(out, i, reuc) {
+		if ((error = create_reuc_extension_data(&reuc_buf, reuc)) < 0)
+			goto done;
+	}
+
+	memset(&extension, 0x0, sizeof(struct index_extension));
+	memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4);
+	extension.extension_size = (uint32_t)reuc_buf.size;
+
+	error = write_extension(file, &extension, &reuc_buf);
+
+	git_buf_free(&reuc_buf);
+
+done:
+	return error;
+}
+
+static int write_index(git_index *index, git_filebuf *file)
+{
+	git_oid hash_final;
+	struct index_header header;
+	bool is_extended;
+
+	assert(index && file);
+
+	is_extended = is_index_extended(index);
+
+	header.signature = htonl(INDEX_HEADER_SIG);
+	header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER);
+	header.entry_count = htonl((uint32_t)index->entries.length);
+
+	if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
+		return -1;
+
+	if (write_entries(index, file) < 0)
+		return -1;
+
+	/* TODO: write tree cache extension */
+
+	/* write the reuc extension */
+	if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
+		return -1;
+
+	/* get out the hash for all the contents we've appended to the file */
+	git_filebuf_hash(&hash_final, file);
+
+	/* write it at the end of the file */
+	return git_filebuf_write(file, hash_final.id, GIT_OID_RAWSZ);
+}
+
+int git_index_entry_stage(const git_index_entry *entry)
+{
+	return index_entry_stage(entry);
+}
+
+typedef struct read_tree_data {
+	git_index *index;
+	git_transfer_progress *stats;
+} read_tree_data;
+
+static int read_tree_cb(const char *root, const git_tree_entry *tentry, void *data)
+{
+	git_index *index = (git_index *)data;
+	git_index_entry *entry = NULL;
+	git_buf path = GIT_BUF_INIT;
+
+	if (git_tree_entry__is_tree(tentry))
+		return 0;
+
+	if (git_buf_joinpath(&path, root, tentry->filename) < 0)
+		return -1;
+
+	entry = git__calloc(1, sizeof(git_index_entry));
+	GITERR_CHECK_ALLOC(entry);
+
+	entry->mode = tentry->attr;
+	entry->oid = tentry->oid;
+
+	if (path.size < GIT_IDXENTRY_NAMEMASK)
+		entry->flags = path.size & GIT_IDXENTRY_NAMEMASK;
+	else
+		entry->flags = GIT_IDXENTRY_NAMEMASK;
+
+	entry->path = git_buf_detach(&path);
+	git_buf_free(&path);
+
+	if (git_vector_insert(&index->entries, entry) < 0) {
+		index_entry_free(entry);
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_index_read_tree(git_index *index, const git_tree *tree)
+{
+	git_index_clear(index);
+
+	return git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, index);
+}
+
+git_repository *git_index_owner(const git_index *index)
+{
+	return INDEX_OWNER(index);
 }
diff --git a/libgit2/src/index.h b/libgit2/src/index.h
--- a/libgit2/src/index.h
+++ b/libgit2/src/index.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -22,17 +22,32 @@
 
 	char *index_file_path;
 
-	time_t last_modified;
+	git_futils_filestamp stamp;
 	git_vector entries;
 
 	unsigned int on_disk:1;
+
+	unsigned int ignore_case:1;
+	unsigned int distrust_filemode:1;
+	unsigned int no_symlinks:1;
+
 	git_tree_cache *tree;
 
-	git_vector unmerged;
+	git_vector reuc;
+
+	git_vector_cmp entries_cmp_path;
+	git_vector_cmp entries_search;
+	git_vector_cmp entries_search_path;
+	git_vector_cmp reuc_search;
 };
 
-extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
+extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
 
-extern unsigned int git_index__prefix_position(git_index *index, const char *path);
+extern size_t git_index__prefix_position(git_index *index, const char *path);
+
+extern int git_index_entry__cmp(const void *a, const void *b);
+extern int git_index_entry__cmp_icase(const void *a, const void *b);
+
+extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
 
 #endif
diff --git a/libgit2/src/indexer.c b/libgit2/src/indexer.c
--- a/libgit2/src/indexer.c
+++ b/libgit2/src/indexer.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,7 +17,7 @@
 #include "posix.h"
 #include "pack.h"
 #include "filebuf.h"
-#include "sha1.h"
+#include "oidmap.h"
 
 #define UINT31_MAX (0x7FFFFFFF)
 
@@ -28,39 +28,32 @@
 	uint64_t offset_long;
 };
 
-struct git_indexer {
-	struct git_pack_file *pack;
-	size_t nr_objects;
-	git_vector objects;
-	git_filebuf file;
-	unsigned int fanout[256];
-	git_oid hash;
-};
-
 struct git_indexer_stream {
 	unsigned int parsed_header :1,
-		opened_pack;
+		opened_pack :1,
+		have_stream :1,
+		have_delta :1;
 	struct git_pack_file *pack;
 	git_filebuf pack_file;
-	git_filebuf index_file;
 	git_off_t off;
+	git_off_t entry_start;
+	git_packfile_stream stream;
 	size_t nr_objects;
 	git_vector objects;
 	git_vector deltas;
 	unsigned int fanout[256];
+	git_hash_ctx hash_ctx;
 	git_oid hash;
+	git_transfer_progress_callback progress_cb;
+	void *progress_payload;
+	char objbuf[8*1024];
 };
 
 struct delta_info {
 	git_off_t delta_off;
 };
 
-const git_oid *git_indexer_hash(git_indexer *idx)
-{
-	return &idx->hash;
-}
-
-const git_oid *git_indexer_stream_hash(git_indexer_stream *idx)
+const git_oid *git_indexer_stream_hash(const git_indexer_stream *idx)
 {
 	return &idx->hash;
 }
@@ -130,23 +123,21 @@
 	return git_oid_cmp(&entrya->oid, &entryb->oid);
 }
 
-static int cache_cmp(const void *a, const void *b)
-{
-	const struct git_pack_entry *ea = a;
-	const struct git_pack_entry *eb = b;
-
-	return git_oid_cmp(&ea->sha1, &eb->sha1);
-}
-
-int git_indexer_stream_new(git_indexer_stream **out, const char *prefix)
+int git_indexer_stream_new(
+		git_indexer_stream **out,
+		const char *prefix,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload)
 {
 	git_indexer_stream *idx;
 	git_buf path = GIT_BUF_INIT;
-	static const char suff[] = "/objects/pack/pack-received";
+	static const char suff[] = "/pack";
 	int error;
 
 	idx = git__calloc(1, sizeof(git_indexer_stream));
 	GITERR_CHECK_ALLOC(idx);
+	idx->progress_cb = progress_cb;
+	idx->progress_payload = progress_payload;
 
 	error = git_buf_joinpath(&path, prefix, suff);
 	if (error < 0)
@@ -171,69 +162,171 @@
 /* Try to store the delta so we can try to resolve it later */
 static int store_delta(git_indexer_stream *idx)
 {
-	git_otype type;
-	git_mwindow *w = NULL;
-	git_mwindow_file *mwf = &idx->pack->mwf;
-	git_off_t entry_start = idx->off;
 	struct delta_info *delta;
-	size_t entry_size;
-	git_rawobj obj;
-	int error;
 
-	/*
-	 * ref-delta objects can refer to object that we haven't
-	 * found yet, so give it another opportunity
-	 */
-	if (git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off) < 0)
+	delta = git__calloc(1, sizeof(struct delta_info));
+	GITERR_CHECK_ALLOC(delta);
+	delta->delta_off = idx->entry_start;
+
+	if (git_vector_insert(&idx->deltas, delta) < 0)
 		return -1;
 
-	git_mwindow_close(&w);
+	return 0;
+}
 
-	/* If it's not a delta, mark it as failure, we can't do anything with it */
-	if (type != GIT_OBJ_REF_DELTA && type != GIT_OBJ_OFS_DELTA)
-		return -1;
+static void hash_header(git_hash_ctx *ctx, git_off_t len, git_otype type)
+{
+	char buffer[64];
+	size_t hdrlen;
 
+	hdrlen = git_odb__format_object_header(buffer, sizeof(buffer), (size_t)len, type);
+	git_hash_update(ctx, buffer, hdrlen);
+}
+
+static int hash_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+{
+	ssize_t read;
+
+	assert(idx && stream);
+
+	do {
+		if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
+			break;
+
+		git_hash_update(&idx->hash_ctx, idx->objbuf, read);
+	} while (read > 0);
+
+	if (read < 0)
+		return (int)read;
+
+	return 0;
+}
+
+/* In order to create the packfile stream, we need to skip over the delta base description */
+static int advance_delta_offset(git_indexer_stream *idx, git_otype type)
+{
+	git_mwindow *w = NULL;
+
+	assert(type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA);
+
 	if (type == GIT_OBJ_REF_DELTA) {
 		idx->off += GIT_OID_RAWSZ;
 	} else {
-		git_off_t base_off;
-
-		base_off = get_delta_base(idx->pack, &w, &idx->off, type, entry_start);
+		git_off_t base_off = get_delta_base(idx->pack, &w, &idx->off, type, idx->entry_start);
 		git_mwindow_close(&w);
 		if (base_off < 0)
 			return (int)base_off;
 	}
 
-	error = packfile_unpack_compressed(&obj, idx->pack, &w, &idx->off, entry_size, type);
-	if (error == GIT_EBUFS) {
-		idx->off = entry_start;
-		return GIT_EBUFS;
-	} else if (error < 0){
-		return -1;
+	return 0;
+}
+
+/* Read from the stream and discard any output */
+static int read_object_stream(git_indexer_stream *idx, git_packfile_stream *stream)
+{
+	ssize_t read;
+
+	assert(stream);
+
+	do {
+		read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf));
+	} while (read > 0);
+
+	if (read < 0)
+		return (int)read;
+
+	return 0;
+}
+
+static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, git_off_t start, git_off_t size)
+{
+	void *ptr;
+	uint32_t crc;
+	unsigned int left, len;
+	git_mwindow *w = NULL;
+
+	crc = crc32(0L, Z_NULL, 0);
+	while (size) {
+		ptr = git_mwindow_open(mwf, &w, start, (size_t)size, &left);
+		if (ptr == NULL)
+			return -1;
+
+		len = min(left, (unsigned int)size);
+		crc = crc32(crc, ptr, len);
+		size -= len;
+		start += len;
+		git_mwindow_close(&w);
 	}
 
-	delta = git__calloc(1, sizeof(struct delta_info));
-	GITERR_CHECK_ALLOC(delta);
-	delta->delta_off = entry_start;
+	*crc_out = htonl(crc);
+	return 0;
+}
 
-	git__free(obj.data);
+static int store_object(git_indexer_stream *idx)
+{
+	int i, error;
+	khiter_t k;
+	git_oid oid;
+	struct entry *entry;
+	git_off_t entry_size;
+	struct git_pack_entry *pentry;
+	git_hash_ctx *ctx = &idx->hash_ctx;
+	git_off_t entry_start = idx->entry_start;
 
-	if (git_vector_insert(&idx->deltas, delta) < 0)
-		return -1;
+	entry = git__calloc(1, sizeof(*entry));
+	GITERR_CHECK_ALLOC(entry);
 
+	pentry = git__malloc(sizeof(struct git_pack_entry));
+	GITERR_CHECK_ALLOC(pentry);
+
+	git_hash_final(&oid, ctx);
+	entry_size = idx->off - entry_start;
+	if (entry_start > UINT31_MAX) {
+		entry->offset = UINT32_MAX;
+		entry->offset_long = entry_start;
+	} else {
+		entry->offset = (uint32_t)entry_start;
+	}
+
+	git_oid_cpy(&pentry->sha1, &oid);
+	pentry->offset = entry_start;
+
+	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+	if (!error) {
+		git__free(pentry);
+		goto on_error;
+	}
+
+	kh_value(idx->pack->idx_cache, k) = pentry;
+
+	git_oid_cpy(&entry->oid, &oid);
+
+	if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
+		goto on_error;
+
+	/* Add the object to the list */
+	if (git_vector_insert(&idx->objects, entry) < 0)
+		goto on_error;
+
+	for (i = oid.id[0]; i < 256; ++i) {
+		idx->fanout[i]++;
+	}
+
 	return 0;
+
+on_error:
+	git__free(entry);
+
+	return -1;
 }
 
 static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
 {
-	int i;
+	int i, error;
+	khiter_t k;
 	git_oid oid;
-	void *packed;
 	size_t entry_size;
-	unsigned int left;
 	struct entry *entry;
-	git_mwindow *w = NULL;
-	git_mwindow_file *mwf = &idx->pack->mwf;
 	struct git_pack_entry *pentry;
 
 	entry = git__calloc(1, sizeof(*entry));
@@ -257,20 +350,21 @@
 
 	git_oid_cpy(&pentry->sha1, &oid);
 	pentry->offset = entry_start;
-	if (git_vector_insert(&idx->pack->cache, pentry) < 0)
+	k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
+	if (!error) {
+		git__free(pentry);
 		goto on_error;
+	}
 
+	kh_value(idx->pack->idx_cache, k) = pentry;
+
 	git_oid_cpy(&entry->oid, &oid);
 	entry->crc = crc32(0L, Z_NULL, 0);
 
 	entry_size = (size_t)(idx->off - entry_start);
-	packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
-	if (packed == NULL)
+	if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
 		goto on_error;
 
-	entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
-	git_mwindow_close(&w);
-
 	/* Add the object to the list */
 	if (git_vector_insert(&idx->objects, entry) < 0)
 		goto on_error;
@@ -283,20 +377,27 @@
 
 on_error:
 	git__free(entry);
-	git__free(pentry);
 	git__free(obj->data);
 	return -1;
 }
 
-int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats)
+static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *stats)
 {
-	int error;
+	if (!idx->progress_cb) return 0;
+	return idx->progress_cb(stats, idx->progress_payload);
+}
+
+int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
+{
+	int error = -1;
 	struct git_pack_header hdr;
-	size_t processed = stats->processed;
+	size_t processed; 
 	git_mwindow_file *mwf = &idx->pack->mwf;
 
 	assert(idx && data && stats);
 
+	processed = stats->indexed_objects;
+
 	if (git_filebuf_write(&idx->pack_file, data, size) < 0)
 		return -1;
 
@@ -311,11 +412,11 @@
 		mwf = &idx->pack->mwf;
 		if (git_mwindow_file_register(&idx->pack->mwf) < 0)
 			return -1;
-
-		return 0;
 	}
 
 	if (!idx->parsed_header) {
+		unsigned int total_objects;
+
 		if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
 			return 0;
 
@@ -328,19 +429,25 @@
 
 		/* for now, limit to 2^32 objects */
 		assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+		if (idx->nr_objects == (size_t)((unsigned int)idx->nr_objects))
+			total_objects = (unsigned int)idx->nr_objects;
+		else
+			total_objects = UINT_MAX;
 
-		if (git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp) < 0)
-			return -1;
+		idx->pack->idx_cache = git_oidmap_alloc();
+		GITERR_CHECK_ALLOC(idx->pack->idx_cache);
 
 		idx->pack->has_cache = 1;
-		if (git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp) < 0)
+		if (git_vector_init(&idx->objects, total_objects, objects_cmp) < 0)
 			return -1;
 
-		if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
+		if (git_vector_init(&idx->deltas, total_objects / 2, NULL) < 0)
 			return -1;
 
-		stats->total = (unsigned int)idx->nr_objects;
-		stats->processed = 0;
+		stats->received_objects = 0;
+		processed = stats->indexed_objects = 0;
+		stats->total_objects = total_objects;
+		do_progress_callback(idx, stats);
 	}
 
 	/* Now that we have data in the pack, let's try to parse it */
@@ -348,42 +455,91 @@
 	/* As the file grows any windows we try to use will be out of date */
 	git_mwindow_free_all(mwf);
 	while (processed < idx->nr_objects) {
-		git_rawobj obj;
+		git_packfile_stream *stream = &idx->stream;
 		git_off_t entry_start = idx->off;
+		size_t entry_size;
+		git_otype type;
+		git_mwindow *w = NULL;
 
 		if (idx->pack->mwf.size <= idx->off + 20)
 			return 0;
 
-		error = git_packfile_unpack(&obj, idx->pack, &idx->off);
-		if (error == GIT_EBUFS) {
-			idx->off = entry_start;
-			return 0;
-		}
-
-		if (error < 0) {
-			idx->off = entry_start;
-			error = store_delta(idx);
-			if (error == GIT_EBUFS)
+		if (!idx->have_stream) {
+			error = git_packfile_unpack_header(&entry_size, &type, mwf, &w, &idx->off);
+			if (error == GIT_EBUFS) {
+				idx->off = entry_start;
 				return 0;
+			}
 			if (error < 0)
-				return error;
+				return -1;
 
-			continue;
+			git_mwindow_close(&w);
+			idx->entry_start = entry_start;
+			git_hash_ctx_init(&idx->hash_ctx);
+
+			if (type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA) {
+				error = advance_delta_offset(idx, type);
+				if (error == GIT_EBUFS) {
+					idx->off = entry_start;
+					return 0;
+				}
+				if (error < 0)
+					return -1;
+
+				idx->have_delta = 1;
+			} else {
+				idx->have_delta = 0;
+				hash_header(&idx->hash_ctx, entry_size, type);
+			}
+
+			idx->have_stream = 1;
+			if (git_packfile_stream_open(stream, idx->pack, idx->off) < 0)
+				goto on_error;
+
 		}
 
-		if (hash_and_save(idx, &obj, entry_start) < 0)
+		if (idx->have_delta) {
+			error = read_object_stream(idx, stream);
+		} else {
+			error = hash_object_stream(idx, stream);
+		}
+
+		idx->off = stream->curpos;
+		if (error == GIT_EBUFS)
+			return 0;
+
+		/* We want to free the stream reasorces no matter what here */
+		idx->have_stream = 0;
+		git_packfile_stream_free(stream);
+
+		if (error < 0)
 			goto on_error;
 
-		git__free(obj.data);
+		if (idx->have_delta) {
+			error = store_delta(idx);
+		} else {
+			error = store_object(idx);
+		}
 
-		stats->processed = (unsigned int)++processed;
+		if (error < 0)
+			goto on_error;
+
+		if (!idx->have_delta) {
+			stats->indexed_objects = (unsigned int)++processed;
+		}
+		stats->received_objects++;
+
+		if (do_progress_callback(idx, stats) != 0) {
+			error = GIT_EUSER;
+			goto on_error;
+		}
 	}
 
 	return 0;
 
 on_error:
 	git_mwindow_free_all(mwf);
-	return -1;
+	return error;
 }
 
 static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
@@ -408,7 +564,7 @@
 	return git_buf_oom(path) ? -1 : 0;
 }
 
-static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
+static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
 {
 	unsigned int i;
 	struct delta_info *delta;
@@ -424,13 +580,14 @@
 			return -1;
 
 		git__free(obj.data);
-		stats->processed++;
+		stats->indexed_objects++;
+		do_progress_callback(idx, stats);
 	}
 
 	return 0;
 }
 
-int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)
+int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *stats)
 {
 	git_mwindow *w = NULL;
 	unsigned int i, long_offsets = 0, left;
@@ -439,11 +596,15 @@
 	struct entry *entry;
 	void *packfile_hash;
 	git_oid file_hash;
-	SHA_CTX ctx;
+	git_hash_ctx ctx;
+	git_filebuf index_file = {0};
 
+	if (git_hash_ctx_init(&ctx) < 0)
+		return -1;
+
 	/* Test for this before resolve_deltas(), as it plays with idx->off */
 	if (idx->off < idx->pack->mwf.size - GIT_OID_RAWSZ) {
-		giterr_set(GITERR_INDEXER, "Indexing error: junk at the end of the pack");
+		giterr_set(GITERR_INDEXER, "Indexing error: unexpected data at the end of the pack");
 		return -1;
 	}
 
@@ -451,7 +612,7 @@
 		if (resolve_deltas(idx, stats) < 0)
 			return -1;
 
-	if (stats->processed != stats->total) {
+	if (stats->indexed_objects != stats->total_objects) {
 		giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
 		return -1;
 	}
@@ -464,31 +625,30 @@
 	if (git_buf_oom(&filename))
 		return -1;
 
-	if (git_filebuf_open(&idx->index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
+	if (git_filebuf_open(&index_file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS) < 0)
 		goto on_error;
 
 	/* Write out the header */
 	hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
 	hdr.idx_version = htonl(2);
-	git_filebuf_write(&idx->index_file, &hdr, sizeof(hdr));
+	git_filebuf_write(&index_file, &hdr, sizeof(hdr));
 
 	/* Write out the fanout table */
 	for (i = 0; i < 256; ++i) {
 		uint32_t n = htonl(idx->fanout[i]);
-		git_filebuf_write(&idx->index_file, &n, sizeof(n));
+		git_filebuf_write(&index_file, &n, sizeof(n));
 	}
 
 	/* Write out the object names (SHA-1 hashes) */
-	SHA1_Init(&ctx);
 	git_vector_foreach(&idx->objects, i, entry) {
-		git_filebuf_write(&idx->index_file, &entry->oid, sizeof(git_oid));
-		SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
+		git_filebuf_write(&index_file, &entry->oid, sizeof(git_oid));
+		git_hash_update(&ctx, &entry->oid, GIT_OID_RAWSZ);
 	}
-	SHA1_Final(idx->hash.id, &ctx);
+	git_hash_final(&idx->hash, &ctx);
 
 	/* Write out the CRC32 values */
 	git_vector_foreach(&idx->objects, i, entry) {
-		git_filebuf_write(&idx->index_file, &entry->crc, sizeof(uint32_t));
+		git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t));
 	}
 
 	/* Write out the offsets */
@@ -500,7 +660,7 @@
 		else
 			n = htonl(entry->offset);
 
-		git_filebuf_write(&idx->index_file, &n, sizeof(uint32_t));
+		git_filebuf_write(&index_file, &n, sizeof(uint32_t));
 	}
 
 	/* Write out the long offsets */
@@ -513,7 +673,7 @@
 		split[0] = htonl(entry->offset_long >> 32);
 		split[1] = htonl(entry->offset_long & 0xffffffff);
 
-		git_filebuf_write(&idx->index_file, &split, sizeof(uint32_t) * 2);
+		git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
 	}
 
 	/* Write out the packfile trailer */
@@ -526,24 +686,26 @@
 	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
 	git_mwindow_close(&w);
 
-	git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+	git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
 
 	/* Write out the packfile trailer to the idx file as well */
-	if (git_filebuf_hash(&file_hash, &idx->index_file) < 0)
+	if (git_filebuf_hash(&file_hash, &index_file) < 0)
 		goto on_error;
 
-	git_filebuf_write(&idx->index_file, &file_hash, sizeof(git_oid));
+	git_filebuf_write(&index_file, &file_hash, sizeof(git_oid));
 
 	/* Figure out what the final name should be */
 	if (index_path_stream(&filename, idx, ".idx") < 0)
 		goto on_error;
 
 	/* Commit file */
-	if (git_filebuf_commit_at(&idx->index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+	if (git_filebuf_commit_at(&index_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
 		goto on_error;
 
 	git_mwindow_free_all(&idx->pack->mwf);
+	/* We need to close the descriptor here so Windows doesn't choke on commit_at */
 	p_close(idx->pack->mwf.fd);
+	idx->pack->mwf.fd = -1;
 
 	if (index_path_stream(&filename, idx, ".pack") < 0)
 		goto on_error;
@@ -556,17 +718,17 @@
 
 on_error:
 	git_mwindow_free_all(&idx->pack->mwf);
-	p_close(idx->pack->mwf.fd);
-	git_filebuf_cleanup(&idx->index_file);
+	git_filebuf_cleanup(&index_file);
 	git_buf_free(&filename);
+	git_hash_ctx_cleanup(&ctx);
 	return -1;
 }
 
 void git_indexer_stream_free(git_indexer_stream *idx)
 {
+	khiter_t k;
 	unsigned int i;
 	struct entry *e;
-	struct git_pack_entry *pe;
 	struct delta_info *delta;
 
 	if (idx == NULL)
@@ -575,325 +737,20 @@
 	git_vector_foreach(&idx->objects, i, e)
 		git__free(e);
 	git_vector_free(&idx->objects);
-	git_vector_foreach(&idx->pack->cache, i, pe)
-		git__free(pe);
-	git_vector_free(&idx->pack->cache);
-	git_vector_foreach(&idx->deltas, i, delta)
-		git__free(delta);
-	git_vector_free(&idx->deltas);
-	git__free(idx->pack);
-	git__free(idx);
-}
 
-int git_indexer_new(git_indexer **out, const char *packname)
-{
-	git_indexer *idx;
-	struct git_pack_header hdr;
-	int error;
-
-	assert(out && packname);
-
-	if (git_path_root(packname) < 0) {
-		giterr_set(GITERR_INDEXER, "Path is not absolute");
-		return -1;
-	}
-
-	idx = git__calloc(1, sizeof(git_indexer));
-	GITERR_CHECK_ALLOC(idx);
-
-	open_pack(&idx->pack, packname);
-
-	if ((error = parse_header(&hdr, idx->pack)) < 0)
-		goto cleanup;
-
-	idx->nr_objects = ntohl(hdr.hdr_entries);
-
-	/* for now, limit to 2^32 objects */
-	assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
-
-	error = git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp);
-	if (error < 0)
-		goto cleanup;
-
-	idx->pack->has_cache = 1;
-	error = git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp);
-	if (error < 0)
-		goto cleanup;
-
-	*out = idx;
-
-	return 0;
-
-cleanup:
-	git_indexer_free(idx);
-
-	return -1;
-}
-
-static int index_path(git_buf *path, git_indexer *idx)
-{
-	const char prefix[] = "pack-", suffix[] = ".idx";
-	size_t slash = (size_t)path->size;
-
-	/* search backwards for '/' */
-	while (slash > 0 && path->ptr[slash - 1] != '/')
-		slash--;
-
-	if (git_buf_grow(path, slash + 1 + strlen(prefix) +
-					 GIT_OID_HEXSZ + strlen(suffix) + 1) < 0)
-		return -1;
-
-	git_buf_truncate(path, slash);
-	git_buf_puts(path, prefix);
-	git_oid_fmt(path->ptr + git_buf_len(path), &idx->hash);
-	path->size += GIT_OID_HEXSZ;
-	git_buf_puts(path, suffix);
-
-	return git_buf_oom(path) ? -1 : 0;
-}
-
-int git_indexer_write(git_indexer *idx)
-{
-	git_mwindow *w = NULL;
-	int error;
-	unsigned int i, long_offsets = 0, left;
-	struct git_pack_idx_header hdr;
-	git_buf filename = GIT_BUF_INIT;
-	struct entry *entry;
-	void *packfile_hash;
-	git_oid file_hash;
-	SHA_CTX ctx;
-
-	git_vector_sort(&idx->objects);
-
-	git_buf_sets(&filename, idx->pack->pack_name);
-	git_buf_truncate(&filename, filename.size - strlen("pack"));
-	git_buf_puts(&filename, "idx");
-	if (git_buf_oom(&filename))
-		return -1;
-
-	error = git_filebuf_open(&idx->file, filename.ptr, GIT_FILEBUF_HASH_CONTENTS);
-	if (error < 0)
-		goto cleanup;
-
-	/* Write out the header */
-	hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
-	hdr.idx_version = htonl(2);
-	error = git_filebuf_write(&idx->file, &hdr, sizeof(hdr));
-	if (error < 0)
-		goto cleanup;
-
-	/* Write out the fanout table */
-	for (i = 0; i < 256; ++i) {
-		uint32_t n = htonl(idx->fanout[i]);
-		error = git_filebuf_write(&idx->file, &n, sizeof(n));
-		if (error < 0)
-			goto cleanup;
-	}
-
-	/* Write out the object names (SHA-1 hashes) */
-	SHA1_Init(&ctx);
-	git_vector_foreach(&idx->objects, i, entry) {
-		error = git_filebuf_write(&idx->file, &entry->oid, sizeof(git_oid));
-		SHA1_Update(&ctx, &entry->oid, GIT_OID_RAWSZ);
-		if (error < 0)
-			goto cleanup;
-	}
-	SHA1_Final(idx->hash.id, &ctx);
-
-	/* Write out the CRC32 values */
-	git_vector_foreach(&idx->objects, i, entry) {
-		error = git_filebuf_write(&idx->file, &entry->crc, sizeof(uint32_t));
-		if (error < 0)
-			goto cleanup;
-	}
-
-	/* Write out the offsets */
-	git_vector_foreach(&idx->objects, i, entry) {
-		uint32_t n;
-
-		if (entry->offset == UINT32_MAX)
-			n = htonl(0x80000000 | long_offsets++);
-		else
-			n = htonl(entry->offset);
-
-		error = git_filebuf_write(&idx->file, &n, sizeof(uint32_t));
-		if (error < 0)
-			goto cleanup;
-	}
-
-	/* Write out the long offsets */
-	git_vector_foreach(&idx->objects, i, entry) {
-		uint32_t split[2];
-
-		if (entry->offset != UINT32_MAX)
-			continue;
-
-		split[0] = htonl(entry->offset_long >> 32);
-		split[1] = htonl(entry->offset_long & 0xffffffff);
-
-		error = git_filebuf_write(&idx->file, &split, sizeof(uint32_t) * 2);
-		if (error < 0)
-			goto cleanup;
-	}
-
-	/* Write out the packfile trailer */
-
-	packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
-	git_mwindow_close(&w);
-	if (packfile_hash == NULL) {
-		error = -1;
-		goto cleanup;
-	}
-
-	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
-
-	git_mwindow_close(&w);
-
-	error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
-	if (error < 0)
-		goto cleanup;
-
-	/* Write out the index sha */
-	error = git_filebuf_hash(&file_hash, &idx->file);
-	if (error < 0)
-		goto cleanup;
-
-	error = git_filebuf_write(&idx->file, &file_hash, sizeof(git_oid));
-	if (error < 0)
-		goto cleanup;
-
-	/* Figure out what the final name should be */
-	error = index_path(&filename, idx);
-	if (error < 0)
-		goto cleanup;
-
-	/* Commit file */
-	error = git_filebuf_commit_at(&idx->file, filename.ptr, GIT_PACK_FILE_MODE);
-
-cleanup:
-	git_mwindow_free_all(&idx->pack->mwf);
-	if (error < 0)
-		git_filebuf_cleanup(&idx->file);
-	git_buf_free(&filename);
-
-	return error;
-}
-
-int git_indexer_run(git_indexer *idx, git_indexer_stats *stats)
-{
-	git_mwindow_file *mwf;
-	git_off_t off = sizeof(struct git_pack_header);
-	int error;
-	struct entry *entry;
-	unsigned int left, processed;
-
-	assert(idx && stats);
-
-	mwf = &idx->pack->mwf;
-	error = git_mwindow_file_register(mwf);
-	if (error < 0)
-		return error;
-
-	stats->total = (unsigned int)idx->nr_objects;
-	stats->processed = processed = 0;
-
-	while (processed < idx->nr_objects) {
-		git_rawobj obj;
-		git_oid oid;
-		struct git_pack_entry *pentry;
-		git_mwindow *w = NULL;
-		int i;
-		git_off_t entry_start = off;
-		void *packed;
-		size_t entry_size;
-		char fmt[GIT_OID_HEXSZ] = {0};
-
-		entry = git__calloc(1, sizeof(*entry));
-		GITERR_CHECK_ALLOC(entry);
-
-		if (off > UINT31_MAX) {
-			entry->offset = UINT32_MAX;
-			entry->offset_long = off;
-		} else {
-			entry->offset = (uint32_t)off;
-		}
-
-		error = git_packfile_unpack(&obj, idx->pack, &off);
-		if (error < 0)
-			goto cleanup;
-
-		/* FIXME: Parse the object instead of hashing it */
-		error = git_odb__hashobj(&oid, &obj);
-		if (error < 0) {
-			giterr_set(GITERR_INDEXER, "Failed to hash object");
-			goto cleanup;
-		}
-
-		pentry = git__malloc(sizeof(struct git_pack_entry));
-		if (pentry == NULL) {
-			error = -1;
-			goto cleanup;
-		}
-
-		git_oid_cpy(&pentry->sha1, &oid);
-		pentry->offset = entry_start;
-		git_oid_fmt(fmt, &oid);
-		printf("adding %s to cache\n", fmt);
-		error = git_vector_insert(&idx->pack->cache, pentry);
-		if (error < 0)
-			goto cleanup;
-
-		git_oid_cpy(&entry->oid, &oid);
-		entry->crc = crc32(0L, Z_NULL, 0);
-
-		entry_size = (size_t)(off - entry_start);
-		packed = git_mwindow_open(mwf, &w, entry_start, entry_size, &left);
-		if (packed == NULL) {
-			error = -1;
-			goto cleanup;
-		}
-		entry->crc = htonl(crc32(entry->crc, packed, (uInt)entry_size));
-		git_mwindow_close(&w);
-
-		/* Add the object to the list */
-		error = git_vector_insert(&idx->objects, entry);
-		if (error < 0)
-			goto cleanup;
-
-		for (i = oid.id[0]; i < 256; ++i) {
-			idx->fanout[i]++;
+	if (idx->pack) {
+		for (k = kh_begin(idx->pack->idx_cache); k != kh_end(idx->pack->idx_cache); k++) {
+			if (kh_exist(idx->pack->idx_cache, k))
+				git__free(kh_value(idx->pack->idx_cache, k));
 		}
 
-		git__free(obj.data);
-
-		stats->processed = ++processed;
+		git_oidmap_free(idx->pack->idx_cache);
 	}
 
-cleanup:
-	git_mwindow_free_all(mwf);
-
-	return error;
-
-}
-
-void git_indexer_free(git_indexer *idx)
-{
-	unsigned int i;
-	struct entry *e;
-	struct git_pack_entry *pe;
-
-	if (idx == NULL)
-		return;
-
-	p_close(idx->pack->mwf.fd);
-	git_vector_foreach(&idx->objects, i, e)
-		git__free(e);
-	git_vector_free(&idx->objects);
-	git_vector_foreach(&idx->pack->cache, i, pe)
-		git__free(pe);
-	git_vector_free(&idx->pack->cache);
-	git__free(idx->pack);
+	git_vector_foreach(&idx->deltas, i, delta)
+		git__free(delta);
+	git_vector_free(&idx->deltas);
+	git_packfile_free(idx->pack);
+	git_filebuf_cleanup(&idx->pack_file);
 	git__free(idx);
 }
-
diff --git a/libgit2/src/iterator.c b/libgit2/src/iterator.c
--- a/libgit2/src/iterator.c
+++ b/libgit2/src/iterator.c
@@ -1,748 +1,1349 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "iterator.h"
-#include "tree.h"
-#include "ignore.h"
-#include "buffer.h"
-#include "git2/submodule.h"
-
-#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC) do { \
-	(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
-	GITERR_CHECK_ALLOC(P); \
-	(P)->base.type    = GIT_ITERATOR_ ## NAME_UC; \
-	(P)->base.start   = start ? git__strdup(start) : NULL; \
-	(P)->base.end     = end ? git__strdup(end) : NULL; \
-	(P)->base.current = NAME_LC ## _iterator__current; \
-	(P)->base.at_end  = NAME_LC ## _iterator__at_end; \
-	(P)->base.advance = NAME_LC ## _iterator__advance; \
-	(P)->base.seek    = NAME_LC ## _iterator__seek; \
-	(P)->base.reset   = NAME_LC ## _iterator__reset; \
-	(P)->base.free    = NAME_LC ## _iterator__free; \
-	if ((start && !(P)->base.start) || (end && !(P)->base.end)) \
-		return -1; \
-	} while (0)
-
-
-static int empty_iterator__no_item(
-	git_iterator *iter, const git_index_entry **entry)
-{
-	GIT_UNUSED(iter);
-	*entry = NULL;
-	return 0;
-}
-
-static int empty_iterator__at_end(git_iterator *iter)
-{
-	GIT_UNUSED(iter);
-	return 1;
-}
-
-static int empty_iterator__noop(git_iterator *iter)
-{
-	GIT_UNUSED(iter);
-	return 0;
-}
-
-static int empty_iterator__seek(git_iterator *iter, const char *prefix)
-{
-	GIT_UNUSED(iter);
-	GIT_UNUSED(prefix);
-	return -1;
-}
-
-static void empty_iterator__free(git_iterator *iter)
-{
-	GIT_UNUSED(iter);
-}
-
-int git_iterator_for_nothing(git_iterator **iter)
-{
-	git_iterator *i = git__calloc(1, sizeof(git_iterator));
-	GITERR_CHECK_ALLOC(i);
-
-	i->type    = GIT_ITERATOR_EMPTY;
-	i->current = empty_iterator__no_item;
-	i->at_end  = empty_iterator__at_end;
-	i->advance = empty_iterator__no_item;
-	i->seek    = empty_iterator__seek;
-	i->reset   = empty_iterator__noop;
-	i->free    = empty_iterator__free;
-
-	*iter = i;
-
-	return 0;
-}
-
-
-typedef struct tree_iterator_frame tree_iterator_frame;
-struct tree_iterator_frame {
-	tree_iterator_frame *next;
-	git_tree *tree;
-	char *start;
-	unsigned int index;
-};
-
-typedef struct {
-	git_iterator base;
-	git_repository *repo;
-	tree_iterator_frame *stack;
-	git_index_entry entry;
-	git_buf path;
-	bool path_has_filename;
-} tree_iterator;
-
-static const git_tree_entry *tree_iterator__tree_entry(tree_iterator *ti)
-{
-	return (ti->stack == NULL) ? NULL :
-		git_tree_entry_byindex(ti->stack->tree, ti->stack->index);
-}
-
-static char *tree_iterator__current_filename(
-	tree_iterator *ti, const git_tree_entry *te)
-{
-	if (!ti->path_has_filename) {
-		if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
-			return NULL;
-		ti->path_has_filename = true;
-	}
-
-	return ti->path.ptr;
-}
-
-static void tree_iterator__pop_frame(tree_iterator *ti)
-{
-	tree_iterator_frame *tf = ti->stack;
-	ti->stack = tf->next;
-	if (ti->stack != NULL) /* don't free the initial tree */
-		git_tree_free(tf->tree);
-	git__free(tf);
-}
-
-static int tree_iterator__to_end(tree_iterator *ti)
-{
-	while (ti->stack && ti->stack->next)
-		tree_iterator__pop_frame(ti);
-
-	if (ti->stack)
-		ti->stack->index = git_tree_entrycount(ti->stack->tree);
-
-	return 0;
-}
-
-static int tree_iterator__current(
-	git_iterator *self, const git_index_entry **entry)
-{
-	tree_iterator *ti = (tree_iterator *)self;
-	const git_tree_entry *te = tree_iterator__tree_entry(ti);
-
-	if (entry)
-		*entry = NULL;
-
-	if (te == NULL)
-		return 0;
-
-	ti->entry.mode = te->attr;
-	git_oid_cpy(&ti->entry.oid, &te->oid);
-
-	ti->entry.path = tree_iterator__current_filename(ti, te);
-	if (ti->entry.path == NULL)
-		return -1;
-
-	if (ti->base.end && git__prefixcmp(ti->entry.path, ti->base.end) > 0)
-		return tree_iterator__to_end(ti);
-
-	if (entry)
-		*entry = &ti->entry;
-
-	return 0;
-}
-
-static int tree_iterator__at_end(git_iterator *self)
-{
-	return (tree_iterator__tree_entry((tree_iterator *)self) == NULL);
-}
-
-static tree_iterator_frame *tree_iterator__alloc_frame(
-	git_tree *tree, char *start)
-{
-	tree_iterator_frame *tf = git__calloc(1, sizeof(tree_iterator_frame));
-	if (!tf)
-		return NULL;
-
-	tf->tree = tree;
-
-	if (start && *start) {
-		tf->start = start;
-		tf->index = git_tree__prefix_position(tree, start);
-	}
-
-	return tf;
-}
-
-static int tree_iterator__expand_tree(tree_iterator *ti)
-{
-	int error;
-	git_tree *subtree;
-	const git_tree_entry *te = tree_iterator__tree_entry(ti);
-	tree_iterator_frame *tf;
-	char *relpath;
-
-	while (te != NULL && git_tree_entry__is_tree(te)) {
-		if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
-			return -1;
-
-		/* check that we have not passed the range end */
-		if (ti->base.end != NULL &&
-			git__prefixcmp(ti->path.ptr, ti->base.end) > 0)
-			return tree_iterator__to_end(ti);
-
-		if ((error = git_tree_lookup(&subtree, ti->repo, &te->oid)) < 0)
-			return error;
-
-		relpath = NULL;
-
-		/* apply range start to new frame if relevant */
-		if (ti->stack->start &&
-			git__prefixcmp(ti->stack->start, te->filename) == 0)
-		{
-			size_t namelen = strlen(te->filename);
-			if (ti->stack->start[namelen] == '/')
-				relpath = ti->stack->start + namelen + 1;
-		}
-
-		if ((tf = tree_iterator__alloc_frame(subtree, relpath)) == NULL)
-			return -1;
-
-		tf->next  = ti->stack;
-		ti->stack = tf;
-
-		te = tree_iterator__tree_entry(ti);
-	}
-
-	return 0;
-}
-
-static int tree_iterator__advance(
-	git_iterator *self, const git_index_entry **entry)
-{
-	int error = 0;
-	tree_iterator *ti = (tree_iterator *)self;
-	const git_tree_entry *te = NULL;
-
-	if (entry != NULL)
-		*entry = NULL;
-
-	if (ti->path_has_filename) {
-		git_buf_rtruncate_at_char(&ti->path, '/');
-		ti->path_has_filename = false;
-	}
-
-	while (ti->stack != NULL) {
-		te = git_tree_entry_byindex(ti->stack->tree, ++ti->stack->index);
-		if (te != NULL)
-			break;
-
-		tree_iterator__pop_frame(ti);
-
-		git_buf_rtruncate_at_char(&ti->path, '/');
-	}
-
-	if (te && git_tree_entry__is_tree(te))
-		error = tree_iterator__expand_tree(ti);
-
-	if (!error)
-		error = tree_iterator__current(self, entry);
-
-	return error;
-}
-
-static int tree_iterator__seek(git_iterator *self, const char *prefix)
-{
-	GIT_UNUSED(self);
-	GIT_UNUSED(prefix);
-	/* pop stack until matches prefix */
-	/* seek item in current frame matching prefix */
-	/* push stack which matches prefix */
-	return -1;
-}
-
-static void tree_iterator__free(git_iterator *self)
-{
-	tree_iterator *ti = (tree_iterator *)self;
-	while (ti->stack != NULL)
-		tree_iterator__pop_frame(ti);
-	git_buf_free(&ti->path);
-}
-
-static int tree_iterator__reset(git_iterator *self)
-{
-	tree_iterator *ti = (tree_iterator *)self;
-
-	while (ti->stack && ti->stack->next)
-		tree_iterator__pop_frame(ti);
-
-	if (ti->stack)
-		ti->stack->index =
-			git_tree__prefix_position(ti->stack->tree, ti->base.start);
-
-	git_buf_clear(&ti->path);
-
-	return tree_iterator__expand_tree(ti);
-}
-
-int git_iterator_for_tree_range(
-	git_iterator **iter,
-	git_repository *repo,
-	git_tree *tree,
-	const char *start,
-	const char *end)
-{
-	int error;
-	tree_iterator *ti;
-
-	if (tree == NULL)
-		return git_iterator_for_nothing(iter);
-
-	ITERATOR_BASE_INIT(ti, tree, TREE);
-
-	ti->repo  = repo;
-	ti->stack = tree_iterator__alloc_frame(tree, ti->base.start);
-
-	if ((error = tree_iterator__expand_tree(ti)) < 0)
-		git_iterator_free((git_iterator *)ti);
-	else
-		*iter = (git_iterator *)ti;
-
-	return error;
-}
-
-
-typedef struct {
-	git_iterator base;
-	git_index *index;
-	unsigned int current;
-} index_iterator;
-
-static int index_iterator__current(
-	git_iterator *self, const git_index_entry **entry)
-{
-	index_iterator *ii = (index_iterator *)self;
-	git_index_entry *ie = git_index_get(ii->index, ii->current);
-
-	if (ie != NULL &&
-		ii->base.end != NULL &&
-		git__prefixcmp(ie->path, ii->base.end) > 0)
-	{
-		ii->current = git_index_entrycount(ii->index);
-		ie = NULL;
-	}
-
-	if (entry)
-		*entry = ie;
-
-	return 0;
-}
-
-static int index_iterator__at_end(git_iterator *self)
-{
-	index_iterator *ii = (index_iterator *)self;
-	return (ii->current >= git_index_entrycount(ii->index));
-}
-
-static int index_iterator__advance(
-	git_iterator *self, const git_index_entry **entry)
-{
-	index_iterator *ii = (index_iterator *)self;
-
-	if (ii->current < git_index_entrycount(ii->index))
-		ii->current++;
-
-	return index_iterator__current(self, entry);
-}
-
-static int index_iterator__seek(git_iterator *self, const char *prefix)
-{
-	GIT_UNUSED(self);
-	GIT_UNUSED(prefix);
-	/* find last item before prefix */
-	return -1;
-}
-
-static int index_iterator__reset(git_iterator *self)
-{
-	index_iterator *ii = (index_iterator *)self;
-	ii->current = 0;
-	return 0;
-}
-
-static void index_iterator__free(git_iterator *self)
-{
-	index_iterator *ii = (index_iterator *)self;
-	git_index_free(ii->index);
-	ii->index = NULL;
-}
-
-int git_iterator_for_index_range(
-	git_iterator **iter,
-	git_repository *repo,
-	const char *start,
-	const char *end)
-{
-	int error;
-	index_iterator *ii;
-
-	ITERATOR_BASE_INIT(ii, index, INDEX);
-
-	if ((error = git_repository_index(&ii->index, repo)) < 0)
-		git__free(ii);
-	else {
-		ii->current = start ? git_index__prefix_position(ii->index, start) : 0;
-		*iter = (git_iterator *)ii;
-	}
-
-	return error;
-}
-
-
-typedef struct workdir_iterator_frame workdir_iterator_frame;
-struct workdir_iterator_frame {
-	workdir_iterator_frame *next;
-	git_vector entries;
-	unsigned int index;
-	char *start;
-};
-
-typedef struct {
-	git_iterator base;
-	git_repository *repo;
-	size_t root_len;
-	workdir_iterator_frame *stack;
-	git_ignores ignores;
-	git_index_entry entry;
-	git_buf path;
-	int is_ignored;
-} workdir_iterator;
-
-static workdir_iterator_frame *workdir_iterator__alloc_frame(void)
-{
-	workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
-	if (wf == NULL)
-		return NULL;
-	if (git_vector_init(&wf->entries, 0, git_path_with_stat_cmp) != 0) {
-		git__free(wf);
-		return NULL;
-	}
-	return wf;
-}
-
-static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
-{
-	unsigned int i;
-	git_path_with_stat *path;
-
-	git_vector_foreach(&wf->entries, i, path)
-		git__free(path);
-	git_vector_free(&wf->entries);
-	git__free(wf);
-}
-
-static int workdir_iterator__update_entry(workdir_iterator *wi);
-
-static int workdir_iterator__entry_cmp(const void *prefix, const void *item)
-{
-	const git_path_with_stat *ps = item;
-	return git__prefixcmp((const char *)prefix, ps->path);
-}
-
-static int workdir_iterator__expand_dir(workdir_iterator *wi)
-{
-	int error;
-	workdir_iterator_frame *wf = workdir_iterator__alloc_frame();
-	GITERR_CHECK_ALLOC(wf);
-
-	error = git_path_dirload_with_stat(wi->path.ptr, wi->root_len, &wf->entries);
-	if (error < 0 || wf->entries.length == 0) {
-		workdir_iterator__free_frame(wf);
-		return GIT_ENOTFOUND;
-	}
-
-	git_vector_sort(&wf->entries);
-
-	if (!wi->stack)
-		wf->start = wi->base.start;
-	else if (wi->stack->start &&
-		git__prefixcmp(wi->stack->start, wi->path.ptr + wi->root_len) == 0)
-		wf->start = wi->stack->start;
-
-	if (wf->start)
-		git_vector_bsearch3(
-			&wf->index, &wf->entries, workdir_iterator__entry_cmp, wf->start);
-
-	wf->next  = wi->stack;
-	wi->stack = wf;
-
-	/* only push new ignores if this is not top level directory */
-	if (wi->stack->next != NULL) {
-		ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
-		(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
-	}
-
-	return workdir_iterator__update_entry(wi);
-}
-
-static int workdir_iterator__current(
-	git_iterator *self, const git_index_entry **entry)
-{
-	workdir_iterator *wi = (workdir_iterator *)self;
-	*entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
-	return 0;
-}
-
-static int workdir_iterator__at_end(git_iterator *self)
-{
-	return (((workdir_iterator *)self)->entry.path == NULL);
-}
-
-static int workdir_iterator__advance(
-	git_iterator *self, const git_index_entry **entry)
-{
-	int error;
-	workdir_iterator *wi = (workdir_iterator *)self;
-	workdir_iterator_frame *wf;
-	git_path_with_stat *next;
-
-	if (entry != NULL)
-		*entry = NULL;
-
-	if (wi->entry.path == NULL)
-		return 0;
-
-	while ((wf = wi->stack) != NULL) {
-		next = git_vector_get(&wf->entries, ++wf->index);
-		if (next != NULL) {
-			if (strcmp(next->path, DOT_GIT "/") == 0)
-				continue;
-			/* else found a good entry */
-			break;
-		}
-
-		/* pop workdir directory stack */
-		wi->stack = wf->next;
-		workdir_iterator__free_frame(wf);
-		git_ignore__pop_dir(&wi->ignores);
-
-		if (wi->stack == NULL) {
-			memset(&wi->entry, 0, sizeof(wi->entry));
-			return 0;
-		}
-	}
-
-	error = workdir_iterator__update_entry(wi);
-
-	if (!error && entry != NULL)
-		error = workdir_iterator__current(self, entry);
-
-	return error;
-}
-
-static int workdir_iterator__seek(git_iterator *self, const char *prefix)
-{
-	GIT_UNUSED(self);
-	GIT_UNUSED(prefix);
-	/* pop stack until matching prefix */
-	/* find prefix item in current frame */
-	/* push subdirectories as deep as possible while matching */
-	return 0;
-}
-
-static int workdir_iterator__reset(git_iterator *self)
-{
-	workdir_iterator *wi = (workdir_iterator *)self;
-	while (wi->stack != NULL && wi->stack->next != NULL) {
-		workdir_iterator_frame *wf = wi->stack;
-		wi->stack = wf->next;
-		workdir_iterator__free_frame(wf);
-		git_ignore__pop_dir(&wi->ignores);
-	}
-	if (wi->stack)
-		wi->stack->index = 0;
-	return 0;
-}
-
-static void workdir_iterator__free(git_iterator *self)
-{
-	workdir_iterator *wi = (workdir_iterator *)self;
-
-	while (wi->stack != NULL) {
-		workdir_iterator_frame *wf = wi->stack;
-		wi->stack = wf->next;
-		workdir_iterator__free_frame(wf);
-	}
-
-	git_ignore__free(&wi->ignores);
-	git_buf_free(&wi->path);
-}
-
-static int workdir_iterator__update_entry(workdir_iterator *wi)
-{
-	git_path_with_stat *ps = git_vector_get(&wi->stack->entries, wi->stack->index);
-
-	git_buf_truncate(&wi->path, wi->root_len);
-	memset(&wi->entry, 0, sizeof(wi->entry));
-
-	if (!ps)
-		return 0;
-
-	if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
-		return -1;
-
-	if (wi->base.end &&
-		git__prefixcmp(wi->path.ptr + wi->root_len, wi->base.end) > 0)
-		return 0;
-
-	wi->entry.path = ps->path;
-
-	/* skip over .git directory */
-	if (strcmp(ps->path, DOT_GIT "/") == 0)
-		return workdir_iterator__advance((git_iterator *)wi, NULL);
-
-	/* if there is an error processing the entry, treat as ignored */
-	wi->is_ignored = 1;
-
-	git_index__init_entry_from_stat(&ps->st, &wi->entry);
-
-	/* need different mode here to keep directories during iteration */
-	wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
-
-	/* if this is a file type we don't handle, treat as ignored */
-	if (wi->entry.mode == 0)
-		return 0;
-
-	/* okay, we are far enough along to look up real ignore rule */
-	if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
-		return 0; /* if error, ignore it and ignore file */
-
-	/* detect submodules */
-	if (S_ISDIR(wi->entry.mode)) {
-		bool is_submodule = git_path_contains(&wi->path, DOT_GIT);
-
-		/* if there is no .git, still check submodules data */
-		if (!is_submodule) {
-			int res = git_submodule_lookup(NULL, wi->repo, wi->entry.path);
-			is_submodule = (res == 0);
-			if (res == GIT_ENOTFOUND)
-				giterr_clear();
-		}
-
-		/* if submodule, mark as GITLINK and remove trailing slash */
-		if (is_submodule) {
-			size_t len = strlen(wi->entry.path);
-			assert(wi->entry.path[len - 1] == '/');
-			wi->entry.path[len - 1] = '\0';
-			wi->entry.mode = S_IFGITLINK;
-		}
-	}
-
-	return 0;
-}
-
-int git_iterator_for_workdir_range(
-	git_iterator **iter,
-	git_repository *repo,
-	const char *start,
-	const char *end)
-{
-	int error;
-	workdir_iterator *wi;
-
-	assert(iter && repo);
-
-	if (git_repository_is_bare(repo)) {
-		giterr_set(GITERR_INVALID,
-			"Cannot scan working directory for bare repo");
-		return -1;
-	}
-
-	ITERATOR_BASE_INIT(wi, workdir, WORKDIR);
-
-	wi->repo = repo;
-
-	if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
-		git_path_to_dir(&wi->path) < 0 ||
-		git_ignore__for_path(repo, "", &wi->ignores) < 0)
-	{
-		git__free(wi);
-		return -1;
-	}
-
-	wi->root_len = wi->path.size;
-
-	if ((error = workdir_iterator__expand_dir(wi)) < 0) {
-		if (error == GIT_ENOTFOUND)
-			error = 0;
-		else {
-			git_iterator_free((git_iterator *)wi);
-			wi = NULL;
-		}
-	}
-
-	*iter = (git_iterator *)wi;
-
-	return error;
-}
-
-
-int git_iterator_current_tree_entry(
-	git_iterator *iter, const git_tree_entry **tree_entry)
-{
-	*tree_entry = (iter->type != GIT_ITERATOR_TREE) ? NULL :
-		tree_iterator__tree_entry((tree_iterator *)iter);
-	return 0;
-}
-
-int git_iterator_current_is_ignored(git_iterator *iter)
-{
-	return (iter->type != GIT_ITERATOR_WORKDIR) ? 0 :
-		((workdir_iterator *)iter)->is_ignored;
-}
-
-int git_iterator_advance_into_directory(
-	git_iterator *iter, const git_index_entry **entry)
-{
-	workdir_iterator *wi = (workdir_iterator *)iter;
-
-	if (iter->type == GIT_ITERATOR_WORKDIR &&
-		wi->entry.path &&
-		S_ISDIR(wi->entry.mode) &&
-		!S_ISGITLINK(wi->entry.mode))
-	{
-		if (workdir_iterator__expand_dir(wi) < 0)
-			/* if error loading or if empty, skip the directory. */
-			return workdir_iterator__advance(iter, entry);
-	}
-
-	return entry ? git_iterator_current(iter, entry) : 0;
-}
-
-int git_iterator_cmp(
-	git_iterator *iter, const char *path_prefix)
-{
-	const git_index_entry *entry;
-
-	/* a "done" iterator is after every prefix */
-	if (git_iterator_current(iter, &entry) < 0 ||
-		entry == NULL)
-		return 1;
-
-	/* a NULL prefix is after any valid iterator */
-	if (!path_prefix)
-		return -1;
-
-	return git__prefixcmp(entry->path, path_prefix);
-}
-
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "iterator.h"
+#include "tree.h"
+#include "ignore.h"
+#include "buffer.h"
+#include "git2/submodule.h"
+#include <ctype.h>
+
+#define ITERATOR_SET_CB(P,NAME_LC) do { \
+	(P)->cb.current = NAME_LC ## _iterator__current; \
+	(P)->cb.advance = NAME_LC ## _iterator__advance; \
+	(P)->cb.advance_into = NAME_LC ## _iterator__advance_into; \
+	(P)->cb.seek    = NAME_LC ## _iterator__seek; \
+	(P)->cb.reset   = NAME_LC ## _iterator__reset; \
+	(P)->cb.at_end  = NAME_LC ## _iterator__at_end; \
+	(P)->cb.free    = NAME_LC ## _iterator__free; \
+	} while (0)
+
+#define ITERATOR_CASE_FLAGS \
+	(GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_IGNORE_CASE)
+
+#define ITERATOR_BASE_INIT(P,NAME_LC,NAME_UC,REPO) do { \
+	(P) = git__calloc(1, sizeof(NAME_LC ## _iterator)); \
+	GITERR_CHECK_ALLOC(P); \
+	(P)->base.type    = GIT_ITERATOR_TYPE_ ## NAME_UC; \
+	(P)->base.cb      = &(P)->cb; \
+	ITERATOR_SET_CB(P,NAME_LC); \
+	(P)->base.repo    = (REPO); \
+	(P)->base.start   = start ? git__strdup(start) : NULL; \
+	(P)->base.end     = end ? git__strdup(end) : NULL; \
+	if ((start && !(P)->base.start) || (end && !(P)->base.end)) { \
+		git__free(P); return -1; } \
+	(P)->base.prefixcomp = git__prefixcmp; \
+	(P)->base.flags = flags & ~ITERATOR_CASE_FLAGS; \
+	if ((P)->base.flags & GIT_ITERATOR_DONT_AUTOEXPAND) \
+		(P)->base.flags |= GIT_ITERATOR_INCLUDE_TREES; \
+	} while (0)
+
+#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
+#define iterator__ignore_case(I)     iterator__flag(I,IGNORE_CASE)
+#define iterator__include_trees(I)   iterator__flag(I,INCLUDE_TREES)
+#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
+#define iterator__do_autoexpand(I)   !iterator__flag(I,DONT_AUTOEXPAND)
+
+#define iterator__end(I) ((git_iterator *)(I))->end
+#define iterator__past_end(I,PATH) \
+	(iterator__end(I) && ((git_iterator *)(I))->prefixcomp((PATH),iterator__end(I)) > 0)
+
+
+static int iterator__reset_range(
+	git_iterator *iter, const char *start, const char *end)
+{
+	if (start) {
+		if (iter->start)
+			git__free(iter->start);
+		iter->start = git__strdup(start);
+		GITERR_CHECK_ALLOC(iter->start);
+	}
+
+	if (end) {
+		if (iter->end)
+			git__free(iter->end);
+		iter->end = git__strdup(end);
+		GITERR_CHECK_ALLOC(iter->end);
+	}
+
+	return 0;
+}
+
+static int iterator__update_ignore_case(
+	git_iterator *iter,
+	git_iterator_flag_t flags)
+{
+	int error = 0, ignore_case = -1;
+
+	if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
+		ignore_case = true;
+	else if ((flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0)
+		ignore_case = false;
+	else {
+		git_index *index;
+
+		if (!(error = git_repository_index__weakptr(&index, iter->repo)))
+			ignore_case = (index->ignore_case != false);
+	}
+
+	if (ignore_case > 0)
+		iter->flags = (iter->flags | GIT_ITERATOR_IGNORE_CASE);
+	else if (ignore_case == 0)
+		iter->flags = (iter->flags & ~GIT_ITERATOR_IGNORE_CASE);
+
+	iter->prefixcomp = iterator__ignore_case(iter) ?
+		git__prefixcmp_icase : git__prefixcmp;
+
+	return error;
+}
+
+GIT_INLINE(void) iterator__clear_entry(const git_index_entry **entry)
+{
+	if (entry) *entry = NULL;
+}
+
+
+static int empty_iterator__noop(const git_index_entry **e, git_iterator *i)
+{
+	GIT_UNUSED(i);
+	iterator__clear_entry(e);
+	return 0;
+}
+
+static int empty_iterator__seek(git_iterator *i, const char *p)
+{
+	GIT_UNUSED(i); GIT_UNUSED(p);
+	return -1;
+}
+
+static int empty_iterator__reset(git_iterator *i, const char *s, const char *e)
+{
+	GIT_UNUSED(i); GIT_UNUSED(s); GIT_UNUSED(e);
+	return 0;
+}
+
+static int empty_iterator__at_end(git_iterator *i)
+{
+	GIT_UNUSED(i);
+	return 1;
+}
+
+static void empty_iterator__free(git_iterator *i)
+{
+	GIT_UNUSED(i);
+}
+
+typedef struct {
+	git_iterator base;
+	git_iterator_callbacks cb;
+} empty_iterator;
+
+int git_iterator_for_nothing(
+	git_iterator **iter,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end)
+{
+	empty_iterator *i;
+
+#define empty_iterator__current empty_iterator__noop
+#define empty_iterator__advance empty_iterator__noop
+#define empty_iterator__advance_into empty_iterator__noop
+
+	ITERATOR_BASE_INIT(i, empty, EMPTY, NULL);
+
+	if ((flags & GIT_ITERATOR_IGNORE_CASE) != 0)
+		i->base.flags |= GIT_ITERATOR_IGNORE_CASE;
+
+	*iter = (git_iterator *)i;
+	return 0;
+}
+
+
+typedef struct tree_iterator_entry tree_iterator_entry;
+struct tree_iterator_entry {
+	tree_iterator_entry *parent;
+	const git_tree_entry *te;
+	git_tree *tree;
+};
+
+typedef struct tree_iterator_frame tree_iterator_frame;
+struct tree_iterator_frame {
+	tree_iterator_frame *up, *down;
+
+	size_t n_entries; /* items in this frame */
+	size_t current;   /* start of currently active range in frame */
+	size_t next;      /* start of next range in frame */
+
+	const char *start;
+	size_t startlen;
+
+	tree_iterator_entry *entries[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+	git_iterator base;
+	git_iterator_callbacks cb;
+	tree_iterator_frame *head, *root;
+	git_pool pool;
+	git_index_entry entry;
+	git_buf path;
+	int path_ambiguities;
+	bool path_has_filename;
+	int (*strncomp)(const char *a, const char *b, size_t sz);
+} tree_iterator;
+
+static char *tree_iterator__current_filename(
+	tree_iterator *ti, const git_tree_entry *te)
+{
+	if (!ti->path_has_filename) {
+		if (git_buf_joinpath(&ti->path, ti->path.ptr, te->filename) < 0)
+			return NULL;
+
+		if (git_tree_entry__is_tree(te) && git_buf_putc(&ti->path, '/') < 0)
+			return NULL;
+
+		ti->path_has_filename = true;
+	}
+
+	return ti->path.ptr;
+}
+
+static void tree_iterator__rewrite_filename(tree_iterator *ti)
+{
+	tree_iterator_entry *scan = ti->head->entries[ti->head->current];
+	ssize_t strpos = ti->path.size;
+	const git_tree_entry *te;
+
+	if (strpos && ti->path.ptr[strpos - 1] == '/')
+		strpos--;
+
+	for (; scan && (te = scan->te); scan = scan->parent) {
+		strpos -= te->filename_len;
+		memcpy(&ti->path.ptr[strpos], te->filename, te->filename_len);
+		strpos -= 1; /* separator */
+	}
+}
+
+static int tree_iterator__te_cmp(
+	const git_tree_entry *a,
+	const git_tree_entry *b,
+	int (*compare)(const char *, const char *, size_t))
+{
+	return git_path_cmp(
+		a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
+		b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
+		compare);
+}
+
+static int tree_iterator__ci_cmp(const void *a, const void *b, void *p)
+{
+	const tree_iterator_entry *ae = a, *be = b;
+	int cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncasecmp);
+
+	if (!cmp) {
+		/* stabilize sort order among equivalent names */
+		if (!ae->parent->te || !be->parent->te)
+			cmp = tree_iterator__te_cmp(ae->te, be->te, git__strncmp);
+		else
+			cmp = tree_iterator__ci_cmp(ae->parent, be->parent, p);
+	}
+
+	return cmp;
+}
+
+static int tree_iterator__search_cmp(const void *key, const void *val, void *p)
+{
+	const tree_iterator_frame *tf = key;
+	const git_tree_entry *te = ((tree_iterator_entry *)val)->te;
+
+	return git_path_cmp(
+		tf->start, tf->startlen, false,
+		te->filename, te->filename_len, te->attr == GIT_FILEMODE_TREE,
+		((tree_iterator *)p)->strncomp);
+}
+
+static int tree_iterator__set_next(tree_iterator *ti, tree_iterator_frame *tf)
+{
+	int error;
+	const git_tree_entry *te, *last = NULL;
+
+	tf->next = tf->current;
+
+	for (; tf->next < tf->n_entries; tf->next++, last = te) {
+		te = tf->entries[tf->next]->te;
+
+		if (last && tree_iterator__te_cmp(last, te, ti->strncomp))
+			break;
+
+		/* load trees for items in [current,next) range */
+		if (git_tree_entry__is_tree(te) &&
+			(error = git_tree_lookup(
+				&tf->entries[tf->next]->tree, ti->base.repo, &te->oid)) < 0)
+			return error;
+	}
+
+	if (tf->next > tf->current + 1)
+		ti->path_ambiguities++;
+
+	if (last && !tree_iterator__current_filename(ti, last))
+		return -1;
+
+	return 0;
+}
+
+GIT_INLINE(bool) tree_iterator__at_tree(tree_iterator *ti)
+{
+	return (ti->head->current < ti->head->n_entries &&
+			ti->head->entries[ti->head->current]->tree != NULL);
+}
+
+static int tree_iterator__push_frame(tree_iterator *ti)
+{
+	int error = 0;
+	tree_iterator_frame *head = ti->head, *tf = NULL;
+	size_t i, n_entries = 0;
+
+	if (head->current >= head->n_entries || !head->entries[head->current]->tree)
+		return 0;
+
+	for (i = head->current; i < head->next; ++i)
+		n_entries += git_tree_entrycount(head->entries[i]->tree);
+
+	tf = git__calloc(sizeof(tree_iterator_frame) +
+		n_entries * sizeof(tree_iterator_entry *), 1);
+	GITERR_CHECK_ALLOC(tf);
+
+	tf->n_entries = n_entries;
+
+	tf->up     = head;
+	head->down = tf;
+	ti->head   = tf;
+
+	for (i = head->current, n_entries = 0; i < head->next; ++i) {
+		git_tree *tree = head->entries[i]->tree;
+		size_t j, max_j = git_tree_entrycount(tree);
+
+		for (j = 0; j < max_j; ++j) {
+			tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1);
+			GITERR_CHECK_ALLOC(entry);
+
+			entry->parent = head->entries[i];
+			entry->te     = git_tree_entry_byindex(tree, j);
+			entry->tree   = NULL;
+
+			tf->entries[n_entries++] = entry;
+		}
+	}
+
+	/* if ignore_case, sort entries case insensitively */
+	if (iterator__ignore_case(ti))
+		git__tsort_r(
+			(void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf);
+
+	/* pick tf->current based on "start" (or start at zero) */
+	if (head->startlen > 0) {
+		git__bsearch_r((void **)tf->entries, tf->n_entries, head,
+			tree_iterator__search_cmp, ti, &tf->current);
+
+		while (tf->current &&
+			   !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti))
+			tf->current--;
+
+		if ((tf->start = strchr(head->start, '/')) != NULL) {
+			tf->start++;
+			tf->startlen = strlen(tf->start);
+		}
+	}
+
+	ti->path_has_filename = false;
+
+	if ((error = tree_iterator__set_next(ti, tf)) < 0)
+		return error;
+
+	/* autoexpand as needed */
+	if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
+		return tree_iterator__push_frame(ti);
+
+	return 0;
+}
+
+static bool tree_iterator__move_to_next(
+	tree_iterator *ti, tree_iterator_frame *tf)
+{
+	if (tf->next > tf->current + 1)
+		ti->path_ambiguities--;
+
+	if (!tf->up) { /* at root */
+		tf->current = tf->next;
+		return false;
+	}
+
+	for (; tf->current < tf->next; tf->current++) {
+		git_tree_free(tf->entries[tf->current]->tree);
+		tf->entries[tf->current]->tree = NULL;
+	}
+
+	return (tf->current < tf->n_entries);
+}
+
+static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
+{
+	tree_iterator_frame *tf = ti->head;
+
+	if (!tf->up)
+		return false;
+
+	ti->head = tf->up;
+	ti->head->down = NULL;
+
+	tree_iterator__move_to_next(ti, tf);
+
+	if (!final) { /* if final, don't bother to clean up */
+		git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
+		git_buf_rtruncate_at_char(&ti->path, '/');
+	}
+
+	git__free(tf);
+
+	return true;
+}
+
+static int tree_iterator__pop_all(tree_iterator *ti, bool to_end, bool final)
+{
+	while (tree_iterator__pop_frame(ti, final)) /* pop to root */;
+
+	if (!final) {
+		ti->head->current = to_end ? ti->head->n_entries : 0;
+		ti->path_ambiguities = 0;
+		git_buf_clear(&ti->path);
+	}
+
+	return 0;
+}
+
+static int tree_iterator__current(
+	const git_index_entry **entry, git_iterator *self)
+{
+	tree_iterator *ti = (tree_iterator *)self;
+	tree_iterator_frame *tf = ti->head;
+	const git_tree_entry *te;
+
+	iterator__clear_entry(entry);
+
+	if (tf->current >= tf->n_entries)
+		return 0;
+	te = tf->entries[tf->current]->te;
+
+	ti->entry.mode = te->attr;
+	git_oid_cpy(&ti->entry.oid, &te->oid);
+
+	ti->entry.path = tree_iterator__current_filename(ti, te);
+	GITERR_CHECK_ALLOC(ti->entry.path);
+
+	if (ti->path_ambiguities > 0)
+		tree_iterator__rewrite_filename(ti);
+
+	if (iterator__past_end(ti, ti->entry.path))
+		return tree_iterator__pop_all(ti, true, false);
+
+	if (entry)
+		*entry = &ti->entry;
+
+	return 0;
+}
+
+static int tree_iterator__advance_into(
+	const git_index_entry **entry, git_iterator *self)
+{
+	int error = 0;
+	tree_iterator *ti = (tree_iterator *)self;
+
+	iterator__clear_entry(entry);
+
+	if (tree_iterator__at_tree(ti) &&
+		!(error = tree_iterator__push_frame(ti)))
+		error = tree_iterator__current(entry, self);
+
+	return error;
+}
+
+static int tree_iterator__advance(
+	const git_index_entry **entry, git_iterator *self)
+{
+	int error;
+	tree_iterator *ti = (tree_iterator *)self;
+	tree_iterator_frame *tf = ti->head;
+
+	iterator__clear_entry(entry);
+
+	if (tf->current > tf->n_entries)
+		return 0;
+
+	if (iterator__do_autoexpand(ti) && iterator__include_trees(ti) &&
+		tree_iterator__at_tree(ti))
+		return tree_iterator__advance_into(entry, self);
+
+	if (ti->path_has_filename) {
+		git_buf_rtruncate_at_char(&ti->path, '/');
+		ti->path_has_filename = false;
+	}
+
+	/* scan forward and up, advancing in frame or popping frame when done */
+	while (!tree_iterator__move_to_next(ti, tf) &&
+		   tree_iterator__pop_frame(ti, false))
+		tf = ti->head;
+
+	/* find next and load trees */
+	if ((error = tree_iterator__set_next(ti, tf)) < 0)
+		return error;
+
+	/* deal with include_trees / auto_expand as needed */
+	if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti))
+		return tree_iterator__advance_into(entry, self);
+
+	return tree_iterator__current(entry, self);
+}
+
+static int tree_iterator__seek(git_iterator *self, const char *prefix)
+{
+	GIT_UNUSED(self); GIT_UNUSED(prefix);
+	return -1;
+}
+
+static int tree_iterator__reset(
+	git_iterator *self, const char *start, const char *end)
+{
+	tree_iterator *ti = (tree_iterator *)self;
+
+	tree_iterator__pop_all(ti, false, false);
+
+	if (iterator__reset_range(self, start, end) < 0)
+		return -1;
+
+	return tree_iterator__push_frame(ti); /* re-expand root tree */
+}
+
+static int tree_iterator__at_end(git_iterator *self)
+{
+	tree_iterator *ti = (tree_iterator *)self;
+	return (ti->head->current >= ti->head->n_entries);
+}
+
+static void tree_iterator__free(git_iterator *self)
+{
+	tree_iterator *ti = (tree_iterator *)self;
+
+	tree_iterator__pop_all(ti, true, false);
+
+	git_tree_free(ti->head->entries[0]->tree);
+	git__free(ti->head);
+	git_pool_clear(&ti->pool);
+	git_buf_free(&ti->path);
+}
+
+static int tree_iterator__create_root_frame(tree_iterator *ti, git_tree *tree)
+{
+	size_t sz = sizeof(tree_iterator_frame) + sizeof(tree_iterator_entry);
+	tree_iterator_frame *root = git__calloc(sz, sizeof(char));
+	GITERR_CHECK_ALLOC(root);
+
+	root->n_entries  = 1;
+	root->next       = 1;
+	root->start      = ti->base.start;
+	root->startlen   = root->start ? strlen(root->start) : 0;
+	root->entries[0] = git_pool_mallocz(&ti->pool, 1);
+	GITERR_CHECK_ALLOC(root->entries[0]);
+	root->entries[0]->tree = tree;
+
+	ti->head = ti->root = root;
+
+	return 0;
+}
+
+int git_iterator_for_tree(
+	git_iterator **iter,
+	git_tree *tree,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end)
+{
+	int error;
+	tree_iterator *ti;
+
+	if (tree == NULL)
+		return git_iterator_for_nothing(iter, flags, start, end);
+
+	if ((error = git_object_dup((git_object **)&tree, (git_object *)tree)) < 0)
+		return error;
+
+	ITERATOR_BASE_INIT(ti, tree, TREE, git_tree_owner(tree));
+
+	if ((error = iterator__update_ignore_case((git_iterator *)ti, flags)) < 0)
+		goto fail;
+	ti->strncomp = iterator__ignore_case(ti) ? git__strncasecmp : git__strncmp;
+
+	if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
+		(error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
+		(error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
+		goto fail;
+
+	*iter = (git_iterator *)ti;
+	return 0;
+
+fail:
+	git_iterator_free((git_iterator *)ti);
+	return error;
+}
+
+
+typedef struct {
+	git_iterator base;
+	git_iterator_callbacks cb;
+	git_index *index;
+	size_t current;
+	/* when not in autoexpand mode, use these to represent "tree" state */
+	git_buf partial;
+	size_t partial_pos;
+	char restore_terminator;
+	git_index_entry tree_entry;
+} index_iterator;
+
+static const git_index_entry *index_iterator__index_entry(index_iterator *ii)
+{
+	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+
+	if (ie != NULL && iterator__past_end(ii, ie->path)) {
+		ii->current = git_index_entrycount(ii->index);
+		ie = NULL;
+	}
+
+	return ie;
+}
+
+static const git_index_entry *index_iterator__skip_conflicts(index_iterator *ii)
+{
+	const git_index_entry *ie;
+
+	while ((ie = index_iterator__index_entry(ii)) != NULL &&
+		   git_index_entry_stage(ie) != 0)
+		ii->current++;
+
+	return ie;
+}
+
+static void index_iterator__next_prefix_tree(index_iterator *ii)
+{
+	const char *slash;
+
+	if (!iterator__include_trees(ii))
+		return;
+
+	slash = strchr(&ii->partial.ptr[ii->partial_pos], '/');
+
+	if (slash != NULL) {
+		ii->partial_pos = (slash - ii->partial.ptr) + 1;
+		ii->restore_terminator = ii->partial.ptr[ii->partial_pos];
+		ii->partial.ptr[ii->partial_pos] = '\0';
+	} else {
+		ii->partial_pos = ii->partial.size;
+	}
+
+	if (index_iterator__index_entry(ii) == NULL)
+		ii->partial_pos = ii->partial.size;
+}
+
+static int index_iterator__first_prefix_tree(index_iterator *ii)
+{
+	const git_index_entry *ie = index_iterator__skip_conflicts(ii);
+	const char *scan, *prior, *slash;
+
+	if (!ie || !iterator__include_trees(ii))
+		return 0;
+
+	/* find longest common prefix with prior index entry */
+	for (scan = slash = ie->path, prior = ii->partial.ptr;
+		 *scan && *scan == *prior; ++scan, ++prior)
+		if (*scan == '/')
+			slash = scan;
+
+	if (git_buf_sets(&ii->partial, ie->path) < 0)
+		return -1;
+
+	ii->partial_pos = (slash - ie->path) + 1;
+	index_iterator__next_prefix_tree(ii);
+
+	return 0;
+}
+
+#define index_iterator__at_tree(I) \
+	(iterator__include_trees(I) && (I)->partial_pos < (I)->partial.size)
+
+static int index_iterator__current(
+	const git_index_entry **entry, git_iterator *self)
+{
+	index_iterator *ii = (index_iterator *)self;
+	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+
+	if (ie != NULL && index_iterator__at_tree(ii)) {
+		ii->tree_entry.path = ii->partial.ptr;
+		ie = &ii->tree_entry;
+	}
+
+	if (entry)
+		*entry = ie;
+
+	return 0;
+}
+
+static int index_iterator__at_end(git_iterator *self)
+{
+	index_iterator *ii = (index_iterator *)self;
+	return (ii->current >= git_index_entrycount(ii->index));
+}
+
+static int index_iterator__advance(
+	const git_index_entry **entry, git_iterator *self)
+{
+	index_iterator *ii = (index_iterator *)self;
+	size_t entrycount = git_index_entrycount(ii->index);
+	const git_index_entry *ie;
+
+	if (index_iterator__at_tree(ii)) {
+		if (iterator__do_autoexpand(ii)) {
+			ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
+			index_iterator__next_prefix_tree(ii);
+		} else {
+			/* advance to sibling tree (i.e. find entry with new prefix) */
+			while (ii->current < entrycount) {
+				ii->current++;
+
+				if (!(ie = git_index_get_byindex(ii->index, ii->current)) ||
+					ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0)
+					break;
+			}
+
+			if (index_iterator__first_prefix_tree(ii) < 0)
+				return -1;
+		}
+	} else {
+		if (ii->current < entrycount)
+			ii->current++;
+
+		if (index_iterator__first_prefix_tree(ii) < 0)
+			return -1;
+	}
+
+	return index_iterator__current(entry, self);
+}
+
+static int index_iterator__advance_into(
+	const git_index_entry **entry, git_iterator *self)
+{
+	index_iterator *ii = (index_iterator *)self;
+	const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current);
+
+	if (ie != NULL && index_iterator__at_tree(ii)) {
+		if (ii->restore_terminator)
+			ii->partial.ptr[ii->partial_pos] = ii->restore_terminator;
+		index_iterator__next_prefix_tree(ii);
+	}
+
+	return index_iterator__current(entry, self);
+}
+
+static int index_iterator__seek(git_iterator *self, const char *prefix)
+{
+	GIT_UNUSED(self); GIT_UNUSED(prefix);
+	return -1;
+}
+
+static int index_iterator__reset(
+	git_iterator *self, const char *start, const char *end)
+{
+	index_iterator *ii = (index_iterator *)self;
+	const git_index_entry *ie;
+
+	if (iterator__reset_range(self, start, end) < 0)
+		return -1;
+
+	ii->current = ii->base.start ?
+		git_index__prefix_position(ii->index, ii->base.start) : 0;
+
+	if ((ie = index_iterator__skip_conflicts(ii)) == NULL)
+		return 0;
+
+	if (git_buf_sets(&ii->partial, ie->path) < 0)
+		return -1;
+
+	ii->partial_pos = 0;
+
+	if (ii->base.start) {
+		size_t startlen = strlen(ii->base.start);
+
+		ii->partial_pos = (startlen > ii->partial.size) ?
+			ii->partial.size : startlen;
+	}
+
+	index_iterator__next_prefix_tree(ii);
+
+	return 0;
+}
+
+static void index_iterator__free(git_iterator *self)
+{
+	index_iterator *ii = (index_iterator *)self;
+	git_index_free(ii->index);
+	ii->index = NULL;
+
+	git_buf_free(&ii->partial);
+}
+
+int git_iterator_for_index(
+	git_iterator **iter,
+	git_index  *index,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end)
+{
+	index_iterator *ii;
+
+	ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index));
+
+	if (index->ignore_case) {
+		ii->base.flags |= GIT_ITERATOR_IGNORE_CASE;
+		ii->base.prefixcomp = git__prefixcmp_icase;
+	}
+
+	ii->index = index;
+	GIT_REFCOUNT_INC(index);
+
+	git_buf_init(&ii->partial, 0);
+	ii->tree_entry.mode = GIT_FILEMODE_TREE;
+
+	index_iterator__reset((git_iterator *)ii, NULL, NULL);
+
+	*iter = (git_iterator *)ii;
+
+	return 0;
+}
+
+
+#define WORKDIR_MAX_DEPTH 100
+
+typedef struct workdir_iterator_frame workdir_iterator_frame;
+struct workdir_iterator_frame {
+	workdir_iterator_frame *next;
+	git_vector entries;
+	size_t index;
+};
+
+typedef struct {
+	git_iterator base;
+	git_iterator_callbacks cb;
+	workdir_iterator_frame *stack;
+	git_ignores ignores;
+	git_index_entry entry;
+	git_buf path;
+	size_t root_len;
+	int is_ignored;
+	int depth;
+} workdir_iterator;
+
+GIT_INLINE(bool) path_is_dotgit(const git_path_with_stat *ps)
+{
+	if (!ps)
+		return false;
+	else {
+		const char *path = ps->path;
+		size_t len  = ps->path_len;
+
+		if (len < 4)
+			return false;
+		if (path[len - 1] == '/')
+			len--;
+		if (tolower(path[len - 1]) != 't' ||
+			tolower(path[len - 2]) != 'i' ||
+			tolower(path[len - 3]) != 'g' ||
+			tolower(path[len - 4]) != '.')
+			return false;
+		return (len == 4 || path[len - 5] == '/');
+	}
+}
+
+static workdir_iterator_frame *workdir_iterator__alloc_frame(
+	workdir_iterator *wi)
+{
+	workdir_iterator_frame *wf = git__calloc(1, sizeof(workdir_iterator_frame));
+	git_vector_cmp entry_compare = CASESELECT(
+		iterator__ignore_case(wi),
+		git_path_with_stat_cmp_icase, git_path_with_stat_cmp);
+
+	if (wf == NULL)
+		return NULL;
+
+	if (git_vector_init(&wf->entries, 0, entry_compare) != 0) {
+		git__free(wf);
+		return NULL;
+	}
+
+	return wf;
+}
+
+static void workdir_iterator__free_frame(workdir_iterator_frame *wf)
+{
+	unsigned int i;
+	git_path_with_stat *path;
+
+	git_vector_foreach(&wf->entries, i, path)
+		git__free(path);
+	git_vector_free(&wf->entries);
+	git__free(wf);
+}
+
+static int workdir_iterator__update_entry(workdir_iterator *wi);
+
+static int workdir_iterator__entry_cmp(const void *i, const void *item)
+{
+	const workdir_iterator *wi = (const workdir_iterator *)i;
+	const git_path_with_stat *ps = item;
+	return wi->base.prefixcomp(wi->base.start, ps->path);
+}
+
+static void workdir_iterator__seek_frame_start(
+	workdir_iterator *wi, workdir_iterator_frame *wf)
+{
+	if (!wf)
+		return;
+
+	if (wi->base.start)
+		git_vector_bsearch2(
+			&wf->index, &wf->entries, workdir_iterator__entry_cmp, wi);
+	else
+		wf->index = 0;
+
+	if (path_is_dotgit(git_vector_get(&wf->entries, wf->index)))
+		wf->index++;
+}
+
+static int workdir_iterator__expand_dir(workdir_iterator *wi)
+{
+	int error;
+	workdir_iterator_frame *wf;
+
+	wf = workdir_iterator__alloc_frame(wi);
+	GITERR_CHECK_ALLOC(wf);
+
+	error = git_path_dirload_with_stat(
+		wi->path.ptr, wi->root_len, iterator__ignore_case(wi),
+		wi->base.start, wi->base.end, &wf->entries);
+
+	if (error < 0 || wf->entries.length == 0) {
+		workdir_iterator__free_frame(wf);
+		return GIT_ENOTFOUND;
+	}
+
+	if (++(wi->depth) > WORKDIR_MAX_DEPTH) {
+		giterr_set(GITERR_REPOSITORY,
+			"Working directory is too deep (%d)", wi->depth);
+		workdir_iterator__free_frame(wf);
+		return -1;
+	}
+
+	workdir_iterator__seek_frame_start(wi, wf);
+
+	/* only push new ignores if this is not top level directory */
+	if (wi->stack != NULL) {
+		ssize_t slash_pos = git_buf_rfind_next(&wi->path, '/');
+		(void)git_ignore__push_dir(&wi->ignores, &wi->path.ptr[slash_pos + 1]);
+	}
+
+	wf->next  = wi->stack;
+	wi->stack = wf;
+
+	return workdir_iterator__update_entry(wi);
+}
+
+static int workdir_iterator__current(
+	const git_index_entry **entry, git_iterator *self)
+{
+	workdir_iterator *wi = (workdir_iterator *)self;
+	if (entry)
+		*entry = (wi->entry.path == NULL) ? NULL : &wi->entry;
+	return 0;
+}
+
+static int workdir_iterator__at_end(git_iterator *self)
+{
+	return (((workdir_iterator *)self)->entry.path == NULL);
+}
+
+static int workdir_iterator__advance_into(
+	const git_index_entry **entry, git_iterator *iter)
+{
+	int error = 0;
+	workdir_iterator *wi = (workdir_iterator *)iter;
+
+	iterator__clear_entry(entry);
+
+	/* workdir iterator will allow you to explicitly advance into a
+	 * commit/submodule (as well as a tree) to avoid some cases where an
+	 * entry is mislabeled as a submodule in the working directory
+	 */
+	if (wi->entry.path != NULL &&
+		(wi->entry.mode == GIT_FILEMODE_TREE ||
+		 wi->entry.mode == GIT_FILEMODE_COMMIT))
+		/* returns GIT_ENOTFOUND if the directory is empty */
+		error = workdir_iterator__expand_dir(wi);
+
+	if (!error && entry)
+		error = workdir_iterator__current(entry, iter);
+
+	return error;
+}
+
+static int workdir_iterator__advance(
+	const git_index_entry **entry, git_iterator *self)
+{
+	int error = 0;
+	workdir_iterator *wi = (workdir_iterator *)self;
+	workdir_iterator_frame *wf;
+	git_path_with_stat *next;
+
+	/* given include_trees & autoexpand, we might have to go into a tree */
+	if (iterator__do_autoexpand(wi) &&
+		wi->entry.path != NULL &&
+		wi->entry.mode == GIT_FILEMODE_TREE)
+	{
+		error = workdir_iterator__advance_into(entry, self);
+
+		/* continue silently past empty directories if autoexpanding */
+		if (error != GIT_ENOTFOUND)
+			return error;
+		giterr_clear();
+		error = 0;
+	}
+
+	if (entry != NULL)
+		*entry = NULL;
+
+	while (wi->entry.path != NULL) {
+		wf   = wi->stack;
+		next = git_vector_get(&wf->entries, ++wf->index);
+
+		if (next != NULL) {
+			/* match git's behavior of ignoring anything named ".git" */
+			if (path_is_dotgit(next))
+				continue;
+			/* else found a good entry */
+			break;
+		}
+
+		/* pop stack if anything is left to pop */
+		if (!wf->next) {
+			memset(&wi->entry, 0, sizeof(wi->entry));
+			return 0;
+		}
+
+		wi->stack = wf->next;
+		wi->depth--;
+		workdir_iterator__free_frame(wf);
+		git_ignore__pop_dir(&wi->ignores);
+	}
+
+	error = workdir_iterator__update_entry(wi);
+
+	if (!error && entry != NULL)
+		error = workdir_iterator__current(entry, self);
+
+	return error;
+}
+
+static int workdir_iterator__seek(git_iterator *self, const char *prefix)
+{
+	GIT_UNUSED(self);
+	GIT_UNUSED(prefix);
+	/* pop stack until matching prefix */
+	/* find prefix item in current frame */
+	/* push subdirectories as deep as possible while matching */
+	return 0;
+}
+
+static int workdir_iterator__reset(
+	git_iterator *self, const char *start, const char *end)
+{
+	workdir_iterator *wi = (workdir_iterator *)self;
+
+	while (wi->stack != NULL && wi->stack->next != NULL) {
+		workdir_iterator_frame *wf = wi->stack;
+		wi->stack = wf->next;
+		workdir_iterator__free_frame(wf);
+		git_ignore__pop_dir(&wi->ignores);
+	}
+	wi->depth = 0;
+
+	if (iterator__reset_range(self, start, end) < 0)
+		return -1;
+
+	workdir_iterator__seek_frame_start(wi, wi->stack);
+
+	return workdir_iterator__update_entry(wi);
+}
+
+static void workdir_iterator__free(git_iterator *self)
+{
+	workdir_iterator *wi = (workdir_iterator *)self;
+
+	while (wi->stack != NULL) {
+		workdir_iterator_frame *wf = wi->stack;
+		wi->stack = wf->next;
+		workdir_iterator__free_frame(wf);
+	}
+
+	git_ignore__free(&wi->ignores);
+	git_buf_free(&wi->path);
+}
+
+static int workdir_iterator__update_entry(workdir_iterator *wi)
+{
+	int error = 0;
+	git_path_with_stat *ps =
+		git_vector_get(&wi->stack->entries, wi->stack->index);
+
+	git_buf_truncate(&wi->path, wi->root_len);
+	memset(&wi->entry, 0, sizeof(wi->entry));
+
+	if (!ps)
+		return 0;
+
+	/* skip over .git entries */
+	if (path_is_dotgit(ps))
+		return workdir_iterator__advance(NULL, (git_iterator *)wi);
+
+	if (git_buf_put(&wi->path, ps->path, ps->path_len) < 0)
+		return -1;
+
+	if (iterator__past_end(wi, wi->path.ptr + wi->root_len))
+		return 0;
+
+	wi->entry.path = ps->path;
+
+	wi->is_ignored = -1;
+
+	git_index_entry__init_from_stat(&wi->entry, &ps->st);
+
+	/* need different mode here to keep directories during iteration */
+	wi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
+
+	/* if this is a file type we don't handle, treat as ignored */
+	if (wi->entry.mode == 0) {
+		wi->is_ignored = 1;
+		return 0;
+	}
+
+	/* if this isn't a tree, then we're done */
+	if (wi->entry.mode != GIT_FILEMODE_TREE)
+		return 0;
+
+	/* detect submodules */
+	error = git_submodule_lookup(NULL, wi->base.repo, wi->entry.path);
+	if (error == GIT_ENOTFOUND)
+		giterr_clear();
+
+	if (error == GIT_EEXISTS) /* if contains .git, treat as untracked submod */
+		error = 0;
+
+	/* if submodule, mark as GITLINK and remove trailing slash */
+	if (!error) {
+		size_t len = strlen(wi->entry.path);
+		assert(wi->entry.path[len - 1] == '/');
+		wi->entry.path[len - 1] = '\0';
+		wi->entry.mode = S_IFGITLINK;
+		return 0;
+	}
+
+	if (iterator__include_trees(wi))
+		return 0;
+
+	return workdir_iterator__advance(NULL, (git_iterator *)wi);
+}
+
+int git_iterator_for_workdir(
+	git_iterator **iter,
+	git_repository *repo,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end)
+{
+	int error;
+	workdir_iterator *wi;
+
+	assert(iter && repo);
+
+	if ((error = git_repository__ensure_not_bare(
+			 repo, "scan working directory")) < 0)
+		return error;
+
+	ITERATOR_BASE_INIT(wi, workdir, WORKDIR, repo);
+
+	if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0)
+		goto fail;
+
+	if (git_buf_sets(&wi->path, git_repository_workdir(repo)) < 0 ||
+		git_path_to_dir(&wi->path) < 0 ||
+		git_ignore__for_path(repo, "", &wi->ignores) < 0)
+	{
+		git__free(wi);
+		return -1;
+	}
+	wi->root_len = wi->path.size;
+
+	if ((error = workdir_iterator__expand_dir(wi)) < 0) {
+		if (error != GIT_ENOTFOUND)
+			goto fail;
+		giterr_clear();
+	}
+
+	*iter = (git_iterator *)wi;
+	return 0;
+
+fail:
+	git_iterator_free((git_iterator *)wi);
+	return error;
+}
+
+
+void git_iterator_free(git_iterator *iter)
+{
+	if (iter == NULL)
+		return;
+
+	iter->cb->free(iter);
+
+	git__free(iter->start);
+	git__free(iter->end);
+
+	memset(iter, 0, sizeof(*iter));
+
+	git__free(iter);
+}
+
+int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
+{
+	bool desire_ignore_case  = (ignore_case != 0);
+
+	if (iterator__ignore_case(iter) == desire_ignore_case)
+		return 0;
+
+	if (iter->type == GIT_ITERATOR_TYPE_EMPTY) {
+		if (desire_ignore_case)
+			iter->flags |= GIT_ITERATOR_IGNORE_CASE;
+		else
+			iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
+	} else {
+		giterr_set(GITERR_INVALID,
+			"Cannot currently set ignore case on non-empty iterators");
+		return -1;
+	}
+
+	return 0;
+}
+
+git_index *git_iterator_get_index(git_iterator *iter)
+{
+	if (iter->type == GIT_ITERATOR_TYPE_INDEX)
+		return ((index_iterator *)iter)->index;
+	return NULL;
+}
+
+int git_iterator_current_tree_entry(
+	const git_tree_entry **tree_entry, git_iterator *iter)
+{
+	if (iter->type != GIT_ITERATOR_TYPE_TREE)
+		*tree_entry = NULL;
+	else {
+		tree_iterator_frame *tf = ((tree_iterator *)iter)->head;
+		*tree_entry = (tf->current < tf->n_entries) ?
+			tf->entries[tf->current]->te : NULL;
+	}
+
+	return 0;
+}
+
+int git_iterator_current_parent_tree(
+	const git_tree **tree_ptr,
+	git_iterator *iter,
+	const char *parent_path)
+{
+	tree_iterator *ti = (tree_iterator *)iter;
+	tree_iterator_frame *tf;
+	const char *scan = parent_path;
+	const git_tree_entry *te;
+
+	*tree_ptr = NULL;
+
+	if (iter->type != GIT_ITERATOR_TYPE_TREE)
+		return 0;
+
+	for (tf = ti->root; *scan; ) {
+		if (!(tf = tf->down) ||
+			tf->current >= tf->n_entries ||
+			!(te = tf->entries[tf->current]->te) ||
+			ti->strncomp(scan, te->filename, te->filename_len) != 0)
+			return 0;
+
+		scan += te->filename_len;
+		if (*scan == '/')
+			scan++;
+	}
+
+	*tree_ptr = tf->entries[tf->current]->tree;
+	return 0;
+}
+
+bool git_iterator_current_is_ignored(git_iterator *iter)
+{
+	workdir_iterator *wi = (workdir_iterator *)iter;
+
+	if (iter->type != GIT_ITERATOR_TYPE_WORKDIR)
+		return false;
+
+	if (wi->is_ignored != -1)
+		return (bool)(wi->is_ignored != 0);
+
+	if (git_ignore__lookup(&wi->ignores, wi->entry.path, &wi->is_ignored) < 0)
+		wi->is_ignored = true;
+
+	return (bool)wi->is_ignored;
+}
+
+int git_iterator_cmp(git_iterator *iter, const char *path_prefix)
+{
+	const git_index_entry *entry;
+
+	/* a "done" iterator is after every prefix */
+	if (git_iterator_current(&entry, iter) < 0 || entry == NULL)
+		return 1;
+
+	/* a NULL prefix is after any valid iterator */
+	if (!path_prefix)
+		return -1;
+
+	return iter->prefixcomp(entry->path, path_prefix);
+}
+
+int git_iterator_current_workdir_path(git_buf **path, git_iterator *iter)
+{
+	workdir_iterator *wi = (workdir_iterator *)iter;
+
+	if (iter->type != GIT_ITERATOR_TYPE_WORKDIR || !wi->entry.path)
+		*path = NULL;
+	else
+		*path = &wi->path;
+
+	return 0;
+}
diff --git a/libgit2/src/iterator.h b/libgit2/src/iterator.h
--- a/libgit2/src/iterator.h
+++ b/libgit2/src/iterator.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,143 +9,197 @@
 
 #include "common.h"
 #include "git2/index.h"
+#include "vector.h"
+#include "buffer.h"
 
 typedef struct git_iterator git_iterator;
 
 typedef enum {
-	GIT_ITERATOR_EMPTY = 0,
-	GIT_ITERATOR_TREE = 1,
-	GIT_ITERATOR_INDEX = 2,
-	GIT_ITERATOR_WORKDIR = 3
+	GIT_ITERATOR_TYPE_EMPTY = 0,
+	GIT_ITERATOR_TYPE_TREE = 1,
+	GIT_ITERATOR_TYPE_INDEX = 2,
+	GIT_ITERATOR_TYPE_WORKDIR = 3,
 } git_iterator_type_t;
 
+typedef enum {
+	/** ignore case for entry sort order */
+	GIT_ITERATOR_IGNORE_CASE = (1 << 0),
+	/** force case sensitivity for entry sort order */
+	GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
+	/** return tree items in addition to blob items */
+	GIT_ITERATOR_INCLUDE_TREES    = (1 << 2),
+	/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
+	GIT_ITERATOR_DONT_AUTOEXPAND  = (1 << 3),
+} git_iterator_flag_t;
+
+typedef struct {
+	int (*current)(const git_index_entry **, git_iterator *);
+	int (*advance)(const git_index_entry **, git_iterator *);
+	int (*advance_into)(const git_index_entry **, git_iterator *);
+	int (*seek)(git_iterator *, const char *prefix);
+	int (*reset)(git_iterator *, const char *start, const char *end);
+	int (*at_end)(git_iterator *);
+	void (*free)(git_iterator *);
+} git_iterator_callbacks;
+
 struct git_iterator {
 	git_iterator_type_t type;
+	git_iterator_callbacks *cb;
+	git_repository *repo;
 	char *start;
 	char *end;
-	int (*current)(git_iterator *, const git_index_entry **);
-	int (*at_end)(git_iterator *);
-	int (*advance)(git_iterator *, const git_index_entry **);
-	int (*seek)(git_iterator *, const char *prefix);
-	int (*reset)(git_iterator *);
-	void (*free)(git_iterator *);
+	int (*prefixcomp)(const char *str, const char *prefix);
+	unsigned int flags;
 };
 
-extern int git_iterator_for_nothing(git_iterator **iter);
-
-extern int git_iterator_for_tree_range(
-	git_iterator **iter, git_repository *repo, git_tree *tree,
-	const char *start, const char *end);
-
-GIT_INLINE(int) git_iterator_for_tree(
-	git_iterator **iter, git_repository *repo, git_tree *tree)
-{
-	return git_iterator_for_tree_range(iter, repo, tree, NULL, NULL);
-}
-
-extern int git_iterator_for_index_range(
-	git_iterator **iter, git_repository *repo,
-	const char *start, const char *end);
+extern int git_iterator_for_nothing(
+	git_iterator **out,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end);
 
-GIT_INLINE(int) git_iterator_for_index(
-	git_iterator **iter, git_repository *repo)
-{
-	return git_iterator_for_index_range(iter, repo, NULL, NULL);
-}
+/* tree iterators will match the ignore_case value from the index of the
+ * repository, unless you override with a non-zero flag value
+ */
+extern int git_iterator_for_tree(
+	git_iterator **out,
+	git_tree *tree,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end);
 
-extern int git_iterator_for_workdir_range(
-	git_iterator **iter, git_repository *repo,
-	const char *start, const char *end);
+/* index iterators will take the ignore_case value from the index; the
+ * ignore_case flags are not used
+ */
+extern int git_iterator_for_index(
+	git_iterator **out,
+	git_index *index,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end);
 
-GIT_INLINE(int) git_iterator_for_workdir(
-	git_iterator **iter, git_repository *repo)
-{
-	return git_iterator_for_workdir_range(iter, repo, NULL, NULL);
-}
+/* workdir iterators will match the ignore_case value from the index of the
+ * repository, unless you override with a non-zero flag value
+ */
+extern int git_iterator_for_workdir(
+	git_iterator **out,
+	git_repository *repo,
+	git_iterator_flag_t flags,
+	const char *start,
+	const char *end);
 
+extern void git_iterator_free(git_iterator *iter);
 
-/* Entry is not guaranteed to be fully populated.  For a tree iterator,
- * we will only populate the mode, oid and path, for example.  For a workdir
- * iterator, we will not populate the oid.
+/* Return a git_index_entry structure for the current value the iterator
+ * is looking at or NULL if the iterator is at the end.
  *
+ * The entry may noy be fully populated.  Tree iterators will only have a
+ * value mode, OID, and path.  Workdir iterators will not have an OID (but
+ * you can use `git_iterator_current_oid()` to calculate it on demand).
+ *
  * You do not need to free the entry.  It is still "owned" by the iterator.
- * Once you call `git_iterator_advance`, then content of the old entry is
- * no longer guaranteed to be valid.
+ * Once you call `git_iterator_advance()` then the old entry is no longer
+ * guaranteed to be valid - it may be freed or just overwritten in place.
  */
 GIT_INLINE(int) git_iterator_current(
-	git_iterator *iter, const git_index_entry **entry)
+	const git_index_entry **entry, git_iterator *iter)
 {
-	return iter->current(iter, entry);
+	return iter->cb->current(entry, iter);
 }
 
-GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
+/**
+ * Advance to the next item for the iterator.
+ *
+ * If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item.  If
+ * GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree
+ * item will skip over all the items under that tree.
+ */
+GIT_INLINE(int) git_iterator_advance(
+	const git_index_entry **entry, git_iterator *iter)
 {
-	return iter->at_end(iter);
+	return iter->cb->advance(entry, iter);
 }
 
-GIT_INLINE(int) git_iterator_advance(
-	git_iterator *iter, const git_index_entry **entry)
+/**
+ * Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set).
+ *
+ * git_iterator_advance() steps through all items being iterated over
+ * (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES),
+ * but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next
+ * sibling of a tree instead of going to the first child of the tree.  In
+ * that case, use this function to advance to the first child of the tree.
+ *
+ * If the current item is not a tree, this is a no-op.
+ *
+ * For working directory iterators only, a tree (i.e. directory) can be
+ * empty.  In that case, this function returns GIT_ENOTFOUND and does not
+ * advance.  That can't happen for tree and index iterators.
+ */
+GIT_INLINE(int) git_iterator_advance_into(
+	const git_index_entry **entry, git_iterator *iter)
 {
-	return iter->advance(iter, entry);
+	return iter->cb->advance_into(entry, iter);
 }
 
 GIT_INLINE(int) git_iterator_seek(
 	git_iterator *iter, const char *prefix)
 {
-	return iter->seek(iter, prefix);
+	return iter->cb->seek(iter, prefix);
 }
 
-GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
+GIT_INLINE(int) git_iterator_reset(
+	git_iterator *iter, const char *start, const char *end)
 {
-	return iter->reset(iter);
+	return iter->cb->reset(iter, start, end);
 }
 
-GIT_INLINE(void) git_iterator_free(git_iterator *iter)
+GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
 {
-	if (iter == NULL)
-		return;
-
-	iter->free(iter);
+	return iter->cb->at_end(iter);
+}
 
-	git__free(iter->start);
-	git__free(iter->end);
+GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
+{
+	return iter->type;
+}
 
-	memset(iter, 0, sizeof(*iter));
+GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter)
+{
+	return iter->repo;
+}
 
-	git__free(iter);
+GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter)
+{
+	return iter->flags;
 }
 
-GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
+GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter)
 {
-	return iter->type;
+	return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
 }
 
+extern int git_iterator_set_ignore_case(git_iterator *iter, bool ignore_case);
+
 extern int git_iterator_current_tree_entry(
-	git_iterator *iter, const git_tree_entry **tree_entry);
+	const git_tree_entry **entry_out, git_iterator *iter);
 
-extern int git_iterator_current_is_ignored(git_iterator *iter);
+extern int git_iterator_current_parent_tree(
+	const git_tree **tree_out, git_iterator *iter, const char *parent_path);
 
-/**
- * Iterate into a workdir directory.
- *
- * Workdir iterators do not automatically descend into directories (so that
- * when comparing two iterator entries you can detect a newly created
- * directory in the workdir).  As a result, you may get S_ISDIR items from
- * a workdir iterator.  If you wish to iterate over the contents of the
- * directories you encounter, then call this function when you encounter
- * a directory.
- *
- * If there are no files in the directory, this will end up acting like a
- * regular advance and will skip past the directory, so you should be
- * prepared for that case.
- *
- * On non-workdir iterators or if not pointing at a directory, this is a
- * no-op and will not advance the iterator.
- */
-extern int git_iterator_advance_into_directory(
-	git_iterator *iter, const git_index_entry **entry);
+extern bool git_iterator_current_is_ignored(git_iterator *iter);
 
 extern int git_iterator_cmp(
 	git_iterator *iter, const char *path_prefix);
+
+/**
+ * Get full path of the current item from a workdir iterator.  This will
+ * return NULL for a non-workdir iterator.  The git_buf is still owned by
+ * the iterator; this is exposed just for efficiency.
+ */
+extern int git_iterator_current_workdir_path(
+	git_buf **path, git_iterator *iter);
+
+/* Return index pointer if index iterator, else NULL */
+extern git_index *git_iterator_get_index(git_iterator *iter);
 
 #endif
diff --git a/libgit2/src/khash.h b/libgit2/src/khash.h
--- a/libgit2/src/khash.h
+++ b/libgit2/src/khash.h
@@ -131,7 +131,9 @@
 #endif
 
 #ifdef _MSC_VER
-#define inline __inline
+#define kh_inline __inline
+#else
+#define kh_inline inline
 #endif
 
 typedef khint32_t khint_t;
@@ -345,7 +347,7 @@
 	__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
-	KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+	KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
 
 /* --- BEGIN OF HASH FUNCTIONS --- */
 
@@ -374,7 +376,7 @@
   @param  s     Pointer to a null terminated string
   @return       The hash value
  */
-static inline khint_t __ac_X31_hash_string(const char *s)
+static kh_inline khint_t __ac_X31_hash_string(const char *s)
 {
 	khint_t h = (khint_t)*s;
 	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
@@ -391,7 +393,7 @@
  */
 #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
 
-static inline khint_t __ac_Wang_hash(khint_t key)
+static kh_inline khint_t __ac_Wang_hash(khint_t key)
 {
     key += ~(key << 15);
     key ^=  (key >> 10);
diff --git a/libgit2/src/map.h b/libgit2/src/map.h
--- a/libgit2/src/map.h
+++ b/libgit2/src/map.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -22,6 +22,10 @@
 #define GIT_MAP_PRIVATE 2
 #define GIT_MAP_TYPE	0xf
 #define GIT_MAP_FIXED	0x10
+
+#ifdef __amigaos4__
+#define MAP_FAILED 0
+#endif
 
 typedef struct { /* memory mapped buffer	*/
 	void *data; /* data bytes			*/
diff --git a/libgit2/src/merge.c b/libgit2/src/merge.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/merge.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "repository.h"
+#include "revwalk.h"
+#include "buffer.h"
+#include "merge.h"
+#include "refs.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/reset.h"
+#include "commit_list.h"
+
+int git_repository_merge_cleanup(git_repository *repo)
+{
+	int error = 0;
+	git_buf merge_head_path = GIT_BUF_INIT,
+		merge_mode_path = GIT_BUF_INIT,
+		merge_msg_path = GIT_BUF_INIT;
+
+	assert(repo);
+
+	if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 ||
+		git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 ||
+		git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
+		return -1;
+
+	if (git_path_isfile(merge_head_path.ptr)) {
+		if ((error = p_unlink(merge_head_path.ptr)) < 0)
+			goto cleanup;
+	}
+
+	if (git_path_isfile(merge_mode_path.ptr))
+		(void)p_unlink(merge_mode_path.ptr);
+
+	if (git_path_isfile(merge_msg_path.ptr))
+		(void)p_unlink(merge_msg_path.ptr);
+
+cleanup:
+	git_buf_free(&merge_msg_path);
+	git_buf_free(&merge_mode_path);
+	git_buf_free(&merge_head_path);
+
+	return error;
+}
+
+int git_merge_base_many(git_oid *out, git_repository *repo, const git_oid input_array[], size_t length)
+{
+	git_revwalk *walk;
+	git_vector list;
+	git_commit_list *result = NULL;
+	int error = -1;
+	unsigned int i;
+	git_commit_list_node *commit;
+
+	assert(out && repo && input_array);
+
+	if (length < 2) {
+		giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length);
+		return -1;
+	}
+
+	if (git_vector_init(&list, length - 1, NULL) < 0)
+		return -1;
+
+	if (git_revwalk_new(&walk, repo) < 0)
+		goto cleanup;
+
+	for (i = 1; i < length; i++) {
+		commit = git_revwalk__commit_lookup(walk, &input_array[i]);
+		if (commit == NULL)
+			goto cleanup;
+
+		git_vector_insert(&list, commit);
+	}
+
+	commit = git_revwalk__commit_lookup(walk, &input_array[0]);
+	if (commit == NULL)
+		goto cleanup;
+
+	if (git_merge__bases_many(&result, walk, commit, &list) < 0)
+		goto cleanup;
+
+	if (!result) {
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	git_oid_cpy(out, &result->item->oid);
+
+	error = 0;
+
+cleanup:
+	git_commit_list_free(&result);
+	git_revwalk_free(walk);
+	git_vector_free(&list);
+	return error;
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+	git_revwalk *walk;
+	git_vector list;
+	git_commit_list *result = NULL;
+	git_commit_list_node *commit;
+	void *contents[1];
+
+	if (git_revwalk_new(&walk, repo) < 0)
+		return -1;
+
+	commit = git_revwalk__commit_lookup(walk, two);
+	if (commit == NULL)
+		goto on_error;
+
+	/* This is just one value, so we can do it on the stack */
+	memset(&list, 0x0, sizeof(git_vector));
+	contents[0] = commit;
+	list.length = 1;
+	list.contents = contents;
+
+	commit = git_revwalk__commit_lookup(walk, one);
+	if (commit == NULL)
+		goto on_error;
+
+	if (git_merge__bases_many(&result, walk, commit, &list) < 0)
+		goto on_error;
+
+	if (!result) {
+		git_revwalk_free(walk);
+		giterr_clear();
+		return GIT_ENOTFOUND;
+	}
+
+	git_oid_cpy(out, &result->item->oid);
+	git_commit_list_free(&result);
+	git_revwalk_free(walk);
+
+	return 0;
+
+on_error:
+	git_revwalk_free(walk);
+	return -1;
+}
+
+static int interesting(git_pqueue *list)
+{
+	unsigned int i;
+	/* element 0 isn't used - we need to start at 1 */
+	for (i = 1; i < list->size; i++) {
+		git_commit_list_node *commit = list->d[i];
+		if ((commit->flags & STALE) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+{
+	int error;
+	unsigned int i;
+	git_commit_list_node *two;
+	git_commit_list *result = NULL, *tmp = NULL;
+	git_pqueue list;
+
+	/* if the commit is repeated, we have a our merge base already */
+	git_vector_foreach(twos, i, two) {
+		if (one == two)
+			return git_commit_list_insert(one, out) ? 0 : -1;
+	}
+
+	if (git_pqueue_init(&list, twos->length * 2, git_commit_list_time_cmp) < 0)
+		return -1;
+
+	if (git_commit_list_parse(walk, one) < 0)
+	    return -1;
+
+	one->flags |= PARENT1;
+	if (git_pqueue_insert(&list, one) < 0)
+		return -1;
+
+	git_vector_foreach(twos, i, two) {
+		git_commit_list_parse(walk, two);
+		two->flags |= PARENT2;
+		if (git_pqueue_insert(&list, two) < 0)
+			return -1;
+	}
+
+	/* as long as there are non-STALE commits */
+	while (interesting(&list)) {
+		git_commit_list_node *commit;
+		int flags;
+
+		commit = git_pqueue_pop(&list);
+
+		flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+		if (flags == (PARENT1 | PARENT2)) {
+			if (!(commit->flags & RESULT)) {
+				commit->flags |= RESULT;
+				if (git_commit_list_insert(commit, &result) == NULL)
+					return -1;
+			}
+			/* we mark the parents of a merge stale */
+			flags |= STALE;
+		}
+
+		for (i = 0; i < commit->out_degree; i++) {
+			git_commit_list_node *p = commit->parents[i];
+			if ((p->flags & flags) == flags)
+				continue;
+
+			if ((error = git_commit_list_parse(walk, p)) < 0)
+				return error;
+
+			p->flags |= flags;
+			if (git_pqueue_insert(&list, p) < 0)
+				return -1;
+		}
+	}
+
+	git_pqueue_free(&list);
+
+	/* filter out any stale commits in the results */
+	tmp = result;
+	result = NULL;
+
+	while (tmp) {
+		struct git_commit_list *next = tmp->next;
+		if (!(tmp->item->flags & STALE))
+			if (git_commit_list_insert_by_date(tmp->item, &result) == NULL)
+				return -1;
+
+		git__free(tmp);
+		tmp = next;
+	}
+
+	*out = result;
+	return 0;
+}
+
+int git_repository_mergehead_foreach(git_repository *repo,
+	git_repository_mergehead_foreach_cb cb,
+	void *payload)
+{
+	git_buf merge_head_path = GIT_BUF_INIT, merge_head_file = GIT_BUF_INIT;
+	char *buffer, *line;
+	size_t line_num = 1;
+	git_oid oid;
+	int error = 0;
+
+	assert(repo && cb);
+
+	if ((error = git_buf_joinpath(&merge_head_path, repo->path_repository,
+		GIT_MERGE_HEAD_FILE)) < 0)
+		return error;
+
+	if ((error = git_futils_readbuffer(&merge_head_file,
+		git_buf_cstr(&merge_head_path))) < 0)
+		goto cleanup;
+
+	buffer = merge_head_file.ptr;
+
+	while ((line = git__strsep(&buffer, "\n")) != NULL) {
+		if (strlen(line) != GIT_OID_HEXSZ) {
+			giterr_set(GITERR_INVALID, "Unable to parse OID - invalid length");
+			error = -1;
+			goto cleanup;
+		}
+
+		if ((error = git_oid_fromstr(&oid, line)) < 0)
+			goto cleanup;
+
+		if (cb(&oid, payload) < 0) {
+			error = GIT_EUSER;
+			goto cleanup;
+		}
+
+		++line_num;
+	}
+
+	if (*buffer) {
+		giterr_set(GITERR_MERGE, "No EOL at line %d", line_num);
+		error = -1;
+		goto cleanup;
+	}
+
+cleanup:
+	git_buf_free(&merge_head_path);
+	git_buf_free(&merge_head_file);
+
+	return error;
+}
diff --git a/libgit2/src/merge.h b/libgit2/src/merge.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/merge.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_merge_h__
+#define INCLUDE_merge_h__
+
+#include "git2/types.h"
+#include "git2/merge.h"
+#include "commit_list.h"
+#include "vector.h"
+
+#define GIT_MERGE_MSG_FILE		"MERGE_MSG"
+#define GIT_MERGE_MODE_FILE		"MERGE_MODE"
+
+#define MERGE_CONFIG_FILE_MODE	0666
+
+int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos);
+
+#endif
diff --git a/libgit2/src/message.c b/libgit2/src/message.c
--- a/libgit2/src/message.c
+++ b/libgit2/src/message.c
@@ -1,12 +1,11 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
 #include "message.h"
-#include <ctype.h>
 
 static size_t line_length_without_trailing_spaces(const char *line, size_t len)
 {
@@ -22,7 +21,7 @@
 
 /* Greatly inspired from git.git "stripspace" */
 /* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
-int git_message_prettify(git_buf *message_out, const char *message, int strip_comments)
+int git_message__prettify(git_buf *message_out, const char *message, int strip_comments)
 {
 	const size_t message_len = strlen(message);
 
@@ -58,4 +57,30 @@
 	}
 
 	return git_buf_oom(message_out) ? -1 : 0;
+}
+
+int git_message_prettify(char *message_out, size_t buffer_size, const char *message, int strip_comments)
+{
+	git_buf buf = GIT_BUF_INIT;
+	ssize_t out_size = -1;
+
+	if (message_out && buffer_size)
+		*message_out = '\0';
+
+	if (git_message__prettify(&buf, message, strip_comments) < 0)
+		goto done;
+
+	if (message_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */
+		giterr_set(GITERR_INVALID, "Buffer too short to hold the cleaned message");
+		goto done;
+	}
+
+	if (message_out)
+		git_buf_copy_cstr(message_out, buffer_size, &buf);
+
+	out_size = buf.size + 1;
+
+done:
+	git_buf_free(&buf);
+	return (int)out_size;
 }
diff --git a/libgit2/src/message.h b/libgit2/src/message.h
--- a/libgit2/src/message.h
+++ b/libgit2/src/message.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,8 +7,9 @@
 #ifndef INCLUDE_message_h__
 #define INCLUDE_message_h__
 
+#include "git2/message.h"
 #include "buffer.h"
 
-int git_message_prettify(git_buf *message_out, const char *message, int strip_comments);
+int git_message__prettify(git_buf *message_out, const char *message, int strip_comments);
 
 #endif /* INCLUDE_message_h__ */
diff --git a/libgit2/src/mwindow.c b/libgit2/src/mwindow.c
--- a/libgit2/src/mwindow.c
+++ b/libgit2/src/mwindow.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -20,26 +20,26 @@
 #define DEFAULT_MAPPED_LIMIT \
 	((1024 * 1024) * (sizeof(void*) >= 8 ? 8192ULL : 256UL))
 
-/*
- * These are the global options for mmmap limits.
- * TODO: allow the user to change these
- */
-static struct {
-	size_t window_size;
-	size_t mapped_limit;
-} _mw_options = {
-	DEFAULT_WINDOW_SIZE,
-	DEFAULT_MAPPED_LIMIT,
-};
+size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
+size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
 
+/* Whenever you want to read or modify this, grab git__mwindow_mutex */
+static git_mwindow_ctl mem_ctl;
+
 /*
  * Free all the windows in a sequence, typically because we're done
  * with the file
  */
 void git_mwindow_free_all(git_mwindow_file *mwf)
 {
-	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
-	unsigned int i;
+	git_mwindow_ctl *ctl = &mem_ctl;
+	size_t i;
+
+	if (git_mutex_lock(&git__mwindow_mutex)) {
+		giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
+		return;
+	}
+
 	/*
 	 * Remove these windows from the global list
 	 */
@@ -67,6 +67,8 @@
 		mwf->windows = w->next;
 		git__free(w);
 	}
+
+	git_mutex_unlock(&git__mwindow_mutex);
 }
 
 /*
@@ -82,14 +84,13 @@
 /*
  * Find the least-recently-used window in a file
  */
-void git_mwindow_scan_lru(
+static void git_mwindow_scan_lru(
 	git_mwindow_file *mwf,
 	git_mwindow **lru_w,
 	git_mwindow **lru_l)
 {
 	git_mwindow *w, *w_l;
 
-	puts("LRU");
 	for (w_l = NULL, w = mwf->windows; w; w = w->next) {
 		if (!w->inuse_cnt) {
 			/*
@@ -108,12 +109,13 @@
 
 /*
  * Close the least recently used window. You should check to see if
- * the file descriptors need closing from time to time.
+ * the file descriptors need closing from time to time. Called under
+ * lock from new_window.
  */
 static int git_mwindow_close_lru(git_mwindow_file *mwf)
 {
-	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
-	unsigned int i;
+	git_mwindow_ctl *ctl = &mem_ctl;
+	size_t i;
 	git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
 
 	/* FIXME: Does this give us any advantage? */
@@ -147,18 +149,20 @@
 	return 0;
 }
 
+/* This gets called under lock from git_mwindow_open */
 static git_mwindow *new_window(
 	git_mwindow_file *mwf,
 	git_file fd,
 	git_off_t size,
 	git_off_t offset)
 {
-	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
-	size_t walign = _mw_options.window_size / 2;
+	git_mwindow_ctl *ctl = &mem_ctl;
+	size_t walign = git_mwindow__window_size / 2;
 	git_off_t len;
 	git_mwindow *w;
 
 	w = git__malloc(sizeof(*w));
+	
 	if (w == NULL)
 		return NULL;
 
@@ -166,16 +170,16 @@
 	w->offset = (offset / walign) * walign;
 
 	len = size - w->offset;
-	if (len > (git_off_t)_mw_options.window_size)
-		len = (git_off_t)_mw_options.window_size;
+	if (len > (git_off_t)git_mwindow__window_size)
+		len = (git_off_t)git_mwindow__window_size;
 
 	ctl->mapped += (size_t)len;
 
-	while (_mw_options.mapped_limit < ctl->mapped &&
+	while (git_mwindow__mapped_limit < ctl->mapped &&
 			git_mwindow_close_lru(mwf) == 0) /* nop */;
 
 	/*
-	 * We treat _mw_options.mapped_limit as a soft limit. If we can't find a
+	 * We treat `mapped_limit` as a soft limit. If we can't find a
 	 * window to close and are above the limit, we still mmap the new
 	 * window.
 	 */
@@ -208,9 +212,14 @@
 	size_t extra,
 	unsigned int *left)
 {
-	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
+	git_mwindow_ctl *ctl = &mem_ctl;
 	git_mwindow *w = *cursor;
 
+	if (git_mutex_lock(&git__mwindow_mutex)) {
+		giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
+		return NULL;
+	}
+
 	if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
 		if (w) {
 			w->inuse_cnt--;
@@ -228,8 +237,10 @@
 		 */
 		if (!w) {
 			w = new_window(mwf, mwf->fd, mwf->size, offset);
-			if (w == NULL)
+			if (w == NULL) {
+				git_mutex_unlock(&git__mwindow_mutex);
 				return NULL;
+			}
 			w->next = mwf->windows;
 			mwf->windows = w;
 		}
@@ -247,26 +258,62 @@
 	if (left)
 		*left = (unsigned int)(w->window_map.len - offset);
 
-	fflush(stdout);
+	git_mutex_unlock(&git__mwindow_mutex);
 	return (unsigned char *) w->window_map.data + offset;
 }
 
 int git_mwindow_file_register(git_mwindow_file *mwf)
 {
-	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
+	git_mwindow_ctl *ctl = &mem_ctl;
+	int ret;
 
+	if (git_mutex_lock(&git__mwindow_mutex)) {
+		giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
+		return -1;
+	}
+
 	if (ctl->windowfiles.length == 0 &&
-		git_vector_init(&ctl->windowfiles, 8, NULL) < 0)
+	    git_vector_init(&ctl->windowfiles, 8, NULL) < 0) {
+		git_mutex_unlock(&git__mwindow_mutex);
 		return -1;
+	}
 
-	return git_vector_insert(&ctl->windowfiles, mwf);
+	ret = git_vector_insert(&ctl->windowfiles, mwf);
+	git_mutex_unlock(&git__mwindow_mutex);
+
+	return ret;
 }
 
+void git_mwindow_file_deregister(git_mwindow_file *mwf)
+{
+	git_mwindow_ctl *ctl = &mem_ctl;
+	git_mwindow_file *cur;
+	size_t i;
+
+	if (git_mutex_lock(&git__mwindow_mutex))
+		return;
+
+	git_vector_foreach(&ctl->windowfiles, i, cur) {
+		if (cur == mwf) {
+			git_vector_remove(&ctl->windowfiles, i);
+			git_mutex_unlock(&git__mwindow_mutex);
+			return;
+		}
+	}
+	git_mutex_unlock(&git__mwindow_mutex);
+}
+
 void git_mwindow_close(git_mwindow **window)
 {
 	git_mwindow *w = *window;
 	if (w) {
+		if (git_mutex_lock(&git__mwindow_mutex)) {
+			giterr_set(GITERR_THREAD, "unable to lock mwindow mutex");
+			return;
+		}
+
 		w->inuse_cnt--;
+		git_mutex_unlock(&git__mwindow_mutex);
 		*window = NULL;
 	}
 }
diff --git a/libgit2/src/mwindow.h b/libgit2/src/mwindow.h
--- a/libgit2/src/mwindow.h
+++ b/libgit2/src/mwindow.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -38,8 +38,8 @@
 int git_mwindow_contains(git_mwindow *win, git_off_t offset);
 void git_mwindow_free_all(git_mwindow_file *mwf);
 unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left);
-void git_mwindow_scan_lru(git_mwindow_file *mwf, git_mwindow **lru_w, git_mwindow **lru_l);
 int git_mwindow_file_register(git_mwindow_file *mwf);
+void git_mwindow_file_deregister(git_mwindow_file *mwf);
 void git_mwindow_close(git_mwindow **w_cursor);
 
 #endif
diff --git a/libgit2/src/netops.c b/libgit2/src/netops.c
--- a/libgit2/src/netops.c
+++ b/libgit2/src/netops.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,15 +10,26 @@
 #	include <sys/select.h>
 #	include <sys/time.h>
 #	include <netdb.h>
+#	include <netinet/in.h>
+#       include <arpa/inet.h>
 #else
-#	include <winsock2.h>
-#	include <Ws2tcpip.h>
+#	include <ws2tcpip.h>
 #	ifdef _MSC_VER
-#		pragma comment(lib, "Ws2_32.lib")
+#		pragma comment(lib, "ws2_32")
 #	endif
 #endif
 
+#ifdef __FreeBSD__
+#	include <netinet/in.h>
+#endif
 
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+#endif
+
+#include <ctype.h>
 #include "git2/errors.h"
 
 #include "common.h"
@@ -29,14 +40,15 @@
 #ifdef GIT_WIN32
 static void net_set_error(const char *str)
 {
-	int size, error = WSAGetLastError();
-	LPSTR err_str = NULL;
-
-	size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
-			     0, error, 0, (LPSTR)&err_str, 0, 0);
+	int error = WSAGetLastError();
+	char * win32_error = git_win32_get_error_message(error);
 
-	giterr_set(GITERR_NET, "%s: %s", str, err_str);
-	LocalFree(err_str);
+	if (win32_error) {
+		giterr_set(GITERR_NET, "%s: %s", str, win32_error);
+		git__free(win32_error);
+	} else {
+		giterr_set(GITERR_NET, str);
+	}
 }
 #else
 static void net_set_error(const char *str)
@@ -45,21 +57,66 @@
 }
 #endif
 
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
+#ifdef GIT_SSL
+static int ssl_set_error(gitno_ssl *ssl, int error)
 {
-	memset(buf, 0x0, sizeof(gitno_buffer));
-	memset(data, 0x0, len);
-	buf->data = data;
-	buf->len = len;
-	buf->offset = 0;
-	buf->fd = fd;
+	int err;
+	unsigned long e;
+
+	err = SSL_get_error(ssl->ssl, error);
+
+	assert(err != SSL_ERROR_WANT_READ);
+	assert(err != SSL_ERROR_WANT_WRITE);
+
+	switch (err) {
+	case SSL_ERROR_WANT_CONNECT:
+	case SSL_ERROR_WANT_ACCEPT:
+		giterr_set(GITERR_NET, "SSL error: connection failure\n");
+		break;
+	case SSL_ERROR_WANT_X509_LOOKUP:
+		giterr_set(GITERR_NET, "SSL error: x509 error\n");
+		break;
+	case SSL_ERROR_SYSCALL:
+		e = ERR_get_error();
+		if (e > 0) {
+			giterr_set(GITERR_NET, "SSL error: %s",
+					ERR_error_string(e, NULL));
+			break;
+		} else if (error < 0) {
+			giterr_set(GITERR_OS, "SSL error: syscall failure");
+			break;
+		}
+		giterr_set(GITERR_NET, "SSL error: received early EOF");
+		break;
+	case SSL_ERROR_SSL:
+		e = ERR_get_error();
+		giterr_set(GITERR_NET, "SSL error: %s",
+				ERR_error_string(e, NULL));
+		break;
+	case SSL_ERROR_NONE:
+	case SSL_ERROR_ZERO_RETURN:
+	default:
+		giterr_set(GITERR_NET, "SSL error: unknown error");
+		break;
+	}
+	return -1;
 }
+#endif
 
 int gitno_recv(gitno_buffer *buf)
 {
+	return buf->recv(buf);
+}
+
+#ifdef GIT_SSL
+static int gitno__recv_ssl(gitno_buffer *buf)
+{
 	int ret;
 
-	ret = p_recv(buf->fd, buf->data + buf->offset, buf->len - buf->offset, 0);
+	do {
+		ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset);
+	} while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ);
+
 	if (ret < 0) {
 		net_set_error("Error receiving socket data");
 		return -1;
@@ -68,7 +125,50 @@
 	buf->offset += ret;
 	return ret;
 }
+#endif
 
+static int gitno__recv(gitno_buffer *buf)
+{
+	int ret;
+
+	ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0);
+	if (ret < 0) {
+		net_set_error("Error receiving socket data");
+		return -1;
+	}
+
+	buf->offset += ret;
+	return ret;
+}
+
+void gitno_buffer_setup_callback(
+	gitno_socket *socket,
+	gitno_buffer *buf,
+	char *data,
+	size_t len,
+	int (*recv)(gitno_buffer *buf), void *cb_data)
+{
+	memset(data, 0x0, len);
+	buf->data = data;
+	buf->len = len;
+	buf->offset = 0;
+	buf->socket = socket;
+	buf->recv = recv;
+	buf->cb_data = cb_data;
+}
+
+void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len)
+{
+#ifdef GIT_SSL
+	if (socket->ssl.ctx) {
+		gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL);
+		return;
+	}
+#endif
+
+	gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL);
+}
+
 /* Consume up to ptr and move the rest of the buffer to the beginning */
 void gitno_consume(gitno_buffer *buf, const char *ptr)
 {
@@ -92,24 +192,284 @@
 	buf->offset -= cons;
 }
 
-int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
+#ifdef GIT_SSL
+
+static int gitno_ssl_teardown(gitno_ssl *ssl)
 {
+	int ret;
+
+	ret = SSL_shutdown(ssl->ssl);
+	if (ret < 0)
+		ret = ssl_set_error(ssl, ret);
+	else
+		ret = 0;
+
+	SSL_free(ssl->ssl);
+	SSL_CTX_free(ssl->ctx);
+	return ret;
+}
+
+/* Match host names according to RFC 2818 rules */
+static int match_host(const char *pattern, const char *host)
+{
+	for (;;) {
+		char c = tolower(*pattern++);
+
+		if (c == '\0')
+			return *host ? -1 : 0;
+
+		if (c == '*') {
+			c = *pattern;
+			/* '*' at the end matches everything left */
+			if (c == '\0')
+				return 0;
+
+	/*
+	 * We've found a pattern, so move towards the next matching
+	 * char. The '.' is handled specially because wildcards aren't
+	 * allowed to cross subdomains.
+	 */
+
+			while(*host) {
+				char h = tolower(*host);
+				if (c == h)
+					return match_host(pattern, host++);
+				if (h == '.')
+					return match_host(pattern, host);
+				host++;
+			}
+			return -1;
+		}
+
+		if (c != tolower(*host++))
+			return -1;
+	}
+
+	return -1;
+}
+
+static int check_host_name(const char *name, const char *host)
+{
+	if (!strcasecmp(name, host))
+		return 0;
+
+	if (match_host(name, host) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int verify_server_cert(gitno_ssl *ssl, const char *host)
+{
+	X509 *cert;
+	X509_NAME *peer_name;
+	ASN1_STRING *str;
+	unsigned char *peer_cn = NULL;
+	int matched = -1, type = GEN_DNS;
+	GENERAL_NAMES *alts;
+	struct in6_addr addr6;
+	struct in_addr addr4;
+	void *addr;
+	int i = -1,j;
+
+	if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) {
+		giterr_set(GITERR_SSL, "The SSL certificate is invalid");
+		return -1;
+	}
+
+	/* Try to parse the host as an IP address to see if it is */
+	if (p_inet_pton(AF_INET, host, &addr4)) {
+		type = GEN_IPADD;
+		addr = &addr4;
+	} else {
+		if(p_inet_pton(AF_INET6, host, &addr6)) {
+			type = GEN_IPADD;
+			addr = &addr6;
+		}
+	}
+
+
+	cert = SSL_get_peer_certificate(ssl->ssl);
+
+	/* Check the alternative names */
+	alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+	if (alts) {
+		int num;
+
+		num = sk_GENERAL_NAME_num(alts);
+		for (i = 0; i < num && matched != 1; i++) {
+			const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
+			const char *name = (char *) ASN1_STRING_data(gn->d.ia5);
+			size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
+
+			/* Skip any names of a type we're not looking for */
+			if (gn->type != type)
+				continue;
+
+			if (type == GEN_DNS) {
+				/* If it contains embedded NULs, don't even try */
+				if (memchr(name, '\0', namelen))
+					continue;
+
+				if (check_host_name(name, host) < 0)
+					matched = 0;
+				else
+					matched = 1;
+			} else if (type == GEN_IPADD) {
+				/* Here name isn't so much a name but a binary representation of the IP */
+				matched = !!memcmp(name, addr, namelen);
+			}
+		}
+	}
+	GENERAL_NAMES_free(alts);
+
+	if (matched == 0)
+		goto cert_fail;
+
+	if (matched == 1)
+		return 0;
+
+	/* If no alternative names are available, check the common name */
+	peer_name = X509_get_subject_name(cert);
+	if (peer_name == NULL)
+		goto on_error;
+
+	if (peer_name) {
+		/* Get the index of the last CN entry */
+		while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
+			i = j;
+	}
+
+	if (i < 0)
+		goto on_error;
+
+	str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
+	if (str == NULL)
+		goto on_error;
+
+	/* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
+	if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
+		int size = ASN1_STRING_length(str);
+
+		if (size > 0) {
+			peer_cn = OPENSSL_malloc(size + 1);
+			GITERR_CHECK_ALLOC(peer_cn);
+			memcpy(peer_cn, ASN1_STRING_data(str), size);
+			peer_cn[size] = '\0';
+		}
+	} else {
+		int size = ASN1_STRING_to_UTF8(&peer_cn, str);
+		GITERR_CHECK_ALLOC(peer_cn);
+		if (memchr(peer_cn, '\0', size))
+			goto cert_fail;
+	}
+
+	if (check_host_name((char *)peer_cn, host) < 0)
+		goto cert_fail;
+
+	OPENSSL_free(peer_cn);
+
+	return 0;
+
+on_error:
+	OPENSSL_free(peer_cn);
+	return ssl_set_error(ssl, 0);
+
+cert_fail:
+	OPENSSL_free(peer_cn);
+	giterr_set(GITERR_SSL, "Certificate host name check failed");
+	return -1;
+}
+
+static int ssl_setup(gitno_socket *socket, const char *host, int flags)
+{
+	int ret;
+
+	SSL_library_init();
+	SSL_load_error_strings();
+	socket->ssl.ctx = SSL_CTX_new(SSLv23_method());
+	if (socket->ssl.ctx == NULL)
+		return ssl_set_error(&socket->ssl, 0);
+
+	SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY);
+	SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL);
+	if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx))
+		return ssl_set_error(&socket->ssl, 0);
+
+	socket->ssl.ssl = SSL_new(socket->ssl.ctx);
+	if (socket->ssl.ssl == NULL)
+		return ssl_set_error(&socket->ssl, 0);
+
+	if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0)
+		return ssl_set_error(&socket->ssl, ret);
+
+	if ((ret = SSL_connect(socket->ssl.ssl)) <= 0)
+		return ssl_set_error(&socket->ssl, ret);
+
+	if (GITNO_CONNECT_SSL_NO_CHECK_CERT & flags)
+		return 0;
+
+	return verify_server_cert(&socket->ssl, host);
+}
+#endif
+
+static int gitno__close(GIT_SOCKET s)
+{
+#ifdef GIT_WIN32
+	if (SOCKET_ERROR == closesocket(s))
+		return -1;
+
+	if (0 != WSACleanup()) {
+		giterr_set(GITERR_OS, "Winsock cleanup failed");
+		return -1;
+	}
+
+	return 0;
+#else
+	return close(s);
+#endif
+}
+
+int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags)
+{
 	struct addrinfo *info = NULL, *p;
 	struct addrinfo hints;
-	int ret;
 	GIT_SOCKET s = INVALID_SOCKET;
+	int ret;
 
+#ifdef GIT_WIN32
+	/* on win32, the WSA context needs to be initialized
+	 * before any socket calls can be performed */
+	WSADATA wsd;
+
+	if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) {
+		giterr_set(GITERR_OS, "Winsock init failed");
+		return -1;
+	}
+
+	if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) {
+		WSACleanup();
+		giterr_set(GITERR_OS, "Winsock init failed");
+		return -1;
+	}
+#endif
+
+	/* Zero the socket structure provided */
+	memset(s_out, 0x0, sizeof(gitno_socket));
+
 	memset(&hints, 0x0, sizeof(struct addrinfo));
-	hints.ai_family = AF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_family = AF_UNSPEC;
 
-	if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {
-		giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, gai_strerror(ret));
+	if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) {
+		giterr_set(GITERR_NET,
+			"Failed to resolve address for %s: %s", host, p_gai_strerror(ret));
 		return -1;
 	}
 
 	for (p = info; p != NULL; p = p->ai_next) {
 		s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+
 		if (s == INVALID_SOCKET) {
 			net_set_error("error creating socket");
 			break;
@@ -119,30 +479,67 @@
 			break;
 
 		/* If we can't connect, try the next one */
-		gitno_close(s);
+		gitno__close(s);
 		s = INVALID_SOCKET;
 	}
 
 	/* Oops, we couldn't connect to any address */
 	if (s == INVALID_SOCKET && p == NULL) {
 		giterr_set(GITERR_OS, "Failed to connect to %s", host);
+		p_freeaddrinfo(info);
 		return -1;
 	}
 
-	freeaddrinfo(info);
-	*sock = s;
+	s_out->socket = s;
+	p_freeaddrinfo(info);
+
+#ifdef GIT_SSL
+	if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0)
+		return -1;
+#else
+	/* SSL is not supported */
+	if (flags & GITNO_CONNECT_SSL) {
+		giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2.");
+		return -1;
+	}
+#endif
+
 	return 0;
 }
 
-int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
+#ifdef GIT_SSL
+static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags)
 {
 	int ret;
 	size_t off = 0;
 
+	GIT_UNUSED(flags);
+
 	while (off < len) {
-		errno = 0;
+		ret = SSL_write(ssl->ssl, msg + off, len - off);
+		if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE)
+			return ssl_set_error(ssl, ret);
 
-		ret = p_send(s, msg + off, len - off, flags);
+		off += ret;
+	}	
+
+	return off;
+}
+#endif
+
+int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags)
+{
+	int ret;
+	size_t off = 0;
+
+#ifdef GIT_SSL
+	if (socket->ssl.ctx)
+		return gitno_send_ssl(&socket->ssl, msg, len, flags);
+#endif
+
+	while (off < len) {
+		errno = 0;
+		ret = p_send(socket->socket, msg + off, len - off, flags);
 		if (ret < 0) {
 			net_set_error("Error sending data");
 			return -1;
@@ -154,19 +551,17 @@
 	return (int)off;
 }
 
-
-#ifdef GIT_WIN32
-int gitno_close(GIT_SOCKET s)
-{
-	return closesocket(s) == SOCKET_ERROR ? -1 : 0;
-}
-#else
-int gitno_close(GIT_SOCKET s)
+int gitno_close(gitno_socket *s)
 {
-	return close(s);
-}
+#ifdef GIT_SSL
+	if (s->ssl.ctx &&
+		gitno_ssl_teardown(&s->ssl) < 0)
+		return -1;
 #endif
 
+	return gitno__close(s->socket);
+}
+
 int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
 {
 	fd_set fds;
@@ -176,33 +571,60 @@
 	tv.tv_usec = usec;
 
 	FD_ZERO(&fds);
-	FD_SET(buf->fd, &fds);
+	FD_SET(buf->socket->socket, &fds);
 
 	/* The select(2) interface is silly */
-	return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);
+	return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv);
 }
 
-int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
+int gitno_extract_url_parts(
+		char **host,
+		char **port,
+		char **username,
+		char **password,
+		const char *url,
+		const char *default_port)
 {
-	char *colon, *slash, *delim;
+	char *colon, *slash, *at, *end;
+	const char *start;
 
+	/*
+	 *
+	 * ==> [user[:pass]@]hostname.tld[:port]/resource
+	 */
+
 	colon = strchr(url, ':');
 	slash = strchr(url, '/');
+	at = strchr(url, '@');
 
 	if (slash == NULL) {
 		giterr_set(GITERR_NET, "Malformed URL: missing /");
 		return -1;
 	}
 
+	start = url;
+	if (at && at < slash) {
+		start = at+1;
+		*username = git__substrdup(url, at - url);
+	}
+
+	if (colon && colon < at) {
+		git__free(*username);
+		*username = git__substrdup(url, colon-url);
+		*password = git__substrdup(colon+1, at-colon-1);
+		colon = strchr(at, ':');
+	}
+
 	if (colon == NULL) {
 		*port = git__strdup(default_port);
 	} else {
-		*port = git__strndup(colon + 1, slash - colon - 1);
+		*port = git__substrdup(colon + 1, slash - colon - 1);
 	}
 	GITERR_CHECK_ALLOC(*port);
 
-	delim = colon == NULL ? slash : colon;
-	*host = git__strndup(url, delim - url);
+	end = colon == NULL ? slash : colon;
+
+	*host = git__substrdup(start, end - start);
 	GITERR_CHECK_ALLOC(*host);
 
 	return 0;
diff --git a/libgit2/src/netops.h b/libgit2/src/netops.h
--- a/libgit2/src/netops.h
+++ b/libgit2/src/netops.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,25 +8,70 @@
 #define INCLUDE_netops_h__
 
 #include "posix.h"
+#include "common.h"
 
-typedef struct gitno_buffer {
+#ifdef GIT_SSL
+# include <openssl/ssl.h>
+#endif
+
+struct gitno_ssl {
+#ifdef GIT_SSL
+	SSL_CTX *ctx;
+	SSL *ssl;
+#else
+	size_t dummy;
+#endif
+};
+
+typedef struct gitno_ssl gitno_ssl;
+
+/* Represents a socket that may or may not be using SSL */
+struct gitno_socket {
+	GIT_SOCKET socket;
+	gitno_ssl ssl;
+};
+
+typedef struct gitno_socket gitno_socket;
+
+struct gitno_buffer {
 	char *data;
 	size_t len;
 	size_t offset;
-	GIT_SOCKET fd;
-} gitno_buffer;
+	gitno_socket *socket;
+	int (*recv)(struct gitno_buffer *buffer);
+	void *cb_data;
+};
 
-void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
+typedef struct gitno_buffer gitno_buffer;
+
+/* Flags to gitno_connect */
+enum {
+	/* Attempt to create an SSL connection. */
+	GITNO_CONNECT_SSL = 1,
+
+	/* Valid only when GITNO_CONNECT_SSL is also specified.
+	 * Indicates that the server certificate should not be validated. */
+	GITNO_CONNECT_SSL_NO_CHECK_CERT = 2,
+};
+
+void gitno_buffer_setup(gitno_socket *t, gitno_buffer *buf, char *data, size_t len);
+void gitno_buffer_setup_callback(gitno_socket *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data);
 int gitno_recv(gitno_buffer *buf);
+
 void gitno_consume(gitno_buffer *buf, const char *ptr);
 void gitno_consume_n(gitno_buffer *buf, size_t cons);
 
-int gitno_connect(GIT_SOCKET *s, const char *host, const char *port);
-int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags);
-int gitno_close(GIT_SOCKET s);
-int gitno_send_chunk_size(int s, size_t len);
+int gitno_connect(gitno_socket *socket, const char *host, const char *port, int flags);
+int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags);
+int gitno_close(gitno_socket *s);
 int gitno_select_in(gitno_buffer *buf, long int sec, long int usec);
 
-int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port);
+int gitno_extract_url_parts(
+		char **host,
+		char **port,
+		char **username,
+		char **password,
+		const char *url,
+		const char *default_port);
 
 #endif
diff --git a/libgit2/src/notes.c b/libgit2/src/notes.c
--- a/libgit2/src/notes.c
+++ b/libgit2/src/notes.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,56 +11,68 @@
 #include "refs.h"
 #include "config.h"
 #include "iterator.h"
+#include "signature.h"
 
-static int find_subtree(git_tree **subtree, const git_oid *root,
-			git_repository *repo, const char *target, int *fanout)
+static int find_subtree_in_current_level(
+	git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	const char *annotated_object_sha,
+	int fanout)
 {
-	int error;
-	unsigned int i;
-	git_tree *tree;
+	size_t i;
 	const git_tree_entry *entry;
 
-	*subtree = NULL;
+	*out = NULL;
 
-	error = git_tree_lookup(&tree, repo, root);
-	if (error < 0)
-		return error;
+	if (parent == NULL)
+		return GIT_ENOTFOUND;
 
-	for (i=0; i<git_tree_entrycount(tree); i++) {
-		entry = git_tree_entry_byindex(tree, i);
+	for (i = 0; i < git_tree_entrycount(parent); i++) {
+		entry = git_tree_entry_byindex(parent, i);
 
 		if (!git__ishex(git_tree_entry_name(entry)))
 			continue;
 
-		/*
-		 * A notes tree follows a strict byte-based progressive fanout
-		 * (i.e. using 2/38, 2/2/36, etc. fanouts, not e.g. 4/36 fanout)
-		 */
-
-		if (S_ISDIR(git_tree_entry_attributes(entry))
+		if (S_ISDIR(git_tree_entry_filemode(entry))
 			&& strlen(git_tree_entry_name(entry)) == 2
-			&& !strncmp(git_tree_entry_name(entry), target + *fanout, 2)) {
+			&& !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2))
+			return git_tree_lookup(out, repo, git_tree_entry_id(entry));
 
-			/* found matching subtree - unpack and resume lookup */
+		/* Not a DIR, so do we have an already existing blob? */
+		if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout))
+			return GIT_EEXISTS;
+	}
 
-			git_oid subtree_sha;
-			git_oid_cpy(&subtree_sha, git_tree_entry_id(entry));
-			git_tree_free(tree);
+	return GIT_ENOTFOUND;
+}
 
-			*fanout += 2;
+static int find_subtree_r(git_tree **out, git_tree *root,
+			git_repository *repo, const char *target, int *fanout)
+{
+	int error;
+	git_tree *subtree = NULL;
 
-			return find_subtree(subtree, &subtree_sha, repo,
-					    target, fanout);
-		}
+	*out = NULL;
+
+	error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout);
+	if (error == GIT_EEXISTS) {
+		return git_tree_lookup(out, repo, git_tree_id(root));
 	}
 
-	*subtree = tree;
-	return 0;
+	if (error < 0)
+		return error;
+
+	*fanout += 2;
+	error = find_subtree_r(out, subtree, repo, target, fanout);
+	git_tree_free(subtree);
+
+	return error;
 }
 
 static int find_blob(git_oid *blob, git_tree *tree, const char *target)
 {
-	unsigned int i;
+	size_t i;
 	const git_tree_entry *entry;
 
 	for (i=0; i<git_tree_entrycount(tree); i++) {
@@ -76,191 +88,285 @@
 	return GIT_ENOTFOUND;
 }
 
-static int note_write(git_oid *out, git_repository *repo,
-		      git_signature *author, git_signature *committer,
-		      const char *notes_ref, const char *note,
-		      const git_oid *tree_sha, const char *target,
-		      int nparents, git_commit **parents)
+static int tree_write(
+	git_tree **out,
+	git_repository *repo,
+	git_tree *source_tree,
+	const git_oid *object_oid,
+	const char *treeentry_name,
+	unsigned int attributes)
 {
-	int error, fanout = 0;
-	git_oid oid;
-	git_tree *tree = NULL;
-	git_tree_entry *entry;
-	git_treebuilder *tb;
-
-	/* check for existing notes tree */
+	int error;
+	git_treebuilder *tb = NULL;
+	const git_tree_entry *entry;
+	git_oid tree_oid;
 
-	if (tree_sha) {
-		error = find_subtree(&tree, tree_sha, repo, target, &fanout);
-		if (error < 0)
-			return error;
+	if ((error = git_treebuilder_create(&tb, source_tree)) < 0)
+		goto cleanup;
 
-		error = find_blob(&oid, tree, target + fanout);
-		if (error != GIT_ENOTFOUND) {
-			git_tree_free(tree);
-			if (!error) {
-				giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", target);
-				error = GIT_EEXISTS;
-			}
-			return error;
-		}
+	if (object_oid) {
+		if ((error = git_treebuilder_insert(
+				&entry, tb, treeentry_name, object_oid, attributes)) < 0)
+			goto cleanup;
+	} else {
+		if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0)
+			goto cleanup;
 	}
 
-	/* no matching tree entry - add note object to target tree */
+	if ((error = git_treebuilder_write(&tree_oid, repo, tb)) < 0)
+		goto cleanup;
 
-	error = git_treebuilder_create(&tb, tree);
-	git_tree_free(tree);
+	error = git_tree_lookup(out, repo, &tree_oid);
 
-	if (error < 0)
-		return error;
+cleanup:
+	git_treebuilder_free(tb);
+	return error;
+}
 
-	if (!tree_sha)
-		/* no notes tree yet - create fanout */
-		fanout += 2;
+static int manipulate_note_in_tree_r(
+	git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	git_oid *note_oid,
+	const char *annotated_object_sha,
+	int fanout,
+	int (*note_exists_cb)(
+		git_tree **out,
+		git_repository *repo,
+		git_tree *parent,
+		git_oid *note_oid,
+		const char *annotated_object_sha,
+		int fanout,
+		int current_error),
+	int (*note_notfound_cb)(
+		git_tree **out,
+		git_repository *repo,
+		git_tree *parent,
+		git_oid *note_oid,
+		const char *annotated_object_sha,
+		int fanout,
+		int current_error))
+{
+	int error;
+	git_tree *subtree = NULL, *new = NULL;
+	char subtree_name[3];
 
-	/* create note object */
-	error = git_blob_create_frombuffer(&oid, repo, note, strlen(note));
-	if (error < 0) {
-		git_treebuilder_free(tb);
-		return error;
-	}
+	error = find_subtree_in_current_level(
+		&subtree, repo, parent, annotated_object_sha, fanout);
 
-	error = git_treebuilder_insert(&entry, tb, target + fanout, &oid, 0100644);
-	if (error < 0) {
-		/* libgit2 doesn't support object removal (gc) yet */
-		/* we leave an orphaned blob object behind - TODO */
+	if (error == GIT_EEXISTS) {
+		error = note_exists_cb(
+			out, repo, parent, note_oid, annotated_object_sha, fanout, error);
+		goto cleanup;
+	}
 
-		git_treebuilder_free(tb);
-		return error;
+	if (error == GIT_ENOTFOUND) {
+		error = note_notfound_cb(
+			out, repo, parent, note_oid, annotated_object_sha, fanout, error);
+		goto cleanup;
 	}
 
-	if (out)
-		git_oid_cpy(out, git_tree_entry_id(entry));
+	if (error < 0)
+		goto cleanup;
 
-	error = git_treebuilder_write(&oid, repo, tb);
-	git_treebuilder_free(tb);
+	/* An existing fanout has been found, let's dig deeper */
+	error = manipulate_note_in_tree_r(
+		&new, repo, subtree, note_oid, annotated_object_sha,
+		fanout + 2, note_exists_cb, note_notfound_cb);
 
 	if (error < 0)
-		return 0;
+		goto cleanup;
 
-	if (!tree_sha) {
-		/* create fanout subtree */
+	strncpy(subtree_name, annotated_object_sha + fanout, 2);
+	subtree_name[2] = '\0';
 
-		char subtree[3];
-		strncpy(subtree, target, 2);
-		subtree[2] = '\0';
+	error = tree_write(out, repo, parent, git_tree_id(new),
+			   subtree_name, GIT_FILEMODE_TREE);
 
-		error = git_treebuilder_create(&tb, NULL);
-		if (error < 0)
-			return error;
 
-		error = git_treebuilder_insert(NULL, tb, subtree, &oid, 0040000);
-		if (error < 0) {
-			git_treebuilder_free(tb);
-			return error;
-		}
+cleanup:
+	git_tree_free(new);
+	git_tree_free(subtree);
+	return error;
+}
 
-		error = git_treebuilder_write(&oid, repo, tb);
+static int remove_note_in_tree_eexists_cb(
+	git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	git_oid *note_oid,
+	const char *annotated_object_sha,
+	int fanout,
+	int current_error)
+{
+	GIT_UNUSED(note_oid);
+	GIT_UNUSED(current_error);
 
-		git_treebuilder_free(tb);
+	return tree_write(out, repo, parent, NULL, annotated_object_sha + fanout, 0);
+}
 
-		if (error < 0)
-			return error;
-	}
+static int remove_note_in_tree_enotfound_cb(
+	git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	git_oid *note_oid,
+	const char *annotated_object_sha,
+	int fanout,
+	int current_error)
+{
+	GIT_UNUSED(out);
+	GIT_UNUSED(repo);
+	GIT_UNUSED(parent);
+	GIT_UNUSED(note_oid);
+	GIT_UNUSED(fanout);
 
-	/* create new notes commit */
+	giterr_set(GITERR_REPOSITORY, "Object '%s' has no note", annotated_object_sha);
+	return current_error;
+}
 
-	error = git_tree_lookup(&tree, repo, &oid);
-	if (error < 0)
-		return error;
+static int insert_note_in_tree_eexists_cb(git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	git_oid *note_oid,
+	const char *annotated_object_sha,
+	int fanout,
+	int current_error)
+{
+	GIT_UNUSED(out);
+	GIT_UNUSED(repo);
+	GIT_UNUSED(parent);
+	GIT_UNUSED(note_oid);
+	GIT_UNUSED(fanout);
 
-	error = git_commit_create(&oid, repo, notes_ref, author, committer,
-				  NULL, GIT_NOTES_DEFAULT_MSG_ADD,
-				  tree, nparents, (const git_commit **) parents);
+	giterr_set(GITERR_REPOSITORY, "Note for '%s' exists already", annotated_object_sha);
+	return current_error;
+}
 
-	git_tree_free(tree);
+static int insert_note_in_tree_enotfound_cb(git_tree **out,
+	git_repository *repo,
+	git_tree *parent,
+	git_oid *note_oid,
+	const char *annotated_object_sha,
+	int fanout,
+	int current_error)
+{
+	GIT_UNUSED(current_error);
 
-	return error;
+	/* No existing fanout at this level, insert in place */
+	return tree_write(
+		out,
+		repo,
+		parent,
+		note_oid,
+		annotated_object_sha + fanout,
+		GIT_FILEMODE_BLOB);
 }
 
-static int note_lookup(git_note **out, git_repository *repo,
-		       const git_oid *tree_sha, const char *target)
+static int note_write(git_oid *out,
+	git_repository *repo,
+	const git_signature *author,
+	const git_signature *committer,
+	const char *notes_ref,
+	const char *note,
+	git_tree *commit_tree,
+	const char *target,
+	git_commit **parents,
+	int allow_note_overwrite)
 {
-	int error, fanout = 0;
+	int error;
 	git_oid oid;
-	git_blob *blob;
-	git_tree *tree;
-	git_note *note;
+	git_tree *tree = NULL;
 
-	error = find_subtree(&tree, tree_sha, repo, target, &fanout);
-	if (error < 0)
-		return error;
+	// TODO: should we apply filters?
+	/* create note object */
+	if ((error = git_blob_create_frombuffer(&oid, repo, note, strlen(note))) < 0)
+		goto cleanup;
 
-	error = find_blob(&oid, tree, target + fanout);
+	if ((error = manipulate_note_in_tree_r(
+		&tree, repo, commit_tree, &oid, target, 0,
+		allow_note_overwrite ? insert_note_in_tree_enotfound_cb : insert_note_in_tree_eexists_cb,
+		insert_note_in_tree_enotfound_cb)) < 0)
+		goto cleanup;
 
+	if (out)
+		git_oid_cpy(out, &oid);
+
+	error = git_commit_create(&oid, repo, notes_ref, author, committer,
+				  NULL, GIT_NOTES_DEFAULT_MSG_ADD,
+				  tree, *parents == NULL ? 0 : 1, (const git_commit **) parents);
+
+cleanup:
 	git_tree_free(tree);
-	if (error < 0)
-		return error;
+	return error;
+}
 
-	error = git_blob_lookup(&blob, repo, &oid);
-	if (error < 0)
-		return error;
+static int note_new(git_note **out, git_oid *note_oid, git_blob *blob)
+{
+	git_note *note = NULL;
 
-	note = git__malloc(sizeof(git_note));
+	note = (git_note *)git__malloc(sizeof(git_note));
 	GITERR_CHECK_ALLOC(note);
 
-	git_oid_cpy(&note->oid, &oid);
-	note->message = git__strdup(git_blob_rawcontent(blob));
+	git_oid_cpy(&note->oid, note_oid);
+	note->message = git__strdup((char *)git_blob_rawcontent(blob));
 	GITERR_CHECK_ALLOC(note->message);
 
 	*out = note;
 
-	git_blob_free(blob);
-	return error;
+	return 0;
 }
 
-static int note_remove(git_repository *repo,
-		       git_signature *author, git_signature *committer,
-		       const char *notes_ref, const git_oid *tree_sha,
-		       const char *target, int nparents, git_commit **parents)
+static int note_lookup(git_note **out, git_repository *repo,
+		       git_tree *tree, const char *target)
 {
 	int error, fanout = 0;
 	git_oid oid;
-	git_tree *tree;
-	git_treebuilder *tb;
+	git_blob *blob = NULL;
+	git_note *note = NULL;
+	git_tree *subtree = NULL;
 
-	error = find_subtree(&tree, tree_sha, repo, target, &fanout);
-	if (error < 0)
-		return error;
+	if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0)
+		goto cleanup;
 
-	error = find_blob(&oid, tree, target + fanout);
-	if (!error)
-		error = git_treebuilder_create(&tb, tree);
+	if ((error = find_blob(&oid, subtree, target + fanout)) < 0)
+		goto cleanup;
 
-	git_tree_free(tree);
-	if (error < 0)
-		return error;
+	if ((error = git_blob_lookup(&blob, repo, &oid)) < 0)
+		goto cleanup;
 
-	error = git_treebuilder_remove(tb, target + fanout);
-	if (!error)
-		error = git_treebuilder_write(&oid, repo, tb);
+	if ((error = note_new(&note, &oid, blob)) < 0)
+		goto cleanup;
 
-	git_treebuilder_free(tb);
-	if (error < 0)
-		return error;
+	*out = note;
 
-	/* create new notes commit */
+cleanup:
+	git_tree_free(subtree);
+	git_blob_free(blob);
+	return error;
+}
 
-	error = git_tree_lookup(&tree, repo, &oid);
-	if (error < 0)
-		return error;
+static int note_remove(git_repository *repo,
+		const git_signature *author, const git_signature *committer,
+		const char *notes_ref, git_tree *tree,
+		const char *target, git_commit **parents)
+{
+	int error;
+	git_tree *tree_after_removal = NULL;
+	git_oid oid;
 
-	error = git_commit_create(&oid, repo, notes_ref, author, committer,
-				  NULL, GIT_NOTES_DEFAULT_MSG_RM,
-				  tree, nparents, (const git_commit **) parents);
+	if ((error = manipulate_note_in_tree_r(
+		&tree_after_removal, repo, tree, NULL, target, 0,
+		remove_note_in_tree_eexists_cb, remove_note_in_tree_enotfound_cb)) < 0)
+		goto cleanup;
 
-	git_tree_free(tree);
+	error = git_commit_create(&oid, repo, notes_ref, author, committer,
+	  NULL, GIT_NOTES_DEFAULT_MSG_RM,
+	  tree_after_removal,
+	  *parents == NULL ? 0 : 1,
+	  (const git_commit **) parents);
 
+cleanup:
+	git_tree_free(tree_after_removal);
 	return error;
 }
 
@@ -291,134 +397,108 @@
 	return note_get_default_ref(notes_ref, repo);
 }
 
-static int retrieve_note_tree_oid(git_oid *tree_oid_out, git_repository *repo, const char *notes_ref)
+static int retrieve_note_tree_and_commit(
+	git_tree **tree_out,
+	git_commit **commit_out,
+	git_repository *repo,
+	const char **notes_ref)
 {
-	int error = -1;
-	git_commit *commit = NULL;
+	int error;
 	git_oid oid;
 
-	if ((error = git_reference_name_to_oid(&oid, repo, notes_ref)) < 0)
-		goto cleanup;
+	if ((error = normalize_namespace(notes_ref, repo)) < 0)
+		return error;
 
-	if (git_commit_lookup(&commit, repo, &oid) < 0)
-		goto cleanup;
+	if ((error = git_reference_name_to_id(&oid, repo, *notes_ref)) < 0)
+		return error;
 
-	git_oid_cpy(tree_oid_out, git_commit_tree_oid(commit));
+	if (git_commit_lookup(commit_out, repo, &oid) < 0)
+		return error;
 
-	error = 0;
+	if ((error = git_commit_tree(tree_out, *commit_out)) < 0)
+		return error;
 
-cleanup:
-	git_commit_free(commit);
-	return error;
+	return 0;
 }
 
 int git_note_read(git_note **out, git_repository *repo,
 		  const char *notes_ref, const git_oid *oid)
 {
 	int error;
-	char *target;
-	git_oid sha;
-
-	*out = NULL;
-
-	if (normalize_namespace(&notes_ref, repo) < 0)
-		return -1;
-
-	if ((error = retrieve_note_tree_oid(&sha, repo, notes_ref)) < 0)
-		return error;
+	char *target = NULL;
+	git_tree *tree = NULL;
+	git_commit *commit = NULL;
 
 	target = git_oid_allocfmt(oid);
 	GITERR_CHECK_ALLOC(target);
 
-	error = note_lookup(out, repo, &sha, target);
+	if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, &notes_ref)) < 0)
+		goto cleanup;
 
+	error = note_lookup(out, repo, tree, target);
+
+cleanup:
 	git__free(target);
+	git_tree_free(tree);
+	git_commit_free(commit);
 	return error;
 }
 
 int git_note_create(
-	git_oid *out, git_repository *repo,
-	git_signature *author, git_signature *committer,
-	const char *notes_ref, const git_oid *oid,
-	const char *note)
+	git_oid *out,
+	git_repository *repo,
+	const git_signature *author,
+	const git_signature *committer,
+	const char *notes_ref,
+	const git_oid *oid,
+	const char *note,
+	int allow_note_overwrite)
 {
-	int error, nparents = 0;
-	char *target;
-	git_oid sha;
+	int error;
+	char *target = NULL;
 	git_commit *commit = NULL;
-	git_reference *ref;
-
-	if (normalize_namespace(&notes_ref, repo) < 0)
-		return -1;
-
-	error = git_reference_lookup(&ref, repo, notes_ref);
-	if (error < 0 && error != GIT_ENOTFOUND)
-		return error;
-
-	if (!error) {
-		assert(git_reference_type(ref) == GIT_REF_OID);
-
-		/* lookup existing notes tree oid */
-
-		git_oid_cpy(&sha, git_reference_oid(ref));
-		git_reference_free(ref);
-
-		error = git_commit_lookup(&commit, repo, &sha);
-		if (error < 0)
-			return error;
-
-		git_oid_cpy(&sha, git_commit_tree_oid(commit));
-		nparents++;
-	}
+	git_tree *tree = NULL;
 
 	target = git_oid_allocfmt(oid);
 	GITERR_CHECK_ALLOC(target);
 
+	error = retrieve_note_tree_and_commit(&tree, &commit, repo, &notes_ref);
+
+	if (error < 0 && error != GIT_ENOTFOUND)
+		goto cleanup;
+
 	error = note_write(out, repo, author, committer, notes_ref,
-			   note, nparents ? &sha : NULL, target,
-			   nparents, &commit);
+			note, tree, target, &commit, allow_note_overwrite);
 
+cleanup:
 	git__free(target);
 	git_commit_free(commit);
+	git_tree_free(tree);
 	return error;
 }
 
 int git_note_remove(git_repository *repo, const char *notes_ref,
-		    git_signature *author, git_signature *committer,
-		    const git_oid *oid)
+		const git_signature *author, const git_signature *committer,
+		const git_oid *oid)
 {
 	int error;
-	char *target;
-	git_oid sha;
-	git_commit *commit;
-	git_reference *ref;
-
-	if (normalize_namespace(&notes_ref, repo) < 0)
-		return -1;
-
-	error = git_reference_lookup(&ref, repo, notes_ref);
-	if (error < 0)
-		return error;
-
-	assert(git_reference_type(ref) == GIT_REF_OID);
-
-	git_oid_cpy(&sha, git_reference_oid(ref));
-	git_reference_free(ref);
-
-	error = git_commit_lookup(&commit, repo, &sha);
-	if (error < 0)
-		return error;
-
-	git_oid_cpy(&sha, git_commit_tree_oid(commit));
+	char *target = NULL;
+	git_commit *commit = NULL;
+	git_tree *tree = NULL;
 
 	target = git_oid_allocfmt(oid);
 	GITERR_CHECK_ALLOC(target);
 
+	if ((error = retrieve_note_tree_and_commit(&tree, &commit, repo, &notes_ref)) < 0)
+		goto cleanup;
+
 	error = note_remove(repo, author, committer, notes_ref,
-			    &sha, target, 1, &commit);
+			    tree, target, &commit);
 
+cleanup:
 	git__free(target);
 	git_commit_free(commit);
+	git_tree_free(tree);
 	return error;
 }
 
@@ -428,13 +508,13 @@
 	return note_get_default_ref(out, repo);
 }
 
-const char * git_note_message(git_note *note)
+const char * git_note_message(const git_note *note)
 {
 	assert(note);
 	return note->message;
 }
 
-const git_oid * git_note_oid(git_note *note)
+const git_oid * git_note_oid(const git_note *note)
 {
 	assert(note);
 	return &note->oid;
@@ -451,17 +531,15 @@
 
 static int process_entry_path(
 	const char* entry_path,
-	const git_oid *note_oid,
-	int (*note_cb)(git_note_data *note_data, void *payload),
-	void *payload)
+	git_oid *annotated_object_id)
 {
-	int i = 0, j = 0, error = -1, len;
+	int error = -1;
+	size_t i = 0, j = 0, len;
 	git_buf buf = GIT_BUF_INIT;
-	git_note_data note_data;
 
-	if (git_buf_puts(&buf, entry_path) < 0)
+	if ((error = git_buf_puts(&buf, entry_path)) < 0)
 		goto cleanup;
-	
+
 	len = git_buf_len(&buf);
 
 	while (i < len) {
@@ -469,10 +547,9 @@
 			i++;
 			continue;
 		}
-		
+
 		if (git__fromhex(buf.ptr[i]) < 0) {
 			/* This is not a note entry */
-			error = 0;
 			goto cleanup;
 		}
 
@@ -488,16 +565,10 @@
 
 	if (j != GIT_OID_HEXSZ) {
 		/* This is not a note entry */
-		error = 0;
 		goto cleanup;
 	}
 
-	if (git_oid_fromstr(&note_data.annotated_object_oid, buf.ptr) < 0)
-		return -1;
-
-	git_oid_cpy(&note_data.blob_oid, note_oid);
-
-	error = note_cb(&note_data, payload);
+	error = git_oid_fromstr(annotated_object_id, buf.ptr);
 
 cleanup:
 	git_buf_free(&buf);
@@ -505,44 +576,86 @@
 }
 
 int git_note_foreach(
-	git_repository *repo,
-	const char *notes_ref,
-	int (*note_cb)(git_note_data *note_data, void *payload),
-	void *payload)
+    git_repository *repo,
+    const char *notes_ref,
+    git_note_foreach_cb note_cb,
+    void *payload)
 {
-	int error = -1;
-	git_oid tree_oid;
-	git_iterator *iter = NULL;
-	git_tree *tree = NULL;
-	const git_index_entry *item;
+    int error;
+    git_note_iterator *iter = NULL;
+    git_oid note_id, annotated_id;
 
-	if (normalize_namespace(&notes_ref, repo) < 0)
-		return -1;
+    if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0)
+        return error;
 
-	if ((error = retrieve_note_tree_oid(&tree_oid, repo, notes_ref)) < 0)
-		goto cleanup;
+    while (!(error = git_note_next(&note_id, &annotated_id, iter))) {
+        if (note_cb(&note_id, &annotated_id, payload)) {
+            error = GIT_EUSER;
+            break;
+        }
+    }
 
-	if (git_tree_lookup(&tree, repo, &tree_oid) < 0)
-		goto cleanup;
+    if (error == GIT_ITEROVER)
+        error = 0;
 
-	if (git_iterator_for_tree(&iter, repo, tree) < 0)
-		goto cleanup;
+    git_note_iterator_free(iter);
+    return error;
+}
 
-	if (git_iterator_current(iter, &item) < 0)
-		goto cleanup;
 
-	while (item) {
-		if (process_entry_path(item->path, &item->oid, note_cb, payload) < 0)
-			goto cleanup;
+void git_note_iterator_free(git_note_iterator *it)
+{
+	if (it == NULL)
+		return;
 
-		if (git_iterator_advance(iter, &item) < 0)
-			goto cleanup;
-	}
+	git_iterator_free(it);
+}
 
-	error = 0;
 
+int git_note_iterator_new(
+	git_note_iterator **it,
+	git_repository *repo,
+	const char *notes_ref)
+{
+	int error;
+	git_commit *commit = NULL;
+	git_tree *tree = NULL;
+
+	error = retrieve_note_tree_and_commit(&tree, &commit, repo, &notes_ref);
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = git_iterator_for_tree(it, tree, 0, NULL, NULL)) < 0)
+		git_iterator_free(*it);
+
 cleanup:
-	git_iterator_free(iter);
 	git_tree_free(tree);
+	git_commit_free(commit);
+
+	return error;
+}
+
+int git_note_next(
+	git_oid* note_id,
+	git_oid* annotated_id,
+	git_note_iterator *it)
+{
+	int error;
+	const git_index_entry *item;
+
+	if ((error = git_iterator_current(&item, it)) < 0)
+		goto exit;
+
+	if (item != NULL) {
+		git_oid_cpy(note_id, &item->oid);
+		error = process_entry_path(item->path, annotated_id);
+
+		if (error >= 0)
+			error = git_iterator_advance(NULL, it);
+	} else {
+		error = GIT_ITEROVER;
+	}
+
+exit:
 	return error;
 }
diff --git a/libgit2/src/notes.h b/libgit2/src/notes.h
--- a/libgit2/src/notes.h
+++ b/libgit2/src/notes.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
 #include "common.h"
 
 #include "git2/oid.h"
+#include "git2/types.h"
 
 #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
 
diff --git a/libgit2/src/object.c b/libgit2/src/object.c
--- a/libgit2/src/object.c
+++ b/libgit2/src/object.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -77,11 +77,63 @@
 	return 0;
 }
 
+int git_object__from_odb_object(
+	git_object **object_out,
+	git_repository *repo,
+	git_odb_object *odb_obj,
+	git_otype type)
+{
+	int error;
+	git_object *object = NULL;
+
+	if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
+		giterr_set(GITERR_INVALID, "The requested type does not match the type in the ODB");
+		return GIT_ENOTFOUND;
+	}
+
+	type = odb_obj->raw.type;
+
+	if ((error = create_object(&object, type)) < 0)
+		return error;
+
+	/* Initialize parent object */
+	git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
+	object->repo = repo;
+
+	switch (type) {
+	case GIT_OBJ_COMMIT:
+		error = git_commit__parse((git_commit *)object, odb_obj);
+		break;
+
+	case GIT_OBJ_TREE:
+		error = git_tree__parse((git_tree *)object, odb_obj);
+		break;
+
+	case GIT_OBJ_TAG:
+		error = git_tag__parse((git_tag *)object, odb_obj);
+		break;
+
+	case GIT_OBJ_BLOB:
+		error = git_blob__parse((git_blob *)object, odb_obj);
+		break;
+
+	default:
+		break;
+	}
+
+	if (error < 0)
+		git_object__free(object);
+	else
+		*object_out = git_cache_try_store(&repo->objects, object);
+
+	return error;
+}
+
 int git_object_lookup_prefix(
 	git_object **object_out,
 	git_repository *repo,
 	const git_oid *id,
-	unsigned int len,
+	size_t len,
 	git_otype type)
 {
 	git_object *object = NULL;
@@ -109,7 +161,7 @@
 		if (object != NULL) {
 			if (type != GIT_OBJ_ANY && type != object->type) {
 				git_object_free(object);
-				giterr_set(GITERR_ODB, "The given type does not match the type in ODB");
+				giterr_set(GITERR_INVALID, "The requested type does not match the type in ODB");
 				return GIT_ENOTFOUND;
 			}
 
@@ -148,51 +200,11 @@
 	if (error < 0)
 		return error;
 
-	if (type != GIT_OBJ_ANY && type != odb_obj->raw.type) {
-		git_odb_object_free(odb_obj);
-		giterr_set(GITERR_ODB, "The given type does not match the type on the ODB");
-		return GIT_ENOTFOUND;
-	}
-
-	type = odb_obj->raw.type;
-
-	if (create_object(&object, type) < 0)
-		return -1;
-
-	/* Initialize parent object */
-	git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
-	object->repo = repo;
-
-	switch (type) {
-	case GIT_OBJ_COMMIT:
-		error = git_commit__parse((git_commit *)object, odb_obj);
-		break;
-
-	case GIT_OBJ_TREE:
-		error = git_tree__parse((git_tree *)object, odb_obj);
-		break;
-
-	case GIT_OBJ_TAG:
-		error = git_tag__parse((git_tag *)object, odb_obj);
-		break;
-
-	case GIT_OBJ_BLOB:
-		error = git_blob__parse((git_blob *)object, odb_obj);
-		break;
-
-	default:
-		break;
-	}
+	error = git_object__from_odb_object(object_out, repo, odb_obj, type);
 
 	git_odb_object_free(odb_obj);
 
-	if (error < 0) {
-		git_object__free(object);
-		return -1;
-	}
-
-	*object_out = git_cache_try_store(&repo->objects, object);
-	return 0;
+	return error;
 }
 
 int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
@@ -292,42 +304,101 @@
 	return git_objects_table[type].size;
 }
 
-int git_object__resolve_to_type(git_object **obj, git_otype type)
+static int dereference_object(git_object **dereferenced, git_object *obj)
 {
-	int error = 0;
-	git_object *scan, *next;
+	git_otype type = git_object_type(obj);
 
-	if (type == GIT_OBJ_ANY)
-		return 0;
+	switch (type) {
+	case GIT_OBJ_COMMIT:
+		return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
 
-	scan = *obj;
+	case GIT_OBJ_TAG:
+		return git_tag_target(dereferenced, (git_tag*)obj);
 
-	while (!error && scan && git_object_type(scan) != type) {
+	case GIT_OBJ_BLOB:
+		return GIT_ENOTFOUND;
 
-		switch (git_object_type(scan)) {
-		case GIT_OBJ_COMMIT:
-		{
-			git_tree *tree = NULL;
-			error = git_commit_tree(&tree, (git_commit *)scan);
-			next = (git_object *)tree;
-			break;
-		}
+	case GIT_OBJ_TREE:
+		return GIT_EAMBIGUOUS;
 
-		case GIT_OBJ_TAG:
-			error = git_tag_target(&next, (git_tag *)scan);
-			break;
+	default:
+		return GIT_EINVALIDSPEC;
+	}
+}
 
-		default:
-			giterr_set(GITERR_REFERENCE, "Object does not resolve to type");
-			error = -1;
-			next = NULL;
-			break;
+static int peel_error(int error, const git_oid *oid, git_otype type)
+{
+	const char *type_name;
+	char hex_oid[GIT_OID_HEXSZ + 1];
+
+	type_name = git_object_type2string(type);
+
+	git_oid_fmt(hex_oid, oid);
+	hex_oid[GIT_OID_HEXSZ] = '\0';
+
+	giterr_set(GITERR_OBJECT, "The git_object of id '%s' can not be "
+		"successfully peeled into a %s (git_otype=%i).", hex_oid, type_name, type);
+
+	return error;
+}
+
+int git_object_peel(
+	git_object **peeled,
+	const git_object *object,
+	git_otype target_type)
+{
+	git_object *source, *deref = NULL;
+	int error;
+
+	if (target_type != GIT_OBJ_TAG && 
+		target_type != GIT_OBJ_COMMIT && 
+		target_type != GIT_OBJ_TREE && 
+		target_type != GIT_OBJ_BLOB && 
+		target_type != GIT_OBJ_ANY)
+			return GIT_EINVALIDSPEC;
+
+	assert(object && peeled);
+
+	if (git_object_type(object) == target_type)
+		return git_object_dup(peeled, (git_object *)object);
+
+	source = (git_object *)object;
+
+	while (!(error = dereference_object(&deref, source))) {
+
+		if (source != object)
+			git_object_free(source);
+
+		if (git_object_type(deref) == target_type) {
+			*peeled = deref;
+			return 0;
 		}
 
-		git_object_free(scan);
-		scan = next;
+		if (target_type == GIT_OBJ_ANY &&
+			git_object_type(deref) != git_object_type(object))
+		{
+			*peeled = deref;
+			return 0;
+		}
+
+		source = deref;
+		deref = NULL;
 	}
 
-	*obj = scan;
+	if (source != object)
+		git_object_free(source);
+
+	git_object_free(deref);
+
+	if (error)
+		error = peel_error(error, git_object_id(object), target_type);
+
 	return error;
+}
+
+int git_object_dup(git_object **dest, git_object *source)
+{
+	git_cached_obj_incref(source);
+	*dest = source;
+	return 0;
 }
diff --git a/libgit2/src/object.h b/libgit2/src/object.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/object.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_object_h__
+#define INCLUDE_object_h__
+
+/** Base git object for inheritance */
+struct git_object {
+	git_cached_obj cached;
+	git_repository *repo;
+	git_otype type;
+};
+
+/* fully free the object; internal method, DO NOT EXPORT */
+void git_object__free(void *object);
+
+int git_object__from_odb_object(
+	git_object **object_out,
+	git_repository *repo,
+	git_odb_object *odb_obj,
+	git_otype type);
+
+int git_object__resolve_to_type(git_object **obj, git_otype type);
+
+int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
+
+void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
+
+#endif
diff --git a/libgit2/src/odb.c b/libgit2/src/odb.c
--- a/libgit2/src/odb.c
+++ b/libgit2/src/odb.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,8 +12,10 @@
 #include "hash.h"
 #include "odb.h"
 #include "delta-apply.h"
+#include "filter.h"
 
 #include "git2/odb_backend.h"
+#include "git2/oid.h"
 
 #define GIT_ALTERNATES_FILE "info/alternates"
 
@@ -21,6 +23,8 @@
 #define GIT_LOOSE_PRIORITY 2
 #define GIT_PACKED_PRIORITY 1
 
+#define GIT_ALTERNATES_MAX_DEPTH 5
+
 typedef struct
 {
 	git_odb_backend *backend;
@@ -28,7 +32,11 @@
 	int is_alternate;
 } backend_internal;
 
-static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
+size_t git_odb__cache_size = GIT_DEFAULT_CACHE_SIZE;
+
+static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+
+int git_odb__format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
 {
 	const char *type_str = git_object_type2string(obj_type);
 	int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
@@ -49,7 +57,7 @@
 	if (!obj->data && obj->len != 0)
 		return -1;
 
-	hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type);
+	hdrlen = git_odb__format_object_header(header, sizeof(header), obj->len, obj->type);
 
 	vec[0].data = header;
 	vec[0].len = hdrlen;
@@ -105,6 +113,9 @@
 
 void git_odb_object_free(git_odb_object *object)
 {
+	if (object == NULL)
+		return;
+
 	git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
 }
 
@@ -112,33 +123,75 @@
 {
 	int hdr_len;
 	char hdr[64], buffer[2048];
-	git_hash_ctx *ctx;
+	git_hash_ctx ctx;
+	ssize_t read_len = 0;
+	int error = 0;
 
-	hdr_len = format_object_header(hdr, sizeof(hdr), size, type);
+	if (!git_object_typeisloose(type)) {
+		giterr_set(GITERR_INVALID, "Invalid object type for hash");
+		return -1;
+	}
 
-	ctx = git_hash_new_ctx();
+	if ((error = git_hash_ctx_init(&ctx)) < 0)
+		return -1;
 
-	git_hash_update(ctx, hdr, hdr_len);
+	hdr_len = git_odb__format_object_header(hdr, sizeof(hdr), size, type);
 
-	while (size > 0) {
-		ssize_t read_len = read(fd, buffer, sizeof(buffer));
+	if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
+		goto done;
 
-		if (read_len < 0) {
-			git_hash_free_ctx(ctx);
-			giterr_set(GITERR_OS, "Error reading file");
-			return -1;
-		}
+	while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
+		if ((error = git_hash_update(&ctx, buffer, read_len)) < 0)
+			goto done;
 
-		git_hash_update(ctx, buffer, read_len);
 		size -= read_len;
 	}
 
-	git_hash_final(out, ctx);
-	git_hash_free_ctx(ctx);
+	/* If p_read returned an error code, the read obviously failed.
+	 * If size is not zero, the file was truncated after we originally
+	 * stat'd it, so we consider this a read failure too */
+	if (read_len < 0 || size > 0) {
+		giterr_set(GITERR_OS, "Error reading file for hashing");
+		error = -1;
 
-	return 0;
+		goto done;
+		return -1;
+	}
+
+	error = git_hash_final(out, &ctx);
+
+done:
+	git_hash_ctx_cleanup(&ctx);
+	return error;
 }
 
+int git_odb__hashfd_filtered(
+	git_oid *out, git_file fd, size_t size, git_otype type, git_vector *filters)
+{
+	int error;
+	git_buf raw = GIT_BUF_INIT;
+	git_buf filtered = GIT_BUF_INIT;
+
+	if (!filters || !filters->length)
+		return git_odb__hashfd(out, fd, size, type);
+
+	/* size of data is used in header, so we have to read the whole file
+	 * into memory to apply filters before beginning to calculate the hash
+	 */
+
+	if (!(error = git_futils_readbuffer_fd(&raw, fd, size)))
+		error = git_filters_apply(&filtered, &raw, filters);
+
+	git_buf_free(&raw);
+
+	if (!error)
+		error = git_odb_hash(out, filtered.ptr, filtered.size, type);
+
+	git_buf_free(&filtered);
+
+	return error;
+}
+
 int git_odb__hashlink(git_oid *out, const char *path)
 {
 	struct stat st;
@@ -159,10 +212,11 @@
 		char *link_data;
 		ssize_t read_len;
 
-		link_data = git__malloc((size_t)size);
+		link_data = git__malloc((size_t)(size + 1));
 		GITERR_CHECK_ALLOC(link_data);
 
-		read_len = p_readlink(path, link_data, (size_t)(size + 1));
+		read_len = p_readlink(path, link_data, (size_t)size);
+		link_data[size] = '\0';
 		if (read_len != (ssize_t)size) {
 			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
 			return -1;
@@ -170,7 +224,7 @@
 
 		result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
 		git__free(link_data);
-	} else { 
+	} else {
 		int fd = git_futils_open_ro(path);
 		if (fd < 0)
 			return -1;
@@ -299,7 +353,7 @@
 	git_odb *db = git__calloc(1, sizeof(*db));
 	GITERR_CHECK_ALLOC(db);
 
-	if (git_cache_init(&db->cache, GIT_DEFAULT_CACHE_SIZE, &free_odb_object) < 0 ||
+	if (git_cache_init(&db->cache, git_odb__cache_size, &free_odb_object) < 0 ||
 		git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
 	{
 		git__free(db);
@@ -317,6 +371,8 @@
 
 	assert(odb && backend);
 
+	GITERR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend");
+
 	/* Check if the backend is already owned by another ODB */
 	assert(!backend->odb || backend->odb == odb);
 
@@ -347,7 +403,7 @@
 	return add_backend_internal(odb, backend, priority, 1);
 }
 
-static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
+static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates, int alternate_depth)
 {
 	git_odb_backend *loose, *packed;
 
@@ -361,10 +417,10 @@
 		add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
 		return -1;
 
-	return 0;
+	return load_alternates(db, objects_dir, alternate_depth);
 }
 
-static int load_alternates(git_odb *odb, const char *objects_dir)
+static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth)
 {
 	git_buf alternates_path = GIT_BUF_INIT;
 	git_buf alternates_buf = GIT_BUF_INIT;
@@ -372,6 +428,11 @@
 	const char *alternate;
 	int result = 0;
 
+	/* Git reports an error, we just ignore anything deeper */
+	if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH) {
+		return 0;
+	}
+
 	if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
 		return -1;
 
@@ -392,14 +453,18 @@
 		if (*alternate == '\0' || *alternate == '#')
 			continue;
 
-		/* relative path: build based on the current `objects` folder */
-		if (*alternate == '.') {
+		/*
+		 * Relative path: build based on the current `objects`
+		 * folder. However, relative paths are only allowed in
+		 * the current repository.
+		 */
+		if (*alternate == '.' && !alternate_depth) {
 			if ((result = git_buf_joinpath(&alternates_path, objects_dir, alternate)) < 0)
 				break;
 			alternate = git_buf_cstr(&alternates_path);
 		}
 
-		if ((result = add_default_backends(odb, alternate, 1)) < 0)
+		if ((result = add_default_backends(odb, alternate, 1, alternate_depth + 1)) < 0)
 			break;
 	}
 
@@ -409,6 +474,11 @@
 	return result;
 }
 
+int git_odb_add_disk_alternate(git_odb *odb, const char *path)
+{
+	return add_default_backends(odb, path, 1, 0);
+}
+
 int git_odb_open(git_odb **out, const char *objects_dir)
 {
 	git_odb *db;
@@ -420,9 +490,7 @@
 	if (git_odb_new(&db) < 0)
 		return -1;
 
-	if (add_default_backends(db, objects_dir, 0) < 0 ||
-		load_alternates(db, objects_dir) < 0)
-	{
+	if (add_default_backends(db, objects_dir, 0, 0) < 0) {
 		git_odb_free(db);
 		return -1;
 	}
@@ -433,7 +501,7 @@
 
 static void odb_free(git_odb *db)
 {
-	unsigned int i;
+	size_t i;
 
 	for (i = 0; i < db->backends.length; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
@@ -461,8 +529,9 @@
 int git_odb_exists(git_odb *db, const git_oid *id)
 {
 	git_odb_object *object;
-	unsigned int i;
+	size_t i;
 	bool found = false;
+	bool refreshed = false;
 
 	assert(db && id);
 
@@ -471,6 +540,7 @@
 		return (int)true;
 	}
 
+attempt_lookup:
 	for (i = 0; i < db->backends.length && !found; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -479,24 +549,51 @@
 			found = b->exists(b, id);
 	}
 
+	if (!found && !refreshed) {
+		if (git_odb_refresh(db) < 0) {
+			giterr_clear();
+			return (int)false;
+		}
+
+		refreshed = true;
+		goto attempt_lookup;
+	}
+
 	return (int)found;
 }
 
 int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
 {
-	unsigned int i;
+	int error;
+	git_odb_object *object;
+
+	error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
+
+	if (object)
+		git_odb_object_free(object);
+
+	return error;
+}
+
+int git_odb__read_header_or_object(
+	git_odb_object **out, size_t *len_p, git_otype *type_p,
+	git_odb *db, const git_oid *id)
+{
+	size_t i;
 	int error = GIT_ENOTFOUND;
 	git_odb_object *object;
 
-	assert(db && id);
+	assert(db && id && out && len_p && type_p);
 
 	if ((object = git_cache_get(&db->cache, id)) != NULL) {
 		*len_p = object->raw.len;
 		*type_p = object->raw.type;
-		git_odb_object_free(object);
+		*out = object;
 		return 0;
 	}
 
+	*out = NULL;
+
 	for (i = 0; i < db->backends.length && error < 0; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -517,22 +614,32 @@
 
 	*len_p = object->raw.len;
 	*type_p = object->raw.type;
-	git_odb_object_free(object);
+	*out = object;
+
 	return 0;
 }
 
 int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
 {
-	unsigned int i;
-	int error = GIT_ENOTFOUND;
+	size_t i;
+	int error;
+	bool refreshed = false;
 	git_rawobj raw;
 
 	assert(out && db && id);
 
+	if (db->backends.length == 0) {
+		giterr_set(GITERR_ODB, "Failed to lookup object: no backends loaded");
+		return GIT_ENOTFOUND;
+	}
+
 	*out = git_cache_get(&db->cache, id);
 	if (*out != NULL)
 		return 0;
 
+attempt_lookup:
+	error = GIT_ENOTFOUND;
+
 	for (i = 0; i < db->backends.length && error < 0; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -541,10 +648,14 @@
 			error = b->read(&raw.data, &raw.len, &raw.type, b, id);
 	}
 
-	/* TODO: If no backends are configured, this returns GIT_ENOTFOUND but
-	 * will never have called giterr_set().
-	 */
+	if (error == GIT_ENOTFOUND && !refreshed) {
+		if ((error = git_odb_refresh(db)) < 0)
+			return error;
 
+		refreshed = true;
+		goto attempt_lookup;
+	}
+
 	if (error && error != GIT_PASSTHROUGH)
 		return error;
 
@@ -553,13 +664,14 @@
 }
 
 int git_odb_read_prefix(
-	git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+	git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
 {
-	unsigned int i;
+	size_t i;
 	int error = GIT_ENOTFOUND;
 	git_oid found_full_oid = {{0}};
 	git_rawobj raw;
-	bool found = false;
+	void *data = NULL;
+	bool found = false, refreshed = false;
 
 	assert(out && db);
 
@@ -575,11 +687,12 @@
 			return 0;
 	}
 
+attempt_lookup:
 	for (i = 0; i < db->backends.length; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
 
-		if (b->read != NULL) {
+		if (b->read_prefix != NULL) {
 			git_oid full_oid;
 			error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len);
 			if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
@@ -588,13 +701,25 @@
 			if (error)
 				return error;
 
+			git__free(data);
+			data = raw.data;
+
 			if (found && git_oid_cmp(&full_oid, &found_full_oid))
 				return git_odb__error_ambiguous("multiple matches for prefix");
+
 			found_full_oid = full_oid;
 			found = true;
 		}
 	}
 
+	if (!found && !refreshed) {
+		if ((error = git_odb_refresh(db)) < 0)
+			return error;
+
+		refreshed = true;
+		goto attempt_lookup;
+	}
+
 	if (!found)
 		return git_odb__error_notfound("no match for prefix", short_id);
 
@@ -602,15 +727,34 @@
 	return 0;
 }
 
+int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
+{
+	unsigned int i;
+	backend_internal *internal;
+
+	git_vector_foreach(&db->backends, i, internal) {
+		git_odb_backend *b = internal->backend;
+		int error = b->foreach(b, cb, payload);
+		if (error < 0)
+			return error;
+	}
+
+	return 0;
+}
+
 int git_odb_write(
 	git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
 {
-	unsigned int i;
+	size_t i;
 	int error = GIT_ERROR;
 	git_odb_stream *stream;
 
 	assert(oid && db);
 
+	git_odb_hash(oid, data, len, type);
+	if (git_odb_exists(db, oid))
+		return 0;
+
 	for (i = 0; i < db->backends.length && error < 0; ++i) {
 		backend_internal *internal = git_vector_get(&db->backends, i);
 		git_odb_backend *b = internal->backend;
@@ -643,7 +787,7 @@
 int git_odb_open_wstream(
 	git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
 {
-	unsigned int i;
+	size_t i;
 	int error = GIT_ERROR;
 
 	assert(stream && db);
@@ -670,7 +814,7 @@
 
 int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
 {
-	unsigned int i;
+	size_t i;
 	int error = GIT_ERROR;
 
 	assert(stream && db);
@@ -687,6 +831,56 @@
 		error = 0;
 
 	return error;
+}
+
+int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload)
+{
+	size_t i;
+	int error = GIT_ERROR;
+
+	assert(out && db);
+
+	for (i = 0; i < db->backends.length && error < 0; ++i) {
+		backend_internal *internal = git_vector_get(&db->backends, i);
+		git_odb_backend *b = internal->backend;
+
+		/* we don't write in alternates! */
+		if (internal->is_alternate)
+			continue;
+
+		if (b->writepack != NULL)
+			error = b->writepack(out, b, progress_cb, progress_payload);
+	}
+
+	if (error == GIT_PASSTHROUGH)
+		error = 0;
+
+	return error;
+}
+
+void *git_odb_backend_malloc(git_odb_backend *backend, size_t len)
+{
+	GIT_UNUSED(backend);
+	return git__malloc(len);
+}
+
+int git_odb_refresh(struct git_odb *db)
+{
+	size_t i;
+	assert(db);
+
+	for (i = 0; i < db->backends.length; ++i) {
+		backend_internal *internal = git_vector_get(&db->backends, i);
+		git_odb_backend *b = internal->backend;
+
+		if (b->refresh != NULL) {
+			int error = b->refresh(b);
+			if (error < 0)
+				return error;
+		}
+	}
+
+	return 0;
 }
 
 int git_odb__error_notfound(const char *message, const git_oid *oid)
diff --git a/libgit2/src/odb.h b/libgit2/src/odb.h
--- a/libgit2/src/odb.h
+++ b/libgit2/src/odb.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -46,6 +46,10 @@
 int git_odb__hashobj(git_oid *id, git_rawobj *obj);
 
 /*
+ * Format the object header such as it would appear in the on-disk object
+ */
+int git_odb__format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type);
+/*
  * Hash an open file descriptor.
  * This is a performance call when the contents of a fd need to be hashed,
  * but the fd is already open and we have the size of the contents.
@@ -58,12 +62,19 @@
 int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type);
 
 /*
- * Hash a `path`, assuming it could be a POSIX symlink: if the path is a symlink,
- * then the raw contents of the symlink will be hashed. Otherwise, this will
- * fallback to `git_odb__hashfd`.
+ * Hash an open file descriptor applying an array of filters
+ * Acts just like git_odb__hashfd with the addition of filters...
+ */
+int git_odb__hashfd_filtered(
+	git_oid *out, git_file fd, size_t len, git_otype type, git_vector *filters);
+
+/*
+ * Hash a `path`, assuming it could be a POSIX symlink: if the path is a
+ * symlink, then the raw contents of the symlink will be hashed. Otherwise,
+ * this will fallback to `git_odb__hashfd`.
  *
- * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may only
- * point to blobs.
+ * The hash type for this call is always `GIT_OBJ_BLOB` because symlinks may
+ * only point to blobs.
  */
 int git_odb__hashlink(git_oid *out, const char *path);
 
@@ -76,5 +87,13 @@
  * Generate a GIT_EAMBIGUOUS error for the ODB.
  */
 int git_odb__error_ambiguous(const char *message);
+
+/*
+ * Attempt to read object header or just return whole object if it could
+ * not be read.
+ */
+int git_odb__read_header_or_object(
+	git_odb_object **out, size_t *len_p, git_otype *type_p,
+	git_odb *db, const git_oid *id);
 
 #endif
diff --git a/libgit2/src/odb_loose.c b/libgit2/src/odb_loose.c
--- a/libgit2/src/odb_loose.c
+++ b/libgit2/src/odb_loose.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -42,7 +42,7 @@
 typedef struct {
 	size_t dir_len;
 	unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
-	unsigned int short_oid_len;
+	size_t short_oid_len;
 	int found;				/* number of matching
 						 * objects already found */
 	unsigned char res_oid[GIT_OID_HEXSZ];	/* hex formatted oid of
@@ -502,7 +502,7 @@
 	git_oid *res_oid,
 	loose_backend *backend,
 	const git_oid *short_oid,
-	unsigned int len)
+	size_t len)
 {
 	char *objects_dir = backend->objects_dir;
 	size_t dir_len = strlen(objects_dir);
@@ -629,7 +629,7 @@
 	git_otype *type_p,
 	git_odb_backend *backend,
 	const git_oid *short_oid,
-	unsigned int len)
+	size_t len)
 {
 	int error = 0;
 
@@ -676,6 +676,91 @@
 	return !error;
 }
 
+struct foreach_state {
+	size_t dir_len;
+	git_odb_foreach_cb cb;
+	void *data;
+	int cb_error;
+};
+
+GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr)
+{
+	int v, i = 0;
+	if (strlen(ptr) != 41)
+		return -1;
+
+	if (ptr[2] != '/') {
+		return -1;
+	}
+
+	v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
+	if (v < 0)
+		return -1;
+
+	oid->id[0] = (unsigned char) v;
+
+	ptr += 3;
+	for (i = 0; i < 38; i += 2) {
+		v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
+		if (v < 0)
+			return -1;
+
+		oid->id[1 + i/2] = (unsigned char) v;
+	}
+
+	return 0;
+}
+
+static int foreach_object_dir_cb(void *_state, git_buf *path)
+{
+	git_oid oid;
+	struct foreach_state *state = (struct foreach_state *) _state;
+
+	if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0)
+		return 0;
+
+	if (state->cb(&oid, state->data)) {
+		state->cb_error = GIT_EUSER;
+		return -1;
+	}
+
+	return 0;
+}
+
+static int foreach_cb(void *_state, git_buf *path)
+{
+	struct foreach_state *state = (struct foreach_state *) _state;
+
+	return git_path_direach(path, foreach_object_dir_cb, state);
+}
+
+static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
+{
+	char *objects_dir;
+	int error;
+	git_buf buf = GIT_BUF_INIT;
+	struct foreach_state state;
+	loose_backend *backend = (loose_backend *) _backend;
+
+	assert(backend && cb);
+
+	objects_dir = backend->objects_dir;
+
+	git_buf_sets(&buf, objects_dir);
+	git_path_to_dir(&buf);
+
+	memset(&state, 0, sizeof(state));
+	state.cb = cb;
+	state.data = data;
+	state.dir_len = git_buf_len(&buf);
+
+	error = git_path_direach(&buf, foreach_cb, &state);
+
+	git_buf_free(&buf);
+
+	return state.cb_error ? state.cb_error : error;
+}
+
 static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
 {
 	loose_writestream *stream = (loose_writestream *)_stream;
@@ -785,7 +870,6 @@
 
 	if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
 		git_filebuf_open(&fbuf, final_path.ptr,
-			GIT_FILEBUF_HASH_CONTENTS |
 			GIT_FILEBUF_TEMPORARY |
 			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
 	{
@@ -795,7 +879,6 @@
 
 	git_filebuf_write(&fbuf, header, header_len);
 	git_filebuf_write(&fbuf, data, len);
-	git_filebuf_hash(oid, &fbuf);
 
 	if (object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
 		git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0 ||
@@ -830,6 +913,7 @@
 	backend = git__calloc(1, sizeof(loose_backend));
 	GITERR_CHECK_ALLOC(backend);
 
+	backend->parent.version = GIT_ODB_BACKEND_VERSION;
 	backend->objects_dir = git__strdup(objects_dir);
 	GITERR_CHECK_ALLOC(backend->objects_dir);
 
@@ -845,6 +929,7 @@
 	backend->parent.read_header = &loose_backend__read_header;
 	backend->parent.writestream = &loose_backend__stream;
 	backend->parent.exists = &loose_backend__exists;
+	backend->parent.foreach = &loose_backend__foreach;
 	backend->parent.free = &loose_backend__free;
 
 	*backend_out = (git_odb_backend *)backend;
diff --git a/libgit2/src/odb_pack.c b/libgit2/src/odb_pack.c
--- a/libgit2/src/odb_pack.c
+++ b/libgit2/src/odb_pack.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -24,9 +24,13 @@
 	git_vector packs;
 	struct git_pack_file *last_found;
 	char *pack_folder;
-	time_t pack_folder_mtime;
 };
 
+struct pack_writepack {
+	struct git_odb_writepack parent;
+	git_indexer_stream *indexer_stream;
+};
+
 /**
  * The wonderful tale of a Packed Object lookup query
  * ===================================================
@@ -128,13 +132,9 @@
  *
  ***********************************************************/
 
-static void pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p);
-static int pack_window_contains(git_mwindow *win, off_t offset);
-
 static int packfile_sort__cb(const void *a_, const void *b_);
 
 static int packfile_load__cb(void *_data, git_buf *path);
-static int packfile_refresh_all(struct pack_backend *backend);
 
 static int pack_entry_find(struct git_pack_entry *e,
 	struct pack_backend *backend, const git_oid *oid);
@@ -149,7 +149,7 @@
 	struct git_pack_entry *e,
 	struct pack_backend *backend,
 	const git_oid *short_oid,
-	unsigned int len);
+	size_t len);
 
 
 
@@ -159,23 +159,6 @@
  *
  ***********************************************************/
 
-GIT_INLINE(void) pack_window_free_all(struct pack_backend *backend, struct git_pack_file *p)
-{
-	GIT_UNUSED(backend);
-	git_mwindow_free_all(&p->mwf);
-}
-
-GIT_INLINE(int) pack_window_contains(git_mwindow *win, off_t offset)
-{
-	/* We must promise at least 20 bytes (one hash) after the
-	 * offset is available from this window, otherwise the offset
-	 * is not actually in this window and a different window (which
-	 * has that one hash excess) must be used. This is to support
-	 * the object header and delta base parsing routines below.
-	 */
-	return git_mwindow_contains(win, offset + 20);
-}
-
 static int packfile_sort__cb(const void *a_, const void *b_)
 {
 	const struct git_pack_file *a = a_;
@@ -212,7 +195,7 @@
 	struct pack_backend *backend = (struct pack_backend *)_data;
 	struct git_pack_file *pack;
 	int error;
-	unsigned int i;
+	size_t i;
 
 	if (git__suffixcmp(path->ptr, ".idx") != 0)
 		return 0; /* not an index */
@@ -233,79 +216,61 @@
 	return git_vector_insert(&backend->packs, pack);
 }
 
-static int packfile_refresh_all(struct pack_backend *backend)
+static int pack_entry_find_inner(
+	struct git_pack_entry *e,
+	struct pack_backend *backend,
+	const git_oid *oid,
+	struct git_pack_file *last_found)
 {
-	int error;
-	struct stat st;
+	size_t i;
 
-	if (backend->pack_folder == NULL)
+	if (last_found &&
+		git_pack_entry_find(e, last_found, oid, GIT_OID_HEXSZ) == 0)
 		return 0;
 
-	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
-		return git_odb__error_notfound("failed to refresh packfiles", NULL);
-
-	if (st.st_mtime != backend->pack_folder_mtime) {
-		git_buf path = GIT_BUF_INIT;
-		git_buf_sets(&path, backend->pack_folder);
-
-		/* reload all packs */
-		error = git_path_direach(&path, packfile_load__cb, (void *)backend);
-
-		git_buf_free(&path);
+	for (i = 0; i < backend->packs.length; ++i) {
+		struct git_pack_file *p;
 
-		if (error < 0)
-			return error;
+		p = git_vector_get(&backend->packs, i);
+		if (p == last_found)
+			continue;
 
-		git_vector_sort(&backend->packs);
-		backend->pack_folder_mtime = st.st_mtime;
+		if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
+			backend->last_found = p;
+			return 0;
+		}
 	}
 
-	return 0;
+	return -1;
 }
 
 static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
 {
-	int error;
-	unsigned int i;
-
-	if ((error = packfile_refresh_all(backend)) < 0)
-		return error;
+	struct git_pack_file *last_found = backend->last_found;
 
 	if (backend->last_found &&
 		git_pack_entry_find(e, backend->last_found, oid, GIT_OID_HEXSZ) == 0)
 		return 0;
 
-	for (i = 0; i < backend->packs.length; ++i) {
-		struct git_pack_file *p;
-
-		p = git_vector_get(&backend->packs, i);
-		if (p == backend->last_found)
-			continue;
-
-		if (git_pack_entry_find(e, p, oid, GIT_OID_HEXSZ) == 0) {
-			backend->last_found = p;
-			return 0;
-		}
-	}
+	if (!pack_entry_find_inner(e, backend, oid, last_found))
+		return 0;
 
 	return git_odb__error_notfound("failed to find pack entry", oid);
 }
 
-static int pack_entry_find_prefix(
-	struct git_pack_entry *e,
-	struct pack_backend *backend,
-	const git_oid *short_oid,
-	unsigned int len)
+static unsigned pack_entry_find_prefix_inner(
+		struct git_pack_entry *e,
+		struct pack_backend *backend,
+		const git_oid *short_oid,
+		size_t len,
+		struct git_pack_file *last_found)
 {
 	int error;
-	unsigned int i;
+	size_t i;
 	unsigned found = 0;
 
-	if ((error = packfile_refresh_all(backend)) < 0)
-		return error;
-
-	if (backend->last_found) {
-		error = git_pack_entry_find(e, backend->last_found, short_oid, len);
+	if (last_found) {
+		error = git_pack_entry_find(e, last_found, short_oid, len);
 		if (error == GIT_EAMBIGUOUS)
 			return error;
 		if (!error)
@@ -316,7 +281,7 @@
 		struct git_pack_file *p;
 
 		p = git_vector_get(&backend->packs, i);
-		if (p == backend->last_found)
+		if (p == last_found)
 			continue;
 
 		error = git_pack_entry_find(e, p, short_oid, len);
@@ -329,6 +294,18 @@
 		}
 	}
 
+	return found;
+}
+
+static int pack_entry_find_prefix(
+	struct git_pack_entry *e,
+	struct pack_backend *backend,
+	const git_oid *short_oid,
+	size_t len)
+{
+	struct git_pack_file *last_found = backend->last_found;
+	unsigned int found = pack_entry_find_prefix_inner(e, backend, short_oid, len, last_found);
+
 	if (!found)
 		return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
 	else if (found > 1)
@@ -345,20 +322,47 @@
  * Implement the git_odb_backend API calls
  *
  ***********************************************************/
+static int pack_backend__refresh(git_odb_backend *_backend)
+{
+	struct pack_backend *backend = (struct pack_backend *)_backend;
 
-/*
-int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+	int error;
+	struct stat st;
+	git_buf path = GIT_BUF_INIT;
+
+	if (backend->pack_folder == NULL)
+		return 0;
+
+	if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
+		return git_odb__error_notfound("failed to refresh packfiles", NULL);
+
+	git_buf_sets(&path, backend->pack_folder);
+
+	/* reload all packs */
+	error = git_path_direach(&path, packfile_load__cb, (void *)backend);
+
+	git_buf_free(&path);
+
+	if (error < 0)
+		return error;
+
+	git_vector_sort(&backend->packs);
+	return 0;
+}
+
+
+static int pack_backend__read_header(size_t *len_p, git_otype *type_p, struct git_odb_backend *backend, const git_oid *oid)
 {
-	pack_location location;
+	struct git_pack_entry e;
+	int error;
 
-	assert(obj && backend && oid);
+	assert(len_p && type_p && backend && oid);
 
-	if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0)
-		return GIT_ENOTFOUND;
+	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
+		return error;
 
-	return read_header_packed(obj, &location);
+	return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
 }
-*/
 
 static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
 {
@@ -384,7 +388,7 @@
 	git_otype *type_p,
 	git_odb_backend *backend,
 	const git_oid *short_oid,
-	unsigned int len)
+	size_t len)
 {
 	int error = 0;
 
@@ -420,18 +424,101 @@
 	return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
 }
 
-static void pack_backend__free(git_odb_backend *_backend)
+static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
 {
+	int error;
+	struct git_pack_file *p;
 	struct pack_backend *backend;
 	unsigned int i;
 
+	assert(_backend && cb);
+	backend = (struct pack_backend *)_backend;
+
+	/* Make sure we know about the packfiles */
+	if ((error = pack_backend__refresh(_backend)) < 0)
+		return error;
+
+	git_vector_foreach(&backend->packs, i, p) {
+		if ((error = git_pack_foreach_entry(p, cb, data)) < 0)
+			return error;
+	}
+
+	return 0;
+}
+
+static int pack_backend__writepack_add(struct git_odb_writepack *_writepack, const void *data, size_t size, git_transfer_progress *stats)
+{
+	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+
+	assert(writepack);
+
+	return git_indexer_stream_add(writepack->indexer_stream, data, size, stats);
+}
+
+static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_transfer_progress *stats)
+{
+	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+
+	assert(writepack);
+
+	return git_indexer_stream_finalize(writepack->indexer_stream, stats);
+}
+
+static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
+{
+	struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+
+	assert(writepack);
+
+	git_indexer_stream_free(writepack->indexer_stream);
+	git__free(writepack);
+}
+
+static int pack_backend__writepack(struct git_odb_writepack **out,
+	git_odb_backend *_backend,
+	git_transfer_progress_callback progress_cb,
+	void *progress_payload)
+{
+	struct pack_backend *backend;
+	struct pack_writepack *writepack;
+
+	assert(out && _backend);
+
+	*out = NULL;
+
+	backend = (struct pack_backend *)_backend;
+
+	writepack = git__calloc(1, sizeof(struct pack_writepack));
+	GITERR_CHECK_ALLOC(writepack);
+
+	if (git_indexer_stream_new(&writepack->indexer_stream,
+		backend->pack_folder, progress_cb, progress_payload) < 0) {
+		git__free(writepack);
+		return -1;
+	}
+
+	writepack->parent.backend = _backend;
+	writepack->parent.add = pack_backend__writepack_add;
+	writepack->parent.commit = pack_backend__writepack_commit;
+	writepack->parent.free = pack_backend__writepack_free;
+
+	*out = (git_odb_writepack *)writepack;
+
+	return 0;
+}
+
+static void pack_backend__free(git_odb_backend *_backend)
+{
+	struct pack_backend *backend;
+	size_t i;
+
 	assert(_backend);
 
 	backend = (struct pack_backend *)_backend;
 
 	for (i = 0; i < backend->packs.length; ++i) {
 		struct git_pack_file *p = git_vector_get(&backend->packs, i);
-		packfile_free(p);
+		git_packfile_free(p);
 	}
 
 	git_vector_free(&backend->packs);
@@ -439,6 +526,43 @@
 	git__free(backend);
 }
 
+int git_odb_backend_one_pack(git_odb_backend **backend_out, const char *idx)
+{
+	struct pack_backend *backend = NULL;
+	struct git_pack_file *packfile = NULL;
+
+	if (git_packfile_check(&packfile, idx) < 0)
+		return -1;
+
+	backend = git__calloc(1, sizeof(struct pack_backend));
+	GITERR_CHECK_ALLOC(backend);
+	backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+	if (git_vector_init(&backend->packs, 1, NULL) < 0)
+		goto on_error;
+
+	if (git_vector_insert(&backend->packs, packfile) < 0)
+		goto on_error;
+
+	backend->parent.read = &pack_backend__read;
+	backend->parent.read_prefix = &pack_backend__read_prefix;
+	backend->parent.read_header = &pack_backend__read_header;
+	backend->parent.exists = &pack_backend__exists;
+	backend->parent.refresh = &pack_backend__refresh;
+	backend->parent.foreach = &pack_backend__foreach;
+	backend->parent.free = &pack_backend__free;
+
+	*backend_out = (git_odb_backend *)backend;
+
+	return 0;
+
+on_error:
+	git_vector_free(&backend->packs);
+	git__free(backend);
+	git__free(packfile);
+	return -1;
+}
+
 int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
 {
 	struct pack_backend *backend = NULL;
@@ -446,6 +570,7 @@
 
 	backend = git__calloc(1, sizeof(struct pack_backend));
 	GITERR_CHECK_ALLOC(backend);
+	backend->parent.version = GIT_ODB_BACKEND_VERSION;
 
 	if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
 		git_buf_joinpath(&path, objects_dir, "pack") < 0)
@@ -455,14 +580,21 @@
 	}
 
 	if (git_path_isdir(git_buf_cstr(&path)) == true) {
+		int error;
+
 		backend->pack_folder = git_buf_detach(&path);
-		backend->pack_folder_mtime = 0;
+		error = pack_backend__refresh((git_odb_backend *)backend);
+		if (error < 0)
+			return error;
 	}
 
 	backend->parent.read = &pack_backend__read;
 	backend->parent.read_prefix = &pack_backend__read_prefix;
-	backend->parent.read_header = NULL;
+	backend->parent.read_header = &pack_backend__read_header;
 	backend->parent.exists = &pack_backend__exists;
+	backend->parent.refresh = &pack_backend__refresh;
+	backend->parent.foreach = &pack_backend__foreach;
+	backend->parent.writepack = &pack_backend__writepack;
 	backend->parent.free = &pack_backend__free;
 
 	*backend_out = (git_odb_backend *)backend;
diff --git a/libgit2/src/offmap.h b/libgit2/src/offmap.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/offmap.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_offmap_h__
+#define INCLUDE_offmap_h__
+
+#include "common.h"
+#include "git2/types.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(off, git_off_t, void *);
+typedef khash_t(off) git_offmap;
+
+#define GIT__USE_OFFMAP \
+	__KHASH_IMPL(off, static kh_inline, git_off_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal);
+
+#define git_offmap_alloc()  kh_init(off)
+#define git_offmap_free(h)  kh_destroy(off, h), h = NULL
+#define git_offmap_clear(h) kh_clear(off, h)
+
+#define git_offmap_num_entries(h) kh_size(h)
+
+#define git_offmap_lookup_index(h, k)  kh_get(off, h, k)
+#define git_offmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_offmap_exists(h, k) (kh_get(off, h, k) != kh_end(h))
+
+#define git_offmap_value_at(h, idx)        kh_val(h, idx)
+#define git_offmap_set_value_at(h, idx, v) kh_val(h, idx) = v
+#define git_offmap_delete_at(h, idx)       kh_del(off, h, idx)
+
+#define git_offmap_insert(h, key, val, rval) do { \
+	khiter_t __pos = kh_put(off, h, key, &rval); \
+	if (rval >= 0) { \
+		if (rval == 0) kh_key(h, __pos) = key; \
+		kh_val(h, __pos) = val; \
+	} } while (0)
+
+#define git_offmap_insert2(h, key, val, oldv, rval) do { \
+	khiter_t __pos = kh_put(off, h, key, &rval); \
+	if (rval >= 0) { \
+		if (rval == 0) { \
+			oldv = kh_val(h, __pos); \
+			kh_key(h, __pos) = key; \
+		} else { oldv = NULL; } \
+		kh_val(h, __pos) = val; \
+	} } while (0)
+
+#define git_offmap_delete(h, key) do { \
+	khiter_t __pos = git_offmap_lookup_index(h, key); \
+	if (git_offmap_valid_index(h, __pos)) \
+		git_offmap_delete_at(h, __pos); } while (0)
+
+#define git_offmap_foreach		kh_foreach
+#define git_offmap_foreach_value	kh_foreach_value
+
+#endif
diff --git a/libgit2/src/oid.c b/libgit2/src/oid.c
--- a/libgit2/src/oid.c
+++ b/libgit2/src/oid.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -24,11 +24,8 @@
 	size_t p;
 	int v;
 
-	if (length < 4)
-		return oid_error_invalid("input too short");
-
 	if (length > GIT_OID_HEXSZ)
-		length = GIT_OID_HEXSZ;
+		return oid_error_invalid("too long");
 
 	for (p = 0; p < length - 1; p += 2) {
 		v = (git__fromhex(str[p + 0]) << 4)
@@ -54,6 +51,11 @@
 	return 0;
 }
 
+int git_oid_fromstrp(git_oid *out, const char *str)
+{
+	return git_oid_fromstrn(out, str, strlen(str));
+}
+
 int git_oid_fromstr(git_oid *out, const char *str)
 {
 	return git_oid_fromstrn(out, str, GIT_OID_HEXSZ);
@@ -98,11 +100,14 @@
 {
 	char str[GIT_OID_HEXSZ];
 
-	if (!out || n == 0 || !oid)
+	if (!out || n == 0)
 		return "";
 
 	n--; /* allow room for terminating NUL */
 
+	if (oid == NULL)
+		n = 0;
+
 	if (n > 0) {
 		git_oid_fmt(str, oid);
 		if (n > GIT_OID_HEXSZ)
@@ -161,12 +166,7 @@
 	memcpy(out->id, src->id, sizeof(out->id));
 }
 
-int git_oid_cmp(const git_oid *a, const git_oid *b)
-{
-	return memcmp(a->id, b->id, sizeof(a->id));
-}
-
-int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, unsigned int len)
+int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
 {
 	const unsigned char *a = oid_a->id;
 	const unsigned char *b = oid_b->id;
@@ -301,7 +301,7 @@
  *	- Each normal node points to 16 children (one for each possible
  *	character in the oid). This is *not* stored in an array of
  *	pointers, because in a 64-bit arch this would be sucking
- *	16*sizeof(void*) = 128 bytes of memory per node, which is fucking
+ *	16*sizeof(void*) = 128 bytes of memory per node, which is
  *	insane. What we do is store Node Indexes, and use these indexes
  *	to look up each node in the om->index array. These indexes are
  *	signed shorts, so this limits the amount of unique OIDs that
diff --git a/libgit2/src/oidmap.h b/libgit2/src/oidmap.h
--- a/libgit2/src/oidmap.h
+++ b/libgit2/src/oidmap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -28,13 +28,8 @@
 	return h;
 }
 
-GIT_INLINE(int) hash_git_oid_equal(const git_oid *a, const git_oid *b)
-{
-	return (memcmp(a->id, b->id, sizeof(a->id)) == 0);
-}
-
 #define GIT__USE_OIDMAP \
-	__KHASH_IMPL(oid, static inline, const git_oid *, void *, 1, hash_git_oid, hash_git_oid_equal)
+	__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, hash_git_oid, git_oid_equal)
 
 #define git_oidmap_alloc() kh_init(oid)
 #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
diff --git a/libgit2/src/pack-objects.c b/libgit2/src/pack-objects.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pack-objects.c
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pack-objects.h"
+
+#include "compress.h"
+#include "delta.h"
+#include "iterator.h"
+#include "netops.h"
+#include "pack.h"
+#include "thread-utils.h"
+#include "tree.h"
+
+#include "git2/pack.h"
+#include "git2/commit.h"
+#include "git2/tag.h"
+#include "git2/indexer.h"
+#include "git2/config.h"
+
+struct unpacked {
+	git_pobject *object;
+	void *data;
+	struct git_delta_index *index;
+	unsigned int depth;
+};
+
+struct tree_walk_context {
+	git_packbuilder *pb;
+	git_buf buf;
+};
+
+#ifdef GIT_THREADS
+
+#define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) do { \
+		int result = git_mutex_##op(&(pb)->mtx); \
+		assert(!result); \
+		GIT_UNUSED(result); \
+	} while (0)
+
+#else
+
+#define GIT_PACKBUILDER__MUTEX_OP(pb,mtx,op) GIT_UNUSED(pb)
+
+#endif /* GIT_THREADS */
+
+#define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock)
+#define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock)
+#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
+#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
+
+static unsigned name_hash(const char *name)
+{
+	unsigned c, hash = 0;
+
+	if (!name)
+		return 0;
+
+	/*
+	 * This effectively just creates a sortable number from the
+	 * last sixteen non-whitespace characters. Last characters
+	 * count "most", so things that end in ".c" sort together.
+	 */
+	while ((c = *name++) != 0) {
+		if (git__isspace(c))
+			continue;
+		hash = (hash >> 2) + (c << 24);
+	}
+	return hash;
+}
+
+static int packbuilder_config(git_packbuilder *pb)
+{
+	git_config *config;
+	int ret;
+	int64_t val;
+
+	if (git_repository_config__weakptr(&config, pb->repo) < 0)
+		return -1;
+
+#define config_get(KEY,DST,DFLT) do { \
+	ret = git_config_get_int64(&val, config, KEY); \
+	if (!ret) (DST) = val; \
+	else if (ret == GIT_ENOTFOUND) (DST) = (DFLT); \
+	else if (ret < 0) return -1; } while (0)
+
+	config_get("pack.deltaCacheSize", pb->max_delta_cache_size,
+		   GIT_PACK_DELTA_CACHE_SIZE);
+	config_get("pack.deltaCacheLimit", pb->cache_max_small_delta_size,
+		   GIT_PACK_DELTA_CACHE_LIMIT);
+	config_get("pack.deltaCacheSize", pb->big_file_threshold,
+		   GIT_PACK_BIG_FILE_THRESHOLD);
+	config_get("pack.windowMemory", pb->window_memory_limit, 0);
+
+#undef config_get
+
+	return 0;
+}
+
+int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
+{
+	git_packbuilder *pb;
+
+	*out = NULL;
+
+	pb = git__calloc(1, sizeof(*pb));
+	GITERR_CHECK_ALLOC(pb);
+
+	pb->object_ix = git_oidmap_alloc();
+
+	if (!pb->object_ix)
+		goto on_error;
+
+	pb->repo = repo;
+	pb->nr_threads = 1; /* do not spawn any thread by default */
+
+	if (git_hash_ctx_init(&pb->ctx) < 0 ||
+		git_repository_odb(&pb->odb, repo) < 0 ||
+		packbuilder_config(pb) < 0)
+		goto on_error;
+
+#ifdef GIT_THREADS
+
+	if (git_mutex_init(&pb->cache_mutex) ||
+		git_mutex_init(&pb->progress_mutex) ||
+		git_cond_init(&pb->progress_cond))
+		goto on_error;
+
+#endif
+
+	*out = pb;
+	return 0;
+
+on_error:
+	git_packbuilder_free(pb);
+	return -1;
+}
+
+unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n)
+{
+	assert(pb);
+
+#ifdef GIT_THREADS
+	pb->nr_threads = n;
+#else
+	GIT_UNUSED(n);
+	assert(1 == pb->nr_threads);
+#endif
+
+	return pb->nr_threads;
+}
+
+static void rehash(git_packbuilder *pb)
+{
+	git_pobject *po;
+	khiter_t pos;
+	unsigned int i;
+	int ret;
+
+	kh_clear(oid, pb->object_ix);
+	for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) {
+		pos = kh_put(oid, pb->object_ix, &po->id, &ret);
+		kh_value(pb->object_ix, pos) = po;
+	}
+}
+
+int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
+			   const char *name)
+{
+	git_pobject *po;
+	khiter_t pos;
+	int ret;
+
+	assert(pb && oid);
+
+	/* If the object already exists in the hash table, then we don't
+	 * have any work to do */
+	pos = kh_get(oid, pb->object_ix, oid);
+	if (pos != kh_end(pb->object_ix))
+		return 0;
+
+	if (pb->nr_objects >= pb->nr_alloc) {
+		pb->nr_alloc = (pb->nr_alloc + 1024) * 3 / 2;
+		pb->object_list = git__realloc(pb->object_list,
+					       pb->nr_alloc * sizeof(*po));
+		GITERR_CHECK_ALLOC(pb->object_list);
+		rehash(pb);
+	}
+
+	po = pb->object_list + pb->nr_objects;
+	memset(po, 0x0, sizeof(*po));
+
+	if (git_odb_read_header(&po->size, &po->type, pb->odb, oid) < 0)
+		return -1;
+
+	pb->nr_objects++;
+	git_oid_cpy(&po->id, oid);
+	po->hash = name_hash(name);
+
+	pos = kh_put(oid, pb->object_ix, &po->id, &ret);
+	assert(ret != 0);
+	kh_value(pb->object_ix, pos) = po;
+
+	pb->done = false;
+	return 0;
+}
+
+/*
+ * The per-object header is a pretty dense thing, which is
+ *  - first byte: low four bits are "size",
+ *    then three bits of "type",
+ *    with the high bit being "size continues".
+ *  - each byte afterwards: low seven bits are size continuation,
+ *    with the high bit being "size continues"
+ */
+static int gen_pack_object_header(
+		unsigned char *hdr,
+		unsigned long size,
+		git_otype type)
+{
+	unsigned char *hdr_base;
+	unsigned char c;
+
+	assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
+
+	/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
+
+	c = (unsigned char)((type << 4) | (size & 15));
+	size >>= 4;
+	hdr_base = hdr;
+
+	while (size) {
+		*hdr++ = c | 0x80;
+		c = size & 0x7f;
+		size >>= 7;
+	}
+	*hdr++ = c;
+
+	return (int)(hdr - hdr_base);
+}
+
+static int get_delta(void **out, git_odb *odb, git_pobject *po)
+{
+	git_odb_object *src = NULL, *trg = NULL;
+	unsigned long delta_size;
+	void *delta_buf;
+
+	*out = NULL;
+
+	if (git_odb_read(&src, odb, &po->delta->id) < 0 ||
+	    git_odb_read(&trg, odb, &po->id) < 0)
+		goto on_error;
+
+	delta_buf = git_delta(
+		git_odb_object_data(src), (unsigned long)git_odb_object_size(src),
+		git_odb_object_data(trg), (unsigned long)git_odb_object_size(trg),
+		&delta_size, 0);
+
+	if (!delta_buf || delta_size != po->delta_size) {
+		giterr_set(GITERR_INVALID, "Delta size changed");
+		goto on_error;
+	}
+
+	*out = delta_buf;
+
+	git_odb_object_free(src);
+	git_odb_object_free(trg);
+	return 0;
+
+on_error:
+	git_odb_object_free(src);
+	git_odb_object_free(trg);
+	return -1;
+}
+
+static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
+{
+	git_odb_object *obj = NULL;
+	git_buf zbuf = GIT_BUF_INIT;
+	git_otype type;
+	unsigned char hdr[10];
+	unsigned int hdr_len;
+	unsigned long size;
+	void *data;
+
+	if (po->delta) {
+		if (po->delta_data)
+			data = po->delta_data;
+		else if (get_delta(&data, pb->odb, po) < 0)
+				goto on_error;
+		size = po->delta_size;
+		type = GIT_OBJ_REF_DELTA;
+	} else {
+		if (git_odb_read(&obj, pb->odb, &po->id))
+			goto on_error;
+
+		data = (void *)git_odb_object_data(obj);
+		size = (unsigned long)git_odb_object_size(obj);
+		type = git_odb_object_type(obj);
+	}
+
+	/* Write header */
+	hdr_len = gen_pack_object_header(hdr, size, type);
+
+	if (git_buf_put(buf, (char *)hdr, hdr_len) < 0)
+		goto on_error;
+
+	if (git_hash_update(&pb->ctx, hdr, hdr_len) < 0)
+		goto on_error;
+
+	if (type == GIT_OBJ_REF_DELTA) {
+		if (git_buf_put(buf, (char *)po->delta->id.id, GIT_OID_RAWSZ) < 0 ||
+			git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ) < 0)
+			goto on_error;
+	}
+
+	/* Write data */
+	if (po->z_delta_size)
+		size = po->z_delta_size;
+	else if (git__compress(&zbuf, data, size) < 0)
+		goto on_error;
+	else {
+		if (po->delta)
+			git__free(data);
+		data = zbuf.ptr;
+		size = (unsigned long)zbuf.size;
+	}
+
+	if (git_buf_put(buf, data, size) < 0 ||
+		git_hash_update(&pb->ctx, data, size) < 0)
+		goto on_error;
+
+	if (po->delta_data)
+		git__free(po->delta_data);
+
+	git_odb_object_free(obj);
+	git_buf_free(&zbuf);
+
+	pb->nr_written++;
+	return 0;
+
+on_error:
+	git_odb_object_free(obj);
+	git_buf_free(&zbuf);
+	return -1;
+}
+
+enum write_one_status {
+	WRITE_ONE_SKIP = -1, /* already written */
+	WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+	WRITE_ONE_WRITTEN = 1, /* normal */
+	WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static int write_one(git_buf *buf, git_packbuilder *pb, git_pobject *po,
+		     enum write_one_status *status)
+{
+	if (po->recursing) {
+		*status = WRITE_ONE_RECURSIVE;
+		return 0;
+	} else if (po->written) {
+		*status = WRITE_ONE_SKIP;
+		return 0;
+	}
+
+	if (po->delta) {
+		po->recursing = 1;
+		if (write_one(buf, pb, po->delta, status) < 0)
+			return -1;
+		switch (*status) {
+		case WRITE_ONE_RECURSIVE:
+			/* we cannot depend on this one */
+			po->delta = NULL;
+			break;
+		default:
+			break;
+		}
+	}
+
+	po->written = 1;
+	po->recursing = 0;
+	return write_object(buf, pb, po);
+}
+
+GIT_INLINE(void) add_to_write_order(git_pobject **wo, unsigned int *endp,
+				    git_pobject *po)
+{
+	if (po->filled)
+		return;
+	wo[(*endp)++] = po;
+	po->filled = 1;
+}
+
+static void add_descendants_to_write_order(git_pobject **wo, unsigned int *endp,
+					   git_pobject *po)
+{
+	int add_to_order = 1;
+	while (po) {
+		if (add_to_order) {
+			git_pobject *s;
+			/* add this node... */
+			add_to_write_order(wo, endp, po);
+			/* all its siblings... */
+			for (s = po->delta_sibling; s; s = s->delta_sibling) {
+				add_to_write_order(wo, endp, s);
+			}
+		}
+		/* drop down a level to add left subtree nodes if possible */
+		if (po->delta_child) {
+			add_to_order = 1;
+			po = po->delta_child;
+		} else {
+			add_to_order = 0;
+			/* our sibling might have some children, it is next */
+			if (po->delta_sibling) {
+				po = po->delta_sibling;
+				continue;
+			}
+			/* go back to our parent node */
+			po = po->delta;
+			while (po && !po->delta_sibling) {
+				/* we're on the right side of a subtree, keep
+				 * going up until we can go right again */
+				po = po->delta;
+			}
+			if (!po) {
+				/* done- we hit our original root node */
+				return;
+			}
+			/* pass it off to sibling at this level */
+			po = po->delta_sibling;
+		}
+	};
+}
+
+static void add_family_to_write_order(git_pobject **wo, unsigned int *endp,
+				      git_pobject *po)
+{
+	git_pobject *root;
+
+	for (root = po; root->delta; root = root->delta)
+		; /* nothing */
+	add_descendants_to_write_order(wo, endp, root);
+}
+
+static int cb_tag_foreach(const char *name, git_oid *oid, void *data)
+{
+	git_packbuilder *pb = data;
+	git_pobject *po;
+	khiter_t pos;
+
+	GIT_UNUSED(name);
+
+	pos = kh_get(oid, pb->object_ix, oid);
+	if (pos == kh_end(pb->object_ix))
+		return 0;
+
+	po = kh_value(pb->object_ix, pos);
+	po->tagged = 1;
+
+	/* TODO: peel objects */
+
+	return 0;
+}
+
+static git_pobject **compute_write_order(git_packbuilder *pb)
+{
+	unsigned int i, wo_end, last_untagged;
+
+	git_pobject **wo = git__malloc(sizeof(*wo) * pb->nr_objects);
+
+	for (i = 0; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		po->tagged = 0;
+		po->filled = 0;
+		po->delta_child = NULL;
+		po->delta_sibling = NULL;
+	}
+
+	/*
+	 * Fully connect delta_child/delta_sibling network.
+	 * Make sure delta_sibling is sorted in the original
+	 * recency order.
+	 */
+	for (i = pb->nr_objects; i > 0;) {
+		git_pobject *po = &pb->object_list[--i];
+		if (!po->delta)
+			continue;
+		/* Mark me as the first child */
+		po->delta_sibling = po->delta->delta_child;
+		po->delta->delta_child = po;
+	}
+
+	/*
+	 * Mark objects that are at the tip of tags.
+	 */
+	if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0)
+		return NULL;
+
+	/*
+	 * Give the objects in the original recency order until
+	 * we see a tagged tip.
+	 */
+	for (i = wo_end = 0; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		if (po->tagged)
+			break;
+		add_to_write_order(wo, &wo_end, po);
+	}
+	last_untagged = i;
+
+	/*
+	 * Then fill all the tagged tips.
+	 */
+	for (; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		if (po->tagged)
+			add_to_write_order(wo, &wo_end, po);
+	}
+
+	/*
+	 * And then all remaining commits and tags.
+	 */
+	for (i = last_untagged; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		if (po->type != GIT_OBJ_COMMIT &&
+		    po->type != GIT_OBJ_TAG)
+			continue;
+		add_to_write_order(wo, &wo_end, po);
+	}
+
+	/*
+	 * And then all the trees.
+	 */
+	for (i = last_untagged; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		if (po->type != GIT_OBJ_TREE)
+			continue;
+		add_to_write_order(wo, &wo_end, po);
+	}
+
+	/*
+	 * Finally all the rest in really tight order
+	 */
+	for (i = last_untagged; i < pb->nr_objects; i++) {
+		git_pobject *po = pb->object_list + i;
+		if (!po->filled)
+			add_family_to_write_order(wo, &wo_end, po);
+	}
+
+	if (wo_end != pb->nr_objects) {
+		giterr_set(GITERR_INVALID, "invalid write order");
+		return NULL;
+	}
+
+	return wo;
+}
+
+static int write_pack(git_packbuilder *pb,
+		      int (*cb)(void *buf, size_t size, void *data),
+		      void *data)
+{
+	git_pobject **write_order;
+	git_pobject *po;
+	git_buf buf = GIT_BUF_INIT;
+	enum write_one_status status;
+	struct git_pack_header ph;
+	unsigned int i = 0;
+
+	write_order = compute_write_order(pb);
+	if (write_order == NULL)
+		goto on_error;
+
+	/* Write pack header */
+	ph.hdr_signature = htonl(PACK_SIGNATURE);
+	ph.hdr_version = htonl(PACK_VERSION);
+	ph.hdr_entries = htonl(pb->nr_objects);
+
+	if (cb(&ph, sizeof(ph), data) < 0)
+		goto on_error;
+
+	if (git_hash_update(&pb->ctx, &ph, sizeof(ph)) < 0)
+		goto on_error;
+
+	pb->nr_remaining = pb->nr_objects;
+	do {
+		pb->nr_written = 0;
+		for ( ; i < pb->nr_objects; ++i) {
+			po = write_order[i];
+			if (write_one(&buf, pb, po, &status) < 0)
+				goto on_error;
+			if (cb(buf.ptr, buf.size, data) < 0)
+				goto on_error;
+			git_buf_clear(&buf);
+		}
+
+		pb->nr_remaining -= pb->nr_written;
+	} while (pb->nr_remaining && i < pb->nr_objects);
+
+	git__free(write_order);
+	git_buf_free(&buf);
+
+	if (git_hash_final(&pb->pack_oid, &pb->ctx) < 0)
+		goto on_error;
+
+	return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
+
+on_error:
+	git__free(write_order);
+	git_buf_free(&buf);
+	return -1;
+}
+
+static int write_pack_buf(void *buf, size_t size, void *data)
+{
+	git_buf *b = (git_buf *)data;
+	return git_buf_put(b, buf, size);
+}
+
+static int write_pack_to_file(void *buf, size_t size, void *data)
+{
+	git_filebuf *file = (git_filebuf *)data;
+	return git_filebuf_write(file, buf, size);
+}
+
+static int write_pack_file(git_packbuilder *pb, const char *path)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+
+	if (git_filebuf_open(&file, path, 0) < 0 ||
+	    write_pack(pb, &write_pack_to_file, &file) < 0 ||
+	    git_filebuf_commit(&file, GIT_PACK_FILE_MODE) < 0) {
+		git_filebuf_cleanup(&file);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int type_size_sort(const void *_a, const void *_b)
+{
+	const git_pobject *a = (git_pobject *)_a;
+	const git_pobject *b = (git_pobject *)_b;
+
+	if (a->type > b->type)
+		return -1;
+	if (a->type < b->type)
+		return 1;
+	if (a->hash > b->hash)
+		return -1;
+	if (a->hash < b->hash)
+		return 1;
+	/*
+	 * TODO
+	 *
+	if (a->preferred_base > b->preferred_base)
+		return -1;
+	if (a->preferred_base < b->preferred_base)
+		return 1;
+	*/
+	if (a->size > b->size)
+		return -1;
+	if (a->size < b->size)
+		return 1;
+	return a < b ? -1 : (a > b); /* newest first */
+}
+
+static int delta_cacheable(git_packbuilder *pb, unsigned long src_size,
+			   unsigned long trg_size, unsigned long delta_size)
+{
+	if (pb->max_delta_cache_size &&
+		pb->delta_cache_size + delta_size > pb->max_delta_cache_size)
+		return 0;
+
+	if (delta_size < pb->cache_max_small_delta_size)
+		return 1;
+
+	/* cache delta, if objects are large enough compared to delta size */
+	if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
+		return 1;
+
+	return 0;
+}
+
+static int try_delta(git_packbuilder *pb, struct unpacked *trg,
+		     struct unpacked *src, unsigned int max_depth,
+		     unsigned long *mem_usage, int *ret)
+{
+	git_pobject *trg_object = trg->object;
+	git_pobject *src_object = src->object;
+	git_odb_object *obj;
+	unsigned long trg_size, src_size, delta_size,
+		      sizediff, max_size, sz;
+	unsigned int ref_depth;
+	void *delta_buf;
+
+	/* Don't bother doing diffs between different types */
+	if (trg_object->type != src_object->type) {
+		*ret = -1;
+		return 0;
+	}
+
+	*ret = 0;
+
+	/* TODO: support reuse-delta */
+
+	/* Let's not bust the allowed depth. */
+	if (src->depth >= max_depth)
+		return 0;
+
+	/* Now some size filtering heuristics. */
+	trg_size = (unsigned long)trg_object->size;
+	if (!trg_object->delta) {
+		max_size = trg_size/2 - 20;
+		ref_depth = 1;
+	} else {
+		max_size = trg_object->delta_size;
+		ref_depth = trg->depth;
+	}
+
+	max_size = (uint64_t)max_size * (max_depth - src->depth) /
+					(max_depth - ref_depth + 1);
+	if (max_size == 0)
+		return 0;
+
+	src_size = (unsigned long)src_object->size;
+	sizediff = src_size < trg_size ? trg_size - src_size : 0;
+	if (sizediff >= max_size)
+		return 0;
+	if (trg_size < src_size / 32)
+		return 0;
+
+	/* Load data if not already done */
+	if (!trg->data) {
+		if (git_odb_read(&obj, pb->odb, &trg_object->id) < 0)
+			return -1;
+
+		sz = (unsigned long)git_odb_object_size(obj);
+		trg->data = git__malloc(sz);
+		GITERR_CHECK_ALLOC(trg->data);
+		memcpy(trg->data, git_odb_object_data(obj), sz);
+
+		git_odb_object_free(obj);
+
+		if (sz != trg_size) {
+			giterr_set(GITERR_INVALID,
+				   "Inconsistent target object length");
+			return -1;
+		}
+
+		*mem_usage += sz;
+	}
+	if (!src->data) {
+		if (git_odb_read(&obj, pb->odb, &src_object->id) < 0)
+			return -1;
+
+		sz = (unsigned long)git_odb_object_size(obj);
+		src->data = git__malloc(sz);
+		GITERR_CHECK_ALLOC(src->data);
+		memcpy(src->data, git_odb_object_data(obj), sz);
+
+		git_odb_object_free(obj);
+
+		if (sz != src_size) {
+			giterr_set(GITERR_INVALID,
+				   "Inconsistent source object length");
+			return -1;
+		}
+
+		*mem_usage += sz;
+	}
+	if (!src->index) {
+		src->index = git_delta_create_index(src->data, src_size);
+		if (!src->index)
+			return 0; /* suboptimal pack - out of memory */
+
+		*mem_usage += git_delta_sizeof_index(src->index);
+	}
+
+	delta_buf = git_delta_create(src->index, trg->data, trg_size,
+				     &delta_size, max_size);
+	if (!delta_buf)
+		return 0;
+
+	if (trg_object->delta) {
+		/* Prefer only shallower same-sized deltas. */
+		if (delta_size == trg_object->delta_size &&
+		    src->depth + 1 >= trg->depth) {
+			git__free(delta_buf);
+			return 0;
+		}
+	}
+
+	git_packbuilder__cache_lock(pb);
+	if (trg_object->delta_data) {
+		git__free(trg_object->delta_data);
+		pb->delta_cache_size -= trg_object->delta_size;
+		trg_object->delta_data = NULL;
+	}
+	if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
+		pb->delta_cache_size += delta_size;
+		git_packbuilder__cache_unlock(pb);
+
+		trg_object->delta_data = git__realloc(delta_buf, delta_size);
+		GITERR_CHECK_ALLOC(trg_object->delta_data);
+	} else {
+		/* create delta when writing the pack */
+		git_packbuilder__cache_unlock(pb);
+		git__free(delta_buf);
+	}
+
+	trg_object->delta = src_object;
+	trg_object->delta_size = delta_size;
+	trg->depth = src->depth + 1;
+
+	*ret = 1;
+	return 0;
+}
+
+static unsigned int check_delta_limit(git_pobject *me, unsigned int n)
+{
+	git_pobject *child = me->delta_child;
+	unsigned int m = n;
+
+	while (child) {
+		unsigned int c = check_delta_limit(child, n + 1);
+		if (m < c)
+			m = c;
+		child = child->delta_sibling;
+	}
+	return m;
+}
+
+static unsigned long free_unpacked(struct unpacked *n)
+{
+	unsigned long freed_mem = git_delta_sizeof_index(n->index);
+	git_delta_free_index(n->index);
+	n->index = NULL;
+	if (n->data) {
+		freed_mem += (unsigned long)n->object->size;
+		git__free(n->data);
+		n->data = NULL;
+	}
+	n->object = NULL;
+	n->depth = 0;
+	return freed_mem;
+}
+
+static int find_deltas(git_packbuilder *pb, git_pobject **list,
+		       unsigned int *list_size, unsigned int window,
+		       unsigned int depth)
+{
+	git_pobject *po;
+	git_buf zbuf = GIT_BUF_INIT;
+	struct unpacked *array;
+	uint32_t idx = 0, count = 0;
+	unsigned long mem_usage = 0;
+	unsigned int i;
+	int error = -1;
+
+	array = git__calloc(window, sizeof(struct unpacked));
+	GITERR_CHECK_ALLOC(array);
+
+	for (;;) {
+		struct unpacked *n = array + idx;
+		unsigned int max_depth;
+		int j, best_base = -1;
+
+		git_packbuilder__progress_lock(pb);
+		if (!*list_size) {
+			git_packbuilder__progress_unlock(pb);
+			break;
+		}
+
+		po = *list++;
+		(*list_size)--;
+		git_packbuilder__progress_unlock(pb);
+
+		mem_usage -= free_unpacked(n);
+		n->object = po;
+
+		while (pb->window_memory_limit &&
+		       mem_usage > pb->window_memory_limit &&
+		       count > 1) {
+			uint32_t tail = (idx + window - count) % window;
+			mem_usage -= free_unpacked(array + tail);
+			count--;
+		}
+
+		/*
+		 * If the current object is at pack edge, take the depth the
+		 * objects that depend on the current object into account
+		 * otherwise they would become too deep.
+		 */
+		max_depth = depth;
+		if (po->delta_child) {
+			max_depth -= check_delta_limit(po, 0);
+			if (max_depth <= 0)
+				goto next;
+		}
+
+		j = window;
+		while (--j > 0) {
+			int ret;
+			uint32_t other_idx = idx + j;
+			struct unpacked *m;
+
+			if (other_idx >= window)
+				other_idx -= window;
+
+			m = array + other_idx;
+			if (!m->object)
+				break;
+
+			if (try_delta(pb, n, m, max_depth, &mem_usage, &ret) < 0)
+				goto on_error;
+			if (ret < 0)
+				break;
+			else if (ret > 0)
+				best_base = other_idx;
+		}
+
+		/*
+		 * If we decided to cache the delta data, then it is best
+		 * to compress it right away.  First because we have to do
+		 * it anyway, and doing it here while we're threaded will
+		 * save a lot of time in the non threaded write phase,
+		 * as well as allow for caching more deltas within
+		 * the same cache size limit.
+		 * ...
+		 * But only if not writing to stdout, since in that case
+		 * the network is most likely throttling writes anyway,
+		 * and therefore it is best to go to the write phase ASAP
+		 * instead, as we can afford spending more time compressing
+		 * between writes at that moment.
+		 */
+		if (po->delta_data) {
+			if (git__compress(&zbuf, po->delta_data, po->delta_size) < 0)
+				goto on_error;
+
+			git__free(po->delta_data);
+			po->delta_data = git__malloc(zbuf.size);
+			GITERR_CHECK_ALLOC(po->delta_data);
+
+			memcpy(po->delta_data, zbuf.ptr, zbuf.size);
+			po->z_delta_size = (unsigned long)zbuf.size;
+			git_buf_clear(&zbuf);
+
+			git_packbuilder__cache_lock(pb);
+			pb->delta_cache_size -= po->delta_size;
+			pb->delta_cache_size += po->z_delta_size;
+			git_packbuilder__cache_unlock(pb);
+		}
+
+		/*
+		 * If we made n a delta, and if n is already at max
+		 * depth, leaving it in the window is pointless.  we
+		 * should evict it first.
+		 */
+		if (po->delta && max_depth <= n->depth)
+			continue;
+
+		/*
+		 * Move the best delta base up in the window, after the
+		 * currently deltified object, to keep it longer.  It will
+		 * be the first base object to be attempted next.
+		 */
+		if (po->delta) {
+			struct unpacked swap = array[best_base];
+			int dist = (window + idx - best_base) % window;
+			int dst = best_base;
+			while (dist--) {
+				int src = (dst + 1) % window;
+				array[dst] = array[src];
+				dst = src;
+			}
+			array[dst] = swap;
+		}
+
+		next:
+		idx++;
+		if (count + 1 < window)
+			count++;
+		if (idx >= window)
+			idx = 0;
+	}
+	error = 0;
+
+on_error:
+	for (i = 0; i < window; ++i) {
+		git__free(array[i].index);
+		git__free(array[i].data);
+	}
+	git__free(array);
+	git_buf_free(&zbuf);
+
+	return error;
+}
+
+#ifdef GIT_THREADS
+
+struct thread_params {
+	git_thread thread;
+	git_packbuilder *pb;
+
+	git_pobject **list;
+
+	git_cond cond;
+	git_mutex mutex;
+
+	unsigned int list_size;
+	unsigned int remaining;
+
+	int window;
+	int depth;
+	int working;
+	int data_ready;
+};
+
+static void *threaded_find_deltas(void *arg)
+{
+	struct thread_params *me = arg;
+
+	while (me->remaining) {
+		if (find_deltas(me->pb, me->list, &me->remaining,
+				me->window, me->depth) < 0) {
+			; /* TODO */
+		}
+
+		git_packbuilder__progress_lock(me->pb);
+		me->working = 0;
+		git_cond_signal(&me->pb->progress_cond);
+		git_packbuilder__progress_unlock(me->pb);
+
+		if (git_mutex_lock(&me->mutex)) {
+			giterr_set(GITERR_THREAD, "unable to lock packfile condition mutex");
+			return NULL;
+		}
+
+		while (!me->data_ready)
+			git_cond_wait(&me->cond, &me->mutex);
+
+		/*
+		 * We must not set ->data_ready before we wait on the
+		 * condition because the main thread may have set it to 1
+		 * before we get here. In order to be sure that new
+		 * work is available if we see 1 in ->data_ready, it
+		 * was initialized to 0 before this thread was spawned
+		 * and we reset it to 0 right away.
+		 */
+		me->data_ready = 0;
+		git_mutex_unlock(&me->mutex);
+	}
+	/* leave ->working 1 so that this doesn't get more work assigned */
+	return NULL;
+}
+
+static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
+			  unsigned int list_size, unsigned int window,
+			  unsigned int depth)
+{
+	struct thread_params *p;
+	int i, ret, active_threads = 0;
+
+	if (!pb->nr_threads)
+		pb->nr_threads = git_online_cpus();
+
+	if (pb->nr_threads <= 1) {
+		find_deltas(pb, list, &list_size, window, depth);
+		return 0;
+	}
+
+	p = git__malloc(pb->nr_threads * sizeof(*p));
+	GITERR_CHECK_ALLOC(p);
+
+	/* Partition the work among the threads */
+	for (i = 0; i < pb->nr_threads; ++i) {
+		unsigned sub_size = list_size / (pb->nr_threads - i);
+
+		/* don't use too small segments or no deltas will be found */
+		if (sub_size < 2*window && i+1 < pb->nr_threads)
+			sub_size = 0;
+
+		p[i].pb = pb;
+		p[i].window = window;
+		p[i].depth = depth;
+		p[i].working = 1;
+		p[i].data_ready = 0;
+
+		/* try to split chunks on "path" boundaries */
+		while (sub_size && sub_size < list_size &&
+		       list[sub_size]->hash &&
+		       list[sub_size]->hash == list[sub_size-1]->hash)
+			sub_size++;
+
+		p[i].list = list;
+		p[i].list_size = sub_size;
+		p[i].remaining = sub_size;
+
+		list += sub_size;
+		list_size -= sub_size;
+	}
+
+	/* Start work threads */
+	for (i = 0; i < pb->nr_threads; ++i) {
+		if (!p[i].list_size)
+			continue;
+
+		git_mutex_init(&p[i].mutex);
+		git_cond_init(&p[i].cond);
+
+		ret = git_thread_create(&p[i].thread, NULL,
+					threaded_find_deltas, &p[i]);
+		if (ret) {
+			giterr_set(GITERR_THREAD, "unable to create thread");
+			return -1;
+		}
+		active_threads++;
+	}
+
+	/*
+	 * Now let's wait for work completion.  Each time a thread is done
+	 * with its work, we steal half of the remaining work from the
+	 * thread with the largest number of unprocessed objects and give
+	 * it to that newly idle thread.  This ensure good load balancing
+	 * until the remaining object list segments are simply too short
+	 * to be worth splitting anymore.
+	 */
+	while (active_threads) {
+		struct thread_params *target = NULL;
+		struct thread_params *victim = NULL;
+		unsigned sub_size = 0;
+
+		/* Start by locating a thread that has transitioned its
+		 * 'working' flag from 1 -> 0. This indicates that it is
+		 * ready to receive more work using our work-stealing
+		 * algorithm. */
+		git_packbuilder__progress_lock(pb);
+		for (;;) {
+			for (i = 0; !target && i < pb->nr_threads; i++)
+				if (!p[i].working)
+					target = &p[i];
+			if (target)
+				break;
+			git_cond_wait(&pb->progress_cond, &pb->progress_mutex);
+		}
+
+		/* At this point we hold the progress lock and have located
+		 * a thread to receive more work. We still need to locate a
+		 * thread from which to steal work (the victim). */
+		for (i = 0; i < pb->nr_threads; i++)
+			if (p[i].remaining > 2*window &&
+			    (!victim || victim->remaining < p[i].remaining))
+				victim = &p[i];
+
+		if (victim) {
+			sub_size = victim->remaining / 2;
+			list = victim->list + victim->list_size - sub_size;
+			while (sub_size && list[0]->hash &&
+			       list[0]->hash == list[-1]->hash) {
+				list++;
+				sub_size--;
+			}
+			if (!sub_size) {
+				/*
+				 * It is possible for some "paths" to have
+				 * so many objects that no hash boundary
+				 * might be found.  Let's just steal the
+				 * exact half in that case.
+				 */
+				sub_size = victim->remaining / 2;
+				list -= sub_size;
+			}
+			target->list = list;
+			victim->list_size -= sub_size;
+			victim->remaining -= sub_size;
+		}
+		target->list_size = sub_size;
+		target->remaining = sub_size;
+		target->working = 1;
+		git_packbuilder__progress_unlock(pb);
+
+		if (git_mutex_lock(&target->mutex)) {
+			giterr_set(GITERR_THREAD, "unable to lock packfile condition mutex");
+			git__free(p);
+			return -1;
+		}
+
+		target->data_ready = 1;
+		git_cond_signal(&target->cond);
+		git_mutex_unlock(&target->mutex);
+
+		if (!sub_size) {
+			git_thread_join(target->thread, NULL);
+			git_cond_free(&target->cond);
+			git_mutex_free(&target->mutex);
+			active_threads--;
+		}
+	}
+
+	git__free(p);
+	return 0;
+}
+
+#else
+#define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d)
+#endif
+
+static int prepare_pack(git_packbuilder *pb)
+{
+	git_pobject **delta_list;
+	unsigned int i, n = 0;
+
+	if (pb->nr_objects == 0 || pb->done)
+		return 0; /* nothing to do */
+
+	delta_list = git__malloc(pb->nr_objects * sizeof(*delta_list));
+	GITERR_CHECK_ALLOC(delta_list);
+
+	for (i = 0; i < pb->nr_objects; ++i) {
+		git_pobject *po = pb->object_list + i;
+
+		/* Make sure the item is within our size limits */
+		if (po->size < 50 || po->size > pb->big_file_threshold)
+			continue;
+
+		delta_list[n++] = po;
+	}
+
+	if (n > 1) {
+		git__tsort((void **)delta_list, n, type_size_sort);
+		if (ll_find_deltas(pb, delta_list, n,
+				   GIT_PACK_WINDOW + 1,
+				   GIT_PACK_DEPTH) < 0) {
+			git__free(delta_list);
+			return -1;
+		}
+	}
+
+	pb->done = true;
+	git__free(delta_list);
+	return 0;
+}
+
+#define PREPARE_PACK if (prepare_pack(pb) < 0) { return -1; }
+
+int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t size, void *payload), void *payload)
+{
+	PREPARE_PACK;
+	return write_pack(pb, cb, payload);
+}
+
+int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
+{
+	PREPARE_PACK;
+	return write_pack(pb, &write_pack_buf, buf);
+}
+
+int git_packbuilder_write(git_packbuilder *pb, const char *path)
+{
+	PREPARE_PACK;
+	return write_pack_file(pb, path);
+}
+
+#undef PREPARE_PACK
+
+static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload)
+{
+	struct tree_walk_context *ctx = payload;
+
+	/* A commit inside a tree represents a submodule commit and should be skipped. */
+	if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT)
+		return 0;
+
+	if (git_buf_sets(&ctx->buf, root) < 0 ||
+		git_buf_puts(&ctx->buf, git_tree_entry_name(entry)) < 0)
+		return -1;
+
+	return git_packbuilder_insert(ctx->pb,
+		git_tree_entry_id(entry),
+		git_buf_cstr(&ctx->buf));
+}
+
+int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid)
+{
+	git_tree *tree;
+	struct tree_walk_context context = { pb, GIT_BUF_INIT };
+
+	if (git_tree_lookup(&tree, pb->repo, oid) < 0 ||
+	    git_packbuilder_insert(pb, oid, NULL) < 0)
+		return -1;
+
+	if (git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context) < 0) {
+		git_tree_free(tree);
+		git_buf_free(&context.buf);
+		return -1;
+	}
+
+	git_tree_free(tree);
+	git_buf_free(&context.buf);
+	return 0;
+}
+
+uint32_t git_packbuilder_object_count(git_packbuilder *pb)
+{
+	return pb->nr_objects;
+}
+
+uint32_t git_packbuilder_written(git_packbuilder *pb)
+{
+	return pb->nr_written;
+}
+
+void git_packbuilder_free(git_packbuilder *pb)
+{
+	if (pb == NULL)
+		return;
+
+#ifdef GIT_THREADS
+
+	git_mutex_free(&pb->cache_mutex);
+	git_mutex_free(&pb->progress_mutex);
+	git_cond_free(&pb->progress_cond);
+
+#endif
+
+	if (pb->odb)
+		git_odb_free(pb->odb);
+
+	if (pb->object_ix)
+		git_oidmap_free(pb->object_ix);
+
+	if (pb->object_list)
+		git__free(pb->object_list);
+
+	git_hash_ctx_cleanup(&pb->ctx);
+
+	git__free(pb);
+}
diff --git a/libgit2/src/pack-objects.h b/libgit2/src/pack-objects.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pack-objects.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_pack_objects_h__
+#define INCLUDE_pack_objects_h__
+
+#include "common.h"
+
+#include "buffer.h"
+#include "hash.h"
+#include "oidmap.h"
+#include "netops.h"
+
+#include "git2/oid.h"
+
+#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
+#define GIT_PACK_DEPTH 50 /* max delta depth */
+#define GIT_PACK_DELTA_CACHE_SIZE (256 * 1024 * 1024)
+#define GIT_PACK_DELTA_CACHE_LIMIT 1000
+#define GIT_PACK_BIG_FILE_THRESHOLD (512 * 1024 * 1024)
+
+typedef struct git_pobject {
+	git_oid id;
+	git_otype type;
+	git_off_t offset;
+
+	size_t size;
+
+	unsigned int hash; /* name hint hash */
+
+	struct git_pobject *delta; /* delta base object */
+	struct git_pobject *delta_child; /* deltified objects who bases me */
+	struct git_pobject *delta_sibling; /* other deltified objects
+					    * who uses the same base as
+					    * me */
+
+	void *delta_data;
+	unsigned long delta_size;
+	unsigned long z_delta_size;
+
+	int written:1,
+	    recursing:1,
+	    tagged:1,
+	    filled:1;
+} git_pobject;
+
+struct git_packbuilder {
+	git_repository *repo; /* associated repository */
+	git_odb *odb; /* associated object database */
+
+	git_hash_ctx ctx;
+
+	uint32_t nr_objects,
+		 nr_alloc,
+		 nr_written,
+		 nr_remaining;
+
+	git_pobject *object_list;
+
+	git_oidmap *object_ix;
+
+	git_oid pack_oid; /* hash of written pack */
+
+	/* synchronization objects */
+	git_mutex cache_mutex;
+	git_mutex progress_mutex;
+	git_cond progress_cond;
+
+	/* configs */
+	uint64_t delta_cache_size;
+	uint64_t max_delta_cache_size;
+	uint64_t cache_max_small_delta_size;
+	uint64_t big_file_threshold;
+	uint64_t window_memory_limit;
+
+	int nr_threads; /* nr of threads to use */
+
+	bool done;
+};
+
+int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
+
+#endif /* INCLUDE_pack_objects_h__ */
diff --git a/libgit2/src/pack.c b/libgit2/src/pack.c
--- a/libgit2/src/pack.c
+++ b/libgit2/src/pack.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -38,7 +38,7 @@
 		git_oid *found_oid,
 		struct git_pack_file *p,
 		const git_oid *short_oid,
-		unsigned int len);
+		size_t len);
 
 static int packfile_error(const char *message)
 {
@@ -46,6 +46,134 @@
 	return -1;
 }
 
+/********************
+ * Delta base cache
+ ********************/
+
+static git_pack_cache_entry *new_cache_object(git_rawobj *source)
+{
+	git_pack_cache_entry *e = git__calloc(1, sizeof(git_pack_cache_entry));
+	if (!e)
+		return NULL;
+
+	memcpy(&e->raw, source, sizeof(git_rawobj));
+
+	return e;
+}
+
+static void free_cache_object(void *o)
+{
+	git_pack_cache_entry *e = (git_pack_cache_entry *)o;
+
+	if (e != NULL) {
+		assert(e->refcount.val == 0);
+		git__free(e->raw.data);
+		git__free(e);
+	}
+}
+
+static void cache_free(git_pack_cache *cache)
+{
+	khiter_t k;
+
+	if (cache->entries) {
+		for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
+			if (kh_exist(cache->entries, k))
+				free_cache_object(kh_value(cache->entries, k));
+		}
+
+		git_offmap_free(cache->entries);
+		git_mutex_free(&cache->lock);
+	}
+}
+
+static int cache_init(git_pack_cache *cache)
+{
+	memset(cache, 0, sizeof(git_pack_cache));
+	cache->entries = git_offmap_alloc();
+	GITERR_CHECK_ALLOC(cache->entries);
+	cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT;
+	git_mutex_init(&cache->lock);
+
+	return 0;
+}
+
+static git_pack_cache_entry *cache_get(git_pack_cache *cache, git_off_t offset)
+{
+	khiter_t k;
+	git_pack_cache_entry *entry = NULL;
+
+	if (git_mutex_lock(&cache->lock) < 0)
+		return NULL;
+
+	k = kh_get(off, cache->entries, offset);
+	if (k != kh_end(cache->entries)) { /* found it */
+		entry = kh_value(cache->entries, k);
+		git_atomic_inc(&entry->refcount);
+		entry->last_usage = cache->use_ctr++;
+	}
+	git_mutex_unlock(&cache->lock);
+
+	return entry;
+}
+
+/* Run with the cache lock held */
+static void free_lowest_entry(git_pack_cache *cache)
+{
+	git_pack_cache_entry *entry;
+	khiter_t k;
+
+	for (k = kh_begin(cache->entries); k != kh_end(cache->entries); k++) {
+		if (!kh_exist(cache->entries, k))
+			continue;
+
+		entry = kh_value(cache->entries, k);
+
+		if (entry && entry->refcount.val == 0) {
+			cache->memory_used -= entry->raw.len;
+			kh_del(off, cache->entries, k);
+			free_cache_object(entry);
+		}
+	}
+}
+
+static int cache_add(git_pack_cache *cache, git_rawobj *base, git_off_t offset)
+{
+	git_pack_cache_entry *entry;
+	int error, exists = 0;
+	khiter_t k;
+
+	if (base->len > GIT_PACK_CACHE_SIZE_LIMIT)
+		return -1;
+
+	entry = new_cache_object(base);
+	if (entry) {
+		if (git_mutex_lock(&cache->lock) < 0) {
+			giterr_set(GITERR_OS, "failed to lock cache");
+			return -1;
+		}
+		/* Add it to the cache if nobody else has */
+		exists = kh_get(off, cache->entries, offset) != kh_end(cache->entries);
+		if (!exists) {
+			while (cache->memory_used + base->len > cache->memory_limit)
+				free_lowest_entry(cache);
+
+			k = kh_put(off, cache->entries, offset, &error);
+			assert(error != 0);
+			kh_value(cache->entries, k) = entry;
+			cache->memory_used += entry->raw.len;
+		}
+		git_mutex_unlock(&cache->lock);
+		/* Somebody beat us to adding it into the cache */
+		if (exists) {
+			git__free(entry);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
 /***********************************************************
  *
  * PACK INDEX METHODS
@@ -54,6 +182,10 @@
 
 static void pack_index_free(struct git_pack_file *p)
 {
+	if (p->oids) {
+		git__free(p->oids);
+		p->oids = NULL;
+	}
 	if (p->index_map.data) {
 		git_futils_mmap_free(&p->index_map);
 		p->index_map.data = NULL;
@@ -262,7 +394,7 @@
 	if (base == NULL)
 		return GIT_EBUFS;
 
-	ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
+		ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
 	git_mwindow_close(w_curs);
 	if (ret == GIT_EBUFS)
 		return ret;
@@ -273,6 +405,56 @@
 	return 0;
 }
 
+int git_packfile_resolve_header(
+		size_t *size_p,
+		git_otype *type_p,
+		struct git_pack_file *p,
+		git_off_t offset)
+{
+	git_mwindow *w_curs = NULL;
+	git_off_t curpos = offset;
+	size_t size;
+	git_otype type;
+	git_off_t base_offset;
+	int error;
+
+	error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+	git_mwindow_close(&w_curs);
+	if (error < 0)
+		return error;
+
+	if (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
+		size_t base_size;
+		git_rawobj delta;
+		base_offset = get_delta_base(p, &w_curs, &curpos, type, offset);
+		git_mwindow_close(&w_curs);
+		error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, size, type);
+		git_mwindow_close(&w_curs);
+		if (error < 0)
+			return error;
+		error = git__delta_read_header(delta.data, delta.len, &base_size, size_p);
+		git__free(delta.data);
+		if (error < 0)
+			return error;
+	} else
+		*size_p = size;
+
+	while (type == GIT_OBJ_OFS_DELTA || type == GIT_OBJ_REF_DELTA) {
+		curpos = base_offset;
+		error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+		git_mwindow_close(&w_curs);
+		if (error < 0)
+			return error;
+		if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA)
+			break;
+		base_offset = get_delta_base(p, &w_curs, &curpos, type, base_offset);
+		git_mwindow_close(&w_curs);
+	}
+	*type_p = type;
+
+	return error;
+}
+
 static int packfile_unpack_delta(
 		git_rawobj *obj,
 		struct git_pack_file *p,
@@ -282,9 +464,10 @@
 		git_otype delta_type,
 		git_off_t obj_offset)
 {
-	git_off_t base_offset;
+	git_off_t base_offset, base_key;
 	git_rawobj base, delta;
-	int error;
+	git_pack_cache_entry *cached = NULL;
+	int error, found_base = 0;
 
 	base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
 	git_mwindow_close(w_curs);
@@ -293,32 +476,49 @@
 	if (base_offset < 0) /* must actually be an error code */
 		return (int)base_offset;
 
-	error = git_packfile_unpack(&base, p, &base_offset);
+	if (!p->bases.entries && (cache_init(&p->bases) < 0))
+		return -1;
 
-	/*
-	 * TODO: git.git tries to load the base from other packfiles
-	 * or loose objects.
-	 *
-	 * We'll need to do this in order to support thin packs.
-	 */
-	if (error < 0)
-		return error;
+	base_key = base_offset; /* git_packfile_unpack modifies base_offset */
+	if ((cached = cache_get(&p->bases, base_offset)) != NULL) {
+		memcpy(&base, &cached->raw, sizeof(git_rawobj));
+		found_base = 1;
+	}
 
+	if (!cached) { /* have to inflate it */
+		error = git_packfile_unpack(&base, p, &base_offset);
+
+		/*
+		 * TODO: git.git tries to load the base from other packfiles
+		 * or loose objects.
+		 *
+		 * We'll need to do this in order to support thin packs.
+		 */
+		if (error < 0)
+			return error;
+	}
+
 	error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type);
 	git_mwindow_close(w_curs);
+
 	if (error < 0) {
-		git__free(base.data);
+		if (!found_base)
+			git__free(base.data);
 		return error;
 	}
 
 	obj->type = base.type;
 	error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
+	if (error < 0)
+		goto on_error;
 
-	git__free(base.data);
-	git__free(delta.data);
+	if (found_base)
+		git_atomic_dec(&cached->refcount);
+	else if (cache_add(&p->bases, &base, base_key) < 0)
+		git__free(base.data);
 
-	/* TODO: we might want to cache this shit. eventually */
-	//add_delta_base_cache(p, base_offset, base, base_size, *type);
+on_error:
+	git__free(delta.data);
 
 	return error; /* error set by git__delta_apply */
 }
@@ -387,6 +587,72 @@
 	git__free(ptr);
 }
 
+int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos)
+{
+	int st;
+
+	memset(obj, 0, sizeof(git_packfile_stream));
+	obj->curpos = curpos;
+	obj->p = p;
+	obj->zstream.zalloc = use_git_alloc;
+	obj->zstream.zfree = use_git_free;
+	obj->zstream.next_in = Z_NULL;
+	obj->zstream.next_out = Z_NULL;
+	st = inflateInit(&obj->zstream);
+	if (st != Z_OK) {
+		git__free(obj);
+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+		return -1;
+	}
+
+	return 0;
+}
+
+ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len)
+{
+	unsigned char *in;
+	size_t written;
+	int st;
+
+	if (obj->done)
+		return 0;
+
+	in = pack_window_open(obj->p, &obj->mw, obj->curpos, &obj->zstream.avail_in);
+	if (in == NULL)
+		return GIT_EBUFS;
+
+	obj->zstream.next_out = buffer;
+	obj->zstream.avail_out = (unsigned int)len;
+	obj->zstream.next_in = in;
+
+	st = inflate(&obj->zstream, Z_SYNC_FLUSH);
+	git_mwindow_close(&obj->mw);
+
+	obj->curpos += obj->zstream.next_in - in;
+	written = len - obj->zstream.avail_out;
+
+	if (st != Z_OK && st != Z_STREAM_END) {
+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+		return -1;
+	}
+
+	if (st == Z_STREAM_END)
+		obj->done = 1;
+
+
+	/* If we didn't write anything out but we're not done, we need more data */
+	if (!written && st != Z_STREAM_END)
+		return GIT_EBUFS;
+
+	return written;
+
+}
+
+void git_packfile_stream_free(git_packfile_stream *obj)
+{
+	inflateEnd(&obj->zstream);
+}
+
 int packfile_unpack_compressed(
 	git_rawobj *obj,
 	struct git_pack_file *p,
@@ -494,14 +760,14 @@
 	} else if (type == GIT_OBJ_REF_DELTA) {
 		/* If we have the cooperative cache, search in it first */
 		if (p->has_cache) {
-			int pos;
-			struct git_pack_entry key;
+			khiter_t k;
+			git_oid oid;
 
-			git_oid_fromraw(&key.sha1, base_info);
-			pos = git_vector_bsearch(&p->cache, &key);
-			if (pos >= 0) {
+			git_oid_fromraw(&oid, base_info);
+			k = kh_get(oid, p->idx_cache, &oid);
+			if (k != kh_end(p->idx_cache)) {
 				*curpos += 20;
-				return ((struct git_pack_entry *)git_vector_get(&p->cache, pos))->offset;
+				return ((struct git_pack_entry *)kh_value(p->idx_cache, k))->offset;
 			}
 		}
 		/* The base entry _must_ be in the same pack */
@@ -529,12 +795,14 @@
 }
 
 
-void packfile_free(struct git_pack_file *p)
+void git_packfile_free(struct git_pack_file *p)
 {
 	assert(p);
 
-	/* clear_delta_base_cache(); */
+	cache_free(&p->bases);
+
 	git_mwindow_free_all(&p->mwf);
+	git_mwindow_file_deregister(&p->mwf);
 
 	if (p->mwf.fd != -1)
 		p_close(p->mwf.fd);
@@ -559,8 +827,10 @@
 
 	/* TODO: open with noatime */
 	p->mwf.fd = git_futils_open_ro(p->pack_name);
-	if (p->mwf.fd < 0)
-		return p->mwf.fd;
+	if (p->mwf.fd < 0) {
+		p->mwf.fd = -1;
+		return -1;
+	}
 
 	if (p_fstat(p->mwf.fd, &st) < 0 ||
 		git_mwindow_file_register(&p->mwf) < 0)
@@ -685,12 +955,76 @@
 	}
 }
 
+static int git__memcmp4(const void *a, const void *b) {
+	return memcmp(a, b, 4);
+}
+
+int git_pack_foreach_entry(
+	struct git_pack_file *p,
+	git_odb_foreach_cb cb,
+	void *data)
+{
+	const unsigned char *index = p->index_map.data, *current;
+	uint32_t i;
+
+	if (index == NULL) {
+		int error;
+
+		if ((error = pack_index_open(p)) < 0)
+			return error;
+
+		assert(p->index_map.data);
+
+		index = p->index_map.data;
+	}
+
+	if (p->index_version > 1) {
+		index += 8;
+	}
+
+	index += 4 * 256;
+
+	if (p->oids == NULL) {
+		git_vector offsets, oids;
+		int error;
+
+		if ((error = git_vector_init(&oids, p->num_objects, NULL)))
+			return error;
+
+		if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4)))
+			return error;
+
+		if (p->index_version > 1) {
+			const unsigned char *off = index + 24 * p->num_objects;
+			for (i = 0; i < p->num_objects; i++)
+				git_vector_insert(&offsets, (void*)&off[4 * i]);
+			git_vector_sort(&offsets);
+			git_vector_foreach(&offsets, i, current)
+				git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
+		} else {
+			for (i = 0; i < p->num_objects; i++)
+				git_vector_insert(&offsets, (void*)&index[24 * i]);
+			git_vector_sort(&offsets);
+			git_vector_foreach(&offsets, i, current)
+				git_vector_insert(&oids, (void*)&current[4]);
+		}
+		git_vector_free(&offsets);
+		p->oids = (git_oid **)oids.contents;
+	}
+
+	for (i = 0; i < p->num_objects; i++)
+		if (cb(p->oids[i], data))
+			return GIT_EUSER;
+
+	return 0;
+}
+
 static int pack_entry_find_offset(
 	git_off_t *offset_out,
 	git_oid *found_oid,
 	struct git_pack_file *p,
 	const git_oid *short_oid,
-	unsigned int len)
+	size_t len)
 {
 	const uint32_t *level1_ofs = p->index_map.data;
 	const unsigned char *index = p->index_map.data;
@@ -783,7 +1117,7 @@
 		struct git_pack_entry *e,
 		struct git_pack_file *p,
 		const git_oid *short_oid,
-		unsigned int len)
+		size_t len)
 {
 	git_off_t offset;
 	git_oid found_oid;
diff --git a/libgit2/src/pack.h b/libgit2/src/pack.h
--- a/libgit2/src/pack.h
+++ b/libgit2/src/pack.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,12 +8,15 @@
 #ifndef INCLUDE_pack_h__
 #define INCLUDE_pack_h__
 
+#include <zlib.h>
+
 #include "git2/oid.h"
 
 #include "common.h"
 #include "map.h"
 #include "mwindow.h"
 #include "odb.h"
+#include "oidmap.h"
 
 #define GIT_PACK_FILE_MODE 0444
 
@@ -51,6 +54,28 @@
 	uint32_t idx_version;
 };
 
+typedef struct git_pack_cache_entry {
+	size_t last_usage; /* enough? */
+	git_atomic refcount;
+	git_rawobj raw;
+} git_pack_cache_entry;
+
+#include "offmap.h"
+
+GIT__USE_OFFMAP;
+GIT__USE_OIDMAP;
+
+#define GIT_PACK_CACHE_MEMORY_LIMIT 16 * 1024 * 1024
+#define GIT_PACK_CACHE_SIZE_LIMIT 1024 * 1024 /* don't bother caching anything over 1MB */
+
+typedef struct {
+	size_t memory_used;
+	size_t memory_limit;
+	size_t use_ctr;
+	git_mutex lock;
+	git_offmap *entries;
+} git_pack_cache;
+
 struct git_pack_file {
 	git_mwindow_file mwf;
 	git_map index_map;
@@ -63,8 +88,11 @@
 	git_time_t mtime;
 	unsigned pack_local:1, pack_keep:1, has_cache:1;
 	git_oid sha1;
-	git_vector cache;
+	git_oidmap *idx_cache;
+	git_oid **oids;
 
+	git_pack_cache bases; /* delta base cache */
+
 	/* something like ".git/objects/pack/xxxxx.pack" */
 	char pack_name[GIT_FLEX_ARRAY]; /* more */
 };
@@ -75,6 +103,14 @@
 	struct git_pack_file *p;
 };
 
+typedef struct git_packfile_stream {
+	git_off_t curpos;
+	int done;
+	z_stream zstream;
+	struct git_pack_file *p;
+	git_mwindow *mw;
+} git_packfile_stream;
+
 int git_packfile_unpack_header(
 		size_t *size_p,
 		git_otype *type_p,
@@ -82,6 +118,12 @@
 		git_mwindow **w_curs,
 		git_off_t *curpos);
 
+int git_packfile_resolve_header(
+		size_t *size_p,
+		git_otype *type_p,
+		struct git_pack_file *p,
+		git_off_t offset);
+
 int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);
 int packfile_unpack_compressed(
 	git_rawobj *obj,
@@ -91,16 +133,24 @@
 	size_t size,
 	git_otype type);
 
+int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, git_off_t curpos);
+ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len);
+void git_packfile_stream_free(git_packfile_stream *obj);
+
 git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
 		git_off_t *curpos, git_otype type,
 		git_off_t delta_obj_offset);
 
-void packfile_free(struct git_pack_file *p);
+void git_packfile_free(struct git_pack_file *p);
 int git_packfile_check(struct git_pack_file **pack_out, const char *path);
 int git_pack_entry_find(
 		struct git_pack_entry *e,
 		struct git_pack_file *p,
 		const git_oid *short_oid,
-		unsigned int len);
+		size_t len);
+int git_pack_foreach_entry(
+		struct git_pack_file *p,
+		git_odb_foreach_cb cb,
+		void *data);
 
 #endif
diff --git a/libgit2/src/path.c b/libgit2/src/path.c
--- a/libgit2/src/path.c
+++ b/libgit2/src/path.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,9 +17,55 @@
 #include <stdio.h>
 #include <ctype.h>
 
+#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
+
+#ifdef GIT_WIN32
+static bool looks_like_network_computer_name(const char *path, int pos)
+{
+	if (pos < 3)
+		return false;
+
+	if (path[0] != '/' || path[1] != '/')
+		return false;
+
+	while (pos-- > 2) {
+		if (path[pos] == '/')
+			return false;
+	}
+
+	return true;
+}
+#endif
+
 /*
  * Based on the Android implementation, BSD licensed.
- * Check http://android.git.kernel.org/
+ * http://android.git.kernel.org/
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 int git_path_basename_r(git_buf *buffer, const char *path)
 {
@@ -105,10 +151,19 @@
 	/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
 		'C:/' here */
 
-	if (len == 2 && isalpha(path[0]) && path[1] == ':') {
+	if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
 		len = 3;
 		goto Exit;
 	}
+
+	/* Similarly checks if we're dealing with a network computer name
+		'//computername/.git' will return '//computername/' */
+
+	if (looks_like_network_computer_name(path, len)) {
+		len++;
+		goto Exit;
+	}
+
 #endif
 
 Exit:
@@ -145,7 +200,21 @@
 	return basename;
 }
 
+size_t git_path_basename_offset(git_buf *buffer)
+{
+	ssize_t slash;
 
+	if (!buffer || buffer->size <= 0)
+		return 0;
+
+	slash = git_buf_rfind_next(buffer, '/');
+
+	if (slash >= 0 && buffer->ptr[slash] == '/')
+		return (size_t)(slash + 1);
+
+	return 0;
+}
+
 const char *git_path_topdir(const char *path)
 {
 	size_t len;
@@ -168,11 +237,11 @@
 {
 	int offset = 0;
 
-#ifdef GIT_WIN32
 	/* Does the root of the path look like a windows drive ? */
-	if (isalpha(path[0]) && (path[1] == ':'))
+	if (LOOKS_LIKE_DRIVE_PREFIX(path))
 		offset += 2;
 
+#ifdef GIT_WIN32
 	/* Are we dealing with a windows network path? */
 	else if ((path[0] == '/' && path[1] == '/') ||
 		(path[0] == '\\' && path[1] == '\\'))
@@ -191,6 +260,31 @@
 	return -1;	/* Not a real error - signals that path is not rooted */
 }
 
+int git_path_join_unrooted(
+	git_buf *path_out, const char *path, const char *base, ssize_t *root_at)
+{
+	int error, root;
+
+	assert(path && path_out);
+
+	root = git_path_root(path);
+
+	if (base != NULL && root < 0) {
+		error = git_buf_joinpath(path_out, base, path);
+
+		if (root_at)
+			*root_at = (ssize_t)strlen(base);
+	}
+	else {
+		error = git_buf_sets(path_out, path);
+
+		if (root_at)
+			*root_at = (root < 0) ? 0 : (ssize_t)root;
+	}
+
+	return error;
+}
+
 int git_path_prettify(git_buf *path_out, const char *path, const char *base)
 {
 	char buf[GIT_PATH_MAX];
@@ -210,7 +304,7 @@
 		giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
 
 		git_buf_clear(path_out);
-		
+
 		return error;
 	}
 
@@ -306,7 +400,7 @@
 	if (offset >= len || file_url[offset] == '/')
 		return error_invalid_local_file_uri(file_url);
 
-#ifndef _MSC_VER
+#ifndef GIT_WIN32
 	offset--;	/* A *nix absolute path starts with a forward slash */
 #endif
 
@@ -341,9 +435,10 @@
 	iter.asize = path->asize;
 
 	while (scan >= stop) {
-		if ((error = cb(data, &iter)) < 0)
-			break;
+		error = cb(data, &iter);
 		iter.ptr[scan] = oldc;
+		if (error < 0)
+			break;
 		scan = git_buf_rfind_next(&iter, '/');
 		if (scan >= 0) {
 			scan++;
@@ -385,6 +480,68 @@
 	return S_ISREG(st.st_mode) != 0;
 }
 
+#ifdef GIT_WIN32
+
+bool git_path_is_empty_dir(const char *path)
+{
+	git_buf pathbuf = GIT_BUF_INIT;
+	HANDLE hFind = INVALID_HANDLE_VALUE;
+	wchar_t wbuf[GIT_WIN_PATH];
+	WIN32_FIND_DATAW ffd;
+	bool retval = true;
+
+	if (!git_path_isdir(path)) return false;
+
+	git_buf_printf(&pathbuf, "%s\\*", path);
+	git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf));
+
+	hFind = FindFirstFileW(wbuf, &ffd);
+	if (INVALID_HANDLE_VALUE == hFind) {
+		giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+		return false;
+	}
+
+	do {
+		if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
+			retval = false;
+		}
+	} while (FindNextFileW(hFind, &ffd) != 0);
+
+	FindClose(hFind);
+	git_buf_free(&pathbuf);
+	return retval;
+}
+
+#else
+
+bool git_path_is_empty_dir(const char *path)
+{
+	DIR *dir = NULL;
+	struct dirent *e;
+	bool retval = true;
+
+	if (!git_path_isdir(path)) return false;
+
+	dir = opendir(path);
+	if (!dir) {
+		giterr_set(GITERR_OS, "Couldn't open '%s'", path);
+		return false;
+	}
+
+	while ((e = readdir(dir)) != NULL) {
+		if (!git_path_is_dot_or_dotdot(e->d_name)) {
+			giterr_set(GITERR_INVALID,
+						  "'%s' exists and is not an empty directory", path);
+			retval = false;
+			break;
+		}
+	}
+	closedir(dir);
+
+	return retval;
+}
+#endif
+
 int git_path_lstat(const char *path, struct stat *st)
 {
 	int err = 0;
@@ -407,7 +564,7 @@
 	size_t sub_size = strlen(sub);
 
 	/* leave base valid even if we could not make space for subdir */
-	if (git_buf_try_grow(dir, dir_size + sub_size + 2) < 0)
+	if (git_buf_try_grow(dir, dir_size + sub_size + 2, false) < 0)
 		return false;
 
 	/* save excursion */
@@ -437,12 +594,7 @@
 
 int git_path_find_dir(git_buf *dir, const char *path, const char *base)
 {
-	int error;
-
-	if (base != NULL && git_path_root(path) < 0)
-		error = git_buf_joinpath(dir, base, path);
-	else
-		error = git_buf_sets(dir, path);
+	int error = git_path_join_unrooted(dir, path, base, NULL);
 
 	if (!error) {
 		char buf[GIT_PATH_MAX];
@@ -460,31 +612,94 @@
 	return error;
 }
 
+int git_path_resolve_relative(git_buf *path, size_t ceiling)
+{
+	char *base, *to, *from, *next;
+	size_t len;
+
+	if (!path || git_buf_oom(path))
+		return -1;
+
+	if (ceiling > path->size)
+		ceiling = path->size;
+
+	/* recognize drive prefixes, etc. that should not be backed over */
+	if (ceiling == 0)
+		ceiling = git_path_root(path->ptr) + 1;
+
+	/* recognize URL prefixes that should not be backed over */
+	if (ceiling == 0) {
+		for (next = path->ptr; *next && git__isalpha(*next); ++next);
+		if (next[0] == ':' && next[1] == '/' && next[2] == '/')
+			ceiling = (next + 3) - path->ptr;
+	}
+
+	base = to = from = path->ptr + ceiling;
+
+	while (*from) {
+		for (next = from; *next && *next != '/'; ++next);
+
+		len = next - from;
+
+		if (len == 1 && from[0] == '.')
+			/* do nothing with singleton dot */;
+
+		else if (len == 2 && from[0] == '.' && from[1] == '.') {
+			while (to > base && to[-1] == '/') to--;
+			while (to > base && to[-1] != '/') to--;
+		}
+
+		else {
+			if (*next == '/')
+				len++;
+
+			if (to != from)
+				memmove(to, from, len);
+
+			to += len;
+		}
+
+		from += len;
+
+		while (*from == '/') from++;
+	}
+
+	*to = '\0';
+
+	path->size = to - path->ptr;
+
+	return 0;
+}
+
+int git_path_apply_relative(git_buf *target, const char *relpath)
+{
+	git_buf_joinpath(target, git_buf_cstr(target), relpath);
+	return git_path_resolve_relative(target, 0);
+}
+
 int git_path_cmp(
 	const char *name1, size_t len1, int isdir1,
-	const char *name2, size_t len2, int isdir2)
+	const char *name2, size_t len2, int isdir2,
+	int (*compare)(const char *, const char *, size_t))
 {
+	unsigned char c1, c2;
 	size_t len = len1 < len2 ? len1 : len2;
 	int cmp;
 
-	cmp = memcmp(name1, name2, len);
+	cmp = compare(name1, name2, len);
 	if (cmp)
 		return cmp;
-	if (len1 < len2)
-		return (!isdir1 && !isdir2) ? -1 :
-			(isdir1 ? '/' - name2[len1] : name2[len1] - '/');
-	if (len1 > len2)
-		return (!isdir1 && !isdir2) ? 1 :
-			(isdir2 ? name1[len2] - '/' : '/' - name1[len2]);
-	return 0;
-}
 
-/* Taken from git.git */
-GIT_INLINE(int) is_dot_or_dotdot(const char *name)
-{
-	return (name[0] == '.' &&
-		(name[1] == '\0' ||
-		 (name[1] == '.' && name[2] == '\0')));
+	c1 = name1[len];
+	c2 = name2[len];
+
+	if (c1 == '\0' && isdir1)
+		c1 = '/';
+
+	if (c2 == '\0' && isdir2)
+		c2 = '/';
+
+	return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
 }
 
 int git_path_direach(
@@ -506,7 +721,7 @@
 		return -1;
 	}
 
-#ifdef __sun
+#if defined(__sun) || defined(__GNU__)
 	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
 #else
 	de_buf = git__malloc(sizeof(struct dirent));
@@ -515,7 +730,7 @@
 	while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
 		int result;
 
-		if (is_dot_or_dotdot(de->d_name))
+		if (git_path_is_dot_or_dotdot(de->d_name))
 			continue;
 
 		if (git_buf_puts(path, de->d_name) < 0) {
@@ -560,7 +775,7 @@
 		return -1;
 	}
 
-#ifdef __sun
+#if defined(__sun) || defined(__GNU__)
 	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
 #else
 	de_buf = git__malloc(sizeof(struct dirent));
@@ -574,7 +789,7 @@
 		char *entry_path;
 		size_t entry_len;
 
-		if (is_dot_or_dotdot(de->d_name))
+		if (git_path_is_dot_or_dotdot(de->d_name))
 			continue;
 
 		entry_len = strlen(de->d_name);
@@ -609,18 +824,30 @@
 int git_path_with_stat_cmp(const void *a, const void *b)
 {
 	const git_path_with_stat *psa = a, *psb = b;
-	return git__strcmp_cb(psa->path, psb->path);
+	return strcmp(psa->path, psb->path);
 }
 
+int git_path_with_stat_cmp_icase(const void *a, const void *b)
+{
+	const git_path_with_stat *psa = a, *psb = b;
+	return strcasecmp(psa->path, psb->path);
+}
+
 int git_path_dirload_with_stat(
 	const char *path,
 	size_t prefix_len,
+	bool ignore_case,
+	const char *start_stat,
+	const char *end_stat,
 	git_vector *contents)
 {
 	int error;
 	unsigned int i;
 	git_path_with_stat *ps;
 	git_buf full = GIT_BUF_INIT;
+	int (*strncomp)(const char *a, const char *b, size_t sz);
+	size_t start_len = start_stat ? strlen(start_stat) : 0;
+	size_t end_len = end_stat ? strlen(end_stat) : 0, cmp_len;
 
 	if (git_buf_set(&full, path, prefix_len) < 0)
 		return -1;
@@ -632,23 +859,45 @@
 		return error;
 	}
 
+	strncomp = ignore_case ? git__strncasecmp : git__strncmp;
+
+	/* stat struct at start of git_path_with_stat, so shift path text */
 	git_vector_foreach(contents, i, ps) {
 		size_t path_len = strlen((char *)ps);
-
 		memmove(ps->path, ps, path_len + 1);
 		ps->path_len = path_len;
+	}
 
+	git_vector_foreach(contents, i, ps) {
+		/* skip if before start_stat or after end_stat */
+		cmp_len = min(start_len, ps->path_len);
+		if (cmp_len && strncomp(ps->path, start_stat, cmp_len) < 0)
+			continue;
+		cmp_len = min(end_len, ps->path_len);
+		if (cmp_len && strncomp(ps->path, end_stat, cmp_len) > 0)
+			continue;
+
+		git_buf_truncate(&full, prefix_len);
+
 		if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 ||
 			(error = git_path_lstat(full.ptr, &ps->st)) < 0)
 			break;
 
-		git_buf_truncate(&full, prefix_len);
-
 		if (S_ISDIR(ps->st.st_mode)) {
-			ps->path[path_len] = '/';
-			ps->path[path_len + 1] = '\0';
+			if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0)
+				break;
+
+			if (p_access(full.ptr, F_OK) == 0) {
+				ps->st.st_mode = GIT_FILEMODE_COMMIT;
+			} else {
+				ps->path[ps->path_len++] = '/';
+				ps->path[ps->path_len] = '\0';
+			}
 		}
 	}
+
+	/* sort now that directory suffix is added */
+	git_vector_sort(contents);
 
 	git_buf_free(&full);
 
diff --git a/libgit2/src/path.h b/libgit2/src/path.h
--- a/libgit2/src/path.h
+++ b/libgit2/src/path.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -58,6 +58,11 @@
 extern char *git_path_basename(const char *path);
 extern int git_path_basename_r(git_buf *buffer, const char *path);
 
+/* Return the offset of the start of the basename.  Unlike the other
+ * basename functions, this returns 0 if the path is empty.
+ */
+extern size_t git_path_basename_offset(git_buf *buffer);
+
 extern const char *git_path_topdir(const char *path);
 
 /**
@@ -80,7 +85,24 @@
  */
 extern void git_path_string_to_dir(char* path, size_t size);
 
+/**
+ * Taken from git.git; returns nonzero if the given path is "." or "..".
+ */
+GIT_INLINE(int) git_path_is_dot_or_dotdot(const char *name)
+{
+	return (name[0] == '.' &&
+			  (name[1] == '\0' ||
+				(name[1] == '.' && name[2] == '\0')));
+}
+
 #ifdef GIT_WIN32
+GIT_INLINE(int) git_path_is_dot_or_dotdotW(const wchar_t *name)
+{
+	return (name[0] == L'.' &&
+			  (name[1] == L'\0' ||
+				(name[1] == L'.' && name[2] == L'\0')));
+}
+
 /**
  * Convert backslashes in path to forward slashes.
  */
@@ -130,6 +152,11 @@
 extern bool git_path_isfile(const char *path);
 
 /**
+ * Check if the given path is a directory, and is empty.
+ */
+extern bool git_path_is_empty_dir(const char *path);
+
+/**
  * Stat a file and/or link and set error if needed.
  */
 extern int git_path_lstat(const char *path, struct stat *st);
@@ -164,6 +191,15 @@
 extern bool git_path_contains_file(git_buf *dir, const char *file);
 
 /**
+ * Prepend base to unrooted path or just copy path over.
+ *
+ * This will optionally return the index into the path where the "root"
+ * is, either the end of the base directory prefix or the path root.
+ */
+extern int git_path_join_unrooted(
+	git_buf *path_out, const char *path, const char *base, ssize_t *root_at);
+
+/**
  * Clean up path, prepending base if it is not already rooted.
  */
 extern int git_path_prettify(git_buf *path_out, const char *path, const char *base);
@@ -186,6 +222,29 @@
 extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);
 
 /**
+ * Resolve relative references within a path.
+ *
+ * This eliminates "./" and "../" relative references inside a path,
+ * as well as condensing multiple slashes into single ones.  It will
+ * not touch the path before the "ceiling" length.
+ *
+ * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL
+ * prefix and not touch that part of the path.
+ */
+extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
+
+/**
+ * Apply a relative path to base path.
+ *
+ * Note that the base path could be a filename or a URL and this
+ * should still work.  The relative path is walked segment by segment
+ * with three rules: series of slashes will be condensed to a single
+ * slash, "." will be eaten with no change, and ".." will remove a
+ * segment from the base path.
+ */
+extern int git_path_apply_relative(git_buf *target, const char *relpath);
+
+/**
  * Walk each directory entry, except '.' and '..', calling fn(state).
  *
  * @param pathbuf buffer the function reads the initial directory
@@ -194,6 +253,7 @@
  *		the input state and the second arg is pathbuf. The function
  *		may modify the pathbuf, but only by appending new text.
  * @param state to pass to fn as the first arg.
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
  */
 extern int git_path_direach(
 	git_buf *pathbuf,
@@ -201,11 +261,12 @@
 	void *state);
 
 /**
- * Sort function to order two paths.
+ * Sort function to order two paths
  */
 extern int git_path_cmp(
 	const char *name1, size_t len1, int isdir1,
-	const char *name2, size_t len2, int isdir2);
+	const char *name2, size_t len2, int isdir2,
+	int (*compare)(const char *, const char *, size_t));
 
 /**
  * Invoke callback up path directory by directory until the ceiling is
@@ -261,18 +322,33 @@
 } git_path_with_stat;
 
 extern int git_path_with_stat_cmp(const void *a, const void *b);
+extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
 
 /**
  * Load all directory entries along with stat info into a vector.
  *
- * This is just like git_path_dirload except that each entry in the
- * vector is a git_path_with_stat structure that contains both the
- * path and the stat info, plus directories will have a / suffixed
- * to their path name.
+ * This adds four things on top of plain `git_path_dirload`:
+ *
+ * 1. Each entry in the vector is a `git_path_with_stat` struct that
+ *    contains both the path and the stat info
+ * 2. The entries will be sorted alphabetically
+ * 3. Entries that are directories will be suffixed with a '/'
+ * 4. Optionally, you can be a start and end prefix and only elements
+ *    after the start and before the end (inclusively) will be stat'ed.
+ *
+ * @param path The directory to read from
+ * @param prefix_len The trailing part of path to prefix to entry paths
+ * @param ignore_case How to sort and compare paths with start/end limits
+ * @param start_stat As optimization, only stat values after this prefix
+ * @param end_stat As optimization, only stat values before this prefix
+ * @param contents Vector to fill with git_path_with_stat structures
  */
 extern int git_path_dirload_with_stat(
 	const char *path,
 	size_t prefix_len,
+	bool ignore_case,
+	const char *start_stat,
+	const char *end_stat,
 	git_vector *contents);
 
 #endif
diff --git a/libgit2/src/pathspec.c b/libgit2/src/pathspec.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pathspec.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pathspec.h"
+#include "buf_text.h"
+#include "attr_file.h"
+
+/* what is the common non-wildcard prefix for all items in the pathspec */
+char *git_pathspec_prefix(const git_strarray *pathspec)
+{
+	git_buf prefix = GIT_BUF_INIT;
+	const char *scan;
+
+	if (!pathspec || !pathspec->count ||
+		git_buf_text_common_prefix(&prefix, pathspec) < 0)
+		return NULL;
+
+	/* diff prefix will only be leading non-wildcards */
+	for (scan = prefix.ptr; *scan; ++scan) {
+		if (git__iswildcard(*scan) &&
+			(scan == prefix.ptr || (*(scan - 1) != '\\')))
+			break;
+	}
+	git_buf_truncate(&prefix, scan - prefix.ptr);
+
+	if (prefix.size <= 0) {
+		git_buf_free(&prefix);
+		return NULL;
+	}
+
+	git_buf_text_unescape(&prefix);
+
+	return git_buf_detach(&prefix);
+}
+
+/* is there anything in the spec that needs to be filtered on */
+bool git_pathspec_is_empty(const git_strarray *pathspec)
+{
+	size_t i;
+
+	if (pathspec == NULL)
+		return true;
+
+	for (i = 0; i < pathspec->count; ++i) {
+		const char *str = pathspec->strings[i];
+
+		if (str && str[0])
+			return false;
+	}
+
+	return true;
+}
+
+/* build a vector of fnmatch patterns to evaluate efficiently */
+int git_pathspec_init(
+	git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
+{
+	size_t i;
+
+	memset(vspec, 0, sizeof(*vspec));
+
+	if (git_pathspec_is_empty(strspec))
+		return 0;
+
+	if (git_vector_init(vspec, strspec->count, NULL) < 0)
+		return -1;
+
+	for (i = 0; i < strspec->count; ++i) {
+		int ret;
+		const char *pattern = strspec->strings[i];
+		git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
+		if (!match)
+			return -1;
+
+		match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
+
+		ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
+		if (ret == GIT_ENOTFOUND) {
+			git__free(match);
+			continue;
+		} else if (ret < 0)
+			return ret;
+
+		if (git_vector_insert(vspec, match) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+/* free data from the pathspec vector */
+void git_pathspec_free(git_vector *vspec)
+{
+	git_attr_fnmatch *match;
+	unsigned int i;
+
+	git_vector_foreach(vspec, i, match) {
+		git__free(match);
+		vspec->contents[i] = NULL;
+	}
+
+	git_vector_free(vspec);
+}
+
+/* match a path against the vectorized pathspec */
+bool git_pathspec_match_path(
+	git_vector *vspec,
+	const char *path,
+	bool disable_fnmatch,
+	bool casefold,
+	const char **matched_pathspec)
+{
+	size_t i;
+	git_attr_fnmatch *match;
+	int fnmatch_flags = 0;
+	int (*use_strcmp)(const char *, const char *);
+	int (*use_strncmp)(const char *, const char *, size_t);
+
+	if (matched_pathspec)
+		*matched_pathspec = NULL;
+
+	if (!vspec || !vspec->length)
+		return true;
+
+	if (disable_fnmatch)
+		fnmatch_flags = -1;
+	else if (casefold)
+		fnmatch_flags = FNM_CASEFOLD;
+
+	if (casefold) {
+		use_strcmp  = git__strcasecmp;
+		use_strncmp = git__strncasecmp;
+	} else {
+		use_strcmp  = git__strcmp;
+		use_strncmp = git__strncmp;
+	}
+
+	git_vector_foreach(vspec, i, match) {
+		int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : FNM_NOMATCH;
+			
+		if (result == FNM_NOMATCH)
+			result = use_strcmp(match->pattern, path) ? FNM_NOMATCH : 0;
+
+		if (fnmatch_flags >= 0 && result == FNM_NOMATCH)
+			result = p_fnmatch(match->pattern, path, fnmatch_flags);
+
+		/* if we didn't match, look for exact dirname prefix match */
+		if (result == FNM_NOMATCH &&
+			(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+			use_strncmp(path, match->pattern, match->length) == 0 &&
+			path[match->length] == '/')
+			result = 0;
+
+		if (result == 0) {
+			if (matched_pathspec)
+				*matched_pathspec = match->pattern;
+
+			return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
+		}
+	}
+
+	return false;
+}
+
diff --git a/libgit2/src/pathspec.h b/libgit2/src/pathspec.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pathspec.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pathspec_h__
+#define INCLUDE_pathspec_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "vector.h"
+#include "pool.h"
+
+/* what is the common non-wildcard prefix for all items in the pathspec */
+extern char *git_pathspec_prefix(const git_strarray *pathspec);
+
+/* is there anything in the spec that needs to be filtered on */
+extern bool git_pathspec_is_empty(const git_strarray *pathspec);
+
+/* build a vector of fnmatch patterns to evaluate efficiently */
+extern int git_pathspec_init(
+	git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
+
+/* free data from the pathspec vector */
+extern void git_pathspec_free(git_vector *vspec);
+
+/*
+ * Match a path against the vectorized pathspec.
+ * The matched pathspec is passed back into the `matched_pathspec` parameter,
+ * unless it is passed as NULL by the caller.
+ */
+extern bool git_pathspec_match_path(
+	git_vector *vspec,
+	const char *path,
+	bool disable_fnmatch,
+	bool casefold,
+	const char **matched_pathspec);
+
+#endif
diff --git a/libgit2/src/pkt.c b/libgit2/src/pkt.c
deleted file mode 100644
--- a/libgit2/src/pkt.c
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-
-#include "git2/types.h"
-#include "git2/errors.h"
-#include "git2/refs.h"
-#include "git2/revwalk.h"
-
-#include "pkt.h"
-#include "util.h"
-#include "netops.h"
-#include "posix.h"
-#include "buffer.h"
-
-#include <ctype.h>
-
-#define PKT_LEN_SIZE 4
-static const char pkt_done_str[] = "0009done\n";
-static const char pkt_flush_str[] = "0000";
-static const char pkt_have_prefix[] = "0032have ";
-static const char pkt_want_prefix[] = "0032want ";
-
-static int flush_pkt(git_pkt **out)
-{
-	git_pkt *pkt;
-
-	pkt = git__malloc(sizeof(git_pkt));
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_FLUSH;
-	*out = pkt;
-
-	return 0;
-}
-
-/* the rest of the line will be useful for multi_ack */
-static int ack_pkt(git_pkt **out, const char *line, size_t len)
-{
-	git_pkt *pkt;
-	GIT_UNUSED(line);
-	GIT_UNUSED(len);
-
-	pkt = git__malloc(sizeof(git_pkt));
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_ACK;
-	*out = pkt;
-
-	return 0;
-}
-
-static int nak_pkt(git_pkt **out)
-{
-	git_pkt *pkt;
-
-	pkt = git__malloc(sizeof(git_pkt));
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_NAK;
-	*out = pkt;
-
-	return 0;
-}
-
-static int pack_pkt(git_pkt **out)
-{
-	git_pkt *pkt;
-
-	pkt = git__malloc(sizeof(git_pkt));
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_PACK;
-	*out = pkt;
-
-	return 0;
-}
-
-static int comment_pkt(git_pkt **out, const char *line, size_t len)
-{
-	git_pkt_comment *pkt;
-
-	pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_COMMENT;
-	memcpy(pkt->comment, line, len);
-	pkt->comment[len] = '\0';
-
-	*out = (git_pkt *) pkt;
-
-	return 0;
-}
-
-static int err_pkt(git_pkt **out, const char *line, size_t len)
-{
-	git_pkt_err *pkt;
-
-	/* Remove "ERR " from the line */
-	line += 4;
-	len -= 4;
-	pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
-	GITERR_CHECK_ALLOC(pkt);
-
-	pkt->type = GIT_PKT_ERR;
-	memcpy(pkt->error, line, len);
-	pkt->error[len] = '\0';
-
-	*out = (git_pkt *) pkt;
-
-	return 0;
-}
-
-/*
- * Parse an other-ref line.
- */
-static int ref_pkt(git_pkt **out, const char *line, size_t len)
-{
-	int error;
-	git_pkt_ref *pkt;
-
-	pkt = git__malloc(sizeof(git_pkt_ref));
-	GITERR_CHECK_ALLOC(pkt);
-
-	memset(pkt, 0x0, sizeof(git_pkt_ref));
-	pkt->type = GIT_PKT_REF;
-	if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
-		goto error_out;
-
-	/* Check for a bit of consistency */
-	if (line[GIT_OID_HEXSZ] != ' ') {
-		giterr_set(GITERR_NET, "Error parsing pkt-line");
-		error = -1;
-		goto error_out;
-	}
-
-	/* Jump from the name */
-	line += GIT_OID_HEXSZ + 1;
-	len -= (GIT_OID_HEXSZ + 1);
-
-	if (line[len - 1] == '\n')
-		--len;
-
-	pkt->head.name = git__malloc(len + 1);
-	GITERR_CHECK_ALLOC(pkt->head.name);
-
-	memcpy(pkt->head.name, line, len);
-	pkt->head.name[len] = '\0';
-
-	if (strlen(pkt->head.name) < len) {
-		pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
-	}
-
-	*out = (git_pkt *)pkt;
-	return 0;
-
-error_out:
-	git__free(pkt);
-	return error;
-}
-
-static int32_t parse_len(const char *line)
-{
-	char num[PKT_LEN_SIZE + 1];
-	int i, error;
-	int32_t len;
-	const char *num_end;
-
-	memcpy(num, line, PKT_LEN_SIZE);
-	num[PKT_LEN_SIZE] = '\0';
-
-	for (i = 0; i < PKT_LEN_SIZE; ++i) {
-		if (!isxdigit(num[i])) {
-			giterr_set(GITERR_NET, "Found invalid hex digit in length");
-			return -1;
-		}
-	}
-
-	if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
-		return error;
-
-	return len;
-}
-
-/*
- * As per the documentation, the syntax is:
- *
- * pkt-line	= data-pkt / flush-pkt
- * data-pkt	= pkt-len pkt-payload
- * pkt-len		= 4*(HEXDIG)
- * pkt-payload = (pkt-len -4)*(OCTET)
- * flush-pkt	= "0000"
- *
- * Which means that the first four bytes are the length of the line,
- * in ASCII hexadecimal (including itself)
- */
-
-int git_pkt_parse_line(
-	git_pkt **head, const char *line, const char **out, size_t bufflen)
-{
-	int ret;
-	int32_t len;
-
-	/* Not even enough for the length */
-	if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
-		return GIT_EBUFS;
-
-	len = parse_len(line);
-	if (len < 0) {
-		/*
-		 * If we fail to parse the length, it might be because the
-		 * server is trying to send us the packfile already.
-		 */
-		if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
-			giterr_clear();
-			*out = line;
-			return pack_pkt(head);
-		}
-
-		return (int)len;
-	}
-
-	/*
-	 * If we were given a buffer length, then make sure there is
-	 * enough in the buffer to satisfy this line
-	 */
-	if (bufflen > 0 && bufflen < (size_t)len)
-		return GIT_EBUFS;
-
-	line += PKT_LEN_SIZE;
-	/*
-	 * TODO: How do we deal with empty lines? Try again? with the next
-	 * line?
-	 */
-	if (len == PKT_LEN_SIZE) {
-		*out = line;
-		return 0;
-	}
-
-	if (len == 0) { /* Flush pkt */
-		*out = line;
-		return flush_pkt(head);
-	}
-
-	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
-
-	/* Assming the minimal size is actually 4 */
-	if (!git__prefixcmp(line, "ACK"))
-		ret = ack_pkt(head, line, len);
-	else if (!git__prefixcmp(line, "NAK"))
-		ret = nak_pkt(head);
-	else if (!git__prefixcmp(line, "ERR "))
-		ret = err_pkt(head, line, len);
-	else if (*line == '#')
-		ret = comment_pkt(head, line, len);
-	else
-		ret = ref_pkt(head, line, len);
-
-	*out = line + len;
-
-	return ret;
-}
-
-void git_pkt_free(git_pkt *pkt)
-{
-	if (pkt->type == GIT_PKT_REF) {
-		git_pkt_ref *p = (git_pkt_ref *) pkt;
-		git__free(p->head.name);
-	}
-
-	git__free(pkt);
-}
-
-int git_pkt_buffer_flush(git_buf *buf)
-{
-	return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
-}
-
-int git_pkt_send_flush(GIT_SOCKET s)
-{
-
-	return gitno_send(s, pkt_flush_str, strlen(pkt_flush_str), 0);
-}
-
-static int buffer_want_with_caps(git_remote_head *head, git_transport_caps *caps, git_buf *buf)
-{
-	char capstr[20];
-	char oid[GIT_OID_HEXSZ +1] = {0};
-	unsigned int len;
-
-	if (caps->ofs_delta)
-		strncpy(capstr, GIT_CAP_OFS_DELTA, sizeof(capstr));
-
-	len = (unsigned int)
-		(strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
-		 strlen(capstr) + 1 /* LF */);
-	git_buf_grow(buf, git_buf_len(buf) + len);
-
-	git_oid_fmt(oid, &head->oid);
-	return git_buf_printf(buf, "%04xwant %s%c%s\n", len, oid, 0, capstr);
-}
-
-/*
- * All "want" packets have the same length and format, so what we do
- * is overwrite the OID each time.
- */
-
-int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf)
-{
-	unsigned int i = 0;
-	git_remote_head *head;
-
-	if (caps->common) {
-		for (; i < refs->length; ++i) {
-			head = refs->contents[i];
-			if (!head->local)
-				break;
-		}
-
-		if (buffer_want_with_caps(refs->contents[i], caps, buf) < 0)
-			return -1;
-
-		i++;
-	}
-
-	for (; i < refs->length; ++i) {
-		char oid[GIT_OID_HEXSZ];
-
-		head = refs->contents[i];
-		if (head->local)
-			continue;
-
-		git_oid_fmt(oid, &head->oid);
-		git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
-		git_buf_put(buf, oid, GIT_OID_HEXSZ);
-		git_buf_putc(buf, '\n');
-		if (git_buf_oom(buf))
-			return -1;
-	}
-
-	return git_pkt_buffer_flush(buf);
-}
-
-int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
-{
-	char oidhex[GIT_OID_HEXSZ + 1];
-
-	memset(oidhex, 0x0, sizeof(oidhex));
-	git_oid_fmt(oidhex, oid);
-	return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
-}
-
-int git_pkt_buffer_done(git_buf *buf)
-{
-	return git_buf_puts(buf, pkt_done_str);
-}
diff --git a/libgit2/src/pkt.h b/libgit2/src/pkt.h
deleted file mode 100644
--- a/libgit2/src/pkt.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#ifndef INCLUDE_pkt_h__
-#define INCLUDE_pkt_h__
-
-#include "common.h"
-#include "transport.h"
-#include "buffer.h"
-#include "posix.h"
-#include "git2/net.h"
-
-enum git_pkt_type {
-	GIT_PKT_CMD,
-	GIT_PKT_FLUSH,
-	GIT_PKT_REF,
-	GIT_PKT_HAVE,
-	GIT_PKT_ACK,
-	GIT_PKT_NAK,
-	GIT_PKT_PACK,
-	GIT_PKT_COMMENT,
-	GIT_PKT_ERR,
-};
-
-/* Used for multi-ack */
-enum git_ack_status {
-	GIT_ACK_NONE,
-	GIT_ACK_CONTINUE,
-	GIT_ACK_COMMON,
-	GIT_ACK_READY
-};
-
-/* This would be a flush pkt */
-typedef struct {
-	enum git_pkt_type type;
-} git_pkt;
-
-struct git_pkt_cmd {
-	enum git_pkt_type type;
-	char *cmd;
-	char *path;
-	char *host;
-};
-
-/* This is a pkt-line with some info in it */
-typedef struct {
-	enum git_pkt_type type;
-	git_remote_head head;
-	char *capabilities;
-} git_pkt_ref;
-
-/* Useful later */
-typedef struct {
-	enum git_pkt_type type;
-	git_oid oid;
-	enum git_ack_status status;
-} git_pkt_ack;
-
-typedef struct {
-	enum git_pkt_type type;
-	char comment[GIT_FLEX_ARRAY];
-} git_pkt_comment;
-
-typedef struct {
-	enum git_pkt_type type;
-	char error[GIT_FLEX_ARRAY];
-} git_pkt_err;
-
-int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len);
-int git_pkt_buffer_flush(git_buf *buf);
-int git_pkt_send_flush(GIT_SOCKET s);
-int git_pkt_buffer_done(git_buf *buf);
-int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf);
-int git_pkt_buffer_have(git_oid *oid, git_buf *buf);
-void git_pkt_free(git_pkt *pkt);
-
-#endif
diff --git a/libgit2/src/pool.c b/libgit2/src/pool.c
--- a/libgit2/src/pool.c
+++ b/libgit2/src/pool.c
@@ -10,6 +10,10 @@
 	char data[GIT_FLEX_ARRAY];
 };
 
+struct pool_freelist {
+	struct pool_freelist *next;
+};
+
 #define GIT_POOL_MIN_USABLE	4
 #define GIT_POOL_MIN_PAGESZ	2 * sizeof(void*)
 
@@ -150,7 +154,7 @@
 		pool->has_multi_item_alloc = 1;
 	else if (pool->free_list != NULL) {
 		ptr = pool->free_list;
-		pool->free_list = *((void **)pool->free_list);
+		pool->free_list = ((struct pool_freelist *)pool->free_list)->next;
 		return ptr;
 	}
 
@@ -206,6 +210,11 @@
 	return git_pool_strndup(pool, str, strlen(str));
 }
 
+char *git_pool_strdup_safe(git_pool *pool, const char *str)
+{
+	return str ? git_pool_strdup(pool, str) : NULL;
+}
+
 char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
 {
 	void *ptr;
@@ -230,12 +239,33 @@
 
 void git_pool_free(git_pool *pool, void *ptr)
 {
-	assert(pool && ptr && pool->item_size >= sizeof(void*));
+	struct pool_freelist *item = ptr;
 
-	*((void **)ptr) = pool->free_list;
-	pool->free_list = ptr;
+	assert(pool && pool->item_size >= sizeof(void*));
+
+	if (item) {
+		item->next = pool->free_list;
+		pool->free_list = item;
+	}
 }
 
+void git_pool_free_array(git_pool *pool, size_t count, void **ptrs)
+{
+	struct pool_freelist **items = (struct pool_freelist **)ptrs;
+	size_t i;
+
+	assert(pool && ptrs && pool->item_size >= sizeof(void*));
+
+	if (!count)
+		return;
+
+	for (i = count - 1; i > 0; --i)
+		items[i]->next = items[i - 1];
+
+	items[i]->next  = pool->free_list;
+	pool->free_list = items[count - 1];
+}
+
 uint32_t git_pool__open_pages(git_pool *pool)
 {
 	uint32_t ct = 0;
@@ -275,6 +305,8 @@
 		SYSTEM_INFO info;
 		GetSystemInfo(&info);
 		size = (uint32_t)info.dwPageSize;
+#elif defined(__amigaos4__)
+		size = (uint32_t)4096; /* 4K as there is no global value we can query */
 #else
 		size = (uint32_t)sysconf(_SC_PAGE_SIZE);
 #endif
diff --git a/libgit2/src/pool.h b/libgit2/src/pool.h
--- a/libgit2/src/pool.h
+++ b/libgit2/src/pool.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -76,6 +76,17 @@
 extern void *git_pool_malloc(git_pool *pool, uint32_t items);
 
 /**
+ * Allocate space and zero it out.
+ */
+GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items)
+{
+	void *ptr = git_pool_malloc(pool, items);
+	if (ptr)
+		memset(ptr, 0, (size_t)items * (size_t)pool->item_size);
+	return ptr;
+}
+
+/**
  * Allocate space and duplicate string data into it.
  *
  * This is allowed only for pools with item_size == sizeof(char)
@@ -90,6 +101,13 @@
 extern char *git_pool_strdup(git_pool *pool, const char *str);
 
 /**
+ * Allocate space and duplicate a string into it, NULL is no error.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup_safe(git_pool *pool, const char *str);
+
+/**
  * Allocate space for the concatenation of two strings.
  *
  * This is allowed only for pools with item_size == sizeof(char)
@@ -107,6 +125,13 @@
  * native block size is large enough to hold a void pointer
  */
 extern void git_pool_free(git_pool *pool, void *ptr);
+
+/**
+ * Push an array of pool allocated blocks efficiently onto the free list.
+ *
+ * This has the same constraints as `git_pool_free()` above.
+ */
+extern void git_pool_free_array(git_pool *pool, size_t count, void **ptrs);
 
 /*
  * Misc utilities
diff --git a/libgit2/src/posix.c b/libgit2/src/posix.c
--- a/libgit2/src/posix.c
+++ b/libgit2/src/posix.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,12 +12,98 @@
 
 #ifndef GIT_WIN32
 
+#ifdef NO_ADDRINFO
+
+int p_getaddrinfo(
+	const char *host,
+	const char *port,
+	struct addrinfo *hints,
+	struct addrinfo **info)
+{
+	struct addrinfo *ainfo, *ai;
+	int p = 0;
+
+	GIT_UNUSED(hints);
+
+	if ((ainfo = malloc(sizeof(struct addrinfo))) == NULL)
+		return -1;
+
+	if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) {
+		free(ainfo);
+		return -2;
+	}
+
+	ainfo->ai_servent = getservbyname(port, 0);
+
+	if (ainfo->ai_servent)
+		ainfo->ai_port = ainfo->ai_servent->s_port;
+	else
+		ainfo->ai_port = atol(port);
+
+	memcpy(&ainfo->ai_addr_in.sin_addr,
+			ainfo->ai_hostent->h_addr_list[0],
+			ainfo->ai_hostent->h_length);
+
+	ainfo->ai_protocol = 0;
+	ainfo->ai_socktype = hints->ai_socktype;
+	ainfo->ai_family = ainfo->ai_hostent->h_addrtype;
+	ainfo->ai_addr_in.sin_family = ainfo->ai_family;
+	ainfo->ai_addr_in.sin_port = ainfo->ai_port;
+	ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in;
+	ainfo->ai_addrlen = sizeof(struct sockaddr_in);
+
+	*info = ainfo;
+
+	if (ainfo->ai_hostent->h_addr_list[1] == NULL) {
+		ainfo->ai_next = NULL;
+		return 0;
+	}
+
+	ai = ainfo;
+
+	for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) {
+		ai->ai_next = malloc(sizeof(struct addrinfo));
+		memcpy(&ai->ai_next, ainfo, sizeof(struct addrinfo));
+		memcpy(&ai->ai_next->ai_addr_in.sin_addr,
+			ainfo->ai_hostent->h_addr_list[p],
+			ainfo->ai_hostent->h_length);
+		ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in;
+		ai = ai->ai_next;
+	}
+
+	ai->ai_next = NULL;
+	return 0;
+}
+
+void p_freeaddrinfo(struct addrinfo *info)
+{
+	struct addrinfo *p, *next;
+
+	p = info;
+
+	while(p != NULL) {
+		next = p->ai_next;
+		free(p);
+		p = next;
+	}
+}
+
+const char *p_gai_strerror(int ret)
+{
+	switch(ret) {
+	case -1: return "Out of memory"; break;
+	case -2: return "Address lookup failed"; break;
+	default: return "Unknown error"; break;
+	}
+}
+
+#endif /* NO_ADDRINFO */
+
 int p_open(const char *path, int flags, ...)
 {
 	mode_t mode = 0;
 
-	if (flags & O_CREAT)
-	{
+	if (flags & O_CREAT) {
 		va_list arg_list;
 
 		va_start(arg_list, flags);
@@ -63,11 +149,12 @@
 	return -1;
 }
 
-#endif
+#endif /* GIT_WIN32 */
 
 int p_read(git_file fd, void *buf, size_t cnt)
 {
 	char *b = buf;
+
 	while (cnt) {
 		ssize_t r;
 #ifdef GIT_WIN32
@@ -92,6 +179,7 @@
 int p_write(git_file fd, const void *buf, size_t cnt)
 {
 	const char *b = buf;
+
 	while (cnt) {
 		ssize_t r;
 #ifdef GIT_WIN32
@@ -114,3 +202,5 @@
 	}
 	return 0;
 }
+
+
diff --git a/libgit2/src/posix.h b/libgit2/src/posix.h
--- a/libgit2/src/posix.h
+++ b/libgit2/src/posix.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,10 +10,18 @@
 #include "common.h"
 #include <fcntl.h>
 #include <time.h>
+#include "fnmatch.h"
 
+#ifndef S_IFGITLINK
 #define S_IFGITLINK 0160000
 #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+#endif
 
+/* if S_ISGID is not defined, then don't try to set it */
+#ifndef S_ISGID
+#define S_ISGID 0
+#endif
+
 #if !defined(O_BINARY)
 #define O_BINARY 0
 #endif
@@ -24,14 +32,13 @@
  * Standard POSIX Methods
  *
  * All the methods starting with the `p_` prefix are
- * direct ports of the standard POSIX methods. 
+ * direct ports of the standard POSIX methods.
  *
  * Some of the methods are slightly wrapped to provide
  * saner defaults. Some of these methods are emulated
  * in Windows platforns.
  *
  * Use your manpages to check the docs on these.
- * Straightforward 
  */
 
 extern int p_read(git_file fd, void *buf, size_t cnt);
@@ -59,10 +66,19 @@
 typedef int GIT_SOCKET;
 #define INVALID_SOCKET -1
 
+#define p_localtime_r localtime_r
+#define p_gmtime_r gmtime_r
+#define p_gettimeofday gettimeofday
+
 #else
 
 typedef SOCKET GIT_SOCKET;
+struct timezone;
+extern struct tm * p_localtime_r (const time_t *timer, struct tm *result);
+extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result);
+extern int p_gettimeofday(struct timeval *tv, struct timezone *tz);
 
+
 #endif
 
 /**
@@ -74,6 +90,41 @@
 #	include "unix/posix.h"
 #endif
 
-#define p_readdir_r(d,e,r) readdir_r(d,e,r)
+#ifdef NO_READDIR_R
+#	include <dirent.h>
+GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+	GIT_UNUSED(entry);
+	*result = readdir(dirp);
+	return 0;
+}
+#else /* NO_READDIR_R */
+#	define p_readdir_r(d,e,r) readdir_r(d,e,r)
+#endif
+
+#ifdef NO_ADDRINFO
+#	include <netdb.h>
+struct addrinfo {
+	struct hostent *ai_hostent;
+	struct servent *ai_servent;
+	struct sockaddr_in ai_addr_in;
+	struct sockaddr *ai_addr;
+	size_t ai_addrlen;
+	int ai_family;
+	int ai_socktype;
+	int ai_protocol;
+	long ai_port;
+	struct addrinfo *ai_next;
+};
+
+extern int p_getaddrinfo(const char *host, const char *port,
+	struct addrinfo *hints, struct addrinfo **info);
+extern void p_freeaddrinfo(struct addrinfo *info);
+extern const char *p_gai_strerror(int ret);
+#else
+#	define p_getaddrinfo(a, b, c, d) getaddrinfo(a, b, c, d)
+#	define p_freeaddrinfo(a) freeaddrinfo(a)
+#	define p_gai_strerror(c) gai_strerror(c)
+#endif /* NO_ADDRINFO */
 
 #endif
diff --git a/libgit2/src/pqueue.c b/libgit2/src/pqueue.c
--- a/libgit2/src/pqueue.c
+++ b/libgit2/src/pqueue.c
@@ -1,8 +1,30 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
+ *
+ * This file is based on a modified version of the priority queue found
+ * in the Apache project and libpqueue library.
+ * 
+ * https://github.com/vy/libpqueue
+ * 
+ * Original file notice:
+ * 
+ * Copyright 2010 Volkan Yazici <volkan.yazici@gmail.com>
+ * Copyright 2006-2010 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
  */
 
 #include "common.h"
diff --git a/libgit2/src/pqueue.h b/libgit2/src/pqueue.h
--- a/libgit2/src/pqueue.h
+++ b/libgit2/src/pqueue.h
@@ -1,8 +1,30 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors.  All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
+ * 
+ * This file is based on a modified version of the priority queue found
+ * in the Apache project and libpqueue library.
+ * 
+ * https://github.com/vy/libpqueue
+ * 
+ * Original file notice:
+ * 
+ * Copyright 2010 Volkan Yazici <volkan.yazici@gmail.com>
+ * Copyright 2006-2010 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
  */
 
 #ifndef INCLUDE_pqueue_h__
diff --git a/libgit2/src/protocol.c b/libgit2/src/protocol.c
deleted file mode 100644
--- a/libgit2/src/protocol.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include "common.h"
-#include "protocol.h"
-#include "pkt.h"
-#include "buffer.h"
-
-int git_protocol_store_refs(git_protocol *p, const char *data, size_t len)
-{
-	git_buf *buf = &p->buf;
-	git_vector *refs = p->refs;
-	int error;
-	const char *line_end, *ptr;
-
-	if (len == 0) { /* EOF */
-		if (git_buf_len(buf) != 0) {
-			giterr_set(GITERR_NET, "Unexpected EOF");
-			return p->error = -1;
-		} else {
-			return 0;
-		}
-	}
-
-	git_buf_put(buf, data, len);
-	ptr = buf->ptr;
-	while (1) {
-		git_pkt *pkt;
-
-		if (git_buf_len(buf) == 0)
-			return 0;
-
-		error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
-		if (error == GIT_EBUFS)
-			return 0; /* Ask for more */
-		if (error < 0)
-			return p->error = -1;
-
-		git_buf_consume(buf, line_end);
-
-		if (pkt->type == GIT_PKT_ERR) {
-			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
-			git__free(pkt);
-			return -1;
-		}
-
-		if (git_vector_insert(refs, pkt) < 0)
-			return p->error = -1;
-
-		if (pkt->type == GIT_PKT_FLUSH)
-			p->flush = 1;
-	}
-
-	return 0;
-}
diff --git a/libgit2/src/protocol.h b/libgit2/src/protocol.h
deleted file mode 100644
--- a/libgit2/src/protocol.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_protocol_h__
-#define INCLUDE_protocol_h__
-
-#include "transport.h"
-#include "buffer.h"
-
-typedef struct {
-	git_transport *transport;
-	git_vector *refs;
-	git_buf buf;
-	int error;
-	unsigned int flush :1;
-} git_protocol;
-
-int git_protocol_store_refs(git_protocol *p, const char *data, size_t len);
-
-#endif
diff --git a/libgit2/src/push.c b/libgit2/src/push.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/push.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+
+#include "common.h"
+#include "pack.h"
+#include "pack-objects.h"
+#include "remote.h"
+#include "vector.h"
+#include "push.h"
+#include "tree.h"
+
+static int push_spec_rref_cmp(const void *a, const void *b)
+{
+	const push_spec *push_spec_a = a, *push_spec_b = b;
+
+	return strcmp(push_spec_a->rref, push_spec_b->rref);
+}
+
+static int push_status_ref_cmp(const void *a, const void *b)
+{
+	const push_status *push_status_a = a, *push_status_b = b;
+
+	return strcmp(push_status_a->ref, push_status_b->ref);
+}
+
+int git_push_new(git_push **out, git_remote *remote)
+{
+	git_push *p;
+
+	*out = NULL;
+
+	p = git__calloc(1, sizeof(*p));
+	GITERR_CHECK_ALLOC(p);
+
+	p->repo = remote->repo;
+	p->remote = remote;
+	p->report_status = 1;
+	p->pb_parallelism = 1;
+
+	if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
+		git__free(p);
+		return -1;
+	}
+
+	if (git_vector_init(&p->status, 0, push_status_ref_cmp) < 0) {
+		git_vector_free(&p->specs);
+		git__free(p);
+		return -1;
+	}
+
+	*out = p;
+	return 0;
+}
+
+int git_push_set_options(git_push *push, const git_push_options *opts)
+{
+	if (!push || !opts)
+		return -1;
+
+	GITERR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
+
+	push->pb_parallelism = opts->pb_parallelism;
+
+	return 0;
+}
+
+static void free_refspec(push_spec *spec)
+{
+	if (spec == NULL)
+		return;
+
+	if (spec->lref)
+		git__free(spec->lref);
+
+	if (spec->rref)
+		git__free(spec->rref);
+
+	git__free(spec);
+}
+
+static int check_rref(char *ref)
+{
+	if (git__prefixcmp(ref, "refs/")) {
+		giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int check_lref(git_push *push, char *ref)
+{
+	/* lref must be resolvable to an existing object */
+	git_object *obj;
+	int error = git_revparse_single(&obj, push->repo, ref);
+	git_object_free(obj);
+
+	if (!error)
+		return 0;
+
+	if (error == GIT_ENOTFOUND)
+		giterr_set(GITERR_REFERENCE,
+			"src refspec '%s' does not match any existing object", ref);
+	else
+		giterr_set(GITERR_INVALID, "Not a valid reference '%s'", ref);
+	return -1;
+}
+
+static int parse_refspec(git_push *push, push_spec **spec, const char *str)
+{
+	push_spec *s;
+	char *delim;
+
+	*spec = NULL;
+
+	s = git__calloc(1, sizeof(*s));
+	GITERR_CHECK_ALLOC(s);
+
+	if (str[0] == '+') {
+		s->force = true;
+		str++;
+	}
+
+	delim = strchr(str, ':');
+	if (delim == NULL) {
+		s->lref = git__strdup(str);
+		if (!s->lref || check_lref(push, s->lref) < 0)
+			goto on_error;
+	} else {
+		if (delim - str) {
+			s->lref = git__strndup(str, delim - str);
+			if (!s->lref || check_lref(push, s->lref) < 0)
+				goto on_error;
+		}
+
+		if (strlen(delim + 1)) {
+			s->rref = git__strdup(delim + 1);
+			if (!s->rref || check_rref(s->rref) < 0)
+				goto on_error;
+		}
+	}
+
+	if (!s->lref && !s->rref)
+		goto on_error;
+
+	/* If rref is ommitted, use the same ref name as lref */
+	if (!s->rref) {
+		s->rref = git__strdup(s->lref);
+		if (!s->rref || check_rref(s->rref) < 0)
+			goto on_error;
+	}
+
+	*spec = s;
+	return 0;
+
+on_error:
+	free_refspec(s);
+	return -1;
+}
+
+int git_push_add_refspec(git_push *push, const char *refspec)
+{
+	push_spec *spec;
+
+	if (parse_refspec(push, &spec, refspec) < 0 ||
+	    git_vector_insert(&push->specs, spec) < 0)
+		return -1;
+
+	return 0;
+}
+
+int git_push_update_tips(git_push *push)
+{
+	git_refspec *fetch_spec = &push->remote->fetch;
+	git_buf remote_ref_name = GIT_BUF_INIT;
+	size_t i, j;
+	push_spec *push_spec;
+	git_reference *remote_ref;
+	push_status *status;
+	int error = 0;
+
+	git_vector_foreach(&push->status, i, status) {
+		/* If this ref update was successful (ok, not ng), it will have an empty message */
+		if (status->msg)
+			continue;
+
+		/* Find the corresponding remote ref */
+		if (!git_refspec_src_matches(fetch_spec, status->ref))
+			continue;
+
+		if ((error = git_refspec_transform_r(&remote_ref_name, fetch_spec, status->ref)) < 0)
+			goto on_error;
+
+		/* Find matching  push ref spec */
+		git_vector_foreach(&push->specs, j, push_spec) {
+			if (!strcmp(push_spec->rref, status->ref))
+				break;
+		}
+
+		/* Could not find the corresponding push ref spec for this push update */
+		if (j == push->specs.length)
+			continue;
+
+		/* Update the remote ref */
+		if (git_oid_iszero(&push_spec->loid)) {
+			error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name));
+
+			if (!error) {
+				if ((error = git_reference_delete(remote_ref)) < 0) {
+					git_reference_free(remote_ref);
+					goto on_error;
+				}
+				git_reference_free(remote_ref);
+			} else if (error == GIT_ENOTFOUND)
+				giterr_clear();
+			else
+				goto on_error;
+		} else if ((error = git_reference_create(NULL, push->remote->repo, git_buf_cstr(&remote_ref_name), &push_spec->loid, 1)) < 0)
+			goto on_error;
+	}
+
+	error = 0;
+
+on_error:
+	git_buf_free(&remote_ref_name);
+	return error;
+}
+
+static int revwalk(git_vector *commits, git_push *push)
+{
+	git_remote_head *head;
+	push_spec *spec;
+	git_revwalk *rw;
+	git_oid oid;
+	unsigned int i;
+	int error = -1;
+
+	if (git_revwalk_new(&rw, push->repo) < 0)
+		return -1;
+
+	git_revwalk_sorting(rw, GIT_SORT_TIME);
+
+	git_vector_foreach(&push->specs, i, spec) {
+		git_otype type;
+		size_t size;
+
+		if (git_oid_iszero(&spec->loid))
+			/*
+			 * Delete reference on remote side;
+			 * nothing to do here.
+			 */
+			continue;
+
+		if (git_oid_equal(&spec->loid, &spec->roid))
+			continue; /* up-to-date */
+
+		if (git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid) < 0)
+			goto on_error;
+
+		if (type == GIT_OBJ_TAG) {
+			git_tag *tag;
+			git_object *target;
+
+			if (git_packbuilder_insert(push->pb, &spec->loid, NULL) < 0)
+				goto on_error;
+
+			if (git_tag_lookup(&tag, push->repo, &spec->loid) < 0)
+				goto on_error;
+
+			if (git_tag_peel(&target, tag) < 0) {
+				git_tag_free(tag);
+				goto on_error;
+			}
+			git_tag_free(tag);
+
+			if (git_object_type(target) == GIT_OBJ_COMMIT) {
+				if (git_revwalk_push(rw, git_object_id(target)) < 0) {
+					git_object_free(target);
+					goto on_error;
+				}
+			} else {
+				if (git_packbuilder_insert(
+					push->pb, git_object_id(target), NULL) < 0) {
+					git_object_free(target);
+					goto on_error;
+				}
+			}
+			git_object_free(target);
+		} else if (git_revwalk_push(rw, &spec->loid) < 0)
+			goto on_error;
+
+		if (!spec->force) {
+			git_oid base;
+
+			if (git_oid_iszero(&spec->roid))
+				continue;
+
+			if (!git_odb_exists(push->repo->_odb, &spec->roid)) {
+				giterr_clear();
+				error = GIT_ENONFASTFORWARD;
+				goto on_error;
+			}
+
+			error = git_merge_base(&base, push->repo,
+					       &spec->loid, &spec->roid);
+
+			if (error == GIT_ENOTFOUND ||
+				(!error && !git_oid_equal(&base, &spec->roid))) {
+				giterr_clear();
+				error = GIT_ENONFASTFORWARD;
+				goto on_error;
+			}
+
+			if (error < 0)
+				goto on_error;
+		}
+	}
+
+	git_vector_foreach(&push->remote->refs, i, head) {
+		if (git_oid_iszero(&head->oid))
+			continue;
+
+		/* TODO */
+		git_revwalk_hide(rw, &head->oid);
+	}
+
+	while ((error = git_revwalk_next(&oid, rw)) == 0) {
+		git_oid *o = git__malloc(GIT_OID_RAWSZ);
+		GITERR_CHECK_ALLOC(o);
+		git_oid_cpy(o, &oid);
+		if (git_vector_insert(commits, o) < 0) {
+			error = -1;
+			goto on_error;
+		}
+	}
+
+on_error:
+	git_revwalk_free(rw);
+	return error == GIT_ITEROVER ? 0 : error;
+}
+
+static int enqueue_object(
+	const git_tree_entry *entry,
+	git_packbuilder *pb)
+{
+	switch (git_tree_entry_type(entry)) {
+		case GIT_OBJ_COMMIT:
+			return 0;
+		case GIT_OBJ_TREE:
+			return git_packbuilder_insert_tree(pb, &entry->oid);
+		default:
+			return git_packbuilder_insert(pb, &entry->oid, entry->filename);
+	}
+}
+
+static int queue_differences(
+	git_tree *base,
+	git_tree *delta,
+	git_packbuilder *pb)
+{
+	git_tree *b_child = NULL, *d_child = NULL;
+	size_t b_length = git_tree_entrycount(base);
+	size_t d_length = git_tree_entrycount(delta);
+	size_t i = 0, j = 0;
+	int error;
+
+	while (i < b_length && j < d_length) {
+		const git_tree_entry *b_entry = git_tree_entry_byindex(base, i);
+		const git_tree_entry *d_entry = git_tree_entry_byindex(delta, j);
+		int cmp = 0;
+
+		if (!git_oid_cmp(&b_entry->oid, &d_entry->oid))
+			goto loop;
+
+		cmp = strcmp(b_entry->filename, d_entry->filename);
+
+		/* If the entries are both trees and they have the same name but are
+		 * different, then we'll recurse after adding the right-hand entry */
+		if (!cmp &&
+			git_tree_entry__is_tree(b_entry) &&
+			git_tree_entry__is_tree(d_entry)) {
+			/* Add the right-hand entry */
+			if ((error = git_packbuilder_insert(pb, &d_entry->oid,
+				d_entry->filename)) < 0)
+				goto on_error;
+
+			/* Acquire the subtrees and recurse */
+			if ((error = git_tree_lookup(&b_child,
+					git_tree_owner(base), &b_entry->oid)) < 0 ||
+				(error = git_tree_lookup(&d_child,
+					git_tree_owner(delta), &d_entry->oid)) < 0 ||
+				(error = queue_differences(b_child, d_child, pb)) < 0)
+				goto on_error;
+
+			git_tree_free(b_child); b_child = NULL;
+			git_tree_free(d_child); d_child = NULL;
+		}
+		/* If the object is new or different in the right-hand tree,
+		 * then enumerate it */
+		else if (cmp >= 0 &&
+			(error = enqueue_object(d_entry, pb)) < 0)
+			goto on_error;
+
+	loop:
+		if (cmp <= 0) i++;
+		if (cmp >= 0) j++;
+	}
+
+	/* Drain the right-hand tree of entries */
+	for (; j < d_length; j++)
+		if ((error = enqueue_object(git_tree_entry_byindex(delta, j), pb)) < 0)
+			goto on_error;
+
+	error = 0;
+
+on_error:
+	if (b_child)
+		git_tree_free(b_child);
+
+	if (d_child)
+		git_tree_free(d_child);
+
+	return error;
+}
+
+static int queue_objects(git_push *push)
+{
+	git_vector commits = GIT_VECTOR_INIT;
+	git_oid *oid;
+	size_t i;
+	unsigned j;
+	int error;
+
+	if ((error = revwalk(&commits, push)) < 0)
+		goto on_error;
+
+	git_vector_foreach(&commits, i, oid) {
+		git_commit *parent = NULL, *commit;
+		git_tree *tree = NULL, *ptree = NULL;
+		size_t parentcount;
+
+		if ((error = git_commit_lookup(&commit,	push->repo, oid)) < 0)
+			goto on_error;
+
+		/* Insert the commit */
+		if ((error = git_packbuilder_insert(push->pb, oid, NULL)) < 0)
+			goto loop_error;
+
+		parentcount = git_commit_parentcount(commit);
+
+		if (!parentcount) {
+			if ((error = git_packbuilder_insert_tree(push->pb,
+				git_commit_tree_id(commit))) < 0)
+				goto loop_error;
+		} else {
+			if ((error = git_tree_lookup(&tree, push->repo,
+					git_commit_tree_id(commit))) < 0 ||
+				(error = git_packbuilder_insert(push->pb,
+					git_commit_tree_id(commit), NULL)) < 0)
+				goto loop_error;
+
+			/* For each parent, add the items which are different */
+			for (j = 0; j < parentcount; j++) {
+				if ((error = git_commit_parent(&parent, commit, j)) < 0 ||
+					(error = git_commit_tree(&ptree, parent)) < 0 ||
+					(error = queue_differences(ptree, tree, push->pb)) < 0)
+					goto loop_error;
+
+				git_tree_free(ptree); ptree = NULL;
+				git_commit_free(parent); parent = NULL;
+			}
+		}
+
+		error = 0;
+
+	loop_error:
+		if (tree)
+			git_tree_free(tree);
+
+		if (ptree)
+			git_tree_free(ptree);
+
+		if (parent)
+			git_commit_free(parent);
+
+		git_commit_free(commit);
+
+		if (error < 0)
+			goto on_error;
+	}
+
+	error = 0;
+
+on_error:
+	git_vector_foreach(&commits, i, oid)
+		git__free(oid);
+
+	git_vector_free(&commits);
+	return error;
+}
+
+static int calculate_work(git_push *push)
+{
+	git_remote_head *head;
+	push_spec *spec;
+	unsigned int i, j;
+
+	/* Update local and remote oids*/
+
+	git_vector_foreach(&push->specs, i, spec) {
+		if (spec->lref) {
+			/* This is a create or update.  Local ref must exist. */
+			if (git_reference_name_to_id(
+					&spec->loid, push->repo, spec->lref) < 0) {
+				giterr_set(GIT_ENOTFOUND, "No such reference '%s'", spec->lref);
+				return -1;
+			}
+		}
+
+		if (spec->rref) {
+			/* Remote ref may or may not (e.g. during create) already exist. */
+			git_vector_foreach(&push->remote->refs, j, head) {
+				if (!strcmp(spec->rref, head->name)) {
+					git_oid_cpy(&spec->roid, &head->oid);
+					break;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int do_push(git_push *push)
+{
+	int error;
+	git_transport *transport = push->remote->transport;
+
+	if (!transport->push) {
+		giterr_set(GITERR_NET, "Remote transport doesn't support push");
+		error = -1;
+		goto on_error;
+	}
+
+	/*
+	 * A pack-file MUST be sent if either create or update command
+	 * is used, even if the server already has all the necessary
+	 * objects.  In this case the client MUST send an empty pack-file.
+	 */
+
+	if ((error = git_packbuilder_new(&push->pb, push->repo)) < 0)
+		goto on_error;
+
+	git_packbuilder_set_threads(push->pb, push->pb_parallelism);
+
+	if ((error = calculate_work(push)) < 0 ||
+		(error = queue_objects(push)) < 0 ||
+		(error = transport->push(transport, push)) < 0)
+		goto on_error;
+
+	error = 0;
+
+on_error:
+	git_packbuilder_free(push->pb);
+	return error;
+}
+
+static int cb_filter_refs(git_remote_head *ref, void *data)
+{
+	git_remote *remote = (git_remote *) data;
+	return git_vector_insert(&remote->refs, ref);
+}
+
+static int filter_refs(git_remote *remote)
+{
+	git_vector_clear(&remote->refs);
+	return git_remote_ls(remote, cb_filter_refs, remote);
+}
+
+int git_push_finish(git_push *push)
+{
+	int error;
+
+	if (!git_remote_connected(push->remote) &&
+		(error = git_remote_connect(push->remote, GIT_DIRECTION_PUSH)) < 0)
+		return error;
+
+	if ((error = filter_refs(push->remote)) < 0 ||
+		(error = do_push(push)) < 0)
+		return error;
+
+	return 0;
+}
+
+int git_push_unpack_ok(git_push *push)
+{
+	return push->unpack_ok;
+}
+
+int git_push_status_foreach(git_push *push,
+		int (*cb)(const char *ref, const char *msg, void *data),
+		void *data)
+{
+	push_status *status;
+	unsigned int i;
+
+	git_vector_foreach(&push->status, i, status) {
+		if (cb(status->ref, status->msg, data) < 0)
+			return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+void git_push_status_free(push_status *status)
+{
+	if (status == NULL)
+		return;
+
+	if (status->msg)
+		git__free(status->msg);
+
+	git__free(status->ref);
+	git__free(status);
+}
+
+void git_push_free(git_push *push)
+{
+	push_spec *spec;
+	push_status *status;
+	unsigned int i;
+
+	if (push == NULL)
+		return;
+
+	git_vector_foreach(&push->specs, i, spec) {
+		free_refspec(spec);
+	}
+	git_vector_free(&push->specs);
+
+	git_vector_foreach(&push->status, i, status) {
+		git_push_status_free(status);
+	}
+	git_vector_free(&push->status);
+
+	git__free(push);
+}
diff --git a/libgit2/src/push.h b/libgit2/src/push.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/push.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_push_h__
+#define INCLUDE_push_h__
+
+#include "git2.h"
+
+typedef struct push_spec {
+	char *lref;
+	char *rref;
+
+	git_oid loid;
+	git_oid roid;
+
+	bool force;
+} push_spec;
+
+typedef struct push_status {
+	bool ok;
+
+	char *ref;
+	char *msg;
+} push_status;
+
+struct git_push {
+	git_repository *repo;
+	git_packbuilder *pb;
+	git_remote *remote;
+	git_vector specs;
+	bool report_status;
+
+	/* report-status */
+	bool unpack_ok;
+	git_vector status;
+
+	/* options */
+	unsigned pb_parallelism;
+};
+
+/**
+ * Free the given push status object
+ *
+ * @param status The push status object
+ */
+void git_push_status_free(push_status *status);
+
+#endif
diff --git a/libgit2/src/refdb.c b/libgit2/src/refdb.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refdb.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "posix.h"
+#include "git2/object.h"
+#include "git2/refs.h"
+#include "git2/refdb.h"
+#include "hash.h"
+#include "refdb.h"
+#include "refs.h"
+
+#include "git2/refdb_backend.h"
+
+int git_refdb_new(git_refdb **out, git_repository *repo)
+{
+	git_refdb *db;
+
+	assert(out && repo);
+
+	db = git__calloc(1, sizeof(*db));
+	GITERR_CHECK_ALLOC(db);
+
+	db->repo = repo;
+
+	*out = db;
+	GIT_REFCOUNT_INC(db);
+	return 0;
+}
+
+int git_refdb_open(git_refdb **out, git_repository *repo)
+{
+	git_refdb *db;
+	git_refdb_backend *dir;
+
+	assert(out && repo);
+
+	*out = NULL;
+
+	if (git_refdb_new(&db, repo) < 0)
+		return -1;
+
+	/* Add the default (filesystem) backend */
+	if (git_refdb_backend_fs(&dir, repo, db) < 0) {
+		git_refdb_free(db);
+		return -1;
+	}
+
+	db->repo = repo;
+	db->backend = dir;
+
+	*out = db;
+	return 0;
+}
+
+int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
+{
+	if (db->backend) {
+		if(db->backend->free)
+			db->backend->free(db->backend);
+		else
+			git__free(db->backend);
+	}
+
+	db->backend = backend;
+
+	return 0;
+}
+
+int git_refdb_compress(git_refdb *db)
+{
+	assert(db);
+	
+	if (db->backend->compress) {
+		return db->backend->compress(db->backend);
+	}
+	
+	return 0;
+}
+
+static void refdb_free(git_refdb *db)
+{
+	if (db->backend) {
+		if(db->backend->free)
+			db->backend->free(db->backend);
+		else
+			git__free(db->backend);
+	}
+
+	git__free(db);
+}
+
+void git_refdb_free(git_refdb *db)
+{
+	if (db == NULL)
+		return;
+
+	GIT_REFCOUNT_DEC(db, refdb_free);
+}
+
+int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
+{
+	assert(exists && refdb && refdb->backend);
+
+	return refdb->backend->exists(exists, refdb->backend, ref_name);
+}
+
+int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
+{
+	assert(db && db->backend && ref_name);
+
+	return db->backend->lookup(out, db->backend, ref_name);
+}
+
+int git_refdb_foreach(
+	git_refdb *db,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload)
+{
+	assert(db && db->backend);
+
+	return db->backend->foreach(db->backend, list_flags, callback, payload);
+}
+
+struct glob_cb_data {
+	const char *glob;
+	git_reference_foreach_cb callback;
+	void *payload;
+};
+
+static int fromglob_cb(const char *reference_name, void *payload)
+{
+	struct glob_cb_data *data = (struct glob_cb_data *)payload;
+
+	if (!p_fnmatch(data->glob, reference_name, 0))
+		return data->callback(reference_name, data->payload);
+
+	return 0;
+}
+
+int git_refdb_foreach_glob(
+	git_refdb *db,
+	const char *glob,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload)
+{
+	int error;
+	struct glob_cb_data data;
+
+	assert(db && db->backend && glob && callback);
+
+	if(db->backend->foreach_glob != NULL)
+		error = db->backend->foreach_glob(db->backend,
+			glob, list_flags, callback, payload);
+	else {
+		data.glob = glob;
+		data.callback = callback;
+		data.payload = payload;
+
+		error = db->backend->foreach(db->backend,
+			list_flags, fromglob_cb, &data);
+	}
+
+	return error;
+}
+
+int git_refdb_write(git_refdb *db, const git_reference *ref)
+{
+	assert(db && db->backend);
+
+	return db->backend->write(db->backend, ref);
+}
+
+int git_refdb_delete(struct git_refdb *db, const git_reference *ref)
+{
+	assert(db && db->backend);
+
+	return db->backend->delete(db->backend, ref);
+}
diff --git a/libgit2/src/refdb.h b/libgit2/src/refdb.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refdb.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_refdb_h__
+#define INCLUDE_refdb_h__
+
+#include "git2/refdb.h"
+#include "repository.h"
+
+struct git_refdb {
+	git_refcount rc;
+	git_repository *repo;
+	git_refdb_backend *backend;
+};
+
+int git_refdb_exists(
+	int *exists,
+	git_refdb *refdb,
+	const char *ref_name);
+
+int git_refdb_lookup(
+	git_reference **out,
+	git_refdb *refdb,
+	const char *ref_name);
+
+int git_refdb_foreach(
+	git_refdb *refdb,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload);
+
+int git_refdb_foreach_glob(
+	git_refdb *refdb,
+	const char *glob,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload);
+
+int git_refdb_write(git_refdb *refdb, const git_reference *ref);
+
+int git_refdb_delete(struct git_refdb *refdb, const git_reference *ref);
+
+#endif
diff --git a/libgit2/src/refdb_fs.c b/libgit2/src/refdb_fs.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refdb_fs.c
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refs.h"
+#include "hash.h"
+#include "repository.h"
+#include "fileops.h"
+#include "pack.h"
+#include "reflog.h"
+#include "config.h"
+#include "refdb.h"
+#include "refdb_fs.h"
+
+#include <git2/tag.h>
+#include <git2/object.h>
+#include <git2/refdb.h>
+#include <git2/refdb_backend.h>
+
+GIT__USE_STRMAP;
+
+#define DEFAULT_NESTING_LEVEL	5
+#define MAX_NESTING_LEVEL		10
+
+enum {
+	GIT_PACKREF_HAS_PEEL = 1,
+	GIT_PACKREF_WAS_LOOSE = 2
+};
+
+struct packref {
+	git_oid oid;
+	git_oid peel;
+	char flags;
+	char name[GIT_FLEX_ARRAY];
+};
+
+typedef struct refdb_fs_backend {
+	git_refdb_backend parent;
+
+	git_repository *repo;
+	const char *path;
+	git_refdb *refdb;
+
+	git_refcache refcache;
+} refdb_fs_backend;
+
+static int reference_read(
+	git_buf *file_content,
+	time_t *mtime,
+	const char *repo_path,
+	const char *ref_name,
+	int *updated)
+{
+	git_buf path = GIT_BUF_INIT;
+	int result;
+
+	assert(file_content && repo_path && ref_name);
+
+	/* Determine the full path of the file */
+	if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
+		return -1;
+	
+	result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, NULL, updated);
+	git_buf_free(&path);
+
+	return result;
+}
+
+static int packed_parse_oid(
+	struct packref **ref_out,
+	const char **buffer_out,
+	const char *buffer_end)
+{
+	struct packref *ref = NULL;
+
+	const char *buffer = *buffer_out;
+	const char *refname_begin, *refname_end;
+
+	size_t refname_len;
+	git_oid id;
+
+	refname_begin = (buffer + GIT_OID_HEXSZ + 1);
+	if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
+		goto corrupt;
+
+	/* Is this a valid object id? */
+	if (git_oid_fromstr(&id, buffer) < 0)
+		goto corrupt;
+
+	refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
+	if (refname_end == NULL)
+		refname_end = buffer_end;
+
+	if (refname_end[-1] == '\r')
+		refname_end--;
+
+	refname_len = refname_end - refname_begin;
+
+	ref = git__malloc(sizeof(struct packref) + refname_len + 1);
+	GITERR_CHECK_ALLOC(ref);
+
+	memcpy(ref->name, refname_begin, refname_len);
+	ref->name[refname_len] = 0;
+
+	git_oid_cpy(&ref->oid, &id);
+
+	ref->flags = 0;
+
+	*ref_out = ref;
+	*buffer_out = refname_end + 1;
+
+	return 0;
+
+corrupt:
+	git__free(ref);
+	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+	return -1;
+}
+
+static int packed_parse_peel(
+	struct packref *tag_ref,
+	const char **buffer_out,
+	const char *buffer_end)
+{
+	const char *buffer = *buffer_out + 1;
+
+	assert(buffer[-1] == '^');
+
+	/* Ensure it's not the first entry of the file */
+	if (tag_ref == NULL)
+		goto corrupt;
+
+	/* Ensure reference is a tag */
+	if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
+		goto corrupt;
+
+	if (buffer + GIT_OID_HEXSZ > buffer_end)
+		goto corrupt;
+
+	/* Is this a valid object id? */
+	if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
+		goto corrupt;
+
+	buffer = buffer + GIT_OID_HEXSZ;
+	if (*buffer == '\r')
+		buffer++;
+
+	if (buffer != buffer_end) {
+		if (*buffer == '\n')
+			buffer++;
+		else
+			goto corrupt;
+	}
+
+	*buffer_out = buffer;
+	return 0;
+
+corrupt:
+	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
+	return -1;
+}
+
+static int packed_load(refdb_fs_backend *backend)
+{
+	int result, updated;
+	git_buf packfile = GIT_BUF_INIT;
+	const char *buffer_start, *buffer_end;
+	git_refcache *ref_cache = &backend->refcache;
+
+	/* First we make sure we have allocated the hash table */
+	if (ref_cache->packfile == NULL) {
+		ref_cache->packfile = git_strmap_alloc();
+		GITERR_CHECK_ALLOC(ref_cache->packfile);
+	}
+	
+	result = reference_read(&packfile, &ref_cache->packfile_time,
+		backend->path, GIT_PACKEDREFS_FILE, &updated);
+
+	/*
+	 * If we couldn't find the file, we need to clear the table and
+	 * return. On any other error, we return that error. If everything
+	 * went fine and the file wasn't updated, then there's nothing new
+	 * for us here, so just return. Anything else means we need to
+	 * refresh the packed refs.
+	 */
+	if (result == GIT_ENOTFOUND) {
+		git_strmap_clear(ref_cache->packfile);
+		return 0;
+	}
+
+	if (result < 0)
+		return -1;
+	
+	if (!updated)
+		return 0;
+
+	/*
+	 * At this point, we want to refresh the packed refs. We already
+	 * have the contents in our buffer.
+	 */
+	git_strmap_clear(ref_cache->packfile);
+
+	buffer_start = (const char *)packfile.ptr;
+	buffer_end = (const char *)(buffer_start) + packfile.size;
+
+	while (buffer_start < buffer_end && buffer_start[0] == '#') {
+		buffer_start = strchr(buffer_start, '\n');
+		if (buffer_start == NULL)
+			goto parse_failed;
+
+		buffer_start++;
+	}
+
+	while (buffer_start < buffer_end) {
+		int err;
+		struct packref *ref = NULL;
+
+		if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
+			goto parse_failed;
+
+		if (buffer_start[0] == '^') {
+			if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
+				goto parse_failed;
+		}
+
+		git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
+		if (err < 0)
+			goto parse_failed;
+	}
+
+	git_buf_free(&packfile);
+	return 0;
+
+parse_failed:
+	git_strmap_free(ref_cache->packfile);
+	ref_cache->packfile = NULL;
+	git_buf_free(&packfile);
+	return -1;
+}
+
+static int loose_parse_oid(git_oid *oid, git_buf *file_content)
+{
+	size_t len;
+	const char *str;
+
+	len = git_buf_len(file_content);
+	if (len < GIT_OID_HEXSZ)
+		goto corrupted;
+
+	/* str is guranteed to be zero-terminated */
+	str = git_buf_cstr(file_content);
+
+	/* we need to get 40 OID characters from the file */
+	if (git_oid_fromstr(oid, git_buf_cstr(file_content)) < 0)
+		goto corrupted;
+
+	/* If the file is longer than 40 chars, the 41st must be a space */
+	str += GIT_OID_HEXSZ;
+	if (*str == '\0' || git__isspace(*str))
+		return 0;
+
+corrupted:
+	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+	return -1;
+}
+
+static int loose_lookup_to_packfile(
+	struct packref **ref_out,
+	refdb_fs_backend *backend,
+	const char *name)
+{
+	git_buf ref_file = GIT_BUF_INIT;
+	struct packref *ref = NULL;
+	size_t name_len;
+
+	*ref_out = NULL;
+
+	if (reference_read(&ref_file, NULL, backend->path, name, NULL) < 0)
+		return -1;
+
+	git_buf_rtrim(&ref_file);
+
+	name_len = strlen(name);
+	ref = git__malloc(sizeof(struct packref) + name_len + 1);
+	GITERR_CHECK_ALLOC(ref);
+
+	memcpy(ref->name, name, name_len);
+	ref->name[name_len] = 0;
+
+	if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
+		git_buf_free(&ref_file);
+		git__free(ref);
+		return -1;
+	}
+
+	ref->flags = GIT_PACKREF_WAS_LOOSE;
+
+	*ref_out = ref;
+	git_buf_free(&ref_file);
+	return 0;
+}
+
+
+static int _dirent_loose_load(void *data, git_buf *full_path)
+{
+	refdb_fs_backend *backend = (refdb_fs_backend *)data;
+	void *old_ref = NULL;
+	struct packref *ref;
+	const char *file_path;
+	int err;
+
+	if (git_path_isdir(full_path->ptr) == true)
+		return git_path_direach(full_path, _dirent_loose_load, backend);
+
+	file_path = full_path->ptr + strlen(backend->path);
+
+	if (loose_lookup_to_packfile(&ref, backend, file_path) < 0)
+		return -1;
+
+	git_strmap_insert2(
+		backend->refcache.packfile, ref->name, ref, old_ref, err);
+	if (err < 0) {
+		git__free(ref);
+		return -1;
+	}
+
+	git__free(old_ref);
+	return 0;
+}
+
+/*
+ * Load all the loose references from the repository
+ * into the in-memory Packfile, and build a vector with
+ * all the references so it can be written back to
+ * disk.
+ */
+static int packed_loadloose(refdb_fs_backend *backend)
+{
+	git_buf refs_path = GIT_BUF_INIT;
+	int result;
+
+	/* the packfile must have been previously loaded! */
+	assert(backend->refcache.packfile);
+
+	if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
+		return -1;
+
+	/*
+	 * Load all the loose files from disk into the Packfile table.
+	 * This will overwrite any old packed entries with their
+	 * updated loose versions
+	 */
+	result = git_path_direach(&refs_path, _dirent_loose_load, backend);
+	git_buf_free(&refs_path);
+
+	return result;
+}
+
+static int refdb_fs_backend__exists(
+	int *exists,
+	git_refdb_backend *_backend,
+	const char *ref_name)
+{
+	refdb_fs_backend *backend;
+	git_buf ref_path = GIT_BUF_INIT;
+
+	assert(_backend);
+	backend = (refdb_fs_backend *)_backend;
+
+	if (packed_load(backend) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&ref_path, backend->path, ref_name) < 0)
+		return -1;
+
+	if (git_path_isfile(ref_path.ptr) == true ||
+		git_strmap_exists(backend->refcache.packfile, ref_path.ptr))
+		*exists = 1;
+	else
+		*exists = 0;
+
+	git_buf_free(&ref_path);
+	return 0;
+}
+
+static const char *loose_parse_symbolic(git_buf *file_content)
+{
+	const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
+	const char *refname_start;
+
+	refname_start = (const char *)file_content->ptr;
+
+	if (git_buf_len(file_content) < header_len + 1) {
+		giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
+		return NULL;
+	}
+
+	/*
+	 * Assume we have already checked for the header
+	 * before calling this function
+	 */
+	refname_start += header_len;
+
+	return refname_start;
+}
+
+static int loose_lookup(
+	git_reference **out,
+	refdb_fs_backend *backend,
+	const char *ref_name)
+{
+	const char *target;
+	git_oid oid;
+	git_buf ref_file = GIT_BUF_INIT;
+	int error = 0;
+
+	error = reference_read(&ref_file, NULL, backend->path, ref_name, NULL);
+
+	if (error < 0)
+		goto done;
+
+	if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
+		git_buf_rtrim(&ref_file);
+
+		if ((target = loose_parse_symbolic(&ref_file)) == NULL) {
+			error = -1;
+			goto done;
+		}
+
+		*out = git_reference__alloc(backend->refdb, ref_name, NULL, target);
+	} else {
+		if ((error = loose_parse_oid(&oid, &ref_file)) < 0)
+			goto done;
+		
+		*out = git_reference__alloc(backend->refdb, ref_name, &oid, NULL);
+	}
+
+	if (*out == NULL)
+		error = -1;
+
+done:
+	git_buf_free(&ref_file);
+	return error;
+}
+
+static int packed_map_entry(
+	struct packref **entry,
+	khiter_t *pos,
+	refdb_fs_backend *backend,
+	const char *ref_name)
+{
+	git_strmap *packfile_refs;
+
+	if (packed_load(backend) < 0)
+		return -1;
+	
+	/* Look up on the packfile */
+	packfile_refs = backend->refcache.packfile;
+
+	*pos = git_strmap_lookup_index(packfile_refs, ref_name);
+	
+	if (!git_strmap_valid_index(packfile_refs, *pos)) {
+		giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref_name);
+		return GIT_ENOTFOUND;
+	}
+
+	*entry = git_strmap_value_at(packfile_refs, *pos);
+	
+	return 0;
+}
+
+static int packed_lookup(
+	git_reference **out,
+	refdb_fs_backend *backend,
+	const char *ref_name)
+{
+	struct packref *entry;
+	khiter_t pos;
+	int error = 0;
+	
+	if ((error = packed_map_entry(&entry, &pos, backend, ref_name)) < 0)
+		return error;
+
+	if ((*out = git_reference__alloc(backend->refdb, ref_name, &entry->oid, NULL)) == NULL)
+		return -1;
+	
+	return 0;
+}
+
+static int refdb_fs_backend__lookup(
+	git_reference **out,
+	git_refdb_backend *_backend,
+	const char *ref_name)
+{
+	refdb_fs_backend *backend;
+	int result;
+
+	assert(_backend);
+
+	backend = (refdb_fs_backend *)_backend;
+
+	if ((result = loose_lookup(out, backend, ref_name)) == 0)
+		return 0;
+
+	/* only try to lookup this reference on the packfile if it
+	 * wasn't found on the loose refs; not if there was a critical error */
+	if (result == GIT_ENOTFOUND) {
+		giterr_clear();
+		result = packed_lookup(out, backend, ref_name);
+	}
+
+	return result;
+}
+
+struct dirent_list_data {
+	refdb_fs_backend *backend;
+	size_t repo_path_len;
+	unsigned int list_type:2;
+
+	git_reference_foreach_cb callback;
+	void *callback_payload;
+	int callback_error;
+};
+
+static git_ref_t loose_guess_rtype(const git_buf *full_path)
+{
+	git_buf ref_file = GIT_BUF_INIT;
+	git_ref_t type;
+
+	type = GIT_REF_INVALID;
+
+	if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {
+		if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
+			type = GIT_REF_SYMBOLIC;
+		else
+			type = GIT_REF_OID;
+	}
+
+	git_buf_free(&ref_file);
+	return type;
+}
+
+static int _dirent_loose_listall(void *_data, git_buf *full_path)
+{
+	struct dirent_list_data *data = (struct dirent_list_data *)_data;
+	const char *file_path = full_path->ptr + data->repo_path_len;
+
+	if (git_path_isdir(full_path->ptr) == true)
+		return git_path_direach(full_path, _dirent_loose_listall, _data);
+
+	/* do not add twice a reference that exists already in the packfile */
+	if (git_strmap_exists(data->backend->refcache.packfile, file_path))
+		return 0;
+
+	if (data->list_type != GIT_REF_LISTALL) {
+		if ((data->list_type & loose_guess_rtype(full_path)) == 0)
+			return 0; /* we are filtering out this reference */
+	}
+
+	/* Locked references aren't returned */
+	if (!git__suffixcmp(file_path, GIT_FILELOCK_EXTENSION))
+		return 0;
+
+	if (data->callback(file_path, data->callback_payload))
+		data->callback_error = GIT_EUSER;
+
+	return data->callback_error;
+}
+
+static int refdb_fs_backend__foreach(
+	git_refdb_backend *_backend,
+	unsigned int list_type,
+	git_reference_foreach_cb callback,
+	void *payload)
+{
+	refdb_fs_backend *backend;
+	int result;
+	struct dirent_list_data data;
+	git_buf refs_path = GIT_BUF_INIT;
+	const char *ref_name;
+	void *ref = NULL;
+	
+	GIT_UNUSED(ref);
+
+	assert(_backend);
+	backend = (refdb_fs_backend *)_backend;
+
+	if (packed_load(backend) < 0)
+		return -1;
+	
+	/* list all the packed references first */
+	if (list_type & GIT_REF_OID) {
+		git_strmap_foreach(backend->refcache.packfile, ref_name, ref, {
+			if (callback(ref_name, payload))
+				return GIT_EUSER;
+		});
+	}
+
+	/* now list the loose references, trying not to
+	 * duplicate the ref names already in the packed-refs file */
+
+	data.repo_path_len = strlen(backend->path);
+	data.list_type = list_type;
+	data.backend = backend;
+	data.callback = callback;
+	data.callback_payload = payload;
+	data.callback_error = 0;
+
+	if (git_buf_joinpath(&refs_path, backend->path, GIT_REFS_DIR) < 0)
+		return -1;
+
+	result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
+
+	git_buf_free(&refs_path);
+
+	return data.callback_error ? GIT_EUSER : result;
+}
+
+static int loose_write(refdb_fs_backend *backend, const git_reference *ref)
+{
+	git_filebuf file = GIT_FILEBUF_INIT;
+	git_buf ref_path = GIT_BUF_INIT;
+
+	/* Remove a possibly existing empty directory hierarchy
+	 * which name would collide with the reference name
+	 */
+	if (git_futils_rmdir_r(ref->name, backend->path,
+		GIT_RMDIR_SKIP_NONEMPTY) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0)
+		return -1;
+
+	if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
+		git_buf_free(&ref_path);
+		return -1;
+	}
+
+	git_buf_free(&ref_path);
+
+	if (ref->type == GIT_REF_OID) {
+		char oid[GIT_OID_HEXSZ + 1];
+
+		git_oid_fmt(oid, &ref->target.oid);
+		oid[GIT_OID_HEXSZ] = '\0';
+
+		git_filebuf_printf(&file, "%s\n", oid);
+
+	} else if (ref->type == GIT_REF_SYMBOLIC) {
+		git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
+	} else {
+		assert(0); /* don't let this happen */
+	}
+
+	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
+}
+
+static int packed_sort(const void *a, const void *b)
+{
+	const struct packref *ref_a = (const struct packref *)a;
+	const struct packref *ref_b = (const struct packref *)b;
+
+	return strcmp(ref_a->name, ref_b->name);
+}
+
+/*
+ * Find out what object this reference resolves to.
+ *
+ * For references that point to a 'big' tag (e.g. an
+ * actual tag object on the repository), we need to
+ * cache on the packfile the OID of the object to
+ * which that 'big tag' is pointing to.
+ */
+static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
+{
+	git_object *object;
+
+	if (ref->flags & GIT_PACKREF_HAS_PEEL)
+		return 0;
+
+	/*
+	 * Only applies to tags, i.e. references
+	 * in the /refs/tags folder
+	 */
+	if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
+		return 0;
+
+	/*
+	 * Find the tagged object in the repository
+	 */
+	if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJ_ANY) < 0)
+		return -1;
+
+	/*
+	 * If the tagged object is a Tag object, we need to resolve it;
+	 * if the ref is actually a 'weak' ref, we don't need to resolve
+	 * anything.
+	 */
+	if (git_object_type(object) == GIT_OBJ_TAG) {
+		git_tag *tag = (git_tag *)object;
+
+		/*
+		 * Find the object pointed at by this tag
+		 */
+		git_oid_cpy(&ref->peel, git_tag_target_id(tag));
+		ref->flags |= GIT_PACKREF_HAS_PEEL;
+
+		/*
+		 * The reference has now cached the resolved OID, and is
+		 * marked at such. When written to the packfile, it'll be
+		 * accompanied by this resolved oid
+		 */
+	}
+
+	git_object_free(object);
+	return 0;
+}
+
+/*
+ * Write a single reference into a packfile
+ */
+static int packed_write_ref(struct packref *ref, git_filebuf *file)
+{
+	char oid[GIT_OID_HEXSZ + 1];
+
+	git_oid_fmt(oid, &ref->oid);
+	oid[GIT_OID_HEXSZ] = 0;
+
+	/*
+	 * For references that peel to an object in the repo, we must
+	 * write the resulting peel on a separate line, e.g.
+	 *
+	 *	6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
+	 *	^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
+	 *
+	 * This obviously only applies to tags.
+	 * The required peels have already been loaded into `ref->peel_target`.
+	 */
+	if (ref->flags & GIT_PACKREF_HAS_PEEL) {
+		char peel[GIT_OID_HEXSZ + 1];
+		git_oid_fmt(peel, &ref->peel);
+		peel[GIT_OID_HEXSZ] = 0;
+
+		if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
+			return -1;
+	} else {
+		if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Remove all loose references
+ *
+ * Once we have successfully written a packfile,
+ * all the loose references that were packed must be
+ * removed from disk.
+ *
+ * This is a dangerous method; make sure the packfile
+ * is well-written, because we are destructing references
+ * here otherwise.
+ */
+static int packed_remove_loose(
+	refdb_fs_backend *backend,
+	git_vector *packing_list)
+{
+	size_t i;
+	git_buf full_path = GIT_BUF_INIT;
+	int failed = 0;
+
+	for (i = 0; i < packing_list->length; ++i) {
+		struct packref *ref = git_vector_get(packing_list, i);
+
+		if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
+			continue;
+
+		if (git_buf_joinpath(&full_path, backend->path, ref->name) < 0)
+			return -1; /* critical; do not try to recover on oom */
+
+		if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
+			if (failed)
+				continue;
+
+			giterr_set(GITERR_REFERENCE,
+				"Failed to remove loose reference '%s' after packing: %s",
+				full_path.ptr, strerror(errno));
+
+			failed = 1;
+		}
+
+		/*
+		 * if we fail to remove a single file, this is *not* good,
+		 * but we should keep going and remove as many as possible.
+		 * After we've removed as many files as possible, we return
+		 * the error code anyway.
+		 */
+	}
+
+	git_buf_free(&full_path);
+	return failed ? -1 : 0;
+}
+
+/*
+ * Write all the contents in the in-memory packfile to disk.
+ */
+static int packed_write(refdb_fs_backend *backend)
+{
+	git_filebuf pack_file = GIT_FILEBUF_INIT;
+	size_t i;
+	git_buf pack_file_path = GIT_BUF_INIT;
+	git_vector packing_list;
+	unsigned int total_refs;
+
+	assert(backend && backend->refcache.packfile);
+
+	total_refs =
+		(unsigned int)git_strmap_num_entries(backend->refcache.packfile);
+
+	if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
+		return -1;
+
+	/* Load all the packfile into a vector */
+	{
+		struct packref *reference;
+
+		/* cannot fail: vector already has the right size */
+		git_strmap_foreach_value(backend->refcache.packfile, reference, {
+			git_vector_insert(&packing_list, reference);
+		});
+	}
+
+	/* sort the vector so the entries appear sorted on the packfile */
+	git_vector_sort(&packing_list);
+
+	/* Now we can open the file! */
+	if (git_buf_joinpath(&pack_file_path,
+		backend->path, GIT_PACKEDREFS_FILE) < 0)
+		goto cleanup_memory;
+
+	if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
+		goto cleanup_packfile;
+
+	/* Packfiles have a header... apparently
+	 * This is in fact not required, but we might as well print it
+	 * just for kicks */
+	if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
+		goto cleanup_packfile;
+
+	for (i = 0; i < packing_list.length; ++i) {
+		struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
+
+		if (packed_find_peel(backend, ref) < 0)
+			goto cleanup_packfile;
+
+		if (packed_write_ref(ref, &pack_file) < 0)
+			goto cleanup_packfile;
+	}
+
+	/* if we've written all the references properly, we can commit
+	 * the packfile to make the changes effective */
+	if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
+		goto cleanup_memory;
+
+	/* when and only when the packfile has been properly written,
+	 * we can go ahead and remove the loose refs */
+	 if (packed_remove_loose(backend, &packing_list) < 0)
+		 goto cleanup_memory;
+
+	 {
+		struct stat st;
+		if (p_stat(pack_file_path.ptr, &st) == 0)
+			backend->refcache.packfile_time = st.st_mtime;
+	 }
+
+	git_vector_free(&packing_list);
+	git_buf_free(&pack_file_path);
+
+	/* we're good now */
+	return 0;
+
+cleanup_packfile:
+	git_filebuf_cleanup(&pack_file);
+
+cleanup_memory:
+	git_vector_free(&packing_list);
+	git_buf_free(&pack_file_path);
+
+	return -1;
+}
+
+static int refdb_fs_backend__write(
+	git_refdb_backend *_backend,
+	const git_reference *ref)
+{
+	refdb_fs_backend *backend;
+
+	assert(_backend);
+	backend = (refdb_fs_backend *)_backend;
+
+	return loose_write(backend, ref);
+}
+
+static int refdb_fs_backend__delete(
+	git_refdb_backend *_backend,
+	const git_reference *ref)
+{
+	refdb_fs_backend *backend;
+	git_repository *repo;
+	git_buf loose_path = GIT_BUF_INIT;
+	struct packref *pack_ref;
+	khiter_t pack_ref_pos;
+	int error = 0, pack_error;
+	bool loose_deleted;
+
+	assert(_backend);
+	assert(ref);
+
+	backend = (refdb_fs_backend *)_backend;
+	repo = backend->repo;
+
+	/* If a loose reference exists, remove it from the filesystem */
+	
+	if (git_buf_joinpath(&loose_path, repo->path_repository, ref->name) < 0)
+		return -1;
+
+	if (git_path_isfile(loose_path.ptr)) {
+		error = p_unlink(loose_path.ptr);
+		loose_deleted = 1;
+	}
+	
+	git_buf_free(&loose_path);
+
+	if (error != 0)
+		return error;
+
+	/* If a packed reference exists, remove it from the packfile and repack */
+
+	if ((pack_error = packed_map_entry(&pack_ref, &pack_ref_pos, backend, ref->name)) == 0) {
+		git_strmap_delete_at(backend->refcache.packfile, pack_ref_pos);
+		git__free(pack_ref);
+
+		error = packed_write(backend);
+	}
+	
+	if (pack_error == GIT_ENOTFOUND)
+		error = loose_deleted ? 0 : GIT_ENOTFOUND;
+	else
+		error = pack_error;
+
+	return error;
+}
+
+static int refdb_fs_backend__compress(git_refdb_backend *_backend)
+{
+	refdb_fs_backend *backend;
+
+	assert(_backend);
+	backend = (refdb_fs_backend *)_backend;
+
+	if (packed_load(backend) < 0 || /* load the existing packfile */
+		packed_loadloose(backend) < 0 || /* add all the loose refs */
+		packed_write(backend) < 0) /* write back to disk */
+		return -1;
+
+	return 0;
+}
+
+static void refcache_free(git_refcache *refs)
+{
+	assert(refs);
+
+	if (refs->packfile) {
+		struct packref *reference;
+
+		git_strmap_foreach_value(refs->packfile, reference, {
+			git__free(reference);
+		});
+
+		git_strmap_free(refs->packfile);
+	}
+}
+
+static void refdb_fs_backend__free(git_refdb_backend *_backend)
+{
+	refdb_fs_backend *backend;
+
+	assert(_backend);
+	backend = (refdb_fs_backend *)_backend;
+
+	refcache_free(&backend->refcache);
+	git__free(backend);
+}
+
+int git_refdb_backend_fs(
+	git_refdb_backend **backend_out,
+	git_repository *repository,
+	git_refdb *refdb)
+{
+	refdb_fs_backend *backend;
+
+	backend = git__calloc(1, sizeof(refdb_fs_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	backend->repo = repository;
+	backend->path = repository->path_repository;
+	backend->refdb = refdb;
+
+	backend->parent.exists = &refdb_fs_backend__exists;
+	backend->parent.lookup = &refdb_fs_backend__lookup;
+	backend->parent.foreach = &refdb_fs_backend__foreach;
+	backend->parent.write = &refdb_fs_backend__write;
+	backend->parent.delete = &refdb_fs_backend__delete;
+	backend->parent.compress = &refdb_fs_backend__compress;
+	backend->parent.free = &refdb_fs_backend__free;
+
+	*backend_out = (git_refdb_backend *)backend;
+	return 0;
+}
diff --git a/libgit2/src/refdb_fs.h b/libgit2/src/refdb_fs.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refdb_fs.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_refdb_fs_h__
+#define INCLUDE_refdb_fs_h__
+
+typedef struct {
+	git_strmap *packfile;
+	time_t packfile_time;
+} git_refcache;
+
+#endif
diff --git a/libgit2/src/reflog.c b/libgit2/src/reflog.c
--- a/libgit2/src/reflog.c
+++ b/libgit2/src/reflog.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,7 +10,7 @@
 #include "filebuf.h"
 #include "signature.h"
 
-static int reflog_init(git_reflog **reflog, git_reference *ref)
+static int reflog_init(git_reflog **reflog, const git_reference *ref)
 {
 	git_reflog *log;
 
@@ -28,68 +28,70 @@
 		return -1;
 	}
 
+	log->owner = git_reference_owner(ref);
 	*reflog = log;
 
 	return 0;
 }
 
-static int reflog_write(const char *log_path, const char *oid_old,
-			const char *oid_new, const git_signature *committer,
-			const char *msg)
+static int serialize_reflog_entry(
+	git_buf *buf,
+	const git_oid *oid_old,
+	const git_oid *oid_new,
+	const git_signature *committer,
+	const char *msg)
 {
-	int error;
-	git_buf log = GIT_BUF_INIT;
-	git_filebuf fbuf = GIT_FILEBUF_INIT;
-	bool trailing_newline = false;
+	char raw_old[GIT_OID_HEXSZ+1];
+	char raw_new[GIT_OID_HEXSZ+1];
 
-	assert(log_path && oid_old && oid_new && committer);
+	git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
+	git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
 
-	if (msg) {
-		const char *newline = strchr(msg, '\n');
-		if (newline) {
-			if (*(newline + 1) == '\0')
-				trailing_newline = true;
-			else {
-				giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
-				return -1;
-			}
-		}
-	}
+	git_buf_clear(buf);
 
-	git_buf_puts(&log, oid_old);
-	git_buf_putc(&log, ' ');
+	git_buf_puts(buf, raw_old);
+	git_buf_putc(buf, ' ');
+	git_buf_puts(buf, raw_new);
 
-	git_buf_puts(&log, oid_new);
+	git_signature__writebuf(buf, " ", committer);
 
-	git_signature__writebuf(&log, " ", committer);
-	git_buf_truncate(&log, log.size - 1); /* drop LF */
+	/* drop trailing LF */
+	git_buf_rtrim(buf);
 
 	if (msg) {
-		git_buf_putc(&log, '\t');
-		git_buf_puts(&log, msg);
+		git_buf_putc(buf, '\t');
+		git_buf_puts(buf, msg);
 	}
 
-	if (!trailing_newline)
-		git_buf_putc(&log, '\n');
+	git_buf_putc(buf, '\n');
 
-	if (git_buf_oom(&log)) {
-		git_buf_free(&log);
-		return -1;
-	}
+	return git_buf_oom(buf);
+}
 
-	error = git_filebuf_open(&fbuf, log_path, GIT_FILEBUF_APPEND);
-	if (!error) {
-		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
-			git_filebuf_cleanup(&fbuf);
-		else
-			error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
-	}
+static int reflog_entry_new(git_reflog_entry **entry)
+{
+	git_reflog_entry *e;
 
-	git_buf_free(&log);
+	assert(entry);
 
-	return error;
+	e = git__malloc(sizeof(git_reflog_entry));
+	GITERR_CHECK_ALLOC(e);
+
+	memset(e, 0, sizeof(git_reflog_entry));
+
+	*entry = e;
+
+	return 0;
 }
 
+static void reflog_entry_free(git_reflog_entry *entry)
+{
+	git_signature_free(entry->committer);
+
+	git__free(entry->msg);
+	git__free(entry);
+}
+
 static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
 {
 	const char *ptr;
@@ -105,8 +107,8 @@
 	} while (0)
 
 	while (buf_size > GIT_REFLOG_SIZE_MIN) {
-		entry = git__malloc(sizeof(git_reflog_entry));
-		GITERR_CHECK_ALLOC(entry);
+		if (reflog_entry_new(&entry) < 0)
+			return -1;
 
 		entry->committer = git__malloc(sizeof(git_signature));
 		GITERR_CHECK_ALLOC(entry->committer);
@@ -153,25 +155,24 @@
 #undef seek_forward
 
 fail:
-	if (entry) {
-		git__free(entry->committer);
-		git__free(entry);
-	}
+	if (entry)
+		reflog_entry_free(entry);
+
 	return -1;
 }
 
 void git_reflog_free(git_reflog *reflog)
 {
-	unsigned int i;
+	size_t i;
 	git_reflog_entry *entry;
 
+	if (reflog == NULL)
+		return;
+
 	for (i=0; i < reflog->entries.length; i++) {
 		entry = git_vector_get(&reflog->entries, i);
 
-		git_signature_free(entry->committer);
-
-		git__free(entry->msg);
-		git__free(entry);
+		reflog_entry_free(entry);
 	}
 
 	git_vector_free(&reflog->entries);
@@ -179,110 +180,230 @@
 	git__free(reflog);
 }
 
-int git_reflog_read(git_reflog **reflog, git_reference *ref)
+static int retrieve_reflog_path(git_buf *path, const git_reference *ref)
 {
-	int error;
+	return git_buf_join_n(path, '/', 3,
+		git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name);
+}
+
+static int create_new_reflog_file(const char *filepath)
+{
+	int fd, error;
+
+	if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+		return error;
+
+	if ((fd = p_open(filepath,
+			O_WRONLY | O_CREAT | O_TRUNC,
+			GIT_REFLOG_FILE_MODE)) < 0)
+		return -1;
+
+	return p_close(fd);
+}
+
+int git_reflog_read(git_reflog **reflog, const git_reference *ref)
+{
+	int error = -1;
 	git_buf log_path = GIT_BUF_INIT;
 	git_buf log_file = GIT_BUF_INIT;
 	git_reflog *log = NULL;
 
+	assert(reflog && ref);
+
 	*reflog = NULL;
 
 	if (reflog_init(&log, ref) < 0)
 		return -1;
 
-	error = git_buf_join_n(&log_path, '/', 3,
-		ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+	if (retrieve_reflog_path(&log_path, ref) < 0)
+		goto cleanup;
 
-	if (!error)
-		error = git_futils_readbuffer(&log_file, log_path.ptr);
+	error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
+	if (error < 0 && error != GIT_ENOTFOUND)
+		goto cleanup;
 
-	if (!error)
-		error = reflog_parse(log, log_file.ptr, log_file.size);
+	if ((error == GIT_ENOTFOUND) &&
+		((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
+		goto cleanup;
 
-	if (!error)
-		*reflog = log;
-	else
-		git_reflog_free(log);
+	if ((error = reflog_parse(log,
+		git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
+		goto cleanup;
 
+	*reflog = log;
+	goto success;
+
+cleanup:
+	git_reflog_free(log);
+
+success:
 	git_buf_free(&log_file);
 	git_buf_free(&log_path);
 
 	return error;
 }
 
-int git_reflog_write(git_reference *ref, const git_oid *oid_old,
-				const git_signature *committer, const char *msg)
+int git_reflog_write(git_reflog *reflog)
 {
-	int error;
-	char old[GIT_OID_HEXSZ+1];
-	char new[GIT_OID_HEXSZ+1];
+	int error = -1;
+	unsigned int i;
+	git_reflog_entry *entry;
 	git_buf log_path = GIT_BUF_INIT;
-	git_reference *r;
-	const git_oid *oid;
+	git_buf log = GIT_BUF_INIT;
+	git_filebuf fbuf = GIT_FILEBUF_INIT;
 
-	if ((error = git_reference_resolve(&r, ref)) < 0)
-		return error;
+	assert(reflog);
 
-	oid = git_reference_oid(r);
-	if (oid == NULL) {
-		giterr_set(GITERR_REFERENCE,
-			"Failed to write reflog. Cannot resolve reference `%s`", r->name);
-		git_reference_free(r);
+	if (git_buf_join_n(&log_path, '/', 3,
+		git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0)
 		return -1;
+
+	if (!git_path_isfile(git_buf_cstr(&log_path))) {
+		giterr_set(GITERR_INVALID,
+			"Log file for reference '%s' doesn't exist.", reflog->ref_name);
+		goto cleanup;
 	}
 
-	git_oid_tostr(new, GIT_OID_HEXSZ+1, oid);
+	if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
+		goto cleanup;
 
-	git_reference_free(r);
+	git_vector_foreach(&reflog->entries, i, entry) {
+		if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
+			goto cleanup;
 
-	error = git_buf_join_n(&log_path, '/', 3,
-		ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
-	if (error < 0)
+		if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+			goto cleanup;
+	}
+
+	error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
+	goto success;
+
+cleanup:
+	git_filebuf_cleanup(&fbuf);
+
+success:
+	git_buf_free(&log);
+	git_buf_free(&log_path);
+	return error;
+}
+
+int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
+				const git_signature *committer, const char *msg)
+{
+	git_reflog_entry *entry;
+	const git_reflog_entry *previous;
+	const char *newline;
+
+	assert(reflog && new_oid && committer);
+
+	if (reflog_entry_new(&entry) < 0)
+		return -1;
+
+	if ((entry->committer = git_signature_dup(committer)) == NULL)
 		goto cleanup;
 
-	if (git_path_exists(log_path.ptr) == false) {
-		error = git_futils_mkpath2file(log_path.ptr, GIT_REFLOG_DIR_MODE);
-	} else if (git_path_isfile(log_path.ptr) == false) {
-		giterr_set(GITERR_REFERENCE,
-			"Failed to write reflog. `%s` is directory", log_path.ptr);
-		error = -1;
-	} else if (oid_old == NULL) {
-		giterr_set(GITERR_REFERENCE,
-			"Failed to write reflog. Old OID cannot be NULL for existing reference");
-		error = -1;
+	if (msg != NULL) {
+		if ((entry->msg = git__strdup(msg)) == NULL)
+			goto cleanup;
+
+		newline = strchr(msg, '\n');
+
+		if (newline) {
+			if (newline[1] != '\0') {
+				giterr_set(GITERR_INVALID, "Reflog message cannot contain newline");
+				goto cleanup;
+			}
+
+			entry->msg[newline - msg] = '\0';
+		}
 	}
-	if (error < 0)
-		goto cleanup;
 
-	if (oid_old)
-		git_oid_tostr(old, sizeof(old), oid_old);
+	previous = git_reflog_entry_byindex(reflog, 0);
+
+	if (previous == NULL)
+		git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO);
 	else
-		p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);
+		git_oid_cpy(&entry->oid_old, &previous->oid_cur);
 
-	error = reflog_write(log_path.ptr, old, new, committer, msg);
+	git_oid_cpy(&entry->oid_cur, new_oid);
 
+	if (git_vector_insert(&reflog->entries, entry) < 0)
+		goto cleanup;
+
+	return 0;
+
 cleanup:
-	git_buf_free(&log_path);
-	return error;
+	reflog_entry_free(entry);
+	return -1;
 }
 
 int git_reflog_rename(git_reference *ref, const char *new_name)
 {
-	int error;
+	int error = 0, fd;
 	git_buf old_path = GIT_BUF_INIT;
 	git_buf new_path = GIT_BUF_INIT;
+	git_buf temp_path = GIT_BUF_INIT;
+	git_buf normalized = GIT_BUF_INIT;
 
-	if (!git_buf_join_n(&old_path, '/', 3, ref->owner->path_repository,
-			GIT_REFLOG_DIR, ref->name) &&
-		!git_buf_join_n(&new_path, '/', 3, ref->owner->path_repository,
-			GIT_REFLOG_DIR, new_name))
-		error = p_rename(git_buf_cstr(&old_path), git_buf_cstr(&new_path));
-	else
+	assert(ref && new_name);
+
+	if ((error = git_reference__normalize_name(
+		&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
+			return error;
+
+	if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0)
+		return -1;
+
+	if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
+		return -1;
+
+	/*
+	 * Move the reflog to a temporary place. This two-phase renaming is required
+	 * in order to cope with funny renaming use cases when one tries to move a reference
+	 * to a partially colliding namespace:
+	 *  - a/b -> a/b/c
+	 *  - a/b/c/d -> a/b/c
+	 */
+	if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
+		return -1;
+
+	if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
 		error = -1;
+		goto cleanup;
+	}
 
+	p_close(fd);
+
+	if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		error = -1;
+		goto cleanup;
+	}
+
+	if (git_path_isdir(git_buf_cstr(&new_path)) && 
+		(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
+		error = -1;
+	}
+
+cleanup:
+	git_buf_free(&temp_path);
 	git_buf_free(&old_path);
 	git_buf_free(&new_path);
+	git_buf_free(&normalized);
 
 	return error;
 }
@@ -292,8 +413,7 @@
 	int error;
 	git_buf path = GIT_BUF_INIT;
 
-	error = git_buf_join_n(
-		&path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+	error = retrieve_reflog_path(&path, ref);
 
 	if (!error && git_path_exists(path.ptr))
 		error = p_unlink(path.ptr);
@@ -303,38 +423,99 @@
 	return error;
 }
 
-unsigned int git_reflog_entrycount(git_reflog *reflog)
+size_t git_reflog_entrycount(git_reflog *reflog)
 {
 	assert(reflog);
 	return reflog->entries.length;
 }
 
-const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, unsigned int idx)
+GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
 {
+	return (total - 1) - idx;
+}
+
+const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
+{
 	assert(reflog);
-	return git_vector_get(&reflog->entries, idx);
+
+	if (idx >= reflog->entries.length)
+		return NULL;
+
+	return git_vector_get(
+		&reflog->entries, reflog_inverse_index(idx, reflog->entries.length));
 }
 
-const git_oid * git_reflog_entry_oidold(const git_reflog_entry *entry)
+const git_oid * git_reflog_entry_id_old(const git_reflog_entry *entry)
 {
 	assert(entry);
 	return &entry->oid_old;
 }
 
-const git_oid * git_reflog_entry_oidnew(const git_reflog_entry *entry)
+const git_oid * git_reflog_entry_id_new(const git_reflog_entry *entry)
 {
 	assert(entry);
 	return &entry->oid_cur;
 }
 
-git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
+const git_signature * git_reflog_entry_committer(const git_reflog_entry *entry)
 {
 	assert(entry);
 	return entry->committer;
 }
 
-char * git_reflog_entry_msg(const git_reflog_entry *entry)
+const char * git_reflog_entry_message(const git_reflog_entry *entry)
 {
 	assert(entry);
 	return entry->msg;
+}
+
+int git_reflog_drop(
+	git_reflog *reflog,
+	size_t idx,
+	int rewrite_previous_entry)
+{
+	size_t entrycount;
+	git_reflog_entry *entry, *previous;
+
+	assert(reflog);
+
+	entrycount = git_reflog_entrycount(reflog);
+
+	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
+
+	if (entry == NULL)
+		return GIT_ENOTFOUND;
+
+	reflog_entry_free(entry);
+
+	if (git_vector_remove(
+			&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
+		return -1;
+
+	if (!rewrite_previous_entry)
+		return 0;
+
+	/* No need to rewrite anything when removing the most recent entry */
+	if (idx == 0)
+		return 0;
+
+	/* Have the latest entry just been dropped? */
+	if (entrycount == 1)
+		return 0;
+
+	entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
+
+	/* If the oldest entry has just been removed... */
+	if (idx == entrycount - 1) {
+		/* ...clear the oid_old member of the "new" oldest entry */
+		if (git_oid_fromstr(&entry->oid_old, GIT_OID_HEX_ZERO) < 0)
+			return -1;
+
+		return 0;
+	}
+
+	previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
+	git_oid_cpy(&entry->oid_old, &previous->oid_cur);
+
+	return 0;
 }
diff --git a/libgit2/src/reflog.h b/libgit2/src/reflog.h
--- a/libgit2/src/reflog.h
+++ b/libgit2/src/reflog.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -28,6 +28,7 @@
 
 struct git_reflog {
 	char *ref_name;
+	git_repository *owner;
 	git_vector entries;
 };
 
diff --git a/libgit2/src/refs.c b/libgit2/src/refs.c
--- a/libgit2/src/refs.c
+++ b/libgit2/src/refs.c
@@ -1,1707 +1,965 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "refs.h"
-#include "hash.h"
-#include "repository.h"
-#include "fileops.h"
-#include "pack.h"
-#include "reflog.h"
-
-#include <git2/tag.h>
-#include <git2/object.h>
-
-GIT__USE_STRMAP;
-
-#define DEFAULT_NESTING_LEVEL	5
-#define MAX_NESTING_LEVEL		10
-
-enum {
-	GIT_PACKREF_HAS_PEEL = 1,
-	GIT_PACKREF_WAS_LOOSE = 2
-};
-
-struct packref {
-	git_oid oid;
-	git_oid peel;
-	char flags;
-	char name[GIT_FLEX_ARRAY];
-};
-
-static int reference_read(
-	git_buf *file_content,
-	time_t *mtime,
-	const char *repo_path,
-	const char *ref_name,
-	int *updated);
-
-/* loose refs */
-static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
-static int loose_parse_oid(git_oid *ref, git_buf *file_content);
-static int loose_lookup(git_reference *ref);
-static int loose_lookup_to_packfile(struct packref **ref_out,
-	git_repository *repo, const char *name);
-static int loose_write(git_reference *ref);
-
-/* packed refs */
-static int packed_parse_peel(struct packref *tag_ref,
-	const char **buffer_out, const char *buffer_end);
-static int packed_parse_oid(struct packref **ref_out,
-	const char **buffer_out, const char *buffer_end);
-static int packed_load(git_repository *repo);
-static int packed_loadloose(git_repository *repository);
-static int packed_write_ref(struct packref *ref, git_filebuf *file);
-static int packed_find_peel(git_repository *repo, struct packref *ref);
-static int packed_remove_loose(git_repository *repo, git_vector *packing_list);
-static int packed_sort(const void *a, const void *b);
-static int packed_lookup(git_reference *ref);
-static int packed_write(git_repository *repo);
-
-/* internal helpers */
-static int reference_path_available(git_repository *repo,
-	const char *ref, const char *old_ref);
-static int reference_delete(git_reference *ref);
-static int reference_lookup(git_reference *ref);
-
-/* name normalization */
-static int normalize_name(char *buffer_out, size_t out_size,
-	const char *name, int is_oid_ref);
-
-
-void git_reference_free(git_reference *reference)
-{
-	if (reference == NULL)
-		return;
-
-	git__free(reference->name);
-	reference->name = NULL;
-
-	if (reference->flags & GIT_REF_SYMBOLIC) {
-		git__free(reference->target.symbolic);
-		reference->target.symbolic = NULL;
-	}
-
-	git__free(reference);
-}
-
-static int reference_alloc(
-	git_reference **ref_out,
-	git_repository *repo,
-	const char *name)
-{
-	git_reference *reference = NULL;
-
-	assert(ref_out && repo && name);
-
-	reference = git__malloc(sizeof(git_reference));
-	GITERR_CHECK_ALLOC(reference);
-
-	memset(reference, 0x0, sizeof(git_reference));
-	reference->owner = repo;
-
-	reference->name = git__strdup(name);
-	GITERR_CHECK_ALLOC(reference->name);
-
-	*ref_out = reference;
-	return 0;
-}
-
-static int reference_read(
-	git_buf *file_content,
-	time_t *mtime,
-	const char *repo_path,
-	const char *ref_name,
-	int *updated)
-{
-	git_buf path = GIT_BUF_INIT;
-	int result;
-
-	assert(file_content && repo_path && ref_name);
-
-	/* Determine the full path of the file */
-	if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
-		return -1;
-
-	result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
-	git_buf_free(&path);
-	return result;
-}
-
-static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
-{
-	const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
-	const char *refname_start;
-	char *eol;
-
-	refname_start = (const char *)file_content->ptr;
-
-	if (git_buf_len(file_content) < header_len + 1)
-		goto corrupt;
-
-	/*
-	 * Assume we have already checked for the header
-	 * before calling this function
-	 */
-	refname_start += header_len;
-
-	ref->target.symbolic = git__strdup(refname_start);
-	GITERR_CHECK_ALLOC(ref->target.symbolic);
-
-	/* remove newline at the end of file */
-	eol = strchr(ref->target.symbolic, '\n');
-	if (eol == NULL)
-		goto corrupt;
-
-	*eol = '\0';
-	if (eol[-1] == '\r')
-		eol[-1] = '\0';
-
-	return 0;
-
-corrupt:
-	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
-	return -1;
-}
-
-static int loose_parse_oid(git_oid *oid, git_buf *file_content)
-{
-	char *buffer;
-
-	buffer = (char *)file_content->ptr;
-
-	/* File format: 40 chars (OID) + newline */
-	if (git_buf_len(file_content) < GIT_OID_HEXSZ + 1)
-		goto corrupt;
-
-	if (git_oid_fromstr(oid, buffer) < 0)
-		goto corrupt;
-
-	buffer = buffer + GIT_OID_HEXSZ;
-	if (*buffer == '\r')
-		buffer++;
-
-	if (*buffer != '\n')
-		goto corrupt;
-
-	return 0;
-
-corrupt:
-	giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
-	return -1;
-}
-
-static git_ref_t loose_guess_rtype(const git_buf *full_path)
-{
-	git_buf ref_file = GIT_BUF_INIT;
-	git_ref_t type;
-
-	type = GIT_REF_INVALID;
-
-	if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {
-		if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
-			type = GIT_REF_SYMBOLIC;
-		else
-			type = GIT_REF_OID;
-	}
-
-	git_buf_free(&ref_file);
-	return type;
-}
-
-static int loose_lookup(git_reference *ref)
-{
-	int result, updated;
-	git_buf ref_file = GIT_BUF_INIT;
-
-	result = reference_read(&ref_file, &ref->mtime,
-		ref->owner->path_repository, ref->name, &updated);
-
-	if (result < 0)
-		return result;
-
-	if (!updated)
-		return 0;
-
-	if (ref->flags & GIT_REF_SYMBOLIC) {
-		git__free(ref->target.symbolic);
-		ref->target.symbolic = NULL;
-	}
-
-	ref->flags = 0;
-
-	if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
-		ref->flags |= GIT_REF_SYMBOLIC;
-		result = loose_parse_symbolic(ref, &ref_file);
-	} else {
-		ref->flags |= GIT_REF_OID;
-		result = loose_parse_oid(&ref->target.oid, &ref_file);
-	}
-
-	git_buf_free(&ref_file);
-	return result;
-}
-
-static int loose_lookup_to_packfile(
-		struct packref **ref_out,
-		git_repository *repo,
-		const char *name)
-{
-	git_buf ref_file = GIT_BUF_INIT;
-	struct packref *ref = NULL;
-	size_t name_len;
-
-	*ref_out = NULL;
-
-	if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0)
-		return -1;
-
-	name_len = strlen(name);
-	ref = git__malloc(sizeof(struct packref) + name_len + 1);
-	GITERR_CHECK_ALLOC(ref);
-
-	memcpy(ref->name, name, name_len);
-	ref->name[name_len] = 0;
-
-	if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
-		git_buf_free(&ref_file);
-		git__free(ref);
-		return -1;
-	}
-
-	ref->flags = GIT_PACKREF_WAS_LOOSE;
-
-	*ref_out = ref;
-	git_buf_free(&ref_file);
-	return 0;
-}
-
-static int loose_write(git_reference *ref)
-{
-	git_filebuf file = GIT_FILEBUF_INIT;
-	git_buf ref_path = GIT_BUF_INIT;
-	struct stat st;
-
-	if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
-		return -1;
-
-	/* Remove a possibly existing empty directory hierarchy 
-	 * which name would collide with the reference name
-	 */
-	if (git_path_isdir(git_buf_cstr(&ref_path)) && 
-		(git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
-			git_buf_free(&ref_path);
-			return -1;
-		}
-
-	if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
-		git_buf_free(&ref_path);
-		return -1;
-	}
-
-	git_buf_free(&ref_path);
-
-	if (ref->flags & GIT_REF_OID) {
-		char oid[GIT_OID_HEXSZ + 1];
-
-		git_oid_fmt(oid, &ref->target.oid);
-		oid[GIT_OID_HEXSZ] = '\0';
-
-		git_filebuf_printf(&file, "%s\n", oid);
-
-	} else if (ref->flags & GIT_REF_SYMBOLIC) {
-		git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
-	} else {
-		assert(0); /* don't let this happen */
-	}
-
-	if (p_stat(ref_path.ptr, &st) == 0)
-		ref->mtime = st.st_mtime;
-
-	return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
-}
-
-static int packed_parse_peel(
-		struct packref *tag_ref,
-		const char **buffer_out,
-		const char *buffer_end)
-{
-	const char *buffer = *buffer_out + 1;
-
-	assert(buffer[-1] == '^');
-
-	/* Ensure it's not the first entry of the file */
-	if (tag_ref == NULL)
-		goto corrupt;
-
-	/* Ensure reference is a tag */
-	if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
-		goto corrupt;
-
-	if (buffer + GIT_OID_HEXSZ >= buffer_end)
-		goto corrupt;
-
-	/* Is this a valid object id? */
-	if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
-		goto corrupt;
-
-	buffer = buffer + GIT_OID_HEXSZ;
-	if (*buffer == '\r')
-		buffer++;
-
-	if (*buffer != '\n')
-		goto corrupt;
-
-	*buffer_out = buffer + 1;
-	return 0;
-
-corrupt:
-	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
-	return -1;
-}
-
-static int packed_parse_oid(
-		struct packref **ref_out,
-		const char **buffer_out,
-		const char *buffer_end)
-{
-	struct packref *ref = NULL;
-
-	const char *buffer = *buffer_out;
-	const char *refname_begin, *refname_end;
-
-	size_t refname_len;
-	git_oid id;
-
-	refname_begin = (buffer + GIT_OID_HEXSZ + 1);
-	if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
-		goto corrupt;
-
-	/* Is this a valid object id? */
-	if (git_oid_fromstr(&id, buffer) < 0)
-		goto corrupt;
-
-	refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
-	if (refname_end == NULL)
-		goto corrupt;
-
-	if (refname_end[-1] == '\r')
-		refname_end--;
-
-	refname_len = refname_end - refname_begin;
-
-	ref = git__malloc(sizeof(struct packref) + refname_len + 1);
-	GITERR_CHECK_ALLOC(ref);
-
-	memcpy(ref->name, refname_begin, refname_len);
-	ref->name[refname_len] = 0;
-
-	git_oid_cpy(&ref->oid, &id);
-
-	ref->flags = 0;
-
-	*ref_out = ref;
-	*buffer_out = refname_end + 1;
-
-	return 0;
-
-corrupt:
-	git__free(ref);
-	giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
-	return -1;
-}
-
-static int packed_load(git_repository *repo)
-{
-	int result, updated;
-	git_buf packfile = GIT_BUF_INIT;
-	const char *buffer_start, *buffer_end;
-	git_refcache *ref_cache = &repo->references;
-
-	/* First we make sure we have allocated the hash table */
-	if (ref_cache->packfile == NULL) {
-		ref_cache->packfile = git_strmap_alloc();
-		GITERR_CHECK_ALLOC(ref_cache->packfile);
-	}
-
-	result = reference_read(&packfile, &ref_cache->packfile_time,
-		repo->path_repository, GIT_PACKEDREFS_FILE, &updated);
-
-	/*
-	 * If we couldn't find the file, we need to clear the table and
-	 * return. On any other error, we return that error. If everything
-	 * went fine and the file wasn't updated, then there's nothing new
-	 * for us here, so just return. Anything else means we need to
-	 * refresh the packed refs.
-	 */
-	if (result == GIT_ENOTFOUND) {
-		git_strmap_clear(ref_cache->packfile);
-		return 0;
-	}
-
-	if (result < 0)
-		return -1;
-
-	if (!updated)
-		return 0;
-
-	/*
-	 * At this point, we want to refresh the packed refs. We already
-	 * have the contents in our buffer.
-	 */
-	git_strmap_clear(ref_cache->packfile);
-
-	buffer_start = (const char *)packfile.ptr;
-	buffer_end = (const char *)(buffer_start) + packfile.size;
-
-	while (buffer_start < buffer_end && buffer_start[0] == '#') {
-		buffer_start = strchr(buffer_start, '\n');
-		if (buffer_start == NULL)
-			goto parse_failed;
-
-		buffer_start++;
-	}
-
-	while (buffer_start < buffer_end) {
-		int err;
-		struct packref *ref = NULL;
-
-		if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
-			goto parse_failed;
-
-		if (buffer_start[0] == '^') {
-			if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
-				goto parse_failed;
-		}
-
-		git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
-		if (err < 0)
-			goto parse_failed;
-	}
-
-	git_buf_free(&packfile);
-	return 0;
-
-parse_failed:
-	git_strmap_free(ref_cache->packfile);
-	ref_cache->packfile = NULL;
-	git_buf_free(&packfile);
-	return -1;
-}
-
-
-struct dirent_list_data {
-	git_repository *repo;
-	size_t repo_path_len;
-	unsigned int list_flags;
-
-	int (*callback)(const char *, void *);
-	void *callback_payload;
-};
-
-static int _dirent_loose_listall(void *_data, git_buf *full_path)
-{
-	struct dirent_list_data *data = (struct dirent_list_data *)_data;
-	const char *file_path = full_path->ptr + data->repo_path_len;
-
-	if (git_path_isdir(full_path->ptr) == true)
-		return git_path_direach(full_path, _dirent_loose_listall, _data);
-
-	/* do not add twice a reference that exists already in the packfile */
-	if ((data->list_flags & GIT_REF_PACKED) != 0 &&
-		git_strmap_exists(data->repo->references.packfile, file_path))
-		return 0;
-
-	if (data->list_flags != GIT_REF_LISTALL) {
-		if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
-			return 0; /* we are filtering out this reference */
-	}
-
-	return data->callback(file_path, data->callback_payload);
-}
-
-static int _dirent_loose_load(void *data, git_buf *full_path)
-{
-	git_repository *repository = (git_repository *)data;
-	void *old_ref = NULL;
-	struct packref *ref;
-	const char *file_path;
-	int err;
-
-	if (git_path_isdir(full_path->ptr) == true)
-		return git_path_direach(full_path, _dirent_loose_load, repository);
-
-	file_path = full_path->ptr + strlen(repository->path_repository);
-
-	if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)
-		return -1;
-
-	git_strmap_insert2(
-		repository->references.packfile, ref->name, ref, old_ref, err);
-	if (err < 0) {
-		git__free(ref);
-		return -1;
-	}
-
-	git__free(old_ref);
-	return 0;
-}
-
-/*
- * Load all the loose references from the repository
- * into the in-memory Packfile, and build a vector with
- * all the references so it can be written back to
- * disk.
- */
-static int packed_loadloose(git_repository *repository)
-{
-	git_buf refs_path = GIT_BUF_INIT;
-	int result;
-
-	/* the packfile must have been previously loaded! */
-	assert(repository->references.packfile);
-
-	if (git_buf_joinpath(&refs_path, repository->path_repository, GIT_REFS_DIR) < 0)
-		return -1;
-
-	/*
-	 * Load all the loose files from disk into the Packfile table.
-	 * This will overwrite any old packed entries with their
-	 * updated loose versions
-	 */
-	result = git_path_direach(&refs_path, _dirent_loose_load, repository);
-	git_buf_free(&refs_path);
-
-	return result;
-}
-
-/*
- * Write a single reference into a packfile
- */
-static int packed_write_ref(struct packref *ref, git_filebuf *file)
-{
-	char oid[GIT_OID_HEXSZ + 1];
-
-	git_oid_fmt(oid, &ref->oid);
-	oid[GIT_OID_HEXSZ] = 0;
-
-	/*
-	 * For references that peel to an object in the repo, we must
-	 * write the resulting peel on a separate line, e.g.
-	 *
-	 *	6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
-	 *	^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
-	 *
-	 * This obviously only applies to tags.
-	 * The required peels have already been loaded into `ref->peel_target`.
-	 */
-	if (ref->flags & GIT_PACKREF_HAS_PEEL) {
-		char peel[GIT_OID_HEXSZ + 1];
-		git_oid_fmt(peel, &ref->peel);
-		peel[GIT_OID_HEXSZ] = 0;
-
-		if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
-			return -1;
-	} else {
-		if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-/*
- * Find out what object this reference resolves to.
- *
- * For references that point to a 'big' tag (e.g. an
- * actual tag object on the repository), we need to
- * cache on the packfile the OID of the object to
- * which that 'big tag' is pointing to.
- */
-static int packed_find_peel(git_repository *repo, struct packref *ref)
-{
-	git_object *object;
-
-	if (ref->flags & GIT_PACKREF_HAS_PEEL)
-		return 0;
-
-	/*
-	 * Only applies to tags, i.e. references
-	 * in the /refs/tags folder
-	 */
-	if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
-		return 0;
-
-	/*
-	 * Find the tagged object in the repository
-	 */
-	if (git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY) < 0)
-		return -1;
-
-	/*
-	 * If the tagged object is a Tag object, we need to resolve it;
-	 * if the ref is actually a 'weak' ref, we don't need to resolve
-	 * anything.
-	 */
-	if (git_object_type(object) == GIT_OBJ_TAG) {
-		git_tag *tag = (git_tag *)object;
-
-		/*
-		 * Find the object pointed at by this tag
-		 */
-		git_oid_cpy(&ref->peel, git_tag_target_oid(tag));
-		ref->flags |= GIT_PACKREF_HAS_PEEL;
-
-		/*
-		 * The reference has now cached the resolved OID, and is
-		 * marked at such. When written to the packfile, it'll be
-		 * accompanied by this resolved oid
-		 */
-	}
-
-	git_object_free(object);
-	return 0;
-}
-
-/*
- * Remove all loose references
- *
- * Once we have successfully written a packfile,
- * all the loose references that were packed must be
- * removed from disk.
- *
- * This is a dangerous method; make sure the packfile
- * is well-written, because we are destructing references
- * here otherwise.
- */
-static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
-{
-	unsigned int i;
-	git_buf full_path = GIT_BUF_INIT;
-	int failed = 0;
-
-	for (i = 0; i < packing_list->length; ++i) {
-		struct packref *ref = git_vector_get(packing_list, i);
-
-		if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
-			continue;
-
-		if (git_buf_joinpath(&full_path, repo->path_repository, ref->name) < 0)
-			return -1; /* critical; do not try to recover on oom */
-
-		if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
-			if (failed)
-				continue;
-
-			giterr_set(GITERR_REFERENCE,
-				"Failed to remove loose reference '%s' after packing: %s",
-				full_path.ptr, strerror(errno));
-
-			failed = 1;
-		}
-
-		/*
-		 * if we fail to remove a single file, this is *not* good,
-		 * but we should keep going and remove as many as possible.
-		 * After we've removed as many files as possible, we return
-		 * the error code anyway.
-		 */
-	}
-
-	git_buf_free(&full_path);
-	return failed ? -1 : 0;
-}
-
-static int packed_sort(const void *a, const void *b)
-{
-	const struct packref *ref_a = (const struct packref *)a;
-	const struct packref *ref_b = (const struct packref *)b;
-
-	return strcmp(ref_a->name, ref_b->name);
-}
-
-/*
- * Write all the contents in the in-memory packfile to disk.
- */
-static int packed_write(git_repository *repo)
-{
-	git_filebuf pack_file = GIT_FILEBUF_INIT;
-	unsigned int i;
-	git_buf pack_file_path = GIT_BUF_INIT;
-	git_vector packing_list;
-	unsigned int total_refs;
-
-	assert(repo && repo->references.packfile);
-
-	total_refs =
-		(unsigned int)git_strmap_num_entries(repo->references.packfile);
-
-	if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
-		return -1;
-
-	/* Load all the packfile into a vector */
-	{
-		struct packref *reference;
-
-		/* cannot fail: vector already has the right size */
-		git_strmap_foreach_value(repo->references.packfile, reference, {
-			git_vector_insert(&packing_list, reference);
-		});
-	}
-
-	/* sort the vector so the entries appear sorted on the packfile */
-	git_vector_sort(&packing_list);
-
-	/* Now we can open the file! */
-	if (git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE) < 0)
-		goto cleanup_memory;
-
-	if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
-		goto cleanup_packfile;
-
-	/* Packfiles have a header... apparently
-	 * This is in fact not required, but we might as well print it
-	 * just for kicks */
-	if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
-		goto cleanup_packfile;
-
-	for (i = 0; i < packing_list.length; ++i) {
-		struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
-
-		if (packed_find_peel(repo, ref) < 0)
-			goto cleanup_packfile;
-
-		if (packed_write_ref(ref, &pack_file) < 0)
-			goto cleanup_packfile;
-	}
-
-	/* if we've written all the references properly, we can commit
-	 * the packfile to make the changes effective */
-	if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
-		goto cleanup_memory;
-
-	/* when and only when the packfile has been properly written,
-	 * we can go ahead and remove the loose refs */
-	 if (packed_remove_loose(repo, &packing_list) < 0)
-		 goto cleanup_memory;
-
-	 {
-		struct stat st;
-		if (p_stat(pack_file_path.ptr, &st) == 0)
-			repo->references.packfile_time = st.st_mtime;
-	 }
-
-	git_vector_free(&packing_list);
-	git_buf_free(&pack_file_path);
-
-	/* we're good now */
-	return 0;
-
-cleanup_packfile:
-	git_filebuf_cleanup(&pack_file);
-
-cleanup_memory:
-	git_vector_free(&packing_list);
-	git_buf_free(&pack_file_path);
-
-	return -1;
-}
-
-struct reference_available_t {
-	const char *new_ref;
-	const char *old_ref;
-	int available;
-};
-
-static int _reference_available_cb(const char *ref, void *data)
-{
-	struct reference_available_t *d;
-
-	assert(ref && data);
-	d = (struct reference_available_t *)data;
-
-	if (!d->old_ref || strcmp(d->old_ref, ref)) {
-		size_t reflen = strlen(ref);
-		size_t newlen = strlen(d->new_ref);
-		size_t cmplen = reflen < newlen ? reflen : newlen;
-		const char *lead = reflen < newlen ? d->new_ref : ref;
-
-		if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
-			d->available = 0;
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static int reference_path_available(
-	git_repository *repo,
-	const char *ref,
-	const char* old_ref)
-{
-	struct reference_available_t data;
-
-	data.new_ref = ref;
-	data.old_ref = old_ref;
-	data.available = 1;
-
-	if (git_reference_foreach(repo, GIT_REF_LISTALL,
-		_reference_available_cb, (void *)&data) < 0)
-		return -1;
-
-	if (!data.available) {
-		giterr_set(GITERR_REFERENCE,
-			"The path to reference '%s' collides with an existing one", ref);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
-{
-	git_buf ref_path = GIT_BUF_INIT;
-
-	if (packed_load(repo) < 0)
-		return -1;
-
-	if (git_buf_joinpath(&ref_path, repo->path_repository, ref_name) < 0)
-		return -1;
-
-	if (git_path_isfile(ref_path.ptr) == true ||
-		git_strmap_exists(repo->references.packfile, ref_path.ptr))
-	{
-		*exists = 1;
-	} else {
-		*exists = 0;
-	}
-
-	git_buf_free(&ref_path);
-	return 0;
-}
-
-/*
- * Check if a reference could be written to disk, based on:
- *
- *	- Whether a reference with the same name already exists,
- *	and we are allowing or disallowing overwrites
- *
- *	- Whether the name of the reference would collide with
- *	an existing path
- */
-static int reference_can_write(
-	git_repository *repo,
-	const char *refname,
-	const char *previous_name,
-	int force)
-{
-	/* see if the reference shares a path with an existing reference;
-	 * if a path is shared, we cannot create the reference, even when forcing */
-	if (reference_path_available(repo, refname, previous_name) < 0)
-		return -1;
-
-	/* check if the reference actually exists, but only if we are not forcing
-	 * the rename. If we are forcing, it's OK to overwrite */
-	if (!force) {
-		int exists;
-
-		if (reference_exists(&exists, repo, refname) < 0)
-			return -1;
-
-		/* We cannot proceed if the reference already exists and we're not forcing
-		 * the rename; the existing one would be overwritten */
-		if (exists) {
-			giterr_set(GITERR_REFERENCE,
-				"A reference with that name (%s) already exists", refname);
-			return GIT_EEXISTS;
-		}
-	}
-
-	/* FIXME: if the reference exists and we are forcing, do we really need to
-	 * remove the reference first?
-	 *
-	 * Two cases:
-	 *
-	 *	- the reference already exists and is loose: not a problem, the file
-	 *	gets overwritten on disk
-	 *
-	 *	- the reference already exists and is packed: we write a new one as
-	 *	loose, which by all means renders the packed one useless
-	 */
-
-	return 0;
-}
-
-
-static int packed_lookup(git_reference *ref)
-{
-	struct packref *pack_ref = NULL;
-	git_strmap *packfile_refs;
-	khiter_t pos;
-
-	if (packed_load(ref->owner) < 0)
-		return -1;
-
-	/* maybe the packfile hasn't changed at all, so we don't
-	 * have to re-lookup the reference */
-	if ((ref->flags & GIT_REF_PACKED) &&
-		ref->mtime == ref->owner->references.packfile_time)
-		return 0;
-
-	if (ref->flags & GIT_REF_SYMBOLIC) {
-		git__free(ref->target.symbolic);
-		ref->target.symbolic = NULL;
-	}
-
-	/* Look up on the packfile */
-	packfile_refs = ref->owner->references.packfile;
-	pos = git_strmap_lookup_index(packfile_refs, ref->name);
-	if (!git_strmap_valid_index(packfile_refs, pos)) {
-		giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);
-		return GIT_ENOTFOUND;
-	}
-
-	pack_ref = git_strmap_value_at(packfile_refs, pos);
-
-	ref->flags = GIT_REF_OID | GIT_REF_PACKED;
-	ref->mtime = ref->owner->references.packfile_time;
-	git_oid_cpy(&ref->target.oid, &pack_ref->oid);
-
-	return 0;
-}
-
-static int reference_lookup(git_reference *ref)
-{
-	int result;
-
-	result = loose_lookup(ref);
-	if (result == 0)
-		return 0;
-
-	/* only try to lookup this reference on the packfile if it
-	 * wasn't found on the loose refs; not if there was a critical error */
-	if (result == GIT_ENOTFOUND) {
-		giterr_clear();
-		result = packed_lookup(ref);
-		if (result == 0)
-			return 0;
-	}
-
-	/* unexpected error; free the reference */
-	git_reference_free(ref);
-	return result;
-}
-
-/*
- * Delete a reference.
- * This is an internal method; the reference is removed
- * from disk or the packfile, but the pointer is not freed
- */
-static int reference_delete(git_reference *ref)
-{
-	int result;
-
-	assert(ref);
-
-	/* If the reference is packed, this is an expensive operation.
-	 * We need to reload the packfile, remove the reference from the
-	 * packing list, and repack */
-	if (ref->flags & GIT_REF_PACKED) {
-		git_strmap *packfile_refs;
-		struct packref *packref;
-		khiter_t pos;
-
-		/* load the existing packfile */
-		if (packed_load(ref->owner) < 0)
-			return -1;
-
-		packfile_refs = ref->owner->references.packfile;
-		pos = git_strmap_lookup_index(packfile_refs, ref->name);
-		if (!git_strmap_valid_index(packfile_refs, pos)) {
-			giterr_set(GITERR_REFERENCE,
-				"Reference %s stopped existing in the packfile", ref->name);
-			return -1;
-		}
-
-		packref = git_strmap_value_at(packfile_refs, pos);
-		git_strmap_delete_at(packfile_refs, pos);
-
-		git__free(packref);
-		if (packed_write(ref->owner) < 0)
-			return -1;
-
-	/* If the reference is loose, we can just remove the reference
-	 * from the filesystem */
-	} else {
-		git_reference *ref_in_pack;
-		git_buf full_path = GIT_BUF_INIT;
-
-		if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0)
-			return -1;
-
-		result = p_unlink(full_path.ptr);
-		git_buf_free(&full_path); /* done with path at this point */
-
-		if (result < 0) {
-			giterr_set(GITERR_OS, "Failed to unlink '%s'", full_path.ptr);
-			return -1;
-		}
-
-		/* When deleting a loose reference, we have to ensure that an older
-		 * packed version of it doesn't exist */
-		if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name) == 0) {
-			assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);
-			return git_reference_delete(ref_in_pack);
-		}
-
-		giterr_clear();
-	}
-
-	return 0;
-}
-
-int git_reference_delete(git_reference *ref)
-{
-	int result = reference_delete(ref);
-	git_reference_free(ref);
-	return result;
-}
-
-int git_reference_lookup(git_reference **ref_out,
-	git_repository *repo, const char *name)
-{
-	return git_reference_lookup_resolved(ref_out, repo, name, 0);
-}
-
-int git_reference_name_to_oid(
-	git_oid *out, git_repository *repo, const char *name)
-{
-	int error;
-	git_reference *ref;
-
-	if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
-		return error;
-
-	git_oid_cpy(out, git_reference_oid(ref));
-	git_reference_free(ref);
-	return 0;
-}
-
-int git_reference_lookup_resolved(
-	git_reference **ref_out,
-	git_repository *repo,
-	const char *name,
-	int max_nesting)
-{
-	git_reference *scan;
-	int result, nesting;
-
-	assert(ref_out && repo && name);
-
-	*ref_out = NULL;
-
-	if (max_nesting > MAX_NESTING_LEVEL)
-		max_nesting = MAX_NESTING_LEVEL;
-	else if (max_nesting < 0)
-		max_nesting = DEFAULT_NESTING_LEVEL;
-
-	scan = git__calloc(1, sizeof(git_reference));
-	GITERR_CHECK_ALLOC(scan);
-
-	scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
-	GITERR_CHECK_ALLOC(scan->name);
-
-	if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
-		git_reference_free(scan);
-		return result;
-	}
-
-	scan->target.symbolic = git__strdup(scan->name);
-	GITERR_CHECK_ALLOC(scan->target.symbolic);
-
-	scan->owner = repo;
-	scan->flags = GIT_REF_SYMBOLIC;
-
-	for (nesting = max_nesting;
-		 nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0;
-		 nesting--)
-	{
-		if (nesting != max_nesting)
-			strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX);
-
-		scan->mtime = 0;
-
-		if ((result = reference_lookup(scan)) < 0)
-			return result; /* lookup git_reference_free on scan already */
-	}
-
-	if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) {
-		giterr_set(GITERR_REFERENCE,
-			"Cannot resolve reference (>%u levels deep)", max_nesting);
-		git_reference_free(scan);
-		return -1;
-	}
-
-	*ref_out = scan;
-	return 0;
-}
-
-/**
- * Getters
- */
-git_ref_t git_reference_type(git_reference *ref)
-{
-	assert(ref);
-
-	if (ref->flags & GIT_REF_OID)
-		return GIT_REF_OID;
-
-	if (ref->flags & GIT_REF_SYMBOLIC)
-		return GIT_REF_SYMBOLIC;
-
-	return GIT_REF_INVALID;
-}
-
-int git_reference_is_packed(git_reference *ref)
-{
-	assert(ref);
-	return !!(ref->flags & GIT_REF_PACKED);
-}
-
-const char *git_reference_name(git_reference *ref)
-{
-	assert(ref);
-	return ref->name;
-}
-
-git_repository *git_reference_owner(git_reference *ref)
-{
-	assert(ref);
-	return ref->owner;
-}
-
-const git_oid *git_reference_oid(git_reference *ref)
-{
-	assert(ref);
-
-	if ((ref->flags & GIT_REF_OID) == 0)
-		return NULL;
-
-	return &ref->target.oid;
-}
-
-const char *git_reference_target(git_reference *ref)
-{
-	assert(ref);
-
-	if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
-		return NULL;
-
-	return ref->target.symbolic;
-}
-
-int git_reference_create_symbolic(
-	git_reference **ref_out,
-	git_repository *repo,
-	const char *name,
-	const char *target,
-	int force)
-{
-	char normalized[GIT_REFNAME_MAX];
-	git_reference *ref = NULL;
-
-	if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
-		return -1;
-
-	if (reference_can_write(repo, normalized, NULL, force) < 0)
-		return -1;
-
-	if (reference_alloc(&ref, repo, normalized) < 0)
-		return -1;
-
-	ref->flags |= GIT_REF_SYMBOLIC;
-
-	/* set the target; this will normalize the name automatically
-	 * and write the reference on disk */
-	if (git_reference_set_target(ref, target) < 0) {
-		git_reference_free(ref);
-		return -1;
-	}
-	if (ref_out == NULL) {
-		git_reference_free(ref);
-	} else {
-		*ref_out = ref;
-	}
-
-	return 0;
-}
-
-int git_reference_create_oid(
-	git_reference **ref_out,
-	git_repository *repo,
-	const char *name,
-	const git_oid *id,
-	int force)
-{
-	git_reference *ref = NULL;
-	char normalized[GIT_REFNAME_MAX];
-
-	if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
-		return -1;
-
-	if (reference_can_write(repo, normalized, NULL, force) < 0)
-		return -1;
-
-	if (reference_alloc(&ref, repo, name) < 0)
-		return -1;
-
-	ref->flags |= GIT_REF_OID;
-
-	/* set the oid; this will write the reference on disk */
-	if (git_reference_set_oid(ref, id) < 0) {
-		git_reference_free(ref);
-		return -1;
-	}
-
-	if (ref_out == NULL) {
-		git_reference_free(ref);
-	} else {
-		*ref_out = ref;
-	}
-
-	return 0;
-}
-/*
- * Change the OID target of a reference.
- *
- * For both loose and packed references, just change
- * the oid in memory and (over)write the file in disk.
- *
- * We do not repack packed references because of performance
- * reasons.
- */
-int git_reference_set_oid(git_reference *ref, const git_oid *id)
-{
-	git_odb *odb = NULL;
-
-	if ((ref->flags & GIT_REF_OID) == 0) {
-		giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
-		return -1;
-	}
-
-	assert(ref->owner);
-
-	if (git_repository_odb__weakptr(&odb, ref->owner) < 0)
-		return -1;
-
-	/* Don't let the user create references to OIDs that
-	 * don't exist in the ODB */
-	if (!git_odb_exists(odb, id)) {
-		giterr_set(GITERR_REFERENCE,
-			"Target OID for the reference doesn't exist on the repository");
-		return -1;
-	}
-
-	/* Update the OID value on `ref` */
-	git_oid_cpy(&ref->target.oid, id);
-
-	/* Write back to disk */
-	return loose_write(ref);
-}
-
-/*
- * Change the target of a symbolic reference.
- *
- * This is easy because symrefs cannot be inside
- * a pack. We just change the target in memory
- * and overwrite the file on disk.
- */
-int git_reference_set_target(git_reference *ref, const char *target)
-{
-	char normalized[GIT_REFNAME_MAX];
-
-	if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
-		giterr_set(GITERR_REFERENCE,
-			"Cannot set symbolic target on a direct reference");
-		return -1;
-	}
-
-	if (normalize_name(normalized, sizeof(normalized), target, 0))
-		return -1;
-
-	git__free(ref->target.symbolic);
-	ref->target.symbolic = git__strdup(normalized);
-	GITERR_CHECK_ALLOC(ref->target.symbolic);
-
-	return loose_write(ref);
-}
-
-int git_reference_rename(git_reference *ref, const char *new_name, int force)
-{
-	int result;
-	git_buf aux_path = GIT_BUF_INIT;
-	char normalized[GIT_REFNAME_MAX];
-
-	const char *head_target = NULL;
-	git_reference *head = NULL;
-
-	if (normalize_name(normalized, sizeof(normalized),
-		new_name, ref->flags & GIT_REF_OID) < 0)
-		return -1;
-
-	if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
-		return -1;
-
-	/* Initialize path now so we won't get an allocation failure once
-	 * we actually start removing things. */
-	if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
-		return -1;
-
-	/*
-	 * Now delete the old ref and remove an possibly existing directory
-	 * named `new_name`. Note that using the internal `reference_delete`
-	 * method deletes the ref from disk but doesn't free the pointer, so
-	 * we can still access the ref's attributes for creating the new one
-	 */
-	if (reference_delete(ref) < 0)
-		goto cleanup;
-
-	/*
-	 * Finally we can create the new reference.
-	 */
-	if (ref->flags & GIT_REF_SYMBOLIC) {
-		result = git_reference_create_symbolic(
-			NULL, ref->owner, new_name, ref->target.symbolic, force);
-	} else {
-		result = git_reference_create_oid(
-			NULL, ref->owner, new_name, &ref->target.oid, force);
-	}
-
-	if (result < 0)
-		goto rollback;
-
-	/*
-	 * Check if we have to update HEAD.
-	 */
-	if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
-		giterr_set(GITERR_REFERENCE,
-			"Failed to update HEAD after renaming reference");
-		goto cleanup;
-	}
-
-	head_target = git_reference_target(head);
-
-	if (head_target && !strcmp(head_target, ref->name)) {
-		if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
-			giterr_set(GITERR_REFERENCE,
-				"Failed to update HEAD after renaming reference");
-			goto cleanup;
-		}
-	}
-
-	/*
-	 * Rename the reflog file.
-	 */
-	if (git_buf_join_n(&aux_path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0)
-		goto cleanup;
-
-	if (git_path_exists(aux_path.ptr) == true) {
-		if (git_reflog_rename(ref, new_name) < 0)
-			goto cleanup;
-	} else {
-		giterr_clear();
-	}
-
-	/*
-	 * Change the name of the reference given by the user.
-	 */
-	git__free(ref->name);
-	ref->name = git__strdup(new_name);
-
-	/* The reference is no longer packed */
-	ref->flags &= ~GIT_REF_PACKED;
-
-	git_reference_free(head);
-	git_buf_free(&aux_path);
-	return 0;
-
-cleanup:
-	git_reference_free(head);
-	git_buf_free(&aux_path);
-	return -1;
-
-rollback:
-	/*
-	 * Try to create the old reference again, ignore failures
-	 */
-	if (ref->flags & GIT_REF_SYMBOLIC)
-		git_reference_create_symbolic(
-			NULL, ref->owner, ref->name, ref->target.symbolic, 0);
-	else
-		git_reference_create_oid(
-			NULL, ref->owner, ref->name, &ref->target.oid, 0);
-
-	/* The reference is no longer packed */
-	ref->flags &= ~GIT_REF_PACKED;
-
-	git_buf_free(&aux_path);
-	return -1;
-}
-
-int git_reference_resolve(git_reference **ref_out, git_reference *ref)
-{
-	if (ref->flags & GIT_REF_OID)
-		return git_reference_lookup(ref_out, ref->owner, ref->name);
-	else
-		return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1);
-}
-
-int git_reference_packall(git_repository *repo)
-{
-	if (packed_load(repo) < 0 || /* load the existing packfile */
-		packed_loadloose(repo) < 0 || /* add all the loose refs */
-		packed_write(repo) < 0) /* write back to disk */
-		return -1;
-
-	return 0;
-}
-
-int git_reference_foreach(
-	git_repository *repo,
-	unsigned int list_flags,
-	int (*callback)(const char *, void *),
-	void *payload)
-{
-	int result;
-	struct dirent_list_data data;
-	git_buf refs_path = GIT_BUF_INIT;
-
-	/* list all the packed references first */
-	if (list_flags & GIT_REF_PACKED) {
-		const char *ref_name;
-		void *ref;
-		GIT_UNUSED(ref);
-
-		if (packed_load(repo) < 0)
-			return -1;
-
-		git_strmap_foreach(repo->references.packfile, ref_name, ref, {
-			if (callback(ref_name, payload) < 0)
-				return 0;
-		});
-	}
-
-	/* now list the loose references, trying not to
-	 * duplicate the ref names already in the packed-refs file */
-
-	data.repo_path_len = strlen(repo->path_repository);
-	data.list_flags = list_flags;
-	data.repo = repo;
-	data.callback = callback;
-	data.callback_payload = payload;
-
-	if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)
-		return -1;
-
-	result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
-	git_buf_free(&refs_path);
-
-	return result;
-}
-
-static int cb__reflist_add(const char *ref, void *data)
-{
-	return git_vector_insert((git_vector *)data, git__strdup(ref));
-}
-
-int git_reference_list(
-	git_strarray *array,
-	git_repository *repo,
-	unsigned int list_flags)
-{
-	git_vector ref_list;
-
-	assert(array && repo);
-
-	array->strings = NULL;
-	array->count = 0;
-
-	if (git_vector_init(&ref_list, 8, NULL) < 0)
-		return -1;
-
-	if (git_reference_foreach(
-			repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {
-		git_vector_free(&ref_list);
-		return -1;
-	}
-
-	array->strings = (char **)ref_list.contents;
-	array->count = ref_list.length;
-	return 0;
-}
-
-int git_reference_reload(git_reference *ref)
-{
-	return reference_lookup(ref);
-}
-
-void git_repository__refcache_free(git_refcache *refs)
-{
-	assert(refs);
-
-	if (refs->packfile) {
-		struct packref *reference;
-
-		git_strmap_foreach_value(refs->packfile, reference, {
-			git__free(reference);
-		});
-
-		git_strmap_free(refs->packfile);
-	}
-}
-
-static int is_valid_ref_char(char ch)
-{
-	if ((unsigned) ch <= ' ')
-		return 0;
-
-	switch (ch) {
-	case '~':
-	case '^':
-	case ':':
-	case '\\':
-	case '?':
-	case '[':
-	case '*':
-		return 0;
-	default:
-		return 1;
-	}
-}
-
-static int normalize_name(
-	char *buffer_out,
-	size_t out_size,
-	const char *name,
-	int is_oid_ref)
-{
-	const char *name_end, *buffer_out_start;
-	const char *current;
-	int contains_a_slash = 0;
-
-	assert(name && buffer_out);
-
-	buffer_out_start = buffer_out;
-	current = name;
-	name_end = name + strlen(name);
-
-	/* Terminating null byte */
-	out_size--;
-
-	/* A refname can not be empty */
-	if (name_end == name)
-		goto invalid_name;
-
-	/* A refname can not end with a dot or a slash */
-	if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
-		goto invalid_name;
-
-	while (current < name_end && out_size) {
-		if (!is_valid_ref_char(*current))
-			goto invalid_name;
-
-		if (buffer_out > buffer_out_start) {
-			char prev = *(buffer_out - 1);
-
-			/* A refname can not start with a dot nor contain a double dot */
-			if (*current == '.' && ((prev == '.') || (prev == '/')))
-				goto invalid_name;
-
-			/* '@{' is forbidden within a refname */
-			if (*current == '{' && prev == '@')
-				goto invalid_name;
-
-			/* Prevent multiple slashes from being added to the output */
-			if (*current == '/' && prev == '/') {
-				current++;
-				continue;
-			}
-		}
-
-		if (*current == '/')
-			contains_a_slash = 1;
-
-		*buffer_out++ = *current++;
-		out_size--;
-	}
-
-	if (!out_size)
-		goto invalid_name;
-
-	/* Object id refname have to contain at least one slash, except
-	 * for HEAD in a detached state or MERGE_HEAD if we're in the
-	 * middle of a merge */
-	if (is_oid_ref &&
-		!contains_a_slash &&
-		strcmp(name, GIT_HEAD_FILE) != 0 &&
-		strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
-		strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
-		goto invalid_name;
-
-	/* A refname can not end with ".lock" */
-	if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
-		goto invalid_name;
-
-	*buffer_out = '\0';
-
-	/*
-	 * For object id references, name has to start with refs/. Again,
-	 * we need to allow HEAD to be in a detached state.
-	 */
-	if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
-		strcmp(buffer_out_start, GIT_HEAD_FILE)))
-		goto invalid_name;
-
-	return 0;
-
-invalid_name:
-	giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
-	return -1;
-}
-
-int git_reference__normalize_name(
-	char *buffer_out,
-	size_t out_size,
-	const char *name)
-{
-	return normalize_name(buffer_out, out_size, name, 0);
-}
-
-int git_reference__normalize_name_oid(
-	char *buffer_out,
-	size_t out_size,
-	const char *name)
-{
-	return normalize_name(buffer_out, out_size, name, 1);
-}
-
-#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
-
-int git_reference_cmp(git_reference *ref1, git_reference *ref2)
-{
-	assert(ref1 && ref2);
-
-	/* let's put symbolic refs before OIDs */
-	if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK))
-		return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1;
-
-	if (ref1->flags & GIT_REF_SYMBOLIC)
-		return strcmp(ref1->target.symbolic, ref2->target.symbolic);
-
-	return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
-}
-
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refs.h"
+#include "hash.h"
+#include "repository.h"
+#include "fileops.h"
+#include "pack.h"
+#include "reflog.h"
+#include "refdb.h"
+
+#include <git2/tag.h>
+#include <git2/object.h>
+#include <git2/oid.h>
+#include <git2/branch.h>
+#include <git2/refs.h>
+#include <git2/refdb.h>
+#include <git2/refdb_backend.h>
+
+GIT__USE_STRMAP;
+
+#define DEFAULT_NESTING_LEVEL	5
+#define MAX_NESTING_LEVEL		10
+
+enum {
+	GIT_PACKREF_HAS_PEEL = 1,
+	GIT_PACKREF_WAS_LOOSE = 2
+};
+
+
+git_reference *git_reference__alloc(
+	git_refdb *refdb,
+	const char *name,
+	const git_oid *oid,
+	const char *symbolic)
+{
+	git_reference *ref;
+	size_t namelen;
+
+	assert(refdb && name && ((oid && !symbolic) || (!oid && symbolic)));
+
+	namelen = strlen(name);
+
+	if ((ref = git__calloc(1, sizeof(git_reference) + namelen + 1)) == NULL)
+		return NULL;
+
+	if (oid) {
+		ref->type = GIT_REF_OID;
+		git_oid_cpy(&ref->target.oid, oid);
+	} else {
+		ref->type = GIT_REF_SYMBOLIC;
+
+		if ((ref->target.symbolic = git__strdup(symbolic)) == NULL) {
+			git__free(ref);
+			return NULL;
+		}
+	}
+
+	ref->db = refdb;
+	memcpy(ref->name, name, namelen + 1);
+
+	return ref;
+}
+
+void git_reference_free(git_reference *reference)
+{
+	if (reference == NULL)
+		return;
+
+	if (reference->type == GIT_REF_SYMBOLIC) {
+		git__free(reference->target.symbolic);
+		reference->target.symbolic = NULL;
+	}
+
+	reference->db = NULL;
+	reference->type = GIT_REF_INVALID;
+
+	git__free(reference);
+}
+
+struct reference_available_t {
+	const char *new_ref;
+	const char *old_ref;
+	int available;
+};
+
+static int _reference_available_cb(const char *ref, void *data)
+{
+	struct reference_available_t *d;
+
+	assert(ref && data);
+	d = (struct reference_available_t *)data;
+
+	if (!d->old_ref || strcmp(d->old_ref, ref)) {
+		size_t reflen = strlen(ref);
+		size_t newlen = strlen(d->new_ref);
+		size_t cmplen = reflen < newlen ? reflen : newlen;
+		const char *lead = reflen < newlen ? d->new_ref : ref;
+
+		if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
+			d->available = 0;
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int reference_path_available(
+	git_repository *repo,
+	const char *ref,
+	const char* old_ref)
+{
+	int error;
+	struct reference_available_t data;
+
+	data.new_ref = ref;
+	data.old_ref = old_ref;
+	data.available = 1;
+
+	error = git_reference_foreach(
+		repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data);
+	if (error < 0)
+		return error;
+
+	if (!data.available) {
+		giterr_set(GITERR_REFERENCE,
+			"The path to reference '%s' collides with an existing one", ref);
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Check if a reference could be written to disk, based on:
+ *
+ *	- Whether a reference with the same name already exists,
+ *	and we are allowing or disallowing overwrites
+ *
+ *	- Whether the name of the reference would collide with
+ *	an existing path
+ */
+static int reference_can_write(
+	git_repository *repo,
+	const char *refname,
+	const char *previous_name,
+	int force)
+{
+	git_refdb *refdb;
+
+	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
+		return -1;
+
+	/* see if the reference shares a path with an existing reference;
+	 * if a path is shared, we cannot create the reference, even when forcing */
+	if (reference_path_available(repo, refname, previous_name) < 0)
+		return -1;
+
+	/* check if the reference actually exists, but only if we are not forcing
+	 * the rename. If we are forcing, it's OK to overwrite */
+	if (!force) {
+		int exists;
+
+		if (git_refdb_exists(&exists, refdb, refname) < 0)
+			return -1;
+
+		/* We cannot proceed if the reference already exists and we're not forcing
+		 * the rename; the existing one would be overwritten */
+		if (exists) {
+			giterr_set(GITERR_REFERENCE,
+				"A reference with that name (%s) already exists", refname);
+			return GIT_EEXISTS;
+		}
+	}
+
+	/* FIXME: if the reference exists and we are forcing, do we really need to
+	 * remove the reference first?
+	 *
+	 * Two cases:
+	 *
+	 *	- the reference already exists and is loose: not a problem, the file
+	 *	gets overwritten on disk
+	 *
+	 *	- the reference already exists and is packed: we write a new one as
+	 *	loose, which by all means renders the packed one useless
+	 */
+
+	return 0;
+}
+
+int git_reference_delete(git_reference *ref)
+{
+	return git_refdb_delete(ref->db, ref);
+}
+
+int git_reference_lookup(git_reference **ref_out,
+	git_repository *repo, const char *name)
+{
+	return git_reference_lookup_resolved(ref_out, repo, name, 0);
+}
+
+int git_reference_name_to_id(
+	git_oid *out, git_repository *repo, const char *name)
+{
+	int error;
+	git_reference *ref;
+
+	if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
+		return error;
+
+	git_oid_cpy(out, git_reference_target(ref));
+	git_reference_free(ref);
+	return 0;
+}
+
+int git_reference_lookup_resolved(
+	git_reference **ref_out,
+	git_repository *repo,
+	const char *name,
+	int max_nesting)
+{
+	char scan_name[GIT_REFNAME_MAX];
+	git_ref_t scan_type;
+	int error = 0, nesting;
+	git_reference *ref = NULL;
+	git_refdb *refdb;
+
+	assert(ref_out && repo && name);
+
+	*ref_out = NULL;
+
+	if (max_nesting > MAX_NESTING_LEVEL)
+		max_nesting = MAX_NESTING_LEVEL;
+	else if (max_nesting < 0)
+		max_nesting = DEFAULT_NESTING_LEVEL;
+	
+	strncpy(scan_name, name, GIT_REFNAME_MAX);
+	scan_type = GIT_REF_SYMBOLIC;
+	
+	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+		return -1;
+
+	if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
+		return error;
+
+	for (nesting = max_nesting;
+		 nesting >= 0 && scan_type == GIT_REF_SYMBOLIC;
+		 nesting--)
+	{
+		if (nesting != max_nesting) {
+			strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
+			git_reference_free(ref);
+		}
+
+		if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
+			return error;
+		
+		scan_type = ref->type;
+	}
+
+	if (scan_type != GIT_REF_OID && max_nesting != 0) {
+		giterr_set(GITERR_REFERENCE,
+			"Cannot resolve reference (>%u levels deep)", max_nesting);
+		git_reference_free(ref);
+		return -1;
+	}
+
+	*ref_out = ref;
+	return 0;
+}
+
+/**
+ * Getters
+ */
+git_ref_t git_reference_type(const git_reference *ref)
+{
+	assert(ref);
+	return ref->type;
+}
+
+const char *git_reference_name(const git_reference *ref)
+{
+	assert(ref);
+	return ref->name;
+}
+
+git_repository *git_reference_owner(const git_reference *ref)
+{
+	assert(ref);
+	return ref->db->repo;
+}
+
+const git_oid *git_reference_target(const git_reference *ref)
+{
+	assert(ref);
+
+	if (ref->type != GIT_REF_OID)
+		return NULL;
+
+	return &ref->target.oid;
+}
+
+const char *git_reference_symbolic_target(const git_reference *ref)
+{
+	assert(ref);
+
+	if (ref->type != GIT_REF_SYMBOLIC)
+		return NULL;
+
+	return ref->target.symbolic;
+}
+
+static int reference__create(
+	git_reference **ref_out,
+	git_repository *repo,
+	const char *name,
+	const git_oid *oid,
+	const char *symbolic,
+	int force)
+{
+	char normalized[GIT_REFNAME_MAX];
+	git_refdb *refdb;
+	git_reference *ref = NULL;
+	int error = 0;
+	
+	if (ref_out)
+		*ref_out = NULL;
+
+	if ((error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name)) < 0 ||
+		(error = reference_can_write(repo, normalized, NULL, force)) < 0 ||
+		(error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+		return error;
+	
+	if ((ref = git_reference__alloc(refdb, name, oid, symbolic)) == NULL)
+		return -1;
+
+	if ((error = git_refdb_write(refdb, ref)) < 0) {
+		git_reference_free(ref);
+		return error;
+	}
+	
+	if (ref_out == NULL)
+		git_reference_free(ref);
+	else
+		*ref_out = ref;
+
+	return 0;
+}
+
+int git_reference_create(
+	git_reference **ref_out,
+	git_repository *repo,
+	const char *name,
+	const git_oid *oid,
+	int force)
+{
+	git_odb *odb;
+	int error = 0;
+
+	assert(repo && name && oid);
+	
+	/* Sanity check the reference being created - target must exist. */
+	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+		return error;
+	
+	if (!git_odb_exists(odb, oid)) {
+		giterr_set(GITERR_REFERENCE,
+			"Target OID for the reference doesn't exist on the repository");
+		return -1;
+	}
+	
+	return reference__create(ref_out, repo, name, oid, NULL, force);
+}
+
+int git_reference_symbolic_create(
+	git_reference **ref_out,
+	git_repository *repo,
+	const char *name,
+	const char *target,
+	int force)
+{
+	char normalized[GIT_REFNAME_MAX];
+	int error = 0;
+
+	assert(repo && name && target);
+	
+	if ((error = git_reference__normalize_name_lax(
+		normalized, sizeof(normalized), target)) < 0)
+		return error;
+
+	return reference__create(ref_out, repo, name, NULL, normalized, force);
+}
+
+int git_reference_set_target(
+	git_reference **out,
+	git_reference *ref,
+	const git_oid *id)
+{
+	assert(out && ref && id);
+	
+	if (ref->type != GIT_REF_OID) {
+		giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
+		return -1;
+	}
+
+	return git_reference_create(out, ref->db->repo, ref->name, id, 1);
+}
+
+int git_reference_symbolic_set_target(
+	git_reference **out,
+	git_reference *ref,
+	const char *target)
+{
+	assert(out && ref && target);
+	
+	if (ref->type != GIT_REF_SYMBOLIC) {
+		giterr_set(GITERR_REFERENCE,
+			"Cannot set symbolic target on a direct reference");
+		return -1;
+	}
+	
+	return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1);
+}
+
+int git_reference_rename(
+	git_reference **out,
+	git_reference *ref,
+	const char *new_name,
+	int force)
+{
+	unsigned int normalization_flags;
+	char normalized[GIT_REFNAME_MAX];
+	bool should_head_be_updated = false;
+	git_reference *result = NULL;
+	git_oid *oid;
+	const char *symbolic;
+	int error = 0;
+	int reference_has_log;
+	
+	*out = NULL;
+
+	normalization_flags = ref->type == GIT_REF_SYMBOLIC ?
+		GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL;
+
+	if ((error = git_reference_normalize_name(normalized, sizeof(normalized), new_name, normalization_flags)) < 0 ||
+		(error = reference_can_write(ref->db->repo, normalized, ref->name, force)) < 0)
+		return error;
+
+	/*
+	 * Create the new reference.
+	 */
+	if (ref->type == GIT_REF_OID) {
+		oid = &ref->target.oid;
+		symbolic = NULL;
+	} else {
+		oid = NULL;
+		symbolic = ref->target.symbolic;
+	}
+	
+	if ((result = git_reference__alloc(ref->db, new_name, oid, symbolic)) == NULL)
+		return -1;
+
+	/* Check if we have to update HEAD. */
+	if ((error = git_branch_is_head(ref)) < 0)
+		goto on_error;
+
+	should_head_be_updated = (error > 0);
+
+	/* Now delete the old ref and save the new one. */
+	if ((error = git_refdb_delete(ref->db, ref)) < 0)
+		goto on_error;
+	
+	/* Save the new reference. */
+	if ((error = git_refdb_write(ref->db, result)) < 0)
+		goto rollback;
+	
+	/* Update HEAD it was poiting to the reference being renamed. */
+	if (should_head_be_updated && (error = git_repository_set_head(ref->db->repo, new_name)) < 0) {
+		giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
+		goto on_error;
+	}
+
+	/* Rename the reflog file, if it exists. */
+	reference_has_log = git_reference_has_log(ref);
+	if (reference_has_log < 0) {
+		error = reference_has_log;
+		goto on_error;
+	}
+	if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
+		goto on_error;
+
+	*out = result;
+
+	return error;
+
+rollback:
+	git_refdb_write(ref->db, ref);
+
+on_error:
+	git_reference_free(result);
+
+	return error;
+}
+
+int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
+{
+	if (ref->type == GIT_REF_OID)
+		return git_reference_lookup(ref_out, ref->db->repo, ref->name);
+	else
+		return git_reference_lookup_resolved(ref_out, ref->db->repo,
+			ref->target.symbolic, -1);
+}
+
+int git_reference_foreach(
+	git_repository *repo,
+	unsigned int list_flags,
+	git_reference_foreach_cb callback,
+	void *payload)
+{
+	git_refdb *refdb;
+	git_repository_refdb__weakptr(&refdb, repo);
+
+	return git_refdb_foreach(refdb, list_flags, callback, payload);
+}
+
+static int cb__reflist_add(const char *ref, void *data)
+{
+	return git_vector_insert((git_vector *)data, git__strdup(ref));
+}
+
+int git_reference_list(
+	git_strarray *array,
+	git_repository *repo,
+	unsigned int list_flags)
+{
+	git_vector ref_list;
+
+	assert(array && repo);
+
+	array->strings = NULL;
+	array->count = 0;
+
+	if (git_vector_init(&ref_list, 8, NULL) < 0)
+		return -1;
+
+	if (git_reference_foreach(
+			repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {
+		git_vector_free(&ref_list);
+		return -1;
+	}
+
+	array->strings = (char **)ref_list.contents;
+	array->count = ref_list.length;
+	return 0;
+}
+
+static int is_valid_ref_char(char ch)
+{
+	if ((unsigned) ch <= ' ')
+		return 0;
+
+	switch (ch) {
+	case '~':
+	case '^':
+	case ':':
+	case '\\':
+	case '?':
+	case '[':
+	case '*':
+		return 0;
+	default:
+		return 1;
+	}
+}
+
+static int ensure_segment_validity(const char *name)
+{
+	const char *current = name;
+	char prev = '\0';
+	const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
+	int segment_len;
+
+	if (*current == '.')
+		return -1; /* Refname starts with "." */
+
+	for (current = name; ; current++) {
+		if (*current == '\0' || *current == '/')
+			break;
+
+		if (!is_valid_ref_char(*current))
+			return -1; /* Illegal character in refname */
+
+		if (prev == '.' && *current == '.')
+			return -1; /* Refname contains ".." */
+
+		if (prev == '@' && *current == '{')
+			return -1; /* Refname contains "@{" */
+
+		prev = *current;
+	}
+
+	segment_len = (int)(current - name);
+
+	/* A refname component can not end with ".lock" */
+	if (segment_len >= lock_len &&
+		!memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
+			return -1;
+
+	return segment_len;
+}
+
+static bool is_all_caps_and_underscore(const char *name, size_t len)
+{
+	size_t i;
+	char c;
+
+	assert(name && len > 0);
+
+	for (i = 0; i < len; i++)
+	{
+		c = name[i];
+		if ((c < 'A' || c > 'Z') && c != '_')
+			return false;
+	}
+
+	if (*name == '_' || name[len - 1] == '_')
+		return false;
+
+	return true;
+}
+
+int git_reference__normalize_name(
+	git_buf *buf,
+	const char *name,
+	unsigned int flags)
+{
+	// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
+
+	char *current;
+	int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
+	unsigned int process_flags;
+	bool normalize = (buf != NULL);
+	assert(name);
+
+	process_flags = flags;
+	current = (char *)name;
+
+	if (*current == '/')
+		goto cleanup;
+
+	if (normalize)
+		git_buf_clear(buf);
+
+	while (true) {
+		segment_len = ensure_segment_validity(current);
+		if (segment_len < 0) {
+			if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
+					current[0] == '*' &&
+					(current[1] == '\0' || current[1] == '/')) {
+				/* Accept one wildcard as a full refname component. */
+				process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
+				segment_len = 1;
+			} else
+				goto cleanup;
+		}
+
+		if (segment_len > 0) {
+			if (normalize) {
+				size_t cur_len = git_buf_len(buf);
+
+				git_buf_joinpath(buf, git_buf_cstr(buf), current);
+				git_buf_truncate(buf,
+					cur_len + segment_len + (segments_count ? 1 : 0));
+
+				if (git_buf_oom(buf)) {
+					error = -1;
+					goto cleanup;
+				}
+			}
+
+			segments_count++;
+		}
+
+		/* No empty segment is allowed when not normalizing */
+		if (segment_len == 0 && !normalize)
+			goto cleanup;
+
+		if (current[segment_len] == '\0')
+			break;
+
+		current += segment_len + 1;
+	}
+
+	/* A refname can not be empty */
+	if (segment_len == 0 && segments_count == 0)
+		goto cleanup;
+
+	/* A refname can not end with "." */
+	if (current[segment_len - 1] == '.')
+		goto cleanup;
+
+	/* A refname can not end with "/" */
+	if (current[segment_len - 1] == '/')
+		goto cleanup;
+
+	if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
+		goto cleanup;
+
+	if ((segments_count == 1 ) &&
+		!(is_all_caps_and_underscore(name, (size_t)segment_len) ||
+			((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
+			goto cleanup;
+
+	if ((segments_count > 1)
+		&& (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
+			goto cleanup;
+
+	error = 0;
+
+cleanup:
+	if (error == GIT_EINVALIDSPEC)
+		giterr_set(
+			GITERR_REFERENCE,
+			"The given reference name '%s' is not valid", name);
+
+	if (error && normalize)
+		git_buf_free(buf);
+
+	return error;
+}
+
+int git_reference_normalize_name(
+	char *buffer_out,
+	size_t buffer_size,
+	const char *name,
+	unsigned int flags)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int error;
+
+	if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
+		goto cleanup;
+
+	if (git_buf_len(&buf) > buffer_size - 1) {
+		giterr_set(
+		GITERR_REFERENCE,
+		"The provided buffer is too short to hold the normalization of '%s'", name);
+		error = GIT_EBUFS;
+		goto cleanup;
+	}
+
+	git_buf_copy_cstr(buffer_out, buffer_size, &buf);
+
+	error = 0;
+
+cleanup:
+	git_buf_free(&buf);
+	return error;
+}
+
+int git_reference__normalize_name_lax(
+	char *buffer_out,
+	size_t out_size,
+	const char *name)
+{
+	return git_reference_normalize_name(
+		buffer_out,
+		out_size,
+		name,
+		GIT_REF_FORMAT_ALLOW_ONELEVEL);
+}
+#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
+
+int git_reference_cmp(git_reference *ref1, git_reference *ref2)
+{
+	assert(ref1 && ref2);
+
+	/* let's put symbolic refs before OIDs */
+	if (ref1->type != ref2->type)
+		return (ref1->type == GIT_REF_SYMBOLIC) ? -1 : 1;
+
+	if (ref1->type == GIT_REF_SYMBOLIC)
+		return strcmp(ref1->target.symbolic, ref2->target.symbolic);
+
+	return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
+}
+
+static int reference__update_terminal(
+	git_repository *repo,
+	const char *ref_name,
+	const git_oid *oid,
+	int nesting)
+{
+	git_reference *ref;
+	int error = 0;
+
+	if (nesting > MAX_NESTING_LEVEL)
+		return GIT_ENOTFOUND;
+	
+	error = git_reference_lookup(&ref, repo, ref_name);
+
+	/* If we haven't found the reference at all, create a new reference. */
+	if (error == GIT_ENOTFOUND) {
+		giterr_clear();
+		return git_reference_create(NULL, repo, ref_name, oid, 0);
+	}
+	
+	if (error < 0)
+		return error;
+	
+	/* If the ref is a symbolic reference, follow its target. */
+	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
+		error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid,
+			nesting+1);
+		git_reference_free(ref);
+	} else {
+		git_reference_free(ref);
+		error = git_reference_create(NULL, repo, ref_name, oid, 1);
+	}
+	
+	return error;
+}
+
+/*
+ * Starting with the reference given by `ref_name`, follows symbolic
+ * references until a direct reference is found and updated the OID
+ * on that direct reference to `oid`.
+ */
+int git_reference__update_terminal(
+	git_repository *repo,
+	const char *ref_name,
+	const git_oid *oid)
+{
+	return reference__update_terminal(repo, ref_name, oid, 0);
+}
+
+int git_reference_foreach_glob(
+	git_repository *repo,
+	const char *glob,
+	unsigned int list_flags,
+	int (*callback)(
+		const char *reference_name,
+		void *payload),
+	void *payload)
+{
+	git_refdb *refdb;
+
+	assert(repo && glob && callback);
+
+	git_repository_refdb__weakptr(&refdb, repo);
+
+	return git_refdb_foreach_glob(refdb, glob, list_flags, callback, payload);
+}
+
+int git_reference_has_log(
+	git_reference *ref)
+{
+	git_buf path = GIT_BUF_INIT;
+	int result;
+
+	assert(ref);
+
+	if (git_buf_join_n(&path, '/', 3, ref->db->repo->path_repository,
+		GIT_REFLOG_DIR, ref->name) < 0)
+		return -1;
+
+	result = git_path_isfile(git_buf_cstr(&path));
+	git_buf_free(&path);
+
+	return result;
+}
+
+int git_reference__is_branch(const char *ref_name)
+{
+	return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
+}
+
+int git_reference_is_branch(git_reference *ref)
+{
+	assert(ref);
+	return git_reference__is_branch(ref->name);
+}
+
+int git_reference__is_remote(const char *ref_name)
+{
+	return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
+}
+
+int git_reference_is_remote(git_reference *ref)
+{
+	assert(ref);
+	return git_reference__is_remote(ref->name);
+}
+
+static int peel_error(int error, git_reference *ref, const char* msg)
+{
+	giterr_set(
+		GITERR_INVALID,
+		"The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
+	return error;
+}
+
+static int reference_target(git_object **object, git_reference *ref)
+{
+	const git_oid *oid;
+
+	oid = git_reference_target(ref);
+
+	return git_object_lookup(object, git_reference_owner(ref), oid, GIT_OBJ_ANY);
+}
+
+int git_reference_peel(
+		git_object **peeled,
+		git_reference *ref,
+		git_otype target_type)
+{
+	git_reference *resolved = NULL;
+	git_object *target = NULL;
+	int error;
+
+	assert(ref);
+
+	if ((error = git_reference_resolve(&resolved, ref)) < 0)
+		return peel_error(error, ref, "Cannot resolve reference");
+
+	if ((error = reference_target(&target, resolved)) < 0) {
+		peel_error(error, ref, "Cannot retrieve reference target");
+		goto cleanup;
+	}
+
+	if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
+		error = git_object_dup(peeled, target);
+	else
+		error = git_object_peel(peeled, target, target_type);
+
+cleanup:
+	git_object_free(target);
+	git_reference_free(resolved);
+	return error;
+}
+
+int git_reference__is_valid_name(
+	const char *refname,
+	unsigned int flags)
+{
+	int error;
+
+	error = git_reference__normalize_name(NULL, refname, flags) == 0;
+	giterr_clear();
+
+	return error;
+}
+
+int git_reference_is_valid_name(
+	const char *refname)
+{
+	return git_reference__is_valid_name(
+		refname,
+		GIT_REF_FORMAT_ALLOW_ONELEVEL);
+}
diff --git a/libgit2/src/refs.h b/libgit2/src/refs.h
--- a/libgit2/src/refs.h
+++ b/libgit2/src/refs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,7 +10,9 @@
 #include "common.h"
 #include "git2/oid.h"
 #include "git2/refs.h"
+#include "git2/refdb.h"
 #include "strmap.h"
+#include "buffer.h"
 
 #define GIT_REFS_DIR "refs/"
 #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
@@ -27,33 +29,43 @@
 #define GIT_PACKEDREFS_FILE_MODE 0666
 
 #define GIT_HEAD_FILE "HEAD"
+#define GIT_ORIG_HEAD_FILE "ORIG_HEAD"
 #define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
 #define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
+#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
+#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD"
+#define GIT_BISECT_LOG_FILE "BISECT_LOG"
+#define GIT_REBASE_MERGE_DIR "rebase-merge/"
+#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
+#define GIT_REBASE_APPLY_DIR "rebase-apply/"
+#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
+#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
 #define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
 
+#define GIT_STASH_FILE "stash"
+#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
+
 #define GIT_REFNAME_MAX 1024
 
 struct git_reference {
-	unsigned int flags;
-	git_repository *owner;
-	char *name;
-	time_t mtime;
+	git_refdb *db;
 
+	git_ref_t type;
+
 	union {
 		git_oid oid;
 		char *symbolic;
 	} target;
+	
+	char name[0];
 };
 
-typedef struct {
-	git_strmap *packfile;
-	time_t packfile_time;
-} git_refcache;
-
-void git_repository__refcache_free(git_refcache *refs);
-
-int git_reference__normalize_name(char *buffer_out, size_t out_size, const char *name);
-int git_reference__normalize_name_oid(char *buffer_out, size_t out_size, const char *name);
+int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name);
+int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags);
+int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid);
+int git_reference__is_valid_name(const char *refname, unsigned int flags);
+int git_reference__is_branch(const char *ref_name);
+int git_reference__is_remote(const char *ref_name);
 
 /**
  * Lookup a reference by name and try to resolve to an OID.
diff --git a/libgit2/src/refspec.c b/libgit2/src/refspec.c
--- a/libgit2/src/refspec.c
+++ b/libgit2/src/refspec.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -11,38 +11,129 @@
 #include "refspec.h"
 #include "util.h"
 #include "posix.h"
+#include "refs.h"
 
-int git_refspec_parse(git_refspec *refspec, const char *str)
+int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
 {
-	char *delim;
+	// Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636
 
+	size_t llen;
+	int is_glob = 0;
+	const char *lhs, *rhs;
+	int flags;
+
+	assert(refspec && input);
+
 	memset(refspec, 0x0, sizeof(git_refspec));
 
-	if (*str == '+') {
+	lhs = input;
+	if (*lhs == '+') {
 		refspec->force = 1;
-		str++;
+		lhs++;
 	}
 
-	delim = strchr(str, ':');
-	if (delim == NULL) {
-		refspec->src = git__strdup(str);
-		GITERR_CHECK_ALLOC(refspec->src);
+	rhs = strrchr(lhs, ':');
+
+	/*
+	 * Before going on, special case ":" (or "+:") as a refspec
+	 * for matching refs.
+	 */
+	if (!is_fetch && rhs == lhs && rhs[1] == '\0') {
+		refspec->matching = 1;
 		return 0;
 	}
 
-	refspec->src = git__strndup(str, delim - str);
-	GITERR_CHECK_ALLOC(refspec->src);
+	if (rhs) {
+		size_t rlen = strlen(++rhs);
+		is_glob = (1 <= rlen && strchr(rhs, '*'));
+		refspec->dst = git__strndup(rhs, rlen);
+	}
 
-	refspec->dst = git__strdup(delim + 1);
-	if (refspec->dst == NULL) {
-		git__free(refspec->src);
-		refspec->src = NULL;
-		return -1;
+	llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
+	if (1 <= llen && memchr(lhs, '*', llen)) {
+		if ((rhs && !is_glob) || (!rhs && is_fetch))
+			goto invalid;
+		is_glob = 1;
+	} else if (rhs && is_glob)
+		goto invalid;
+
+	refspec->pattern = is_glob;
+	refspec->src = git__strndup(lhs, llen);
+	flags = GIT_REF_FORMAT_ALLOW_ONELEVEL
+		| (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0);
+
+	if (is_fetch) {
+		/*
+			* LHS
+			* - empty is allowed; it means HEAD.
+			* - otherwise it must be a valid looking ref.
+			*/
+		if (!*refspec->src)
+			; /* empty is ok */
+		else if (!git_reference__is_valid_name(refspec->src, flags))
+			goto invalid;
+		/*
+			* RHS
+			* - missing is ok, and is same as empty.
+			* - empty is ok; it means not to store.
+			* - otherwise it must be a valid looking ref.
+			*/
+		if (!refspec->dst)
+			; /* ok */
+		else if (!*refspec->dst)
+			; /* ok */
+		else if (!git_reference__is_valid_name(refspec->dst, flags))
+			goto invalid;
+	} else {
+		/*
+			* LHS
+			* - empty is allowed; it means delete.
+			* - when wildcarded, it must be a valid looking ref.
+			* - otherwise, it must be an extended SHA-1, but
+			*   there is no existing way to validate this.
+			*/
+		if (!*refspec->src)
+			; /* empty is ok */
+		else if (is_glob) {
+			if (!git_reference__is_valid_name(refspec->src, flags))
+				goto invalid;
+		}
+		else {
+			; /* anything goes, for now */
+		}
+		/*
+			* RHS
+			* - missing is allowed, but LHS then must be a
+			*   valid looking ref.
+			* - empty is not allowed.
+			* - otherwise it must be a valid looking ref.
+			*/
+		if (!refspec->dst) {
+			if (!git_reference__is_valid_name(refspec->src, flags))
+				goto invalid;
+		} else if (!*refspec->dst) {
+			goto invalid;
+		} else {
+			if (!git_reference__is_valid_name(refspec->dst, flags))
+				goto invalid;
+		}
 	}
 
 	return 0;
+
+ invalid:
+	return -1;
 }
 
+void git_refspec__free(git_refspec *refspec)
+{
+	if (refspec == NULL)
+		return;
+
+	git__free(refspec->src);
+	git__free(refspec->dst);
+}
+
 const char *git_refspec_src(const git_refspec *refspec)
 {
 	return refspec == NULL ? NULL : refspec->src;
@@ -53,6 +144,13 @@
 	return refspec == NULL ? NULL : refspec->dst;
 }
 
+int git_refspec_force(const git_refspec *refspec)
+{
+	assert(refspec);
+
+	return refspec->force;
+}
+
 int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
 {
 	if (refspec == NULL || refspec->src == NULL)
@@ -61,11 +159,19 @@
 	return (p_fnmatch(refspec->src, refname, 0) == 0);
 }
 
-int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
 {
+	if (refspec == NULL || refspec->dst == NULL)
+		return false;
+
+	return (p_fnmatch(refspec->dst, refname, 0) == 0);
+}
+
+static int refspec_transform_internal(char *out, size_t outlen, const char *from, const char *to, const char *name)
+{
 	size_t baselen, namelen;
 
-	baselen = strlen(spec->dst);
+	baselen = strlen(to);
 	if (outlen <= baselen) {
 		giterr_set(GITERR_INVALID, "Reference name too long");
 		return GIT_EBUFS;
@@ -75,8 +181,8 @@
 	 * No '*' at the end means that it's mapped to one specific local
 	 * branch, so no actual transformation is needed.
 	 */
-	if (spec->dst[baselen - 1] != '*') {
-		memcpy(out, spec->dst, baselen + 1); /* include '\0' */
+	if (to[baselen - 1] != '*') {
+		memcpy(out, to, baselen + 1); /* include '\0' */
 		return 0;
 	}
 
@@ -84,7 +190,7 @@
 	baselen--;
 
 	/* skip the prefix, -1 is for the '*' */
-	name += strlen(spec->src) - 1;
+	name += strlen(from) - 1;
 
 	namelen = strlen(name);
 
@@ -93,26 +199,36 @@
 		return GIT_EBUFS;
 	}
 
-	memcpy(out, spec->dst, baselen);
+	memcpy(out, to, baselen);
 	memcpy(out + baselen, name, namelen + 1);
 
 	return 0;
 }
 
-int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
+int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
 {
-	if (git_buf_sets(out, spec->dst) < 0)
+	return refspec_transform_internal(out, outlen, spec->src, spec->dst, name);
+}
+
+int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+{
+	return refspec_transform_internal(out, outlen, spec->dst, spec->src, name);
+}
+
+static int refspec_transform(git_buf *out, const char *from, const char *to, const char *name)
+{
+	if (git_buf_sets(out, to) < 0)
 		return -1;
 
 	/*
-	 * No '*' at the end means that it's mapped to one specific local
+	 * No '*' at the end means that it's mapped to one specific
 	 * branch, so no actual transformation is needed.
 	 */
 	if (git_buf_len(out) > 0 && out->ptr[git_buf_len(out) - 1] != '*')
 		return 0;
 
 	git_buf_truncate(out, git_buf_len(out) - 1); /* remove trailing '*' */
-	git_buf_puts(out, name + strlen(spec->src) - 1);
+	git_buf_puts(out, name + strlen(from) - 1);
 
 	if (git_buf_oom(out))
 		return -1;
@@ -120,3 +236,31 @@
 	return 0;
 }
 
+int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
+{
+	return refspec_transform(out, spec->src, spec->dst, name);
+}
+
+int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *name)
+{
+	return refspec_transform(out, spec->dst, spec->src, name);
+}
+
+int git_refspec__serialize(git_buf *out, const git_refspec *refspec)
+{
+	if (refspec->force)
+		git_buf_putc(out, '+');
+
+	git_buf_printf(out, "%s:%s",
+		refspec->src != NULL ? refspec->src : "",
+		refspec->dst != NULL ? refspec->dst : "");
+
+	return git_buf_oom(out) == false;
+}
+
+int git_refspec_is_wildcard(const git_refspec *spec)
+{
+	assert(spec && spec->src);
+
+	return (spec->src[strlen(spec->src) - 1] == '*');
+}
diff --git a/libgit2/src/refspec.h b/libgit2/src/refspec.h
--- a/libgit2/src/refspec.h
+++ b/libgit2/src/refspec.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -19,8 +19,16 @@
 		matching :1;
 };
 
+#define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*"
+
 int git_refspec_parse(struct git_refspec *refspec, const char *str);
+int git_refspec__parse(
+	struct git_refspec *refspec,
+	const char *str,
+	bool is_fetch);
 
+void git_refspec__free(git_refspec *refspec);
+
 /**
  * Transform a reference to its target following the refspec's rules,
  * and writes the results into a git_buf.
@@ -31,5 +39,26 @@
  * @return 0 or error if buffer allocation fails
  */
 int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name);
+
+/**
+ * Transform a reference from its target following the refspec's rules,
+ * and writes the results into a git_buf.
+ *
+ * @param out where to store the source name
+ * @param spec the refspec
+ * @param name the name of the reference to transform
+ * @return 0 or error if buffer allocation fails
+ */
+int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *name);
+
+int git_refspec__serialize(git_buf *out, const git_refspec *refspec);
+
+/**
+ * Determines if a refspec is a wildcard refspec.
+ *
+ * @param spec the refspec
+ * @return 1 if the refspec is a wildcard, 0 otherwise
+ */
+int git_refspec_is_wildcard(const git_refspec *spec);
 
 #endif
diff --git a/libgit2/src/remote.c b/libgit2/src/remote.c
--- a/libgit2/src/remote.c
+++ b/libgit2/src/remote.c
@@ -1,510 +1,1391 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "git2/remote.h"
-#include "git2/config.h"
-#include "git2/types.h"
-
-#include "config.h"
-#include "repository.h"
-#include "remote.h"
-#include "fetch.h"
-#include "refs.h"
-
-#include <regex.h>
-
-static int refspec_parse(git_refspec *refspec, const char *str)
-{
-	char *delim;
-
-	memset(refspec, 0x0, sizeof(git_refspec));
-
-	if (*str == '+') {
-		refspec->force = 1;
-		str++;
-	}
-
-	delim = strchr(str, ':');
-	if (delim == NULL) {
-		giterr_set(GITERR_NET, "Invalid refspec, missing ':'");
-		return -1;
-	}
-
-	refspec->src = git__strndup(str, delim - str);
-	GITERR_CHECK_ALLOC(refspec->src);
-
-	refspec->dst = git__strdup(delim + 1);
-	GITERR_CHECK_ALLOC(refspec->dst);
-
-	return 0;
-}
-
-static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var)
-{
-	int error;
-	const char *val;
-
-	if ((error = git_config_get_string(&val, cfg, var)) < 0)
-		return error;
-
-	return refspec_parse(refspec, val);
-}
-
-int git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
-{
-	git_remote *remote;
-
-	/* name is optional */
-	assert(out && repo && url);
-
-	remote = git__malloc(sizeof(git_remote));
-	GITERR_CHECK_ALLOC(remote);
-
-	memset(remote, 0x0, sizeof(git_remote));
-	remote->repo = repo;
-
-	if (git_vector_init(&remote->refs, 32, NULL) < 0)
-		return -1;
-
-	remote->url = git__strdup(url);
-	GITERR_CHECK_ALLOC(remote->url);
-
-	if (name != NULL) {
-		remote->name = git__strdup(name);
-		GITERR_CHECK_ALLOC(remote->name);
-	}
-
-	if (fetch != NULL) {
-		if (refspec_parse(&remote->fetch, fetch) < 0)
-			goto on_error;
-	}
-
-	*out = remote;
-	return 0;
-
-on_error:
-	git_remote_free(remote);
-	return -1;
-}
-
-int git_remote_load(git_remote **out, git_repository *repo, const char *name)
-{
-	git_remote *remote;
-	git_buf buf = GIT_BUF_INIT;
-	const char *val;
-	int error = 0;
-	git_config *config;
-
-	assert(out && repo && name);
-
-	if (git_repository_config__weakptr(&config, repo) < 0)
-		return -1;
-
-	remote = git__malloc(sizeof(git_remote));
-	GITERR_CHECK_ALLOC(remote);
-
-	memset(remote, 0x0, sizeof(git_remote));
-	remote->name = git__strdup(name);
-	GITERR_CHECK_ALLOC(remote->name);
-
-	if (git_vector_init(&remote->refs, 32, NULL) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	if (git_buf_printf(&buf, "remote.%s.url", name) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
-		goto cleanup;
-
-	remote->repo = repo;
-	remote->url = git__strdup(val);
-	GITERR_CHECK_ALLOC(remote->url);
-
-	git_buf_clear(&buf);
-	if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf));
-	if (error == GIT_ENOTFOUND)
-		error = 0;
-
-	if (error < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	git_buf_clear(&buf);
-	if (git_buf_printf(&buf, "remote.%s.push", name) < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf));
-	if (error == GIT_ENOTFOUND)
-		error = 0;
-
-	if (error < 0) {
-		error = -1;
-		goto cleanup;
-	}
-
-	*out = remote;
-
-cleanup:
-	git_buf_free(&buf);
-
-	if (error < 0)
-		git_remote_free(remote);
-
-	return error;
-}
-
-int git_remote_save(const git_remote *remote)
-{
-	git_config *config;
-	git_buf buf = GIT_BUF_INIT, value = GIT_BUF_INIT;
-
-	if (git_repository_config__weakptr(&config, remote->repo) < 0)
-		return -1;
-
-	if (git_buf_printf(&buf, "remote.%s.%s", remote->name, "url") < 0)
-		return -1;
-
-	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
-		git_buf_free(&buf);
-		return -1;
-	}
-
-	if (remote->fetch.src != NULL && remote->fetch.dst != NULL) {
-		git_buf_clear(&buf);
-		git_buf_clear(&value);
-		git_buf_printf(&buf, "remote.%s.fetch", remote->name);
-		git_buf_printf(&value, "%s:%s", remote->fetch.src, remote->fetch.dst);
-		if (git_buf_oom(&buf) || git_buf_oom(&value))
-			return -1;
-
-		if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
-			goto on_error;
-	}
-
-	if (remote->push.src != NULL && remote->push.dst != NULL) {
-		git_buf_clear(&buf);
-		git_buf_clear(&value);
-		git_buf_printf(&buf, "remote.%s.push", remote->name);
-		git_buf_printf(&value, "%s:%s", remote->push.src, remote->push.dst);
-		if (git_buf_oom(&buf) || git_buf_oom(&value))
-			return -1;
-
-		if (git_config_set_string(config, git_buf_cstr(&buf), git_buf_cstr(&value)) < 0)
-			goto on_error;
-	}
-
-	git_buf_free(&buf);
-	git_buf_free(&value);
-
-	return 0;
-
-on_error:
-	git_buf_free(&buf);
-	git_buf_free(&value);
-	return -1;
-}
-
-const char *git_remote_name(git_remote *remote)
-{
-	assert(remote);
-	return remote->name;
-}
-
-const char *git_remote_url(git_remote *remote)
-{
-	assert(remote);
-	return remote->url;
-}
-
-int git_remote_set_fetchspec(git_remote *remote, const char *spec)
-{
-	git_refspec refspec;
-
-	assert(remote && spec);
-
-	if (refspec_parse(&refspec, spec) < 0)
-		return -1;
-
-	git__free(remote->fetch.src);
-	git__free(remote->fetch.dst);
-	remote->fetch.src = refspec.src;
-	remote->fetch.dst = refspec.dst;
-
-	return 0;
-}
-
-const git_refspec *git_remote_fetchspec(git_remote *remote)
-{
-	assert(remote);
-	return &remote->fetch;
-}
-
-int git_remote_set_pushspec(git_remote *remote, const char *spec)
-{
-	git_refspec refspec;
-
-	assert(remote && spec);
-
-	if (refspec_parse(&refspec, spec) < 0)
-		return -1;
-
-	git__free(remote->push.src);
-	git__free(remote->push.dst);
-	remote->push.src = refspec.src;
-	remote->push.dst = refspec.dst;
-
-	return 0;
-}
-
-const git_refspec *git_remote_pushspec(git_remote *remote)
-{
-	assert(remote);
-	return &remote->push;
-}
-
-int git_remote_connect(git_remote *remote, int direction)
-{
-	git_transport *t;
-
-	assert(remote);
-
-	if (git_transport_new(&t, remote->url) < 0)
-		return -1;
-
-	if (t->connect(t, direction) < 0) {
-		goto on_error;
-	}
-
-	remote->transport = t;
-
-	return 0;
-
-on_error:
-	t->free(t);
-	return -1;
-}
-
-int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
-{
-	assert(remote);
-
-	if (!remote->transport || !remote->transport->connected) {
-		giterr_set(GITERR_NET, "The remote is not connected");
-		return -1;
-	}
-
-	return remote->transport->ls(remote->transport, list_cb, payload);
-}
-
-int git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
-{
-	int error;
-
-	assert(remote && bytes && stats);
-
-	if ((error = git_fetch_negotiate(remote)) < 0)
-		return error;
-
-	return git_fetch_download_pack(remote, bytes, stats);
-}
-
-int git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b))
-{
-	int error = 0;
-	unsigned int i = 0;
-	git_buf refname = GIT_BUF_INIT;
-	git_oid old;
-	git_vector *refs = &remote->refs;
-	git_remote_head *head;
-	git_reference *ref;
-	struct git_refspec *spec = &remote->fetch;
-
-	assert(remote);
-
-	if (refs->length == 0)
-		return 0;
-
-	/* HEAD is only allowed to be the first in the list */
-	head = refs->contents[0];
-	if (!strcmp(head->name, GIT_HEAD_FILE)) {
-		if (git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
-			return -1;
-
-		i = 1;
-		git_reference_free(ref);
-	}
-
-	for (; i < refs->length; ++i) {
-		head = refs->contents[i];
-
-		if (git_refspec_transform_r(&refname, spec, head->name) < 0)
-			goto on_error;
-
-		error = git_reference_name_to_oid(&old, remote->repo, refname.ptr);
-		if (error < 0 && error != GIT_ENOTFOUND)
-			goto on_error;
-
-		if (error == GIT_ENOTFOUND)
-			memset(&old, 0, GIT_OID_RAWSZ);
-
-		if (!git_oid_cmp(&old, &head->oid))
-			continue;
-
-		if (git_reference_create_oid(&ref, remote->repo, refname.ptr, &head->oid, 1) < 0)
-			break;
-
-		git_reference_free(ref);
-
-		if (cb != NULL) {
-			if (cb(refname.ptr, &old, &head->oid) < 0)
-				goto on_error;
-		}
-	}
-
-	git_buf_free(&refname);
-	return 0;
-
-on_error:
-	git_buf_free(&refname);
-	return -1;
-
-}
-
-int git_remote_connected(git_remote *remote)
-{
-	assert(remote);
-	return remote->transport == NULL ? 0 : remote->transport->connected;
-}
-
-void git_remote_disconnect(git_remote *remote)
-{
-	assert(remote);
-
-	if (remote->transport != NULL && remote->transport->connected)
-			remote->transport->close(remote->transport);
-}
-
-void git_remote_free(git_remote *remote)
-{
-	if (remote == NULL)
-		return;
-
-	if (remote->transport != NULL) {
-		git_remote_disconnect(remote);
-
-		remote->transport->free(remote->transport);
-		remote->transport = NULL;
-	}
-
-	git_vector_free(&remote->refs);
-
-	git__free(remote->fetch.src);
-	git__free(remote->fetch.dst);
-	git__free(remote->push.src);
-	git__free(remote->push.dst);
-	git__free(remote->url);
-	git__free(remote->name);
-	git__free(remote);
-}
-
-struct cb_data {
-	git_vector *list;
-	regex_t *preg;
-};
-
-static int remote_list_cb(const char *name, const char *value, void *data_)
-{
-	struct cb_data *data = (struct cb_data *)data_;
-	size_t nmatch = 2;
-	regmatch_t pmatch[2];
-	GIT_UNUSED(value);
-
-	if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
-		char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
-		GITERR_CHECK_ALLOC(remote_name);
-
-		if (git_vector_insert(data->list, remote_name) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-int git_remote_list(git_strarray *remotes_list, git_repository *repo)
-{
-	git_config *cfg;
-	git_vector list;
-	regex_t preg;
-	struct cb_data data;
-	int error;
-
-	if (git_repository_config__weakptr(&cfg, repo) < 0)
-		return -1;
-
-	if (git_vector_init(&list, 4, NULL) < 0)
-		return -1;
-
-	if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
-		giterr_set(GITERR_OS, "Remote catch regex failed to compile");
-		return -1;
-	}
-
-	data.list = &list;
-	data.preg = &preg;
-	error = git_config_foreach(cfg, remote_list_cb, &data);
-	regfree(&preg);
-	if (error < 0) {
-		size_t i;
-		char *elem;
-		git_vector_foreach(&list, i, elem) {
-			git__free(elem);
-		}
-
-		git_vector_free(&list);
-		return error;
-	}
-
-	remotes_list->strings = (char **)list.contents;
-	remotes_list->count = list.length;
-
-	return 0;
-}
-
-int git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url)
-{
-	git_buf buf = GIT_BUF_INIT;
-
-	if (git_buf_printf(&buf, "refs/heads/*:refs/remotes/%s/*", name) < 0)
-		return -1;
-
-	if (git_remote_new(out, repo, name, url, git_buf_cstr(&buf)) < 0)
-		goto on_error;
-
-	git_buf_free(&buf);
-
-	if (git_remote_save(*out) < 0)
-		goto on_error;
-
-	return 0;
-
-on_error:
-	git_buf_free(&buf);
-	git_remote_free(*out);
-	return -1;
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+#include "config.h"
+#include "repository.h"
+#include "remote.h"
+#include "fetch.h"
+#include "refs.h"
+#include "refspec.h"
+#include "fetchhead.h"
+
+#include <regex.h>
+
+static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const char *var, bool is_fetch)
+{
+	int error;
+	const char *val;
+
+	if ((error = git_config_get_string(&val, cfg, var)) < 0)
+		return error;
+
+	return git_refspec__parse(refspec, val, is_fetch);
+}
+
+static int download_tags_value(git_remote *remote, git_config *cfg)
+{
+	const char *val;
+	git_buf buf = GIT_BUF_INIT;
+	int error;
+
+	if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSET)
+		return 0;
+
+	/* This is the default, let's see if we need to change it */
+	remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
+	if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
+		return -1;
+
+	error = git_config_get_string(&val, cfg, git_buf_cstr(&buf));
+	git_buf_free(&buf);
+	if (!error && !strcmp(val, "--no-tags"))
+		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
+	else if (!error && !strcmp(val, "--tags"))
+		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+
+	if (error == GIT_ENOTFOUND)
+		error = 0;
+
+	return error;
+}
+
+static int ensure_remote_name_is_valid(const char *name)
+{
+	int error = 0;
+
+	if (!git_remote_is_valid_name(name)) {
+		giterr_set(
+			GITERR_CONFIG,
+			"'%s' is not a valid remote name.", name);
+		error = GIT_EINVALIDSPEC;
+	}
+
+	return error;
+}
+
+static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
+{
+	git_remote *remote;
+	git_buf fetchbuf = GIT_BUF_INIT;
+	int error = -1;
+
+	/* name is optional */
+	assert(out && repo && url);
+
+	remote = git__calloc(1, sizeof(git_remote));
+	GITERR_CHECK_ALLOC(remote);
+
+	remote->repo = repo;
+	remote->check_cert = 1;
+	remote->update_fetchhead = 1;
+
+	if (git_vector_init(&remote->refs, 32, NULL) < 0)
+		goto on_error;
+
+	remote->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(remote->url);
+
+	if (name != NULL) {
+		remote->name = git__strdup(name);
+		GITERR_CHECK_ALLOC(remote->name);
+	}
+
+	if (fetch != NULL) {
+		if (git_refspec__parse(&remote->fetch, fetch, true) < 0)
+			goto on_error;
+	}
+
+	/* A remote without a name doesn't download tags */
+	if (!name) {
+		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
+	}
+
+	*out = remote;
+	git_buf_free(&fetchbuf);
+	return 0;
+
+on_error:
+	git_remote_free(remote);
+	git_buf_free(&fetchbuf);
+	return error;
+}
+
+static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
+{
+	int error;
+	git_remote *remote;
+
+	error = git_remote_load(&remote, repo, name);
+
+	if (error == GIT_ENOTFOUND)
+		return 0;
+
+	if (error < 0)
+		return error;
+
+	git_remote_free(remote);
+
+	giterr_set(
+		GITERR_CONFIG,
+		"Remote '%s' already exists.", name);
+
+	return GIT_EEXISTS;
+}
+
+
+int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
+{
+	git_buf buf = GIT_BUF_INIT;
+	git_remote *remote = NULL;
+	int error;
+
+	if ((error = ensure_remote_name_is_valid(name)) < 0)
+		return error;
+
+	if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
+		return error;
+
+	if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
+		return -1;
+
+	if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0)
+		goto on_error;
+
+	git_buf_free(&buf);
+
+	if (git_remote_save(remote) < 0)
+		goto on_error;
+
+	*out = remote;
+
+	return 0;
+
+on_error:
+	git_buf_free(&buf);
+	git_remote_free(remote);
+	return -1;
+}
+
+int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
+{
+	int error;
+	git_remote *remote;
+
+	if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0)
+		return error;
+
+	*out = remote;
+	return 0;
+}
+
+int git_remote_load(git_remote **out, git_repository *repo, const char *name)
+{
+	git_remote *remote;
+	git_buf buf = GIT_BUF_INIT;
+	const char *val;
+	int error = 0;
+	git_config *config;
+
+	assert(out && repo && name);
+
+	if ((error = ensure_remote_name_is_valid(name)) < 0)
+		return error;
+
+	if (git_repository_config__weakptr(&config, repo) < 0)
+		return -1;
+
+	remote = git__malloc(sizeof(git_remote));
+	GITERR_CHECK_ALLOC(remote);
+
+	memset(remote, 0x0, sizeof(git_remote));
+	remote->check_cert = 1;
+	remote->update_fetchhead = 1;
+	remote->name = git__strdup(name);
+	GITERR_CHECK_ALLOC(remote->name);
+
+	if (git_vector_init(&remote->refs, 32, NULL) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (git_buf_printf(&buf, "remote.%s.url", name) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if ((error = git_config_get_string(&val, config, git_buf_cstr(&buf))) < 0)
+		goto cleanup;
+	
+	if (strlen(val) == 0) {
+		giterr_set(GITERR_INVALID, "Malformed remote '%s' - missing URL", name);
+		error = -1;
+		goto cleanup;
+	}
+
+	remote->repo = repo;
+	remote->url = git__strdup(val);
+	GITERR_CHECK_ALLOC(remote->url);
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.pushurl", name) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	error = git_config_get_string(&val, config, git_buf_cstr(&buf));
+	if (error == GIT_ENOTFOUND) {
+		val = NULL;
+		error = 0;
+	}
+
+	if (error < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (val) {
+		remote->pushurl = git__strdup(val);
+		GITERR_CHECK_ALLOC(remote->pushurl);
+	}
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.fetch", name) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	error = parse_remote_refspec(config, &remote->fetch, git_buf_cstr(&buf), true);
+	if (error == GIT_ENOTFOUND)
+		error = 0;
+
+	if (error < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.push", name) < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	error = parse_remote_refspec(config, &remote->push, git_buf_cstr(&buf), false);
+	if (error == GIT_ENOTFOUND)
+		error = 0;
+
+	if (error < 0) {
+		error = -1;
+		goto cleanup;
+	}
+
+	if (download_tags_value(remote, config) < 0)
+		goto cleanup;
+
+	*out = remote;
+
+cleanup:
+	git_buf_free(&buf);
+
+	if (error < 0)
+		git_remote_free(remote);
+
+	return error;
+}
+
+static int update_config_refspec(
+	git_config *config,
+	const char *remote_name,
+	const git_refspec *refspec,
+	int git_direction)
+{
+	git_buf name = GIT_BUF_INIT, value = GIT_BUF_INIT;
+	int error = -1;
+
+	if (refspec->src == NULL || refspec->dst == NULL)
+		return 0;
+
+	if (git_buf_printf(
+		&name,
+		"remote.%s.%s",
+		remote_name,
+		git_direction == GIT_DIRECTION_FETCH ? "fetch" : "push") < 0)
+			goto cleanup;
+
+	if (git_refspec__serialize(&value, refspec) < 0)
+		goto cleanup;
+
+	error = git_config_set_string(
+		config,
+		git_buf_cstr(&name),
+		git_buf_cstr(&value));
+
+cleanup:
+	git_buf_free(&name);
+	git_buf_free(&value);
+
+	return error;
+}
+
+int git_remote_save(const git_remote *remote)
+{
+	int error;
+	git_config *config;
+	const char *tagopt = NULL;
+	git_buf buf = GIT_BUF_INIT;
+
+	assert(remote);
+
+	if (!remote->name) {
+		giterr_set(GITERR_INVALID, "Can't save an in-memory remote.");
+		return GIT_EINVALIDSPEC;
+	}
+
+	if ((error = ensure_remote_name_is_valid(remote->name)) < 0)
+		return error;
+
+	if (git_repository_config__weakptr(&config, remote->repo) < 0)
+		return -1;
+
+	if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
+		return -1;
+
+	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
+		git_buf_free(&buf);
+		return -1;
+	}
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
+		return -1;
+
+	if (remote->pushurl) {
+		if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
+			git_buf_free(&buf);
+			return -1;
+		}
+	} else {
+		int error = git_config_delete_entry(config, git_buf_cstr(&buf));
+		if (error == GIT_ENOTFOUND) {
+			error = 0;
+			giterr_clear();
+		}
+		if (error < 0) {
+			git_buf_free(&buf);
+			return -1;
+		}
+	}
+
+	if (update_config_refspec(
+		config,
+		remote->name,
+		&remote->fetch,
+		GIT_DIRECTION_FETCH) < 0)
+			goto on_error;
+
+	if (update_config_refspec(
+		config,
+		remote->name,
+		&remote->push,
+		GIT_DIRECTION_PUSH) < 0)
+			goto on_error;
+
+	/*
+	 * What action to take depends on the old and new values. This
+	 * is describes by the table below. tagopt means whether the
+	 * is already a value set in the config
+	 *
+	 *            AUTO     ALL or NONE
+	 *         +-----------------------+
+	 *  tagopt | remove  |     set     |
+	 *         +---------+-------------|
+	 * !tagopt | nothing |     set     |
+	 *         +---------+-------------+
+	 */
+
+	git_buf_clear(&buf);
+	if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
+		goto on_error;
+
+	error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf));
+	if (error < 0 && error != GIT_ENOTFOUND)
+		goto on_error;
+
+	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+		if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0)
+			goto on_error;
+	} else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
+		if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0)
+			goto on_error;
+	} else if (tagopt) {
+		if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
+			goto on_error;
+	}
+
+	git_buf_free(&buf);
+
+	return 0;
+
+on_error:
+	git_buf_free(&buf);
+	return -1;
+}
+
+const char *git_remote_name(const git_remote *remote)
+{
+	assert(remote);
+	return remote->name;
+}
+
+const char *git_remote_url(const git_remote *remote)
+{
+	assert(remote);
+	return remote->url;
+}
+
+int git_remote_set_url(git_remote *remote, const char* url)
+{
+	assert(remote);
+	assert(url);
+
+	git__free(remote->url);
+	remote->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(remote->url);
+
+	return 0;
+}
+
+const char *git_remote_pushurl(const git_remote *remote)
+{
+	assert(remote);
+	return remote->pushurl;
+}
+
+int git_remote_set_pushurl(git_remote *remote, const char* url)
+{
+	assert(remote);
+
+	git__free(remote->pushurl);
+	if (url) {
+		remote->pushurl = git__strdup(url);
+		GITERR_CHECK_ALLOC(remote->pushurl);
+	} else {
+		remote->pushurl = NULL;
+	}
+	return 0;
+}
+
+int git_remote_set_fetchspec(git_remote *remote, const char *spec)
+{
+	git_refspec refspec;
+
+	assert(remote && spec);
+
+	if (git_refspec__parse(&refspec, spec, true) < 0)
+		return -1;
+
+	git_refspec__free(&remote->fetch);
+	memcpy(&remote->fetch, &refspec, sizeof(git_refspec));
+
+	return 0;
+}
+
+const git_refspec *git_remote_fetchspec(const git_remote *remote)
+{
+	assert(remote);
+	return &remote->fetch;
+}
+
+int git_remote_set_pushspec(git_remote *remote, const char *spec)
+{
+	git_refspec refspec;
+
+	assert(remote && spec);
+
+	if (git_refspec__parse(&refspec, spec, false) < 0)
+		return -1;
+
+	git_refspec__free(&remote->push);
+	remote->push.src = refspec.src;
+	remote->push.dst = refspec.dst;
+
+	return 0;
+}
+
+const git_refspec *git_remote_pushspec(const git_remote *remote)
+{
+	assert(remote);
+	return &remote->push;
+}
+
+const char* git_remote__urlfordirection(git_remote *remote, int direction)
+{
+	assert(remote);
+
+	if (direction == GIT_DIRECTION_FETCH) {
+		return remote->url;
+	}
+
+	if (direction == GIT_DIRECTION_PUSH) {
+		return remote->pushurl ? remote->pushurl : remote->url;
+	}
+
+	return NULL;
+}
+
+int git_remote_connect(git_remote *remote, git_direction direction)
+{
+	git_transport *t;
+	const char *url;
+	int flags = GIT_TRANSPORTFLAGS_NONE;
+
+	assert(remote);
+
+	t = remote->transport;
+
+	url = git_remote__urlfordirection(remote, direction);
+	if (url == NULL )
+		return -1;
+
+	/* A transport could have been supplied in advance with
+	 * git_remote_set_transport */
+	if (!t && git_transport_new(&t, remote, url) < 0)
+		return -1;
+
+	if (t->set_callbacks &&
+		t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload) < 0)
+		goto on_error;
+
+	if (!remote->check_cert)
+		flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
+
+	if (t->connect(t, url, remote->cred_acquire_cb, remote->cred_acquire_payload, direction, flags) < 0)
+		goto on_error;
+
+	remote->transport = t;
+
+	return 0;
+
+on_error:
+	t->free(t);
+
+	if (t == remote->transport)
+		remote->transport = NULL;
+
+	return -1;
+}
+
+int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
+{
+	assert(remote);
+
+	return remote->transport->ls(remote->transport, list_cb, payload);
+}
+
+int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
+{
+	git_config *cfg;
+	const char *val;
+
+	assert(remote);
+
+	if (!proxy_url || !remote->repo)
+		return -1;
+
+	*proxy_url = NULL;
+
+	if (git_repository_config__weakptr(&cfg, remote->repo) < 0)
+		return -1;
+
+	/* Go through the possible sources for proxy configuration, from most specific
+	 * to least specific. */
+
+	/* remote.<name>.proxy config setting */
+	if (remote->name && 0 != *(remote->name)) {
+		git_buf buf = GIT_BUF_INIT;
+
+		if (git_buf_printf(&buf, "remote.%s.proxy", remote->name) < 0)
+			return -1;
+
+		if (!git_config_get_string(&val, cfg, git_buf_cstr(&buf)) &&
+			val && ('\0' != *val)) {
+			git_buf_free(&buf);
+
+			*proxy_url = git__strdup(val);
+			GITERR_CHECK_ALLOC(*proxy_url);
+			return 0;
+		}
+
+		git_buf_free(&buf);
+	}
+
+	/* http.proxy config setting */
+	if (!git_config_get_string(&val, cfg, "http.proxy") &&
+		val && ('\0' != *val)) {
+		*proxy_url = git__strdup(val);
+		GITERR_CHECK_ALLOC(*proxy_url);
+		return 0;
+	}
+
+	/* HTTP_PROXY / HTTPS_PROXY environment variables */
+	val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY");
+
+	if (val && ('\0' != *val)) {
+		*proxy_url = git__strdup(val);
+		GITERR_CHECK_ALLOC(*proxy_url);
+		return 0;
+	}
+
+	return 0;
+}
+
+int git_remote_download(
+		git_remote *remote,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload)
+{
+	int error;
+
+	assert(remote);
+
+	if ((error = git_fetch_negotiate(remote)) < 0)
+		return error;
+
+	return git_fetch_download_pack(remote, progress_cb, progress_payload);
+}
+
+static int update_tips_callback(git_remote_head *head, void *payload)
+{
+	git_vector *refs = (git_vector *)payload;
+
+	return git_vector_insert(refs, head);
+}
+
+static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
+{
+	unsigned int i;
+	git_remote_head *remote_ref;
+
+	assert(update_heads && fetchspec_src);
+
+	*out = NULL;
+
+	git_vector_foreach(update_heads, i, remote_ref) {
+		if (strcmp(remote_ref->name, fetchspec_src) == 0) {
+			*out = remote_ref;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_vector *update_heads, git_reference *ref)
+{
+	git_reference *resolved_ref = NULL;
+	git_reference *tracking_ref = NULL;
+	git_buf remote_name = GIT_BUF_INIT;
+	int error = 0;
+
+	assert(out && remote && ref);
+
+	*out = NULL;
+
+	if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
+		(!git_reference_is_branch(resolved_ref)) ||
+		(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
+		(error = git_refspec_transform_l(&remote_name, &remote->fetch, git_reference_name(tracking_ref))) < 0) {
+		/* Not an error if HEAD is orphaned or no tracking branch */
+		if (error == GIT_ENOTFOUND)
+			error = 0;
+
+		goto cleanup;
+	}
+
+	error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name));
+
+cleanup:
+	git_reference_free(tracking_ref);
+	git_reference_free(resolved_ref);
+	git_buf_free(&remote_name);
+	return error;
+}
+
+static int git_remote_write_fetchhead(git_remote *remote, git_vector *update_heads)
+{
+	struct git_refspec *spec;
+	git_reference *head_ref = NULL;
+	git_fetchhead_ref *fetchhead_ref;
+	git_remote_head *remote_ref, *merge_remote_ref;
+	git_vector fetchhead_refs;
+	bool include_all_fetchheads;
+	unsigned int i = 0;
+	int error = 0;
+
+	assert(remote);
+
+	/* no heads, nothing to do */
+	if (update_heads->length == 0)
+		return 0;
+
+	spec = &remote->fetch;
+
+	if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
+		return -1;
+
+	/* Iff refspec is * (but not subdir slash star), include tags */
+	include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
+
+	/* Determine what to merge: if refspec was a wildcard, just use HEAD */
+	if (git_refspec_is_wildcard(spec)) {
+		if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
+			(error = remote_head_for_ref(&merge_remote_ref, remote, update_heads, head_ref)) < 0)
+				goto cleanup;
+	} else {
+		/* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
+		if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
+			goto cleanup;
+	}
+
+	/* Create the FETCH_HEAD file */
+	git_vector_foreach(update_heads, i, remote_ref) {
+		int merge_this_fetchhead = (merge_remote_ref == remote_ref);
+
+		if (!include_all_fetchheads &&
+			!git_refspec_src_matches(spec, remote_ref->name) &&
+			!merge_this_fetchhead)
+			continue;
+
+		if (git_fetchhead_ref_create(&fetchhead_ref,
+			&remote_ref->oid,
+			merge_this_fetchhead,
+			remote_ref->name,
+			git_remote_url(remote)) < 0)
+			goto cleanup;
+
+		if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
+			goto cleanup;
+	}
+
+	git_fetchhead_write(remote->repo, &fetchhead_refs);
+
+cleanup:
+	for (i = 0; i < fetchhead_refs.length; ++i)
+		git_fetchhead_ref_free(fetchhead_refs.contents[i]);
+
+	git_vector_free(&fetchhead_refs);
+	git_reference_free(head_ref);
+
+	return error;
+}
+
+int git_remote_update_tips(git_remote *remote)
+{
+	int error = 0, autotag;
+	unsigned int i = 0;
+	git_buf refname = GIT_BUF_INIT;
+	git_oid old;
+	git_odb *odb;
+	git_remote_head *head;
+	git_reference *ref;
+	struct git_refspec *spec;
+	git_refspec tagspec;
+	git_vector refs, update_heads;
+
+	assert(remote);
+
+	spec = &remote->fetch;
+	
+	if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
+		return -1;
+
+	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+		return -1;
+
+	/* Make a copy of the transport's refs */
+	if (git_vector_init(&refs, 16, NULL) < 0 ||
+		git_vector_init(&update_heads, 16, NULL) < 0)
+		return -1;
+
+	if (git_remote_ls(remote, update_tips_callback, &refs) < 0)
+		goto on_error;
+
+	/* Let's go find HEAD, if it exists. Check only the first ref in the vector. */
+	if (refs.length > 0) {
+		head = (git_remote_head *)refs.contents[0];
+
+		if (!strcmp(head->name, GIT_HEAD_FILE))	{
+			if (git_reference_create(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1) < 0)
+				goto on_error;
+
+			i = 1;
+			git_reference_free(ref);
+		}
+	}
+
+	for (; i < refs.length; ++i) {
+		head = (git_remote_head *)refs.contents[i];
+		autotag = 0;
+
+		/* Ignore malformed ref names (which also saves us from tag^{} */
+		if (!git_reference_is_valid_name(head->name))
+			continue;
+
+		if (git_refspec_src_matches(spec, head->name)) {
+			if (git_refspec_transform_r(&refname, spec, head->name) < 0)
+				goto on_error;
+		} else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
+
+			if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL)
+				autotag = 1;
+
+			if (!git_refspec_src_matches(&tagspec, head->name))
+				continue;
+
+			git_buf_clear(&refname);
+			if (git_buf_puts(&refname, head->name) < 0)
+				goto on_error;
+		} else {
+			continue;
+		}
+
+		if (autotag && !git_odb_exists(odb, &head->oid))
+			continue;
+
+		if (git_vector_insert(&update_heads, head) < 0)
+			goto on_error;
+
+		error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
+		if (error < 0 && error != GIT_ENOTFOUND)
+			goto on_error;
+
+		if (error == GIT_ENOTFOUND)
+			memset(&old, 0, GIT_OID_RAWSZ);
+
+		if (!git_oid_cmp(&old, &head->oid))
+			continue;
+
+		/* In autotag mode, don't overwrite any locally-existing tags */
+		error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag);
+		if (error < 0 && error != GIT_EEXISTS)
+			goto on_error;
+
+		git_reference_free(ref);
+
+		if (remote->callbacks.update_tips != NULL) {
+			if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0)
+				goto on_error;
+		}
+	}
+
+	if (git_remote_update_fetchhead(remote) &&
+		(error = git_remote_write_fetchhead(remote, &update_heads)) < 0)
+		goto on_error;
+
+	git_vector_free(&refs);
+	git_vector_free(&update_heads);
+	git_refspec__free(&tagspec);
+	git_buf_free(&refname);
+	return 0;
+
+on_error:
+	git_vector_free(&refs);
+	git_vector_free(&update_heads);
+	git_refspec__free(&tagspec);
+	git_buf_free(&refname);
+	return -1;
+
+}
+
+int git_remote_connected(git_remote *remote)
+{
+	assert(remote);
+
+	if (!remote->transport || !remote->transport->is_connected)
+		return 0;
+
+	/* Ask the transport if it's connected. */
+	return remote->transport->is_connected(remote->transport);
+}
+
+void git_remote_stop(git_remote *remote)
+{
+	assert(remote);
+
+	if (remote->transport && remote->transport->cancel)
+		remote->transport->cancel(remote->transport);
+}
+
+void git_remote_disconnect(git_remote *remote)
+{
+	assert(remote);
+
+	if (git_remote_connected(remote))
+		remote->transport->close(remote->transport);
+}
+
+void git_remote_free(git_remote *remote)
+{
+	if (remote == NULL)
+		return;
+
+	if (remote->transport != NULL) {
+		git_remote_disconnect(remote);
+
+		remote->transport->free(remote->transport);
+		remote->transport = NULL;
+	}
+
+	git_vector_free(&remote->refs);
+
+	git_refspec__free(&remote->fetch);
+	git_refspec__free(&remote->push);
+	git__free(remote->url);
+	git__free(remote->pushurl);
+	git__free(remote->name);
+	git__free(remote);
+}
+
+struct cb_data {
+	git_vector *list;
+	regex_t *preg;
+};
+
+static int remote_list_cb(const git_config_entry *entry, void *data_)
+{
+	struct cb_data *data = (struct cb_data *)data_;
+	size_t nmatch = 2;
+	regmatch_t pmatch[2];
+	const char *name = entry->name;
+
+	if (!regexec(data->preg, name, nmatch, pmatch, 0)) {
+		char *remote_name = git__strndup(&name[pmatch[1].rm_so], pmatch[1].rm_eo - pmatch[1].rm_so);
+		GITERR_CHECK_ALLOC(remote_name);
+
+		if (git_vector_insert(data->list, remote_name) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int git_remote_list(git_strarray *remotes_list, git_repository *repo)
+{
+	git_config *cfg;
+	git_vector list;
+	regex_t preg;
+	struct cb_data data;
+	int error;
+
+	if (git_repository_config__weakptr(&cfg, repo) < 0)
+		return -1;
+
+	if (git_vector_init(&list, 4, NULL) < 0)
+		return -1;
+
+	if (regcomp(&preg, "^remote\\.(.*)\\.url$", REG_EXTENDED) < 0) {
+		giterr_set(GITERR_OS, "Remote catch regex failed to compile");
+		return -1;
+	}
+
+	data.list = &list;
+	data.preg = &preg;
+	error = git_config_foreach(cfg, remote_list_cb, &data);
+	regfree(&preg);
+	if (error < 0) {
+		size_t i;
+		char *elem;
+		git_vector_foreach(&list, i, elem) {
+			git__free(elem);
+		}
+
+		git_vector_free(&list);
+
+		/* cb error is converted to GIT_EUSER by git_config_foreach */
+		if (error == GIT_EUSER)
+			error = -1;
+
+		return error;
+	}
+
+	remotes_list->strings = (char **)list.contents;
+	remotes_list->count = list.length;
+
+	return 0;
+}
+
+void git_remote_check_cert(git_remote *remote, int check)
+{
+	assert(remote);
+
+	remote->check_cert = check;
+}
+
+int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
+{
+	assert(remote && callbacks);
+
+	GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
+
+	memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
+
+	if (remote->transport && remote->transport->set_callbacks)
+		remote->transport->set_callbacks(remote->transport,
+			remote->callbacks.progress,
+			NULL,
+			remote->callbacks.payload);
+
+	return 0;
+}
+
+void git_remote_set_cred_acquire_cb(
+	git_remote *remote,
+	git_cred_acquire_cb cred_acquire_cb,
+	void *payload)
+{
+	assert(remote);
+
+	remote->cred_acquire_cb = cred_acquire_cb;
+	remote->cred_acquire_payload = payload;
+}
+
+int git_remote_set_transport(git_remote *remote, git_transport *transport)
+{
+	assert(remote && transport);
+
+	GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+
+	if (remote->transport) {
+		giterr_set(GITERR_NET, "A transport is already bound to this remote");
+		return -1;
+	}
+
+	remote->transport = transport;
+	return 0;
+}
+
+const git_transfer_progress* git_remote_stats(git_remote *remote)
+{
+	assert(remote);
+	return &remote->stats;
+}
+
+git_remote_autotag_option_t git_remote_autotag(git_remote *remote)
+{
+	return remote->download_tags;
+}
+
+void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value)
+{
+	remote->download_tags = value;
+}
+
+static int rename_remote_config_section(
+	git_repository *repo,
+	const char *old_name,
+	const char *new_name)
+{
+	git_buf old_section_name = GIT_BUF_INIT,
+		new_section_name = GIT_BUF_INIT;
+	int error = -1;
+
+	if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
+		goto cleanup;
+
+	if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
+		goto cleanup;
+
+	error = git_config_rename_section(
+		repo,
+		git_buf_cstr(&old_section_name),
+		git_buf_cstr(&new_section_name));
+
+cleanup:
+	git_buf_free(&old_section_name);
+	git_buf_free(&new_section_name);
+
+	return error;
+}
+
+struct update_data
+{
+	git_config *config;
+	const char *old_remote_name;
+	const char *new_remote_name;
+};
+
+static int update_config_entries_cb(
+	const git_config_entry *entry,
+	void *payload)
+{
+	struct update_data *data = (struct update_data *)payload;
+
+	if (strcmp(entry->value, data->old_remote_name))
+		return 0;
+
+	return git_config_set_string(
+		data->config,
+		entry->name,
+		data->new_remote_name);
+}
+
+static int update_branch_remote_config_entry(
+	git_repository *repo,
+	const char *old_name,
+	const char *new_name)
+{
+	git_config *config;
+	struct update_data data;
+
+	if (git_repository_config__weakptr(&config, repo) < 0)
+		return -1;
+
+	data.config = config;
+	data.old_remote_name = old_name;
+	data.new_remote_name = new_name;
+
+	return git_config_foreach_match(
+		config,
+		"branch\\..+\\.remote",
+		update_config_entries_cb, &data);
+}
+
+static int rename_cb(const char *ref, void *data)
+{
+	if (git__prefixcmp(ref, GIT_REFS_REMOTES_DIR))
+		return 0;
+	
+	return git_vector_insert((git_vector *)data, git__strdup(ref));
+}
+
+static int rename_one_remote_reference(
+	git_repository *repo,
+	const char *reference_name,
+	const char *old_remote_name,
+	const char *new_remote_name)
+{
+	int error = -1;
+	git_buf new_name = GIT_BUF_INIT;
+	git_reference *reference = NULL;
+	git_reference *newref = NULL;
+
+	if (git_buf_printf(
+		&new_name,
+		GIT_REFS_REMOTES_DIR "%s%s",
+		new_remote_name,
+		reference_name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0)
+			return -1;
+
+	if (git_reference_lookup(&reference, repo, reference_name) < 0)
+		goto cleanup;
+
+	error = git_reference_rename(&newref, reference, git_buf_cstr(&new_name), 0);
+	git_reference_free(reference);
+
+cleanup:
+	git_reference_free(newref);
+	git_buf_free(&new_name);
+	return error;
+}
+
+static int rename_remote_references(
+	git_repository *repo,
+	const char *old_name,
+	const char *new_name)
+{
+	git_vector refnames;
+	int error = -1;
+	unsigned int i;
+	char *name;
+
+	if (git_vector_init(&refnames, 8, NULL) < 0)
+		goto cleanup;
+
+	if (git_reference_foreach(
+		repo,
+		GIT_REF_LISTALL,
+		rename_cb,
+		&refnames) < 0)
+			goto cleanup;
+
+	git_vector_foreach(&refnames, i, name) {
+		if ((error = rename_one_remote_reference(repo, name, old_name, new_name)) < 0)
+			goto cleanup;
+	}
+
+	error = 0;
+cleanup:
+	git_vector_foreach(&refnames, i, name) {
+		git__free(name);
+	}
+
+	git_vector_free(&refnames);
+	return error;
+}
+
+static int rename_fetch_refspecs(
+	git_remote *remote,
+	const char *new_name,
+	int (*callback)(const char *problematic_refspec, void *payload),
+	void *payload)
+{
+	git_config *config;
+	const git_refspec *fetch_refspec;
+	git_buf dst_prefix = GIT_BUF_INIT, serialized = GIT_BUF_INIT;
+	const char* pos;
+	int error = -1;
+
+	fetch_refspec = git_remote_fetchspec(remote);
+
+	/* Is there a refspec to deal with? */
+	if (fetch_refspec->src == NULL &&
+		fetch_refspec->dst == NULL)
+		return 0;
+
+	if (git_refspec__serialize(&serialized, fetch_refspec) < 0)
+		goto cleanup;
+
+	/* Is it an in-memory remote? */
+	if (!remote->name) {
+		error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
+		goto cleanup;
+	}
+
+	if (git_buf_printf(&dst_prefix, ":refs/remotes/%s/", remote->name) < 0)
+		goto cleanup;
+
+	pos = strstr(git_buf_cstr(&serialized), git_buf_cstr(&dst_prefix));
+
+	/* Does the dst part of the refspec follow the extected standard format? */
+	if (!pos) {
+		error = (callback(git_buf_cstr(&serialized), payload) < 0) ? GIT_EUSER : 0;
+		goto cleanup;
+	}
+
+	if (git_buf_splice(
+		&serialized,
+		pos - git_buf_cstr(&serialized) + strlen(":refs/remotes/"),
+		strlen(remote->name), new_name,
+		strlen(new_name)) < 0)
+			goto cleanup;
+
+	git_refspec__free(&remote->fetch);
+
+	if (git_refspec__parse(&remote->fetch, git_buf_cstr(&serialized), true) < 0)
+		goto cleanup;
+
+	if (git_repository_config__weakptr(&config, remote->repo) < 0)
+		goto cleanup;
+
+	error = update_config_refspec(config, new_name, &remote->fetch, GIT_DIRECTION_FETCH);
+
+cleanup:
+	git_buf_free(&serialized);
+	git_buf_free(&dst_prefix);
+	return error;
+}
+
+int git_remote_rename(
+	git_remote *remote,
+	const char *new_name,
+	git_remote_rename_problem_cb callback,
+	void *payload)
+{
+	int error;
+
+	assert(remote && new_name);
+
+	if (!remote->name) {
+		giterr_set(GITERR_INVALID, "Can't rename an in-memory remote.");
+		return GIT_EINVALIDSPEC;
+	}
+
+	if ((error = ensure_remote_name_is_valid(new_name)) < 0)
+		return error;
+
+	if (remote->repo) {
+		if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
+			return error;
+
+		if (!remote->name) {
+			if ((error = rename_fetch_refspecs(
+				remote,
+				new_name,
+				callback,
+				payload)) < 0)
+				return error;
+
+			remote->name = git__strdup(new_name);
+
+			if (!remote->name) return 0;
+			return git_remote_save(remote);
+		}
+
+		if ((error = rename_remote_config_section(
+			remote->repo,
+			remote->name,
+			new_name)) < 0)
+				return error;
+
+		if ((error = update_branch_remote_config_entry(
+			remote->repo,
+			remote->name,
+			new_name)) < 0)
+				return error;
+
+		if ((error = rename_remote_references(
+			remote->repo,
+			remote->name,
+			new_name)) < 0)
+				return error;
+
+		if ((error = rename_fetch_refspecs(
+			remote,
+			new_name,
+			callback,
+			payload)) < 0)
+			return error;
+	}
+
+	git__free(remote->name);
+	remote->name = git__strdup(new_name);
+
+	return 0;
+}
+
+int git_remote_update_fetchhead(git_remote *remote)
+{
+	return remote->update_fetchhead;
+}
+
+void git_remote_set_update_fetchhead(git_remote *remote, int value)
+{
+	remote->update_fetchhead = value;
+}
+
+int git_remote_is_valid_name(
+	const char *remote_name)
+{
+	git_buf buf = GIT_BUF_INIT;
+	git_refspec refspec;
+	int error = -1;
+
+	if (!remote_name || *remote_name == '\0')
+		return 0;
+
+	git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
+	error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
+
+	git_buf_free(&buf);
+	git_refspec__free(&refspec);
+
+	giterr_clear();
+	return error == 0;
 }
diff --git a/libgit2/src/remote.h b/libgit2/src/remote.h
--- a/libgit2/src/remote.h
+++ b/libgit2/src/remote.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,19 +7,34 @@
 #ifndef INCLUDE_remote_h__
 #define INCLUDE_remote_h__
 
+#include "git2/remote.h"
+#include "git2/transport.h"
+
 #include "refspec.h"
-#include "transport.h"
 #include "repository.h"
 
+#define GIT_REMOTE_ORIGIN "origin"
+
 struct git_remote {
 	char *name;
 	char *url;
+	char *pushurl;
 	git_vector refs;
 	struct git_refspec fetch;
 	struct git_refspec push;
+	git_cred_acquire_cb cred_acquire_cb;
+	void *cred_acquire_payload;
 	git_transport *transport;
 	git_repository *repo;
-	unsigned int need_pack:1;
+	git_remote_callbacks callbacks;
+	git_transfer_progress stats;
+	unsigned int need_pack;
+	git_remote_autotag_option_t download_tags;
+	unsigned int check_cert;
+	unsigned int update_fetchhead;
 };
+
+const char* git_remote__urlfordirection(struct git_remote *remote, int direction);
+int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
 
 #endif
diff --git a/libgit2/src/repo_template.h b/libgit2/src/repo_template.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/repo_template.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_repo_template_h__
+#define INCLUDE_repo_template_h__
+
+#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
+#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
+
+#define GIT_HOOKS_DIR "hooks/"
+#define GIT_HOOKS_DIR_MODE 0777
+
+#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
+#define GIT_HOOKS_README_MODE 0777
+#define GIT_HOOKS_README_CONTENT \
+"#!/bin/sh\n"\
+"#\n"\
+"# Place appropriately named executable hook scripts into this directory\n"\
+"# to intercept various actions that git takes.  See `git help hooks` for\n"\
+"# more information.\n"
+
+#define GIT_INFO_DIR "info/"
+#define GIT_INFO_DIR_MODE 0777
+
+#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
+#define GIT_INFO_EXCLUDE_MODE 0666
+#define GIT_INFO_EXCLUDE_CONTENT \
+"# File patterns to ignore; see `git help ignore` for more information.\n"\
+"# Lines that start with '#' are comments.\n"
+
+#define GIT_DESC_FILE "description"
+#define GIT_DESC_MODE 0666
+#define GIT_DESC_CONTENT \
+"Unnamed repository; edit this file 'description' to name the repository.\n"
+
+typedef struct {
+	const char *path;
+	mode_t mode;
+	const char *content;
+} repo_template_item;
+
+static repo_template_item repo_template[] = {
+	{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
+	{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
+	{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL },     /* '/refs/heads/' */
+	{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL },      /* '/refs/tags/' */
+	{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL },         /* '/hooks/' */
+	{ GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL },           /* '/info/' */
+	{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
+	{ GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
+	{ GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
+	{ NULL, 0, NULL }
+};
+
+#endif
diff --git a/libgit2/src/repository.c b/libgit2/src/repository.c
--- a/libgit2/src/repository.c
+++ b/libgit2/src/repository.c
@@ -1,957 +1,1737 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#include <stdarg.h>
-#include <ctype.h>
-
-#include "git2/object.h"
-
-#include "common.h"
-#include "repository.h"
-#include "commit.h"
-#include "tag.h"
-#include "blob.h"
-#include "fileops.h"
-#include "config.h"
-#include "refs.h"
-
-#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
-#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
-
-#define GIT_FILE_CONTENT_PREFIX "gitdir:"
-
-#define GIT_BRANCH_MASTER "master"
-
-#define GIT_REPO_VERSION 0
-
-static void drop_odb(git_repository *repo)
-{
-	if (repo->_odb != NULL) {
-		GIT_REFCOUNT_OWN(repo->_odb, NULL);
-		git_odb_free(repo->_odb);
-		repo->_odb = NULL;
-	}
-}
-
-static void drop_config(git_repository *repo)
-{
-	if (repo->_config != NULL) {
-		GIT_REFCOUNT_OWN(repo->_config, NULL);
-		git_config_free(repo->_config);
-		repo->_config = NULL;
-	}
-
-	git_repository__cvar_cache_clear(repo);
-}
-
-static void drop_index(git_repository *repo)
-{
-	if (repo->_index != NULL) {
-		GIT_REFCOUNT_OWN(repo->_index, NULL);
-		git_index_free(repo->_index);
-		repo->_index = NULL;
-	}
-}
-
-void git_repository_free(git_repository *repo)
-{
-	if (repo == NULL)
-		return;
-
-	git_cache_free(&repo->objects);
-	git_repository__refcache_free(&repo->references);
-	git_attr_cache_flush(repo);
-	git_submodule_config_free(repo);
-
-	git__free(repo->path_repository);
-	git__free(repo->workdir);
-
-	drop_config(repo);
-	drop_index(repo);
-	drop_odb(repo);
-
-	git__free(repo);
-}
-
-/*
- * Git repository open methods
- *
- * Open a repository object from its path
- */
-static bool valid_repository_path(git_buf *repository_path)
-{
-	/* Check OBJECTS_DIR first, since it will generate the longest path name */
-	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
-		return false;
-
-	/* Ensure HEAD file exists */
-	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
-		return false;
-
-	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)
-		return false;
-
-	return true;
-}
-
-static git_repository *repository_alloc(void)
-{
-	git_repository *repo = git__malloc(sizeof(git_repository));
-	if (!repo)
-		return NULL;
-
-	memset(repo, 0x0, sizeof(git_repository));
-
-	if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
-		git__free(repo);
-		return NULL;
-	}
-
-	/* set all the entries in the cvar cache to `unset` */
-	git_repository__cvar_cache_clear(repo);
-
-	return repo;
-}
-
-int git_repository_new(git_repository **out)
-{
-	*out = repository_alloc();
-	return 0;
-}
-
-static int load_config_data(git_repository *repo)
-{
-	int is_bare;
-	git_config *config;
-
-	if (git_repository_config__weakptr(&config, repo) < 0)
-		return -1;
-
-	if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
-		return -1; /* FIXME: We assume that a missing core.bare
-					  variable is an error. Is this right? */
-
-	repo->is_bare = is_bare;
-	return 0;
-}
-
-static int load_workdir(git_repository *repo, git_buf *parent_path)
-{
-	int         error;
-	git_config *config;
-	const char *worktree;
-	git_buf     worktree_buf = GIT_BUF_INIT;
-
-	if (repo->is_bare)
-		return 0;
-
-	if (git_repository_config__weakptr(&config, repo) < 0)
-		return -1;
-
-	error = git_config_get_string(&worktree, config, "core.worktree");
-	if (!error && worktree != NULL)
-		repo->workdir = git__strdup(worktree);
-	else if (error != GIT_ENOTFOUND)
-		return error;
-	else {
-		giterr_clear();
-
-		if (parent_path && git_path_isdir(parent_path->ptr))
-			repo->workdir = git_buf_detach(parent_path);
-		else {
-			git_path_dirname_r(&worktree_buf, repo->path_repository);
-			git_path_to_dir(&worktree_buf);
-			repo->workdir = git_buf_detach(&worktree_buf);
-		}
-	}
-
-	GITERR_CHECK_ALLOC(repo->workdir);
-
-	return 0;
-}
-
-/*
- * This function returns furthest offset into path where a ceiling dir
- * is found, so we can stop processing the path at that point.
- *
- * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
- * the stack could remove directories name limits, but at the cost of doing
- * repeated malloc/frees inside the loop below, so let's not do it now.
- */
-static int find_ceiling_dir_offset(
-	const char *path,
-	const char *ceiling_directories)
-{
-	char buf[GIT_PATH_MAX + 1];
-	char buf2[GIT_PATH_MAX + 1];
-	const char *ceil, *sep;
-	size_t len, max_len = 0, min_len;
-
-	assert(path);
-
-	min_len = (size_t)(git_path_root(path) + 1);
-
-	if (ceiling_directories == NULL || min_len == 0)
-		return (int)min_len;
-
-	for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
-		for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
-		len = sep - ceil;
-
-		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
-			continue;
-
-		strncpy(buf, ceil, len);
-		buf[len] = '\0';
-
-		if (p_realpath(buf, buf2) == NULL)
-			continue;
-
-		len = strlen(buf2);
-		if (len > 0 && buf2[len-1] == '/')
-			buf[--len] = '\0';
-
-		if (!strncmp(path, buf2, len) &&
-			path[len] == '/' &&
-			len > max_len)
-		{
-			max_len = len;
-		}
-	}
-
-	return (int)(max_len <= min_len ? min_len : max_len);
-}
-
-/*
- * Read the contents of `file_path` and set `path_out` to the repo dir that
- * it points to.  Before calling, set `path_out` to the base directory that
- * should be used if the contents of `file_path` are a relative path.
- */
-static int read_gitfile(git_buf *path_out, const char *file_path)
-{
-	int     error = 0;
-	git_buf file = GIT_BUF_INIT;
-	size_t  prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
-
-	assert(path_out && file_path);
-
-	if (git_futils_readbuffer(&file, file_path) < 0)
-		return -1;
-
-	git_buf_rtrim(&file);
-
-	if (file.size <= prefix_len ||
-		memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
-	{
-		giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
-		error = -1;
-	}
-	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
-		const char *gitlink = ((const char *)file.ptr) + prefix_len;
-		while (*gitlink && git__isspace(*gitlink)) gitlink++;
-		error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
-	}
-
-	git_buf_free(&file);
-	return error;
-}
-
-static int find_repo(
-	git_buf *repo_path,
-	git_buf *parent_path,
-	const char *start_path,
-	uint32_t flags,
-	const char *ceiling_dirs)
-{
-	int error;
-	git_buf path = GIT_BUF_INIT;
-	struct stat st;
-	dev_t initial_device = 0;
-	bool try_with_dot_git = false;
-	int ceiling_offset;
-
-	git_buf_free(repo_path);
-
-	if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
-		return error;
-
-	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
-
-	if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
-		return error;
-
-	while (!error && !git_buf_len(repo_path)) {
-		if (p_stat(path.ptr, &st) == 0) {
-			/* check that we have not crossed device boundaries */
-			if (initial_device == 0)
-				initial_device = st.st_dev;
-			else if (st.st_dev != initial_device &&
-				(flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
-				break;
-
-			if (S_ISDIR(st.st_mode)) {
-				if (valid_repository_path(&path)) {
-					git_path_to_dir(&path);
-					git_buf_set(repo_path, path.ptr, path.size);
-					break;
-				}
-			}
-			else if (S_ISREG(st.st_mode)) {
-				git_buf repo_link = GIT_BUF_INIT;
-
-				if (!(error = read_gitfile(&repo_link, path.ptr))) {
-					if (valid_repository_path(&repo_link))
-						git_buf_swap(repo_path, &repo_link);
-
-					git_buf_free(&repo_link);
-					break;
-				}
-				git_buf_free(&repo_link);
-			}
-		}
-
-		/* move up one directory level */
-		if (git_path_dirname_r(&path, path.ptr) < 0) {
-			error = -1;
-			break;
-		}
-
-		if (try_with_dot_git) {
-			/* if we tried original dir with and without .git AND either hit
-			 * directory ceiling or NO_SEARCH was requested, then be done.
-			 */
-			if (path.ptr[ceiling_offset] == '\0' ||
-				(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
-				break;
-			/* otherwise look first for .git item */
-			error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
-		}
-		try_with_dot_git = !try_with_dot_git;
-	}
-
-	if (!error && parent_path != NULL) {
-		if (!git_buf_len(repo_path))
-			git_buf_clear(parent_path);
-		else {
-			git_path_dirname_r(parent_path, path.ptr);
-			git_path_to_dir(parent_path);
-		}
-		if (git_buf_oom(parent_path))
-			return -1;
-	}
-
-	git_buf_free(&path);
-
-	if (!git_buf_len(repo_path) && !error) {
-		giterr_set(GITERR_REPOSITORY,
-			"Could not find repository from '%s'", start_path);
-		error = GIT_ENOTFOUND;
-	}
-
-	return error;
-}
-
-int git_repository_open_ext(
-	git_repository **repo_ptr,
-	const char *start_path,
-	uint32_t flags,
-	const char *ceiling_dirs)
-{
-	int error;
-	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
-	git_repository *repo;
-
-	*repo_ptr = NULL;
-
-	if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
-		return error;
-
-	repo = repository_alloc();
-	GITERR_CHECK_ALLOC(repo);
-
-	repo->path_repository = git_buf_detach(&path);
-	GITERR_CHECK_ALLOC(repo->path_repository);
-
-	if ((error = load_config_data(repo)) < 0 ||
-		(error = load_workdir(repo, &parent)) < 0)
-	{
-		git_repository_free(repo);
-		return error;
-	}
-
-	git_buf_free(&parent);
-	*repo_ptr = repo;
-	return 0;
-}
-
-int git_repository_open(git_repository **repo_out, const char *path)
-{
-	return git_repository_open_ext(
-		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
-}
-
-int git_repository_discover(
-	char *repository_path,
-	size_t size,
-	const char *start_path,
-	int across_fs,
-	const char *ceiling_dirs)
-{
-	git_buf path = GIT_BUF_INIT;
-	uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
-	int error;
-
-	assert(start_path && repository_path && size > 0);
-
-	*repository_path = '\0';
-
-	if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
-		return error != GIT_ENOTFOUND ? -1 : error;
-
-	if (size < (size_t)(path.size + 1)) {
-		giterr_set(GITERR_REPOSITORY,
-			"The given buffer is too long to store the discovered path");
-		git_buf_free(&path);
-		return -1;
-	}
-
-	/* success: we discovered a repository */
-	git_buf_copy_cstr(repository_path, size, &path);
-	git_buf_free(&path);
-	return 0;
-}
-
-static int load_config(
-	git_config **out,
-	git_repository *repo,
-	const char *global_config_path,
-	const char *system_config_path)
-{
-	git_buf config_path = GIT_BUF_INIT;
-	git_config *cfg = NULL;
-
-	assert(repo && out);
-
-	if (git_config_new(&cfg) < 0)
-		return -1;
-
-	if (git_buf_joinpath(
-		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)
-		goto on_error;
-
-	if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)
-		goto on_error;
-
-	git_buf_free(&config_path);
-
-	if (global_config_path != NULL) {
-		if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)
-			goto on_error;
-	}
-
-	if (system_config_path != NULL) {
-		if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)
-			goto on_error;
-	}
-
-	*out = cfg;
-	return 0;
-
-on_error:
-	git_buf_free(&config_path);
-	git_config_free(cfg);
-	*out = NULL;
-	return -1;
-}
-
-int git_repository_config__weakptr(git_config **out, git_repository *repo)
-{
-	if (repo->_config == NULL) {
-		git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
-		int res;
-
-		const char *global_config_path = NULL;
-		const char *system_config_path = NULL;
-
-		if (git_config_find_global_r(&global_buf) == 0)
-			global_config_path = global_buf.ptr;
-
-		if (git_config_find_system_r(&system_buf) == 0)
-			system_config_path = system_buf.ptr;
-
-		res = load_config(&repo->_config, repo, global_config_path, system_config_path);
-
-		git_buf_free(&global_buf);
-		git_buf_free(&system_buf);
-
-		if (res < 0)
-			return -1;
-
-		GIT_REFCOUNT_OWN(repo->_config, repo);
-	}
-
-	*out = repo->_config;
-	return 0;
-}
-
-int git_repository_config(git_config **out, git_repository *repo)
-{
-	if (git_repository_config__weakptr(out, repo) < 0)
-		return -1;
-
-	GIT_REFCOUNT_INC(*out);
-	return 0;
-}
-
-void git_repository_set_config(git_repository *repo, git_config *config)
-{
-	assert(repo && config);
-
-	drop_config(repo);
-
-	repo->_config = config;
-	GIT_REFCOUNT_OWN(repo->_config, repo);
-}
-
-int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
-{
-	assert(repo && out);
-
-	if (repo->_odb == NULL) {
-		git_buf odb_path = GIT_BUF_INIT;
-		int res;
-
-		if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
-			return -1;
-
-		res = git_odb_open(&repo->_odb, odb_path.ptr);
-		git_buf_free(&odb_path); /* done with path */
-
-		if (res < 0)
-			return -1;
-
-		GIT_REFCOUNT_OWN(repo->_odb, repo);
-	}
-
-	*out = repo->_odb;
-	return 0;
-}
-
-int git_repository_odb(git_odb **out, git_repository *repo)
-{
-	if (git_repository_odb__weakptr(out, repo) < 0)
-		return -1;
-
-	GIT_REFCOUNT_INC(*out);
-	return 0;
-}
-
-void git_repository_set_odb(git_repository *repo, git_odb *odb)
-{
-	assert(repo && odb);
-
-	drop_odb(repo);
-
-	repo->_odb = odb;
-	GIT_REFCOUNT_OWN(repo->_odb, repo);
-	GIT_REFCOUNT_INC(odb);
-}
-
-int git_repository_index__weakptr(git_index **out, git_repository *repo)
-{
-	assert(out && repo);
-
-	if (repo->_index == NULL) {
-		int res;
-		git_buf index_path = GIT_BUF_INIT;
-
-		if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
-			return -1;
-
-		res = git_index_open(&repo->_index, index_path.ptr);
-		git_buf_free(&index_path); /* done with path */
-
-		if (res < 0)
-			return -1;
-
-		GIT_REFCOUNT_OWN(repo->_index, repo);
-	}
-
-	*out = repo->_index;
-	return 0;
-}
-
-int git_repository_index(git_index **out, git_repository *repo)
-{
-	if (git_repository_index__weakptr(out, repo) < 0)
-		return -1;
-
-	GIT_REFCOUNT_INC(*out);
-	return 0;
-}
-
-void git_repository_set_index(git_repository *repo, git_index *index)
-{
-	assert(repo && index);
-
-	drop_index(repo);
-
-	repo->_index = index;
-	GIT_REFCOUNT_OWN(repo->_index, repo);
-	GIT_REFCOUNT_INC(index);
-}
-
-static int check_repositoryformatversion(git_repository *repo)
-{
-	git_config *config;
-	int version;
-
-	if (git_repository_config__weakptr(&config, repo) < 0)
-		return -1;
-
-	if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
-		return -1;
-
-	if (GIT_REPO_VERSION < version) {
-		giterr_set(GITERR_REPOSITORY,
-			"Unsupported repository version %d. Only versions up to %d are supported.",
-			version, GIT_REPO_VERSION);
-		return -1;
-	}
-
-	return 0;
-}
-
-static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare)
-{
-	git_repository *repo = NULL;
-
-	GIT_UNUSED(is_bare);
-
-	if (git_repository_open(&repo, repository_path) < 0)
-		return -1;
-
-	if (check_repositoryformatversion(repo) < 0) {
-		git_repository_free(repo);
-		return -1;
-	}
-
-	/* TODO: reinitialize the templates */
-
-	*repo_out = repo;
-	return 0;
-}
-
-static int repo_init_createhead(const char *git_dir)
-{
-	git_buf ref_path = GIT_BUF_INIT;
-	git_filebuf ref = GIT_FILEBUF_INIT;
-
-	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
-		git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
-		git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
-		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
-		return -1;
-
-	git_buf_free(&ref_path);
-	return 0;
-}
-
-static int repo_init_config(const char *git_dir, int is_bare)
-{
-	git_buf cfg_path = GIT_BUF_INIT;
-	git_config *config = NULL;
-
-#define SET_REPO_CONFIG(type, name, val) {\
-	if (git_config_set_##type(config, name, val) < 0) { \
-		git_buf_free(&cfg_path); \
-		git_config_free(config); \
-		return -1; } \
-}
-
-	if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
-		return -1;
-
-	if (git_config_open_ondisk(&config, cfg_path.ptr) < 0) {
-		git_buf_free(&cfg_path);
-		return -1;
-	}
-
-	SET_REPO_CONFIG(bool, "core.bare", is_bare);
-	SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
-	/* TODO: what other defaults? */
-
-	git_buf_free(&cfg_path);
-	git_config_free(config);
-	return 0;
-}
-
-#define GIT_HOOKS_DIR "hooks/"
-#define GIT_HOOKS_DIR_MODE 0755
-
-#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
-#define GIT_HOOKS_README_MODE 0755
-#define GIT_HOOKS_README_CONTENT \
-"#!/bin/sh\n"\
-"#\n"\
-"# Place appropriately named executable hook scripts into this directory\n"\
-"# to intercept various actions that git takes.  See `git help hooks` for\n"\
-"# more information.\n"
-
-#define GIT_INFO_DIR "info/"
-#define GIT_INFO_DIR_MODE 0755
-
-#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
-#define GIT_INFO_EXCLUDE_MODE 0644
-#define GIT_INFO_EXCLUDE_CONTENT \
-"# File patterns to ignore; see `git help ignore` for more information.\n"\
-"# Lines that start with '#' are comments.\n"
-
-#define GIT_DESC_FILE "description"
-#define GIT_DESC_MODE 0644
-#define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"
-
-static int repo_write_template(
-	const char *git_dir, const char *file, mode_t mode, const char *content)
-{
-	git_buf path = GIT_BUF_INIT;
-	int fd, error = 0;
-
-	if (git_buf_joinpath(&path, git_dir, file) < 0)
-		return -1;
-
-	fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode);
-
-	if (fd >= 0) {
-		error = p_write(fd, content, strlen(content));
-
-		p_close(fd);
-	}
-	else if (errno != EEXIST)
-		error = fd;
-
-	git_buf_free(&path);
-
-	if (error)
-		giterr_set(GITERR_OS,
-			"Failed to initialize repository with template '%s'", file);
-
-	return error;
-}
-
-static int repo_init_structure(const char *git_dir, int is_bare)
-{
-	int i;
-	struct { const char *dir; mode_t mode; } dirs[] = {
-		{ GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
-		{ GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
-		{ GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE },     /* '/refs/heads/' */
-		{ GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE },      /* '/refs/tags/' */
-		{ GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE },         /* '/hooks/' */
-		{ GIT_INFO_DIR, GIT_INFO_DIR_MODE },           /* '/info/' */
-		{ NULL, 0 }
-	};
-	struct { const char *file; mode_t mode; const char *content; } tmpl[] = {
-		{ GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
-		{ GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
-		{ GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
-		{ NULL, 0, NULL }
-	};
-
-	/* Make the base directory */
-	if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
-		return -1;
-
-	/* Hides the ".git" directory */
-	if (!is_bare) {
-#ifdef GIT_WIN32
-		if (p_hide_directory__w32(git_dir) < 0) {
-			giterr_set(GITERR_REPOSITORY,
-				"Failed to mark Git repository folder as hidden");
-			return -1;
-		}
-#endif
-	}
-
-	/* Make subdirectories as needed */
-	for (i = 0; dirs[i].dir != NULL; ++i) {
-		if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
-			return -1;
-	}
-
-	/* Make template files as needed */
-	for (i = 0; tmpl[i].file != NULL; ++i) {
-		if (repo_write_template(
-				git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
-int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
-{
-	git_buf repository_path = GIT_BUF_INIT;
-
-	assert(repo_out && path);
-
-	if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
-		return -1;
-
-	if (git_path_isdir(repository_path.ptr) == true) {
-		if (valid_repository_path(&repository_path) == true) {
-			int res = repo_init_reinit(repo_out, repository_path.ptr, is_bare);
-			git_buf_free(&repository_path);
-			return res;
-		}
-	}
-
-	if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
-		repo_init_config(repository_path.ptr, is_bare) < 0 || 
-		repo_init_createhead(repository_path.ptr) < 0 ||
-		git_repository_open(repo_out, repository_path.ptr) < 0) {
-		git_buf_free(&repository_path);
-		return -1;
-	}
-
-	git_buf_free(&repository_path);
-	return 0;
-}
-
-int git_repository_head_detached(git_repository *repo)
-{
-	git_reference *ref;
-	git_odb *odb = NULL;
-	int exists;
-
-	if (git_repository_odb__weakptr(&odb, repo) < 0)
-		return -1;
-
-	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
-		return -1;
-
-	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
-		git_reference_free(ref);
-		return 0;
-	}
-
-	exists = git_odb_exists(odb, git_reference_oid(ref));
-
-	git_reference_free(ref);
-	return exists;
-}
-
-int git_repository_head(git_reference **head_out, git_repository *repo)
-{
-	return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
-}
-
-int git_repository_head_orphan(git_repository *repo)
-{
-	git_reference *ref = NULL;
-	int error;
-
-	error = git_repository_head(&ref, repo);
-	git_reference_free(ref);
-
-	if (error == GIT_ENOTFOUND)
-		return 1;
-
-	if (error < 0)
-		return -1;
-
-	return 0;
-}
-
-int git_repository_is_empty(git_repository *repo)
-{
-	git_reference *head = NULL, *branch = NULL;
-	int error;
-
-	if (git_reference_lookup(&head, repo, "HEAD") < 0)
-		return -1;
-
-	if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
-		git_reference_free(head);
-		return 0;
-	}
-
-	if (strcmp(git_reference_target(head), "refs/heads/master") != 0) {
-		git_reference_free(head);
-		return 0;
-	}
-
-	error = git_reference_resolve(&branch, head);
-
-	git_reference_free(head);
-	git_reference_free(branch);
-
-	if (error == GIT_ENOTFOUND)
-		return 1;
-
-	if (error < 0)
-		return -1;
-
-	return 0;
-}
-
-const char *git_repository_path(git_repository *repo)
-{
-	assert(repo);
-	return repo->path_repository;
-}
-
-const char *git_repository_workdir(git_repository *repo)
-{
-	assert(repo);
-
-	if (repo->is_bare)
-		return NULL;
-
-	return repo->workdir;
-}
-
-int git_repository_set_workdir(git_repository *repo, const char *workdir)
-{
-	git_buf path = GIT_BUF_INIT;
-
-	assert(repo && workdir);
-
-	if (git_path_prettify_dir(&path, workdir, NULL) < 0)
-		return -1;
-
-	git__free(repo->workdir);
-
-	repo->workdir = git_buf_detach(&path);
-	repo->is_bare = 0;
-	return 0;
-}
-
-int git_repository_is_bare(git_repository *repo)
-{
-	assert(repo);
-	return repo->is_bare;
-}
-
-int git_repository_head_tree(git_tree **tree, git_repository *repo)
-{
-	git_oid head_oid;
-	git_object *obj = NULL;
-
-	if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
-		/* cannot resolve HEAD - probably brand new repo */
-		giterr_clear();
-		*tree = NULL;
-		return 0;
-	}
-
-	if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||
-		git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)
-		return -1;
-
-	*tree = (git_tree *)obj;
-	return 0;
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include <stdarg.h>
+#include <ctype.h>
+
+#include "git2/object.h"
+#include "git2/refdb.h"
+
+#include "common.h"
+#include "repository.h"
+#include "commit.h"
+#include "tag.h"
+#include "blob.h"
+#include "fileops.h"
+#include "config.h"
+#include "refs.h"
+#include "filter.h"
+#include "odb.h"
+#include "remote.h"
+#include "merge.h"
+
+#define GIT_FILE_CONTENT_PREFIX "gitdir:"
+
+#define GIT_BRANCH_MASTER "master"
+
+#define GIT_REPO_VERSION 0
+
+#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
+
+static void drop_odb(git_repository *repo)
+{
+	if (repo->_odb != NULL) {
+		GIT_REFCOUNT_OWN(repo->_odb, NULL);
+		git_odb_free(repo->_odb);
+		repo->_odb = NULL;
+	}
+}
+
+static void drop_refdb(git_repository *repo)
+{
+	if (repo->_refdb != NULL) {
+		GIT_REFCOUNT_OWN(repo->_refdb, NULL);
+		git_refdb_free(repo->_refdb);
+		repo->_refdb = NULL;
+	}
+}
+
+static void drop_config(git_repository *repo)
+{
+	if (repo->_config != NULL) {
+		GIT_REFCOUNT_OWN(repo->_config, NULL);
+		git_config_free(repo->_config);
+		repo->_config = NULL;
+	}
+
+	git_repository__cvar_cache_clear(repo);
+}
+
+static void drop_index(git_repository *repo)
+{
+	if (repo->_index != NULL) {
+		GIT_REFCOUNT_OWN(repo->_index, NULL);
+		git_index_free(repo->_index);
+		repo->_index = NULL;
+	}
+}
+
+void git_repository_free(git_repository *repo)
+{
+	if (repo == NULL)
+		return;
+
+	git_cache_free(&repo->objects);
+	git_attr_cache_flush(repo);
+	git_submodule_config_free(repo);
+
+	git__free(repo->path_repository);
+	git__free(repo->workdir);
+
+	drop_config(repo);
+	drop_index(repo);
+	drop_odb(repo);
+	drop_refdb(repo);
+
+	git__free(repo);
+}
+
+/*
+ * Git repository open methods
+ *
+ * Open a repository object from its path
+ */
+static bool valid_repository_path(git_buf *repository_path)
+{
+	/* Check OBJECTS_DIR first, since it will generate the longest path name */
+	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
+		return false;
+
+	/* Ensure HEAD file exists */
+	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
+		return false;
+
+	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)
+		return false;
+
+	return true;
+}
+
+static git_repository *repository_alloc(void)
+{
+	git_repository *repo = git__malloc(sizeof(git_repository));
+	if (!repo)
+		return NULL;
+
+	memset(repo, 0x0, sizeof(git_repository));
+
+	if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
+		git__free(repo);
+		return NULL;
+	}
+
+	/* set all the entries in the cvar cache to `unset` */
+	git_repository__cvar_cache_clear(repo);
+
+	return repo;
+}
+
+int git_repository_new(git_repository **out)
+{
+	*out = repository_alloc();
+	return 0;
+}
+
+static int load_config_data(git_repository *repo)
+{
+	int is_bare;
+	git_config *config;
+
+	if (git_repository_config__weakptr(&config, repo) < 0)
+		return -1;
+
+	/* Try to figure out if it's bare, default to non-bare if it's not set */
+	if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
+		repo->is_bare = 0;
+	else
+		repo->is_bare = is_bare;
+
+	return 0;
+}
+
+static int load_workdir(git_repository *repo, git_buf *parent_path)
+{
+	int         error;
+	git_config *config;
+	const char *worktree;
+	git_buf     worktree_buf = GIT_BUF_INIT;
+
+	if (repo->is_bare)
+		return 0;
+
+	if (git_repository_config__weakptr(&config, repo) < 0)
+		return -1;
+
+	error = git_config_get_string(&worktree, config, "core.worktree");
+	if (!error && worktree != NULL) {
+		error = git_path_prettify_dir(
+			&worktree_buf, worktree, repo->path_repository);
+		if (error < 0)
+			return error;
+		repo->workdir = git_buf_detach(&worktree_buf);
+	}
+	else if (error != GIT_ENOTFOUND)
+		return error;
+	else {
+		giterr_clear();
+
+		if (parent_path && git_path_isdir(parent_path->ptr))
+			repo->workdir = git_buf_detach(parent_path);
+		else {
+			git_path_dirname_r(&worktree_buf, repo->path_repository);
+			git_path_to_dir(&worktree_buf);
+			repo->workdir = git_buf_detach(&worktree_buf);
+		}
+	}
+
+	GITERR_CHECK_ALLOC(repo->workdir);
+
+	return 0;
+}
+
+/*
+ * This function returns furthest offset into path where a ceiling dir
+ * is found, so we can stop processing the path at that point.
+ *
+ * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
+ * the stack could remove directories name limits, but at the cost of doing
+ * repeated malloc/frees inside the loop below, so let's not do it now.
+ */
+static int find_ceiling_dir_offset(
+	const char *path,
+	const char *ceiling_directories)
+{
+	char buf[GIT_PATH_MAX + 1];
+	char buf2[GIT_PATH_MAX + 1];
+	const char *ceil, *sep;
+	size_t len, max_len = 0, min_len;
+
+	assert(path);
+
+	min_len = (size_t)(git_path_root(path) + 1);
+
+	if (ceiling_directories == NULL || min_len == 0)
+		return (int)min_len;
+
+	for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
+		for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
+		len = sep - ceil;
+
+		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
+			continue;
+
+		strncpy(buf, ceil, len);
+		buf[len] = '\0';
+
+		if (p_realpath(buf, buf2) == NULL)
+			continue;
+
+		len = strlen(buf2);
+		if (len > 0 && buf2[len-1] == '/')
+			buf[--len] = '\0';
+
+		if (!strncmp(path, buf2, len) &&
+			path[len] == '/' &&
+			len > max_len)
+		{
+			max_len = len;
+		}
+	}
+
+	return (int)(max_len <= min_len ? min_len : max_len);
+}
+
+/*
+ * Read the contents of `file_path` and set `path_out` to the repo dir that
+ * it points to.  Before calling, set `path_out` to the base directory that
+ * should be used if the contents of `file_path` are a relative path.
+ */
+static int read_gitfile(git_buf *path_out, const char *file_path)
+{
+	int     error = 0;
+	git_buf file = GIT_BUF_INIT;
+	size_t  prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
+
+	assert(path_out && file_path);
+
+	if (git_futils_readbuffer(&file, file_path) < 0)
+		return -1;
+
+	git_buf_rtrim(&file);
+
+	if (git_buf_len(&file) <= prefix_len ||
+		memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
+	{
+		giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
+		error = -1;
+	}
+	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
+		const char *gitlink = git_buf_cstr(&file) + prefix_len;
+		while (*gitlink && git__isspace(*gitlink)) gitlink++;
+		error = git_path_prettify_dir(
+			path_out, gitlink, git_buf_cstr(path_out));
+	}
+
+	git_buf_free(&file);
+	return error;
+}
+
+static int find_repo(
+	git_buf *repo_path,
+	git_buf *parent_path,
+	const char *start_path,
+	uint32_t flags,
+	const char *ceiling_dirs)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+	struct stat st;
+	dev_t initial_device = 0;
+	bool try_with_dot_git = false;
+	int ceiling_offset;
+
+	git_buf_free(repo_path);
+
+	if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
+		return error;
+
+	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+
+	if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+		return error;
+
+	while (!error && !git_buf_len(repo_path)) {
+		if (p_stat(path.ptr, &st) == 0) {
+			/* check that we have not crossed device boundaries */
+			if (initial_device == 0)
+				initial_device = st.st_dev;
+			else if (st.st_dev != initial_device &&
+				(flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
+				break;
+
+			if (S_ISDIR(st.st_mode)) {
+				if (valid_repository_path(&path)) {
+					git_path_to_dir(&path);
+					git_buf_set(repo_path, path.ptr, path.size);
+					break;
+				}
+			}
+			else if (S_ISREG(st.st_mode)) {
+				git_buf repo_link = GIT_BUF_INIT;
+
+				if (!(error = read_gitfile(&repo_link, path.ptr))) {
+					if (valid_repository_path(&repo_link))
+						git_buf_swap(repo_path, &repo_link);
+
+					git_buf_free(&repo_link);
+					break;
+				}
+				git_buf_free(&repo_link);
+			}
+		}
+
+		/* move up one directory level */
+		if (git_path_dirname_r(&path, path.ptr) < 0) {
+			error = -1;
+			break;
+		}
+
+		if (try_with_dot_git) {
+			/* if we tried original dir with and without .git AND either hit
+			 * directory ceiling or NO_SEARCH was requested, then be done.
+			 */
+			if (path.ptr[ceiling_offset] == '\0' ||
+				(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
+				break;
+			/* otherwise look first for .git item */
+			error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
+		}
+		try_with_dot_git = !try_with_dot_git;
+	}
+
+	if (!error && parent_path != NULL) {
+		if (!git_buf_len(repo_path))
+			git_buf_clear(parent_path);
+		else {
+			git_path_dirname_r(parent_path, path.ptr);
+			git_path_to_dir(parent_path);
+		}
+		if (git_buf_oom(parent_path))
+			return -1;
+	}
+
+	git_buf_free(&path);
+
+	if (!git_buf_len(repo_path) && !error) {
+		giterr_set(GITERR_REPOSITORY,
+			"Could not find repository from '%s'", start_path);
+		error = GIT_ENOTFOUND;
+	}
+
+	return error;
+}
+
+int git_repository_open_ext(
+	git_repository **repo_ptr,
+	const char *start_path,
+	unsigned int flags,
+	const char *ceiling_dirs)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
+	git_repository *repo;
+
+	if (repo_ptr)
+		*repo_ptr = NULL;
+
+	error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
+	if (error < 0 || !repo_ptr)
+		return error;
+
+	repo = repository_alloc();
+	GITERR_CHECK_ALLOC(repo);
+
+	repo->path_repository = git_buf_detach(&path);
+	GITERR_CHECK_ALLOC(repo->path_repository);
+
+	if ((error = load_config_data(repo)) < 0 ||
+		(error = load_workdir(repo, &parent)) < 0)
+	{
+		git_repository_free(repo);
+		return error;
+	}
+
+	git_buf_free(&parent);
+	*repo_ptr = repo;
+	return 0;
+}
+
+int git_repository_open(git_repository **repo_out, const char *path)
+{
+	return git_repository_open_ext(
+		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
+}
+
+int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
+{
+	git_repository *repo;
+
+	repo = repository_alloc();
+	GITERR_CHECK_ALLOC(repo);
+
+	git_repository_set_odb(repo, odb);
+	*repo_out = repo;
+
+	return 0;
+}
+
+int git_repository_discover(
+	char *repository_path,
+	size_t size,
+	const char *start_path,
+	int across_fs,
+	const char *ceiling_dirs)
+{
+	git_buf path = GIT_BUF_INIT;
+	uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+	int error;
+
+	assert(start_path && repository_path && size > 0);
+
+	*repository_path = '\0';
+
+	if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
+		return error != GIT_ENOTFOUND ? -1 : error;
+
+	if (size < (size_t)(path.size + 1)) {
+		giterr_set(GITERR_REPOSITORY,
+			"The given buffer is too small to store the discovered path");
+		git_buf_free(&path);
+		return -1;
+	}
+
+	/* success: we discovered a repository */
+	git_buf_copy_cstr(repository_path, size, &path);
+	git_buf_free(&path);
+	return 0;
+}
+
+static int load_config(
+	git_config **out,
+	git_repository *repo,
+	const char *global_config_path,
+	const char *xdg_config_path,
+	const char *system_config_path)
+{
+	int error;
+	git_buf config_path = GIT_BUF_INIT;
+	git_config *cfg = NULL;
+
+	assert(repo && out);
+
+	if ((error = git_config_new(&cfg)) < 0)
+		return error;
+
+	error = git_buf_joinpath(
+		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
+	if (error < 0)
+		goto on_error;
+
+	if ((error = git_config_add_file_ondisk(
+			cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 &&
+		error != GIT_ENOTFOUND)
+		goto on_error;
+
+	git_buf_free(&config_path);
+
+	if (global_config_path != NULL &&
+		(error = git_config_add_file_ondisk(
+			cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
+		error != GIT_ENOTFOUND)
+		goto on_error;
+
+	if (xdg_config_path != NULL &&
+		(error = git_config_add_file_ondisk(
+			cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 &&
+		error != GIT_ENOTFOUND)
+		goto on_error;
+
+	if (system_config_path != NULL &&
+		(error = git_config_add_file_ondisk(
+			cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 &&
+		error != GIT_ENOTFOUND)
+		goto on_error;
+
+	giterr_clear(); /* clear any lingering ENOTFOUND errors */
+
+	*out = cfg;
+	return 0;
+
+on_error:
+	git_buf_free(&config_path);
+	git_config_free(cfg);
+	*out = NULL;
+	return error;
+}
+
+int git_repository_config__weakptr(git_config **out, git_repository *repo)
+{
+	if (repo->_config == NULL) {
+		git_buf global_buf = GIT_BUF_INIT, xdg_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
+		int res;
+
+		const char *global_config_path = NULL;
+		const char *xdg_config_path = NULL;
+		const char *system_config_path = NULL;
+
+		if (git_config_find_global_r(&global_buf) == 0)
+			global_config_path = global_buf.ptr;
+
+		if (git_config_find_xdg_r(&xdg_buf) == 0)
+			xdg_config_path = xdg_buf.ptr;
+
+		if (git_config_find_system_r(&system_buf) == 0)
+			system_config_path = system_buf.ptr;
+
+		res = load_config(&repo->_config, repo, global_config_path, xdg_config_path, system_config_path);
+
+		git_buf_free(&global_buf);
+		git_buf_free(&xdg_buf);
+		git_buf_free(&system_buf);
+
+		if (res < 0)
+			return -1;
+
+		GIT_REFCOUNT_OWN(repo->_config, repo);
+	}
+
+	*out = repo->_config;
+	return 0;
+}
+
+int git_repository_config(git_config **out, git_repository *repo)
+{
+	if (git_repository_config__weakptr(out, repo) < 0)
+		return -1;
+
+	GIT_REFCOUNT_INC(*out);
+	return 0;
+}
+
+void git_repository_set_config(git_repository *repo, git_config *config)
+{
+	assert(repo && config);
+
+	drop_config(repo);
+
+	repo->_config = config;
+	GIT_REFCOUNT_OWN(repo->_config, repo);
+	GIT_REFCOUNT_INC(repo->_config);
+}
+
+int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
+{
+	assert(repo && out);
+
+	if (repo->_odb == NULL) {
+		git_buf odb_path = GIT_BUF_INIT;
+		int res;
+
+		if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
+			return -1;
+
+		res = git_odb_open(&repo->_odb, odb_path.ptr);
+		git_buf_free(&odb_path); /* done with path */
+
+		if (res < 0)
+			return -1;
+
+		GIT_REFCOUNT_OWN(repo->_odb, repo);
+	}
+
+	*out = repo->_odb;
+	return 0;
+}
+
+int git_repository_odb(git_odb **out, git_repository *repo)
+{
+	if (git_repository_odb__weakptr(out, repo) < 0)
+		return -1;
+
+	GIT_REFCOUNT_INC(*out);
+	return 0;
+}
+
+void git_repository_set_odb(git_repository *repo, git_odb *odb)
+{
+	assert(repo && odb);
+
+	drop_odb(repo);
+
+	repo->_odb = odb;
+	GIT_REFCOUNT_OWN(repo->_odb, repo);
+	GIT_REFCOUNT_INC(odb);
+}
+
+int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
+{
+	assert(out && repo);
+
+	if (repo->_refdb == NULL) {
+		int res;
+
+		res = git_refdb_open(&repo->_refdb, repo);
+
+		if (res < 0)
+			return -1;
+
+		GIT_REFCOUNT_OWN(repo->_refdb, repo);
+	}
+
+	*out = repo->_refdb;
+	return 0;
+}
+
+int git_repository_refdb(git_refdb **out, git_repository *repo)
+{
+	if (git_repository_refdb__weakptr(out, repo) < 0)
+		return -1;
+
+	GIT_REFCOUNT_INC(*out);
+	return 0;
+}
+
+void git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
+{
+	assert (repo && refdb);
+
+	drop_refdb(repo);
+
+	repo->_refdb = refdb;
+	GIT_REFCOUNT_OWN(repo->_refdb, repo);
+	GIT_REFCOUNT_INC(refdb);
+}
+
+int git_repository_index__weakptr(git_index **out, git_repository *repo)
+{
+	assert(out && repo);
+
+	if (repo->_index == NULL) {
+		int res;
+		git_buf index_path = GIT_BUF_INIT;
+
+		if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
+			return -1;
+
+		res = git_index_open(&repo->_index, index_path.ptr);
+		git_buf_free(&index_path); /* done with path */
+
+		if (res < 0)
+			return -1;
+
+		GIT_REFCOUNT_OWN(repo->_index, repo);
+
+		if (git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER) < 0)
+			return -1;
+	}
+
+	*out = repo->_index;
+	return 0;
+}
+
+int git_repository_index(git_index **out, git_repository *repo)
+{
+	if (git_repository_index__weakptr(out, repo) < 0)
+		return -1;
+
+	GIT_REFCOUNT_INC(*out);
+	return 0;
+}
+
+void git_repository_set_index(git_repository *repo, git_index *index)
+{
+	assert(repo && index);
+
+	drop_index(repo);
+
+	repo->_index = index;
+	GIT_REFCOUNT_OWN(repo->_index, repo);
+	GIT_REFCOUNT_INC(index);
+}
+
+static int check_repositoryformatversion(git_config *config)
+{
+	int version;
+
+	if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
+		return -1;
+
+	if (GIT_REPO_VERSION < version) {
+		giterr_set(GITERR_REPOSITORY,
+			"Unsupported repository version %d. Only versions up to %d are supported.",
+			version, GIT_REPO_VERSION);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int repo_init_create_head(const char *git_dir, const char *ref_name)
+{
+	git_buf ref_path = GIT_BUF_INIT;
+	git_filebuf ref = GIT_FILEBUF_INIT;
+	const char *fmt;
+
+	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
+		git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
+		goto fail;
+
+	if (!ref_name)
+		ref_name = GIT_BRANCH_MASTER;
+
+	if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
+		fmt = "ref: %s\n";
+	else
+		fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
+
+	if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
+		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
+		goto fail;
+
+	git_buf_free(&ref_path);
+	return 0;
+
+fail:
+	git_buf_free(&ref_path);
+	git_filebuf_cleanup(&ref);
+	return -1;
+}
+
+static bool is_chmod_supported(const char *file_path)
+{
+	struct stat st1, st2;
+	static int _is_supported = -1;
+
+	if (_is_supported > -1)
+		return _is_supported;
+
+	if (p_stat(file_path, &st1) < 0)
+		return false;
+
+	if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
+		return false;
+
+	if (p_stat(file_path, &st2) < 0)
+		return false;
+
+	_is_supported = (st1.st_mode != st2.st_mode);
+
+	return _is_supported;
+}
+
+static bool is_filesystem_case_insensitive(const char *gitdir_path)
+{
+	git_buf path = GIT_BUF_INIT;
+	static int _is_insensitive = -1;
+
+	if (_is_insensitive > -1)
+		return _is_insensitive;
+
+	if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
+		goto cleanup;
+
+	_is_insensitive = git_path_exists(git_buf_cstr(&path));
+
+cleanup:
+	git_buf_free(&path);
+	return _is_insensitive;
+}
+
+static bool are_symlinks_supported(const char *wd_path)
+{
+	git_buf path = GIT_BUF_INIT;
+	int fd;
+	struct stat st;
+	static int _symlinks_supported = -1;
+
+	if (_symlinks_supported > -1)
+		return _symlinks_supported;
+
+	if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
+		p_close(fd) < 0 ||
+		p_unlink(path.ptr) < 0 ||
+		p_symlink("testing", path.ptr) < 0 ||
+		p_lstat(path.ptr, &st) < 0)
+		_symlinks_supported = false;
+	else
+		_symlinks_supported = (S_ISLNK(st.st_mode) != 0);
+
+	(void)p_unlink(path.ptr);
+	git_buf_free(&path);
+
+	return _symlinks_supported;
+}
+
+static int create_empty_file(const char *path, mode_t mode)
+{
+	int fd;
+
+	if ((fd = p_creat(path, mode)) < 0) {
+		giterr_set(GITERR_OS, "Error while creating '%s'", path);
+		return -1;
+	}
+
+	if (p_close(fd) < 0) {
+		giterr_set(GITERR_OS, "Error while closing '%s'", path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int repo_init_config(
+	const char *repo_dir,
+	const char *work_dir,
+	git_repository_init_options *opts)
+{
+	int error = 0;
+	git_buf cfg_path = GIT_BUF_INIT;
+	git_config *config = NULL;
+
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
+	if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+		goto cleanup; } while (0)
+
+	if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+		return -1;
+
+	if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
+		create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
+			git_buf_free(&cfg_path);
+			return -1;
+	}
+
+	if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
+		git_buf_free(&cfg_path);
+		return -1;
+	}
+
+	if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
+		(error = check_repositoryformatversion(config)) < 0)
+		goto cleanup;
+
+	SET_REPO_CONFIG(
+		bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
+	SET_REPO_CONFIG(
+		int32, "core.repositoryformatversion", GIT_REPO_VERSION);
+	SET_REPO_CONFIG(
+		bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
+
+	if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
+		SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
+
+		if (!are_symlinks_supported(work_dir))
+			SET_REPO_CONFIG(bool, "core.symlinks", false);
+
+		if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+			SET_REPO_CONFIG(string, "core.worktree", work_dir);
+		}
+		else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
+			if (git_config_delete_entry(config, "core.worktree") < 0)
+				giterr_clear();
+		}
+	} else {
+		if (!are_symlinks_supported(repo_dir))
+			SET_REPO_CONFIG(bool, "core.symlinks", false);
+	}
+
+	if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
+		is_filesystem_case_insensitive(repo_dir))
+		SET_REPO_CONFIG(bool, "core.ignorecase", true);
+
+	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+		SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
+		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+	}
+	else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+		SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
+		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+	}
+
+cleanup:
+	git_buf_free(&cfg_path);
+	git_config_free(config);
+
+	return error;
+}
+
+static int repo_write_template(
+	const char *git_dir,
+	bool allow_overwrite,
+	const char *file,
+	mode_t mode,
+	bool hidden,
+	const char *content)
+{
+	git_buf path = GIT_BUF_INIT;
+	int fd, error = 0, flags;
+
+	if (git_buf_joinpath(&path, git_dir, file) < 0)
+		return -1;
+
+	if (allow_overwrite)
+		flags = O_WRONLY | O_CREAT | O_TRUNC;
+	else
+		flags = O_WRONLY | O_CREAT | O_EXCL;
+
+	fd = p_open(git_buf_cstr(&path), flags, mode);
+
+	if (fd >= 0) {
+		error = p_write(fd, content, strlen(content));
+
+		p_close(fd);
+	}
+	else if (errno != EEXIST)
+		error = fd;
+
+#ifdef GIT_WIN32
+	if (!error && hidden) {
+		if (p_hide_directory__w32(path.ptr) < 0)
+			error = -1;
+	}
+#else
+	GIT_UNUSED(hidden);
+#endif
+
+	git_buf_free(&path);
+
+	if (error)
+		giterr_set(GITERR_OS,
+			"Failed to initialize repository with template '%s'", file);
+
+	return error;
+}
+
+static int repo_write_gitlink(
+	const char *in_dir, const char *to_repo)
+{
+	int error;
+	git_buf buf = GIT_BUF_INIT;
+	struct stat st;
+
+	git_path_dirname_r(&buf, to_repo);
+	git_path_to_dir(&buf);
+	if (git_buf_oom(&buf))
+		return -1;
+
+	/* don't write gitlink to natural workdir */
+	if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
+		strcmp(in_dir, buf.ptr) == 0)
+	{
+		error = GIT_PASSTHROUGH;
+		goto cleanup;
+	}
+
+	if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
+		goto cleanup;
+
+	if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
+		giterr_set(GITERR_REPOSITORY,
+			"Cannot overwrite gitlink file into path '%s'", in_dir);
+		error = GIT_EEXISTS;
+		goto cleanup;
+	}
+
+	git_buf_clear(&buf);
+
+	error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);
+
+	if (!error)
+		error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
+
+cleanup:
+	git_buf_free(&buf);
+	return error;
+}
+
+static mode_t pick_dir_mode(git_repository_init_options *opts)
+{
+	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
+		return 0777;
+	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
+		return (0775 | S_ISGID);
+	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
+		return (0777 | S_ISGID);
+	return opts->mode;
+}
+
+#include "repo_template.h"
+
+static int repo_init_structure(
+	const char *repo_dir,
+	const char *work_dir,
+	git_repository_init_options *opts)
+{
+	int error = 0;
+	repo_template_item *tpl;
+	bool external_tpl =
+		((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
+	mode_t dmode = pick_dir_mode(opts);
+
+	/* Hide the ".git" directory */
+#ifdef GIT_WIN32
+	if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
+		if (p_hide_directory__w32(repo_dir) < 0) {
+			giterr_set(GITERR_REPOSITORY,
+				"Failed to mark Git repository folder as hidden");
+			return -1;
+		}
+	}
+#endif
+
+	/* Create the .git gitlink if appropriate */
+	if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
+		(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
+	{
+		if (repo_write_gitlink(work_dir, repo_dir) < 0)
+			return -1;
+	}
+
+	/* Copy external template if requested */
+	if (external_tpl) {
+		git_config *cfg;
+		const char *tdir;
+
+		if (opts->template_path)
+			tdir = opts->template_path;
+		else if ((error = git_config_open_default(&cfg)) < 0)
+			return error;
+		else {
+			error = git_config_get_string(&tdir, cfg, "init.templatedir");
+
+			git_config_free(cfg);
+
+			if (error && error != GIT_ENOTFOUND)
+				return error;
+
+			giterr_clear();
+			tdir = GIT_TEMPLATE_DIR;
+		}
+
+		error = git_futils_cp_r(tdir, repo_dir,
+			GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
+			GIT_CPDIR_SIMPLE_TO_MODE, dmode);
+
+		if (error < 0) {
+			if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
+				return error;
+
+			/* if template was default, ignore error and use internal */
+			giterr_clear();
+			external_tpl = false;
+			error = 0;
+		}
+	}
+
+	/* Copy internal template
+	 * - always ensure existence of dirs
+	 * - only create files if no external template was specified
+	 */
+	for (tpl = repo_template; !error && tpl->path; ++tpl) {
+		if (!tpl->content)
+			error = git_futils_mkdir(
+				tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
+		else if (!external_tpl) {
+			const char *content = tpl->content;
+
+			if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
+				content = opts->description;
+
+			error = repo_write_template(
+				repo_dir, false, tpl->path, tpl->mode, false, content);
+		}
+	}
+
+	return error;
+}
+
+static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
+{
+	/* When making parent directories during repository initialization
+	 * don't try to set gid or grant world write access
+	 */
+	return git_futils_mkdir(
+		buf->ptr, NULL, mode & ~(S_ISGID | 0002),
+		GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
+		(skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
+}
+
+static int repo_init_directories(
+	git_buf *repo_path,
+	git_buf *wd_path,
+	const char *given_repo,
+	git_repository_init_options *opts)
+{
+	int error = 0;
+	bool is_bare, add_dotgit, has_dotgit, natural_wd;
+	mode_t dirmode;
+
+	/* There are three possible rules for what we are allowed to create:
+	 * - MKPATH means anything we need
+	 * - MKDIR means just the .git directory and its parent and the workdir
+	 * - Neither means only the .git directory can be created
+	 *
+	 * There are 5 "segments" of path that we might need to deal with:
+	 * 1. The .git directory
+	 * 2. The parent of the .git directory
+	 * 3. Everything above the parent of the .git directory
+	 * 4. The working directory (often the same as #2)
+	 * 5. Everything above the working directory (often the same as #3)
+	 *
+	 * For all directories created, we start with the init_mode value for
+	 * permissions and then strip off bits in some cases:
+	 *
+	 * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH
+	 * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID
+	 * For all rules, we create #1 using the untouched init_mode
+	 */
+
+	/* set up repo path */
+
+	is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
+
+	add_dotgit =
+		(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
+		!is_bare &&
+		git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
+		git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
+
+	if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
+		return -1;
+
+	has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
+	if (has_dotgit)
+		opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
+
+	/* set up workdir path */
+
+	if (!is_bare) {
+		if (opts->workdir_path) {
+			if (git_path_join_unrooted(
+					wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
+				return -1;
+		} else if (has_dotgit) {
+			if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
+				return -1;
+		} else {
+			giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
+				" for non-bare repository that isn't a '.git' directory");
+			return -1;
+		}
+
+		if (git_path_to_dir(wd_path) < 0)
+			return -1;
+	} else {
+		git_buf_clear(wd_path);
+	}
+
+	natural_wd =
+		has_dotgit &&
+		wd_path->size > 0 &&
+		wd_path->size + strlen(GIT_DIR) == repo_path->size &&
+		memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
+	if (natural_wd)
+		opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
+
+	/* create directories as needed / requested */
+
+	dirmode = pick_dir_mode(opts);
+
+	if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
+		/* create path #5 */
+		if (wd_path->size > 0 &&
+			(error = mkdir_parent(wd_path, dirmode, false)) < 0)
+			return error;
+
+		/* create path #3 (if not the same as #5) */
+		if (!natural_wd &&
+			(error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0)
+			return error;
+	}
+
+	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
+	{
+		/* create path #4 */
+		if (wd_path->size > 0 &&
+			(error = git_futils_mkdir(
+				wd_path->ptr, NULL, dirmode & ~S_ISGID,
+				GIT_MKDIR_VERIFY_DIR)) < 0)
+			return error;
+
+		/* create path #2 (if not the same as #4) */
+		if (!natural_wd &&
+			(error = git_futils_mkdir(
+				repo_path->ptr, NULL, dirmode & ~S_ISGID,
+				GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
+			return error;
+	}
+
+	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
+		has_dotgit)
+	{
+		/* create path #1 */
+		error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
+			GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
+	}
+
+	/* prettify both directories now that they are created */
+
+	if (!error) {
+		error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);
+
+		if (!error && wd_path->size > 0)
+			error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
+	}
+
+	return error;
+}
+
+static int repo_init_create_origin(git_repository *repo, const char *url)
+{
+	int error;
+	git_remote *remote;
+
+	if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
+		git_remote_free(remote);
+	}
+
+	return error;
+}
+
+int git_repository_init(
+	git_repository **repo_out, const char *path, unsigned is_bare)
+{
+	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+	opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
+	if (is_bare)
+		opts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+	return git_repository_init_ext(repo_out, path, &opts);
+}
+
+int git_repository_init_ext(
+	git_repository **out,
+	const char *given_repo,
+	git_repository_init_options *opts)
+{
+	int error;
+	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;
+
+	assert(out && given_repo && opts);
+
+	GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
+
+	error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
+	if (error < 0)
+		goto cleanup;
+
+	if (valid_repository_path(&repo_path)) {
+
+		if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
+			giterr_set(GITERR_REPOSITORY,
+				"Attempt to reinitialize '%s'", given_repo);
+			error = GIT_EEXISTS;
+			goto cleanup;
+		}
+
+		opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
+
+		error = repo_init_config(
+			git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
+
+		/* TODO: reinitialize the templates */
+	}
+	else {
+		if (!(error = repo_init_structure(
+				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
+			!(error = repo_init_config(
+				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
+			error = repo_init_create_head(
+				git_buf_cstr(&repo_path), opts->initial_head);
+	}
+	if (error < 0)
+		goto cleanup;
+
+	error = git_repository_open(out, git_buf_cstr(&repo_path));
+
+	if (!error && opts->origin_url)
+		error = repo_init_create_origin(*out, opts->origin_url);
+
+cleanup:
+	git_buf_free(&repo_path);
+	git_buf_free(&wd_path);
+
+	return error;
+}
+
+int git_repository_head_detached(git_repository *repo)
+{
+	git_reference *ref;
+	git_odb *odb = NULL;
+	int exists;
+
+	if (git_repository_odb__weakptr(&odb, repo) < 0)
+		return -1;
+
+	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+		return -1;
+
+	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
+		git_reference_free(ref);
+		return 0;
+	}
+
+	exists = git_odb_exists(odb, git_reference_target(ref));
+
+	git_reference_free(ref);
+	return exists;
+}
+
+int git_repository_head(git_reference **head_out, git_repository *repo)
+{
+	git_reference *head;
+	int error;
+
+	if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
+		return error;
+
+	if (git_reference_type(head) == GIT_REF_OID) {
+		*head_out = head;
+		return 0;
+	}
+
+	error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
+	git_reference_free(head);
+
+	return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
+}
+
+int git_repository_head_orphan(git_repository *repo)
+{
+	git_reference *ref = NULL;
+	int error;
+
+	error = git_repository_head(&ref, repo);
+	git_reference_free(ref);
+
+	if (error == GIT_EORPHANEDHEAD)
+		return 1;
+
+	if (error < 0)
+		return -1;
+
+	return 0;
+}
+
+static int at_least_one_cb(const char *refname, void *payload)
+{
+	GIT_UNUSED(refname);
+	GIT_UNUSED(payload);
+
+	return GIT_EUSER;
+}
+
+static int repo_contains_no_reference(git_repository *repo)
+{
+	int error;
+	
+	error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
+
+	if (error == GIT_EUSER)
+		return 0;
+
+	return error == 0 ? 1 : error;
+}
+
+int git_repository_is_empty(git_repository *repo)
+{
+	git_reference *head = NULL;
+	int error;
+
+	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
+		return -1;
+
+	if (!(error = git_reference_type(head) == GIT_REF_SYMBOLIC))
+		goto cleanup;
+
+	if (!(error = strcmp(
+		git_reference_symbolic_target(head),
+		GIT_REFS_HEADS_DIR "master") == 0))
+			goto cleanup;
+
+	error = repo_contains_no_reference(repo);
+
+cleanup:
+	git_reference_free(head);
+	return error < 0 ? -1 : error;
+}
+
+const char *git_repository_path(git_repository *repo)
+{
+	assert(repo);
+	return repo->path_repository;
+}
+
+const char *git_repository_workdir(git_repository *repo)
+{
+	assert(repo);
+
+	if (repo->is_bare)
+		return NULL;
+
+	return repo->workdir;
+}
+
+int git_repository_set_workdir(
+	git_repository *repo, const char *workdir, int update_gitlink)
+{
+	int error = 0;
+	git_buf path = GIT_BUF_INIT;
+
+	assert(repo && workdir);
+
+	if (git_path_prettify_dir(&path, workdir, NULL) < 0)
+		return -1;
+
+	if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
+		return 0;
+
+	if (update_gitlink) {
+		git_config *config;
+
+		if (git_repository_config__weakptr(&config, repo) < 0)
+			return -1;
+
+		error = repo_write_gitlink(path.ptr, git_repository_path(repo));
+
+		/* passthrough error means gitlink is unnecessary */
+		if (error == GIT_PASSTHROUGH)
+			error = git_config_delete_entry(config, "core.worktree");
+		else if (!error)
+			error = git_config_set_string(config, "core.worktree", path.ptr);
+
+		if (!error)
+			error = git_config_set_bool(config, "core.bare", false);
+	}
+
+	if (!error) {
+		char *old_workdir = repo->workdir;
+
+		repo->workdir = git_buf_detach(&path);
+		repo->is_bare = 0;
+
+		git__free(old_workdir);
+	}
+
+	return error;
+}
+
+int git_repository_is_bare(git_repository *repo)
+{
+	assert(repo);
+	return repo->is_bare;
+}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo)
+{
+	git_reference *head;
+	git_object *obj;
+	int error;
+
+	if ((error = git_repository_head(&head, repo)) < 0)
+		return error;
+
+	if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
+		goto cleanup;
+
+	*tree = (git_tree *)obj;
+
+cleanup:
+	git_reference_free(head);
+	return error;
+}
+
+int git_repository_message(char *buffer, size_t len, git_repository *repo)
+{
+	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
+	struct stat st;
+	int error;
+
+	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
+		return -1;
+
+	if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
+		if (errno == ENOENT)
+			error = GIT_ENOTFOUND;
+	}
+	else if (buffer != NULL) {
+		error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
+		git_buf_copy_cstr(buffer, len, &buf);
+	}
+
+	git_buf_free(&path);
+	git_buf_free(&buf);
+
+	if (!error)
+		error = (int)st.st_size + 1; /* add 1 for NUL byte */
+
+	return error;
+}
+
+int git_repository_message_remove(git_repository *repo)
+{
+	git_buf path = GIT_BUF_INIT;
+	int error;
+
+	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
+		return -1;
+
+	error = p_unlink(git_buf_cstr(&path));
+	git_buf_free(&path);
+
+	return error;
+}
+
+int git_repository_hashfile(
+    git_oid *out,
+    git_repository *repo,
+    const char *path,
+    git_otype type,
+    const char *as_path)
+{
+	int error;
+	git_vector filters = GIT_VECTOR_INIT;
+	git_file fd = -1;
+	git_off_t len;
+	git_buf full_path = GIT_BUF_INIT;
+
+	assert(out && path && repo); /* as_path can be NULL */
+
+	/* At some point, it would be nice if repo could be NULL to just
+	 * apply filter rules defined in system and global files, but for
+	 * now that is not possible because git_filters_load() needs it.
+	 */
+
+	error = git_path_join_unrooted(
+		&full_path, path, repo ? git_repository_workdir(repo) : NULL, NULL);
+	if (error < 0)
+		return error;
+
+	if (!as_path)
+		as_path = path;
+
+	/* passing empty string for "as_path" indicated --no-filters */
+	if (strlen(as_path) > 0) {
+		error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
+		if (error < 0)
+			return error;
+	} else {
+		error = 0;
+	}
+
+	/* at this point, error is a count of the number of loaded filters */
+
+	fd = git_futils_open_ro(full_path.ptr);
+	if (fd < 0) {
+		error = fd;
+		goto cleanup;
+	}
+
+	len = git_futils_filesize(fd);
+	if (len < 0) {
+		error = (int)len;
+		goto cleanup;
+	}
+
+	if (!git__is_sizet(len)) {
+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+		error = -1;
+		goto cleanup;
+	}
+
+	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
+
+cleanup:
+	if (fd >= 0)
+		p_close(fd);
+	git_filters_free(&filters);
+	git_buf_free(&full_path);
+
+	return error;
+}
+
+static bool looks_like_a_branch(const char *refname)
+{
+	return git__prefixcmp(refname, GIT_REFS_HEADS_DIR) == 0;
+}
+
+int git_repository_set_head(
+	git_repository* repo,
+	const char* refname)
+{
+	git_reference *ref,
+		*new_head = NULL;
+	int error;
+
+	assert(repo && refname);
+
+	error = git_reference_lookup(&ref, repo, refname);
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return error;
+
+	if (!error) {
+		if (git_reference_is_branch(ref))
+			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1);
+		else
+			error = git_repository_set_head_detached(repo, git_reference_target(ref));
+	} else if (looks_like_a_branch(refname))
+		error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1);
+
+	git_reference_free(ref);
+	git_reference_free(new_head);
+	return error;
+}
+
+int git_repository_set_head_detached(
+	git_repository* repo,
+	const git_oid* commitish)
+{
+	int error;
+	git_object *object,
+		*peeled = NULL;
+	git_reference *new_head = NULL;
+
+	assert(repo && commitish);
+
+	if ((error = git_object_lookup(&object, repo, commitish, GIT_OBJ_ANY)) < 0)
+		return error;
+
+	if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
+		goto cleanup;
+
+	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1);
+
+cleanup:
+	git_object_free(object);
+	git_object_free(peeled);
+	git_reference_free(new_head);
+	return error;
+}
+
+int git_repository_detach_head(
+	git_repository* repo)
+{
+	git_reference *old_head = NULL,
+		*new_head = NULL;
+	git_object *object = NULL;
+	int error;
+
+	assert(repo);
+
+	if ((error = git_repository_head(&old_head, repo)) < 0)
+		return error;
+
+	if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0)
+		goto cleanup;
+
+	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1);
+
+cleanup:
+	git_object_free(object);
+	git_reference_free(old_head);
+	git_reference_free(new_head);
+	return error;
+}
+
+/**
+ * Loosely ported from git.git
+ * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
+ */
+int git_repository_state(git_repository *repo)
+{
+	git_buf repo_path = GIT_BUF_INIT;
+	int state = GIT_REPOSITORY_STATE_NONE;
+
+	assert(repo);
+
+	if (git_buf_puts(&repo_path, repo->path_repository) < 0)
+		return -1;
+
+	if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
+		state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE;
+	else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR))
+		state = GIT_REPOSITORY_STATE_REBASE_MERGE;
+	else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE))
+		state = GIT_REPOSITORY_STATE_REBASE;
+	else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE))
+		state = GIT_REPOSITORY_STATE_APPLY_MAILBOX;
+	else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR))
+		state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE;
+	else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE))
+		state = GIT_REPOSITORY_STATE_MERGE;
+	else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE))
+		state = GIT_REPOSITORY_STATE_REVERT;
+	else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE))
+		state = GIT_REPOSITORY_STATE_CHERRY_PICK;
+	else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
+		state = GIT_REPOSITORY_STATE_BISECT;
+
+	git_buf_free(&repo_path);
+	return state;
 }
diff --git a/libgit2/src/repository.h b/libgit2/src/repository.h
--- a/libgit2/src/repository.h
+++ b/libgit2/src/repository.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -18,8 +18,10 @@
 #include "refs.h"
 #include "buffer.h"
 #include "odb.h"
-#include "attr.h"
+#include "object.h"
+#include "attrcache.h"
 #include "strmap.h"
+#include "refdb.h"
 
 #define DOT_GIT ".git"
 #define GIT_DIR DOT_GIT "/"
@@ -68,20 +70,21 @@
 	GIT_EOL_DEFAULT = GIT_EOL_NATIVE
 } git_cvar_value;
 
-/** Base git object for inheritance */
-struct git_object {
-	git_cached_obj cached;
-	git_repository *repo;
-	git_otype type;
+/* internal repository init flags */
+enum {
+	GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16),
+	GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17),
+	GIT_REPOSITORY_INIT__IS_REINIT  = (1u << 18),
 };
 
+/** Internal structure for repository object */
 struct git_repository {
 	git_odb *_odb;
+	git_refdb *_refdb;
 	git_config *_config;
 	git_index *_index;
 
 	git_cache objects;
-	git_refcache references;
 	git_attr_cache attrcache;
 	git_strmap *submodules;
 
@@ -94,15 +97,6 @@
 	git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
 };
 
-/* fully free the object; internal method, do not
- * export */
-void git_object__free(void *object);
-
-int git_object__resolve_to_type(git_object **obj, git_otype type);
-
-int git_oid__parse(git_oid *oid, const char **buffer_out, const char *buffer_end, const char *header);
-void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid);
-
 GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
 {
 	return &repo->attrcache;
@@ -119,10 +113,11 @@
  */
 int git_repository_config__weakptr(git_config **out, git_repository *repo);
 int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
+int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo);
 int git_repository_index__weakptr(git_index **out, git_repository *repo);
 
 /*
- * CVAR cache 
+ * CVAR cache
  *
  * Efficient access to the most used config variables of a repository.
  * The cache is cleared everytime the config backend is replaced.
@@ -134,5 +129,20 @@
  * Submodule cache
  */
 extern void git_submodule_config_free(git_repository *repo);
+
+GIT_INLINE(int) git_repository__ensure_not_bare(
+	git_repository *repo,
+	const char *operation_name)
+{
+	if (!git_repository_is_bare(repo))
+		return 0;
+
+	giterr_set(
+		GITERR_REPOSITORY,
+		"Cannot %s. This operation is not allowed against bare repositories.",
+		operation_name);
+
+	return GIT_EBAREREPO;
+}
 
 #endif
diff --git a/libgit2/src/reset.c b/libgit2/src/reset.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/reset.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "commit.h"
+#include "tag.h"
+#include "merge.h"
+#include "diff.h"
+#include "git2/reset.h"
+#include "git2/checkout.h"
+#include "git2/merge.h"
+#include "git2/refs.h"
+
+#define ERROR_MSG "Cannot perform reset"
+
+int git_reset_default(
+	git_repository *repo,
+	git_object *target,
+	git_strarray* pathspecs)
+{
+	git_object *commit = NULL;
+	git_tree *tree = NULL;
+	git_diff_list *diff = NULL;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	size_t i;
+	git_diff_delta *delta;
+	git_index_entry entry;
+	int error;
+	git_index *index = NULL;
+
+	assert(pathspecs != NULL && pathspecs->count > 0); 
+
+	memset(&entry, 0, sizeof(git_index_entry));
+
+	if ((error = git_repository_index(&index, repo)) < 0)
+		goto cleanup;
+
+	if (target) {
+		if (git_object_owner(target) != repo) {
+			giterr_set(GITERR_OBJECT,
+				"%s_default - The given target does not belong to this repository.", ERROR_MSG);
+			return -1;
+		}
+
+		if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
+			(error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
+			goto cleanup;
+	}
+
+	opts.pathspec = *pathspecs;
+	opts.flags = GIT_DIFF_REVERSE;
+
+	if ((error = git_diff_tree_to_index(
+		&diff, repo, tree, index, &opts)) < 0)
+			goto cleanup;
+
+	git_vector_foreach(&diff->deltas, i, delta) {
+		if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
+			goto cleanup;
+
+		assert(delta->status == GIT_DELTA_ADDED ||
+			delta->status == GIT_DELTA_MODIFIED ||
+			delta->status == GIT_DELTA_DELETED);
+
+		if (delta->status == GIT_DELTA_DELETED) {
+			if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
+				goto cleanup;
+		} else {
+			entry.mode = delta->new_file.mode;
+			git_oid_cpy(&entry.oid, &delta->new_file.oid);
+			entry.path = (char *)delta->new_file.path;
+
+			if ((error = git_index_add(index, &entry)) < 0)
+				goto cleanup;
+		}
+	}
+
+	error = git_index_write(index);
+
+cleanup:
+	git_object_free(commit);
+	git_tree_free(tree);
+	git_index_free(index);
+	git_diff_list_free(diff);
+
+	return error;
+}
+
+int git_reset(
+	git_repository *repo,
+	git_object *target,
+	git_reset_t reset_type)
+{
+	git_object *commit = NULL;
+	git_index *index = NULL;
+	git_tree *tree = NULL;
+	int error = 0;
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	assert(repo && target);
+
+	if (git_object_owner(target) != repo) {
+		giterr_set(GITERR_OBJECT,
+			"%s - The given target does not belong to this repository.", ERROR_MSG);
+		return -1;
+	}
+
+	if (reset_type != GIT_RESET_SOFT &&
+		(error = git_repository__ensure_not_bare(repo,
+			reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
+		return error;
+
+	if ((error = git_object_peel(&commit, target, GIT_OBJ_COMMIT)) < 0 ||
+		(error = git_repository_index(&index, repo)) < 0 ||
+		(error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
+		goto cleanup;
+
+	if (reset_type == GIT_RESET_SOFT &&
+		(git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
+		 git_index_has_conflicts(index)))
+	{
+		giterr_set(GITERR_OBJECT, "%s (soft) in the middle of a merge.", ERROR_MSG);
+		error = GIT_EUNMERGED;
+		goto cleanup;
+	}
+
+	/* move HEAD to the new target */
+	if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE,
+		git_object_id(commit))) < 0)
+		goto cleanup;
+
+	if (reset_type == GIT_RESET_HARD) {
+		/* overwrite working directory with HEAD */
+		opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+		if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
+			goto cleanup;
+	}
+
+	if (reset_type > GIT_RESET_SOFT) {
+		/* reset index to the target content */
+
+		if ((error = git_index_read_tree(index, tree)) < 0 ||
+			(error = git_index_write(index)) < 0)
+			goto cleanup;
+
+		if ((error = git_repository_merge_cleanup(repo)) < 0) {
+			giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	git_object_free(commit);
+	git_index_free(index);
+	git_tree_free(tree);
+
+	return error;
+}
diff --git a/libgit2/src/revparse.c b/libgit2/src/revparse.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/revparse.c
@@ -0,0 +1,912 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <assert.h>
+
+#include "common.h"
+#include "buffer.h"
+#include "tree.h"
+#include "refdb.h"
+
+#include "git2.h"
+
+static int disambiguate_refname(git_reference **out, git_repository *repo, const char *refname)
+{
+	int error, i;
+	bool fallbackmode = true;
+	git_reference *ref;
+	git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
+
+	static const char* formatters[] = {
+		"%s",
+		GIT_REFS_DIR "%s",
+		GIT_REFS_TAGS_DIR "%s",
+		GIT_REFS_HEADS_DIR "%s",
+		GIT_REFS_REMOTES_DIR "%s",
+		GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
+		NULL
+	};
+
+	if (*refname)
+		git_buf_puts(&name, refname);
+	else {
+		git_buf_puts(&name, GIT_HEAD_FILE);
+		fallbackmode = false;
+	}
+
+	for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
+
+		git_buf_clear(&refnamebuf);
+
+		if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
+			goto cleanup;
+
+		if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
+			error = GIT_EINVALIDSPEC;
+			continue;
+		}
+
+		error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);
+
+		if (!error) {
+			*out = ref;
+			error = 0;
+			goto cleanup;
+		}
+
+		if (error != GIT_ENOTFOUND)
+			goto cleanup;
+	}
+
+cleanup:
+	git_buf_free(&name);
+	git_buf_free(&refnamebuf);
+	return error;
+}
+
+static int maybe_sha_or_abbrev(git_object** out, git_repository *repo, const char *spec, size_t speclen)
+{
+	git_oid oid;
+
+	if (git_oid_fromstrn(&oid, spec, speclen) < 0)
+		return GIT_ENOTFOUND;
+
+	return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJ_ANY);
+}
+
+static int maybe_sha(git_object** out, git_repository *repo, const char *spec)
+{
+	size_t speclen = strlen(spec);
+
+	if (speclen != GIT_OID_HEXSZ)
+		return GIT_ENOTFOUND;
+
+	return maybe_sha_or_abbrev(out, repo, spec, speclen);
+}
+
+static int maybe_abbrev(git_object** out, git_repository *repo, const char *spec)
+{
+	size_t speclen = strlen(spec);
+
+	return maybe_sha_or_abbrev(out, repo, spec, speclen);
+}
+
+static int build_regex(regex_t *regex, const char *pattern)
+{
+	int error;
+
+	if (*pattern == '\0') {
+		giterr_set(GITERR_REGEX, "Empty pattern");
+		return GIT_EINVALIDSPEC;
+	}
+
+	error = regcomp(regex, pattern, REG_EXTENDED);
+	if (!error)
+		return 0;
+
+	error = giterr_set_regex(regex, error);
+
+	regfree(regex);
+
+	return error;
+}
+
+static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
+{
+	const char *substr;
+	int error;
+	regex_t regex;
+
+	substr = strstr(spec, "-g");
+
+	if (substr == NULL)
+		return GIT_ENOTFOUND;
+
+	if (build_regex(&regex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
+		return -1;
+
+	error = regexec(&regex, spec, 0, NULL, 0);
+	regfree(&regex);
+
+	if (error)
+		return GIT_ENOTFOUND;
+
+	return maybe_abbrev(out, repo, substr+2);
+}
+
+static int revparse_lookup_object(git_object **out, git_repository *repo, const char *spec)
+{
+	int error;
+	git_reference *ref;
+
+	error = maybe_sha(out, repo, spec);
+	if (!error)
+		return 0;
+
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return error;
+
+	error = disambiguate_refname(&ref, repo, spec);
+	if (!error) {
+		error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY);
+		git_reference_free(ref);
+		return error;
+	}
+
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return error;
+
+	error = maybe_abbrev(out, repo, spec);
+	if (!error)
+		return 0;
+
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return error;
+
+	error = maybe_describe(out, repo, spec);
+	if (!error)
+		return 0;
+
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return error;
+
+	giterr_set(GITERR_REFERENCE, "Refspec '%s' not found.", spec);
+	return GIT_ENOTFOUND;
+}
+
+static int try_parse_numeric(int *n, const char *curly_braces_content)
+{
+	int32_t content;
+	const char *end_ptr;
+
+	if (git__strtol32(&content, curly_braces_content, &end_ptr, 10) < 0)
+		return -1;
+
+	if (*end_ptr != '\0')
+		return -1;
+
+	*n = (int)content;
+	return 0;
+}
+
+static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
+{
+	git_reference *ref = NULL;
+	git_reflog *reflog = NULL;
+	regex_t preg;
+	int error = -1;
+	size_t i, numentries, cur;
+	const git_reflog_entry *entry;
+	const char *msg;
+	regmatch_t regexmatches[2];
+	git_buf buf = GIT_BUF_INIT;
+
+	cur = position;
+
+	if (*identifier != '\0' || *base_ref != NULL)
+		return GIT_EINVALIDSPEC;
+
+	if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0)
+		return -1;
+
+	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+		goto cleanup;
+
+	if (git_reflog_read(&reflog, ref) < 0)
+		goto cleanup;
+
+	numentries  = git_reflog_entrycount(reflog);
+
+	for (i = 0; i < numentries; i++) {
+		entry = git_reflog_entry_byindex(reflog, i);
+		msg = git_reflog_entry_message(entry);
+
+		if (regexec(&preg, msg, 2, regexmatches, 0))
+			continue;
+
+		cur--;
+
+		if (cur > 0)
+			continue;
+
+		git_buf_put(&buf, msg+regexmatches[1].rm_so, regexmatches[1].rm_eo - regexmatches[1].rm_so);
+
+		if ((error = disambiguate_refname(base_ref, repo, git_buf_cstr(&buf))) == 0)
+			goto cleanup;
+
+		if (error < 0 && error != GIT_ENOTFOUND)
+			goto cleanup;
+
+		error = maybe_abbrev(out, repo, git_buf_cstr(&buf));
+
+		goto cleanup;
+	}
+
+	error = GIT_ENOTFOUND;
+
+cleanup:
+	git_reference_free(ref);
+	git_buf_free(&buf);
+	regfree(&preg);
+	git_reflog_free(reflog);
+	return error;
+}
+
+static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
+{
+	git_reflog *reflog;
+	int error = -1;
+	size_t numentries;
+	const git_reflog_entry *entry;
+	bool search_by_pos = (identifier <= 100000000);
+
+	if (git_reflog_read(&reflog, ref) < 0)
+		return -1;
+
+	numentries = git_reflog_entrycount(reflog);
+
+	if (search_by_pos) {
+		if (numentries < identifier + 1) {
+			giterr_set(
+				GITERR_REFERENCE,
+				"Reflog for '%s' has only "PRIuZ" entries, asked for "PRIuZ,
+				git_reference_name(ref), numentries, identifier);
+
+			error = GIT_ENOTFOUND;
+			goto cleanup;
+		}
+
+		entry = git_reflog_entry_byindex(reflog, identifier);
+		git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+		error = 0;
+		goto cleanup;
+
+	} else {
+		size_t i;
+		git_time commit_time;
+
+		for (i = 0; i < numentries; i++) {
+			entry = git_reflog_entry_byindex(reflog, i);
+			commit_time = git_reflog_entry_committer(entry)->when;
+
+			if (commit_time.time > (git_time_t)identifier)
+				continue;
+
+			git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+			error = 0;
+			goto cleanup;
+		}
+
+		error = GIT_ENOTFOUND;
+	}
+
+cleanup:
+	git_reflog_free(reflog);
+	return error;
+}
+
+static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
+{
+	git_reference *ref;
+	git_oid oid;
+	int error = -1;
+
+	if (*base_ref == NULL) {
+		if ((error = disambiguate_refname(&ref, repo, identifier)) < 0)
+			return error;
+	} else {
+		ref = *base_ref;
+		*base_ref = NULL;
+	}
+
+	if (position == 0) {
+		error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJ_ANY);
+		goto cleanup;
+	}
+
+	if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0)
+		goto cleanup;
+
+	error = git_object_lookup(out, repo, &oid, GIT_OBJ_ANY);
+
+cleanup:
+	git_reference_free(ref);
+	return error;
+}
+
+static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo)
+{
+	git_reference *tracking, *ref;
+	int error = -1;
+
+	if (*base_ref == NULL) {
+		if ((error = disambiguate_refname(&ref, repo, identifier)) < 0)
+			return error;
+	} else {
+		ref = *base_ref;
+		*base_ref = NULL;
+	}
+
+	if (!git_reference_is_branch(ref)) {
+		error = GIT_EINVALIDSPEC;
+		goto cleanup;
+	}
+
+	if ((error = git_branch_upstream(&tracking, ref)) < 0)
+		goto cleanup;
+
+	*base_ref = tracking;
+
+cleanup:
+	git_reference_free(ref);
+	return error;
+}
+
+static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository* repo, const char *curly_braces_content)
+{
+	bool is_numeric;
+	int parsed = 0, error = -1;
+	git_buf identifier = GIT_BUF_INIT;
+	git_time_t timestamp;
+
+	assert(*out == NULL);
+
+	if (git_buf_put(&identifier, spec, identifier_len) < 0)
+		return -1;
+
+	is_numeric = !try_parse_numeric(&parsed, curly_braces_content);
+
+	if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) {
+		error = GIT_EINVALIDSPEC;
+		goto cleanup;
+	}
+
+	if (is_numeric) {
+		if (parsed < 0)
+			error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_buf_cstr(&identifier), -parsed);
+		else
+			error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), parsed);
+
+		goto cleanup;
+	}
+
+	if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) {
+		error = retrieve_remote_tracking_reference(ref, git_buf_cstr(&identifier), repo);
+
+		goto cleanup;
+	}
+
+	if (git__date_parse(&timestamp, curly_braces_content) < 0)
+		goto cleanup;
+
+	error = retrieve_revobject_from_reflog(out, ref, repo, git_buf_cstr(&identifier), (size_t)timestamp);
+
+cleanup:
+	git_buf_free(&identifier);
+	return error;
+}
+
+static git_otype parse_obj_type(const char *str)
+{
+	if (!strcmp(str, "commit"))
+		return GIT_OBJ_COMMIT;
+
+	if (!strcmp(str, "tree"))
+		return GIT_OBJ_TREE;
+
+	if (!strcmp(str, "blob"))
+		return GIT_OBJ_BLOB;
+
+	if (!strcmp(str, "tag"))
+		return GIT_OBJ_TAG;
+
+	return GIT_OBJ_BAD;
+}
+
+static int dereference_to_non_tag(git_object **out, git_object *obj)
+{
+	if (git_object_type(obj) == GIT_OBJ_TAG)
+		return git_tag_peel(out, (git_tag *)obj);
+
+	return git_object_dup(out, obj);
+}
+
+static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n)
+{
+	git_object *temp_commit = NULL;
+	int error;
+
+	if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0)
+		return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
+			GIT_EINVALIDSPEC : error;
+
+	if (n == 0) {
+		*out = temp_commit;
+		return 0;
+	}
+
+	error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1);
+
+	git_object_free(temp_commit);
+	return error;
+}
+
+static int handle_linear_syntax(git_object **out, git_object *obj, int n)
+{
+	git_object *temp_commit = NULL;
+	int error;
+
+	if ((error = git_object_peel(&temp_commit, obj, GIT_OBJ_COMMIT)) < 0)
+		return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
+			GIT_EINVALIDSPEC : error;
+
+	error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n);
+
+	git_object_free(temp_commit);
+	return error;
+}
+
+static int handle_colon_syntax(
+	git_object **out,
+	git_object *obj,
+	const char *path)
+{
+	git_object *tree;
+	int error = -1;
+	git_tree_entry *entry = NULL;
+
+	if ((error = git_object_peel(&tree, obj, GIT_OBJ_TREE)) < 0)
+		return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error;
+
+	if (*path == '\0') {
+		*out = tree;
+		return 0;
+	}
+
+	/*
+	 * TODO: Handle the relative path syntax
+	 * (:./relative/path and :../relative/path)
+	 */
+	if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0)
+		goto cleanup;
+
+	error = git_tree_entry_to_object(out, git_object_owner(tree), entry);
+
+cleanup:
+	git_tree_entry_free(entry);
+	git_object_free(tree);
+
+	return error;
+}
+
+static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
+{
+	int error;
+	git_oid oid;
+	git_object *obj;
+
+	while (!(error = git_revwalk_next(&oid, walk))) {
+
+		error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJ_COMMIT);
+		if ((error < 0) && (error != GIT_ENOTFOUND))
+			return -1;
+
+		if (!regexec(regex, git_commit_message((git_commit*)obj), 0, NULL, 0)) {
+			*out = obj;
+			return 0;
+		}
+
+		git_object_free(obj);
+	}
+
+	if (error < 0 && error == GIT_ITEROVER)
+		error = GIT_ENOTFOUND;
+
+	return error;
+}
+
+static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern)
+{
+	regex_t preg;
+	git_revwalk *walk = NULL;
+	int error;
+
+	if ((error = build_regex(&preg, pattern)) < 0)
+		return error;
+
+	if ((error = git_revwalk_new(&walk, repo)) < 0)
+		goto cleanup;
+
+	git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+	if (spec_oid == NULL) {
+		// TODO: @carlosmn: The glob should be refs/* but this makes git_revwalk_next() fails
+		if ((error = git_revwalk_push_glob(walk, GIT_REFS_HEADS_DIR "*")) < 0)
+			goto cleanup;
+	} else if ((error = git_revwalk_push(walk, spec_oid)) < 0)
+			goto cleanup;
+
+	error = walk_and_search(out, walk, &preg);
+
+cleanup:
+	regfree(&preg);
+	git_revwalk_free(walk);
+
+	return error;
+}
+
+static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content)
+{
+	git_otype expected_type;
+
+	if (*curly_braces_content == '\0')
+		return dereference_to_non_tag(out, obj);
+
+	if (*curly_braces_content == '/')
+		return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1);
+
+	expected_type = parse_obj_type(curly_braces_content);
+
+	if (expected_type == GIT_OBJ_BAD)
+		return GIT_EINVALIDSPEC;
+
+	return git_object_peel(out, obj, expected_type);
+}
+
+static int extract_curly_braces_content(git_buf *buf, const char *spec, size_t *pos)
+{
+	git_buf_clear(buf);
+
+	assert(spec[*pos] == '^' || spec[*pos] == '@');
+
+	(*pos)++;
+
+	if (spec[*pos] == '\0' || spec[*pos] != '{')
+		return GIT_EINVALIDSPEC;
+
+	(*pos)++;
+
+	while (spec[*pos] != '}') {
+		if (spec[*pos] == '\0')
+			return GIT_EINVALIDSPEC;
+
+		git_buf_putc(buf, spec[(*pos)++]);
+	}
+
+	(*pos)++;
+
+	return 0;
+}
+
+static int extract_path(git_buf *buf, const char *spec, size_t *pos)
+{
+	git_buf_clear(buf);
+
+	assert(spec[*pos] == ':');
+
+	(*pos)++;
+
+	if (git_buf_puts(buf, spec + *pos) < 0)
+		return -1;
+
+	*pos += git_buf_len(buf);
+
+	return 0;
+}
+
+static int extract_how_many(int *n, const char *spec, size_t *pos)
+{
+	const char *end_ptr;
+	int parsed, accumulated;
+	char kind = spec[*pos];
+
+	assert(spec[*pos] == '^' || spec[*pos] == '~');
+
+	accumulated = 0;
+
+	do {
+		do {
+			(*pos)++;
+			accumulated++;
+		} while (spec[(*pos)] == kind && kind == '~');
+
+		if (git__isdigit(spec[*pos])) {
+			if (git__strtol32(&parsed, spec + *pos, &end_ptr, 10) < 0)
+				return GIT_EINVALIDSPEC;
+
+			accumulated += (parsed - 1);
+			*pos = end_ptr - spec;
+		}
+
+	} 	while (spec[(*pos)] == kind && kind == '~');
+
+	*n = accumulated;
+
+	return 0;
+}
+
+static int object_from_reference(git_object **object, git_reference *reference)
+{
+	git_reference *resolved = NULL;
+	int error;
+
+	if (git_reference_resolve(&resolved, reference) < 0)
+		return -1;
+
+	error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJ_ANY);
+	git_reference_free(resolved);
+
+	return error;
+}
+
+static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier)
+{
+	int error;
+	git_buf identifier = GIT_BUF_INIT;
+
+	if (*object != NULL)
+		return 0;
+
+	if (*reference != NULL) {
+		if ((error = object_from_reference(object, *reference)) < 0)
+			return error;
+
+		git_reference_free(*reference);
+		*reference = NULL;
+		return 0;
+	}
+
+	if (!allow_empty_identifier && identifier_len == 0)
+		return GIT_EINVALIDSPEC;
+
+	if (git_buf_put(&identifier, spec, identifier_len) < 0)
+		return -1;
+
+	error = revparse_lookup_object(object, repo, git_buf_cstr(&identifier));
+	git_buf_free(&identifier);
+
+	return error;
+}
+
+static int ensure_base_rev_is_not_known_yet(git_object *object)
+{
+	if (object == NULL)
+		return 0;
+
+	return GIT_EINVALIDSPEC;
+}
+
+static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len)
+{
+	if (object != NULL)
+		return true;
+
+	if (reference != NULL)
+		return true;
+
+	if (identifier_len > 0)
+		return true;
+
+	return false;
+}
+
+static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference)
+{
+	if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL)
+		return 0;
+
+	return GIT_EINVALIDSPEC;
+}
+
+int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
+{
+	size_t pos = 0, identifier_len = 0;
+	int error = -1, n;
+	git_buf buf = GIT_BUF_INIT;
+
+	git_reference *reference = NULL;
+	git_object *base_rev = NULL;
+
+	assert(out && repo && spec);
+
+	*out = NULL;
+
+	while (spec[pos]) {
+		switch (spec[pos]) {
+		case '^':
+			if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+				goto cleanup;
+
+			if (spec[pos+1] == '{') {
+				git_object *temp_object = NULL;
+
+				if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
+					goto cleanup;
+
+				if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
+					goto cleanup;
+
+				git_object_free(base_rev);
+				base_rev = temp_object;
+			} else {
+				git_object *temp_object = NULL;
+
+				if ((error = extract_how_many(&n, spec, &pos)) < 0)
+					goto cleanup;
+
+				if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0)
+					goto cleanup;
+
+				git_object_free(base_rev);
+				base_rev = temp_object;
+			}
+			break;
+
+		case '~':
+		{
+			git_object *temp_object = NULL;
+
+			if ((error = extract_how_many(&n, spec, &pos)) < 0)
+				goto cleanup;
+
+			if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+				goto cleanup;
+
+			if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0)
+				goto cleanup;
+
+			git_object_free(base_rev);
+			base_rev = temp_object;
+			break;
+		}
+
+		case ':':
+		{
+			git_object *temp_object = NULL;
+
+			if ((error = extract_path(&buf, spec, &pos)) < 0)
+				goto cleanup;
+
+			if (any_left_hand_identifier(base_rev, reference, identifier_len)) {
+				if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0)
+					goto cleanup;
+
+				if ((error = handle_colon_syntax(&temp_object, base_rev, git_buf_cstr(&buf))) < 0)
+					goto cleanup;
+			} else {
+				if (*git_buf_cstr(&buf) == '/') {
+					if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_buf_cstr(&buf) + 1)) < 0)
+						goto cleanup;
+				} else {
+
+					/*
+					 * TODO: support merge-stage path lookup (":2:Makefile")
+					 * and plain index blob lookup (:i-am/a/blob)
+					 */
+					giterr_set(GITERR_INVALID, "Unimplemented");
+					error = GIT_ERROR;
+					goto cleanup;
+				}
+			}
+
+			git_object_free(base_rev);
+			base_rev = temp_object;
+			break;
+		}
+
+		case '@':
+		{
+			if (spec[pos+1] == '{') {
+				git_object *temp_object = NULL;
+
+				if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
+					goto cleanup;
+
+				if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0)
+					goto cleanup;
+
+				if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_buf_cstr(&buf))) < 0)
+					goto cleanup;
+
+				if (temp_object != NULL)
+					base_rev = temp_object;
+				break;
+			} else {
+				/* Fall through */
+			}
+		}
+
+		default:
+			if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
+				goto cleanup;
+
+			pos++;
+			identifier_len++;
+		}
+	}
+
+	if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+		goto cleanup;
+
+	*out = base_rev;
+	error = 0;
+
+cleanup:
+	if (error) {
+		if (error == GIT_EINVALIDSPEC)
+			giterr_set(GITERR_INVALID,
+				"Failed to parse revision specifier - Invalid pattern '%s'", spec);
+
+		git_object_free(base_rev);
+	}
+	git_reference_free(reference);
+	git_buf_free(&buf);
+	return error;
+}
+
+
+int git_revparse(
+	git_revspec *revspec,
+	git_repository *repo,
+	const char *spec)
+{
+	const char *dotdot;
+	int error = 0;
+
+	assert(revspec && repo && spec);
+
+	memset(revspec, 0x0, sizeof(*revspec));
+
+	if ((dotdot = strstr(spec, "..")) != NULL) {
+		char *lstr;
+		const char *rstr;
+		revspec->flags = GIT_REVPARSE_RANGE;
+
+		lstr = git__substrdup(spec, dotdot - spec);
+		rstr = dotdot + 2;
+		if (dotdot[2] == '.') {
+			revspec->flags |= GIT_REVPARSE_MERGE_BASE;
+			rstr++;
+		}
+
+		if ((error = git_revparse_single(&revspec->from, repo, lstr)) < 0) {
+			return error;
+		}
+
+		if ((error = git_revparse_single(&revspec->to, repo, rstr)) < 0) {
+			return error;
+		}
+
+		git__free((void*)lstr);
+	} else {
+		revspec->flags = GIT_REVPARSE_SINGLE;
+		error = git_revparse_single(&revspec->from, repo, spec);
+	}
+
+	return error;
+}
+
diff --git a/libgit2/src/revwalk.c b/libgit2/src/revwalk.c
--- a/libgit2/src/revwalk.c
+++ b/libgit2/src/revwalk.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,146 +8,18 @@
 #include "common.h"
 #include "commit.h"
 #include "odb.h"
-#include "pqueue.h"
 #include "pool.h"
-#include "oidmap.h"
 
-#include "git2/revwalk.h"
-#include "git2/merge.h"
+#include "revwalk.h"
+#include "git2/revparse.h"
+#include "merge.h"
 
 #include <regex.h>
 
-GIT__USE_OIDMAP;
-
-#define PARENT1  (1 << 0)
-#define PARENT2  (1 << 1)
-#define RESULT   (1 << 2)
-#define STALE    (1 << 3)
-
-typedef struct commit_object {
-	git_oid oid;
-	uint32_t time;
-	unsigned int seen:1,
-			 uninteresting:1,
-			 topo_delay:1,
-			 parsed:1,
-			 flags : 4;
-
-	unsigned short in_degree;
-	unsigned short out_degree;
-
-	struct commit_object **parents;
-} commit_object;
-
-typedef struct commit_list {
-	commit_object *item;
-	struct commit_list *next;
-} commit_list;
-
-struct git_revwalk {
-	git_repository *repo;
-	git_odb *odb;
-
-	git_oidmap *commits;
-	git_pool commit_pool;
-
-	commit_list *iterator_topo;
-	commit_list *iterator_rand;
-	commit_list *iterator_reverse;
-	git_pqueue iterator_time;
-
-	int (*get_next)(commit_object **, git_revwalk *);
-	int (*enqueue)(git_revwalk *, commit_object *);
-
-	unsigned walking:1;
-	unsigned int sorting;
-
-	/* merge base calculation */
-	commit_object *one;
-	git_vector twos;
-};
-
-static int commit_time_cmp(void *a, void *b)
-{
-	commit_object *commit_a = (commit_object *)a;
-	commit_object *commit_b = (commit_object *)b;
-
-	return (commit_a->time < commit_b->time);
-}
-
-static commit_list *commit_list_insert(commit_object *item, commit_list **list_p)
-{
-	commit_list *new_list = git__malloc(sizeof(commit_list));
-	if (new_list != NULL) {
-		new_list->item = item;
-		new_list->next = *list_p;
-	}
-	*list_p = new_list;
-	return new_list;
-}
-
-static commit_list *commit_list_insert_by_date(commit_object *item, commit_list **list_p)
-{
-	commit_list **pp = list_p;
-	commit_list *p;
-
-	while ((p = *pp) != NULL) {
-		if (commit_time_cmp(p->item, item) < 0)
-			break;
-
-		pp = &p->next;
-	}
-
-	return commit_list_insert(item, pp);
-}
-static void commit_list_free(commit_list **list_p)
-{
-	commit_list *list = *list_p;
-
-	while (list) {
-		commit_list *temp = list;
-		list = temp->next;
-		git__free(temp);
-	}
-
-	*list_p = NULL;
-}
-
-static commit_object *commit_list_pop(commit_list **stack)
-{
-	commit_list *top = *stack;
-	commit_object *item = top ? top->item : NULL;
-
-	if (top) {
-		*stack = top->next;
-		git__free(top);
-	}
-	return item;
-}
-
-#define PARENTS_PER_COMMIT	2
-#define COMMIT_ALLOC \
-	(sizeof(commit_object) + PARENTS_PER_COMMIT * sizeof(commit_object *))
-
-static commit_object *alloc_commit(git_revwalk *walk)
-{
-	return (commit_object *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
-}
-
-static commit_object **alloc_parents(
-	git_revwalk *walk, commit_object *commit, size_t n_parents)
-{
-	if (n_parents <= PARENTS_PER_COMMIT)
-		return (commit_object **)((char *)commit + sizeof(commit_object));
-
-	return (commit_object **)git_pool_malloc(
-		&walk->commit_pool, (uint32_t)(n_parents * sizeof(commit_object *)));
-}
-
-
-static commit_object *commit_lookup(git_revwalk *walk, const git_oid *oid)
+git_commit_list_node *git_revwalk__commit_lookup(
+	git_revwalk *walk, const git_oid *oid)
 {
-	commit_object *commit;
+	git_commit_list_node *commit;
 	khiter_t pos;
 	int ret;
 
@@ -156,7 +28,7 @@
 	if (pos != kh_end(walk->commits))
 		return kh_value(walk->commits, pos);
 
-	commit = alloc_commit(walk);
+	commit = git_commit_list_alloc_node(walk);
 	if (commit == NULL)
 		return NULL;
 
@@ -169,227 +41,7 @@
 	return commit;
 }
 
-static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawobj *raw)
-{
-	const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
-
-	unsigned char *buffer = raw->data;
-	unsigned char *buffer_end = buffer + raw->len;
-	unsigned char *parents_start;
-
-	int i, parents = 0;
-	int commit_time;
-
-	buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
-
-	parents_start = buffer;
-	while (buffer + parent_len < buffer_end && memcmp(buffer, "parent ", strlen("parent ")) == 0) {
-		parents++;
-		buffer += parent_len;
-	}
-
-	commit->parents = alloc_parents(walk, commit, parents);
-	GITERR_CHECK_ALLOC(commit->parents);
-
-	buffer = parents_start;
-	for (i = 0; i < parents; ++i) {
-		git_oid oid;
-
-		if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
-			return -1;
-
-		commit->parents[i] = commit_lookup(walk, &oid);
-		if (commit->parents[i] == NULL)
-			return -1;
-
-		buffer += parent_len;
-	}
-
-	commit->out_degree = (unsigned short)parents;
-
-	if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) {
-		giterr_set(GITERR_ODB, "Failed to parse commit. Object is corrupted");
-		return -1;
-	}
-
-	buffer = memchr(buffer, '>', buffer_end - buffer);
-	if (buffer == NULL) {
-		giterr_set(GITERR_ODB, "Failed to parse commit. Can't find author");
-		return -1;
-	}
-
-	if (git__strtol32(&commit_time, (char *)buffer + 2, NULL, 10) < 0) {
-		giterr_set(GITERR_ODB, "Failed to parse commit. Can't parse commit time");
-		return -1;
-	}
-
-	commit->time = (time_t)commit_time;
-	commit->parsed = 1;
-	return 0;
-}
-
-static int commit_parse(git_revwalk *walk, commit_object *commit)
-{
-	git_odb_object *obj;
-	int error;
-
-	if (commit->parsed)
-		return 0;
-
-	if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
-		return error;
-
-	if (obj->raw.type != GIT_OBJ_COMMIT) {
-		git_odb_object_free(obj);
-		giterr_set(GITERR_INVALID, "Failed to parse commit. Object is no commit object");
-		return -1;
-	}
-
-	error = commit_quick_parse(walk, commit, &obj->raw);
-	git_odb_object_free(obj);
-	return error;
-}
-
-static int interesting(git_pqueue *list)
-{
-	unsigned int i;
-	for (i = 1; i < git_pqueue_size(list); i++) {
-		commit_object *commit = list->d[i];
-		if ((commit->flags & STALE) == 0)
-			return 1;
-	}
-
-	return 0;
-}
-
-static int merge_bases_many(commit_list **out, git_revwalk *walk, commit_object *one, git_vector *twos)
-{
-	int error;
-	unsigned int i;
-	commit_object *two;
-	commit_list *result = NULL, *tmp = NULL;
-	git_pqueue list;
-
-	/* if the commit is repeated, we have a our merge base already */
-	git_vector_foreach(twos, i, two) {
-		if (one == two)
-			return commit_list_insert(one, out) ? 0 : -1;
-	}
-
-	if (git_pqueue_init(&list, twos->length * 2, commit_time_cmp) < 0)
-		return -1;
-
-	if (commit_parse(walk, one) < 0)
-	    return -1;
-
-	one->flags |= PARENT1;
-	if (git_pqueue_insert(&list, one) < 0)
-		return -1;
-
-	git_vector_foreach(twos, i, two) {
-		commit_parse(walk, two);
-		two->flags |= PARENT2;
-		if (git_pqueue_insert(&list, two) < 0)
-			return -1;
-	}
-
-	/* as long as there are non-STALE commits */
-	while (interesting(&list)) {
-		commit_object *commit;
-		int flags;
-
-		commit = git_pqueue_pop(&list);
-
-		flags = commit->flags & (PARENT1 | PARENT2 | STALE);
-		if (flags == (PARENT1 | PARENT2)) {
-			if (!(commit->flags & RESULT)) {
-				commit->flags |= RESULT;
-				if (commit_list_insert(commit, &result) == NULL)
-					return -1;
-			}
-			/* we mark the parents of a merge stale */
-			flags |= STALE;
-		}
-
-		for (i = 0; i < commit->out_degree; i++) {
-			commit_object *p = commit->parents[i];
-			if ((p->flags & flags) == flags)
-				continue;
-
-			if ((error = commit_parse(walk, p)) < 0)
-				return error;
-
-			p->flags |= flags;
-			if (git_pqueue_insert(&list, p) < 0)
-				return -1;
-		}
-	}
-
-	git_pqueue_free(&list);
-
-	/* filter out any stale commits in the results */
-	tmp = result;
-	result = NULL;
-
-	while (tmp) {
-		struct commit_list *next = tmp->next;
-		if (!(tmp->item->flags & STALE))
-			if (commit_list_insert_by_date(tmp->item, &result) == NULL)
-				return -1;
-
-		git__free(tmp);
-		tmp = next;
-	}
-
-	*out = result;
-	return 0;
-}
-
-int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two)
-{
-	git_revwalk *walk;
-	git_vector list;
-	commit_list *result = NULL;
-	commit_object *commit;
-	void *contents[1];
-
-	if (git_revwalk_new(&walk, repo) < 0)
-		return -1;
-
-	commit = commit_lookup(walk, two);
-	if (commit == NULL)
-		goto on_error;
-
-	/* This is just one value, so we can do it on the stack */
-	memset(&list, 0x0, sizeof(git_vector));
-	contents[0] = commit;
-	list.length = 1;
-	list.contents = contents;
-
-	commit = commit_lookup(walk, one);
-	if (commit == NULL)
-		goto on_error;
-
-	if (merge_bases_many(&result, walk, commit, &list) < 0)
-		goto on_error;
-
-	if (!result) {
-		git_revwalk_free(walk);
-		return GIT_ENOTFOUND;
-	}
-
-	git_oid_cpy(out, &result->item->oid);
-	commit_list_free(&result);
-	git_revwalk_free(walk);
-
-	return 0;
-
-on_error:
-	git_revwalk_free(walk);
-	return -1;
-}
-
-static void mark_uninteresting(commit_object *commit)
+static void mark_uninteresting(git_commit_list_node *commit)
 {
 	unsigned short i;
 	assert(commit);
@@ -405,7 +57,7 @@
 			mark_uninteresting(commit->parents[i]);
 }
 
-static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
+static int process_commit(git_revwalk *walk, git_commit_list_node *commit, int hide)
 {
 	int error;
 
@@ -417,13 +69,13 @@
 
 	commit->seen = 1;
 
-	if ((error = commit_parse(walk, commit)) < 0)
+	if ((error = git_commit_list_parse(walk, commit)) < 0)
 		return error;
 
 	return walk->enqueue(walk, commit);
 }
 
-static int process_commit_parents(git_revwalk *walk, commit_object *commit)
+static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit)
 {
 	unsigned short i;
 	int error = 0;
@@ -436,9 +88,22 @@
 
 static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
 {
-	commit_object *commit;
+	git_object *obj;
+	git_otype type;
+	git_commit_list_node *commit;
 
-	commit = commit_lookup(walk, oid);
+	if (git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY) < 0)
+		return -1;
+
+	type = git_object_type(obj);
+	git_object_free(obj);
+
+	if (type != GIT_OBJ_COMMIT) {
+		giterr_set(GITERR_INVALID, "Object is no commit object");
+		return -1;
+	}
+
+	commit = git_revwalk__commit_lookup(walk, oid);
 	if (commit == NULL)
 		return -1; /* error already reported by failed lookup */
 
@@ -470,7 +135,7 @@
 {
 	git_oid oid;
 
-	if (git_reference_name_to_oid(&oid, walk->repo, refname) < 0)
+	if (git_reference_name_to_id(&oid, walk->repo, refname) < 0)
 		return -1;
 
 	return push_commit(walk, &oid, hide);
@@ -478,7 +143,6 @@
 
 struct push_cb_data {
 	git_revwalk *walk;
-	const char *glob;
 	int hide;
 };
 
@@ -486,10 +150,7 @@
 {
 	struct push_cb_data *data = (struct push_cb_data *)data_;
 
-	if (!p_fnmatch(data->glob, refname, 0))
-		return push_ref(data->walk, refname, data->hide);
-
-	return 0;
+	return push_ref(data->walk, refname, data->hide);
 }
 
 static int push_glob(git_revwalk *walk, const char *glob, int hide)
@@ -522,11 +183,10 @@
 		goto on_error;
 
 	data.walk = walk;
-	data.glob = git_buf_cstr(&buf);
 	data.hide = hide;
 
-	if (git_reference_foreach(
-			walk->repo, GIT_REF_LISTALL, push_glob_cb, &data) < 0)
+	if (git_reference_foreach_glob(
+		walk->repo, git_buf_cstr(&buf), GIT_REF_LISTALL, push_glob_cb, &data) < 0)
 		goto on_error;
 
 	regfree(&preg);
@@ -569,26 +229,51 @@
 	return push_ref(walk, refname, 0);
 }
 
+int git_revwalk_push_range(git_revwalk *walk, const char *range)
+{
+	git_revspec revspec;
+	int error = 0;
+
+	if ((error = git_revparse(&revspec, walk->repo, range)))
+		return error;
+
+	if (revspec.flags & GIT_REVPARSE_MERGE_BASE) {
+		/* TODO: support "<commit>...<commit>" */
+		giterr_set(GITERR_INVALID, "Symmetric differences not implemented in revwalk");
+		return GIT_EINVALIDSPEC;
+	}
+
+	if ((error = push_commit(walk, git_object_id(revspec.from), 1)))
+		goto out;
+
+	error = push_commit(walk, git_object_id(revspec.to), 0);
+
+out:
+	git_object_free(revspec.from);
+	git_object_free(revspec.to);
+	return error;
+}
+
 int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
 {
 	assert(walk && refname);
 	return push_ref(walk, refname, 1);
 }
 
-static int revwalk_enqueue_timesort(git_revwalk *walk, commit_object *commit)
+static int revwalk_enqueue_timesort(git_revwalk *walk, git_commit_list_node *commit)
 {
 	return git_pqueue_insert(&walk->iterator_time, commit);
 }
 
-static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit)
+static int revwalk_enqueue_unsorted(git_revwalk *walk, git_commit_list_node *commit)
 {
-	return commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
+	return git_commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
 }
 
-static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
+static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk *walk)
 {
 	int error;
-	commit_object *next;
+	git_commit_list_node *next;
 
 	while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
 		if ((error = process_commit_parents(walk, next)) < 0)
@@ -600,15 +285,16 @@
 		}
 	}
 
-	return GIT_REVWALKOVER;
+	giterr_clear();
+	return GIT_ITEROVER;
 }
 
-static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
+static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk *walk)
 {
 	int error;
-	commit_object *next;
+	git_commit_list_node *next;
 
-	while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) {
+	while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) {
 		if ((error = process_commit_parents(walk, next)) < 0)
 			return error;
 
@@ -618,18 +304,21 @@
 		}
 	}
 
-	return GIT_REVWALKOVER;
+	giterr_clear();
+	return GIT_ITEROVER;
 }
 
-static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
+static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
 {
-	commit_object *next;
+	git_commit_list_node *next;
 	unsigned short i;
 
 	for (;;) {
-		next = commit_list_pop(&walk->iterator_topo);
-		if (next == NULL)
-			return GIT_REVWALKOVER;
+		next = git_commit_list_pop(&walk->iterator_topo);
+		if (next == NULL) {
+			giterr_clear();
+			return GIT_ITEROVER;
+		}
 
 		if (next->in_degree > 0) {
 			next->topo_delay = 1;
@@ -637,11 +326,11 @@
 		}
 
 		for (i = 0; i < next->out_degree; ++i) {
-			commit_object *parent = next->parents[i];
+			git_commit_list_node *parent = next->parents[i];
 
 			if (--parent->in_degree == 0 && parent->topo_delay) {
 				parent->topo_delay = 0;
-				if (commit_list_insert(parent, &walk->iterator_topo) == NULL)
+				if (git_commit_list_insert(parent, &walk->iterator_topo) == NULL)
 					return -1;
 			}
 		}
@@ -651,10 +340,10 @@
 	}
 }
 
-static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)
+static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk)
 {
-	*object_out = commit_list_pop(&walk->iterator_reverse);
-	return *object_out ? 0 : GIT_REVWALKOVER;
+	*object_out = git_commit_list_pop(&walk->iterator_reverse);
+	return *object_out ? 0 : GIT_ITEROVER;
 }
 
 
@@ -662,21 +351,23 @@
 {
 	int error;
 	unsigned int i;
-	commit_object *next, *two;
-	commit_list *bases = NULL;
+	git_commit_list_node *next, *two;
+	git_commit_list *bases = NULL;
 
 	/*
 	 * If walk->one is NULL, there were no positive references,
 	 * so we know that the walk is already over.
 	 */
-	if (walk->one == NULL)
-		return GIT_REVWALKOVER;
+	if (walk->one == NULL) {
+		giterr_clear();
+		return GIT_ITEROVER;
+	}
 
 	/* first figure out what the merge bases are */
-	if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
+	if (git_merge__bases_many(&bases, walk, walk->one, &walk->twos) < 0)
 		return -1;
 
-	commit_list_free(&bases);
+	git_commit_list_free(&bases);
 	if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)
 		return -1;
 
@@ -690,15 +381,15 @@
 
 		while ((error = walk->get_next(&next, walk)) == 0) {
 			for (i = 0; i < next->out_degree; ++i) {
-				commit_object *parent = next->parents[i];
+				git_commit_list_node *parent = next->parents[i];
 				parent->in_degree++;
 			}
 
-			if (commit_list_insert(next, &walk->iterator_topo) == NULL)
+			if (git_commit_list_insert(next, &walk->iterator_topo) == NULL)
 				return -1;
 		}
 
-		if (error != GIT_REVWALKOVER)
+		if (error != GIT_ITEROVER)
 			return error;
 
 		walk->get_next = &revwalk_next_toposort;
@@ -707,10 +398,10 @@
 	if (walk->sorting & GIT_SORT_REVERSE) {
 
 		while ((error = walk->get_next(&next, walk)) == 0)
-			if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
+			if (git_commit_list_insert(next, &walk->iterator_reverse) == NULL)
 				return -1;
 
-		if (error != GIT_REVWALKOVER)
+		if (error != GIT_ITEROVER)
 			return error;
 
 		walk->get_next = &revwalk_next_reverse;
@@ -721,9 +412,6 @@
 }
 
 
-
-
-
 int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
 {
 	git_revwalk *walk;
@@ -736,7 +424,7 @@
 	walk->commits = git_oidmap_alloc();
 	GITERR_CHECK_ALLOC(walk->commits);
 
-	if (git_pqueue_init(&walk->iterator_time, 8, commit_time_cmp) < 0 ||
+	if (git_pqueue_init(&walk->iterator_time, 8, git_commit_list_time_cmp) < 0 ||
 		git_vector_init(&walk->twos, 4, NULL) < 0 ||
 		git_pool_init(&walk->commit_pool, 1,
 			git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
@@ -798,7 +486,7 @@
 int git_revwalk_next(git_oid *oid, git_revwalk *walk)
 {
 	int error;
-	commit_object *next;
+	git_commit_list_node *next;
 
 	assert(walk && oid);
 
@@ -809,9 +497,10 @@
 
 	error = walk->get_next(&next, walk);
 
-	if (error == GIT_REVWALKOVER) {
+	if (error == GIT_ITEROVER) {
 		git_revwalk_reset(walk);
-		return GIT_REVWALKOVER;
+		giterr_clear();
+		return GIT_ITEROVER;
 	}
 
 	if (!error)
@@ -822,7 +511,7 @@
 
 void git_revwalk_reset(git_revwalk *walk)
 {
-	commit_object *commit;
+	git_commit_list_node *commit;
 
 	assert(walk);
 
@@ -834,9 +523,9 @@
 		});
 
 	git_pqueue_clear(&walk->iterator_time);
-	commit_list_free(&walk->iterator_topo);
-	commit_list_free(&walk->iterator_rand);
-	commit_list_free(&walk->iterator_reverse);
+	git_commit_list_free(&walk->iterator_topo);
+	git_commit_list_free(&walk->iterator_rand);
+	git_commit_list_free(&walk->iterator_reverse);
 	walk->walking = 0;
 
 	walk->one = NULL;
diff --git a/libgit2/src/revwalk.h b/libgit2/src/revwalk.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/revwalk.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_revwalk_h__
+#define INCLUDE_revwalk_h__
+
+#include "git2/revwalk.h"
+#include "oidmap.h"
+#include "commit_list.h"
+#include "pqueue.h"
+#include "pool.h"
+#include "vector.h"
+
+GIT__USE_OIDMAP;
+
+struct git_revwalk {
+	git_repository *repo;
+	git_odb *odb;
+
+	git_oidmap *commits;
+	git_pool commit_pool;
+
+	git_commit_list *iterator_topo;
+	git_commit_list *iterator_rand;
+	git_commit_list *iterator_reverse;
+	git_pqueue iterator_time;
+
+	int (*get_next)(git_commit_list_node **, git_revwalk *);
+	int (*enqueue)(git_revwalk *, git_commit_list_node *);
+
+	unsigned walking:1;
+	unsigned int sorting;
+
+	/* merge base calculation */
+	git_commit_list_node *one;
+	git_vector twos;
+};
+
+git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid);
+
+#endif
diff --git a/libgit2/src/sha1.c b/libgit2/src/sha1.c
deleted file mode 100644
--- a/libgit2/src/sha1.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "sha1.h"
-
-#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
-
-/*
- * Force usage of rol or ror by selecting the one with the smaller constant.
- * It _can_ generate slightly smaller code (a constant of 1 is special), but
- * perhaps more importantly it's possibly faster on any uarch that does a
- * rotate with a loop.
- */
-
-#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
-#define SHA_ROL(x,n)	SHA_ASM("rol", x, n)
-#define SHA_ROR(x,n)	SHA_ASM("ror", x, n)
-
-#else
-
-#define SHA_ROT(X,l,r)	(((X) << (l)) | ((X) >> (r)))
-#define SHA_ROL(X,n)	SHA_ROT(X,n,32-(n))
-#define SHA_ROR(X,n)	SHA_ROT(X,32-(n),n)
-
-#endif
-
-/*
- * If you have 32 registers or more, the compiler can (and should)
- * try to change the array[] accesses into registers. However, on
- * machines with less than ~25 registers, that won't really work,
- * and at least gcc will make an unholy mess of it.
- *
- * So to avoid that mess which just slows things down, we force
- * the stores to memory to actually happen (we might be better off
- * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
- * suggested by Artur Skawina - that will also make gcc unable to
- * try to do the silly "optimize away loads" part because it won't
- * see what the value will be).
- *
- * Ben Herrenschmidt reports that on PPC, the C version comes close
- * to the optimized asm with this (ie on PPC you don't want that
- * 'volatile', since there are lots of registers).
- *
- * On ARM we get the best code generation by forcing a full memory barrier
- * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
- * the stack frame size simply explode and performance goes down the drain.
- */
-
-#if defined(__i386__) || defined(__x86_64__)
- #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
-#elif defined(__GNUC__) && defined(__arm__)
- #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
-#else
- #define setW(x, val) (W(x) = (val))
-#endif
-
-/*
- * Performance might be improved if the CPU architecture is OK with
- * unaligned 32-bit loads and a fast ntohl() is available.
- * Otherwise fall back to byte loads and shifts which is portable,
- * and is faster on architectures with memory alignment issues.
- */
-
-#if defined(__i386__) || defined(__x86_64__) || \
-	defined(_M_IX86) || defined(_M_X64) || \
-	defined(__ppc__) || defined(__ppc64__) || \
-	defined(__powerpc__) || defined(__powerpc64__) || \
-	defined(__s390__) || defined(__s390x__)
-
-#define get_be32(p)	ntohl(*(const unsigned int *)(p))
-#define put_be32(p, v)	do { *(unsigned int *)(p) = htonl(v); } while (0)
-
-#else
-
-#define get_be32(p)	( \
-	(*((const unsigned char *)(p) + 0) << 24) | \
-	(*((const unsigned char *)(p) + 1) << 16) | \
-	(*((const unsigned char *)(p) + 2) << 8) | \
-	(*((const unsigned char *)(p) + 3) << 0) )
-#define put_be32(p, v)	do { \
-	unsigned int __v = (v); \
-	*((unsigned char *)(p) + 0) = __v >> 24; \
-	*((unsigned char *)(p) + 1) = __v >> 16; \
-	*((unsigned char *)(p) + 2) = __v >> 8; \
-	*((unsigned char *)(p) + 3) = __v >> 0; } while (0)
-
-#endif
-
-/* This "rolls" over the 512-bit array */
-#define W(x) (array[(x)&15])
-
-/*
- * Where do we get the source from? The first 16 iterations get it from
- * the input data, the next mix it from the 512-bit array.
- */
-#define SHA_SRC(t) get_be32(data + t)
-#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
-
-#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
-	unsigned int TEMP = input(t); setW(t, TEMP); \
-	E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
-	B = SHA_ROR(B, 2); } while (0)
-
-#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
-#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
-#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
-#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
-
-static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
-{
-	unsigned int A,B,C,D,E;
-	unsigned int array[16];
-
-	A = ctx->H[0];
-	B = ctx->H[1];
-	C = ctx->H[2];
-	D = ctx->H[3];
-	E = ctx->H[4];
-
-	/* Round 1 - iterations 0-16 take their input from 'data' */
-	T_0_15( 0, A, B, C, D, E);
-	T_0_15( 1, E, A, B, C, D);
-	T_0_15( 2, D, E, A, B, C);
-	T_0_15( 3, C, D, E, A, B);
-	T_0_15( 4, B, C, D, E, A);
-	T_0_15( 5, A, B, C, D, E);
-	T_0_15( 6, E, A, B, C, D);
-	T_0_15( 7, D, E, A, B, C);
-	T_0_15( 8, C, D, E, A, B);
-	T_0_15( 9, B, C, D, E, A);
-	T_0_15(10, A, B, C, D, E);
-	T_0_15(11, E, A, B, C, D);
-	T_0_15(12, D, E, A, B, C);
-	T_0_15(13, C, D, E, A, B);
-	T_0_15(14, B, C, D, E, A);
-	T_0_15(15, A, B, C, D, E);
-
-	/* Round 1 - tail. Input from 512-bit mixing array */
-	T_16_19(16, E, A, B, C, D);
-	T_16_19(17, D, E, A, B, C);
-	T_16_19(18, C, D, E, A, B);
-	T_16_19(19, B, C, D, E, A);
-
-	/* Round 2 */
-	T_20_39(20, A, B, C, D, E);
-	T_20_39(21, E, A, B, C, D);
-	T_20_39(22, D, E, A, B, C);
-	T_20_39(23, C, D, E, A, B);
-	T_20_39(24, B, C, D, E, A);
-	T_20_39(25, A, B, C, D, E);
-	T_20_39(26, E, A, B, C, D);
-	T_20_39(27, D, E, A, B, C);
-	T_20_39(28, C, D, E, A, B);
-	T_20_39(29, B, C, D, E, A);
-	T_20_39(30, A, B, C, D, E);
-	T_20_39(31, E, A, B, C, D);
-	T_20_39(32, D, E, A, B, C);
-	T_20_39(33, C, D, E, A, B);
-	T_20_39(34, B, C, D, E, A);
-	T_20_39(35, A, B, C, D, E);
-	T_20_39(36, E, A, B, C, D);
-	T_20_39(37, D, E, A, B, C);
-	T_20_39(38, C, D, E, A, B);
-	T_20_39(39, B, C, D, E, A);
-
-	/* Round 3 */
-	T_40_59(40, A, B, C, D, E);
-	T_40_59(41, E, A, B, C, D);
-	T_40_59(42, D, E, A, B, C);
-	T_40_59(43, C, D, E, A, B);
-	T_40_59(44, B, C, D, E, A);
-	T_40_59(45, A, B, C, D, E);
-	T_40_59(46, E, A, B, C, D);
-	T_40_59(47, D, E, A, B, C);
-	T_40_59(48, C, D, E, A, B);
-	T_40_59(49, B, C, D, E, A);
-	T_40_59(50, A, B, C, D, E);
-	T_40_59(51, E, A, B, C, D);
-	T_40_59(52, D, E, A, B, C);
-	T_40_59(53, C, D, E, A, B);
-	T_40_59(54, B, C, D, E, A);
-	T_40_59(55, A, B, C, D, E);
-	T_40_59(56, E, A, B, C, D);
-	T_40_59(57, D, E, A, B, C);
-	T_40_59(58, C, D, E, A, B);
-	T_40_59(59, B, C, D, E, A);
-
-	/* Round 4 */
-	T_60_79(60, A, B, C, D, E);
-	T_60_79(61, E, A, B, C, D);
-	T_60_79(62, D, E, A, B, C);
-	T_60_79(63, C, D, E, A, B);
-	T_60_79(64, B, C, D, E, A);
-	T_60_79(65, A, B, C, D, E);
-	T_60_79(66, E, A, B, C, D);
-	T_60_79(67, D, E, A, B, C);
-	T_60_79(68, C, D, E, A, B);
-	T_60_79(69, B, C, D, E, A);
-	T_60_79(70, A, B, C, D, E);
-	T_60_79(71, E, A, B, C, D);
-	T_60_79(72, D, E, A, B, C);
-	T_60_79(73, C, D, E, A, B);
-	T_60_79(74, B, C, D, E, A);
-	T_60_79(75, A, B, C, D, E);
-	T_60_79(76, E, A, B, C, D);
-	T_60_79(77, D, E, A, B, C);
-	T_60_79(78, C, D, E, A, B);
-	T_60_79(79, B, C, D, E, A);
-
-	ctx->H[0] += A;
-	ctx->H[1] += B;
-	ctx->H[2] += C;
-	ctx->H[3] += D;
-	ctx->H[4] += E;
-}
-
-void git__blk_SHA1_Init(blk_SHA_CTX *ctx)
-{
-	ctx->size = 0;
-
-	/* Initialize H with the magic constants (see FIPS180 for constants) */
-	ctx->H[0] = 0x67452301;
-	ctx->H[1] = 0xefcdab89;
-	ctx->H[2] = 0x98badcfe;
-	ctx->H[3] = 0x10325476;
-	ctx->H[4] = 0xc3d2e1f0;
-}
-
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, size_t len)
-{
-	unsigned int lenW = ctx->size & 63;
-
-	ctx->size += len;
-
-	/* Read the data into W and process blocks as they get full */
-	if (lenW) {
-		unsigned int left = 64 - lenW;
-		if (len < left)
-			left = (unsigned int)len;
-		memcpy(lenW + (char *)ctx->W, data, left);
-		lenW = (lenW + left) & 63;
-		len -= left;
-		data = ((const char *)data + left);
-		if (lenW)
-			return;
-		blk_SHA1_Block(ctx, ctx->W);
-	}
-	while (len >= 64) {
-		blk_SHA1_Block(ctx, data);
-		data = ((const char *)data + 64);
-		len -= 64;
-	}
-	if (len)
-		memcpy(ctx->W, data, len);
-}
-
-void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
-{
-	static const unsigned char pad[64] = { 0x80 };
-	unsigned int padlen[2];
-	int i;
-
-	/* Pad with a binary 1 (ie 0x80), then zeroes, then length */
-	padlen[0] = htonl((uint32_t)(ctx->size >> 29));
-	padlen[1] = htonl((uint32_t)(ctx->size << 3));
-
-	i = ctx->size & 63;
-	git__blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
-	git__blk_SHA1_Update(ctx, padlen, 8);
-
-	/* Output hash */
-	for (i = 0; i < 5; i++)
-		put_be32(hashout + i*4, ctx->H[i]);
-}
diff --git a/libgit2/src/sha1.h b/libgit2/src/sha1.h
deleted file mode 100644
--- a/libgit2/src/sha1.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-typedef struct {
-	unsigned long long size;
-	unsigned int H[5];
-	unsigned int W[16];
-} blk_SHA_CTX;
-
-void git__blk_SHA1_Init(blk_SHA_CTX *ctx);
-void git__blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, size_t len);
-void git__blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
-
-#define SHA_CTX		blk_SHA_CTX
-#define SHA1_Init	git__blk_SHA1_Init
-#define SHA1_Update	git__blk_SHA1_Update
-#define SHA1_Final	git__blk_SHA1_Final
diff --git a/libgit2/src/sha1_lookup.c b/libgit2/src/sha1_lookup.c
--- a/libgit2/src/sha1_lookup.c
+++ b/libgit2/src/sha1_lookup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/sha1_lookup.h b/libgit2/src/sha1_lookup.h
--- a/libgit2/src/sha1_lookup.h
+++ b/libgit2/src/sha1_lookup.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/signature.c b/libgit2/src/signature.c
--- a/libgit2/src/signature.c
+++ b/libgit2/src/signature.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -22,80 +22,60 @@
 	git__free(sig);
 }
 
-static const char *skip_leading_spaces(const char *buffer, const char *buffer_end)
-{
-	while (*buffer == ' ' && buffer < buffer_end)
-		buffer++;
-
-	return buffer;
-}
-
-static const char *skip_trailing_spaces(const char *buffer_start, const char *buffer_end)
-{
-	while (*buffer_end == ' ' && buffer_end > buffer_start)
-		buffer_end--;
-
-	return buffer_end;
-}
-
 static int signature_error(const char *msg)
 {
 	giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg);
 	return -1;
 }
 
-static int process_trimming(const char *input, char **storage, const char *input_end, int fail_when_empty)
+static bool contains_angle_brackets(const char *input)
 {
-	const char *left, *right;
-	size_t trimmed_input_length;
-
-	assert(storage);
-
-	left = skip_leading_spaces(input, input_end);
-	right = skip_trailing_spaces(input, input_end - 1);
-
-	if (right < left) {
-		if (fail_when_empty)
-			return signature_error("input is either empty of contains only spaces");
+	return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
+}
 
-		right = left - 1;
+static char *extract_trimmed(const char *ptr, size_t len)
+{
+	while (len && ptr[0] == ' ') {
+		ptr++; len--;
 	}
 
-	trimmed_input_length = right - left + 1;
-
-	*storage = git__malloc(trimmed_input_length + 1);
-	GITERR_CHECK_ALLOC(*storage);
-
-	memcpy(*storage, left, trimmed_input_length);
-	(*storage)[trimmed_input_length] = 0;
+	while (len && ptr[len - 1] == ' ') {
+		len--;
+	}
 
-	return 0;
+	return git__substrdup(ptr, len);
 }
 
 int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
 {
-	int error;
 	git_signature *p = NULL;
 
 	assert(name && email);
 
 	*sig_out = NULL;
 
+	if (contains_angle_brackets(name) ||
+		contains_angle_brackets(email)) {
+		return signature_error(
+			"Neither `name` nor `email` should contain angle brackets chars.");
+	}
+
 	p = git__calloc(1, sizeof(git_signature));
 	GITERR_CHECK_ALLOC(p);
 
-	if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 ||
-		(error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0)
-	{
+	p->name = extract_trimmed(name, strlen(name));
+	p->email = extract_trimmed(email, strlen(email));
+
+	if (p->name == NULL || p->email == NULL ||
+		p->name[0] == '\0' || p->email[0] == '\0') {
 		git_signature_free(p);
-		return error;
+		return -1;
 	}
-
+		
 	p->when.time = time;
 	p->when.offset = offset;
 
 	*sig_out = p;
-
 	return 0;
 }
 
@@ -111,36 +91,26 @@
 {
 	time_t now;
 	time_t offset;
-	struct tm *utc_tm, *local_tm;
+	struct tm *utc_tm;
 	git_signature *sig;
-
-#ifndef GIT_WIN32
-	struct tm _utc, _local;
-#endif
+	struct tm _utc;
 
 	*sig_out = NULL;
 
-	time(&now);
-
-	/**
-	 * On Win32, `gmtime_r` doesn't exist but
-	 * `gmtime` is threadsafe, so we can use that
+	/*
+	 * Get the current time as seconds since the epoch and
+	 * transform that into a tm struct containing the time at
+	 * UTC. Give that to mktime which considers it a local time
+	 * (tm_isdst = -1 asks it to take DST into account) and gives
+	 * us that time as seconds since the epoch. The difference
+	 * between its return value and 'now' is our offset to UTC.
 	 */
-#ifdef GIT_WIN32
-	utc_tm = gmtime(&now);
-	local_tm = localtime(&now);
-#else
-	utc_tm = gmtime_r(&now, &_utc);
-	local_tm = localtime_r(&now, &_local);
-#endif
-
-	offset = mktime(local_tm) - mktime(utc_tm);
+	time(&now);
+	utc_tm = p_gmtime_r(&now, &_utc);
+	utc_tm->tm_isdst = -1;
+	offset = (time_t)difftime(now, mktime(utc_tm));
 	offset /= 60;
 
-	/* mktime takes care of setting tm_isdst correctly */
-	if (local_tm->tm_isdst)
-		offset += 60;
-
 	if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
 		return -1;
 
@@ -149,169 +119,71 @@
 	return 0;
 }
 
-static int timezone_error(const char *msg)
-{
-	giterr_set(GITERR_INVALID, "Failed to parse TZ offset - %s", msg);
-	return -1;
-}
-
-static int parse_timezone_offset(const char *buffer, int *offset_out)
-{
-	int dec_offset;
-	int mins, hours, offset;
-
-	const char *offset_start;
-	const char *offset_end;
-
-	offset_start = buffer;
-
-	if (*offset_start == '\n') {
-		*offset_out = 0;
-		return 0;
-	}
-
-	if (offset_start[0] != '-' && offset_start[0] != '+')
-		return timezone_error("does not start with '+' or '-'");
-
-	if (offset_start[1] < '0' || offset_start[1] > '9')
-		return timezone_error("expected initial digit");
-
-	if (git__strtol32(&dec_offset, offset_start + 1, &offset_end, 10) < 0)
-		return timezone_error("not a valid number");
-
-	if (offset_end - offset_start != 5)
-		return timezone_error("invalid length");
-
-	if (dec_offset > 1400)
-		return timezone_error("value too large");
-
-	hours = dec_offset / 100;
-	mins = dec_offset % 100;
-
-	if (hours > 14)	// see http://www.worldtimezone.com/faq.html
-		return timezone_error("hour value too large");
-
-	if (mins > 59)
-		return timezone_error("minutes value too large");
-
-	offset = (hours * 60) + mins;
-
-	if (offset_start[0] == '-')
-		offset *= -1;
-
-	*offset_out = offset;
-
-	return 0;
-}
-
-static int process_next_token(const char **buffer_out, char **storage,
-	const char *token_end, const char *right_boundary)
-{
-	int error = process_trimming(*buffer_out, storage, token_end, 0);
-	if (error < 0)
-		return error;
-
-	*buffer_out = token_end + 1;
-
-	if (*buffer_out > right_boundary)
-		return signature_error("signature is too short");
-
-	return 0;
-}
-
-static const char *scan_for_previous_token(const char *buffer, const char *left_boundary)
-{
-	const char *start;
-
-	if (buffer <= left_boundary)
-		return NULL;
-
-	start = skip_trailing_spaces(left_boundary, buffer);
-
-	/* Search for previous occurence of space */
-	while (start[-1] != ' ' && start > left_boundary)
-		start--;
-
-	return start;
-}
-
-static int parse_time(git_time_t *time_out, const char *buffer)
-{
-	int time;
-	int error;
-
-	if (*buffer == '+' || *buffer == '-') {
-		giterr_set(GITERR_INVALID, "Failed while parsing time. '%s' actually looks like a timezone offset.", buffer);
-		return -1;
-	}
-
-	error = git__strtol32(&time, buffer, &buffer, 10);
-
-	if (!error)
-		*time_out = (git_time_t)time;
-
-	return error;
-}
-
 int git_signature__parse(git_signature *sig, const char **buffer_out,
 		const char *buffer_end, const char *header, char ender)
 {
 	const char *buffer = *buffer_out;
-	const char *line_end, *name_end, *email_end, *tz_start, *time_start;
-	int error = 0;
+	const char *email_start, *email_end;
 
-	memset(sig, 0x0, sizeof(git_signature));
+	memset(sig, 0, sizeof(git_signature));
 
-	if ((line_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
+	if ((buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
 		return signature_error("no newline given");
 
 	if (header) {
 		const size_t header_len = strlen(header);
 
-		if (memcmp(buffer, header, header_len) != 0)
+		if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
 			return signature_error("expected prefix doesn't match actual");
 
 		buffer += header_len;
 	}
 
-	if (buffer > line_end)
-		return signature_error("signature too short");
+	email_start = git__memrchr(buffer, '<', buffer_end - buffer);
+	email_end = git__memrchr(buffer, '>', buffer_end - buffer);
 
-	if ((name_end = strchr(buffer, '<')) == NULL)
-		return signature_error("character '<' not allowed in signature");
+	if (!email_start || !email_end || email_end <= email_start)
+		return signature_error("malformed e-mail");
 
-	if ((email_end = strchr(name_end, '>')) == NULL)
-		return signature_error("character '>' not allowed in signature");
+	email_start += 1;
+	sig->name = extract_trimmed(buffer, email_start - buffer - 1);
+	sig->email = extract_trimmed(email_start, email_end - email_start);
 
-	if (email_end < name_end)
-		return signature_error("malformed e-mail");
+	/* Do we even have a time at the end of the signature? */
+	if (email_end + 2 < buffer_end) {
+		const char *time_start = email_end + 2;
+		const char *time_end;
 
-	error = process_next_token(&buffer, &sig->name, name_end, line_end);
-	if (error < 0)
-		return error;
+		if (git__strtol64(&sig->when.time, time_start, &time_end, 10) < 0)
+			return signature_error("invalid Unix timestamp");
 
-	error = process_next_token(&buffer, &sig->email, email_end, line_end);
-	if (error < 0)
-		return error;
+		/* do we have a timezone? */
+		if (time_end + 1 < buffer_end) {
+			int offset, hours, mins;
+			const char *tz_start, *tz_end;
 
-	tz_start = scan_for_previous_token(line_end - 1, buffer);
+			tz_start = time_end + 1;
 
-	if (tz_start == NULL)
-		goto clean_exit;	/* No timezone nor date */
+			if ((tz_start[0] != '-' && tz_start[0] != '+') || 
+				git__strtol32(&offset, tz_start + 1, &tz_end, 10) < 0)
+				return signature_error("malformed timezone");
 
-	time_start = scan_for_previous_token(tz_start - 1, buffer);
-	if (time_start == NULL || parse_time(&sig->when.time, time_start) < 0) {
-		/* The tz_start might point at the time */
-		parse_time(&sig->when.time, tz_start);
-		goto clean_exit;
-	}
+			hours = offset / 100;
+			mins = offset % 100;
 
-	if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {
-		sig->when.time = 0; /* Bogus timezone, we reset the time */
+			/*
+			 * only store timezone if it's not overflowing;
+			 * see http://www.worldtimezone.com/faq.html
+			 */
+			if (hours < 14 && mins < 59) {
+				sig->when.offset = (hours * 60) + mins;
+				if (tz_start[0] == '-')
+					sig->when.offset = -sig->when.offset;
+			}
+		}
 	}
 
-clean_exit:
-	*buffer_out = line_end + 1;
+	*buffer_out = buffer_end + 1;
 	return 0;
 }
 
diff --git a/libgit2/src/signature.h b/libgit2/src/signature.h
--- a/libgit2/src/signature.h
+++ b/libgit2/src/signature.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/stash.c b/libgit2/src/stash.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/stash.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "repository.h"
+#include "commit.h"
+#include "tree.h"
+#include "reflog.h"
+#include "git2/diff.h"
+#include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/checkout.h"
+#include "signature.h"
+
+static int create_error(int error, const char *msg)
+{
+	giterr_set(GITERR_STASH, "Cannot stash changes - %s", msg);
+	return error;
+}
+
+static int retrieve_head(git_reference **out, git_repository *repo)
+{
+	int error = git_repository_head(out, repo);
+
+	if (error == GIT_EORPHANEDHEAD)
+		return create_error(error, "You do not have the initial commit yet.");
+
+	return error;
+}
+
+static int append_abbreviated_oid(git_buf *out, const git_oid *b_commit)
+{
+	char *formatted_oid;
+
+	formatted_oid = git_oid_allocfmt(b_commit);
+	GITERR_CHECK_ALLOC(formatted_oid);
+
+	git_buf_put(out, formatted_oid, 7);
+	git__free(formatted_oid);
+
+	return git_buf_oom(out) ? -1 : 0;
+}
+
+static int append_commit_description(git_buf *out, git_commit* commit)
+{
+	const char *message;
+	size_t pos = 0, len;
+
+	if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
+		return -1;
+
+	message = git_commit_message(commit);
+	len = strlen(message);
+
+	/* TODO: Replace with proper commit short message
+	 * when git_commit_message_short() is implemented.
+	 */
+	while (pos < len && message[pos] != '\n')
+		pos++;
+
+	git_buf_putc(out, ' ');
+	git_buf_put(out, message, pos);
+	git_buf_putc(out, '\n');
+
+	return git_buf_oom(out) ? -1 : 0;
+}
+
+static int retrieve_base_commit_and_message(
+	git_commit **b_commit,
+	git_buf *stash_message,
+	git_repository *repo)
+{
+	git_reference *head = NULL;
+	int error;
+
+	if ((error = retrieve_head(&head, repo)) < 0)
+		return error;
+
+	if (strcmp("HEAD", git_reference_name(head)) == 0)
+		error = git_buf_puts(stash_message, "(no branch): ");
+	else
+		error = git_buf_printf(
+			stash_message,
+			"%s: ",
+			git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = git_commit_lookup(
+			 b_commit, repo, git_reference_target(head))) < 0)
+		goto cleanup;
+
+	if ((error = append_commit_description(stash_message, *b_commit)) < 0)
+		goto cleanup;
+
+cleanup:
+	git_reference_free(head);
+	return error;
+}
+
+static int build_tree_from_index(git_tree **out, git_index *index)
+{
+	int error;
+	git_oid i_tree_oid;
+
+	if ((error = git_index_write_tree(&i_tree_oid, index)) < 0)
+		return -1;
+
+	return git_tree_lookup(out, git_index_owner(index), &i_tree_oid);
+}
+
+static int commit_index(
+	git_commit **i_commit,
+	git_index *index,
+	git_signature *stasher,
+	const char *message,
+	const git_commit *parent)
+{
+	git_tree *i_tree = NULL;
+	git_oid i_commit_oid;
+	git_buf msg = GIT_BUF_INIT;
+	int error;
+
+	if ((error = build_tree_from_index(&i_tree, index)) < 0)
+		goto cleanup;
+
+	if ((error = git_buf_printf(&msg, "index on %s\n", message)) < 0)
+		goto cleanup;
+
+	if ((error = git_commit_create(
+		&i_commit_oid,
+		git_index_owner(index),
+		NULL,
+		stasher,
+		stasher,
+		NULL,
+		git_buf_cstr(&msg),
+		i_tree,
+		1,
+		&parent)) < 0)
+		goto cleanup;
+
+	error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
+
+cleanup:
+	git_tree_free(i_tree);
+	git_buf_free(&msg);
+	return error;
+}
+
+struct cb_data {
+	git_index *index;
+
+	int error;
+
+	bool include_changed;
+	bool include_untracked;
+	bool include_ignored;
+};
+
+static int update_index_cb(
+	const git_diff_delta *delta,
+	float progress,
+	void *payload)
+{
+	struct cb_data *data = (struct cb_data *)payload;
+	const char *add_path = NULL;
+
+	GIT_UNUSED(progress);
+
+	switch (delta->status) {
+	case GIT_DELTA_IGNORED:
+		if (data->include_ignored)
+			add_path = delta->new_file.path;
+		break;
+
+	case GIT_DELTA_UNTRACKED:
+		if (data->include_untracked)
+			add_path = delta->new_file.path;
+		break;
+
+	case GIT_DELTA_ADDED:
+	case GIT_DELTA_MODIFIED:
+		if (data->include_changed)
+			add_path = delta->new_file.path;
+		break;
+
+	case GIT_DELTA_DELETED:
+		if (!data->include_changed)
+			break;
+		if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
+			data->error = git_index_remove(
+				data->index, delta->old_file.path, 0);
+		break;
+
+	default:
+		/* Unimplemented */
+		giterr_set(
+			GITERR_INVALID,
+			"Cannot update index. Unimplemented status (%d)",
+			delta->status);
+		data->error = -1;
+		break;
+	}
+
+	if (add_path != NULL)
+		data->error = git_index_add_bypath(data->index, add_path);
+
+	return data->error;
+}
+
+static int build_untracked_tree(
+	git_tree **tree_out,
+	git_index *index,
+	git_commit *i_commit,
+	uint32_t flags)
+{
+	git_tree *i_tree = NULL;
+	git_diff_list *diff = NULL;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	struct cb_data data = {0};
+	int error;
+
+	git_index_clear(index);
+
+	data.index = index;
+
+	if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
+		opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
+			GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+		data.include_untracked = true;
+	}
+
+	if (flags & GIT_STASH_INCLUDE_IGNORED) {
+		opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+		data.include_ignored = true;
+	}
+
+	if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
+		goto cleanup;
+
+	if ((error = git_diff_tree_to_workdir(
+			&diff, git_index_owner(index), i_tree, &opts)) < 0)
+		goto cleanup;
+
+	if ((error = git_diff_foreach(
+			diff, update_index_cb, NULL, NULL, &data)) < 0)
+	{
+		if (error == GIT_EUSER)
+			error = data.error;
+		goto cleanup;
+	}
+
+	error = build_tree_from_index(tree_out, index);
+
+cleanup:
+	git_diff_list_free(diff);
+	git_tree_free(i_tree);
+	return error;
+}
+
+static int commit_untracked(
+	git_commit **u_commit,
+	git_index *index,
+	git_signature *stasher,
+	const char *message,
+	git_commit *i_commit,
+	uint32_t flags)
+{
+	git_tree *u_tree = NULL;
+	git_oid u_commit_oid;
+	git_buf msg = GIT_BUF_INIT;
+	int error;
+
+	if ((error = build_untracked_tree(&u_tree, index, i_commit, flags)) < 0)
+		goto cleanup;
+
+	if ((error = git_buf_printf(&msg, "untracked files on %s\n", message)) < 0)
+		goto cleanup;
+
+	if ((error = git_commit_create(
+		&u_commit_oid,
+		git_index_owner(index),
+		NULL,
+		stasher,
+		stasher,
+		NULL,
+		git_buf_cstr(&msg),
+		u_tree,
+		0,
+		NULL)) < 0)
+		goto cleanup;
+
+	error = git_commit_lookup(u_commit, git_index_owner(index), &u_commit_oid);
+
+cleanup:
+	git_tree_free(u_tree);
+	git_buf_free(&msg);
+	return error;
+}
+
+static int build_workdir_tree(
+	git_tree **tree_out,
+	git_index *index,
+	git_commit *b_commit)
+{
+	git_repository *repo = git_index_owner(index);
+	git_tree *b_tree = NULL;
+	git_diff_list *diff = NULL, *diff2 = NULL;
+	git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+	struct cb_data data = {0};
+	int error;
+
+	if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
+		goto cleanup;
+
+	if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
+		goto cleanup;
+
+	if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
+		goto cleanup;
+
+	if ((error = git_diff_merge(diff, diff2)) < 0)
+		goto cleanup;
+
+	data.index = index;
+	data.include_changed = true;
+
+	if ((error = git_diff_foreach(
+			diff, update_index_cb, NULL, NULL, &data)) < 0)
+	{
+		if (error == GIT_EUSER)
+			error = data.error;
+		goto cleanup;
+	}
+
+
+	if ((error = build_tree_from_index(tree_out, index)) < 0)
+		goto cleanup;
+
+cleanup:
+	git_diff_list_free(diff);
+	git_diff_list_free(diff2);
+	git_tree_free(b_tree);
+
+	return error;
+}
+
+static int commit_worktree(
+	git_oid *w_commit_oid,
+	git_index *index,
+	git_signature *stasher,
+	const char *message,
+	git_commit *i_commit,
+	git_commit *b_commit,
+	git_commit *u_commit)
+{
+	int error = 0;
+	git_tree *w_tree = NULL, *i_tree = NULL;
+	const git_commit *parents[] = {	NULL, NULL,	NULL };
+
+	parents[0] = b_commit;
+	parents[1] = i_commit;
+	parents[2] = u_commit;
+
+	if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
+		goto cleanup;
+
+	if ((error = git_index_read_tree(index, i_tree)) < 0)
+		goto cleanup;
+
+	if ((error = build_workdir_tree(&w_tree, index, b_commit)) < 0)
+		goto cleanup;
+
+	error = git_commit_create(
+		w_commit_oid,
+		git_index_owner(index),
+		NULL,
+		stasher,
+		stasher,
+		NULL,
+		message,
+		w_tree,
+		u_commit ? 3 : 2,
+		parents);
+
+cleanup:
+	git_tree_free(i_tree);
+	git_tree_free(w_tree);
+	return error;
+}
+
+static int prepare_worktree_commit_message(
+	git_buf* msg,
+	const char *user_message)
+{
+	git_buf buf = GIT_BUF_INIT;
+	int error;
+
+	if ((error = git_buf_set(&buf, git_buf_cstr(msg), git_buf_len(msg))) < 0)
+		return error;
+
+	git_buf_clear(msg);
+
+	if (!user_message)
+		git_buf_printf(msg, "WIP on %s", git_buf_cstr(&buf));
+	else {
+		const char *colon;
+
+		if ((colon = strchr(git_buf_cstr(&buf), ':')) == NULL)
+			goto cleanup;
+
+		git_buf_puts(msg, "On ");
+		git_buf_put(msg, git_buf_cstr(&buf), colon - buf.ptr);
+		git_buf_printf(msg, ": %s\n", user_message);
+	}
+
+	error = (git_buf_oom(msg) || git_buf_oom(&buf)) ? -1 : 0;
+
+cleanup:
+	git_buf_free(&buf);
+
+	return error;
+}
+
+static int update_reflog(
+	git_oid *w_commit_oid,
+	git_repository *repo,
+	git_signature *stasher,
+	const char *message)
+{
+	git_reference *stash = NULL;
+	git_reflog *reflog = NULL;
+	int error;
+
+	if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_read(&reflog, stash)) < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_write(reflog)) < 0)
+		goto cleanup;
+
+cleanup:
+	git_reference_free(stash);
+	git_reflog_free(reflog);
+	return error;
+}
+
+static int is_dirty_cb(const char *path, unsigned int status, void *payload)
+{
+	GIT_UNUSED(path);
+	GIT_UNUSED(status);
+	GIT_UNUSED(payload);
+
+	return 1;
+}
+
+static int ensure_there_are_changes_to_stash(
+	git_repository *repo,
+	bool include_untracked_files,
+	bool include_ignored_files)
+{
+	int error;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+	if (include_untracked_files)
+		opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+	if (include_ignored_files)
+		opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+	error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
+
+	if (error == GIT_EUSER)
+		return 0;
+
+	if (!error)
+		return create_error(GIT_ENOTFOUND, "There is nothing to stash.");
+
+	return error;
+}
+
+static int reset_index_and_workdir(
+	git_repository *repo,
+	git_commit *commit,
+	bool remove_untracked)
+{
+	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
+
+	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+	if (remove_untracked)
+		opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+	return git_checkout_tree(repo, (git_object *)commit, &opts);
+}
+
+int git_stash_save(
+	git_oid *out,
+	git_repository *repo,
+	git_signature *stasher,
+	const char *message,
+	uint32_t flags)
+{
+	git_index *index = NULL;
+	git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
+	git_buf msg = GIT_BUF_INIT;
+	int error;
+
+	assert(out && repo && stasher);
+
+	if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
+		return error;
+
+	if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
+		goto cleanup;
+
+	if ((error = ensure_there_are_changes_to_stash(
+		repo,
+		(flags & GIT_STASH_INCLUDE_UNTRACKED) != 0,
+		(flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0)
+		goto cleanup;
+
+	if ((error = git_repository_index(&index, repo)) < 0)
+		goto cleanup;
+
+	if ((error = commit_index(
+			&i_commit, index, stasher, git_buf_cstr(&msg), b_commit)) < 0)
+		goto cleanup;
+
+	if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
+		(error = commit_untracked(
+			&u_commit, index, stasher, git_buf_cstr(&msg),
+			i_commit, flags)) < 0)
+		goto cleanup;
+
+	if ((error = prepare_worktree_commit_message(&msg, message)) < 0)
+		goto cleanup;
+
+	if ((error = commit_worktree(
+			out, index, stasher, git_buf_cstr(&msg),
+			i_commit, b_commit, u_commit)) < 0)
+		goto cleanup;
+
+	git_buf_rtrim(&msg);
+
+	if ((error = update_reflog(out, repo, stasher, git_buf_cstr(&msg))) < 0)
+		goto cleanup;
+
+	if ((error = reset_index_and_workdir(
+		repo,
+		((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit,
+		(flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0)
+		goto cleanup;
+
+cleanup:
+
+	git_buf_free(&msg);
+	git_commit_free(i_commit);
+	git_commit_free(b_commit);
+	git_commit_free(u_commit);
+	git_index_free(index);
+
+	return error;
+}
+
+int git_stash_foreach(
+	git_repository *repo,
+	git_stash_cb callback,
+	void *payload)
+{
+	git_reference *stash;
+	git_reflog *reflog = NULL;
+	int error;
+	size_t i, max;
+	const git_reflog_entry *entry;
+
+	error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
+	if (error == GIT_ENOTFOUND)
+		return 0;
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_read(&reflog, stash)) < 0)
+		goto cleanup;
+
+	max = git_reflog_entrycount(reflog);
+	for (i = 0; i < max; i++) {
+		entry = git_reflog_entry_byindex(reflog, i);
+
+		if (callback(i,
+			git_reflog_entry_message(entry),
+			git_reflog_entry_id_new(entry),
+			payload)) {
+				error = GIT_EUSER;
+				break;
+		}
+	}
+
+cleanup:
+	git_reference_free(stash);
+	git_reflog_free(reflog);
+	return error;
+}
+
+int git_stash_drop(
+	git_repository *repo,
+	size_t index)
+{
+	git_reference *stash;
+	git_reflog *reflog = NULL;
+	size_t max;
+	int error;
+
+	if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+		return error;
+
+	if ((error = git_reflog_read(&reflog, stash)) < 0)
+		goto cleanup;
+
+	max = git_reflog_entrycount(reflog);
+
+	if (index > max - 1) {
+		error = GIT_ENOTFOUND;
+		giterr_set(GITERR_STASH, "No stashed state at position %" PRIuZ, index);
+		goto cleanup;
+	}
+
+	if ((error = git_reflog_drop(reflog, index, true)) < 0)
+		goto cleanup;
+
+	if ((error = git_reflog_write(reflog)) < 0)
+		goto cleanup;
+
+	if (max == 1) {
+		error = git_reference_delete(stash);
+		git_reference_free(stash);
+		stash = NULL;
+	} else if (index == 0) {
+		const git_reflog_entry *entry;
+
+		entry = git_reflog_entry_byindex(reflog, 0);
+		
+		git_reference_free(stash);
+		error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1);
+	}
+
+cleanup:
+	git_reference_free(stash);
+	git_reflog_free(reflog);
+	return error;
+}
diff --git a/libgit2/src/status.c b/libgit2/src/status.c
--- a/libgit2/src/status.c
+++ b/libgit2/src/status.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -17,6 +17,7 @@
 
 #include "git2/diff.h"
 #include "diff.h"
+#include "diff_output.h"
 
 static unsigned int index_delta2status(git_delta_t index_status)
 {
@@ -25,7 +26,6 @@
 	switch (index_status) {
 	case GIT_DELTA_ADDED:
 	case GIT_DELTA_COPIED:
-	case GIT_DELTA_RENAMED:
 		st = GIT_STATUS_INDEX_NEW;
 		break;
 	case GIT_DELTA_DELETED:
@@ -34,6 +34,12 @@
 	case GIT_DELTA_MODIFIED:
 		st = GIT_STATUS_INDEX_MODIFIED;
 		break;
+	case GIT_DELTA_RENAMED:
+		st = GIT_STATUS_INDEX_RENAMED;
+		break;
+	case GIT_DELTA_TYPECHANGE:
+		st = GIT_STATUS_INDEX_TYPECHANGE;
+		break;
 	default:
 		break;
 	}
@@ -47,8 +53,8 @@
 
 	switch (workdir_status) {
 	case GIT_DELTA_ADDED:
-	case GIT_DELTA_COPIED:
 	case GIT_DELTA_RENAMED:
+	case GIT_DELTA_COPIED:
 	case GIT_DELTA_UNTRACKED:
 		st = GIT_STATUS_WT_NEW;
 		break;
@@ -61,6 +67,9 @@
 	case GIT_DELTA_IGNORED:
 		st = GIT_STATUS_IGNORED;
 		break;
+	case GIT_DELTA_TYPECHANGE:
+		st = GIT_STATUS_WT_TYPECHANGE;
+		break;
 	default:
 		break;
 	}
@@ -68,29 +77,76 @@
 	return st;
 }
 
+typedef struct {
+	git_status_cb cb;
+	void *payload;
+	const git_status_options *opts;
+} status_user_callback;
+
+static int status_invoke_cb(
+	git_diff_delta *h2i, git_diff_delta *i2w, void *payload)
+{
+	status_user_callback *usercb = payload;
+	const char *path = NULL;
+	unsigned int status = 0;
+
+	if (i2w) {
+		path = i2w->old_file.path;
+		status |= workdir_delta2status(i2w->status);
+	}
+	if (h2i) {
+		path = h2i->old_file.path;
+		status |= index_delta2status(h2i->status);
+	}
+
+	/* if excluding submodules and this is a submodule everywhere */
+	if (usercb->opts &&
+		(usercb->opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
+	{
+		bool in_tree  = (h2i && h2i->status != GIT_DELTA_ADDED);
+		bool in_index = (h2i && h2i->status != GIT_DELTA_DELETED);
+		bool in_wd    = (i2w && i2w->status != GIT_DELTA_DELETED);
+
+		if ((!in_tree || h2i->old_file.mode == GIT_FILEMODE_COMMIT) &&
+			(!in_index || h2i->new_file.mode == GIT_FILEMODE_COMMIT) &&
+			(!in_wd || i2w->new_file.mode == GIT_FILEMODE_COMMIT))
+			return 0;
+	}
+
+	return usercb->cb(path, status, usercb->payload);
+}
+
 int git_status_foreach_ext(
 	git_repository *repo,
 	const git_status_options *opts,
-	int (*cb)(const char *, unsigned int, void *),
-	void *cbdata)
+	git_status_cb cb,
+	void *payload)
 {
-	int err = 0, cmp;
-	git_diff_options diffopt;
-	git_diff_list *idx2head = NULL, *wd2idx = NULL;
+	int err = 0;
+	git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
+	git_diff_list *head2idx = NULL, *idx2wd = NULL;
 	git_tree *head = NULL;
 	git_status_show_t show =
 		opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
-	git_diff_delta *i2h, *w2i;
-	unsigned int i, j, i_max, j_max;
+	status_user_callback usercb;
 
 	assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
 
-	if ((err = git_repository_head_tree(&head, repo)) < 0)
+	GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+
+	if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
+		(err = git_repository__ensure_not_bare(repo, "status")) < 0)
 		return err;
 
-	memset(&diffopt, 0, sizeof(diffopt));
+	/* if there is no HEAD, that's okay - we'll make an empty iterator */
+	if (((err = git_repository_head_tree(&head, repo)) < 0) &&
+		!(err == GIT_ENOTFOUND || err == GIT_EORPHANEDHEAD))
+			return err;
+
 	memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
 
+	diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+
 	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
 		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
 	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
@@ -99,62 +155,58 @@
 		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
 	if ((opts->flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
 		diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
-	/* TODO: support EXCLUDE_SUBMODULES flag */
-
-	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY &&
-		(err = git_diff_index_to_tree(repo, &diffopt, head, &idx2head)) < 0)
-		goto cleanup;
-
-	if (show != GIT_STATUS_SHOW_INDEX_ONLY &&
-		(err = git_diff_workdir_to_index(repo, &diffopt, &wd2idx)) < 0)
-		goto cleanup;
+	if ((opts->flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
+		diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+	if ((opts->flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
+		diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
+	if ((opts->flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
+		diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
 
-	if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
-		for (i = 0; !err && i < idx2head->deltas.length; i++) {
-			i2h = GIT_VECTOR_GET(&idx2head->deltas, i);
-			err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
-		}
-		git_diff_list_free(idx2head);
-		idx2head = NULL;
+	if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
+		err = git_diff_tree_to_index(&head2idx, repo, head, NULL, &diffopt);
+		if (err < 0)
+			goto cleanup;
 	}
 
-	i_max = idx2head ? idx2head->deltas.length : 0;
-	j_max = wd2idx   ? wd2idx->deltas.length   : 0;
+	if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
+		err = git_diff_index_to_workdir(&idx2wd, repo, NULL, &diffopt);
+		if (err < 0)
+			goto cleanup;
+	}
 
-	for (i = 0, j = 0; !err && (i < i_max || j < j_max); ) {
-		i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
-		w2i = wd2idx   ? GIT_VECTOR_GET(&wd2idx->deltas,j)   : NULL;
+	usercb.cb = cb;
+	usercb.payload = payload;
+	usercb.opts = opts;
 
-		cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
+	if (show == GIT_STATUS_SHOW_INDEX_THEN_WORKDIR) {
+		if ((err = git_diff__paired_foreach(
+				 head2idx, NULL, status_invoke_cb, &usercb)) < 0)
+			goto cleanup;
 
-		if (cmp < 0) {
-			err = cb(i2h->old_file.path, index_delta2status(i2h->status), cbdata);
-			i++;
-		} else if (cmp > 0) {
-			err = cb(w2i->old_file.path, workdir_delta2status(w2i->status), cbdata);
-			j++;
-		} else {
-			err = cb(i2h->old_file.path, index_delta2status(i2h->status) |
-					 workdir_delta2status(w2i->status), cbdata);
-			i++; j++;
-		}
+		git_diff_list_free(head2idx);
+		head2idx = NULL;
 	}
 
+	err = git_diff__paired_foreach(head2idx, idx2wd, status_invoke_cb, &usercb);
+
 cleanup:
 	git_tree_free(head);
-	git_diff_list_free(idx2head);
-	git_diff_list_free(wd2idx);
+	git_diff_list_free(head2idx);
+	git_diff_list_free(idx2wd);
+
+	if (err == GIT_EUSER)
+		giterr_clear();
+
 	return err;
 }
 
 int git_status_foreach(
 	git_repository *repo,
-	int (*callback)(const char *, unsigned int, void *),
+	git_status_cb callback,
 	void *payload)
 {
-	git_status_options opts;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
 
-	memset(&opts, 0, sizeof(opts));
 	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
 	opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
 		GIT_STATUS_OPT_INCLUDE_UNTRACKED |
@@ -164,22 +216,29 @@
 }
 
 struct status_file_info {
+	char *expected;
 	unsigned int count;
 	unsigned int status;
-	char *expected;
+	int fnm_flags;
+	int ambiguous;
 };
 
 static int get_one_status(const char *path, unsigned int status, void *data)
 {
 	struct status_file_info *sfi = data;
+	int (*strcomp)(const char *a, const char *b);
 
 	sfi->count++;
 	sfi->status = status;
 
-	if (sfi->count > 1 || strcmp(sfi->expected, path) != 0) {
-		giterr_set(GITERR_INVALID,
-			"Ambiguous path '%s' given to git_status_file", sfi->expected);
-		return -1;
+	strcomp = (sfi->fnm_flags & FNM_CASEFOLD) ? git__strcasecmp : git__strcmp;
+
+	if (sfi->count > 1 ||
+		(strcomp(sfi->expected, path) != 0 &&
+		 p_fnmatch(sfi->expected, path, sfi->fnm_flags) != 0))
+	{
+		sfi->ambiguous = true;
+		return GIT_EAMBIGUOUS;
 	}
 
 	return 0;
@@ -191,16 +250,20 @@
 	const char *path)
 {
 	int error;
-	git_status_options opts;
-	struct status_file_info sfi;
+	git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+	struct status_file_info sfi = {0};
+	git_index *index;
 
 	assert(status_flags && repo && path);
 
-	memset(&sfi, 0, sizeof(sfi));
+	if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+		return error;
+
 	if ((sfi.expected = git__strdup(path)) == NULL)
 		return -1;
+	if (index->ignore_case)
+		sfi.fnm_flags = FNM_CASEFOLD;
 
-	memset(&opts, 0, sizeof(opts));
 	opts.show  = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
 	opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
 		GIT_STATUS_OPT_INCLUDE_UNTRACKED |
@@ -211,12 +274,31 @@
 
 	error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
 
-	if (!error && !sfi.count) {
+	if (error < 0 && sfi.ambiguous) {
 		giterr_set(GITERR_INVALID,
-			"Attempt to get status of nonexistent file '%s'", path);
-		error = GIT_ENOTFOUND;
+			"Ambiguous path '%s' given to git_status_file", sfi.expected);
+		error = GIT_EAMBIGUOUS;
 	}
 
+	if (!error && !sfi.count) {
+		git_buf full = GIT_BUF_INIT;
+
+		/* if the file actually exists and we still did not get a callback
+		 * for it, then it must be contained inside an ignored directory, so
+		 * mark it as such instead of generating an error.
+		 */
+		if (!git_buf_joinpath(&full, git_repository_workdir(repo), path) &&
+			git_path_exists(full.ptr))
+			sfi.status = GIT_STATUS_IGNORED;
+		else {
+			giterr_set(GITERR_INVALID,
+				"Attempt to get status of nonexistent file '%s'", path);
+			error = GIT_ENOTFOUND;
+		}
+
+		git_buf_free(&full);
+	}
+
 	*status_flags = sfi.status;
 
 	git__free(sfi.expected);
@@ -229,14 +311,6 @@
 	git_repository *repo,
 	const char *path)
 {
-	int error;
-	git_ignores ignores;
-
-	if (git_ignore__for_path(repo, path, &ignores) < 0)
-		return -1;
-
-	error = git_ignore__lookup(&ignores, path, ignored);
-	git_ignore__free(&ignores);
-	return error;
+	return git_ignore_path_is_ignored(ignored, repo, path);
 }
 
diff --git a/libgit2/src/strmap.h b/libgit2/src/strmap.h
--- a/libgit2/src/strmap.h
+++ b/libgit2/src/strmap.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -19,7 +19,7 @@
 typedef khash_t(str) git_strmap;
 
 #define GIT__USE_STRMAP \
-	__KHASH_IMPL(str, static inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+	__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
 
 #define git_strmap_alloc()  kh_init(str)
 #define git_strmap_free(h)  kh_destroy(str, h), h = NULL
diff --git a/libgit2/src/submodule.c b/libgit2/src/submodule.c
--- a/libgit2/src/submodule.c
+++ b/libgit2/src/submodule.c
@@ -1,387 +1,1511 @@
 /*
- * Copyright (C) 2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include "common.h"
-#include "git2/config.h"
-#include "git2/types.h"
-#include "git2/repository.h"
-#include "git2/index.h"
-#include "git2/submodule.h"
-#include "buffer.h"
-#include "vector.h"
-#include "posix.h"
-#include "config_file.h"
-#include "config.h"
-#include "repository.h"
-
-static git_cvar_map _sm_update_map[] = {
-	{GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
-	{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
-	{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE}
-};
-
-static git_cvar_map _sm_ignore_map[] = {
-	{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
-	{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
-	{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
-	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE}
-};
-
-static inline khint_t str_hash_no_trailing_slash(const char *s)
-{
-	khint_t h;
-
-	for (h = 0; *s; ++s)
-		if (s[1] || *s != '/')
-			h = (h << 5) - h + *s;
-
-	return h;
-}
-
-static inline int str_equal_no_trailing_slash(const char *a, const char *b)
-{
-	size_t alen = a ? strlen(a) : 0;
-	size_t blen = b ? strlen(b) : 0;
-
-	if (alen && a[alen] == '/')
-		alen--;
-	if (blen && b[blen] == '/')
-		blen--;
-
-	return (alen == blen && strncmp(a, b, alen) == 0);
-}
-
-__KHASH_IMPL(str, static inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash);
-
-static git_submodule *submodule_alloc(const char *name)
-{
-	git_submodule *sm = git__calloc(1, sizeof(git_submodule));
-	if (sm == NULL)
-		return sm;
-
-	sm->path = sm->name = git__strdup(name);
-	if (!sm->name) {
-		git__free(sm);
-		return NULL;
-	}
-
-	return sm;
-}
-
-static void submodule_release(git_submodule *sm, int decr)
-{
-	if (!sm)
-		return;
-
-	sm->refcount -= decr;
-
-	if (sm->refcount == 0) {
-		if (sm->name != sm->path)
-			git__free(sm->path);
-		git__free(sm->name);
-		git__free(sm->url);
-		git__free(sm);
-	}
-}
-
-static int submodule_from_entry(
-	git_strmap *smcfg, git_index_entry *entry)
-{
-	git_submodule *sm;
-	void *old_sm;
-	khiter_t pos;
-	int error;
-
-	pos = git_strmap_lookup_index(smcfg, entry->path);
-
-	if (git_strmap_valid_index(smcfg, pos))
-		sm = git_strmap_value_at(smcfg, pos);
-	else
-		sm = submodule_alloc(entry->path);
-
-	git_oid_cpy(&sm->oid, &entry->oid);
-
-	if (strcmp(sm->path, entry->path) != 0) {
-		if (sm->path != sm->name) {
-			git__free(sm->path);
-			sm->path = sm->name;
-		}
-		sm->path = git__strdup(entry->path);
-		if (!sm->path)
-			goto fail;
-	}
-
-	git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
-	if (error < 0)
-		goto fail;
-	sm->refcount++;
-
-	if (old_sm && ((git_submodule *)old_sm) != sm) {
-		/* TODO: log warning about multiple entrys for same submodule path */
-		submodule_release(old_sm, 1);
-	}
-
-	return 0;
-
-fail:
-	submodule_release(sm, 0);
-	return -1;
-}
-
-static int submodule_from_config(
-	const char *key, const char *value, void *data)
-{
-	git_strmap *smcfg = data;
-	const char *namestart;
-	const char *property;
-	git_buf name = GIT_BUF_INIT;
-	git_submodule *sm;
-	void *old_sm = NULL;
-	bool is_path;
-	khiter_t pos;
-	int error;
-
-	if (git__prefixcmp(key, "submodule.") != 0)
-		return 0;
-
-	namestart = key + strlen("submodule.");
-	property  = strrchr(namestart, '.');
-	if (property == NULL)
-		return 0;
-	property++;
-	is_path = (strcmp(property, "path") == 0);
-
-	if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
-		return -1;
-
-	pos = git_strmap_lookup_index(smcfg, name.ptr);
-	if (!git_strmap_valid_index(smcfg, pos) && is_path)
-		pos = git_strmap_lookup_index(smcfg, value);
-	if (!git_strmap_valid_index(smcfg, pos))
-		sm = submodule_alloc(name.ptr);
-	else
-		sm = git_strmap_value_at(smcfg, pos);
-	if (!sm)
-		goto fail;
-
-	if (strcmp(sm->name, name.ptr) != 0) {
-		assert(sm->path == sm->name);
-		sm->name = git_buf_detach(&name);
-
-		git_strmap_insert2(smcfg, sm->name, sm, old_sm, error);
-		if (error < 0)
-			goto fail;
-		sm->refcount++;
-	}
-	else if (is_path && strcmp(sm->path, value) != 0) {
-		assert(sm->path == sm->name);
-		sm->path = git__strdup(value);
-		if (sm->path == NULL)
-			goto fail;
-
-		git_strmap_insert2(smcfg, sm->path, sm, old_sm, error);
-		if (error < 0)
-			goto fail;
-		sm->refcount++;
-	}
-	git_buf_free(&name);
-
-	if (old_sm && ((git_submodule *)old_sm) != sm) {
-		/* TODO: log warning about multiple submodules with same path */
-		submodule_release(old_sm, 1);
-	}
-
-	if (is_path)
-		return 0;
-
-	/* copy other properties into submodule entry */
-	if (strcmp(property, "url") == 0) {
-		if (sm->url) {
-			git__free(sm->url);
-			sm->url = NULL;
-		}
-		if ((sm->url = git__strdup(value)) == NULL)
-			goto fail;
-	}
-	else if (strcmp(property, "update") == 0) {
-		int val;
-		if (git_config_lookup_map_value(
-			_sm_update_map, ARRAY_SIZE(_sm_update_map), value, &val) < 0) {
-			giterr_set(GITERR_INVALID,
-				"Invalid value for submodule update property: '%s'", value);
-			goto fail;
-		}
-		sm->update = (git_submodule_update_t)val;
-	}
-	else if (strcmp(property, "fetchRecurseSubmodules") == 0) {
-		if (git__parse_bool(&sm->fetch_recurse, value) < 0) {
-			giterr_set(GITERR_INVALID,
-				"Invalid value for submodule 'fetchRecurseSubmodules' property: '%s'", value);
-			goto fail;
-		}
-	}
-	else if (strcmp(property, "ignore") == 0) {
-		int val;
-		if (git_config_lookup_map_value(
-			_sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value, &val) < 0) {
-			giterr_set(GITERR_INVALID,
-				"Invalid value for submodule ignore property: '%s'", value);
-			goto fail;
-		}
-		sm->ignore = (git_submodule_ignore_t)val;
-	}
-	/* ignore other unknown submodule properties */
-
-	return 0;
-
-fail:
-	submodule_release(sm, 0);
-	git_buf_free(&name);
-	return -1;
-}
-
-static int load_submodule_config(git_repository *repo)
-{
-	int error;
-	git_index *index;
-	unsigned int i, max_i;
-	git_oid gitmodules_oid;
-	git_strmap *smcfg;
-	struct git_config_file *mods = NULL;
-
-	if (repo->submodules)
-		return 0;
-
-	/* submodule data is kept in a hashtable with each submodule stored
-	 * under both its name and its path.  These are usually the same, but
-	 * that is not guaranteed.
-	 */
-	smcfg = git_strmap_alloc();
-	GITERR_CHECK_ALLOC(smcfg);
-
-	/* scan index for gitmodules (and .gitmodules entry) */
-	if ((error = git_repository_index__weakptr(&index, repo)) < 0)
-		goto cleanup;
-	memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
-	max_i = git_index_entrycount(index);
-
-	for (i = 0; i < max_i; i++) {
-		git_index_entry *entry = git_index_get(index, i);
-		if (S_ISGITLINK(entry->mode)) {
-			if ((error = submodule_from_entry(smcfg, entry)) < 0)
-				goto cleanup;
-		}
-		else if (strcmp(entry->path, ".gitmodules") == 0)
-			git_oid_cpy(&gitmodules_oid, &entry->oid);
-	}
-
-	/* load .gitmodules from workdir if it exists */
-	if (git_repository_workdir(repo) != NULL) {
-		/* look in workdir for .gitmodules */
-		git_buf path = GIT_BUF_INIT;
-		if (!git_buf_joinpath(
-				&path, git_repository_workdir(repo), ".gitmodules") &&
-			git_path_isfile(path.ptr))
-		{
-			if (!(error = git_config_file__ondisk(&mods, path.ptr)))
-				error = git_config_file_open(mods);
-		}
-		git_buf_free(&path);
-	}
-
-	/* load .gitmodules from object cache if not in workdir */
-	if (!error && mods == NULL && !git_oid_iszero(&gitmodules_oid)) {
-		/* TODO: is it worth loading gitmodules from object cache? */
-	}
-
-	/* process .gitmodules info */
-	if (!error && mods != NULL)
-		error = git_config_file_foreach(mods, submodule_from_config, smcfg);
-
-	/* store submodule config in repo */
-	if (!error)
-		repo->submodules = smcfg;
-
-cleanup:
-	if (mods != NULL)
-		git_config_file_free(mods);
-	if (error)
-		git_strmap_free(smcfg);
-	return error;
-}
-
-void git_submodule_config_free(git_repository *repo)
-{
-	git_strmap *smcfg = repo->submodules;
-	git_submodule *sm;
-
-	repo->submodules = NULL;
-
-	if (smcfg == NULL)
-		return;
-
-	git_strmap_foreach_value(smcfg, sm, {
-		submodule_release(sm,1);
-	});
-	git_strmap_free(smcfg);
-}
-
-static int submodule_cmp(const void *a, const void *b)
-{
-	return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
-}
-
-int git_submodule_foreach(
-	git_repository *repo,
-	int (*callback)(const char *name, void *payload),
-	void *payload)
-{
-	int error;
-	git_submodule *sm;
-	git_vector seen = GIT_VECTOR_INIT;
-	seen._cmp = submodule_cmp;
-
-	if ((error = load_submodule_config(repo)) < 0)
-		return error;
-
-	git_strmap_foreach_value(repo->submodules, sm, {
-		/* usually the following will not come into play */
-		if (sm->refcount > 1) {
-			if (git_vector_bsearch(&seen, sm) != GIT_ENOTFOUND)
-				continue;
-			if ((error = git_vector_insert(&seen, sm)) < 0)
-				break;
-		}
-
-		if ((error = callback(sm->name, payload)) < 0)
-			break;
-	});
-
-	git_vector_free(&seen);
-
-	return error;
-}
-
-int git_submodule_lookup(
-	git_submodule **sm_ptr, /* NULL allowed if user only wants to test */
-	git_repository *repo,
-	const char *name)       /* trailing slash is allowed */
-{
-	khiter_t pos;
-
-	if (load_submodule_config(repo) < 0)
-		return -1;
-
-	pos = git_strmap_lookup_index(repo->submodules, name);
-	if (!git_strmap_valid_index(repo->submodules, pos))
-		return GIT_ENOTFOUND;
-
-	if (sm_ptr)
-		*sm_ptr = git_strmap_value_at(repo->submodules, pos);
-
-	return 0;
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/repository.h"
+#include "git2/index.h"
+#include "git2/submodule.h"
+#include "buffer.h"
+#include "buf_text.h"
+#include "vector.h"
+#include "posix.h"
+#include "config_file.h"
+#include "config.h"
+#include "repository.h"
+#include "submodule.h"
+#include "tree.h"
+#include "iterator.h"
+
+#define GIT_MODULES_FILE ".gitmodules"
+
+static git_cvar_map _sm_update_map[] = {
+	{GIT_CVAR_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+	{GIT_CVAR_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+	{GIT_CVAR_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
+	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
+};
+
+static git_cvar_map _sm_ignore_map[] = {
+	{GIT_CVAR_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
+	{GIT_CVAR_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+	{GIT_CVAR_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+	{GIT_CVAR_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+};
+
+static kh_inline khint_t str_hash_no_trailing_slash(const char *s)
+{
+	khint_t h;
+
+	for (h = 0; *s; ++s)
+		if (s[1] != '\0' || *s != '/')
+			h = (h << 5) - h + *s;
+
+	return h;
+}
+
+static kh_inline int str_equal_no_trailing_slash(const char *a, const char *b)
+{
+	size_t alen = a ? strlen(a) : 0;
+	size_t blen = b ? strlen(b) : 0;
+
+	if (alen > 0 && a[alen - 1] == '/')
+		alen--;
+	if (blen > 0 && b[blen - 1] == '/')
+		blen--;
+
+	return (alen == blen && strncmp(a, b, alen) == 0);
+}
+
+__KHASH_IMPL(
+	str, static kh_inline, const char *, void *, 1,
+	str_hash_no_trailing_slash, str_equal_no_trailing_slash);
+
+static int load_submodule_config(git_repository *repo);
+static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *);
+static int lookup_head_remote(git_buf *url, git_repository *repo);
+static int submodule_get(git_submodule **, git_repository *, const char *, const char *);
+static void submodule_release(git_submodule *sm, int decr);
+static int submodule_load_from_index(git_repository *, const git_index_entry *);
+static int submodule_load_from_head(git_repository*, const char*, const git_oid*);
+static int submodule_load_from_config(const git_config_entry *, void *);
+static int submodule_load_from_wd_lite(git_submodule *, const char *, void *);
+static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool);
+static void submodule_mode_mismatch(git_repository *, const char *, unsigned int);
+static int submodule_index_status(unsigned int *status, git_submodule *sm);
+static int submodule_wd_status(unsigned int *status, git_submodule *sm);
+
+static int submodule_cmp(const void *a, const void *b)
+{
+	return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
+
+static int submodule_config_key_trunc_puts(git_buf *key, const char *suffix)
+{
+	ssize_t idx = git_buf_rfind(key, '.');
+	git_buf_truncate(key, (size_t)(idx + 1));
+	return git_buf_puts(key, suffix);
+}
+
+/*
+ * PUBLIC APIS
+ */
+
+int git_submodule_lookup(
+	git_submodule **sm_ptr, /* NULL if user only wants to test existence */
+	git_repository *repo,
+	const char *name)       /* trailing slash is allowed */
+{
+	int error;
+	khiter_t pos;
+
+	assert(repo && name);
+
+	if ((error = load_submodule_config(repo)) < 0)
+		return error;
+
+	pos = git_strmap_lookup_index(repo->submodules, name);
+
+	if (!git_strmap_valid_index(repo->submodules, pos)) {
+		error = GIT_ENOTFOUND;
+
+		/* check if a plausible submodule exists at path */
+		if (git_repository_workdir(repo)) {
+			git_buf path = GIT_BUF_INIT;
+
+			if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0)
+				return -1;
+
+			if (git_path_contains_dir(&path, DOT_GIT))
+				error = GIT_EEXISTS;
+
+			git_buf_free(&path);
+		}
+
+		return error;
+	}
+
+	if (sm_ptr)
+		*sm_ptr = git_strmap_value_at(repo->submodules, pos);
+
+	return 0;
+}
+
+int git_submodule_foreach(
+	git_repository *repo,
+	int (*callback)(git_submodule *sm, const char *name, void *payload),
+	void *payload)
+{
+	int error;
+	git_submodule *sm;
+	git_vector seen = GIT_VECTOR_INIT;
+	seen._cmp = submodule_cmp;
+
+	assert(repo && callback);
+
+	if ((error = load_submodule_config(repo)) < 0)
+		return error;
+
+	git_strmap_foreach_value(repo->submodules, sm, {
+		/* Usually the following will not come into play - it just prevents
+		 * us from issuing a callback twice for a submodule where the name
+		 * and path are not the same.
+		 */
+		if (sm->refcount > 1) {
+			if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND)
+				continue;
+			if ((error = git_vector_insert(&seen, sm)) < 0)
+				break;
+		}
+
+		if (callback(sm, sm->name, payload)) {
+			giterr_clear();
+			error = GIT_EUSER;
+			break;
+		}
+	});
+
+	git_vector_free(&seen);
+
+	return error;
+}
+
+void git_submodule_config_free(git_repository *repo)
+{
+	git_strmap *smcfg;
+	git_submodule *sm;
+
+	assert(repo);
+
+	smcfg = repo->submodules;
+	repo->submodules = NULL;
+
+	if (smcfg == NULL)
+		return;
+
+	git_strmap_foreach_value(smcfg, sm, {
+		submodule_release(sm,1);
+	});
+	git_strmap_free(smcfg);
+}
+
+int git_submodule_add_setup(
+	git_submodule **submodule,
+	git_repository *repo,
+	const char *url,
+	const char *path,
+	int use_gitlink)
+{
+	int error = 0;
+	git_config_backend *mods = NULL;
+	git_submodule *sm;
+	git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT;
+	git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+	git_repository *subrepo = NULL;
+
+	assert(repo && url && path);
+
+	/* see if there is already an entry for this submodule */
+
+	if (git_submodule_lookup(&sm, repo, path) < 0)
+		giterr_clear();
+	else {
+		giterr_set(GITERR_SUBMODULE,
+			"Attempt to add a submodule that already exists");
+		return GIT_EEXISTS;
+	}
+
+	/* resolve parameters */
+
+	if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) {
+		if (!(error = lookup_head_remote(&real_url, repo)))
+			error = git_path_apply_relative(&real_url, url);
+	} else if (strchr(url, ':') != NULL || url[0] == '/') {
+		error = git_buf_sets(&real_url, url);
+	} else {
+		giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL");
+		error = -1;
+	}
+	if (error)
+		goto cleanup;
+
+	/* validate and normalize path */
+
+	if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
+		path += strlen(git_repository_workdir(repo));
+
+	if (git_path_root(path) >= 0) {
+		giterr_set(GITERR_SUBMODULE, "Submodule path must be a relative path");
+		error = -1;
+		goto cleanup;
+	}
+
+	/* update .gitmodules */
+
+	if ((mods = open_gitmodules(repo, true, NULL)) == NULL) {
+		giterr_set(GITERR_SUBMODULE,
+			"Adding submodules to a bare repository is not supported (for now)");
+		return -1;
+	}
+
+	if ((error = git_buf_printf(&name, "submodule.%s.path", path)) < 0 ||
+		(error = git_config_file_set_string(mods, name.ptr, path)) < 0)
+		goto cleanup;
+
+	if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
+		(error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0)
+		goto cleanup;
+
+	git_buf_clear(&name);
+
+	/* init submodule repository and add origin remote as needed */
+
+	error = git_buf_joinpath(&name, git_repository_workdir(repo), path);
+	if (error < 0)
+		goto cleanup;
+
+	/* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+	 * gitlink in the sub-repo workdir directory to that repository
+	 *
+	 * Old style: sub-repo goes directly into repo/<name>/.git/
+	 */
+
+	initopt.flags = GIT_REPOSITORY_INIT_MKPATH |
+		GIT_REPOSITORY_INIT_NO_REINIT;
+	initopt.origin_url = real_url.ptr;
+
+	if (git_path_exists(name.ptr) &&
+		git_path_contains(&name, DOT_GIT))
+	{
+		/* repo appears to already exist - reinit? */
+	}
+	else if (use_gitlink) {
+		git_buf repodir = GIT_BUF_INIT;
+
+		error = git_buf_join_n(
+			&repodir, '/', 3, git_repository_path(repo), "modules", path);
+		if (error < 0)
+			goto cleanup;
+
+		initopt.workdir_path = name.ptr;
+		initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+		error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+
+		git_buf_free(&repodir);
+	}
+	else {
+		error = git_repository_init_ext(&subrepo, name.ptr, &initopt);
+	}
+	if (error < 0)
+		goto cleanup;
+
+	/* add submodule to hash and "reload" it */
+
+	if (!(error = submodule_get(&sm, repo, path, NULL)) &&
+		!(error = git_submodule_reload(sm)))
+		error = git_submodule_init(sm, false);
+
+cleanup:
+	if (submodule != NULL)
+		*submodule = !error ? sm : NULL;
+
+	if (mods != NULL)
+		git_config_file_free(mods);
+	git_repository_free(subrepo);
+	git_buf_free(&real_url);
+	git_buf_free(&name);
+
+	return error;
+}
+
+int git_submodule_add_finalize(git_submodule *sm)
+{
+	int error;
+	git_index *index;
+
+	assert(sm);
+
+	if ((error = git_repository_index__weakptr(&index, sm->owner)) < 0 ||
+		(error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
+		return error;
+
+	return git_submodule_add_to_index(sm, true);
+}
+
+int git_submodule_add_to_index(git_submodule *sm, int write_index)
+{
+	int error;
+	git_repository *repo, *sm_repo = NULL;
+	git_index *index;
+	git_buf path = GIT_BUF_INIT;
+	git_commit *head;
+	git_index_entry entry;
+	struct stat st;
+
+	assert(sm);
+
+	repo = sm->owner;
+
+	/* force reload of wd OID by git_submodule_open */
+	sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
+
+	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+		(error = git_buf_joinpath(
+			&path, git_repository_workdir(repo), sm->path)) < 0 ||
+		(error = git_submodule_open(&sm_repo, sm)) < 0)
+		goto cleanup;
+
+	/* read stat information for submodule working directory */
+	if (p_stat(path.ptr, &st) < 0) {
+		giterr_set(GITERR_SUBMODULE,
+			"Cannot add submodule without working directory");
+		error = -1;
+		goto cleanup;
+	}
+
+	memset(&entry, 0, sizeof(entry));
+	entry.path = sm->path;
+	git_index_entry__init_from_stat(&entry, &st);
+
+	/* calling git_submodule_open will have set sm->wd_oid if possible */
+	if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
+		giterr_set(GITERR_SUBMODULE,
+			"Cannot add submodule without HEAD to index");
+		error = -1;
+		goto cleanup;
+	}
+	git_oid_cpy(&entry.oid, &sm->wd_oid);
+
+	if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
+		goto cleanup;
+
+	entry.ctime.seconds = git_commit_time(head);
+	entry.ctime.nanoseconds = 0;
+	entry.mtime.seconds = git_commit_time(head);
+	entry.mtime.nanoseconds = 0;
+
+	git_commit_free(head);
+
+	/* add it */
+	error = git_index_add(index, &entry);
+
+	/* write it, if requested */
+	if (!error && write_index) {
+		error = git_index_write(index);
+
+		if (!error)
+			git_oid_cpy(&sm->index_oid, &sm->wd_oid);
+	}
+
+cleanup:
+	git_repository_free(sm_repo);
+	git_buf_free(&path);
+	return error;
+}
+
+int git_submodule_save(git_submodule *submodule)
+{
+	int error = 0;
+	git_config_backend *mods;
+	git_buf key = GIT_BUF_INIT;
+
+	assert(submodule);
+
+	mods = open_gitmodules(submodule->owner, true, NULL);
+	if (!mods) {
+		giterr_set(GITERR_SUBMODULE,
+			"Adding submodules to a bare repository is not supported (for now)");
+		return -1;
+	}
+
+	if ((error = git_buf_printf(&key, "submodule.%s.", submodule->name)) < 0)
+		goto cleanup;
+
+	/* save values for path, url, update, ignore, fetchRecurseSubmodules */
+
+	if ((error = submodule_config_key_trunc_puts(&key, "path")) < 0 ||
+		(error = git_config_file_set_string(mods, key.ptr, submodule->path)) < 0)
+		goto cleanup;
+
+	if ((error = submodule_config_key_trunc_puts(&key, "url")) < 0 ||
+		(error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0)
+		goto cleanup;
+
+	if (!(error = submodule_config_key_trunc_puts(&key, "update")) &&
+		submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
+	{
+		const char *val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+			NULL : _sm_update_map[submodule->update].str_match;
+		error = git_config_file_set_string(mods, key.ptr, val);
+	}
+	if (error < 0)
+		goto cleanup;
+
+	if (!(error = submodule_config_key_trunc_puts(&key, "ignore")) &&
+		submodule->ignore != GIT_SUBMODULE_IGNORE_DEFAULT)
+	{
+		const char *val = (submodule->ignore == GIT_SUBMODULE_IGNORE_NONE) ?
+			NULL : _sm_ignore_map[submodule->ignore].str_match;
+		error = git_config_file_set_string(mods, key.ptr, val);
+	}
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = submodule_config_key_trunc_puts(
+			&key, "fetchRecurseSubmodules")) < 0 ||
+		(error = git_config_file_set_string(
+			mods, key.ptr, submodule->fetch_recurse ? "true" : "false")) < 0)
+		goto cleanup;
+
+	/* update internal defaults */
+
+	submodule->ignore_default = submodule->ignore;
+	submodule->update_default = submodule->update;
+	submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+
+cleanup:
+	if (mods != NULL)
+		git_config_file_free(mods);
+	git_buf_free(&key);
+
+	return error;
+}
+
+git_repository *git_submodule_owner(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->owner;
+}
+
+const char *git_submodule_name(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->name;
+}
+
+const char *git_submodule_path(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->path;
+}
+
+const char *git_submodule_url(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->url;
+}
+
+int git_submodule_set_url(git_submodule *submodule, const char *url)
+{
+	assert(submodule && url);
+
+	git__free(submodule->url);
+
+	submodule->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(submodule->url);
+
+	return 0;
+}
+
+const git_oid *git_submodule_index_id(git_submodule *submodule)
+{
+	assert(submodule);
+
+	if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
+		return &submodule->index_oid;
+	else
+		return NULL;
+}
+
+const git_oid *git_submodule_head_id(git_submodule *submodule)
+{
+	assert(submodule);
+
+	if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
+		return &submodule->head_oid;
+	else
+		return NULL;
+}
+
+const git_oid *git_submodule_wd_id(git_submodule *submodule)
+{
+	assert(submodule);
+
+	if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
+		git_repository *subrepo;
+
+		/* calling submodule open grabs the HEAD OID if possible */
+		if (!git_submodule_open(&subrepo, submodule))
+			git_repository_free(subrepo);
+		else
+			giterr_clear();
+	}
+
+	if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
+		return &submodule->wd_oid;
+	else
+		return NULL;
+}
+
+git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->ignore;
+}
+
+git_submodule_ignore_t git_submodule_set_ignore(
+	git_submodule *submodule, git_submodule_ignore_t ignore)
+{
+	git_submodule_ignore_t old;
+
+	assert(submodule);
+
+	if (ignore == GIT_SUBMODULE_IGNORE_DEFAULT)
+		ignore = submodule->ignore_default;
+
+	old = submodule->ignore;
+	submodule->ignore = ignore;
+	return old;
+}
+
+git_submodule_update_t git_submodule_update(git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->update;
+}
+
+git_submodule_update_t git_submodule_set_update(
+	git_submodule *submodule, git_submodule_update_t update)
+{
+	git_submodule_update_t old;
+
+	assert(submodule);
+
+	if (update == GIT_SUBMODULE_UPDATE_DEFAULT)
+		update = submodule->update_default;
+
+	old = submodule->update;
+	submodule->update = update;
+	return old;
+}
+
+int git_submodule_fetch_recurse_submodules(
+	git_submodule *submodule)
+{
+	assert(submodule);
+	return submodule->fetch_recurse;
+}
+
+int git_submodule_set_fetch_recurse_submodules(
+	git_submodule *submodule,
+	int fetch_recurse_submodules)
+{
+	int old;
+
+	assert(submodule);
+
+	old = submodule->fetch_recurse;
+	submodule->fetch_recurse = (fetch_recurse_submodules != 0);
+	return old;
+}
+
+int git_submodule_init(git_submodule *submodule, int overwrite)
+{
+	int error;
+
+	/* write "submodule.NAME.url" */
+
+	if (!submodule->url) {
+		giterr_set(GITERR_SUBMODULE,
+			"No URL configured for submodule '%s'", submodule->name);
+		return -1;
+	}
+
+	error = submodule_update_config(
+		submodule, "url", submodule->url, overwrite != 0, false);
+	if (error < 0)
+		return error;
+
+	/* write "submodule.NAME.update" if not default */
+
+	if (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT)
+		error = submodule_update_config(
+			submodule, "update", NULL, (overwrite != 0), false);
+	else if (submodule->update != GIT_SUBMODULE_UPDATE_DEFAULT)
+		error = submodule_update_config(
+			submodule, "update",
+			_sm_update_map[submodule->update].str_match,
+			(overwrite != 0), false);
+
+	return error;
+}
+
+int git_submodule_sync(git_submodule *submodule)
+{
+	if (!submodule->url) {
+		giterr_set(GITERR_SUBMODULE,
+			"No URL configured for submodule '%s'", submodule->name);
+		return -1;
+	}
+
+	/* copy URL over to config only if it already exists */
+
+	return submodule_update_config(
+		submodule, "url", submodule->url, true, true);
+}
+
+int git_submodule_open(
+	git_repository **subrepo,
+	git_submodule *submodule)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+	git_repository *repo;
+	const char *workdir;
+
+	assert(submodule && subrepo);
+
+	repo = submodule->owner;
+	workdir = git_repository_workdir(repo);
+
+	if (!workdir) {
+		giterr_set(GITERR_REPOSITORY,
+			"Cannot open submodule repository in a bare repo");
+		return GIT_ENOTFOUND;
+	}
+
+	if ((submodule->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0) {
+		giterr_set(GITERR_REPOSITORY,
+			"Cannot open submodule repository that is not checked out");
+		return GIT_ENOTFOUND;
+	}
+
+	if (git_buf_joinpath(&path, workdir, submodule->path) < 0)
+		return -1;
+
+	error = git_repository_open(subrepo, path.ptr);
+
+	git_buf_free(&path);
+
+	/* if we have opened the submodule successfully, let's grab the HEAD OID */
+	if (!error) {
+		if (!git_reference_name_to_id(
+				&submodule->wd_oid, *subrepo, GIT_HEAD_FILE))
+			submodule->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
+		else
+			giterr_clear();
+	}
+
+	return error;
+}
+
+int git_submodule_reload_all(git_repository *repo)
+{
+	assert(repo);
+	git_submodule_config_free(repo);
+	return load_submodule_config(repo);
+}
+
+int git_submodule_reload(git_submodule *submodule)
+{
+	git_repository *repo;
+	git_index *index;
+	int error;
+	size_t pos;
+	git_tree *head;
+	git_config_backend *mods;
+
+	assert(submodule);
+
+	/* refresh index data */
+
+	repo = submodule->owner;
+	if (git_repository_index__weakptr(&index, repo) < 0)
+		return -1;
+
+	submodule->flags = submodule->flags &
+		~(GIT_SUBMODULE_STATUS_IN_INDEX |
+		  GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
+
+	if (!git_index_find(&pos, index, submodule->path)) {
+		const git_index_entry *entry = git_index_get_byindex(index, pos);
+
+		if (S_ISGITLINK(entry->mode)) {
+			if ((error = submodule_load_from_index(repo, entry)) < 0)
+				return error;
+		} else {
+			submodule_mode_mismatch(
+				repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
+		}
+	}
+
+	/* refresh HEAD tree data */
+
+	if (!(error = git_repository_head_tree(&head, repo))) {
+		git_tree_entry *te;
+
+		submodule->flags = submodule->flags &
+			~(GIT_SUBMODULE_STATUS_IN_HEAD |
+			  GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
+
+		if (!(error = git_tree_entry_bypath(&te, head, submodule->path))) {
+
+			if (S_ISGITLINK(te->attr)) {
+				error = submodule_load_from_head(repo, submodule->path, &te->oid);
+			} else {
+				submodule_mode_mismatch(
+					repo, submodule->path,
+					GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
+			}
+
+			git_tree_entry_free(te);
+		}
+		else if (error == GIT_ENOTFOUND) {
+			giterr_clear();
+			error = 0;
+		}
+
+		git_tree_free(head);
+	}
+
+	if (error < 0)
+		return error;
+
+	/* refresh config data */
+
+	if ((mods = open_gitmodules(repo, false, NULL)) != NULL) {
+		git_buf path = GIT_BUF_INIT;
+
+		git_buf_sets(&path, "submodule\\.");
+		git_buf_text_puts_escape_regex(&path, submodule->name);
+		git_buf_puts(&path, ".*");
+
+		if (git_buf_oom(&path))
+			error = -1;
+		else
+			error = git_config_file_foreach_match(
+				mods, path.ptr, submodule_load_from_config, repo);
+
+		git_buf_free(&path);
+		git_config_file_free(mods);
+	}
+
+	if (error < 0)
+		return error;
+
+	/* refresh wd data */
+
+	submodule->flags = submodule->flags &
+		~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID);
+
+	error = submodule_load_from_wd_lite(submodule, submodule->path, NULL);
+
+	return error;
+}
+
+int git_submodule_status(
+	unsigned int *status,
+	git_submodule *submodule)
+{
+	int error = 0;
+	unsigned int status_val;
+
+	assert(status && submodule);
+
+	status_val = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(submodule->flags);
+
+	if (submodule->ignore != GIT_SUBMODULE_IGNORE_ALL) {
+		if (!(error = submodule_index_status(&status_val, submodule)))
+			error = submodule_wd_status(&status_val, submodule);
+	}
+
+	*status = status_val;
+
+	return error;
+}
+
+int git_submodule_location(
+	unsigned int *location_status,
+	git_submodule *submodule)
+{
+	assert(location_status && submodule);
+
+	*location_status = submodule->flags &
+		(GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX |
+		 GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD);
+
+	return 0;
+}
+
+
+/*
+ * INTERNAL FUNCTIONS
+ */
+
+static git_submodule *submodule_alloc(git_repository *repo, const char *name)
+{
+	git_submodule *sm;
+
+	if (!name || !strlen(name)) {
+		giterr_set(GITERR_SUBMODULE, "Invalid submodule name");
+		return NULL;
+	}
+
+	sm = git__calloc(1, sizeof(git_submodule));
+	if (sm == NULL)
+		goto fail;
+
+	sm->path = sm->name = git__strdup(name);
+	if (!sm->name)
+		goto fail;
+
+	sm->owner = repo;
+	sm->refcount = 1;
+
+	return sm;
+
+fail:
+	submodule_release(sm, 0);
+	return NULL;
+}
+
+static void submodule_release(git_submodule *sm, int decr)
+{
+	if (!sm)
+		return;
+
+	sm->refcount -= decr;
+
+	if (sm->refcount == 0) {
+		if (sm->name != sm->path) {
+			git__free(sm->path);
+			sm->path = NULL;
+		}
+
+		git__free(sm->name);
+		sm->name = NULL;
+
+		git__free(sm->url);
+		sm->url = NULL;
+
+		sm->owner = NULL;
+
+		git__free(sm);
+	}
+}
+
+static int submodule_get(
+	git_submodule **sm_ptr,
+	git_repository *repo,
+	const char *name,
+	const char *alternate)
+{
+	git_strmap *smcfg = repo->submodules;
+	khiter_t pos;
+	git_submodule *sm;
+	int error;
+
+	assert(repo && name);
+
+	pos = git_strmap_lookup_index(smcfg, name);
+
+	if (!git_strmap_valid_index(smcfg, pos) && alternate)
+		pos = git_strmap_lookup_index(smcfg, alternate);
+
+	if (!git_strmap_valid_index(smcfg, pos)) {
+		sm = submodule_alloc(repo, name);
+
+		/* insert value at name - if another thread beats us to it, then use
+		 * their record and release our own.
+		 */
+		pos = kh_put(str, smcfg, sm->name, &error);
+
+		if (error < 0) {
+			submodule_release(sm, 1);
+			sm = NULL;
+		} else if (error == 0) {
+			submodule_release(sm, 1);
+			sm = git_strmap_value_at(smcfg, pos);
+		} else {
+			git_strmap_set_value_at(smcfg, pos, sm);
+		}
+	} else {
+		sm = git_strmap_value_at(smcfg, pos);
+	}
+
+	*sm_ptr = sm;
+
+	return (sm != NULL) ? 0 : -1;
+}
+
+static int submodule_load_from_index(
+	git_repository *repo, const git_index_entry *entry)
+{
+	git_submodule *sm;
+
+	if (submodule_get(&sm, repo, entry->path, NULL) < 0)
+		return -1;
+
+	if (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) {
+		sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
+		return 0;
+	}
+
+	sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX;
+
+	git_oid_cpy(&sm->index_oid, &entry->oid);
+	sm->flags |= GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
+
+	return 0;
+}
+
+static int submodule_load_from_head(
+	git_repository *repo, const char *path, const git_oid *oid)
+{
+	git_submodule *sm;
+
+	if (submodule_get(&sm, repo, path, NULL) < 0)
+		return -1;
+
+	sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD;
+
+	git_oid_cpy(&sm->head_oid, oid);
+	sm->flags |= GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
+
+	return 0;
+}
+
+static int submodule_config_error(const char *property, const char *value)
+{
+	giterr_set(GITERR_INVALID,
+		"Invalid value for submodule '%s' property: '%s'", property, value);
+	return -1;
+}
+
+static int submodule_load_from_config(
+	const git_config_entry *entry, void *data)
+{
+	git_repository *repo = data;
+	git_strmap *smcfg = repo->submodules;
+	const char *namestart, *property, *alternate = NULL;
+	const char *key = entry->name, *value = entry->value;
+	git_buf name = GIT_BUF_INIT;
+	git_submodule *sm;
+	bool is_path;
+	int error = 0;
+
+	if (git__prefixcmp(key, "submodule.") != 0)
+		return 0;
+
+	namestart = key + strlen("submodule.");
+	property  = strrchr(namestart, '.');
+	if (property == NULL)
+		return 0;
+	property++;
+	is_path = (strcasecmp(property, "path") == 0);
+
+	if (git_buf_set(&name, namestart, property - namestart - 1) < 0)
+		return -1;
+
+	if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) {
+		git_buf_free(&name);
+		return -1;
+	}
+
+	sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+
+	/* Only from config might we get differing names & paths.  If so, then
+	 * update the submodule and insert under the alternative key.
+	 */
+
+	/* TODO: if case insensitive filesystem, then the following strcmps
+	 * should be strcasecmp
+	 */
+
+	if (strcmp(sm->name, name.ptr) != 0) {
+		alternate = sm->name = git_buf_detach(&name);
+	} else if (is_path && value && strcmp(sm->path, value) != 0) {
+		alternate = sm->path = git__strdup(value);
+		if (!sm->path)
+			error = -1;
+	}
+	if (alternate) {
+		void *old_sm = NULL;
+		git_strmap_insert2(smcfg, alternate, sm, old_sm, error);
+
+		if (error >= 0)
+			sm->refcount++; /* inserted under a new key */
+
+		/* if we replaced an old module under this key, release the old one */
+		if (old_sm && ((git_submodule *)old_sm) != sm) {
+			submodule_release(old_sm, 1);
+			/* TODO: log warning about multiple submodules with same path */
+		}
+	}
+
+	git_buf_free(&name);
+	if (error < 0)
+		return error;
+
+	/* TODO: Look up path in index and if it is present but not a GITLINK
+	 * then this should be deleted (at least to match git's behavior)
+	 */
+
+	if (is_path)
+		return 0;
+
+	/* copy other properties into submodule entry */
+	if (strcasecmp(property, "url") == 0) {
+		git__free(sm->url);
+		sm->url = NULL;
+
+		if (value != NULL && (sm->url = git__strdup(value)) == NULL)
+			return -1;
+	}
+	else if (strcasecmp(property, "update") == 0) {
+		int val;
+		if (git_config_lookup_map_value(
+			&val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0)
+			return submodule_config_error("update", value);
+		sm->update_default = sm->update = (git_submodule_update_t)val;
+	}
+	else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) {
+		if (git__parse_bool(&sm->fetch_recurse, value) < 0)
+			return submodule_config_error("fetchRecurseSubmodules", value);
+	}
+	else if (strcasecmp(property, "ignore") == 0) {
+		int val;
+		if (git_config_lookup_map_value(
+			&val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0)
+			return submodule_config_error("ignore", value);
+		sm->ignore_default = sm->ignore = (git_submodule_ignore_t)val;
+	}
+	/* ignore other unknown submodule properties */
+
+	return 0;
+}
+
+static int submodule_load_from_wd_lite(
+	git_submodule *sm, const char *name, void *payload)
+{
+	git_repository *repo = git_submodule_owner(sm);
+	git_buf path = GIT_BUF_INIT;
+
+	GIT_UNUSED(name);
+	GIT_UNUSED(payload);
+
+	if (git_buf_joinpath(&path, git_repository_workdir(repo), sm->path) < 0)
+		return -1;
+
+	if (git_path_isdir(path.ptr))
+		sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+	if (git_path_contains(&path, DOT_GIT))
+		sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
+
+	git_buf_free(&path);
+
+	return 0;
+}
+
+static void submodule_mode_mismatch(
+	git_repository *repo, const char *path, unsigned int flag)
+{
+	khiter_t pos = git_strmap_lookup_index(repo->submodules, path);
+
+	if (git_strmap_valid_index(repo->submodules, pos)) {
+		git_submodule *sm = git_strmap_value_at(repo->submodules, pos);
+
+		sm->flags |= flag;
+	}
+}
+
+static int load_submodule_config_from_index(
+	git_repository *repo, git_oid *gitmodules_oid)
+{
+	int error;
+	git_index *index;
+	git_iterator *i;
+	const git_index_entry *entry;
+
+	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+		(error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0)
+		return error;
+
+	error = git_iterator_current(&entry, i);
+
+	while (!error && entry != NULL) {
+
+		if (S_ISGITLINK(entry->mode)) {
+			error = submodule_load_from_index(repo, entry);
+			if (error < 0)
+				break;
+		} else {
+			submodule_mode_mismatch(
+				repo, entry->path, GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE);
+
+			if (strcmp(entry->path, GIT_MODULES_FILE) == 0)
+				git_oid_cpy(gitmodules_oid, &entry->oid);
+		}
+
+		error = git_iterator_advance(&entry, i);
+	}
+
+	git_iterator_free(i);
+
+	return error;
+}
+
+static int load_submodule_config_from_head(
+	git_repository *repo, git_oid *gitmodules_oid)
+{
+	int error;
+	git_tree *head;
+	git_iterator *i;
+	const git_index_entry *entry;
+
+	if ((error = git_repository_head_tree(&head, repo)) < 0)
+		return error;
+
+	if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) {
+		git_tree_free(head);
+		return error;
+	}
+
+	error = git_iterator_current(&entry, i);
+
+	while (!error && entry != NULL) {
+
+		if (S_ISGITLINK(entry->mode)) {
+			error = submodule_load_from_head(repo, entry->path, &entry->oid);
+			if (error < 0)
+				break;
+		} else {
+			submodule_mode_mismatch(
+				repo, entry->path, GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE);
+
+			if (strcmp(entry->path, GIT_MODULES_FILE) == 0 &&
+				git_oid_iszero(gitmodules_oid))
+				git_oid_cpy(gitmodules_oid, &entry->oid);
+		}
+
+		error = git_iterator_advance(&entry, i);
+	}
+
+	git_iterator_free(i);
+	git_tree_free(head);
+
+	return error;
+}
+
+static git_config_backend *open_gitmodules(
+	git_repository *repo,
+	bool okay_to_create,
+	const git_oid *gitmodules_oid)
+{
+	const char *workdir = git_repository_workdir(repo);
+	git_buf path = GIT_BUF_INIT;
+	git_config_backend *mods = NULL;
+
+	if (workdir != NULL) {
+		if (git_buf_joinpath(&path, workdir, GIT_MODULES_FILE) != 0)
+			return NULL;
+
+		if (okay_to_create || git_path_isfile(path.ptr)) {
+			/* git_config_file__ondisk should only fail if OOM */
+			if (git_config_file__ondisk(&mods, path.ptr) < 0)
+				mods = NULL;
+			/* open should only fail here if the file is malformed */
+			else if (git_config_file_open(mods, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+				git_config_file_free(mods);
+				mods = NULL;
+			}
+		}
+	}
+
+	if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) {
+		/* TODO: Retrieve .gitmodules content from ODB */
+
+		/* Should we actually do this?  Core git does not, but it means you
+		 * can't really get much information about submodules on bare repos.
+		 */
+	}
+
+	git_buf_free(&path);
+
+	return mods;
+}
+
+static int load_submodule_config(git_repository *repo)
+{
+	int error;
+	git_oid gitmodules_oid;
+	git_buf path = GIT_BUF_INIT;
+	git_config_backend *mods = NULL;
+
+	if (repo->submodules)
+		return 0;
+
+	memset(&gitmodules_oid, 0, sizeof(gitmodules_oid));
+
+	/* Submodule data is kept in a hashtable keyed by both name and path.
+	 * These are usually the same, but that is not guaranteed.
+	 */
+	if (!repo->submodules) {
+		repo->submodules = git_strmap_alloc();
+		GITERR_CHECK_ALLOC(repo->submodules);
+	}
+
+	/* add submodule information from index */
+
+	if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0)
+		goto cleanup;
+
+	/* add submodule information from HEAD */
+
+	if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0)
+		goto cleanup;
+
+	/* add submodule information from .gitmodules */
+
+	if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL)
+		error = git_config_file_foreach(mods, submodule_load_from_config, repo);
+
+	if (error != 0)
+		goto cleanup;
+
+	/* shallow scan submodules in work tree */
+
+	if (!git_repository_is_bare(repo))
+		error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL);
+
+cleanup:
+	git_buf_free(&path);
+
+	if (mods != NULL)
+		git_config_file_free(mods);
+
+	if (error)
+		git_submodule_config_free(repo);
+
+	return error;
+}
+
+static int lookup_head_remote(git_buf *url, git_repository *repo)
+{
+	int error;
+	git_config *cfg;
+	git_reference *head = NULL, *remote = NULL;
+	const char *tgt, *scan;
+	git_buf key = GIT_BUF_INIT;
+
+	/* 1. resolve HEAD -> refs/heads/BRANCH
+	 * 2. lookup config branch.BRANCH.remote -> ORIGIN
+	 * 3. lookup remote.ORIGIN.url
+	 */
+
+	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+		return error;
+
+	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
+		giterr_set(GITERR_SUBMODULE,
+			"Cannot resolve relative URL when HEAD cannot be resolved");
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
+		giterr_set(GITERR_SUBMODULE,
+			"Cannot resolve relative URL when HEAD is not symbolic");
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	if ((error = git_branch_upstream(&remote, head)) < 0)
+		goto cleanup;
+
+	/* remote should refer to something like refs/remotes/ORIGIN/BRANCH */
+
+	if (git_reference_type(remote) != GIT_REF_SYMBOLIC ||
+		git__prefixcmp(git_reference_symbolic_target(remote), GIT_REFS_REMOTES_DIR) != 0)
+	{
+		giterr_set(GITERR_SUBMODULE,
+			"Cannot resolve relative URL when HEAD is not symbolic");
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	scan = tgt = git_reference_symbolic_target(remote) + strlen(GIT_REFS_REMOTES_DIR);
+	while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\')))
+		scan++; /* find non-escaped slash to end ORIGIN name */
+
+	error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt);
+	if (error < 0)
+		goto cleanup;
+
+	if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0)
+		goto cleanup;
+
+	error = git_buf_sets(url, tgt);
+
+cleanup:
+	git_buf_free(&key);
+	git_reference_free(head);
+	git_reference_free(remote);
+
+	return error;
+}
+
+static int submodule_update_config(
+	git_submodule *submodule,
+	const char *attr,
+	const char *value,
+	bool overwrite,
+	bool only_existing)
+{
+	int error;
+	git_config *config;
+	git_buf key = GIT_BUF_INIT;
+	const char *old = NULL;
+
+	assert(submodule);
+
+	error = git_repository_config__weakptr(&config, submodule->owner);
+	if (error < 0)
+		return error;
+
+	error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr);
+	if (error < 0)
+		goto cleanup;
+
+	if (git_config_get_string(&old, config, key.ptr) < 0)
+		giterr_clear();
+
+	if (!old && only_existing)
+		goto cleanup;
+	if (old && !overwrite)
+		goto cleanup;
+	if ((!old && !value) || (old && value && strcmp(old, value) == 0))
+		goto cleanup;
+
+	if (!value)
+		error = git_config_delete_entry(config, key.ptr);
+	else
+		error = git_config_set_string(config, key.ptr, value);
+
+cleanup:
+	git_buf_free(&key);
+	return error;
+}
+
+static int submodule_index_status(unsigned int *status, git_submodule *sm)
+{
+	const git_oid *head_oid  = git_submodule_head_id(sm);
+	const git_oid *index_oid = git_submodule_index_id(sm);
+
+	if (!head_oid) {
+		if (index_oid)
+			*status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
+	}
+	else if (!index_oid)
+		*status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
+	else if (!git_oid_equal(head_oid, index_oid))
+		*status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
+
+	return 0;
+}
+
+static int submodule_wd_status(unsigned int *status, git_submodule *sm)
+{
+	int error = 0;
+	const git_oid *wd_oid, *index_oid;
+	git_repository *sm_repo = NULL;
+
+	/* open repo now if we need it (so wd_id() call won't reopen) */
+	if ((sm->ignore == GIT_SUBMODULE_IGNORE_NONE ||
+		 sm->ignore == GIT_SUBMODULE_IGNORE_UNTRACKED) &&
+		(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0)
+	{
+		if ((error = git_submodule_open(&sm_repo, sm)) < 0)
+			return error;
+	}
+
+	index_oid = git_submodule_index_id(sm);
+	wd_oid    = git_submodule_wd_id(sm);
+
+	if (!index_oid) {
+		if (wd_oid)
+			*status |= GIT_SUBMODULE_STATUS_WD_ADDED;
+	}
+	else if (!wd_oid) {
+		if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
+			(sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
+			*status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
+		else
+			*status |= GIT_SUBMODULE_STATUS_WD_DELETED;
+	}
+	else if (!git_oid_equal(index_oid, wd_oid))
+		*status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
+
+	if (sm_repo != NULL) {
+		git_tree *sm_head;
+		git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+		git_diff_list *diff;
+
+		/* the diffs below could be optimized with an early termination
+		 * option to the git_diff functions, but for now this is sufficient
+		 * (and certainly no worse that what core git does).
+		 */
+
+		/* perform head-to-index diff on submodule */
+
+		if ((error = git_repository_head_tree(&sm_head, sm_repo)) < 0)
+			return error;
+
+		if (sm->ignore == GIT_SUBMODULE_IGNORE_NONE)
+			opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+		error = git_diff_tree_to_index(&diff, sm_repo, sm_head, NULL, &opt);
+
+		if (!error) {
+			if (git_diff_num_deltas(diff) > 0)
+				*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
+
+			git_diff_list_free(diff);
+			diff = NULL;
+		}
+
+		git_tree_free(sm_head);
+
+		if (error < 0)
+			return error;
+
+		/* perform index-to-workdir diff on submodule */
+
+		error = git_diff_index_to_workdir(&diff, sm_repo, NULL, &opt);
+
+		if (!error) {
+			size_t untracked =
+				git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
+
+			if (untracked > 0)
+				*status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
+
+			if (git_diff_num_deltas(diff) != untracked)
+				*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
+
+			git_diff_list_free(diff);
+			diff = NULL;
+		}
+
+		git_repository_free(sm_repo);
+	}
+
+	return error;
 }
diff --git a/libgit2/src/submodule.h b/libgit2/src/submodule.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/submodule.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_submodule_h__
+#define INCLUDE_submodule_h__
+
+/* Notes:
+ *
+ * Submodule information can be in four places: the index, the config files
+ * (both .git/config and .gitmodules), the HEAD tree, and the working
+ * directory.
+ *
+ * In the index:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the HEAD tree:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the config files:
+ * - submodule is found by submodule "name" which is usually the path
+ * - may be missing or present
+ * - will have a name, path, url, and other properties
+ *
+ * In the working directory:
+ * - submodule is found by path
+ * - may be missing, an empty directory, a checked out directory,
+ *   or of the wrong type
+ * - if checked out, will have a HEAD oid
+ * - if checked out, will have git history that can be used to compare oids
+ * - if checked out, may have modified files and/or untracked files
+ */
+
+/**
+ * Description of submodule
+ *
+ * This record describes a submodule found in a repository.  There should be
+ * an entry for every submodule found in the HEAD and index, and for every
+ * submodule described in .gitmodules.  The fields are as follows:
+ *
+ * - `owner` is the git_repository containing this submodule
+ * - `name` is the name of the submodule from .gitmodules.
+ * - `path` is the path to the submodule from the repo root.  It is almost
+ *    always the same as `name`.
+ * - `url` is the url for the submodule.
+ * - `tree_oid` is the SHA1 for the submodule path in the repo HEAD.
+ * - `index_oid` is the SHA1 for the submodule recorded in the index.
+ * - `workdir_oid` is the SHA1 for the HEAD of the checked out submodule.
+ * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
+ * - `refcount` tracks how many hashmap entries there are for this submodule.
+ *   It only comes into play if the name and path of the submodule differ.
+ * - `flags` is for internal use, tracking where this submodule has been
+ *   found (head, index, config, workdir) and other misc info about it.
+ *
+ * If the submodule has been added to .gitmodules but not yet git added,
+ * then the `index_oid` will be valid and zero.  If the submodule has been
+ * deleted, but the delete has not been committed yet, then the `index_oid`
+ * will be set, but the `url` will be NULL.
+ */
+struct git_submodule {
+	git_repository *owner;
+	char *name;
+	char *path; /* important: may point to same string data as "name" */
+	char *url;
+	uint32_t flags;
+	git_oid head_oid;
+	git_oid index_oid;
+	git_oid wd_oid;
+	/* information from config */
+	git_submodule_update_t update;
+	git_submodule_update_t update_default;
+	git_submodule_ignore_t ignore;
+	git_submodule_ignore_t ignore_default;
+	int fetch_recurse;
+	/* internal information */
+	int refcount;
+};
+
+/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
+enum {
+	GIT_SUBMODULE_STATUS__WD_SCANNED          = (1u << 20),
+	GIT_SUBMODULE_STATUS__HEAD_OID_VALID      = (1u << 21),
+	GIT_SUBMODULE_STATUS__INDEX_OID_VALID     = (1u << 22),
+	GIT_SUBMODULE_STATUS__WD_OID_VALID        = (1u << 23),
+	GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE  = (1u << 24),
+	GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25),
+	GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE    = (1u << 26),
+	GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27),
+};
+
+#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
+	((S) & ~(0xFFFFFFFFu << 20))
+
+#endif
diff --git a/libgit2/src/tag.c b/libgit2/src/tag.c
--- a/libgit2/src/tag.c
+++ b/libgit2/src/tag.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -22,41 +22,41 @@
 	git__free(tag);
 }
 
-const git_oid *git_tag_id(git_tag *c)
+const git_oid *git_tag_id(const git_tag *c)
 {
-	return git_object_id((git_object *)c);
+	return git_object_id((const git_object *)c);
 }
 
-int git_tag_target(git_object **target, git_tag *t)
+int git_tag_target(git_object **target, const git_tag *t)
 {
 	assert(t);
 	return git_object_lookup(target, t->object.repo, &t->target, t->type);
 }
 
-const git_oid *git_tag_target_oid(git_tag *t)
+const git_oid *git_tag_target_id(const git_tag *t)
 {
 	assert(t);
 	return &t->target;
 }
 
-git_otype git_tag_type(git_tag *t)
+git_otype git_tag_target_type(const git_tag *t)
 {
 	assert(t);
 	return t->type;
 }
 
-const char *git_tag_name(git_tag *t)
+const char *git_tag_name(const git_tag *t)
 {
 	assert(t);
 	return t->tag_name;
 }
 
-const git_signature *git_tag_tagger(git_tag *t)
+const git_signature *git_tag_tagger(const git_tag *t)
 {
 	return t->tagger;
 }
 
-const char *git_tag_message(git_tag *t)
+const char *git_tag_message(const git_tag *t)
 {
 	assert(t);
 	return t->message;
@@ -131,7 +131,7 @@
 	buffer = search + 1;
 
 	tag->tagger = NULL;
-	if (*buffer != '\n') {
+	if (buffer < buffer_end && *buffer != '\n') {
 		tag->tagger = git__malloc(sizeof(git_signature));
 		GITERR_CHECK_ALLOC(tag->tagger);
 
@@ -139,16 +139,19 @@
 			return -1;
 	}
 
-	if( *buffer != '\n' )
-		return tag_error("No new line before message");
+	tag->message = NULL;
+	if (buffer < buffer_end) {
+		if( *buffer != '\n' )
+			return tag_error("No new line before message");
 
-	text_len = buffer_end - ++buffer;
+		text_len = buffer_end - ++buffer;
 
-	tag->message = git__malloc(text_len + 1);
-	GITERR_CHECK_ALLOC(tag->message);
+		tag->message = git__malloc(text_len + 1);
+		GITERR_CHECK_ALLOC(tag->message);
 
-	memcpy(tag->message, buffer, text_len);
-	tag->message[text_len] = '\0';
+		memcpy(tag->message, buffer, text_len);
+		tag->message[text_len] = '\0';
+	}
 
 	return 0;
 }
@@ -185,7 +188,7 @@
 	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
 		return -1;
 
-	return git_reference_name_to_oid(oid, repo, ref_name_out->ptr);
+	return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
 }
 
 static int write_tag_annotation(
@@ -196,7 +199,7 @@
 		const git_signature *tagger,
 		const char *message)
 {
-	git_buf tag = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
+	git_buf tag = GIT_BUF_INIT;
 	git_odb *odb;
 
 	git_oid__writebuf(&tag, "object ", git_object_id(target));
@@ -205,15 +208,9 @@
 	git_signature__writebuf(&tag, "tagger ", tagger);
 	git_buf_putc(&tag, '\n');
 
-	/* Remove comments by default */
-	if (git_message_prettify(&cleaned_message, message, 1) < 0)
-		goto on_error;
-
-	if (git_buf_puts(&tag, git_buf_cstr(&cleaned_message)) < 0)
+	if (git_buf_puts(&tag, message) < 0)
 		goto on_error;
 
-	git_buf_free(&cleaned_message);
-
 	if (git_repository_odb__weakptr(&odb, repo) < 0)
 		goto on_error;
 
@@ -225,7 +222,6 @@
 
 on_error:
 	git_buf_free(&tag);
-	git_buf_free(&cleaned_message);
 	giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
 	return -1;
 }
@@ -255,7 +251,7 @@
 
 	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
 	if (error < 0 && error != GIT_ENOTFOUND)
-		return -1;
+		goto cleanup;
 
 	/** Ensure the tag name doesn't conflict with an already existing
 	 *	reference unless overwriting has explictly been requested **/
@@ -271,8 +267,9 @@
 	} else
 		git_oid_cpy(oid, git_object_id(target));
 
-	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
+	error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
 
+cleanup:
 	git_reference_free(new_ref);
 	git_buf_free(&ref_name);
 	return error;
@@ -362,7 +359,7 @@
 		return -1;
 	}
 
-	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
+	error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
 
 	git_reference_free(new_ref);
 	git_buf_free(&ref_name);
@@ -379,18 +376,21 @@
 
 int git_tag_delete(git_repository *repo, const char *tag_name)
 {
-	int error;
 	git_reference *tag_ref;
 	git_buf ref_name = GIT_BUF_INIT;
+	int error;
 
 	error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
 
 	git_buf_free(&ref_name);
 
 	if (error < 0)
-		return -1;
+		return error;
 
-	return git_reference_delete(tag_ref);
+	if ((error = git_reference_delete(tag_ref)) == 0)
+		git_reference_free(tag_ref);
+	
+	return error;
 }
 
 int git_tag__parse(git_tag *tag, git_odb_object *obj)
@@ -400,22 +400,52 @@
 }
 
 typedef struct {
- git_vector *taglist;
- const char *pattern;
+	git_repository *repo;
+	git_tag_foreach_cb cb;
+	void *cb_data;
+} tag_cb_data;
+
+static int tags_cb(const char *ref, void *data)
+{
+	git_oid oid;
+	tag_cb_data *d = (tag_cb_data *)data;
+
+	if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
+		return 0; /* no tag */
+
+	if (git_reference_name_to_id(&oid, d->repo, ref) < 0)
+		return -1;
+
+	return d->cb(ref, &oid, d->cb_data);
+}
+
+int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
+{
+	tag_cb_data data;
+
+	assert(repo && cb);
+
+	data.cb = cb;
+	data.cb_data = cb_data;
+	data.repo = repo;
+
+	return git_reference_foreach(repo, GIT_REF_OID, &tags_cb, &data);
+}
+
+typedef struct {
+	git_vector *taglist;
+	const char *pattern;
 } tag_filter_data;
 
 #define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
 
-static int tag_list_cb(const char *tag_name, void *payload)
+static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
 {
-	tag_filter_data *filter;
-
-	if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)
-		return 0;
+	tag_filter_data *filter = (tag_filter_data *)data;
+	GIT_UNUSED(oid);
 
-	filter = (tag_filter_data *)payload;
 	if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
-		return git_vector_insert(filter->taglist, git__strdup(tag_name));
+		return git_vector_insert(filter->taglist, git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN));
 
 	return 0;
 }
@@ -434,7 +464,7 @@
 	filter.taglist = &taglist;
 	filter.pattern = pattern;
 
-	error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);
+	error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
 	if (error < 0) {
 		git_vector_free(&taglist);
 		return -1;
@@ -450,22 +480,7 @@
 	return git_tag_list_match(tag_names, "", repo);
 }
 
-int git_tag_peel(git_object **tag_target, git_tag *tag)
+int git_tag_peel(git_object **tag_target, const git_tag *tag)
 {
-	int error;
-	git_object *target;
-
-	assert(tag_target && tag);
-
-	if (git_tag_target(&target, tag) < 0)
-		return -1;
-
-	if (git_object_type(target) == GIT_OBJ_TAG) {
-		error = git_tag_peel(tag_target, (git_tag *)target);
-		git_object_free(target);
-		return error;
-	}
-
-	*tag_target = target;
-	return 0;
+	return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJ_ANY);
 }
diff --git a/libgit2/src/tag.h b/libgit2/src/tag.h
--- a/libgit2/src/tag.h
+++ b/libgit2/src/tag.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/thread-utils.c b/libgit2/src/thread-utils.c
--- a/libgit2/src/thread-utils.c
+++ b/libgit2/src/thread-utils.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/thread-utils.h b/libgit2/src/thread-utils.h
--- a/libgit2/src/thread-utils.h
+++ b/libgit2/src/thread-utils.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -38,13 +38,13 @@
 #define git_mutex_unlock(a) pthread_mutex_unlock(a)
 #define git_mutex_free(a)	pthread_mutex_destroy(a)
 
-/* Pthreads condition vars -- disabled by now */
-#define git_cond unsigned int //pthread_cond_t
-#define git_cond_init(c, a)	(void)0		//pthread_cond_init(c, a)
-#define git_cond_free(c) (void)0		//pthread_cond_destroy(c)
-#define git_cond_wait(c, l)	(void)0		//pthread_cond_wait(c, l)
-#define git_cond_signal(c)	(void)0		//pthread_cond_signal(c)
-#define git_cond_broadcast(c) (void)0	//pthread_cond_broadcast(c)
+/* Pthreads condition vars */
+#define git_cond pthread_cond_t
+#define git_cond_init(c)	pthread_cond_init(c, NULL)
+#define git_cond_free(c) 	pthread_cond_destroy(c)
+#define git_cond_wait(c, l)	pthread_cond_wait(c, l)
+#define git_cond_signal(c)	pthread_cond_signal(c)
+#define git_cond_broadcast(c)	pthread_cond_broadcast(c)
 
 GIT_INLINE(int) git_atomic_inc(git_atomic *a)
 {
@@ -79,7 +79,7 @@
 /* Pthreads Mutex */
 #define git_mutex unsigned int
 #define git_mutex_init(a) (void)0
-#define git_mutex_lock(a) (void)0
+#define git_mutex_lock(a) 0
 #define git_mutex_unlock(a) (void)0
 #define git_mutex_free(a) (void)0
 
diff --git a/libgit2/src/trace.c b/libgit2/src/trace.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/trace.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "buffer.h"
+#include "common.h"
+#include "global.h"
+#include "trace.h"
+#include "git2/trace.h"
+
+#ifdef GIT_TRACE
+
+struct git_trace_data git_trace__data = {0};
+
+#endif
+
+int git_trace_set(git_trace_level_t level, git_trace_callback callback)
+{
+#ifdef GIT_TRACE
+	assert(level == 0 || callback != NULL);
+
+	git_trace__data.level = level;
+	git_trace__data.callback = callback;
+	GIT_MEMORY_BARRIER;
+	
+	return 0;
+#else
+	GIT_UNUSED(level);
+	GIT_UNUSED(callback);
+
+	giterr_set(GITERR_INVALID,
+		"This version of libgit2 was not built with tracing.");
+	return -1;
+#endif
+}
+
diff --git a/libgit2/src/trace.h b/libgit2/src/trace.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/trace.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_trace_h__
+#define INCLUDE_trace_h__
+
+#include <stdarg.h>
+
+#include <git2/trace.h>
+#include "buffer.h"
+
+#ifdef GIT_TRACE
+
+struct git_trace_data {
+	git_trace_level_t level;
+	git_trace_callback callback;
+};
+
+extern struct git_trace_data git_trace__data;
+
+GIT_INLINE(void) git_trace__write_fmt(
+	git_trace_level_t level,
+	const char *fmt, ...)
+{
+	git_trace_callback callback = git_trace__data.callback;	
+	git_buf message = GIT_BUF_INIT;
+	va_list ap;
+		
+	va_start(ap, fmt);
+	git_buf_vprintf(&message, fmt, ap);
+	va_end(ap);
+		
+	callback(level, git_buf_cstr(&message));
+
+	git_buf_free(&message);
+}
+
+#define git_trace_level()		(git_trace__data.level)
+#define git_trace(l, ...)		{ \
+									if (git_trace__data.level >= l && \
+										git_trace__data.callback != NULL) { \
+										git_trace__write_fmt(l, __VA_ARGS__); \
+									} \
+								}
+
+#else
+
+#define git_trace_level()		((void)0)
+#define git_trace(lvl, ...)		((void)0)
+
+#endif
+
+#endif
diff --git a/libgit2/src/transport.c b/libgit2/src/transport.c
--- a/libgit2/src/transport.c
+++ b/libgit2/src/transport.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,76 +8,116 @@
 #include "git2/types.h"
 #include "git2/remote.h"
 #include "git2/net.h"
-#include "transport.h"
+#include "git2/transport.h"
 #include "path.h"
 
-static struct {
+typedef struct transport_definition {
 	char *prefix;
+	unsigned priority;
 	git_transport_cb fn;
-} transports[] = {
-	{"git://", git_transport_git},
-	{"http://", git_transport_http},
-	{"https://", git_transport_dummy},
-	{"file://", git_transport_local},
-	{"git+ssh://", git_transport_dummy},
-	{"ssh+git://", git_transport_dummy},
-	{NULL, 0}
+	void *param;
+} transport_definition;
+
+static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL };
+static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL };
+
+static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1 };
+static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0 };
+
+static transport_definition transports[] = {
+	{"git://", 1, git_transport_smart, &git_subtransport_definition},
+	{"http://", 1, git_transport_smart, &http_subtransport_definition},
+	{"https://", 1, git_transport_smart, &http_subtransport_definition},
+	{"file://", 1, git_transport_local, NULL},
+	{"git+ssh://", 1, git_transport_dummy, NULL},
+	{"ssh+git://", 1, git_transport_dummy, NULL},
+	{NULL, 0, 0}
 };
 
 #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
 
-static git_transport_cb transport_find_fn(const char *url)
+static int transport_find_fn(const char *url, git_transport_cb *callback, void **param)
 {
 	size_t i = 0;
+	unsigned priority = 0;
+	transport_definition *definition = NULL, *definition_iter;
 
 	// First, check to see if it's an obvious URL, which a URL scheme
 	for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
-		if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix)))
-			return transports[i].fn;
+		definition_iter = &transports[i];
+
+		if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix)))
+			continue;
+
+		if (definition_iter->priority > priority)
+			definition = definition_iter;
 	}
 
-	/* still here? Check to see if the path points to a file on the local file system */
-	if (git_path_exists(url) && git_path_isdir(url))
-		return &git_transport_local;
+#ifdef GIT_WIN32
+	/* On Windows, it might not be possible to discern between absolute local
+	 * and ssh paths - first check if this is a valid local path that points
+	 * to a directory and if so assume local path, else assume SSH */
 
-	/* It could be a SSH remote path. Check to see if there's a : */
-	if (strrchr(url, ':'))
-		return &git_transport_dummy;	/* SSH is an unsupported transport mechanism in this version of libgit2 */
+	/* Check to see if the path points to a file on the local file system */
+	if (!definition && git_path_exists(url) && git_path_isdir(url))
+		definition = &local_transport_definition;
 
-	return NULL;
+	/* It could be a SSH remote path. Check to see if there's a :
+	 * SSH is an unsupported transport mechanism in this version of libgit2 */
+	if (!definition && strrchr(url, ':'))
+		definition = &dummy_transport_definition; 
+#else
+	/* For other systems, perform the SSH check first, to avoid going to the
+	 * filesystem if it is not necessary */
+
+	/* It could be a SSH remote path. Check to see if there's a :
+	 * SSH is an unsupported transport mechanism in this version of libgit2 */
+	if (!definition && strrchr(url, ':'))
+		definition = &dummy_transport_definition;
+
+	/* Check to see if the path points to a file on the local file system */
+	if (!definition && git_path_exists(url) && git_path_isdir(url))
+		definition = &local_transport_definition;
+#endif
+
+	if (!definition)
+		return -1;
+
+	*callback = definition->fn;
+	*param = definition->param;
+	
+	return 0;
 }
 
 /**************
  * Public API *
  **************/
 
-int git_transport_dummy(git_transport **transport)
+int git_transport_dummy(git_transport **transport, git_remote *owner, void *param)
 {
 	GIT_UNUSED(transport);
+	GIT_UNUSED(owner);
+	GIT_UNUSED(param);
 	giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
 	return -1;
 }
 
-int git_transport_new(git_transport **out, const char *url)
+int git_transport_new(git_transport **out, git_remote *owner, const char *url)
 {
 	git_transport_cb fn;
 	git_transport *transport;
+	void *param;
 	int error;
 
-	fn = transport_find_fn(url);
-
-	if (fn == NULL) {
+	if (transport_find_fn(url, &fn, &param) < 0) {
 		giterr_set(GITERR_NET, "Unsupported URL protocol");
 		return -1;
 	}
 
-	error = fn(&transport);
+	error = fn(&transport, owner, param);
 	if (error < 0)
 		return error;
 
-	transport->url = git__strdup(url);
-	GITERR_CHECK_ALLOC(transport->url);
-
 	*out = transport;
 
 	return 0;
@@ -86,12 +126,19 @@
 /* from remote.h */
 int git_remote_valid_url(const char *url)
 {
-	return transport_find_fn(url) != NULL;
+	git_transport_cb fn;
+	void *param;
+
+	return !transport_find_fn(url, &fn, &param);
 }
 
 int git_remote_supported_url(const char* url)
 {
-	git_transport_cb transport_fn = transport_find_fn(url);
+	git_transport_cb fn;
+	void *param;
 
-	return ((transport_fn != NULL) && (transport_fn != &git_transport_dummy));
+	if (transport_find_fn(url, &fn, &param) < 0)
+		return 0;
+
+	return fn != &git_transport_dummy;
 }
diff --git a/libgit2/src/transport.h b/libgit2/src/transport.h
deleted file mode 100644
--- a/libgit2/src/transport.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_transport_h__
-#define INCLUDE_transport_h__
-
-#include "git2/net.h"
-#include "git2/indexer.h"
-#include "vector.h"
-
-#define GIT_CAP_OFS_DELTA "ofs-delta"
-
-typedef struct git_transport_caps {
-	int common:1,
-		ofs_delta:1;
-} git_transport_caps;
-
-/*
- * A day in the life of a network operation
- * ========================================
- *
- * The library gets told to ls-remote/push/fetch on/to/from some
- * remote. We look at the URL of the remote and fill the function
- * table with whatever is appropriate (the remote may be git over git,
- * ssh or http(s). It may even be an hg or svn repository, the library
- * at this level doesn't care, it just calls the helpers.
- *
- * The first call is to ->connect() which connects to the remote,
- * making use of the direction if necessary. This function must also
- * store the remote heads and any other information it needs.
- *
- * The next useful step is to call ->ls() to get the list of
- * references available to the remote. These references may have been
- * collected on connect, or we may build them now. For ls-remote,
- * nothing else is needed other than closing the connection.
- * Otherwise, the higher leves decide which objects we want to
- * have. ->send_have() is used to tell the other end what we have. If
- * we do need to download a pack, ->download_pack() is called.
- *
- * When we're done, we call ->close() to close the
- * connection. ->free() takes care of freeing all the resources.
- */
-
-struct git_transport {
-	/**
-	 * Where the repo lives
-	 */
-	char *url;
-	/**
-	 * Whether we want to push or fetch
-	 */
-	int direction : 1, /* 0 fetch, 1 push */
-		connected : 1;
-	/**
-	 * Connect and store the remote heads
-	 */
-	int (*connect)(struct git_transport *transport, int dir);
-	/**
-	 * Give a list of references, useful for ls-remote
-	 */
-	int (*ls)(struct git_transport *transport, git_headlist_cb list_cb, void *opaque);
-	/**
-	 * Push the changes over
-	 */
-	int (*push)(struct git_transport *transport);
-	/**
-	 * Negotiate the minimal amount of objects that need to be
-	 * retrieved
-	 */
-	int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants);
-	/**
-	 * Download the packfile
-	 */
-	int (*download_pack)(struct git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
-	/**
-	 * Fetch the changes
-	 */
-	int (*fetch)(struct git_transport *transport);
-	/**
-	 * Close the connection
-	 */
-	int (*close)(struct git_transport *transport);
-	/**
-	 * Free the associated resources
-	 */
-	void (*free)(struct git_transport *transport);
-};
-
-
-int git_transport_new(struct git_transport **transport, const char *url);
-int git_transport_local(struct git_transport **transport);
-int git_transport_git(struct git_transport **transport);
-int git_transport_http(struct git_transport **transport);
-int git_transport_dummy(struct git_transport **transport);
-
-/**
-  Returns true if the passed URL is valid (a URL with a Git supported scheme,
-  or pointing to an existing path)
-*/
-int git_transport_valid_url(const char *url);
-
-typedef struct git_transport git_transport;
-typedef int (*git_transport_cb)(git_transport **transport);
-
-#endif
diff --git a/libgit2/src/transports/cred.c b/libgit2/src/transports/cred.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/cred.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+#include "smart.h"
+#include "git2/cred_helpers.h"
+
+static void plaintext_free(struct git_cred *cred)
+{
+	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+	size_t pass_len = strlen(c->password);
+
+	git__free(c->username);
+
+	/* Zero the memory which previously held the password */
+	memset(c->password, 0x0, pass_len);
+	git__free(c->password);
+
+	memset(c, 0, sizeof(*c));
+
+	git__free(c);
+}
+
+int git_cred_userpass_plaintext_new(
+	git_cred **cred,
+	const char *username,
+	const char *password)
+{
+	git_cred_userpass_plaintext *c;
+
+	if (!cred)
+		return -1;
+
+	c = git__malloc(sizeof(git_cred_userpass_plaintext));
+	GITERR_CHECK_ALLOC(c);
+
+	c->parent.credtype = GIT_CREDTYPE_USERPASS_PLAINTEXT;
+	c->parent.free = plaintext_free;
+	c->username = git__strdup(username);
+
+	if (!c->username) {
+		git__free(c);
+		return -1;
+	}
+
+	c->password = git__strdup(password);
+
+	if (!c->password) {
+		git__free(c->username);
+		git__free(c);
+		return -1;
+	}
+
+	*cred = &c->parent;
+	return 0;
+}
diff --git a/libgit2/src/transports/cred_helpers.c b/libgit2/src/transports/cred_helpers.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/cred_helpers.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2/cred_helpers.h"
+
+int git_cred_userpass(
+		git_cred **cred,
+		const char *url,
+		const char *user_from_url,
+		unsigned int allowed_types,
+		void *payload)
+{
+	git_cred_userpass_payload *userpass = (git_cred_userpass_payload*)payload;
+	const char *effective_username = NULL;
+
+	GIT_UNUSED(url);
+
+	if (!userpass || !userpass->password) return -1;
+
+	/* Username resolution: a username can be passed with the URL, the
+	 * credentials payload, or both. Here's what we do.  Note that if we get
+	 * this far, we know that any password the url may contain has already
+	 * failed at least once, so we ignore it.
+	 *
+	 * |  Payload    |   URL    |   Used    |
+	 * +-------------+----------+-----------+
+	 * |    yes      |   no     |  payload  |
+	 * |    yes      |   yes    |  payload  |
+	 * |    no       |   yes    |  url      |
+	 * |    no       |   no     |  FAIL     |
+	 */
+	if (userpass->username)
+		effective_username = userpass->username;
+	else if (user_from_url)
+		effective_username = user_from_url;
+	else
+		return -1;
+
+	if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 ||
+			git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
+		return -1;
+
+	return 0;
+}
diff --git a/libgit2/src/transports/git.c b/libgit2/src/transports/git.c
--- a/libgit2/src/transports/git.c
+++ b/libgit2/src/transports/git.c
@@ -1,50 +1,42 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
  */
 
-#include "git2/net.h"
-#include "git2/common.h"
-#include "git2/types.h"
-#include "git2/errors.h"
-#include "git2/net.h"
-#include "git2/revwalk.h"
-
-#include "vector.h"
-#include "transport.h"
-#include "pkt.h"
-#include "common.h"
+#include "git2.h"
+#include "buffer.h"
 #include "netops.h"
-#include "filebuf.h"
-#include "repository.h"
-#include "fetch.h"
-#include "protocol.h"
 
+#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
+
+static const char prefix_git[] = "git://";
+static const char cmd_uploadpack[] = "git-upload-pack";
+static const char cmd_receivepack[] = "git-receive-pack";
+
 typedef struct {
-	git_transport parent;
-	git_protocol proto;
-	GIT_SOCKET socket;
-	git_vector refs;
-	git_remote_head **heads;
-	git_transport_caps caps;
-	char buff[1024];
-	gitno_buffer buf;
-#ifdef GIT_WIN32
-	WSADATA wsd;
-#endif
-} transport_git;
+	git_smart_subtransport_stream parent;
+	gitno_socket socket;
+	const char *cmd;
+	char *url;
+	unsigned sent_command : 1;
+} git_stream;
 
+typedef struct {
+	git_smart_subtransport parent;
+	git_transport *owner;
+	git_stream *current_stream;
+} git_subtransport;
+
 /*
- * Create a git procol request.
+ * Create a git protocol request.
  *
  * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
  */
 static int gen_proto(git_buf *request, const char *cmd, const char *url)
 {
 	char *delim, *repo;
-	char default_command[] = "git-upload-pack";
 	char host[] = "host=";
 	size_t len;
 
@@ -60,9 +52,6 @@
 	if (delim == NULL)
 		delim = strchr(url, '/');
 
-	if (cmd == NULL)
-		cmd = default_command;
-
 	len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
 
 	git_buf_grow(request, len);
@@ -77,402 +66,287 @@
 	return 0;
 }
 
-static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
+static int send_command(git_stream *s)
 {
 	int error;
 	git_buf request = GIT_BUF_INIT;
 
-	error = gen_proto(&request, cmd, url);
+	error = gen_proto(&request, s->cmd, s->url);
 	if (error < 0)
 		goto cleanup;
 
-	error = gitno_send(s, request.ptr, request.size, 0);
+	/* It looks like negative values are errors here, and positive values
+	 * are the number of bytes sent. */
+	error = gitno_send(&s->socket, request.ptr, request.size, 0);
 
+	if (error >= 0)
+		s->sent_command = 1;
+
 cleanup:
 	git_buf_free(&request);
 	return error;
 }
 
-/*
- * Parse the URL and connect to a server, storing the socket in
- * out. For convenience this also takes care of asking for the remote
- * refs
- */
-static int do_connect(transport_git *t, const char *url)
+static int git_stream_read(
+	git_smart_subtransport_stream *stream,
+	char *buffer,
+	size_t buf_size,
+	size_t *bytes_read)
 {
-	char *host, *port;
-	const char prefix[] = "git://";
-	int error;
-
-	t->socket = INVALID_SOCKET;
+	git_stream *s = (git_stream *)stream;
+	gitno_buffer buf;
 
-	if (!git__prefixcmp(url, prefix))
-		url += strlen(prefix);
+	*bytes_read = 0;
 
-	if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 0)
+	if (!s->sent_command && send_command(s) < 0)
 		return -1;
 
-	if ((error = gitno_connect(&t->socket, host, port)) == 0) {
-		error = send_request(t->socket, NULL, url);
-	}
-
-	git__free(host);
-	git__free(port);
-
-	if (error < 0 && t->socket != INVALID_SOCKET) {
-		gitno_close(t->socket);
-		t->socket = INVALID_SOCKET;
-	}
+	gitno_buffer_setup(&s->socket, &buf, buffer, buf_size);
 
-	if (t->socket == INVALID_SOCKET) {
-		giterr_set(GITERR_NET, "Failed to connect to the host");
+	if (gitno_recv(&buf) < 0)
 		return -1;
-	}
 
+	*bytes_read = buf.offset;
+
 	return 0;
 }
 
-/*
- * Read from the socket and store the references in the vector
- */
-static int store_refs(transport_git *t)
+static int git_stream_write(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
 {
-	gitno_buffer *buf = &t->buf;
-	int ret = 0;
-
-	while (1) {
-		if ((ret = gitno_recv(buf)) < 0)
-			return -1;
-		if (ret == 0) /* Orderly shutdown, so exit */
-			return 0;
-
-		ret = git_protocol_store_refs(&t->proto, buf->data, buf->offset);
-		if (ret == GIT_EBUFS) {
-			gitno_consume_n(buf, buf->len);
-			continue;
-		}
-
-		if (ret < 0)
-			return ret;
+	git_stream *s = (git_stream *)stream;
 
-		gitno_consume_n(buf, buf->offset);
+	if (!s->sent_command && send_command(s) < 0)
+		return -1;
 
-		if (t->proto.flush) { /* No more refs */
-			t->proto.flush = 0;
-			return 0;
-		}
-	}
+	return gitno_send(&s->socket, buffer, len, 0);
 }
 
-static int detect_caps(transport_git *t)
+static void git_stream_free(git_smart_subtransport_stream *stream)
 {
-	git_vector *refs = &t->refs;
-	git_pkt_ref *pkt;
-	git_transport_caps *caps = &t->caps;
-	const char *ptr;
-
-	pkt = git_vector_get(refs, 0);
-	/* No refs or capabilites, odd but not a problem */
-	if (pkt == NULL || pkt->capabilities == NULL)
-		return 0;
+	git_stream *s = (git_stream *)stream;
+	git_subtransport *t = OWNING_SUBTRANSPORT(s);
+	int ret;
 
-	ptr = pkt->capabilities;
-	while (ptr != NULL && *ptr != '\0') {
-		if (*ptr == ' ')
-			ptr++;
+	GIT_UNUSED(ret);
 
-		if(!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
-			caps->common = caps->ofs_delta = 1;
-			ptr += strlen(GIT_CAP_OFS_DELTA);
-			continue;
-		}
+	t->current_stream = NULL;
 
-		/* We don't know this capability, so skip it */
-		ptr = strchr(ptr, ' ');
+	if (s->socket.socket) {
+		ret = gitno_close(&s->socket);
+		assert(!ret);
 	}
 
-	return 0;
+	git__free(s->url);
+	git__free(s);	
 }
 
-/*
- * Since this is a network connection, we need to parse and store the
- * pkt-lines at this stage and keep them there.
- */
-static int git_connect(git_transport *transport, int direction)
+static int git_stream_alloc(
+	git_subtransport *t,
+	const char *url,
+	const char *cmd,
+	git_smart_subtransport_stream **stream)
 {
-	transport_git *t = (transport_git *) transport;
-
-	if (direction == GIT_DIR_PUSH) {
-		giterr_set(GITERR_NET, "Pushing over git:// is not supported");
-		return -1;
-	}
+	git_stream *s;
 
-	t->parent.direction = direction;
-	if (git_vector_init(&t->refs, 16, NULL) < 0)
+	if (!stream)
 		return -1;
 
-	/* Connect and ask for the refs */
-	if (do_connect(t, transport->url) < 0)
-		goto cleanup;
+	s = git__calloc(sizeof(git_stream), 1);
+	GITERR_CHECK_ALLOC(s);
 
-	gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
+	s->parent.subtransport = &t->parent;
+	s->parent.read = git_stream_read;
+	s->parent.write = git_stream_write;
+	s->parent.free = git_stream_free;
 
-	t->parent.connected = 1;
-	if (store_refs(t) < 0)
-		goto cleanup;
+	s->cmd = cmd;
+	s->url = git__strdup(url);
 
-	if (detect_caps(t) < 0)
-		goto cleanup;
+	if (!s->url) {
+		git__free(s);
+		return -1;
+	}
 
+	*stream = &s->parent;
 	return 0;
-cleanup:
-	git_vector_free(&t->refs);
-	return -1;
 }
 
-static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
+static int _git_uploadpack_ls(
+	git_subtransport *t,
+	const char *url,
+	git_smart_subtransport_stream **stream)
 {
-	transport_git *t = (transport_git *) transport;
-	git_vector *refs = &t->refs;
-	unsigned int i;
-	git_pkt *p = NULL;
-
-	git_vector_foreach(refs, i, p) {
-		git_pkt_ref *pkt = NULL;
-
-		if (p->type != GIT_PKT_REF)
-			continue;
+	char *host, *port, *user=NULL, *pass=NULL;
+	git_stream *s;
 
-		pkt = (git_pkt_ref *)p;
+	*stream = NULL;
 
-		if (list_cb(&pkt->head, opaque) < 0) {
-			giterr_set(GITERR_NET, "User callback returned error");
-			return -1;
-		}
-	}
+	if (!git__prefixcmp(url, prefix_git))
+		url += strlen(prefix_git);
 
-	return 0;
-}
+	if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0)
+		return -1;
 
-/* Wait until we get an ack from the */
-static int recv_pkt(gitno_buffer *buf)
-{
-	const char *ptr = buf->data, *line_end;
-	git_pkt *pkt;
-	int pkt_type, error;
+	s = (git_stream *)*stream;
 
-	do {
-		/* Wait for max. 1 second */
-		if ((error = gitno_select_in(buf, 1, 0)) < 0) {
-			return -1;
-		} else if (error == 0) {
-			/*
-			 * Some servers don't respond immediately, so if this
-			 * happens, we keep sending information until it
-			 * answers. Pretend we received a NAK to convince higher
-			 * layers to do so.
-			 */
-			return GIT_PKT_NAK;
-		}
+	if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
+		goto on_error;
 
-		if ((error = gitno_recv(buf)) < 0)
-			return -1;
+	if (gitno_connect(&s->socket, host, port, 0) < 0)
+		goto on_error;
 
-		error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
-		if (error == GIT_EBUFS)
-			continue;
-		if (error < 0)
-			return -1;
-	} while (error);
+	t->current_stream = s;
+	git__free(host);
+	git__free(port);
+	git__free(user);
+	git__free(pass);
+	return 0;
 
-	gitno_consume(buf, line_end);
-	pkt_type = pkt->type;
-	git__free(pkt);
+on_error:
+	if (*stream)
+		git_stream_free(*stream);
 
-	return pkt_type;
+	git__free(host);
+	git__free(port);
+	return -1;
 }
 
-static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
+static int _git_uploadpack(
+	git_subtransport *t,
+	const char *url,
+	git_smart_subtransport_stream **stream)
 {
-	transport_git *t = (transport_git *) transport;
-	git_revwalk *walk;
-	git_oid oid;
-	int error;
-	unsigned int i;
-	git_buf data = GIT_BUF_INIT;
-	gitno_buffer *buf = &t->buf;
-
-	if (git_pkt_buffer_wants(wants, &t->caps, &data) < 0)
-		return -1;
+	GIT_UNUSED(url);
 
-	if (git_fetch_setup_walk(&walk, repo) < 0)
-		goto on_error;
+	if (t->current_stream) {
+		*stream = &t->current_stream->parent;
+		return 0;
+	}
 
-	if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
-		goto on_error;
+	giterr_set(GITERR_NET, "Must call UPLOADPACK_LS before UPLOADPACK");
+	return -1;
+}
 
-	git_buf_clear(&data);
-	/*
-	 * We don't support any kind of ACK extensions, so the negotiation
-	 * boils down to sending what we have and listening for an ACK
-	 * every once in a while.
-	 */
-	i = 0;
-	while ((error = git_revwalk_next(&oid, walk)) == 0) {
-		git_pkt_buffer_have(&oid, &data);
-		i++;
-		if (i % 20 == 0) {
-			int pkt_type;
+static int _git_receivepack_ls(
+	git_subtransport *t,
+	const char *url,
+	git_smart_subtransport_stream **stream)
+{
+	char *host, *port, *user=NULL, *pass=NULL;
+	git_stream *s;
 
-			git_pkt_buffer_flush(&data);
-			if (git_buf_oom(&data))
-				goto on_error;
+	*stream = NULL;
 
-			if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
-				goto on_error;
+	if (!git__prefixcmp(url, prefix_git))
+		url += strlen(prefix_git);
 
-			pkt_type = recv_pkt(buf);
+	if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0)
+		return -1;
 
-			if (pkt_type == GIT_PKT_ACK) {
-				break;
-			} else if (pkt_type == GIT_PKT_NAK) {
-				continue;
-			} else {
-				giterr_set(GITERR_NET, "Unexpected pkt type");
-				goto on_error;
-			}
+	s = (git_stream *)*stream;
 
-		}
-	}
-	if (error < 0 && error != GIT_REVWALKOVER)
+	if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0)
 		goto on_error;
 
-	/* Tell the other end that we're done negotiating */
-	git_buf_clear(&data);
-	git_pkt_buffer_flush(&data);
-	git_pkt_buffer_done(&data);
-	if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+	if (gitno_connect(&s->socket, host, port, 0) < 0)
 		goto on_error;
 
-	git_buf_free(&data);
-	git_revwalk_free(walk);
+	t->current_stream = s;
+	git__free(host);
+	git__free(port);
+	git__free(user);
+	git__free(pass);
 	return 0;
 
 on_error:
-	git_buf_free(&data);
-	git_revwalk_free(walk);
+	if (*stream)
+		git_stream_free(*stream);
+
+	git__free(host);
+	git__free(port);
 	return -1;
 }
 
-static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
+static int _git_receivepack(
+	git_subtransport *t,
+	const char *url,
+	git_smart_subtransport_stream **stream)
 {
-	transport_git *t = (transport_git *) transport;
-	int error = 0, read_bytes;
-	gitno_buffer *buf = &t->buf;
-	git_pkt *pkt;
-	const char *line_end, *ptr;
+	GIT_UNUSED(url);
 
-	/*
-	 * For now, we ignore everything and wait for the pack
-	 */
-	do {
-		ptr = buf->data;
-		/* Whilst we're searching for the pack */
-		while (1) {
-			if (buf->offset == 0) {
-				break;
-			}
+	if (t->current_stream) {
+		*stream = &t->current_stream->parent;
+		return 0;
+	}
 
-			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
-			if (error == GIT_EBUFS)
-				break;
+	giterr_set(GITERR_NET, "Must call RECEIVEPACK_LS before RECEIVEPACK");
+	return -1;
+}
 
-			if (error < 0)
-				return error;
+static int _git_action(
+	git_smart_subtransport_stream **stream,
+	git_smart_subtransport *subtransport,
+	const char *url,
+	git_smart_service_t action)
+{
+	git_subtransport *t = (git_subtransport *) subtransport;
 
-			if (pkt->type == GIT_PKT_PACK) {
-				git__free(pkt);
-				return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
-			}
+	switch (action) {
+		case GIT_SERVICE_UPLOADPACK_LS:
+			return _git_uploadpack_ls(t, url, stream);
 
-			/* For now we don't care about anything */
-			git__free(pkt);
-			gitno_consume(buf, line_end);
-		}
+		case GIT_SERVICE_UPLOADPACK:
+			return _git_uploadpack(t, url, stream);
 
-		read_bytes = gitno_recv(buf);
-	} while (read_bytes);
+		case GIT_SERVICE_RECEIVEPACK_LS:
+			return _git_receivepack_ls(t, url, stream);
 
-	return read_bytes;
+		case GIT_SERVICE_RECEIVEPACK:
+			return _git_receivepack(t, url, stream);
+	}
+
+	*stream = NULL;
+	return -1;
 }
 
-static int git_close(git_transport *transport)
+static int _git_close(git_smart_subtransport *subtransport)
 {
-	transport_git *t = (transport_git*) transport;
+	git_subtransport *t = (git_subtransport *) subtransport;
 
-	/* Can't do anything if there's an error, so don't bother checking  */
-	git_pkt_send_flush(t->socket);
-	if (gitno_close(t->socket) < 0) {
-		giterr_set(GITERR_NET, "Failed to close socket");
-		return -1;
-	}
+	assert(!t->current_stream);
 
-#ifdef GIT_WIN32
-	WSACleanup();
-#endif
+	GIT_UNUSED(t);
 
 	return 0;
 }
 
-static void git_free(git_transport *transport)
+static void _git_free(git_smart_subtransport *subtransport)
 {
-	transport_git *t = (transport_git *) transport;
-	git_vector *refs = &t->refs;
-	unsigned int i;
+	git_subtransport *t = (git_subtransport *) subtransport;
 
-	for (i = 0; i < refs->length; ++i) {
-		git_pkt *p = git_vector_get(refs, i);
-		git_pkt_free(p);
-	}
+	assert(!t->current_stream);
 
-	git_vector_free(refs);
-	git__free(t->heads);
-	git_buf_free(&t->proto.buf);
-	git__free(t->parent.url);
 	git__free(t);
 }
 
-int git_transport_git(git_transport **out)
+int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner)
 {
-	transport_git *t;
-#ifdef GIT_WIN32
-	int ret;
-#endif
-
-	t = git__malloc(sizeof(transport_git));
-	GITERR_CHECK_ALLOC(t);
-
-	memset(t, 0x0, sizeof(transport_git));
+	git_subtransport *t;
 
-	t->parent.connect = git_connect;
-	t->parent.ls = git_ls;
-	t->parent.negotiate_fetch = git_negotiate_fetch;
-	t->parent.download_pack = git_download_pack;
-	t->parent.close = git_close;
-	t->parent.free = git_free;
-	t->proto.refs = &t->refs;
-	t->proto.transport = (git_transport *) t;
+	if (!out)
+		return -1;
 
-	*out = (git_transport *) t;
+	t = git__calloc(sizeof(git_subtransport), 1);
+	GITERR_CHECK_ALLOC(t);
 
-#ifdef GIT_WIN32
-	ret = WSAStartup(MAKEWORD(2,2), &t->wsd);
-	if (ret != 0) {
-		git_free(*out);
-		giterr_set(GITERR_NET, "Winsock init failed");
-		return -1;
-	}
-#endif
+	t->owner = owner;
+	t->parent.action = _git_action;
+	t->parent.close = _git_close;
+	t->parent.free = _git_free;
 
+	*out = (git_smart_subtransport *) t;
 	return 0;
 }
diff --git a/libgit2/src/transports/http.c b/libgit2/src/transports/http.c
--- a/libgit2/src/transports/http.c
+++ b/libgit2/src/transports/http.c
@@ -1,682 +1,953 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-
-#include <stdlib.h>
-#include "git2.h"
-#include "http_parser.h"
-
-#include "transport.h"
-#include "common.h"
-#include "netops.h"
-#include "buffer.h"
-#include "pkt.h"
-#include "refs.h"
-#include "pack.h"
-#include "fetch.h"
-#include "filebuf.h"
-#include "repository.h"
-#include "protocol.h"
-
-enum last_cb {
-	NONE,
-	FIELD,
-	VALUE
-};
-
-typedef struct {
-	git_transport parent;
-	git_protocol proto;
-	git_vector refs;
-	git_vector common;
-	GIT_SOCKET socket;
-	git_buf buf;
-	git_remote_head **heads;
-	int error;
-	int transfer_finished :1,
-		ct_found :1,
-		ct_finished :1,
-		pack_ready :1;
-	enum last_cb last_cb;
-	http_parser parser;
-	char *content_type;
-	char *host;
-	char *port;
-	char *service;
-	git_transport_caps caps;
-#ifdef GIT_WIN32
-	WSADATA wsd;
-#endif
-} transport_http;
-
-static int gen_request(git_buf *buf, const char *url, const char *host, const char *op,
-                       const char *service, ssize_t content_length, int ls)
-{
-	const char *path = url;
-
-	path = strchr(path, '/');
-	if (path == NULL) /* Is 'git fetch http://host.com/' valid? */
-		path = "/";
-
-	if (ls) {
-		git_buf_printf(buf, "%s %s/info/refs?service=git-%s HTTP/1.1\r\n", op, path, service);
-	} else {
-		git_buf_printf(buf, "%s %s/git-%s HTTP/1.1\r\n", op, path, service);
-	}
-	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
-	git_buf_printf(buf, "Host: %s\r\n", host);
-	if (content_length > 0) {
-		git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", service);
-		git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", service);
-		git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
-	} else {
-		git_buf_puts(buf, "Accept: */*\r\n");
-	}
-	git_buf_puts(buf, "\r\n");
-
-	if (git_buf_oom(buf))
-		return -1;
-
-	return 0;
-}
-
-static int do_connect(transport_http *t, const char *host, const char *port)
-{
-	GIT_SOCKET s;
-
-	if (t->parent.connected && http_should_keep_alive(&t->parser))
-		return 0;
-
-	if (gitno_connect(&s, host, port) < 0)
-		return -1;
-
-	t->socket = s;
-	t->parent.connected = 1;
-
-	return 0;
-}
-
-/*
- * The HTTP parser is streaming, so we need to wait until we're in the
- * field handler before we can be sure that we can store the previous
- * value. Right now, we only care about the
- * Content-Type. on_header_{field,value} should be kept generic enough
- * to work for any request.
- */
-
-static const char *typestr = "Content-Type";
-
-static int on_header_field(http_parser *parser, const char *str, size_t len)
-{
-	transport_http *t = (transport_http *) parser->data;
-	git_buf *buf = &t->buf;
-
-	if (t->last_cb == VALUE && t->ct_found) {
-		t->ct_finished = 1;
-		t->ct_found = 0;
-		t->content_type = git__strdup(git_buf_cstr(buf));
-		GITERR_CHECK_ALLOC(t->content_type);
-		git_buf_clear(buf);
-	}
-
-	if (t->ct_found) {
-		t->last_cb = FIELD;
-		return 0;
-	}
-
-	if (t->last_cb != FIELD)
-		git_buf_clear(buf);
-
-	git_buf_put(buf, str, len);
-	t->last_cb = FIELD;
-
-	return git_buf_oom(buf);
-}
-
-static int on_header_value(http_parser *parser, const char *str, size_t len)
-{
-	transport_http *t = (transport_http *) parser->data;
-	git_buf *buf = &t->buf;
-
-	if (t->ct_finished) {
-		t->last_cb = VALUE;
-		return 0;
-	}
-
-	if (t->last_cb == VALUE)
-		git_buf_put(buf, str, len);
-
-	if (t->last_cb == FIELD && !strcmp(git_buf_cstr(buf), typestr)) {
-		t->ct_found = 1;
-		git_buf_clear(buf);
-		git_buf_put(buf, str, len);
-	}
-
-	t->last_cb = VALUE;
-
-	return git_buf_oom(buf);
-}
-
-static int on_headers_complete(http_parser *parser)
-{
-	transport_http *t = (transport_http *) parser->data;
-	git_buf *buf = &t->buf;
-
-	/* The content-type is text/plain for 404, so don't validate */
-	if (parser->status_code == 404) {
-		git_buf_clear(buf);
-		return 0;
-	}
-
-	if (t->content_type == NULL) {
-		t->content_type = git__strdup(git_buf_cstr(buf));
-		if (t->content_type == NULL)
-			return t->error = -1;
-	}
-
-	git_buf_clear(buf);
-	git_buf_printf(buf, "application/x-git-%s-advertisement", t->service);
-	if (git_buf_oom(buf))
-		return t->error = -1;
-
-	if (strcmp(t->content_type, git_buf_cstr(buf)))
-		return t->error = -1;
-
-	git_buf_clear(buf);
-	return 0;
-}
-
-static int on_body_store_refs(http_parser *parser, const char *str, size_t len)
-{
-	transport_http *t = (transport_http *) parser->data;
-
-	if (parser->status_code == 404) {
-		return git_buf_put(&t->buf, str, len);
-	}
-
-	return git_protocol_store_refs(&t->proto, str, len);
-}
-
-static int on_message_complete(http_parser *parser)
-{
-	transport_http *t = (transport_http *) parser->data;
-
-	t->transfer_finished = 1;
-
-	if (parser->status_code == 404) {
-		giterr_set(GITERR_NET, "Remote error: %s", git_buf_cstr(&t->buf));
-		t->error = -1;
-	}
-
-	return 0;
-}
-
-static int store_refs(transport_http *t)
-{
-	http_parser_settings settings;
-	char buffer[1024];
-	gitno_buffer buf;
-	git_pkt *pkt;
-	int ret;
-
-	http_parser_init(&t->parser, HTTP_RESPONSE);
-	t->parser.data = t;
-	memset(&settings, 0x0, sizeof(http_parser_settings));
-	settings.on_header_field = on_header_field;
-	settings.on_header_value = on_header_value;
-	settings.on_headers_complete = on_headers_complete;
-	settings.on_body = on_body_store_refs;
-	settings.on_message_complete = on_message_complete;
-
-	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
-
-	while(1) {
-		size_t parsed;
-
-		if ((ret = gitno_recv(&buf)) < 0)
-			return -1;
-
-		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
-		/* Both should happen at the same time */
-		if (parsed != buf.offset || t->error < 0)
-			return t->error;
-
-		gitno_consume_n(&buf, parsed);
-
-		if (ret == 0 || t->transfer_finished)
-			return 0;
-	}
-
-	pkt = git_vector_get(&t->refs, 0);
-	if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) {
-		giterr_set(GITERR_NET, "Invalid HTTP response");
-		return t->error = -1;
-	} else {
-		git_vector_remove(&t->refs, 0);
-	}
-
-	return 0;
-}
-
-static int http_connect(git_transport *transport, int direction)
-{
-	transport_http *t = (transport_http *) transport;
-	int ret;
-	git_buf request = GIT_BUF_INIT;
-	const char *service = "upload-pack";
-	const char *url = t->parent.url, *prefix = "http://";
-
-	if (direction == GIT_DIR_PUSH) {
-		giterr_set(GITERR_NET, "Pushing over HTTP is not implemented");
-		return -1;
-	}
-
-	t->parent.direction = direction;
-	if (git_vector_init(&t->refs, 16, NULL) < 0)
-		return -1;
-
-	if (!git__prefixcmp(url, prefix))
-		url += strlen(prefix);
-
-	if ((ret = gitno_extract_host_and_port(&t->host, &t->port, url, "80")) < 0)
-		goto cleanup;
-
-	t->service = git__strdup(service);
-	GITERR_CHECK_ALLOC(t->service);
-
-	if ((ret = do_connect(t, t->host, t->port)) < 0)
-		goto cleanup;
-
-	/* Generate and send the HTTP request */
-	if ((ret = gen_request(&request, url, t->host, "GET", service, 0, 1)) < 0) {
-		giterr_set(GITERR_NET, "Failed to generate request");
-		goto cleanup;
-	}
-
-	if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
-		goto cleanup;
-
-	ret = store_refs(t);
-
-cleanup:
-	git_buf_free(&request);
-	git_buf_clear(&t->buf);
-
-	return ret;
-}
-
-static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
-{
-	transport_http *t = (transport_http *) transport;
-	git_vector *refs = &t->refs;
-	unsigned int i;
-	git_pkt_ref *p;
-
-	git_vector_foreach(refs, i, p) {
-		if (p->type != GIT_PKT_REF)
-			continue;
-
-		if (list_cb(&p->head, opaque) < 0) {
-			giterr_set(GITERR_NET, "The user callback returned error");
-			return -1;
-		}
-	}
-
-	return 0;
-}
-
-static int on_body_parse_response(http_parser *parser, const char *str, size_t len)
-{
-	transport_http *t = (transport_http *) parser->data;
-	git_buf *buf = &t->buf;
-	git_vector *common = &t->common;
-	int error;
-	const char *line_end, *ptr;
-
-	if (len == 0) { /* EOF */
-		if (git_buf_len(buf) != 0) {
-			giterr_set(GITERR_NET, "Unexpected EOF");
-			return t->error = -1;
-		} else {
-			return 0;
-		}
-	}
-
-	git_buf_put(buf, str, len);
-	ptr = buf->ptr;
-	while (1) {
-		git_pkt *pkt;
-
-		if (git_buf_len(buf) == 0)
-			return 0;
-
-		error = git_pkt_parse_line(&pkt, ptr, &line_end, git_buf_len(buf));
-		if (error == GIT_EBUFS) {
-			return 0; /* Ask for more */
-		}
-		if (error < 0)
-			return t->error = -1;
-
-		git_buf_consume(buf, line_end);
-
-		if (pkt->type == GIT_PKT_PACK) {
-			git__free(pkt);
-			t->pack_ready = 1;
-			return 0;
-		}
-
-		if (pkt->type == GIT_PKT_NAK) {
-			git__free(pkt);
-			return 0;
-		}
-
-		if (pkt->type != GIT_PKT_ACK) {
-			git__free(pkt);
-			continue;
-		}
-
-		if (git_vector_insert(common, pkt) < 0)
-			return -1;
-	}
-
-	return error;
-
-}
-
-static int parse_response(transport_http *t)
-{
-	int ret = 0;
-	http_parser_settings settings;
-	char buffer[1024];
-	gitno_buffer buf;
-
-	http_parser_init(&t->parser, HTTP_RESPONSE);
-	t->parser.data = t;
-	t->transfer_finished = 0;
-	memset(&settings, 0x0, sizeof(http_parser_settings));
-	settings.on_header_field = on_header_field;
-	settings.on_header_value = on_header_value;
-	settings.on_headers_complete = on_headers_complete;
-	settings.on_body = on_body_parse_response;
-	settings.on_message_complete = on_message_complete;
-
-	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
-
-	while(1) {
-		size_t parsed;
-
-		if ((ret = gitno_recv(&buf)) < 0)
-			return -1;
-
-		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
-		/* Both should happen at the same time */
-		if (parsed != buf.offset || t->error < 0)
-			return t->error;
-
-		gitno_consume_n(&buf, parsed);
-
-		if (ret == 0 || t->transfer_finished || t->pack_ready) {
-			return 0;
-		}
-	}
-
-	return ret;
-}
-
-static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
-{
-	transport_http *t = (transport_http *) transport;
-	int ret;
-	unsigned int i;
-	char buff[128];
-	gitno_buffer buf;
-	git_revwalk *walk = NULL;
-	git_oid oid;
-	git_pkt_ack *pkt;
-	git_vector *common = &t->common;
-	const char *prefix = "http://", *url = t->parent.url;
-	git_buf request = GIT_BUF_INIT, data = GIT_BUF_INIT;
-	gitno_buffer_setup(&buf, buff, sizeof(buff), t->socket);
-
-	/* TODO: Store url in the transport */
-	if (!git__prefixcmp(url, prefix))
-		url += strlen(prefix);
-
-	if (git_vector_init(common, 16, NULL) < 0)
-		return -1;
-
-	if (git_fetch_setup_walk(&walk, repo) < 0)
-		return -1;
-
-	do {
-		if ((ret = do_connect(t, t->host, t->port)) < 0)
-			goto cleanup;
-
-		if ((ret = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
-			goto cleanup;
-
-		/* We need to send these on each connection */
-		git_vector_foreach (common, i, pkt) {
-			if ((ret = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
-				goto cleanup;
-		}
-
-		i = 0;
-		while ((i < 20) && ((ret = git_revwalk_next(&oid, walk)) == 0)) {
-			if ((ret = git_pkt_buffer_have(&oid, &data)) < 0)
-				goto cleanup;
-
-			i++;
-		}
-
-		git_pkt_buffer_done(&data);
-
-		if ((ret = gen_request(&request, url, t->host, "POST", "upload-pack", data.size, 0)) < 0)
-			goto cleanup;
-
-		if ((ret = gitno_send(t->socket, request.ptr, request.size, 0)) < 0)
-			goto cleanup;
-
-		if ((ret = gitno_send(t->socket, data.ptr, data.size, 0)) < 0)
-			goto cleanup;
-
-		git_buf_clear(&request);
-		git_buf_clear(&data);
-
-		if (ret < 0 || i >= 256)
-			break;
-
-		if ((ret = parse_response(t)) < 0)
-			goto cleanup;
-
-		if (t->pack_ready) {
-			ret = 0;
-			goto cleanup;
-		}
-
-	} while(1);
-
-cleanup:
-	git_buf_free(&request);
-	git_buf_free(&data);
-	git_revwalk_free(walk);
-	return ret;
-}
-
-typedef struct {
-	git_indexer_stream *idx;
-	git_indexer_stats *stats;
-	transport_http *transport;
-} download_pack_cbdata;
-
-static int on_message_complete_download_pack(http_parser *parser)
-{
-	download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
-
-	data->transport->transfer_finished = 1;
-
-	return 0;
-}
-static int on_body_download_pack(http_parser *parser, const char *str, size_t len)
-{
-	download_pack_cbdata *data = (download_pack_cbdata *) parser->data;
-	transport_http *t = data->transport;
-	git_indexer_stream *idx = data->idx;
-	git_indexer_stats *stats = data->stats;
-
-	return t->error = git_indexer_stream_add(idx, str, len, stats);
-}
-
-/*
- * As the server is probably using Transfer-Encoding: chunked, we have
- * to use the HTTP parser to download the pack instead of giving it to
- * the simple downloader. Furthermore, we're using keep-alive
- * connections, so the simple downloader would just hang.
- */
-static int http_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
-{
-	transport_http *t = (transport_http *) transport;
-	git_buf *oldbuf = &t->buf;
-	int recvd;
-	http_parser_settings settings;
-	char buffer[1024];
-	gitno_buffer buf;
-	git_indexer_stream *idx = NULL;
-	download_pack_cbdata data;
-
-	gitno_buffer_setup(&buf, buffer, sizeof(buffer), t->socket);
-
-	if (memcmp(oldbuf->ptr, "PACK", strlen("PACK"))) {
-		giterr_set(GITERR_NET, "The pack doesn't start with a pack signature");
-		return -1;
-	}
-
-	if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
-		return -1;
-
-
-	/*
-	 * This is part of the previous response, so we don't want to
-	 * re-init the parser, just set these two callbacks.
-	 */
-	memset(stats, 0, sizeof(git_indexer_stats));
-	data.stats = stats;
-	data.idx = idx;
-	data.transport = t;
-	t->parser.data = &data;
-	t->transfer_finished = 0;
-	memset(&settings, 0x0, sizeof(settings));
-	settings.on_message_complete = on_message_complete_download_pack;
-	settings.on_body = on_body_download_pack;
-	*bytes = git_buf_len(oldbuf);
-
-	if (git_indexer_stream_add(idx, git_buf_cstr(oldbuf), git_buf_len(oldbuf), stats) < 0)
-		goto on_error;
-
-	do {
-		size_t parsed;
-
-		if ((recvd = gitno_recv(&buf)) < 0)
-			goto on_error;
-
-		parsed = http_parser_execute(&t->parser, &settings, buf.data, buf.offset);
-		if (parsed != buf.offset || t->error < 0)
-			goto on_error;
-
-		*bytes += recvd;
-		gitno_consume_n(&buf, parsed);
-	} while (recvd > 0 && !t->transfer_finished);
-
-	if (git_indexer_stream_finalize(idx, stats) < 0)
-		goto on_error;
-
-	git_indexer_stream_free(idx);
-	return 0;
-
-on_error:
-	git_indexer_stream_free(idx);
-	return -1;
-}
-
-static int http_close(git_transport *transport)
-{
-	transport_http *t = (transport_http *) transport;
-
-	if (gitno_close(t->socket) < 0) {
-		giterr_set(GITERR_OS, "Failed to close the socket: %s", strerror(errno));
-		return -1;
-	}
-
-	return 0;
-}
-
-
-static void http_free(git_transport *transport)
-{
-	transport_http *t = (transport_http *) transport;
-	git_vector *refs = &t->refs;
-	git_vector *common = &t->common;
-	unsigned int i;
-	git_pkt *p;
-
-#ifdef GIT_WIN32
-	/* cleanup the WSA context. note that this context
-	 * can be initialized more than once with WSAStartup(),
-	 * and needs to be cleaned one time for each init call
-	 */
-	WSACleanup();
-#endif
-
-	git_vector_foreach(refs, i, p) {
-		git_pkt_free(p);
-	}
-	git_vector_free(refs);
-	git_vector_foreach(common, i, p) {
-		git_pkt_free(p);
-	}
-	git_vector_free(common);
-	git_buf_free(&t->buf);
-	git_buf_free(&t->proto.buf);
-	git__free(t->heads);
-	git__free(t->content_type);
-	git__free(t->host);
-	git__free(t->port);
-	git__free(t->service);
-	git__free(t->parent.url);
-	git__free(t);
-}
-
-int git_transport_http(git_transport **out)
-{
-	transport_http *t;
-
-	t = git__malloc(sizeof(transport_http));
-	GITERR_CHECK_ALLOC(t);
-
-	memset(t, 0x0, sizeof(transport_http));
-
-	t->parent.connect = http_connect;
-	t->parent.ls = http_ls;
-	t->parent.negotiate_fetch = http_negotiate_fetch;
-	t->parent.download_pack = http_download_pack;
-	t->parent.close = http_close;
-	t->parent.free = http_free;
-	t->proto.refs = &t->refs;
-	t->proto.transport = (git_transport *) t;
-
-#ifdef GIT_WIN32
-	/* on win32, the WSA context needs to be initialized
-	 * before any socket calls can be performed */
-	if (WSAStartup(MAKEWORD(2,2), &t->wsd) != 0) {
-		http_free((git_transport *) t);
-		giterr_set(GITERR_OS, "Winsock init failed");
-		return -1;
-	}
-#endif
-
-	*out = (git_transport *) t;
-	return 0;
-}
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef GIT_WINHTTP
+
+#include "git2.h"
+#include "http_parser.h"
+#include "buffer.h"
+#include "netops.h"
+#include "smart.h"
+
+static const char *prefix_http = "http://";
+static const char *prefix_https = "https://";
+static const char *upload_pack_service = "upload-pack";
+static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
+static const char *upload_pack_service_url = "/git-upload-pack";
+static const char *receive_pack_service = "receive-pack";
+static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
+static const char *receive_pack_service_url = "/git-receive-pack";
+static const char *get_verb = "GET";
+static const char *post_verb = "POST";
+static const char *basic_authtype = "Basic";
+
+#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
+
+#define PARSE_ERROR_GENERIC	-1
+#define PARSE_ERROR_REPLAY	-2
+
+#define CHUNK_SIZE	4096
+
+enum last_cb {
+	NONE,
+	FIELD,
+	VALUE
+};
+
+typedef enum {
+	GIT_HTTP_AUTH_BASIC = 1,
+} http_authmechanism_t;
+
+typedef struct {
+	git_smart_subtransport_stream parent;
+	const char *service;
+	const char *service_url;
+	char *redirect_url;
+	const char *verb;
+	char *chunk_buffer;
+	unsigned chunk_buffer_len;
+	unsigned sent_request : 1,
+		received_response : 1,
+		chunked : 1,
+		redirect_count : 3;
+} http_stream;
+
+typedef struct {
+	git_smart_subtransport parent;
+	transport_smart *owner;
+	gitno_socket socket;
+	const char *path;
+	char *host;
+	char *port;
+	char *user_from_url;
+	char *pass_from_url;
+	git_cred *cred;
+	git_cred *url_cred;
+	http_authmechanism_t auth_mechanism;
+	unsigned connected : 1,
+		use_ssl : 1;
+
+	/* Parser structures */
+	http_parser parser;
+	http_parser_settings settings;
+	gitno_buffer parse_buffer;
+	git_buf parse_header_name;
+	git_buf parse_header_value;
+	char parse_buffer_data[2048];
+	char *content_type;
+	char *location;
+	git_vector www_authenticate;
+	enum last_cb last_cb;
+	int parse_error;
+	unsigned parse_finished : 1;
+} http_subtransport;
+
+typedef struct {
+	http_stream *s;
+	http_subtransport *t;
+
+	/* Target buffer details from read() */
+	char *buffer;
+	size_t buf_size;
+	size_t *bytes_read;
+} parser_context;
+
+static int apply_basic_credential(git_buf *buf, git_cred *cred)
+{
+	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+	git_buf raw = GIT_BUF_INIT;
+	int error = -1;
+
+	git_buf_printf(&raw, "%s:%s", c->username, c->password);
+
+	if (git_buf_oom(&raw) ||
+		git_buf_puts(buf, "Authorization: Basic ") < 0 ||
+		git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 ||
+		git_buf_puts(buf, "\r\n") < 0)
+		goto on_error;
+
+	error = 0;
+
+on_error:
+	if (raw.size)
+		memset(raw.ptr, 0x0, raw.size);
+
+	git_buf_free(&raw);
+	return error;
+}
+
+static int gen_request(
+	git_buf *buf,
+	http_stream *s,
+	size_t content_length)
+{
+	http_subtransport *t = OWNING_SUBTRANSPORT(s);
+
+	if (!t->path)
+		t->path = "/";
+
+	/* If we were redirected, make sure to respect that here */
+	if (s->redirect_url)
+		git_buf_printf(buf, "%s %s HTTP/1.1\r\n", s->verb, s->redirect_url);
+	else
+		git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, t->path, s->service_url);
+
+	git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
+	git_buf_printf(buf, "Host: %s\r\n", t->host);
+
+	if (s->chunked || content_length > 0) {
+		git_buf_printf(buf, "Accept: application/x-git-%s-result\r\n", s->service);
+		git_buf_printf(buf, "Content-Type: application/x-git-%s-request\r\n", s->service);
+
+		if (s->chunked)
+			git_buf_puts(buf, "Transfer-Encoding: chunked\r\n");
+		else
+			git_buf_printf(buf, "Content-Length: %"PRIuZ "\r\n", content_length);
+	} else
+		git_buf_puts(buf, "Accept: */*\r\n");
+
+	/* Apply credentials to the request */
+	if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
+		t->auth_mechanism == GIT_HTTP_AUTH_BASIC &&
+		apply_basic_credential(buf, t->cred) < 0)
+		return -1;
+
+	/* Use url-parsed basic auth if username and password are both provided */
+	if (!t->cred && t->user_from_url && t->pass_from_url) {
+		if (!t->url_cred &&
+			 git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+			return -1;
+		if (apply_basic_credential(buf, t->url_cred) < 0) return -1;
+	}
+
+	git_buf_puts(buf, "\r\n");
+
+	if (git_buf_oom(buf))
+		return -1;
+
+	return 0;
+}
+
+static int parse_unauthorized_response(
+	git_vector *www_authenticate,
+	int *allowed_types,
+	http_authmechanism_t *auth_mechanism)
+{
+	unsigned i;
+	char *entry;
+
+	git_vector_foreach(www_authenticate, i, entry) {
+		if (!strncmp(entry, basic_authtype, 5) &&
+			(entry[5] == '\0' || entry[5] == ' ')) {
+			*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+			*auth_mechanism = GIT_HTTP_AUTH_BASIC;
+		}
+	}
+
+	return 0;
+}
+
+static int on_header_ready(http_subtransport *t)
+{
+	git_buf *name = &t->parse_header_name;
+	git_buf *value = &t->parse_header_value;
+
+	if (!strcasecmp("Content-Type", git_buf_cstr(name))) {
+		if (!t->content_type) {
+			t->content_type = git__strdup(git_buf_cstr(value));
+			GITERR_CHECK_ALLOC(t->content_type);
+		}
+	}
+	else if (!strcmp("WWW-Authenticate", git_buf_cstr(name))) {
+		char *dup = git__strdup(git_buf_cstr(value));
+		GITERR_CHECK_ALLOC(dup);
+
+		git_vector_insert(&t->www_authenticate, dup);
+	}
+	else if (!strcasecmp("Location", git_buf_cstr(name))) {
+		if (!t->location) {
+			t->location= git__strdup(git_buf_cstr(value));
+			GITERR_CHECK_ALLOC(t->location);
+		}
+	}
+
+	return 0;
+}
+
+static int on_header_field(http_parser *parser, const char *str, size_t len)
+{
+	parser_context *ctx = (parser_context *) parser->data;
+	http_subtransport *t = ctx->t;
+
+	/* Both parse_header_name and parse_header_value are populated
+	 * and ready for consumption */
+	if (VALUE == t->last_cb)
+		if (on_header_ready(t) < 0)
+			return t->parse_error = PARSE_ERROR_GENERIC;
+
+	if (NONE == t->last_cb || VALUE == t->last_cb)
+		git_buf_clear(&t->parse_header_name);
+
+	if (git_buf_put(&t->parse_header_name, str, len) < 0)
+		return t->parse_error = PARSE_ERROR_GENERIC;
+
+	t->last_cb = FIELD;
+	return 0;
+}
+
+static int on_header_value(http_parser *parser, const char *str, size_t len)
+{
+	parser_context *ctx = (parser_context *) parser->data;
+	http_subtransport *t = ctx->t;
+
+	assert(NONE != t->last_cb);
+
+	if (FIELD == t->last_cb)
+		git_buf_clear(&t->parse_header_value);
+
+	if (git_buf_put(&t->parse_header_value, str, len) < 0)
+		return t->parse_error = PARSE_ERROR_GENERIC;
+
+	t->last_cb = VALUE;
+	return 0;
+}
+
+static int on_headers_complete(http_parser *parser)
+{
+	parser_context *ctx = (parser_context *) parser->data;
+	http_subtransport *t = ctx->t;
+	http_stream *s = ctx->s;
+	git_buf buf = GIT_BUF_INIT;
+
+	/* Both parse_header_name and parse_header_value are populated
+	 * and ready for consumption. */
+	if (VALUE == t->last_cb)
+		if (on_header_ready(t) < 0)
+			return t->parse_error = PARSE_ERROR_GENERIC;
+
+	/* Check for an authentication failure. */
+	if (parser->status_code == 401 &&
+		get_verb == s->verb &&
+		t->owner->cred_acquire_cb) {
+		int allowed_types = 0;
+
+		if (parse_unauthorized_response(&t->www_authenticate,
+			&allowed_types, &t->auth_mechanism) < 0)
+			return t->parse_error = PARSE_ERROR_GENERIC;
+
+		if (allowed_types &&
+			(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+
+			if (t->owner->cred_acquire_cb(&t->cred,
+					t->owner->url,
+					t->user_from_url,
+					allowed_types,
+					t->owner->cred_acquire_payload) < 0)
+				return PARSE_ERROR_GENERIC;
+
+			assert(t->cred);
+
+			/* Successfully acquired a credential. */
+			return t->parse_error = PARSE_ERROR_REPLAY;
+		}
+	}
+
+	/* Check for a redirect.
+	 * Right now we only permit a redirect to the same hostname. */
+	if ((parser->status_code == 301 ||
+		 parser->status_code == 302 ||
+		 (parser->status_code == 303 && get_verb == s->verb) ||
+		 parser->status_code == 307) &&
+		t->location) {
+
+		if (s->redirect_count >= 7) {
+			giterr_set(GITERR_NET, "Too many redirects");
+			return t->parse_error = PARSE_ERROR_GENERIC;
+		}
+
+		if (t->location[0] != '/') {
+			giterr_set(GITERR_NET, "Only relative redirects are supported");
+			return t->parse_error = PARSE_ERROR_GENERIC;
+		}
+
+		/* Set the redirect URL on the stream. This is a transfer of
+		 * ownership of the memory. */
+		if (s->redirect_url)
+			git__free(s->redirect_url);
+
+		s->redirect_url = t->location;
+		t->location = NULL;
+
+		t->connected = 0;
+		s->redirect_count++;
+
+		return t->parse_error = PARSE_ERROR_REPLAY;
+	}
+
+	/* Check for a 200 HTTP status code. */
+	if (parser->status_code != 200) {
+		giterr_set(GITERR_NET,
+			"Unexpected HTTP status code: %d",
+			parser->status_code);
+		return t->parse_error = PARSE_ERROR_GENERIC;
+	}
+
+	/* The response must contain a Content-Type header. */
+	if (!t->content_type) {
+		giterr_set(GITERR_NET, "No Content-Type header in response");
+		return t->parse_error = PARSE_ERROR_GENERIC;
+	}
+
+	/* The Content-Type header must match our expectation. */
+	if (get_verb == s->verb)
+		git_buf_printf(&buf,
+			"application/x-git-%s-advertisement",
+			ctx->s->service);
+	else
+		git_buf_printf(&buf,
+			"application/x-git-%s-result",
+			ctx->s->service);
+
+	if (git_buf_oom(&buf))
+		return t->parse_error = PARSE_ERROR_GENERIC;
+
+	if (strcmp(t->content_type, git_buf_cstr(&buf))) {
+		git_buf_free(&buf);
+		giterr_set(GITERR_NET,
+			"Invalid Content-Type: %s",
+			t->content_type);
+		return t->parse_error = PARSE_ERROR_GENERIC;
+	}
+
+	git_buf_free(&buf);
+
+	return 0;
+}
+
+static int on_message_complete(http_parser *parser)
+{
+	parser_context *ctx = (parser_context *) parser->data;
+	http_subtransport *t = ctx->t;
+
+	t->parse_finished = 1;
+
+	return 0;
+}
+
+static int on_body_fill_buffer(http_parser *parser, const char *str, size_t len)
+{
+	parser_context *ctx = (parser_context *) parser->data;
+	http_subtransport *t = ctx->t;
+
+	if (ctx->buf_size < len) {
+		giterr_set(GITERR_NET, "Can't fit data in the buffer");
+		return t->parse_error = PARSE_ERROR_GENERIC;
+	}
+
+	memcpy(ctx->buffer, str, len);
+	*(ctx->bytes_read) += len;
+	ctx->buffer += len;
+	ctx->buf_size -= len;
+
+	return 0;
+}
+
+static void clear_parser_state(http_subtransport *t)
+{
+	unsigned i;
+	char *entry;
+
+	http_parser_init(&t->parser, HTTP_RESPONSE);
+	gitno_buffer_setup(&t->socket,
+		&t->parse_buffer,
+		t->parse_buffer_data,
+		sizeof(t->parse_buffer_data));
+
+	t->last_cb = NONE;
+	t->parse_error = 0;
+	t->parse_finished = 0;
+
+	git_buf_free(&t->parse_header_name);
+	git_buf_init(&t->parse_header_name, 0);
+
+	git_buf_free(&t->parse_header_value);
+	git_buf_init(&t->parse_header_value, 0);
+
+	git__free(t->content_type);
+	t->content_type = NULL;
+
+	git__free(t->location);
+	t->location = NULL;
+
+	git_vector_foreach(&t->www_authenticate, i, entry)
+		git__free(entry);
+
+	git_vector_free(&t->www_authenticate);
+}
+
+static int write_chunk(gitno_socket *socket, const char *buffer, size_t len)
+{
+	git_buf buf = GIT_BUF_INIT;
+
+	/* Chunk header */
+	git_buf_printf(&buf, "%X\r\n", (unsigned)len);
+
+	if (git_buf_oom(&buf))
+		return -1;
+
+	if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) {
+		git_buf_free(&buf);
+		return -1;
+	}
+
+	git_buf_free(&buf);
+
+	/* Chunk body */
+	if (len > 0 && gitno_send(socket, buffer, len, 0) < 0)
+		return -1;
+
+	/* Chunk footer */
+	if (gitno_send(socket, "\r\n", 2, 0) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int http_connect(http_subtransport *t)
+{
+	int flags = 0;
+
+	if (t->connected &&
+		http_should_keep_alive(&t->parser) &&
+		http_body_is_final(&t->parser))
+		return 0;
+
+	if (t->socket.socket)
+		gitno_close(&t->socket);
+
+	if (t->use_ssl) {
+		int tflags;
+
+		if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0)
+			return -1;
+
+		flags |= GITNO_CONNECT_SSL;
+
+		if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags)
+			flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT;
+	}
+
+	if (gitno_connect(&t->socket, t->host, t->port, flags) < 0)
+		return -1;
+
+	t->connected = 1;
+	return 0;
+}
+
+static int http_stream_read(
+	git_smart_subtransport_stream *stream,
+	char *buffer,
+	size_t buf_size,
+	size_t *bytes_read)
+{
+	http_stream *s = (http_stream *)stream;
+	http_subtransport *t = OWNING_SUBTRANSPORT(s);
+	parser_context ctx;
+	size_t bytes_parsed;
+
+replay:
+	*bytes_read = 0;
+
+	assert(t->connected);
+
+	if (!s->sent_request) {
+		git_buf request = GIT_BUF_INIT;
+
+		clear_parser_state(t);
+
+		if (gen_request(&request, s, 0) < 0) {
+			giterr_set(GITERR_NET, "Failed to generate request");
+			return -1;
+		}
+
+		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
+			git_buf_free(&request);
+			return -1;
+		}
+
+		git_buf_free(&request);
+
+		s->sent_request = 1;
+	}
+
+	if (!s->received_response) {
+		if (s->chunked) {
+			assert(s->verb == post_verb);
+
+			/* Flush, if necessary */
+			if (s->chunk_buffer_len > 0 &&
+				write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+
+			/* Write the final chunk. */
+			if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0)
+				return -1;
+		}
+
+		s->received_response = 1;
+	}
+
+	while (!*bytes_read && !t->parse_finished) {
+		t->parse_buffer.offset = 0;
+
+		if (gitno_recv(&t->parse_buffer) < 0)
+			return -1;
+
+		/* This call to http_parser_execute will result in invocations of the
+		 * on_* family of callbacks. The most interesting of these is
+		 * on_body_fill_buffer, which is called when data is ready to be copied
+		 * into the target buffer. We need to marshal the buffer, buf_size, and
+		 * bytes_read parameters to this callback. */
+		ctx.t = t;
+		ctx.s = s;
+		ctx.buffer = buffer;
+		ctx.buf_size = buf_size;
+		ctx.bytes_read = bytes_read;
+
+		/* Set the context, call the parser, then unset the context. */
+		t->parser.data = &ctx;
+
+		bytes_parsed = http_parser_execute(&t->parser,
+			&t->settings,
+			t->parse_buffer.data,
+			t->parse_buffer.offset);
+
+		t->parser.data = NULL;
+
+		/* If there was a handled authentication failure, then parse_error
+		 * will have signaled us that we should replay the request. */
+		if (PARSE_ERROR_REPLAY == t->parse_error) {
+			s->sent_request = 0;
+
+			if (http_connect(t) < 0)
+				return -1;
+
+			goto replay;
+		}
+
+		if (t->parse_error < 0)
+			return -1;
+
+		if (bytes_parsed != t->parse_buffer.offset) {
+			giterr_set(GITERR_NET,
+				"HTTP parser error: %s",
+				http_errno_description((enum http_errno)t->parser.http_errno));
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+static int http_stream_write_chunked(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
+{
+	http_stream *s = (http_stream *)stream;
+	http_subtransport *t = OWNING_SUBTRANSPORT(s);
+
+	assert(t->connected);
+
+	/* Send the request, if necessary */
+	if (!s->sent_request) {
+		git_buf request = GIT_BUF_INIT;
+
+		clear_parser_state(t);
+
+		if (gen_request(&request, s, 0) < 0) {
+			giterr_set(GITERR_NET, "Failed to generate request");
+			return -1;
+		}
+
+		if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) {
+			git_buf_free(&request);
+			return -1;
+		}
+
+		git_buf_free(&request);
+
+		s->sent_request = 1;
+	}
+
+	if (len > CHUNK_SIZE) {
+		/* Flush, if necessary */
+		if (s->chunk_buffer_len > 0) {
+			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+		}
+
+		/* Write chunk directly */
+		if (write_chunk(&t->socket, buffer, len) < 0)
+			return -1;
+	}
+	else {
+		/* Append as much to the buffer as we can */
+		int count = min(CHUNK_SIZE - s->chunk_buffer_len, len);
+
+		if (!s->chunk_buffer)
+			s->chunk_buffer = git__malloc(CHUNK_SIZE);
+
+		memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
+		s->chunk_buffer_len += count;
+		buffer += count;
+		len -= count;
+
+		/* Is the buffer full? If so, then flush */
+		if (CHUNK_SIZE == s->chunk_buffer_len) {
+			if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+
+			if (len > 0) {
+				memcpy(s->chunk_buffer, buffer, len);
+				s->chunk_buffer_len = len;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int http_stream_write_single(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
+{
+	http_stream *s = (http_stream *)stream;
+	http_subtransport *t = OWNING_SUBTRANSPORT(s);
+	git_buf request = GIT_BUF_INIT;
+
+	assert(t->connected);
+
+	if (s->sent_request) {
+		giterr_set(GITERR_NET, "Subtransport configured for only one write");
+		return -1;
+	}
+
+	clear_parser_state(t);
+
+	if (gen_request(&request, s, len) < 0) {
+		giterr_set(GITERR_NET, "Failed to generate request");
+		return -1;
+	}
+
+	if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0)
+		goto on_error;
+
+	if (len && gitno_send(&t->socket, buffer, len, 0) < 0)
+		goto on_error;
+
+	git_buf_free(&request);
+	s->sent_request = 1;
+
+	return 0;
+
+on_error:
+	git_buf_free(&request);
+	return -1;
+}
+
+static void http_stream_free(git_smart_subtransport_stream *stream)
+{
+	http_stream *s = (http_stream *)stream;
+
+	if (s->chunk_buffer)
+		git__free(s->chunk_buffer);
+
+	if (s->redirect_url)
+		git__free(s->redirect_url);
+
+	git__free(s);
+}
+
+static int http_stream_alloc(http_subtransport *t,
+	git_smart_subtransport_stream **stream)
+{
+	http_stream *s;
+
+	if (!stream)
+		return -1;
+
+	s = git__calloc(sizeof(http_stream), 1);
+	GITERR_CHECK_ALLOC(s);
+
+	s->parent.subtransport = &t->parent;
+	s->parent.read = http_stream_read;
+	s->parent.write = http_stream_write_single;
+	s->parent.free = http_stream_free;
+
+	*stream = (git_smart_subtransport_stream *)s;
+	return 0;
+}
+
+static int http_uploadpack_ls(
+	http_subtransport *t,
+	git_smart_subtransport_stream **stream)
+{
+	http_stream *s;
+
+	if (http_stream_alloc(t, stream) < 0)
+		return -1;
+
+	s = (http_stream *)*stream;
+
+	s->service = upload_pack_service;
+	s->service_url = upload_pack_ls_service_url;
+	s->verb = get_verb;
+
+	return 0;
+}
+
+static int http_uploadpack(
+	http_subtransport *t,
+	git_smart_subtransport_stream **stream)
+{
+	http_stream *s;
+
+	if (http_stream_alloc(t, stream) < 0)
+		return -1;
+
+	s = (http_stream *)*stream;
+
+	s->service = upload_pack_service;
+	s->service_url = upload_pack_service_url;
+	s->verb = post_verb;
+
+	return 0;
+}
+
+static int http_receivepack_ls(
+	http_subtransport *t,
+	git_smart_subtransport_stream **stream)
+{
+	http_stream *s;
+
+	if (http_stream_alloc(t, stream) < 0)
+		return -1;
+
+	s = (http_stream *)*stream;
+
+	s->service = receive_pack_service;
+	s->service_url = receive_pack_ls_service_url;
+	s->verb = get_verb;
+
+	return 0;
+}
+
+static int http_receivepack(
+	http_subtransport *t,
+	git_smart_subtransport_stream **stream)
+{
+	http_stream *s;
+
+	if (http_stream_alloc(t, stream) < 0)
+		return -1;
+
+	s = (http_stream *)*stream;
+
+	/* Use Transfer-Encoding: chunked for this request */
+	s->chunked = 1;
+	s->parent.write = http_stream_write_chunked;
+
+	s->service = receive_pack_service;
+	s->service_url = receive_pack_service_url;
+	s->verb = post_verb;
+
+	return 0;
+}
+
+static int http_action(
+	git_smart_subtransport_stream **stream,
+	git_smart_subtransport *subtransport,
+	const char *url,
+	git_smart_service_t action)
+{
+	http_subtransport *t = (http_subtransport *)subtransport;
+	const char *default_port = NULL;
+	int ret;
+
+	if (!stream)
+		return -1;
+
+	if (!t->host || !t->port || !t->path) {
+		if (!git__prefixcmp(url, prefix_http)) {
+			url = url + strlen(prefix_http);
+			default_port = "80";
+		}
+
+		if (!git__prefixcmp(url, prefix_https)) {
+			url += strlen(prefix_https);
+			default_port = "443";
+			t->use_ssl = 1;
+		}
+
+		if (!default_port)
+			return -1;
+
+		if ((ret = gitno_extract_url_parts(&t->host, &t->port,
+						&t->user_from_url, &t->pass_from_url, url, default_port)) < 0)
+			return ret;
+
+		t->path = strchr(url, '/');
+	}
+
+	if (http_connect(t) < 0)
+		return -1;
+
+	switch (action)
+	{
+		case GIT_SERVICE_UPLOADPACK_LS:
+			return http_uploadpack_ls(t, stream);
+
+		case GIT_SERVICE_UPLOADPACK:
+			return http_uploadpack(t, stream);
+
+		case GIT_SERVICE_RECEIVEPACK_LS:
+			return http_receivepack_ls(t, stream);
+
+		case GIT_SERVICE_RECEIVEPACK:
+			return http_receivepack(t, stream);
+	}
+
+	*stream = NULL;
+	return -1;
+}
+
+static int http_close(git_smart_subtransport *subtransport)
+{
+	http_subtransport *t = (http_subtransport *) subtransport;
+
+	clear_parser_state(t);
+
+	if (t->socket.socket) {
+		gitno_close(&t->socket);
+		memset(&t->socket, 0x0, sizeof(gitno_socket));
+	}
+
+	if (t->cred) {
+		t->cred->free(t->cred);
+		t->cred = NULL;
+	}
+
+	if (t->url_cred) {
+		t->url_cred->free(t->url_cred);
+		t->url_cred = NULL;
+	}
+
+	if (t->host) {
+		git__free(t->host);
+		t->host = NULL;
+	}
+
+	if (t->port) {
+		git__free(t->port);
+		t->port = NULL;
+	}
+
+	if (t->user_from_url) {
+		git__free(t->user_from_url);
+		t->user_from_url = NULL;
+	}
+
+	if (t->pass_from_url) {
+		git__free(t->pass_from_url);
+		t->pass_from_url = NULL;
+	}
+
+	return 0;
+}
+
+static void http_free(git_smart_subtransport *subtransport)
+{
+	http_subtransport *t = (http_subtransport *) subtransport;
+
+	http_close(subtransport);
+
+	git__free(t);
+}
+
+int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
+{
+	http_subtransport *t;
+
+	if (!out)
+		return -1;
+
+	t = git__calloc(sizeof(http_subtransport), 1);
+	GITERR_CHECK_ALLOC(t);
+
+	t->owner = (transport_smart *)owner;
+	t->parent.action = http_action;
+	t->parent.close = http_close;
+	t->parent.free = http_free;
+
+	t->settings.on_header_field = on_header_field;
+	t->settings.on_header_value = on_header_value;
+	t->settings.on_headers_complete = on_headers_complete;
+	t->settings.on_body = on_body_fill_buffer;
+	t->settings.on_message_complete = on_message_complete;
+
+	*out = (git_smart_subtransport *) t;
+	return 0;
+}
+
+#endif /* !GIT_WINHTTP */
diff --git a/libgit2/src/transports/local.c b/libgit2/src/transports/local.c
--- a/libgit2/src/transports/local.c
+++ b/libgit2/src/transports/local.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,16 +10,34 @@
 #include "git2/repository.h"
 #include "git2/object.h"
 #include "git2/tag.h"
+#include "git2/transport.h"
+#include "git2/revwalk.h"
+#include "git2/odb_backend.h"
+#include "git2/pack.h"
+#include "git2/commit.h"
+#include "git2/revparse.h"
+#include "git2/push.h"
+#include "pack-objects.h"
 #include "refs.h"
-#include "transport.h"
 #include "posix.h"
 #include "path.h"
 #include "buffer.h"
+#include "repository.h"
+#include "odb.h"
+#include "push.h"
+#include "remote.h"
 
 typedef struct {
 	git_transport parent;
+	git_remote *owner;
+	char *url;
+	int direction;
+	int flags;
+	git_atomic cancelled;
 	git_repository *repo;
 	git_vector refs;
+	unsigned connected : 1,
+		have_refs : 1;
 } transport_local;
 
 static int add_ref(transport_local *t, const char *name)
@@ -28,15 +46,28 @@
 	git_remote_head *head;
 	git_object *obj = NULL, *target = NULL;
 	git_buf buf = GIT_BUF_INIT;
+	int error;
 
-	head = git__malloc(sizeof(git_remote_head));
+	head = git__calloc(1, sizeof(git_remote_head));
 	GITERR_CHECK_ALLOC(head);
 
 	head->name = git__strdup(name);
 	GITERR_CHECK_ALLOC(head->name);
 
-	if (git_reference_name_to_oid(&head->oid, t->repo, name) < 0 ||
-		git_vector_insert(&t->refs, head) < 0)
+	error = git_reference_name_to_id(&head->oid, t->repo, name);
+	if (error < 0) {
+		git__free(head->name);
+		git__free(head);
+		if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
+			/* This is actually okay.  Empty repos often have a HEAD that points to
+			 * a nonexistent "refs/heads/master". */
+			giterr_clear();
+			return 0;
+		}
+		return error;
+	}
+
+	if (git_vector_insert(&t->refs, head) < 0)
 	{
 		git__free(head->name);
 		git__free(head);
@@ -52,14 +83,16 @@
 
 	head = NULL;
 
-	/* If it's not an annotated tag, just get out */
-	if (git_object_type(obj) != GIT_OBJ_TAG) {
+	/* If it's not an annotated tag, or if we're mocking
+	 * git-receive-pack, just get out */
+	if (git_object_type(obj) != GIT_OBJ_TAG ||
+		t->direction != GIT_DIRECTION_FETCH) {
 		git_object_free(obj);
 		return 0;
 	}
 
 	/* And if it's a tag, peel it, and add it to the list */
-	head = git__malloc(sizeof(git_remote_head));
+	head = git__calloc(1, sizeof(git_remote_head));
 	GITERR_CHECK_ALLOC(head);
 	if (git_buf_join(&buf, 0, name, peeled) < 0)
 		return -1;
@@ -92,14 +125,14 @@
 	assert(t);
 
 	if (git_reference_list(&ref_names, t->repo, GIT_REF_LISTALL) < 0 ||
-		git_vector_init(&t->refs, (unsigned int)ref_names.count, NULL) < 0)
+		git_vector_init(&t->refs, ref_names.count, NULL) < 0)
 		goto on_error;
 
 	/* Sort the references first */
 	git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
 
-	/* Add HEAD */
-	if (add_ref(t, GIT_HEAD_FILE) < 0)
+	/* Add HEAD iff direction is fetch */
+	if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
 		goto on_error;
 
 	for (i = 0; i < ref_names.count; ++i) {
@@ -107,6 +140,7 @@
 			goto on_error;
 	}
 
+	t->have_refs = 1;
 	git_strarray_free(&ref_names);
 	return 0;
 
@@ -116,28 +150,16 @@
 	return -1;
 }
 
-static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
-{
-	transport_local  *t = (transport_local *) transport;
-	git_vector *refs = &t->refs;
-	unsigned int i;
-	git_remote_head *h;
-
-	assert(transport && transport->connected);
-
-	git_vector_foreach(refs, i, h) {
-		if (list_cb(h, payload) < 0)
-			return -1;
-	}
-
-	return 0;
-}
-
 /*
  * Try to open the url as a git directory. The direction doesn't
  * matter in this case because we're calulating the heads ourselves.
  */
-static int local_connect(git_transport *transport, int direction)
+static int local_connect(
+	git_transport *transport,
+	const char *url,
+	git_cred_acquire_cb cred_acquire_cb,
+	void *cred_acquire_payload,
+	int direction, int flags)
 {
 	git_repository *repo;
 	int error;
@@ -145,18 +167,24 @@
 	const char *path;
 	git_buf buf = GIT_BUF_INIT;
 
-	GIT_UNUSED(direction);
+	GIT_UNUSED(cred_acquire_cb);
+	GIT_UNUSED(cred_acquire_payload);
 
+	t->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(t->url);
+	t->direction = direction;
+	t->flags = flags;
+
 	/* The repo layer doesn't want the prefix */
-	if (!git__prefixcmp(transport->url, "file://")) {
-		if (git_path_fromurl(&buf, transport->url) < 0) {
+	if (!git__prefixcmp(t->url, "file://")) {
+		if (git_path_fromurl(&buf, t->url) < 0) {
 			git_buf_free(&buf);
 			return -1;
 		}
 		path = git_buf_cstr(&buf);
 
 	} else { /* We assume transport->url is already a path */
-		path = transport->url;
+		path = t->url;
 	}
 
 	error = git_repository_open(&repo, path);
@@ -171,47 +199,411 @@
 	if (store_refs(t) < 0)
 		return -1;
 
-	t->parent.connected = 1;
+	t->connected = 1;
 
 	return 0;
 }
 
-static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
+static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
 {
-	GIT_UNUSED(transport);
-	GIT_UNUSED(repo);
-	GIT_UNUSED(wants);
+	transport_local *t = (transport_local *)transport;
+	unsigned int i;
+	git_remote_head *head = NULL;
 
-	giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");
-	return -1;
+	if (!t->have_refs) {
+		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
+		return -1;
+	}
+
+	git_vector_foreach(&t->refs, i, head) {
+		if (list_cb(head, payload))
+			return GIT_EUSER;
+	}
+
+	return 0;
 }
 
+static int local_negotiate_fetch(
+	git_transport *transport,
+	git_repository *repo,
+	const git_remote_head * const *refs,
+	size_t count)
+{
+	transport_local *t = (transport_local*)transport;
+	git_remote_head *rhead;
+	unsigned int i;
+
+	GIT_UNUSED(refs);
+	GIT_UNUSED(count);
+
+	/* Fill in the loids */
+	git_vector_foreach(&t->refs, i, rhead) {
+		git_object *obj;
+
+		int error = git_revparse_single(&obj, repo, rhead->name);
+		if (!error)
+			git_oid_cpy(&rhead->loid, git_object_id(obj));
+		else if (error != GIT_ENOTFOUND)
+			return error;
+		git_object_free(obj);
+		giterr_clear();
+	}
+
+	return 0;
+}
+
+static int local_push_copy_object(
+	git_odb *local_odb,
+	git_odb *remote_odb,
+	git_pobject *obj)
+{
+	int error = 0;
+	git_odb_object *odb_obj = NULL;
+	git_odb_stream *odb_stream;
+	size_t odb_obj_size;
+	git_otype odb_obj_type;
+	git_oid remote_odb_obj_oid;
+
+	/* Object already exists in the remote ODB; do nothing and return 0*/
+	if (git_odb_exists(remote_odb, &obj->id))
+		return 0;
+
+	if ((error = git_odb_read(&odb_obj, local_odb, &obj->id)) < 0)
+		return error;
+
+	odb_obj_size = git_odb_object_size(odb_obj);
+	odb_obj_type = git_odb_object_type(odb_obj);
+
+	if ((error = git_odb_open_wstream(&odb_stream, remote_odb,
+		odb_obj_size, odb_obj_type)) < 0)
+		goto on_error;
+
+	if (odb_stream->write(odb_stream, (char *)git_odb_object_data(odb_obj),
+		odb_obj_size) < 0 ||
+		odb_stream->finalize_write(&remote_odb_obj_oid, odb_stream) < 0) {
+		error = -1;
+	} else if (git_oid_cmp(&obj->id, &remote_odb_obj_oid) != 0) {
+		giterr_set(GITERR_ODB, "Error when writing object to remote odb "
+			"during local push operation. Remote odb object oid does not "
+			"match local oid.");
+		error = -1;
+	}
+
+	odb_stream->free(odb_stream);
+
+on_error:
+	git_odb_object_free(odb_obj);
+	return error;
+}
+
+static int local_push_update_remote_ref(
+	git_repository *remote_repo,
+	const char *lref,
+	const char *rref,
+	git_oid *loid,
+	git_oid *roid)
+{
+	int error;
+	git_reference *remote_ref = NULL;
+
+	/* rref will be NULL if it is implicit in the pushspec (e.g. 'b1:') */
+	rref = rref ? rref : lref;
+
+	if (lref) {
+		/* Create or update a ref */
+		if ((error = git_reference_create(NULL, remote_repo, rref, loid,
+				!git_oid_iszero(roid))) < 0)
+			return error;
+	} else {
+		/* Delete a ref */
+		if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+			return error;
+		}
+
+		if ((error = git_reference_delete(remote_ref)) < 0)
+			return error;
+
+		git_reference_free(remote_ref);
+	}
+
+	return 0;
+}
+
+static int local_push(
+	git_transport *transport,
+	git_push *push)
+{
+	transport_local *t = (transport_local *)transport;
+	git_odb *remote_odb = NULL;
+	git_odb *local_odb = NULL;
+	git_repository *remote_repo = NULL;
+	push_spec *spec;
+	char *url = NULL;
+	int error;
+	unsigned int i;
+	size_t j;
+
+	if ((error = git_repository_open(&remote_repo, push->remote->url)) < 0)
+		return error;
+
+	/* We don't currently support pushing locally to non-bare repos. Proper 
+	   non-bare repo push support would require checking configs to see if
+	   we should override the default 'don't let this happen' behavior */
+	if (!remote_repo->is_bare) {
+		error = -1;
+		goto on_error;
+	}
+
+	if ((error = git_repository_odb__weakptr(&remote_odb, remote_repo)) < 0 ||
+		(error = git_repository_odb__weakptr(&local_odb, push->repo)) < 0)
+		goto on_error;
+
+	for (i = 0; i < push->pb->nr_objects; i++) {
+		if ((error = local_push_copy_object(local_odb, remote_odb,
+			&push->pb->object_list[i])) < 0)
+			goto on_error;
+	}
+
+	push->unpack_ok = 1;
+
+	git_vector_foreach(&push->specs, j, spec) {
+		push_status *status;
+		const git_error *last;
+		char *ref = spec->rref ? spec->rref : spec->lref;
+
+		status = git__calloc(sizeof(push_status), 1);
+		if (!status)
+			goto on_error;
+
+		status->ref = git__strdup(ref);
+		if (!status->ref) {
+			git_push_status_free(status);
+			goto on_error;
+		}
+
+		error = local_push_update_remote_ref(remote_repo, spec->lref, spec->rref,
+			&spec->loid, &spec->roid);
+
+		switch (error) {
+			case GIT_OK:
+				break;
+			case GIT_EINVALIDSPEC:
+				status->msg = git__strdup("funny refname");
+				break;
+			case GIT_ENOTFOUND:
+				status->msg = git__strdup("Remote branch not found to delete");
+				break;
+			default:
+				last = giterr_last();
+
+				if (last && last->message)
+					status->msg = git__strdup(last->message);
+				else
+					status->msg = git__strdup("Unspecified error encountered");
+				break;
+		}
+
+		/* failed to allocate memory for a status message */
+		if (error < 0 && !status->msg) {
+			git_push_status_free(status);
+			goto on_error;
+		}
+
+		/* failed to insert the ref update status */
+		if ((error = git_vector_insert(&push->status, status)) < 0) {
+			git_push_status_free(status);
+			goto on_error;
+		}
+	}
+
+	if (push->specs.length) {
+		int flags = t->flags;
+		url = git__strdup(t->url);
+
+		if (!url || t->parent.close(&t->parent) < 0 ||
+			t->parent.connect(&t->parent, url,
+			push->remote->cred_acquire_cb, NULL, GIT_DIRECTION_PUSH, flags))
+			goto on_error;
+	}
+
+	error = 0;
+
+on_error:
+	git_repository_free(remote_repo);
+	git__free(url);
+
+	return error;
+}
+
+typedef struct foreach_data {
+	git_transfer_progress *stats;
+	git_transfer_progress_callback progress_cb;
+	void *progress_payload;
+	git_odb_writepack *writepack;
+} foreach_data;
+
+static int foreach_cb(void *buf, size_t len, void *payload)
+{
+	foreach_data *data = (foreach_data*)payload;
+
+	data->stats->received_bytes += len;
+	return data->writepack->add(data->writepack, buf, len, data->stats);
+}
+
+static int local_download_pack(
+		git_transport *transport,
+		git_repository *repo,
+		git_transfer_progress *stats,
+		git_transfer_progress_callback progress_cb,
+		void *progress_payload)
+{
+	transport_local *t = (transport_local*)transport;
+	git_revwalk *walk = NULL;
+	git_remote_head *rhead;
+	unsigned int i;
+	int error = -1;
+	git_oid oid;
+	git_packbuilder *pack = NULL;
+	git_odb_writepack *writepack = NULL;
+	git_odb *odb = NULL;
+
+	if ((error = git_revwalk_new(&walk, t->repo)) < 0)
+		goto cleanup;
+	git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+	if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
+		goto cleanup;
+
+	stats->total_objects = 0;
+	stats->indexed_objects = 0;
+	stats->received_objects = 0;
+	stats->received_bytes = 0;
+
+	git_vector_foreach(&t->refs, i, rhead) {
+		git_object *obj;
+		if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJ_ANY)) < 0)
+			goto cleanup;
+
+		if (git_object_type(obj) == GIT_OBJ_COMMIT) {
+			/* Revwalker includes only wanted commits */
+			error = git_revwalk_push(walk, &rhead->oid);
+			if (!git_oid_iszero(&rhead->loid))
+				error = git_revwalk_hide(walk, &rhead->loid);
+		} else {
+			/* Tag or some other wanted object. Add it on its own */
+			error = git_packbuilder_insert(pack, &rhead->oid, rhead->name);
+		}
+      git_object_free(obj);
+	}
+
+	/* Walk the objects, building a packfile */
+	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+		goto cleanup;
+
+	while ((error = git_revwalk_next(&oid, walk)) == 0) {
+		git_commit *commit;
+
+		/* Skip commits we already have */
+		if (git_odb_exists(odb, &oid)) continue;
+
+		if (!git_object_lookup((git_object**)&commit, t->repo, &oid, GIT_OBJ_COMMIT)) {
+			const git_oid *tree_oid = git_commit_tree_id(commit);
+
+			/* Add the commit and its tree */
+			if ((error = git_packbuilder_insert(pack, &oid, NULL)) < 0 ||
+				 (error = git_packbuilder_insert_tree(pack, tree_oid)) < 0) {
+				git_commit_free(commit);
+				goto cleanup;
+			}
+
+			git_commit_free(commit);
+		}
+	}
+
+	if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0)
+		goto cleanup;
+
+	/* Write the data to the ODB */
+	{
+		foreach_data data = {0};
+		data.stats = stats;
+		data.progress_cb = progress_cb;
+		data.progress_payload = progress_payload;
+		data.writepack = writepack;
+
+		if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) < 0)
+			goto cleanup;
+	}
+	error = writepack->commit(writepack, stats);
+
+cleanup:
+	if (writepack) writepack->free(writepack);
+	git_packbuilder_free(pack);
+	git_revwalk_free(walk);
+	return error;
+}
+
+static int local_is_connected(git_transport *transport)
+{
+	transport_local *t = (transport_local *)transport;
+
+	return t->connected;
+}
+
+static int local_read_flags(git_transport *transport, int *flags)
+{
+	transport_local *t = (transport_local *)transport;
+
+	*flags = t->flags;
+
+	return 0;
+}
+
+static void local_cancel(git_transport *transport)
+{
+	transport_local *t = (transport_local *)transport;
+
+	git_atomic_set(&t->cancelled, 1);
+}
+
 static int local_close(git_transport *transport)
 {
 	transport_local *t = (transport_local *)transport;
 
-	git_repository_free(t->repo);
-	t->repo = NULL;
+	t->connected = 0;
 
+	if (t->repo) {
+		git_repository_free(t->repo);
+		t->repo = NULL;
+	}
+
+	if (t->url) {
+		git__free(t->url);
+		t->url = NULL;
+	}
+
 	return 0;
 }
 
 static void local_free(git_transport *transport)
 {
-	unsigned int i;
-	transport_local *t = (transport_local *) transport;
-	git_vector *vec = &t->refs;
-	git_remote_head *h;
+	transport_local *t = (transport_local *)transport;
+	size_t i;
+	git_remote_head *head;
 
-	assert(transport);
+	/* Close the transport, if it's still open. */
+	local_close(transport);
 
-	git_vector_foreach (vec, i, h) {
-		git__free(h->name);
-		git__free(h);
+	git_vector_foreach(&t->refs, i, head) {
+		git__free(head->name);
+		git__free(head);
 	}
-	git_vector_free(vec);
 
-	git__free(t->parent.url);
+	git_vector_free(&t->refs);
+
+	/* Free the transport */
 	git__free(t);
 }
 
@@ -219,20 +611,28 @@
  * Public API *
  **************/
 
-int git_transport_local(git_transport **out)
+int git_transport_local(git_transport **out, git_remote *owner, void *param)
 {
 	transport_local *t;
 
-	t = git__malloc(sizeof(transport_local));
-	GITERR_CHECK_ALLOC(t);
+	GIT_UNUSED(param);
 
-	memset(t, 0x0, sizeof(transport_local));
+	t = git__calloc(1, sizeof(transport_local));
+	GITERR_CHECK_ALLOC(t);
 
+	t->parent.version = GIT_TRANSPORT_VERSION;
 	t->parent.connect = local_connect;
-	t->parent.ls = local_ls;
 	t->parent.negotiate_fetch = local_negotiate_fetch;
+	t->parent.download_pack = local_download_pack;
+	t->parent.push = local_push;
 	t->parent.close = local_close;
 	t->parent.free = local_free;
+	t->parent.ls = local_ls;
+	t->parent.is_connected = local_is_connected;
+	t->parent.read_flags = local_read_flags;
+	t->parent.cancel = local_cancel;
+
+	t->owner = owner;
 
 	*out = (git_transport *) t;
 
diff --git a/libgit2/src/transports/smart.c b/libgit2/src/transports/smart.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/smart.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "git2.h"
+#include "smart.h"
+#include "refs.h"
+
+static int git_smart__recv_cb(gitno_buffer *buf)
+{
+	transport_smart *t = (transport_smart *) buf->cb_data;
+	size_t old_len, bytes_read;
+	int error;
+
+	assert(t->current_stream);
+
+	old_len = buf->offset;
+
+	if ((error = t->current_stream->read(t->current_stream, buf->data + buf->offset, buf->len - buf->offset, &bytes_read)) < 0)
+		return error;
+
+	buf->offset += bytes_read;
+
+	if (t->packetsize_cb)
+		t->packetsize_cb(bytes_read, t->packetsize_payload);
+
+	return (int)(buf->offset - old_len);
+}
+
+GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport)
+{
+	if (t->current_stream) {
+		t->current_stream->free(t->current_stream);
+		t->current_stream = NULL;
+	}
+
+	if (close_subtransport &&
+		t->wrapped->close(t->wrapped) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int git_smart__set_callbacks(
+	git_transport *transport,
+	git_transport_message_cb progress_cb,
+	git_transport_message_cb error_cb,
+	void *message_cb_payload)
+{
+	transport_smart *t = (transport_smart *)transport;
+
+	t->progress_cb = progress_cb;
+	t->error_cb = error_cb;
+	t->message_cb_payload = message_cb_payload;
+
+	return 0;
+}
+
+static int git_smart__connect(
+	git_transport *transport,
+	const char *url,
+	git_cred_acquire_cb cred_acquire_cb,
+	void *cred_acquire_payload,
+	int direction,
+	int flags)
+{
+	transport_smart *t = (transport_smart *)transport;
+	git_smart_subtransport_stream *stream;
+	int error;
+	git_pkt *pkt;
+	git_pkt_ref *first;
+	git_smart_service_t service;
+
+	if (git_smart__reset_stream(t, true) < 0)
+		return -1;
+
+	t->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(t->url);
+
+	t->direction = direction;
+	t->flags = flags;
+	t->cred_acquire_cb = cred_acquire_cb;
+	t->cred_acquire_payload = cred_acquire_payload;
+
+	if (GIT_DIRECTION_FETCH == t->direction)
+		service = GIT_SERVICE_UPLOADPACK_LS;
+	else if (GIT_DIRECTION_PUSH == t->direction)
+		service = GIT_SERVICE_RECEIVEPACK_LS;
+	else {
+		giterr_set(GITERR_NET, "Invalid direction");
+		return -1;
+	}
+
+	if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0)
+		return error;
+
+	/* Save off the current stream (i.e. socket) that we are working with */
+	t->current_stream = stream;
+
+	gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
+
+	/* 2 flushes for RPC; 1 for stateful */
+	if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
+		return error;
+
+	/* Strip the comment packet for RPC */
+	if (t->rpc) {
+		pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+
+		if (!pkt || GIT_PKT_COMMENT != pkt->type) {
+			giterr_set(GITERR_NET, "Invalid response");
+			return -1;
+		} else {
+			/* Remove the comment pkt from the list */
+			git_vector_remove(&t->refs, 0);
+			git__free(pkt);
+		}
+	}
+
+	/* We now have loaded the refs. */
+	t->have_refs = 1;
+
+	first = (git_pkt_ref *)git_vector_get(&t->refs, 0);
+
+	/* Detect capabilities */
+	if (git_smart__detect_caps(first, &t->caps) < 0)
+		return -1;
+
+	/* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
+	if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
+		git_oid_iszero(&first->head.oid)) {
+		git_vector_clear(&t->refs);
+		git_pkt_free((git_pkt *)first);
+	}
+
+	if (t->rpc && git_smart__reset_stream(t, false) < 0)
+		return -1;
+
+	/* We're now logically connected. */
+	t->connected = 1;
+
+	return 0;
+}
+
+static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload)
+{
+	transport_smart *t = (transport_smart *)transport;
+	unsigned int i;
+	git_pkt *p = NULL;
+
+	if (!t->have_refs) {
+		giterr_set(GITERR_NET, "The transport has not yet loaded the refs");
+		return -1;
+	}
+
+	git_vector_foreach(&t->refs, i, p) {
+		git_pkt_ref *pkt = NULL;
+
+		if (p->type != GIT_PKT_REF)
+			continue;
+
+		pkt = (git_pkt_ref *)p;
+
+		if (list_cb(&pkt->head, payload))
+			return GIT_EUSER;
+	}
+
+	return 0;
+}
+
+int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
+{
+	transport_smart *t = (transport_smart *)transport;
+	git_smart_subtransport_stream *stream;
+	int error;
+
+	if (t->rpc && git_smart__reset_stream(t, false) < 0)
+		return -1;
+
+	if (GIT_DIRECTION_FETCH != t->direction) {
+		giterr_set(GITERR_NET, "This operation is only valid for fetch");
+		return -1;
+	}
+
+	if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0)
+		return error;
+
+	/* If this is a stateful implementation, the stream we get back should be the same */
+	assert(t->rpc || t->current_stream == stream);
+
+	/* Save off the current stream (i.e. socket) that we are working with */
+	t->current_stream = stream;
+
+	if ((error = stream->write(stream, (const char *)data, len)) < 0)
+		return error;
+
+	gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
+
+	return 0;
+}
+
+int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream)
+{
+	int error;
+
+	if (t->rpc && git_smart__reset_stream(t, false) < 0)
+		return -1;
+
+	if (GIT_DIRECTION_PUSH != t->direction) {
+		giterr_set(GITERR_NET, "This operation is only valid for push");
+		return -1;
+	}
+
+	if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0)
+		return error;
+
+	/* If this is a stateful implementation, the stream we get back should be the same */
+	assert(t->rpc || t->current_stream == *stream);
+
+	/* Save off the current stream (i.e. socket) that we are working with */
+	t->current_stream = *stream;
+
+	gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t);
+
+	return 0;
+}
+
+static void git_smart__cancel(git_transport *transport)
+{
+	transport_smart *t = (transport_smart *)transport;
+
+	git_atomic_set(&t->cancelled, 1);
+}
+
+static int git_smart__is_connected(git_transport *transport)
+{
+	transport_smart *t = (transport_smart *)transport;
+
+	return t->connected;
+}
+
+static int git_smart__read_flags(git_transport *transport, int *flags)
+{
+	transport_smart *t = (transport_smart *)transport;
+
+	*flags = t->flags;
+
+	return 0;
+}
+
+static int git_smart__close(git_transport *transport)
+{
+	transport_smart *t = (transport_smart *)transport;
+	git_vector *common = &t->common;
+	unsigned int i;
+	git_pkt *p;
+	int ret;
+
+	ret = git_smart__reset_stream(t, true);
+
+	git_vector_foreach(common, i, p)
+		git_pkt_free(p);
+
+	git_vector_free(common);
+
+	if (t->url) {
+		git__free(t->url);
+		t->url = NULL;
+	}
+
+	t->connected = 0;
+
+	return ret;
+}
+
+static void git_smart__free(git_transport *transport)
+{
+	transport_smart *t = (transport_smart *)transport;
+	git_vector *refs = &t->refs;
+	unsigned int i;
+	git_pkt *p;
+
+	/* Make sure that the current stream is closed, if we have one. */
+	git_smart__close(transport);
+
+	/* Free the subtransport */
+	t->wrapped->free(t->wrapped);
+
+	git_vector_foreach(refs, i, p)
+		git_pkt_free(p);
+
+	git_vector_free(refs);
+
+	git__free(t);
+}
+
+static int ref_name_cmp(const void *a, const void *b)
+{
+	const git_pkt_ref *ref_a = a, *ref_b = b;
+
+	return strcmp(ref_a->head.name, ref_b->head.name);
+}
+
+int git_transport_smart(git_transport **out, git_remote *owner, void *param)
+{
+	transport_smart *t;
+	git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param;
+
+	if (!param)
+		return -1;
+
+	t = git__calloc(sizeof(transport_smart), 1);
+	GITERR_CHECK_ALLOC(t);
+
+	t->parent.version = GIT_TRANSPORT_VERSION;
+	t->parent.set_callbacks = git_smart__set_callbacks;
+	t->parent.connect = git_smart__connect;
+	t->parent.close = git_smart__close;
+	t->parent.free = git_smart__free;
+	t->parent.negotiate_fetch = git_smart__negotiate_fetch;
+	t->parent.download_pack = git_smart__download_pack;
+	t->parent.push = git_smart__push;
+	t->parent.ls = git_smart__ls;
+	t->parent.is_connected = git_smart__is_connected;
+	t->parent.read_flags = git_smart__read_flags;
+	t->parent.cancel = git_smart__cancel;
+	
+	t->owner = owner;
+	t->rpc = definition->rpc;
+
+	if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0) {
+		git__free(t);
+		return -1;
+	}
+
+	if (definition->callback(&t->wrapped, &t->parent) < 0) {
+		git__free(t);
+		return -1;
+	}	
+
+	*out = (git_transport *) t;
+	return 0;
+}
diff --git a/libgit2/src/transports/smart_pkt.c b/libgit2/src/transports/smart_pkt.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/smart_pkt.c
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/types.h"
+#include "git2/errors.h"
+#include "git2/refs.h"
+#include "git2/revwalk.h"
+
+#include "smart.h"
+#include "util.h"
+#include "netops.h"
+#include "posix.h"
+#include "buffer.h"
+
+#include <ctype.h>
+
+#define PKT_LEN_SIZE 4
+static const char pkt_done_str[] = "0009done\n";
+static const char pkt_flush_str[] = "0000";
+static const char pkt_have_prefix[] = "0032have ";
+static const char pkt_want_prefix[] = "0032want ";
+
+static int flush_pkt(git_pkt **out)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_FLUSH;
+	*out = pkt;
+
+	return 0;
+}
+
+/* the rest of the line will be useful for multi_ack */
+static int ack_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_ack *pkt;
+	GIT_UNUSED(line);
+	GIT_UNUSED(len);
+
+	pkt = git__calloc(1, sizeof(git_pkt_ack));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_ACK;
+	line += 3;
+	len -= 3;
+
+	if (len >= GIT_OID_HEXSZ) {
+		git_oid_fromstr(&pkt->oid, line + 1);
+		line += GIT_OID_HEXSZ + 1;
+		len -= GIT_OID_HEXSZ + 1;
+	}
+
+	if (len >= 7) {
+		if (!git__prefixcmp(line + 1, "continue"))
+			pkt->status = GIT_ACK_CONTINUE;
+	}
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
+static int nak_pkt(git_pkt **out)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_NAK;
+	*out = pkt;
+
+	return 0;
+}
+
+static int pack_pkt(git_pkt **out)
+{
+	git_pkt *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_PACK;
+	*out = pkt;
+
+	return 0;
+}
+
+static int comment_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_comment *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt_comment) + len + 1);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_COMMENT;
+	memcpy(pkt->comment, line, len);
+	pkt->comment[len] = '\0';
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
+static int err_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_err *pkt;
+
+	/* Remove "ERR " from the line */
+	line += 4;
+	len -= 4;
+	pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_ERR;
+	pkt->len = (int)len;
+	memcpy(pkt->error, line, len);
+	pkt->error[len] = '\0';
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
+static int data_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_data *pkt;
+
+	line++;
+	len--;
+	pkt = git__malloc(sizeof(git_pkt_data) + len);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_DATA;
+	pkt->len = (int) len;
+	memcpy(pkt->data, line, len);
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
+static int progress_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_progress *pkt;
+
+	line++;
+	len--;
+	pkt = git__malloc(sizeof(git_pkt_progress) + len);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_PROGRESS;
+	pkt->len = (int) len;
+	memcpy(pkt->data, line, len);
+
+	*out = (git_pkt *) pkt;
+
+	return 0;
+}
+
+static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_err *pkt;
+
+	line++;
+	len--;
+	pkt = git__malloc(sizeof(git_pkt_err) + len + 1);
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_ERR;
+	pkt->len = (int)len;
+	memcpy(pkt->error, line, len);
+	pkt->error[len] = '\0';
+
+	*out = (git_pkt *)pkt;
+
+	return 0;
+}
+
+/*
+ * Parse an other-ref line.
+ */
+static int ref_pkt(git_pkt **out, const char *line, size_t len)
+{
+	int error;
+	git_pkt_ref *pkt;
+
+	pkt = git__malloc(sizeof(git_pkt_ref));
+	GITERR_CHECK_ALLOC(pkt);
+
+	memset(pkt, 0x0, sizeof(git_pkt_ref));
+	pkt->type = GIT_PKT_REF;
+	if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0)
+		goto error_out;
+
+	/* Check for a bit of consistency */
+	if (line[GIT_OID_HEXSZ] != ' ') {
+		giterr_set(GITERR_NET, "Error parsing pkt-line");
+		error = -1;
+		goto error_out;
+	}
+
+	/* Jump from the name */
+	line += GIT_OID_HEXSZ + 1;
+	len -= (GIT_OID_HEXSZ + 1);
+
+	if (line[len - 1] == '\n')
+		--len;
+
+	pkt->head.name = git__malloc(len + 1);
+	GITERR_CHECK_ALLOC(pkt->head.name);
+
+	memcpy(pkt->head.name, line, len);
+	pkt->head.name[len] = '\0';
+
+	if (strlen(pkt->head.name) < len) {
+		pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+	}
+
+	*out = (git_pkt *)pkt;
+	return 0;
+
+error_out:
+	git__free(pkt);
+	return error;
+}
+
+static int ok_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_ok *pkt;
+	const char *ptr;
+
+	pkt = git__malloc(sizeof(*pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_OK;
+
+	line += 3; /* skip "ok " */
+	ptr = strchr(line, '\n');
+	len = ptr - line;
+
+	pkt->ref = git__malloc(len + 1);
+	GITERR_CHECK_ALLOC(pkt->ref);
+
+	memcpy(pkt->ref, line, len);
+	pkt->ref[len] = '\0';
+
+	*out = (git_pkt *)pkt;
+	return 0;
+}
+
+static int ng_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_ng *pkt;
+	const char *ptr;
+
+	pkt = git__malloc(sizeof(*pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_NG;
+
+	line += 3; /* skip "ng " */
+	ptr = strchr(line, ' ');
+	len = ptr - line;
+
+	pkt->ref = git__malloc(len + 1);
+	GITERR_CHECK_ALLOC(pkt->ref);
+
+	memcpy(pkt->ref, line, len);
+	pkt->ref[len] = '\0';
+
+	line = ptr + 1;
+	ptr = strchr(line, '\n');
+	len = ptr - line;
+
+	pkt->msg = git__malloc(len + 1);
+	GITERR_CHECK_ALLOC(pkt->msg);
+
+	memcpy(pkt->msg, line, len);
+	pkt->msg[len] = '\0';
+
+	*out = (git_pkt *)pkt;
+	return 0;
+}
+
+static int unpack_pkt(git_pkt **out, const char *line, size_t len)
+{
+	git_pkt_unpack *pkt;
+
+	GIT_UNUSED(len);
+
+	pkt = git__malloc(sizeof(*pkt));
+	GITERR_CHECK_ALLOC(pkt);
+
+	pkt->type = GIT_PKT_UNPACK;
+	if (!git__prefixcmp(line, "unpack ok"))
+		pkt->unpack_ok = 1;
+	else
+		pkt->unpack_ok = 0;
+
+	*out = (git_pkt *)pkt;
+	return 0;
+}
+
+static int32_t parse_len(const char *line)
+{
+	char num[PKT_LEN_SIZE + 1];
+	int i, error;
+	int32_t len;
+	const char *num_end;
+
+	memcpy(num, line, PKT_LEN_SIZE);
+	num[PKT_LEN_SIZE] = '\0';
+
+	for (i = 0; i < PKT_LEN_SIZE; ++i) {
+		if (!isxdigit(num[i])) {
+			giterr_set(GITERR_NET, "Found invalid hex digit in length");
+			return -1;
+		}
+	}
+
+	if ((error = git__strtol32(&len, num, &num_end, 16)) < 0)
+		return error;
+
+	return len;
+}
+
+/*
+ * As per the documentation, the syntax is:
+ *
+ * pkt-line	= data-pkt / flush-pkt
+ * data-pkt	= pkt-len pkt-payload
+ * pkt-len		= 4*(HEXDIG)
+ * pkt-payload = (pkt-len -4)*(OCTET)
+ * flush-pkt	= "0000"
+ *
+ * Which means that the first four bytes are the length of the line,
+ * in ASCII hexadecimal (including itself)
+ */
+
+int git_pkt_parse_line(
+	git_pkt **head, const char *line, const char **out, size_t bufflen)
+{
+	int ret;
+	int32_t len;
+
+	/* Not even enough for the length */
+	if (bufflen > 0 && bufflen < PKT_LEN_SIZE)
+		return GIT_EBUFS;
+
+	len = parse_len(line);
+	if (len < 0) {
+		/*
+		 * If we fail to parse the length, it might be because the
+		 * server is trying to send us the packfile already.
+		 */
+		if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) {
+			giterr_clear();
+			*out = line;
+			return pack_pkt(head);
+		}
+
+		return (int)len;
+	}
+
+	/*
+	 * If we were given a buffer length, then make sure there is
+	 * enough in the buffer to satisfy this line
+	 */
+	if (bufflen > 0 && bufflen < (size_t)len)
+		return GIT_EBUFS;
+
+	line += PKT_LEN_SIZE;
+	/*
+	 * TODO: How do we deal with empty lines? Try again? with the next
+	 * line?
+	 */
+	if (len == PKT_LEN_SIZE) {
+		*out = line;
+		return 0;
+	}
+
+	if (len == 0) { /* Flush pkt */
+		*out = line;
+		return flush_pkt(head);
+	}
+
+	len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
+
+	if (*line == GIT_SIDE_BAND_DATA)
+		ret = data_pkt(head, line, len);
+	else if (*line == GIT_SIDE_BAND_PROGRESS)
+		ret = progress_pkt(head, line, len);
+	else if (*line == GIT_SIDE_BAND_ERROR)
+		ret = sideband_error_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "ACK"))
+		ret = ack_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "NAK"))
+		ret = nak_pkt(head);
+	else if (!git__prefixcmp(line, "ERR "))
+		ret = err_pkt(head, line, len);
+	else if (*line == '#')
+		ret = comment_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "ok"))
+		ret = ok_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "ng"))
+		ret = ng_pkt(head, line, len);
+	else if (!git__prefixcmp(line, "unpack"))
+		ret = unpack_pkt(head, line, len);
+	else
+		ret = ref_pkt(head, line, len);
+
+	*out = line + len;
+
+	return ret;
+}
+
+void git_pkt_free(git_pkt *pkt)
+{
+	if (pkt->type == GIT_PKT_REF) {
+		git_pkt_ref *p = (git_pkt_ref *) pkt;
+		git__free(p->head.name);
+	}
+
+	if (pkt->type == GIT_PKT_OK) {
+		git_pkt_ok *p = (git_pkt_ok *) pkt;
+		git__free(p->ref);
+	}
+
+	if (pkt->type == GIT_PKT_NG) {
+		git_pkt_ng *p = (git_pkt_ng *) pkt;
+		git__free(p->ref);
+		git__free(p->msg);
+	}
+
+	git__free(pkt);
+}
+
+int git_pkt_buffer_flush(git_buf *buf)
+{
+	return git_buf_put(buf, pkt_flush_str, strlen(pkt_flush_str));
+}
+
+static int buffer_want_with_caps(const git_remote_head *head, transport_smart_caps *caps, git_buf *buf)
+{
+	git_buf str = GIT_BUF_INIT;
+	char oid[GIT_OID_HEXSZ +1] = {0};
+	unsigned int len;
+
+	/* Prefer side-band-64k if the server supports both */
+	if (caps->side_band) {
+		if (caps->side_band_64k)
+			git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
+		else
+			git_buf_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
+	}
+	if (caps->ofs_delta)
+		git_buf_puts(&str, GIT_CAP_OFS_DELTA " ");
+
+	if (caps->multi_ack)
+		git_buf_puts(&str, GIT_CAP_MULTI_ACK " ");
+
+	if (caps->include_tag)
+		git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
+
+	if (git_buf_oom(&str))
+		return -1;
+
+	len = (unsigned int)
+		(strlen("XXXXwant ") + GIT_OID_HEXSZ + 1 /* NUL */ +
+		 git_buf_len(&str) + 1 /* LF */);
+	git_buf_grow(buf, git_buf_len(buf) + len);
+	git_oid_fmt(oid, &head->oid);
+	git_buf_printf(buf, "%04xwant %s %s\n", len, oid, git_buf_cstr(&str));
+	git_buf_free(&str);
+
+	return git_buf_oom(buf);
+}
+
+/*
+ * All "want" packets have the same length and format, so what we do
+ * is overwrite the OID each time.
+ */
+
+int git_pkt_buffer_wants(
+	const git_remote_head * const *refs,
+	size_t count,
+	transport_smart_caps *caps,
+	git_buf *buf)
+{
+	size_t i = 0;
+	const git_remote_head *head;
+
+	if (caps->common) {
+		for (; i < count; ++i) {
+			head = refs[i];
+			if (!head->local)
+				break;
+		}
+
+		if (buffer_want_with_caps(refs[i], caps, buf) < 0)
+			return -1;
+
+		i++;
+	}
+
+	for (; i < count; ++i) {
+		char oid[GIT_OID_HEXSZ];
+
+		head = refs[i];
+		if (head->local)
+			continue;
+
+		git_oid_fmt(oid, &head->oid);
+		git_buf_put(buf, pkt_want_prefix, strlen(pkt_want_prefix));
+		git_buf_put(buf, oid, GIT_OID_HEXSZ);
+		git_buf_putc(buf, '\n');
+		if (git_buf_oom(buf))
+			return -1;
+	}
+
+	return git_pkt_buffer_flush(buf);
+}
+
+int git_pkt_buffer_have(git_oid *oid, git_buf *buf)
+{
+	char oidhex[GIT_OID_HEXSZ + 1];
+
+	memset(oidhex, 0x0, sizeof(oidhex));
+	git_oid_fmt(oidhex, oid);
+	return git_buf_printf(buf, "%s%s\n", pkt_have_prefix, oidhex);
+}
+
+int git_pkt_buffer_done(git_buf *buf)
+{
+	return git_buf_puts(buf, pkt_done_str);
+}
diff --git a/libgit2/src/transports/smart_protocol.c b/libgit2/src/transports/smart_protocol.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/smart_protocol.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "git2.h"
+
+#include "smart.h"
+#include "refs.h"
+#include "repository.h"
+#include "push.h"
+#include "pack-objects.h"
+#include "remote.h"
+
+#define NETWORK_XFER_THRESHOLD (100*1024)
+
+int git_smart__store_refs(transport_smart *t, int flushes)
+{
+	gitno_buffer *buf = &t->buffer;
+	git_vector *refs = &t->refs;
+	int error, flush = 0, recvd;
+	const char *line_end;
+	git_pkt *pkt;
+
+	/* Clear existing refs in case git_remote_connect() is called again
+	 * after git_remote_disconnect().
+	 */
+	git_vector_clear(refs);
+
+	do {
+		if (buf->offset > 0)
+			error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
+		else
+			error = GIT_EBUFS;
+
+		if (error < 0 && error != GIT_EBUFS)
+			return -1;
+
+		if (error == GIT_EBUFS) {
+			if ((recvd = gitno_recv(buf)) < 0)
+				return -1;
+
+			if (recvd == 0 && !flush) {
+				giterr_set(GITERR_NET, "Early EOF");
+				return -1;
+			}
+
+			continue;
+		}
+
+		gitno_consume(buf, line_end);
+		if (pkt->type == GIT_PKT_ERR) {
+			giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
+			git__free(pkt);
+			return -1;
+		}
+
+		if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
+			return -1;
+
+		if (pkt->type == GIT_PKT_FLUSH) {
+			flush++;
+			git_pkt_free(pkt);
+		}
+	} while (flush < flushes);
+
+	return flush;
+}
+
+int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
+{
+	const char *ptr;
+
+	/* No refs or capabilites, odd but not a problem */
+	if (pkt == NULL || pkt->capabilities == NULL)
+		return 0;
+
+	ptr = pkt->capabilities;
+	while (ptr != NULL && *ptr != '\0') {
+		if (*ptr == ' ')
+			ptr++;
+
+		if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
+			caps->common = caps->ofs_delta = 1;
+			ptr += strlen(GIT_CAP_OFS_DELTA);
+			continue;
+		}
+
+		if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
+			caps->common = caps->multi_ack = 1;
+			ptr += strlen(GIT_CAP_MULTI_ACK);
+			continue;
+		}
+
+		if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
+			caps->common = caps->include_tag = 1;
+			ptr += strlen(GIT_CAP_INCLUDE_TAG);
+			continue;
+		}
+
+		/* Keep side-band check after side-band-64k */
+		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
+			caps->common = caps->side_band_64k = 1;
+			ptr += strlen(GIT_CAP_SIDE_BAND_64K);
+			continue;
+		}
+
+		if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
+			caps->common = caps->side_band = 1;
+			ptr += strlen(GIT_CAP_SIDE_BAND);
+			continue;
+		}
+
+		if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
+			caps->common = caps->delete_refs = 1;
+			ptr += strlen(GIT_CAP_DELETE_REFS);
+			continue;
+		}
+
+		/* We don't know this capability, so skip it */
+		ptr = strchr(ptr, ' ');
+	}
+
+	return 0;
+}
+
+static int recv_pkt(git_pkt **out, gitno_buffer *buf)
+{
+	const char *ptr = buf->data, *line_end = ptr;
+	git_pkt *pkt;
+	int pkt_type, error = 0, ret;
+
+	do {
+		if (buf->offset > 0)
+			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
+		else
+			error = GIT_EBUFS;
+
+		if (error == 0)
+			break; /* return the pkt */
+
+		if (error < 0 && error != GIT_EBUFS)
+			return -1;
+
+		if ((ret = gitno_recv(buf)) < 0)
+			return -1;
+	} while (error);
+
+	gitno_consume(buf, line_end);
+	pkt_type = pkt->type;
+	if (out != NULL)
+		*out = pkt;
+	else
+		git__free(pkt);
+
+	return pkt_type;
+}
+
+static int store_common(transport_smart *t)
+{
+	git_pkt *pkt = NULL;
+	gitno_buffer *buf = &t->buffer;
+
+	do {
+		if (recv_pkt(&pkt, buf) < 0)
+			return -1;
+
+		if (pkt->type == GIT_PKT_ACK) {
+			if (git_vector_insert(&t->common, pkt) < 0)
+				return -1;
+		} else {
+			git__free(pkt);
+			return 0;
+		}
+
+	} while (1);
+
+	return 0;
+}
+
+static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
+{
+	git_revwalk *walk;
+	git_strarray refs;
+	unsigned int i;
+	git_reference *ref;
+
+	if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)
+		return -1;
+
+	if (git_revwalk_new(&walk, repo) < 0)
+		return -1;
+
+	git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+	for (i = 0; i < refs.count; ++i) {
+		/* No tags */
+		if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
+			continue;
+
+		if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
+			goto on_error;
+
+		if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
+			continue;
+		if (git_revwalk_push(walk, git_reference_target(ref)) < 0)
+			goto on_error;
+
+		git_reference_free(ref);
+	}
+
+	git_strarray_free(&refs);
+	*out = walk;
+	return 0;
+
+on_error:
+	git_reference_free(ref);
+	git_strarray_free(&refs);
+	return -1;
+}
+
+int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count)
+{
+	transport_smart *t = (transport_smart *)transport;
+	gitno_buffer *buf = &t->buffer;
+	git_buf data = GIT_BUF_INIT;
+	git_revwalk *walk = NULL;
+	int error = -1, pkt_type;
+	unsigned int i;
+	git_oid oid;
+
+	/* No own logic, do our thing */
+	if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+		return error;
+
+	if ((error = fetch_setup_walk(&walk, repo)) < 0)
+		goto on_error;
+	/*
+	 * We don't support any kind of ACK extensions, so the negotiation
+	 * boils down to sending what we have and listening for an ACK
+	 * every once in a while.
+	 */
+	i = 0;
+	while (true) {
+		error = git_revwalk_next(&oid, walk);
+
+		if (error < 0) {
+			if (GIT_ITEROVER == error)
+				break;
+
+			goto on_error;
+		}
+
+		git_pkt_buffer_have(&oid, &data);
+		i++;
+		if (i % 20 == 0) {
+			if (t->cancelled.val) {
+				giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+				error = GIT_EUSER;
+				goto on_error;
+			}
+
+			git_pkt_buffer_flush(&data);
+			if (git_buf_oom(&data)) {
+				error = -1;
+				goto on_error;
+			}
+
+			if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+				goto on_error;
+
+			git_buf_clear(&data);
+			if (t->caps.multi_ack) {
+				if ((error = store_common(t)) < 0)
+					goto on_error;
+			} else {
+				pkt_type = recv_pkt(NULL, buf);
+
+				if (pkt_type == GIT_PKT_ACK) {
+					break;
+				} else if (pkt_type == GIT_PKT_NAK) {
+					continue;
+				} else if (pkt_type < 0) {
+					/* recv_pkt returned an error */
+					error = pkt_type;
+					goto on_error;
+				} else {
+					giterr_set(GITERR_NET, "Unexpected pkt type");
+					error = -1;
+					goto on_error;
+				}
+			}
+		}
+
+		if (t->common.length > 0)
+			break;
+
+		if (i % 20 == 0 && t->rpc) {
+			git_pkt_ack *pkt;
+			unsigned int i;
+
+			if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+				goto on_error;
+
+			git_vector_foreach(&t->common, i, pkt) {
+				if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
+					goto on_error;
+			}
+
+			if (git_buf_oom(&data)) {
+				error = -1;
+				goto on_error;
+			}
+		}
+	}
+
+	/* Tell the other end that we're done negotiating */
+	if (t->rpc && t->common.length > 0) {
+		git_pkt_ack *pkt;
+		unsigned int i;
+
+		if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0)
+			goto on_error;
+
+		git_vector_foreach(&t->common, i, pkt) {
+			if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
+				goto on_error;
+		}
+
+		if (git_buf_oom(&data)) {
+			error = -1;
+			goto on_error;
+		}
+	}
+
+	if ((error = git_pkt_buffer_done(&data)) < 0)
+		goto on_error;
+
+	if (t->cancelled.val) {
+		giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+		error = GIT_EUSER;
+		goto on_error;
+	}
+	if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+		goto on_error;
+
+	git_buf_free(&data);
+	git_revwalk_free(walk);
+
+	/* Now let's eat up whatever the server gives us */
+	if (!t->caps.multi_ack) {
+		pkt_type = recv_pkt(NULL, buf);
+
+		if (pkt_type < 0) {
+			return pkt_type;
+		} else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
+			giterr_set(GITERR_NET, "Unexpected pkt type");
+			return -1;
+		}
+	} else {
+		git_pkt_ack *pkt;
+		do {
+			if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
+				return error;
+
+			if (pkt->type == GIT_PKT_NAK ||
+			    (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) {
+				git__free(pkt);
+				break;
+			}
+
+			git__free(pkt);
+		} while (1);
+	}
+
+	return 0;
+
+on_error:
+	git_revwalk_free(walk);
+	git_buf_free(&data);
+	return error;
+}
+
+static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
+{
+	int recvd;
+
+	do {
+		if (t->cancelled.val) {
+			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+			return GIT_EUSER;
+		}
+
+		if (writepack->add(writepack, buf->data, buf->offset, stats) < 0)
+			return -1;
+
+		gitno_consume_n(buf, buf->offset);
+
+		if ((recvd = gitno_recv(buf)) < 0)
+			return -1;
+	} while(recvd > 0);
+
+	if (writepack->commit(writepack, stats))
+		return -1;
+
+	return 0;
+}
+
+struct network_packetsize_payload
+{
+	git_transfer_progress_callback callback;
+	void *payload;
+	git_transfer_progress *stats;
+	size_t last_fired_bytes;
+};
+
+static void network_packetsize(size_t received, void *payload)
+{
+	struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
+
+	/* Accumulate bytes */
+	npp->stats->received_bytes += received;
+
+	/* Fire notification if the threshold is reached */
+	if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
+		npp->last_fired_bytes = npp->stats->received_bytes;
+		npp->callback(npp->stats, npp->payload);
+	}
+}
+
+int git_smart__download_pack(
+	git_transport *transport,
+	git_repository *repo,
+	git_transfer_progress *stats,
+	git_transfer_progress_callback progress_cb,
+	void *progress_payload)
+{
+	transport_smart *t = (transport_smart *)transport;
+	gitno_buffer *buf = &t->buffer;
+	git_odb *odb;
+	struct git_odb_writepack *writepack = NULL;
+	int error = -1;
+	struct network_packetsize_payload npp = {0};
+
+	memset(stats, 0, sizeof(git_transfer_progress));
+
+	if (progress_cb) {
+		npp.callback = progress_cb;
+		npp.payload = progress_payload;
+		npp.stats = stats;
+		t->packetsize_cb = &network_packetsize;
+		t->packetsize_payload = &npp;
+
+		/* We might have something in the buffer already from negotiate_fetch */
+		if (t->buffer.offset > 0)
+			t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
+	}
+
+	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+		((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
+		goto on_error;
+
+	/*
+	 * If the remote doesn't support the side-band, we can feed
+	 * the data directly to the pack writer. Otherwise, we need to
+	 * check which one belongs there.
+	 */
+	if (!t->caps.side_band && !t->caps.side_band_64k) {
+		if (no_sideband(t, writepack, buf, stats) < 0)
+			goto on_error;
+
+		goto on_success;
+	}
+
+	do {
+		git_pkt *pkt;
+
+		if (t->cancelled.val) {
+			giterr_set(GITERR_NET, "The fetch was cancelled by the user");
+			error = GIT_EUSER;
+			goto on_error;
+		}
+
+		if (recv_pkt(&pkt, buf) < 0)
+			goto on_error;
+
+		if (pkt->type == GIT_PKT_PROGRESS) {
+			if (t->progress_cb) {
+				git_pkt_progress *p = (git_pkt_progress *) pkt;
+				t->progress_cb(p->data, p->len, t->message_cb_payload);
+			}
+			git__free(pkt);
+		} else if (pkt->type == GIT_PKT_DATA) {
+			git_pkt_data *p = (git_pkt_data *) pkt;
+			error = writepack->add(writepack, p->data, p->len, stats);
+
+			git__free(pkt);
+			if (error < 0)
+				goto on_error;
+		} else if (pkt->type == GIT_PKT_FLUSH) {
+			/* A flush indicates the end of the packfile */
+			git__free(pkt);
+			break;
+		}
+	} while (1);
+
+	if (writepack->commit(writepack, stats) < 0)
+		goto on_error;
+
+on_success:
+	error = 0;
+
+on_error:
+	if (writepack)
+		writepack->free(writepack);
+
+	/* Trailing execution of progress_cb, if necessary */
+	if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes)
+		npp.callback(npp.stats, npp.payload);
+
+	return error;
+}
+
+static int gen_pktline(git_buf *buf, git_push *push)
+{
+	push_spec *spec;
+	size_t i, len;
+	char old_id[41], new_id[41];
+
+	old_id[40] = '\0'; new_id[40] = '\0';
+
+	git_vector_foreach(&push->specs, i, spec) {
+		len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref);
+
+		if (i == 0) {
+			++len; /* '\0' */
+			if (push->report_status)
+				len += strlen(GIT_CAP_REPORT_STATUS) + 1;
+			len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
+		}
+
+		git_oid_fmt(old_id, &spec->roid);
+		git_oid_fmt(new_id, &spec->loid);
+
+		git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->rref);
+
+		if (i == 0) {
+			git_buf_putc(buf, '\0');
+			/* Core git always starts their capabilities string with a space */
+			if (push->report_status) {
+				git_buf_putc(buf, ' ');
+				git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
+			}
+			git_buf_putc(buf, ' ');
+			git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
+		}
+
+		git_buf_putc(buf, '\n');
+	}
+
+	git_buf_puts(buf, "0000");
+	return git_buf_oom(buf) ? -1 : 0;
+}
+
+static int add_push_report_pkt(git_push *push, git_pkt *pkt)
+{
+	push_status *status;
+
+	switch (pkt->type) {
+		case GIT_PKT_OK:
+			status = git__malloc(sizeof(push_status));
+			GITERR_CHECK_ALLOC(status);
+			status->msg = NULL;
+			status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
+			if (!status->ref ||
+				git_vector_insert(&push->status, status) < 0) {
+				git_push_status_free(status);
+				return -1;
+			}
+			break;
+		case GIT_PKT_NG:
+			status = git__calloc(sizeof(push_status), 1);
+			GITERR_CHECK_ALLOC(status);
+			status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
+			status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
+			if (!status->ref || !status->msg ||
+				git_vector_insert(&push->status, status) < 0) {
+				git_push_status_free(status);
+				return -1;
+			}
+			break;
+		case GIT_PKT_UNPACK:
+			push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
+			break;
+		case GIT_PKT_FLUSH:
+			return GIT_ITEROVER;
+		default:
+			giterr_set(GITERR_NET, "report-status: protocol error");
+			return -1;
+	}
+
+	return 0;
+}
+
+static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
+{
+	git_pkt *pkt;
+	const char *line = data_pkt->data, *line_end;
+	size_t line_len = data_pkt->len;
+	int error;
+
+	while (line_len > 0) {
+		error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
+
+		if (error < 0)
+			return error;
+
+		/* Advance in the buffer */
+		line_len -= (line_end - line);
+		line = line_end;
+
+		error = add_push_report_pkt(push, pkt);
+
+		git_pkt_free(pkt);
+
+		if (error < 0 && error != GIT_ITEROVER)
+			return error;
+	}
+
+	return 0;
+}
+
+static int parse_report(gitno_buffer *buf, git_push *push)
+{
+	git_pkt *pkt;
+	const char *line_end;
+	int error, recvd;
+
+	for (;;) {
+		if (buf->offset > 0)
+			error = git_pkt_parse_line(&pkt, buf->data,
+						   &line_end, buf->offset);
+		else
+			error = GIT_EBUFS;
+
+		if (error < 0 && error != GIT_EBUFS)
+			return -1;
+
+		if (error == GIT_EBUFS) {
+			if ((recvd = gitno_recv(buf)) < 0)
+				return -1;
+
+			if (recvd == 0) {
+				giterr_set(GITERR_NET, "Early EOF");
+				return -1;
+			}
+			continue;
+		}
+
+		gitno_consume(buf, line_end);
+
+		error = 0;
+
+		switch (pkt->type) {
+			case GIT_PKT_DATA:
+				/* This is a sideband packet which contains other packets */
+				error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
+				break;
+			case GIT_PKT_ERR:
+				giterr_set(GITERR_NET, "report-status: Error reported: %s",
+					((git_pkt_err *)pkt)->error);
+				error = -1;
+				break;
+			case GIT_PKT_PROGRESS:
+				break;
+			default:
+				error = add_push_report_pkt(push, pkt);
+				break;
+		}
+
+		git_pkt_free(pkt);
+
+		/* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
+		if (error == GIT_ITEROVER)
+			return 0;
+
+		if (error < 0)
+			return error;
+	}
+}
+
+static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
+{
+	git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
+	GITERR_CHECK_ALLOC(added);
+
+	added->type = GIT_PKT_REF;
+	git_oid_cpy(&added->head.oid, &push_spec->loid);
+	added->head.name = git__strdup(push_spec->rref);
+
+	if (!added->head.name ||
+		git_vector_insert(refs, added) < 0) {
+		git_pkt_free((git_pkt *)added);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int update_refs_from_report(
+	git_vector *refs,
+	git_vector *push_specs,
+	git_vector *push_report)
+{
+	git_pkt_ref *ref;
+	push_spec *push_spec;
+	push_status *push_status;
+	size_t i, j, refs_len;
+	int cmp;
+
+	/* For each push spec we sent to the server, we should have
+	 * gotten back a status packet in the push report */
+	if (push_specs->length != push_report->length) {
+		giterr_set(GITERR_NET, "report-status: protocol error");
+		return -1;
+	}
+
+	/* We require that push_specs be sorted with push_spec_rref_cmp,
+	 * and that push_report be sorted with push_status_ref_cmp */
+	git_vector_sort(push_specs);
+	git_vector_sort(push_report);
+
+	git_vector_foreach(push_specs, i, push_spec) {
+		push_status = git_vector_get(push_report, i);
+
+		/* For each push spec we sent to the server, we should have
+		 * gotten back a status packet in the push report which matches */
+		if (strcmp(push_spec->rref, push_status->ref)) {
+			giterr_set(GITERR_NET, "report-status: protocol error");
+			return -1;
+		}
+	}
+
+	/* We require that refs be sorted with ref_name_cmp */
+	git_vector_sort(refs);
+	i = j = 0;
+	refs_len = refs->length;
+
+	/* Merge join push_specs with refs */
+	while (i < push_specs->length && j < refs_len) {
+		push_spec = git_vector_get(push_specs, i);
+		push_status = git_vector_get(push_report, i);
+		ref = git_vector_get(refs, j);
+
+		cmp = strcmp(push_spec->rref, ref->head.name);
+
+		/* Iterate appropriately */
+		if (cmp <= 0) i++;
+		if (cmp >= 0) j++;
+
+		/* Add case */
+		if (cmp < 0 &&
+			!push_status->msg &&
+			add_ref_from_push_spec(refs, push_spec) < 0)
+			return -1;
+
+		/* Update case, delete case */
+		if (cmp == 0 &&
+			!push_status->msg)
+			git_oid_cpy(&ref->head.oid, &push_spec->loid);
+	}
+
+	for (; i < push_specs->length; i++) {
+		push_spec = git_vector_get(push_specs, i);
+		push_status = git_vector_get(push_report, i);
+
+		/* Add case */
+		if (!push_status->msg &&
+			add_ref_from_push_spec(refs, push_spec) < 0)
+			return -1;
+	}
+
+	/* Remove any refs which we updated to have a zero OID. */
+	git_vector_rforeach(refs, i, ref) {
+		if (git_oid_iszero(&ref->head.oid)) {
+			git_vector_remove(refs, i);
+			git_pkt_free((git_pkt *)ref);
+		}
+	}
+
+	git_vector_sort(refs);
+
+	return 0;
+}
+
+static int stream_thunk(void *buf, size_t size, void *data)
+{
+	git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data;
+
+	return s->write(s, (const char *)buf, size);
+}
+
+int git_smart__push(git_transport *transport, git_push *push)
+{
+	transport_smart *t = (transport_smart *)transport;
+	git_smart_subtransport_stream *s;
+	git_buf pktline = GIT_BUF_INIT;
+	int error = -1;
+
+#ifdef PUSH_DEBUG
+{
+	git_remote_head *head;
+	push_spec *spec;
+	unsigned int i;
+	char hex[41]; hex[40] = '\0';
+
+	git_vector_foreach(&push->remote->refs, i, head) {
+		git_oid_fmt(hex, &head->oid);
+		fprintf(stderr, "%s (%s)\n", hex, head->name);
+	}
+
+	git_vector_foreach(&push->specs, i, spec) {
+		git_oid_fmt(hex, &spec->roid);
+		fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
+		git_oid_fmt(hex, &spec->loid);
+		fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
+			spec->rref : spec->lref);
+	}
+}
+#endif
+
+	if (git_smart__get_push_stream(t, &s) < 0 ||
+		gen_pktline(&pktline, push) < 0 ||
+		s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0 ||
+		git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0)
+		goto on_error;
+
+	/* If we sent nothing or the server doesn't support report-status, then
+	 * we consider the pack to have been unpacked successfully */
+	if (!push->specs.length || !push->report_status)
+		push->unpack_ok = 1;
+	else if (parse_report(&t->buffer, push) < 0)
+		goto on_error;
+
+	if (push->status.length &&
+		update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
+		goto on_error;
+
+	error = 0;
+
+on_error:
+	git_buf_free(&pktline);
+
+	return error;
+}
diff --git a/libgit2/src/transports/winhttp.c b/libgit2/src/transports/winhttp.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/winhttp.c
@@ -0,0 +1,1136 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifdef GIT_WINHTTP
+
+#include "git2.h"
+#include "git2/transport.h"
+#include "buffer.h"
+#include "posix.h"
+#include "netops.h"
+#include "smart.h"
+#include "remote.h"
+#include "repository.h"
+
+#include <winhttp.h>
+#pragma comment(lib, "winhttp")
+
+/* For UuidCreate */
+#pragma comment(lib, "rpcrt4")
+
+#define WIDEN2(s) L ## s
+#define WIDEN(s) WIDEN2(s)
+
+#define MAX_CONTENT_TYPE_LEN	100
+#define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE	109
+#define CACHED_POST_BODY_BUF_SIZE	4096
+#define UUID_LENGTH_CCH	32
+
+static const char *prefix_http = "http://";
+static const char *prefix_https = "https://";
+static const char *upload_pack_service = "upload-pack";
+static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
+static const char *upload_pack_service_url = "/git-upload-pack";
+static const char *receive_pack_service = "receive-pack";
+static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
+static const char *receive_pack_service_url = "/git-receive-pack";
+static const wchar_t *get_verb = L"GET";
+static const wchar_t *post_verb = L"POST";
+static const wchar_t *pragma_nocache = L"Pragma: no-cache";
+static const wchar_t *transfer_encoding = L"Transfer-Encoding: chunked";
+static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
+	SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+	SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+
+#define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport)
+
+typedef enum {
+	GIT_WINHTTP_AUTH_BASIC = 1,
+} winhttp_authmechanism_t;
+
+typedef struct {
+	git_smart_subtransport_stream parent;
+	const char *service;
+	const char *service_url;
+	const wchar_t *verb;
+	HINTERNET request;
+	wchar_t *request_uri;
+	char *chunk_buffer;
+	unsigned chunk_buffer_len;
+	HANDLE post_body;
+	DWORD post_body_len;
+	unsigned sent_request : 1,
+		received_response : 1,
+		chunked : 1;
+} winhttp_stream;
+
+typedef struct {
+	git_smart_subtransport parent;
+	transport_smart *owner;
+	const char *path;
+	char *host;
+	char *port;
+	char *user_from_url;
+	char *pass_from_url;
+	git_cred *cred;
+	git_cred *url_cred;
+	int auth_mechanism;
+	HINTERNET session;
+	HINTERNET connection;
+	unsigned use_ssl : 1;
+} winhttp_subtransport;
+
+static int apply_basic_credential(HINTERNET request, git_cred *cred)
+{
+	git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
+	git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT;
+	wchar_t *wide = NULL;
+	int error = -1, wide_len = 0;
+
+	git_buf_printf(&raw, "%s:%s", c->username, c->password);
+
+	if (git_buf_oom(&raw) ||
+		git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
+		git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
+		goto on_error;
+
+	wide_len = MultiByteToWideChar(CP_UTF8,	MB_ERR_INVALID_CHARS,
+		git_buf_cstr(&buf),	-1, NULL, 0);
+
+	if (!wide_len) {
+		giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
+		goto on_error;
+	}
+
+	wide = git__malloc(wide_len * sizeof(wchar_t));
+
+	if (!wide)
+		goto on_error;
+
+	if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+		git_buf_cstr(&buf), -1, wide, wide_len)) {
+		giterr_set(GITERR_OS, "Failed to convert string to wide form");
+		goto on_error;
+	}
+
+	if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+		giterr_set(GITERR_OS, "Failed to add a header to the request");
+		goto on_error;
+	}
+
+	error = 0;
+
+on_error:
+	/* We were dealing with plaintext passwords, so clean up after ourselves a bit. */
+	if (wide)
+		memset(wide, 0x0, wide_len * sizeof(wchar_t));
+
+	if (buf.size)
+		memset(buf.ptr, 0x0, buf.size);
+
+	if (raw.size)
+		memset(raw.ptr, 0x0, raw.size);
+
+	git__free(wide);
+	git_buf_free(&buf);
+	git_buf_free(&raw);
+	return error;
+}
+
+static int winhttp_stream_connect(winhttp_stream *s)
+{
+	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+	git_buf buf = GIT_BUF_INIT;
+	char *proxy_url = NULL;
+	wchar_t ct[MAX_CONTENT_TYPE_LEN];
+	wchar_t *types[] = { L"*/*", NULL };
+	BOOL peerdist = FALSE;
+	int error = -1, wide_len;
+
+	/* Prepare URL */
+	git_buf_printf(&buf, "%s%s", t->path, s->service_url);
+
+	if (git_buf_oom(&buf))
+		return -1;
+
+	/* Convert URL to wide characters */
+	wide_len = MultiByteToWideChar(CP_UTF8,	MB_ERR_INVALID_CHARS,
+		git_buf_cstr(&buf),	-1, NULL, 0);
+
+	if (!wide_len) {
+		giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
+		goto on_error;
+	}
+
+	s->request_uri = git__malloc(wide_len * sizeof(wchar_t));
+
+	if (!s->request_uri)
+		goto on_error;
+
+	if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+		git_buf_cstr(&buf), -1, s->request_uri, wide_len)) {
+		giterr_set(GITERR_OS, "Failed to convert string to wide form");
+		goto on_error;
+	}
+
+	/* Establish request */
+	s->request = WinHttpOpenRequest(
+			t->connection,
+			s->verb,
+			s->request_uri,
+			NULL,
+			WINHTTP_NO_REFERER,
+			types,
+			t->use_ssl ? WINHTTP_FLAG_SECURE : 0);
+
+	if (!s->request) {
+		giterr_set(GITERR_OS, "Failed to open request");
+		goto on_error;
+	}
+
+	/* Set proxy if necessary */
+	if (git_remote__get_http_proxy(t->owner->owner, t->use_ssl, &proxy_url) < 0)
+		goto on_error;
+
+	if (proxy_url) {
+		WINHTTP_PROXY_INFO proxy_info;
+		wchar_t *proxy_wide;
+
+		/* Convert URL to wide characters */
+		wide_len = MultiByteToWideChar(CP_UTF8,	MB_ERR_INVALID_CHARS,
+			proxy_url, -1, NULL, 0);
+
+		if (!wide_len) {
+			giterr_set(GITERR_OS, "Failed to measure string for wide conversion");
+			goto on_error;
+		}
+
+		proxy_wide = git__malloc(wide_len * sizeof(wchar_t));
+
+		if (!proxy_wide)
+			goto on_error;
+
+		if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+			proxy_url, -1, proxy_wide, wide_len)) {
+			giterr_set(GITERR_OS, "Failed to convert string to wide form");
+			git__free(proxy_wide);
+			goto on_error;
+		}
+
+		/* Strip any trailing forward slash on the proxy URL;
+		 * WinHTTP doesn't like it if one is present */
+		if (wide_len > 1 && L'/' == proxy_wide[wide_len - 2])
+			proxy_wide[wide_len - 2] = L'\0';
+
+		proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+		proxy_info.lpszProxy = proxy_wide;
+		proxy_info.lpszProxyBypass = NULL;
+
+		if (!WinHttpSetOption(s->request,
+			WINHTTP_OPTION_PROXY,
+			&proxy_info,
+			sizeof(WINHTTP_PROXY_INFO))) {
+			giterr_set(GITERR_OS, "Failed to set proxy");
+			git__free(proxy_wide);
+			goto on_error;
+		}
+
+		git__free(proxy_wide);
+	}
+
+	/* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
+	 * adds itself. This option may not be supported by the underlying
+	 * platform, so we do not error-check it */
+	WinHttpSetOption(s->request,
+		WINHTTP_OPTION_PEERDIST_EXTENSION_STATE,
+		&peerdist,
+		sizeof(peerdist));
+
+	/* Send Pragma: no-cache header */
+	if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+		giterr_set(GITERR_OS, "Failed to add a header to the request");
+		goto on_error;
+	}
+
+	/* Send Content-Type header -- only necessary on a POST */
+	if (post_verb == s->verb) {
+		git_buf_clear(&buf);
+		if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
+			goto on_error;
+
+		git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
+
+		if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			goto on_error;
+		}
+	}
+
+	/* If requested, disable certificate validation */
+	if (t->use_ssl) {
+		int flags;
+
+		if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0)
+			goto on_error;
+
+		if ((GIT_TRANSPORTFLAGS_NO_CHECK_CERT & flags) &&
+			!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS,
+			(LPVOID)&no_check_cert_flags, sizeof(no_check_cert_flags))) {
+			giterr_set(GITERR_OS, "Failed to set options to ignore cert errors");
+			goto on_error;
+		}
+	}
+
+	/* If we have a credential on the subtransport, apply it to the request */
+	if (t->cred &&
+		t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
+		t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
+		apply_basic_credential(s->request, t->cred) < 0)
+		goto on_error;
+
+	/* If no other credentials have been applied and the URL has username and
+	 * password, use those */
+	if (!t->cred && t->user_from_url && t->pass_from_url) {
+		if (!t->url_cred &&
+			 git_cred_userpass_plaintext_new(&t->url_cred, t->user_from_url, t->pass_from_url) < 0)
+			goto on_error;
+		if (apply_basic_credential(s->request, t->url_cred) < 0)
+			goto on_error;
+	}
+
+	/* We've done everything up to calling WinHttpSendRequest. */
+
+	error = 0;
+
+on_error:
+	git__free(proxy_url);
+	git_buf_free(&buf);
+	return error;
+}
+
+static int parse_unauthorized_response(
+	HINTERNET request,
+	int *allowed_types,
+	int *auth_mechanism)
+{
+	DWORD supported, first, target;
+
+	*allowed_types = 0;
+	*auth_mechanism = 0;
+
+	/* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes(). 
+	 * We can assume this was already done, since we know we are unauthorized. 
+	 */
+	if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
+		giterr_set(GITERR_OS, "Failed to parse supported auth schemes"); 
+		return -1;
+	}
+
+	if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
+		*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+		*auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
+	}
+
+	return 0;
+}
+
+static int write_chunk(HINTERNET request, const char *buffer, size_t len)
+{
+	DWORD bytes_written;
+	git_buf buf = GIT_BUF_INIT;
+
+	/* Chunk header */
+	git_buf_printf(&buf, "%X\r\n", len);
+
+	if (git_buf_oom(&buf))
+		return -1;
+
+	if (!WinHttpWriteData(request,
+		git_buf_cstr(&buf),	(DWORD)git_buf_len(&buf),
+		&bytes_written)) {
+		git_buf_free(&buf);
+		giterr_set(GITERR_OS, "Failed to write chunk header");
+		return -1;
+	}
+
+	git_buf_free(&buf);
+
+	/* Chunk body */
+	if (!WinHttpWriteData(request,
+		buffer, (DWORD)len,
+		&bytes_written)) {
+		giterr_set(GITERR_OS, "Failed to write chunk");
+		return -1;
+	}
+
+	/* Chunk footer */
+	if (!WinHttpWriteData(request,
+		"\r\n", 2,
+		&bytes_written)) {
+		giterr_set(GITERR_OS, "Failed to write chunk footer");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int winhttp_stream_read(
+	git_smart_subtransport_stream *stream,
+	char *buffer,
+	size_t buf_size,
+	size_t *bytes_read)
+{
+	winhttp_stream *s = (winhttp_stream *)stream;
+	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+	DWORD dw_bytes_read;
+	char replay_count = 0;
+
+replay:
+	/* Enforce a reasonable cap on the number of replays */
+	if (++replay_count >= 7) {
+		giterr_set(GITERR_NET, "Too many redirects or authentication replays");
+		return -1;
+	}
+
+	/* Connect if necessary */
+	if (!s->request && winhttp_stream_connect(s) < 0)
+		return -1;
+
+	if (!s->received_response) {
+		DWORD status_code, status_code_length, content_type_length, bytes_written;
+		char expected_content_type_8[MAX_CONTENT_TYPE_LEN];
+		wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN];
+
+		if (!s->sent_request) {
+			if (!WinHttpSendRequest(s->request,
+				WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+				WINHTTP_NO_REQUEST_DATA, 0,
+				s->post_body_len, 0)) {
+				giterr_set(GITERR_OS, "Failed to send request");
+				return -1;
+			}
+
+			s->sent_request = 1;
+		}
+
+		if (s->chunked) {
+			assert(s->verb == post_verb);
+
+			/* Flush, if necessary */
+			if (s->chunk_buffer_len > 0 &&
+				write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+
+			/* Write the final chunk. */
+			if (!WinHttpWriteData(s->request,
+				"0\r\n\r\n", 5,
+				&bytes_written)) {
+				giterr_set(GITERR_OS, "Failed to write final chunk");
+				return -1;
+			}
+		}
+		else if (s->post_body) {
+			char *buffer;
+			DWORD len = s->post_body_len, bytes_read;
+
+			if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body,
+					0, 0, FILE_BEGIN) &&
+				NO_ERROR != GetLastError()) {
+				giterr_set(GITERR_OS, "Failed to reset file pointer");
+				return -1;
+			}
+
+			buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
+
+			while (len > 0) {
+				DWORD bytes_written;
+
+				if (!ReadFile(s->post_body, buffer,
+					min(CACHED_POST_BODY_BUF_SIZE, len),
+					&bytes_read, NULL) ||
+					!bytes_read) {
+					git__free(buffer);
+					giterr_set(GITERR_OS, "Failed to read from temp file");
+					return -1;
+				}
+
+				if (!WinHttpWriteData(s->request, buffer,
+					bytes_read, &bytes_written)) {
+					git__free(buffer);
+					giterr_set(GITERR_OS, "Failed to write data");
+					return -1;
+				}
+
+				len -= bytes_read;
+				assert(bytes_read == bytes_written);
+			}
+
+			git__free(buffer);
+
+			/* Eagerly close the temp file */
+			CloseHandle(s->post_body);
+			s->post_body = NULL;
+		}
+
+		if (!WinHttpReceiveResponse(s->request, 0)) {
+			giterr_set(GITERR_OS, "Failed to receive response");
+			return -1;
+		}
+
+		/* Verify that we got a 200 back */
+		status_code_length = sizeof(status_code);
+
+		if (!WinHttpQueryHeaders(s->request,
+			WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+			WINHTTP_HEADER_NAME_BY_INDEX,
+			&status_code, &status_code_length,
+			WINHTTP_NO_HEADER_INDEX)) {
+				giterr_set(GITERR_OS, "Failed to retrieve status code");
+				return -1;
+		}
+
+		/* The implementation of WinHTTP prior to Windows 7 will not
+		 * redirect to an identical URI. Some Git hosters use self-redirects
+		 * as part of their DoS mitigation strategy. Check first to see if we
+		 * have a redirect status code, and that we haven't already streamed
+		 * a post body. (We can't replay a streamed POST.) */
+		if (!s->chunked &&
+			(HTTP_STATUS_MOVED == status_code ||
+			 HTTP_STATUS_REDIRECT == status_code ||
+			 (HTTP_STATUS_REDIRECT_METHOD == status_code &&
+			  get_verb == s->verb) ||
+			 HTTP_STATUS_REDIRECT_KEEP_VERB == status_code)) {
+
+			/* Check for Windows 7. This workaround is only necessary on
+			 * Windows Vista and earlier. Windows 7 is version 6.1. */
+			if (!git_has_win32_version(6, 1)) {
+				wchar_t *location;
+				DWORD location_length;
+				int redirect_cmp;
+
+				/* OK, fetch the Location header from the redirect. */
+				if (WinHttpQueryHeaders(s->request,
+					WINHTTP_QUERY_LOCATION,
+					WINHTTP_HEADER_NAME_BY_INDEX,
+					WINHTTP_NO_OUTPUT_BUFFER,
+					&location_length,
+					WINHTTP_NO_HEADER_INDEX) ||
+					GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+					giterr_set(GITERR_OS, "Failed to read Location header");
+					return -1;
+				}
+
+				location = git__malloc(location_length);
+				GITERR_CHECK_ALLOC(location);
+
+				if (!WinHttpQueryHeaders(s->request,
+					WINHTTP_QUERY_LOCATION,
+					WINHTTP_HEADER_NAME_BY_INDEX,
+					location,
+					&location_length,
+					WINHTTP_NO_HEADER_INDEX)) {
+					giterr_set(GITERR_OS, "Failed to read Location header");
+					git__free(location);
+					return -1;
+				}
+
+				/* Compare the Location header with the request URI */
+				redirect_cmp = wcscmp(location, s->request_uri);
+				git__free(location);
+
+				if (!redirect_cmp) {
+					/* Replay the request */
+					WinHttpCloseHandle(s->request);
+					s->request = NULL;
+					s->sent_request = 0;
+
+					goto replay;
+				}
+			}
+		}
+
+		/* Handle authentication failures */
+		if (HTTP_STATUS_DENIED == status_code &&
+			get_verb == s->verb && t->owner->cred_acquire_cb) {
+			int allowed_types;
+
+			if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+				return -1;
+
+			if (allowed_types &&
+				(!t->cred || 0 == (t->cred->credtype & allowed_types))) {
+
+				if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->user_from_url, allowed_types, t->owner->cred_acquire_payload) < 0)
+					return -1;
+
+				assert(t->cred);
+
+				WinHttpCloseHandle(s->request);
+				s->request = NULL;
+				s->sent_request = 0;
+
+				/* Successfully acquired a credential */
+				goto replay;
+			}
+		}
+
+		if (HTTP_STATUS_OK != status_code) {
+			giterr_set(GITERR_NET, "Request failed with status code: %d", status_code);
+			return -1;
+		}
+
+		/* Verify that we got the correct content-type back */
+		if (post_verb == s->verb)
+			snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
+		else
+			snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
+
+		git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8);
+		content_type_length = sizeof(content_type);
+
+		if (!WinHttpQueryHeaders(s->request,
+			WINHTTP_QUERY_CONTENT_TYPE,
+			WINHTTP_HEADER_NAME_BY_INDEX,
+			&content_type, &content_type_length,
+			WINHTTP_NO_HEADER_INDEX)) {
+				giterr_set(GITERR_OS, "Failed to retrieve response content-type");
+				return -1;
+		}
+
+		if (wcscmp(expected_content_type, content_type)) {
+			giterr_set(GITERR_NET, "Received unexpected content-type");
+			return -1;
+		}
+
+		s->received_response = 1;
+	}
+
+	if (!WinHttpReadData(s->request,
+		(LPVOID)buffer,
+		(DWORD)buf_size,
+		&dw_bytes_read))
+	{
+		giterr_set(GITERR_OS, "Failed to read data");
+		return -1;
+	}
+
+	*bytes_read = dw_bytes_read;
+
+	return 0;
+}
+
+static int winhttp_stream_write_single(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
+{
+	winhttp_stream *s = (winhttp_stream *)stream;
+	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+	DWORD bytes_written;
+
+	if (!s->request && winhttp_stream_connect(s) < 0)
+		return -1;
+
+	/* This implementation of write permits only a single call. */
+	if (s->sent_request) {
+		giterr_set(GITERR_NET, "Subtransport configured for only one write");
+		return -1;
+	}
+
+	if (!WinHttpSendRequest(s->request,
+			WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+			WINHTTP_NO_REQUEST_DATA, 0,
+			(DWORD)len, 0)) {
+		giterr_set(GITERR_OS, "Failed to send request");
+		return -1;
+	}
+
+	s->sent_request = 1;
+
+	if (!WinHttpWriteData(s->request,
+			(LPCVOID)buffer,
+			(DWORD)len,
+			&bytes_written)) {
+		giterr_set(GITERR_OS, "Failed to write data");
+		return -1;
+	}
+
+	assert((DWORD)len == bytes_written);
+
+	return 0;
+}
+
+static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
+{
+	UUID uuid;
+	RPC_STATUS status = UuidCreate(&uuid);
+	HRESULT result;
+
+	if (RPC_S_OK != status &&
+		RPC_S_UUID_LOCAL_ONLY != status &&
+		RPC_S_UUID_NO_ADDRESS != status) {
+		giterr_set(GITERR_NET, "Unable to generate name for temp file");
+		return -1;
+	}
+
+	if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
+		giterr_set(GITERR_NET, "Buffer too small for name of temp file");
+		return -1;
+	}
+
+	result = StringCbPrintfW(
+		buffer, buffer_len_cch,
+		L"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
+		uuid.Data1, uuid.Data2, uuid.Data3,
+		uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
+		uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);
+
+	if (FAILED(result)) {
+		giterr_set(GITERR_OS, "Unable to generate name for temp file");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch)
+{
+	size_t len;
+
+	if (!GetTempPathW(buffer_len_cch, buffer)) {
+		giterr_set(GITERR_OS, "Failed to get temp path");
+		return -1;
+	}
+
+	len = wcslen(buffer);
+
+	if (buffer[len - 1] != '\\' && len < buffer_len_cch)
+		buffer[len++] = '\\';
+
+	if (put_uuid_string(&buffer[len], (size_t)buffer_len_cch - len) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int winhttp_stream_write_buffered(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
+{
+	winhttp_stream *s = (winhttp_stream *)stream;
+	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+	DWORD bytes_written;
+
+	if (!s->request && winhttp_stream_connect(s) < 0)
+		return -1;
+
+	/* Buffer the payload, using a temporary file so we delegate
+	 * memory management of the data to the operating system. */
+	if (!s->post_body) {
+		wchar_t temp_path[MAX_PATH + 1];
+
+		if (get_temp_file(temp_path, MAX_PATH + 1) < 0)
+			return -1;
+
+		s->post_body = CreateFileW(temp_path,
+			GENERIC_READ | GENERIC_WRITE,
+			FILE_SHARE_DELETE, NULL,
+			CREATE_NEW,
+			FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN,
+			NULL);
+
+		if (INVALID_HANDLE_VALUE == s->post_body) {
+			s->post_body = NULL;
+			giterr_set(GITERR_OS, "Failed to create temporary file");
+			return -1;
+		}
+	}
+
+	if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) {
+		giterr_set(GITERR_OS, "Failed to write to temporary file");
+		return -1;
+	}
+
+	assert((DWORD)len == bytes_written);
+
+	s->post_body_len += bytes_written;
+
+	return 0;
+}
+
+static int winhttp_stream_write_chunked(
+	git_smart_subtransport_stream *stream,
+	const char *buffer,
+	size_t len)
+{
+	winhttp_stream *s = (winhttp_stream *)stream;
+	winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+
+	if (!s->request && winhttp_stream_connect(s) < 0)
+		return -1;
+
+	if (!s->sent_request) {
+		/* Send Transfer-Encoding: chunked header */
+		if (!WinHttpAddRequestHeaders(s->request,
+			transfer_encoding, (ULONG) -1L,
+			WINHTTP_ADDREQ_FLAG_ADD)) {
+			giterr_set(GITERR_OS, "Failed to add a header to the request");
+			return -1;
+		}
+
+		if (!WinHttpSendRequest(s->request,
+			WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+			WINHTTP_NO_REQUEST_DATA, 0,
+			WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) {
+			giterr_set(GITERR_OS, "Failed to send request");
+			return -1;
+		}
+
+		s->sent_request = 1;
+	}
+
+	if (len > CACHED_POST_BODY_BUF_SIZE) {
+		/* Flush, if necessary */
+		if (s->chunk_buffer_len > 0) {
+			if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+		}
+
+		/* Write chunk directly */
+		if (write_chunk(s->request, buffer, len) < 0)
+			return -1;
+	}
+	else {
+		/* Append as much to the buffer as we can */
+		int count = min(CACHED_POST_BODY_BUF_SIZE - s->chunk_buffer_len, (int)len);
+
+		if (!s->chunk_buffer)
+			s->chunk_buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
+
+		memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
+		s->chunk_buffer_len += count;
+		buffer += count;
+		len -= count;
+
+		/* Is the buffer full? If so, then flush */
+		if (CACHED_POST_BODY_BUF_SIZE == s->chunk_buffer_len) {
+			if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+				return -1;
+
+			s->chunk_buffer_len = 0;
+
+			/* Is there any remaining data from the source? */
+			if (len > 0) {
+				memcpy(s->chunk_buffer, buffer, len);
+				s->chunk_buffer_len = (unsigned int)len;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void winhttp_stream_free(git_smart_subtransport_stream *stream)
+{
+	winhttp_stream *s = (winhttp_stream *)stream;
+
+	if (s->chunk_buffer) {
+		git__free(s->chunk_buffer);
+		s->chunk_buffer = NULL;
+	}
+
+	if (s->post_body) {
+		CloseHandle(s->post_body);
+		s->post_body = NULL;
+	}
+
+	if (s->request_uri) {
+		git__free(s->request_uri);
+		s->request_uri = NULL;
+	}
+
+	if (s->request) {
+		WinHttpCloseHandle(s->request);
+		s->request = NULL;
+	}
+
+	git__free(s);
+}
+
+static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream)
+{
+	winhttp_stream *s;
+
+	if (!stream)
+		return -1;
+
+	s = git__calloc(sizeof(winhttp_stream), 1);
+	GITERR_CHECK_ALLOC(s);
+
+	s->parent.subtransport = &t->parent;
+	s->parent.read = winhttp_stream_read;
+	s->parent.write = winhttp_stream_write_single;
+	s->parent.free = winhttp_stream_free;
+
+	*stream = s;
+
+	return 0;
+}
+
+static int winhttp_connect(
+	winhttp_subtransport *t,
+	const char *url)
+{
+	wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
+	wchar_t host[GIT_WIN_PATH];
+	int32_t port;
+	const char *default_port;
+	int ret;
+
+	if (!git__prefixcmp(url, prefix_http)) {
+		url = url + strlen(prefix_http);
+		default_port = "80";
+	}
+
+	if (!git__prefixcmp(url, prefix_https)) {
+		url += strlen(prefix_https);
+		default_port = "443";
+		t->use_ssl = 1;
+	}
+
+	if ((ret = gitno_extract_url_parts(&t->host, &t->port, &t->user_from_url,
+					&t->pass_from_url, url, default_port)) < 0)
+		return ret;
+
+	t->path = strchr(url, '/');
+
+	/* Prepare port */
+	if (git__strtol32(&port, t->port, NULL, 10) < 0)
+		return -1;
+
+	/* Prepare host */
+	git__utf8_to_16(host, GIT_WIN_PATH, t->host);
+
+	/* Establish session */
+	t->session = WinHttpOpen(
+			ua,
+			WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+			WINHTTP_NO_PROXY_NAME,
+			WINHTTP_NO_PROXY_BYPASS,
+			0);
+
+	if (!t->session) {
+		giterr_set(GITERR_OS, "Failed to init WinHTTP");
+		return -1;
+	}
+	
+	/* Establish connection */
+	t->connection = WinHttpConnect(
+			t->session,
+			host,
+			port,
+			0);
+
+	if (!t->connection) {
+		giterr_set(GITERR_OS, "Failed to connect to host");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int winhttp_uploadpack_ls(
+	winhttp_subtransport *t,
+	winhttp_stream *s)
+{
+	s->service = upload_pack_service;
+	s->service_url = upload_pack_ls_service_url;
+	s->verb = get_verb;
+
+	return 0;
+}
+
+static int winhttp_uploadpack(
+	winhttp_subtransport *t,
+	winhttp_stream *s)
+{
+	s->service = upload_pack_service;
+	s->service_url = upload_pack_service_url;
+	s->verb = post_verb;
+
+	return 0;
+}
+
+static int winhttp_receivepack_ls(
+	winhttp_subtransport *t,
+	winhttp_stream *s)
+{
+	s->service = receive_pack_service;
+	s->service_url = receive_pack_ls_service_url;
+	s->verb = get_verb;
+
+	return 0;
+}
+
+static int winhttp_receivepack(
+	winhttp_subtransport *t,
+	winhttp_stream *s)
+{
+	/* WinHTTP only supports Transfer-Encoding: chunked
+	 * on Windows Vista (NT 6.0) and higher. */
+	s->chunked = git_has_win32_version(6, 0);
+
+	if (s->chunked)
+		s->parent.write = winhttp_stream_write_chunked;
+	else
+		s->parent.write = winhttp_stream_write_buffered;
+
+	s->service = receive_pack_service;
+	s->service_url = receive_pack_service_url;
+	s->verb = post_verb;
+
+	return 0;
+}
+
+static int winhttp_action(
+	git_smart_subtransport_stream **stream,
+	git_smart_subtransport *subtransport,
+	const char *url,
+	git_smart_service_t action)
+{
+	winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+	winhttp_stream *s;
+	int ret = -1;
+
+	if (!t->connection &&
+		winhttp_connect(t, url) < 0)
+		return -1;
+
+	if (winhttp_stream_alloc(t, &s) < 0)
+		return -1;
+
+	if (!stream)
+		return -1;
+
+	switch (action)
+	{
+		case GIT_SERVICE_UPLOADPACK_LS:
+			ret = winhttp_uploadpack_ls(t, s);
+			break;
+
+		case GIT_SERVICE_UPLOADPACK:
+			ret = winhttp_uploadpack(t, s);
+			break;
+
+		case GIT_SERVICE_RECEIVEPACK_LS:
+			ret = winhttp_receivepack_ls(t, s);
+			break;
+
+		case GIT_SERVICE_RECEIVEPACK:
+			ret = winhttp_receivepack(t, s);
+			break;
+
+		default:
+			assert(0);
+	}
+
+	if (!ret)
+		*stream = &s->parent;
+
+	return ret;
+}
+
+static int winhttp_close(git_smart_subtransport *subtransport)
+{
+	winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+	int ret = 0;
+
+	if (t->host) {
+		git__free(t->host);
+		t->host = NULL;
+	}
+
+	if (t->port) {
+		git__free(t->port);
+		t->port = NULL;
+	}
+
+	if (t->user_from_url) {
+		git__free(t->user_from_url);
+		t->user_from_url = NULL;
+	}
+
+	if (t->pass_from_url) {
+		git__free(t->pass_from_url);
+		t->pass_from_url = NULL;
+	}
+
+	if (t->cred) {
+		t->cred->free(t->cred);
+		t->cred = NULL;
+	}
+
+	if (t->url_cred) {
+		t->url_cred->free(t->url_cred);
+		t->url_cred = NULL;
+	}
+
+	if (t->connection) {
+		if (!WinHttpCloseHandle(t->connection)) {
+			giterr_set(GITERR_OS, "Unable to close connection");
+			ret = -1;
+		}
+
+		t->connection = NULL;
+	}
+
+	if (t->session) {
+		if (!WinHttpCloseHandle(t->session)) {
+			giterr_set(GITERR_OS, "Unable to close session");
+			ret = -1;
+		}
+
+		t->session = NULL;
+	}
+
+	return ret;
+}
+
+static void winhttp_free(git_smart_subtransport *subtransport)
+{
+	winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+
+	winhttp_close(subtransport);
+
+	git__free(t);
+}
+
+int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner)
+{
+	winhttp_subtransport *t;
+
+	if (!out)
+		return -1;
+
+	t = git__calloc(sizeof(winhttp_subtransport), 1);
+	GITERR_CHECK_ALLOC(t);
+
+	t->owner = (transport_smart *)owner;
+	t->parent.action = winhttp_action;
+	t->parent.close = winhttp_close;
+	t->parent.free = winhttp_free;
+
+	*out = (git_smart_subtransport *) t;
+	return 0;
+}
+
+#endif /* GIT_WINHTTP */
diff --git a/libgit2/src/tree-cache.c b/libgit2/src/tree-cache.c
--- a/libgit2/src/tree-cache.c
+++ b/libgit2/src/tree-cache.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -69,7 +69,7 @@
 			return NULL;
 		}
 
-		if (end == NULL || end + 1 == '\0')
+		if (end == NULL || *end + 1 == '\0')
 			return tree;
 
 		ptr = end + 1;
@@ -104,7 +104,7 @@
 	tree->name[name_len] = '\0';
 
 	/* Blank-terminated ASCII decimal number of entries in this tree */
-	if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < -1)
+	if (git__strtol32(&count, buffer, &buffer, 10) < 0)
 		goto corrupted;
 
 	tree->entries = count;
diff --git a/libgit2/src/tree-cache.h b/libgit2/src/tree-cache.h
--- a/libgit2/src/tree-cache.h
+++ b/libgit2/src/tree-cache.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/tree.c b/libgit2/src/tree.c
--- a/libgit2/src/tree.c
+++ b/libgit2/src/tree.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,30 +12,90 @@
 #include "git2/object.h"
 
 #define DEFAULT_TREE_SIZE 16
-#define MAX_FILEMODE 0777777
 #define MAX_FILEMODE_BYTES 6
 
-static int valid_attributes(const int attributes)
+static bool valid_filemode(const int filemode)
 {
-	return attributes >= 0 && attributes <= MAX_FILEMODE;
+	return (filemode == GIT_FILEMODE_TREE
+		|| filemode == GIT_FILEMODE_BLOB
+		|| filemode == GIT_FILEMODE_BLOB_EXECUTABLE
+		|| filemode == GIT_FILEMODE_LINK
+		|| filemode == GIT_FILEMODE_COMMIT);
 }
 
+GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
+{
+	/* Tree bits set, but it's not a commit */
+	if (filemode & GIT_FILEMODE_TREE && !(filemode & 0100000))
+		return GIT_FILEMODE_TREE;
+
+	/* If any of the x bits is set */
+	if (filemode & 0111)
+		return GIT_FILEMODE_BLOB_EXECUTABLE;
+
+	/* 16XXXX means commit */
+	if ((filemode & GIT_FILEMODE_COMMIT) == GIT_FILEMODE_COMMIT)
+		return GIT_FILEMODE_COMMIT;
+
+	/* 12XXXX means commit */
+	if ((filemode & GIT_FILEMODE_LINK) == GIT_FILEMODE_LINK)
+		return GIT_FILEMODE_LINK;
+
+	/* Otherwise, return a blob */
+	return GIT_FILEMODE_BLOB;
+}
+
 static int valid_entry_name(const char *filename)
 {
-	return strlen(filename) > 0 && strchr(filename, '/') == NULL;
+	return *filename != '\0' &&
+		strchr(filename, '/') == NULL &&
+		(*filename != '.' ||
+		 (strcmp(filename, ".") != 0 &&
+		  strcmp(filename, "..") != 0 &&
+		  strcmp(filename, DOT_GIT) != 0));
 }
 
 static int entry_sort_cmp(const void *a, const void *b)
 {
-	const git_tree_entry *entry_a = (const git_tree_entry *)(a);
-	const git_tree_entry *entry_b = (const git_tree_entry *)(b);
+	const git_tree_entry *e1 = (const git_tree_entry *)a;
+	const git_tree_entry *e2 = (const git_tree_entry *)b;
 
 	return git_path_cmp(
-		entry_a->filename, entry_a->filename_len, git_tree_entry__is_tree(entry_a),
-		entry_b->filename, entry_b->filename_len, git_tree_entry__is_tree(entry_b));
+		e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
+		e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
+		git__strncmp);
 }
 
+int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
+{
+	return entry_sort_cmp(e1, e2);
+}
 
+int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2)
+{
+	return git_path_cmp(
+		e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
+		e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
+		git__strncasecmp);
+}
+
+static git_tree_entry *alloc_entry(const char *filename)
+{
+	git_tree_entry *entry = NULL;
+	size_t filename_len = strlen(filename);
+
+	entry = git__malloc(sizeof(git_tree_entry) + filename_len + 1);
+	if (!entry)
+		return NULL;
+
+	memset(entry, 0x0, sizeof(git_tree_entry));
+	memcpy(entry->filename, filename, filename_len);
+	entry->filename[filename_len] = 0;
+	entry->filename_len = filename_len;
+
+	return entry;
+}
+
 struct tree_key_search {
 	const char *filename;
 	size_t filename_len;
@@ -76,76 +136,116 @@
  * ambiguous because of folder vs file sorting, we look linearly
  * around the area for our target file.
  */
-static int tree_key_search(git_vector *entries, const char *filename)
+static int tree_key_search(
+	size_t *at_pos, git_vector *entries, const char *filename, size_t filename_len)
 {
 	struct tree_key_search ksearch;
 	const git_tree_entry *entry;
-
-	int homing, i;
+	size_t homing, i;
 
 	ksearch.filename = filename;
-	ksearch.filename_len = strlen(filename);
+	ksearch.filename_len = filename_len;
 
 	/* Initial homing search; find an entry on the tree with
 	 * the same prefix as the filename we're looking for */
-	homing = git_vector_bsearch2(entries, &homing_search_cmp, &ksearch);
-	if (homing < 0)
-		return homing;
+	if (git_vector_bsearch2(&homing, entries, &homing_search_cmp, &ksearch) < 0)
+		return GIT_ENOTFOUND;
 
 	/* We found a common prefix. Look forward as long as
 	 * there are entries that share the common prefix */
-	for (i = homing; i < (int)entries->length; ++i) {
+	for (i = homing; i < entries->length; ++i) {
 		entry = entries->contents[i];
 
 		if (homing_search_cmp(&ksearch, entry) < 0)
 			break;
 
-		if (strcmp(filename, entry->filename) == 0)
-			return i;
+		if (entry->filename_len == filename_len &&
+			memcmp(filename, entry->filename, filename_len) == 0) {
+			if (at_pos)
+				*at_pos = i;
+
+			return 0;
+		}
 	}
 
 	/* If we haven't found our filename yet, look backwards
 	 * too as long as we have entries with the same prefix */
-	for (i = homing - 1; i >= 0; --i) {
-		entry = entries->contents[i];
+	if (homing > 0) {
+		i = homing - 1;
 
-		if (homing_search_cmp(&ksearch, entry) > 0)
-			break;
+		do {
+			entry = entries->contents[i];
 
-		if (strcmp(filename, entry->filename) == 0)
-			return i;
+			if (homing_search_cmp(&ksearch, entry) > 0)
+				break;
+
+			if (entry->filename_len == filename_len &&
+				memcmp(filename, entry->filename, filename_len) == 0) {
+				if (at_pos)
+					*at_pos = i;
+
+				return 0;
+			}
+		} while (i-- > 0);
 	}
 
 	/* The filename doesn't exist at all */
 	return GIT_ENOTFOUND;
 }
 
-void git_tree__free(git_tree *tree)
+void git_tree_entry_free(git_tree_entry *entry)
 {
-	unsigned int i;
+	if (entry == NULL)
+		return;
 
-	for (i = 0; i < tree->entries.length; ++i) {
-		git_tree_entry *e;
-		e = git_vector_get(&tree->entries, i);
+	git__free(entry);
+}
 
-		git__free(e->filename);
-		git__free(e);
-	}
+git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry)
+{
+	size_t total_size;
+	git_tree_entry *copy;
 
+	assert(entry);
+
+	total_size = sizeof(git_tree_entry) + entry->filename_len + 1;
+
+	copy = git__malloc(total_size);
+	if (!copy)
+		return NULL;
+
+	memcpy(copy, entry, total_size);
+
+	return copy;
+}
+
+void git_tree__free(git_tree *tree)
+{
+	size_t i;
+	git_tree_entry *e;
+
+	git_vector_foreach(&tree->entries, i, e)
+		git_tree_entry_free(e);
+
 	git_vector_free(&tree->entries);
 	git__free(tree);
 }
 
-const git_oid *git_tree_id(git_tree *c)
+const git_oid *git_tree_id(const git_tree *t)
 {
-	return git_object_id((git_object *)c);
+	return git_object_id((const git_object *)t);
 }
 
-unsigned int git_tree_entry_attributes(const git_tree_entry *entry)
+git_repository *git_tree_owner(const git_tree *t)
 {
-	return entry->attr;
+	return git_object_owner((const git_object *)t);
 }
 
+git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
+{
+	return (git_filemode_t)entry->attr;
+}
+
 const char *git_tree_entry_name(const git_tree_entry *entry)
 {
 	assert(entry);
@@ -179,36 +279,61 @@
 	return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJ_ANY);
 }
 
-const git_tree_entry *git_tree_entry_byname(git_tree *tree, const char *filename)
+static const git_tree_entry *entry_fromname(
+	git_tree *tree, const char *name, size_t name_len)
 {
-	int idx;
-
-	assert(tree && filename);
+	size_t idx;
 
-	idx = tree_key_search(&tree->entries, filename);
-	if (idx == GIT_ENOTFOUND)
+	if (tree_key_search(&idx, &tree->entries, name, name_len) < 0)
 		return NULL;
 
 	return git_vector_get(&tree->entries, idx);
 }
 
-const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
+const git_tree_entry *git_tree_entry_byname(
+	git_tree *tree, const char *filename)
 {
+	assert(tree && filename);
+	return entry_fromname(tree, filename, strlen(filename));
+}
+
+const git_tree_entry *git_tree_entry_byindex(
+	git_tree *tree, size_t idx)
+{
 	assert(tree);
 	return git_vector_get(&tree->entries, idx);
 }
 
+const git_tree_entry *git_tree_entry_byoid(
+	const git_tree *tree, const git_oid *oid)
+{
+	size_t i;
+	const git_tree_entry *e;
+
+	assert(tree);
+
+	git_vector_foreach(&tree->entries, i, e) {
+		if (memcmp(&e->oid.id, &oid->id, sizeof(oid->id)) == 0)
+			return e;
+	}
+
+	return NULL;
+}
+
 int git_tree__prefix_position(git_tree *tree, const char *path)
 {
 	git_vector *entries = &tree->entries;
 	struct tree_key_search ksearch;
-	unsigned int at_pos;
+	size_t at_pos;
 
+	if (!path)
+		return 0;
+
 	ksearch.filename = path;
 	ksearch.filename_len = strlen(path);
 
 	/* Find tree entry with appropriate prefix */
-	git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);
+	git_vector_bsearch2(&at_pos, entries, &homing_search_cmp, &ksearch);
 
 	for (; at_pos < entries->length; ++at_pos) {
 		const git_tree_entry *entry = entries->contents[at_pos];
@@ -222,10 +347,10 @@
 			break;
 	}
 
-	return at_pos;
+	return (int)at_pos;
 }
 
-unsigned int git_tree_entrycount(git_tree *tree)
+size_t git_tree_entrycount(const git_tree *tree)
 {
 	assert(tree);
 	return tree->entries.length;
@@ -233,20 +358,16 @@
 
 unsigned int git_treebuilder_entrycount(git_treebuilder *bld)
 {
-        int count = 0;
-        int i;
 	assert(bld);
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *entry = bld->entries.contents[i];
-		if (! entry->removed)
-                  ++count;
-	}
-	return count;
+	return (unsigned int)bld->entrycount;
 }
 
-static int tree_error(const char *str)
+static int tree_error(const char *str, const char *path)
 {
-	giterr_set(GITERR_TREE, "%s", str);
+	if (path)
+		giterr_set(GITERR_TREE, "%s - %s", str, path);
+	else
+		giterr_set(GITERR_TREE, "%s", str);
 	return -1;
 }
 
@@ -257,29 +378,32 @@
 
 	while (buffer < buffer_end) {
 		git_tree_entry *entry;
-		int tmp;
-
-		entry = git__calloc(1, sizeof(git_tree_entry));
-		GITERR_CHECK_ALLOC(entry);
-
-		if (git_vector_insert(&tree->entries, entry) < 0)
-			return -1;
+		int attr;
 
-		if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 ||
-			!buffer || !valid_attributes(tmp))
-			return tree_error("Failed to parse tree. Can't parse attributes");
+		if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer)
+			return tree_error("Failed to parse tree. Can't parse filemode", NULL);
 
-		entry->attr = tmp;
+		attr = normalize_filemode(attr); /* make sure to normalize the filemode */
 
 		if (*buffer++ != ' ')
-			return tree_error("Failed to parse tree. Object is corrupted");
+			return tree_error("Failed to parse tree. Object is corrupted", NULL);
 
 		if (memchr(buffer, 0, buffer_end - buffer) == NULL)
-			return tree_error("Failed to parse tree. Object is corrupted");
+			return tree_error("Failed to parse tree. Object is corrupted", NULL);
 
-		entry->filename = git__strdup(buffer);
-		entry->filename_len = strlen(buffer);
+		/** Allocate the entry and store it in the entries vector */
+		{
+			entry = alloc_entry(buffer);
+			GITERR_CHECK_ALLOC(entry);
 
+			if (git_vector_insert(&tree->entries, entry) < 0) {
+				git__free(entry);
+				return -1;
+			}
+
+			entry->attr = attr;
+		}
+
 		while (buffer < buffer_end && *buffer != 0)
 			buffer++;
 
@@ -298,14 +422,13 @@
 	return tree_parse_buffer(tree, (char *)obj->raw.data, (char *)obj->raw.data + obj->raw.len);
 }
 
-static unsigned int find_next_dir(const char *dirname, git_index *index, unsigned int start)
+static size_t find_next_dir(const char *dirname, git_index *index, size_t start)
 {
-	unsigned int i, entries = git_index_entrycount(index);
-	size_t dirlen;
+	size_t dirlen, i, entries = git_index_entrycount(index);
 
 	dirlen = strlen(dirname);
 	for (i = start; i < entries; ++i) {
-		git_index_entry *entry = git_index_get(index, i);
+		const git_index_entry *entry = git_index_get_byindex(index, i);
 		if (strlen(entry->path) < dirlen ||
 		    memcmp(entry->path, dirname, dirlen) ||
 			(dirlen > 0 && entry->path[dirlen] != '/')) {
@@ -316,22 +439,29 @@
 	return i;
 }
 
-static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
+static int append_entry(
+	git_treebuilder *bld,
+	const char *filename,
+	const git_oid *id,
+	git_filemode_t filemode)
 {
 	git_tree_entry *entry;
 
-	entry = git__calloc(1, sizeof(git_tree_entry));
-	GITERR_CHECK_ALLOC(entry);
+	if (!valid_entry_name(filename))
+		return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
 
-	entry->filename = git__strdup(filename);
-	entry->filename_len = strlen(entry->filename);
+	entry = alloc_entry(filename);
+	GITERR_CHECK_ALLOC(entry);
 
 	git_oid_cpy(&entry->oid, id);
-	entry->attr = attributes;
+	entry->attr = (uint16_t)filemode;
 
-	if (git_vector_insert(&bld->entries, entry) < 0)
+	if (git_vector_insert(&bld->entries, entry) < 0) {
+		git__free(entry);
 		return -1;
+	}
 
+	bld->entrycount++;
 	return 0;
 }
 
@@ -340,11 +470,10 @@
 	git_repository *repo,
 	git_index *index,
 	const char *dirname,
-	unsigned int start)
+	size_t start)
 {
 	git_treebuilder *bld = NULL;
-
-	unsigned int i, entries = git_index_entrycount(index);
+	size_t i, entries = git_index_entrycount(index);
 	int error;
 	size_t dirname_len = strlen(dirname);
 	const git_tree_cache *cache;
@@ -352,13 +481,11 @@
 	cache = git_tree_cache_get(index->tree, dirname);
 	if (cache != NULL && cache->entries >= 0){
 		git_oid_cpy(oid, &cache->oid);
-		return find_next_dir(dirname, index, start);
+		return (int)find_next_dir(dirname, index, start);
 	}
 
-	error = git_treebuilder_create(&bld, NULL);
-	if (bld == NULL) {
+	if ((error = git_treebuilder_create(&bld, NULL)) < 0 || bld == NULL)
 		return -1;
-	}
 
 	/*
 	 * This loop is unfortunate, but necessary. The index doesn't have
@@ -366,8 +493,8 @@
 	 * need to keep track of the current position.
 	 */
 	for (i = start; i < entries; ++i) {
-		git_index_entry *entry = git_index_get(index, i);
-		char *filename, *next_slash;
+		const git_index_entry *entry = git_index_get_byindex(index, i);
+		const char *filename, *next_slash;
 
 	/*
 	 * If we've left our (sub)tree, exit the loop and return. The
@@ -398,7 +525,8 @@
 			/* Write out the subtree */
 			written = write_tree(&sub_oid, repo, index, subdir, i);
 			if (written < 0) {
-				tree_error("Failed to write subtree");
+				tree_error("Failed to write subtree", subdir);
+				git__free(subdir);
 				goto on_error;
 			} else {
 				i = written - 1; /* -1 because of the loop increment */
@@ -416,18 +544,15 @@
 			} else {
 				last_comp = subdir;
 			}
+
 			error = append_entry(bld, last_comp, &sub_oid, S_IFDIR);
 			git__free(subdir);
-			if (error < 0) {
-				tree_error("Failed to insert dir");
+			if (error < 0)
 				goto on_error;
-			}
 		} else {
 			error = append_entry(bld, filename, &entry->oid, entry->mode);
-			if (error < 0) {
-				tree_error("Failed to insert file");
+			if (error < 0)
 				goto on_error;
-			}
 		}
 	}
 
@@ -435,43 +560,54 @@
 		goto on_error;
 
 	git_treebuilder_free(bld);
-	return i;
+	return (int)i;
 
 on_error:
 	git_treebuilder_free(bld);
 	return -1;
 }
 
-int git_tree_create_fromindex(git_oid *oid, git_index *index)
+int git_tree__write_index(
+	git_oid *oid, git_index *index, git_repository *repo)
 {
 	int ret;
-	git_repository *repo;
+	bool old_ignore_case = false;
 
-	repo = (git_repository *)GIT_REFCOUNT_OWNER(index);
+	assert(oid && index && repo);
 
-	if (repo == NULL)
-		return tree_error("Failed to create tree. "
-		  "The index file is not backed up by an existing repository");
+	if (git_index_has_conflicts(index)) {
+		giterr_set(GITERR_INDEX,
+			"Cannot create a tree from a not fully merged index.");
+		return GIT_EUNMERGED;
+	}
 
 	if (index->tree != NULL && index->tree->entries >= 0) {
 		git_oid_cpy(oid, &index->tree->oid);
 		return 0;
 	}
 
-	/* The tree cache didn't help us */
+	/* The tree cache didn't help us; we'll have to write
+	 * out a tree. If the index is ignore_case, we must
+	 * make it case-sensitive for the duration of the tree-write
+	 * operation. */
+
+	if (index->ignore_case) {
+		old_ignore_case = true;
+		git_index__set_ignore_case(index, false);
+	}
+
 	ret = write_tree(oid, repo, index, "", 0);
-	return ret < 0 ? ret : 0;
-}
 
-static void sort_entries(git_treebuilder *bld)
-{
-	git_vector_sort(&bld->entries);
+	if (old_ignore_case)
+		git_index__set_ignore_case(index, true);
+
+	return ret < 0 ? ret : 0;
 }
 
 int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
 {
 	git_treebuilder *bld;
-	unsigned int i, source_entries = DEFAULT_TREE_SIZE;
+	size_t i, source_entries = DEFAULT_TREE_SIZE;
 
 	assert(builder_p);
 
@@ -485,10 +621,13 @@
 		goto on_error;
 
 	if (source != NULL) {
-		for (i = 0; i < source->entries.length; ++i) {
-			git_tree_entry *entry_src = source->entries.contents[i];
+		git_tree_entry *entry_src;
 
-			if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0)
+		git_vector_foreach(&source->entries, i, entry_src) {
+			if (append_entry(
+				bld, entry_src->filename,
+				&entry_src->oid,
+				entry_src->attr) < 0)
 				goto on_error;
 		}
 	}
@@ -501,42 +640,46 @@
 	return -1;
 }
 
-int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
+int git_treebuilder_insert(
+	const git_tree_entry **entry_out,
+	git_treebuilder *bld,
+	const char *filename,
+	const git_oid *id,
+	git_filemode_t filemode)
 {
 	git_tree_entry *entry;
-	int pos;
+	size_t pos;
 
 	assert(bld && id && filename);
 
-	if (!valid_attributes(attributes))
-		return tree_error("Failed to insert entry. Invalid attributes");
+	if (!valid_filemode(filemode))
+		return tree_error("Failed to insert entry. Invalid filemode for file", filename);
 
 	if (!valid_entry_name(filename))
-		return tree_error("Failed to insert entry. Invalid name for a tree entry");
-
-	pos = tree_key_search(&bld->entries, filename);
+		return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
 
-	if (pos >= 0) {
+	if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) {
 		entry = git_vector_get(&bld->entries, pos);
-		if (entry->removed)
+		if (entry->removed) {
 			entry->removed = 0;
+			bld->entrycount++;
+		}
 	} else {
-		entry = git__calloc(1, sizeof(git_tree_entry));
+		entry = alloc_entry(filename);
 		GITERR_CHECK_ALLOC(entry);
 
-		entry->filename = git__strdup(filename);
-		entry->filename_len = strlen(entry->filename);
+		if (git_vector_insert(&bld->entries, entry) < 0) {
+			git__free(entry);
+			return -1;
+		}
+
+		bld->entrycount++;
 	}
 
 	git_oid_cpy(&entry->oid, id);
-	entry->attr = attributes;
-
-	if (pos == GIT_ENOTFOUND) {
-		if (git_vector_insert(&bld->entries, entry) < 0)
-			return -1;
-	}
+	entry->attr = filemode;
 
-	if (entry_out != NULL)
+	if (entry_out)
 		*entry_out = entry;
 
 	return 0;
@@ -544,13 +687,12 @@
 
 static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
 {
-	int idx;
+	size_t idx;
 	git_tree_entry *entry;
 
 	assert(bld && filename);
 
-	idx = tree_key_search(&bld->entries, filename);
-	if (idx < 0)
+	if (tree_key_search(&idx, &bld->entries, filename, strlen(filename)) < 0)
 		return NULL;
 
 	entry = git_vector_get(&bld->entries, idx);
@@ -570,27 +712,29 @@
 	git_tree_entry *remove_ptr = treebuilder_get(bld, filename);
 
 	if (remove_ptr == NULL || remove_ptr->removed)
-		return tree_error("Failed to remove entry. File isn't in the tree");
+		return tree_error("Failed to remove entry. File isn't in the tree", filename);
 
 	remove_ptr->removed = 1;
+	bld->entrycount--;
 	return 0;
 }
 
 int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
 {
-	unsigned int i;
+	int error = 0;
+	size_t i;
 	git_buf tree = GIT_BUF_INIT;
 	git_odb *odb;
 
 	assert(bld);
 
-	sort_entries(bld);
+	git_vector_sort(&bld->entries);
 
 	/* Grow the buffer beforehand to an estimated size */
-	git_buf_grow(&tree, bld->entries.length * 72);
+	error = git_buf_grow(&tree, bld->entries.length * 72);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *entry = bld->entries.contents[i];
+	for (i = 0; i < bld->entries.length && !error; ++i) {
+		git_tree_entry *entry = git_vector_get(&bld->entries, i);
 
 		if (entry->removed)
 			continue;
@@ -598,153 +742,154 @@
 		git_buf_printf(&tree, "%o ", entry->attr);
 		git_buf_put(&tree, entry->filename, entry->filename_len + 1);
 		git_buf_put(&tree, (char *)entry->oid.id, GIT_OID_RAWSZ);
-	}
 
-	if (git_buf_oom(&tree))
-		goto on_error;
-
-	if (git_repository_odb__weakptr(&odb, repo) < 0)
-		goto on_error;
-
+		if (git_buf_oom(&tree))
+			error = -1;
+	}
 
-	if (git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)
-		goto on_error;
+	if (!error &&
+		!(error = git_repository_odb__weakptr(&odb, repo)))
+		error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
 
 	git_buf_free(&tree);
-	return 0;
-
-on_error:
-	git_buf_free(&tree);
-	return -1;
+	return error;
 }
 
-void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
+void git_treebuilder_filter(
+	git_treebuilder *bld,
+	git_treebuilder_filter_cb filter,
+	void *payload)
 {
-	unsigned int i;
+	size_t i;
+	git_tree_entry *entry;
 
 	assert(bld && filter);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *entry = bld->entries.contents[i];
-		if (!entry->removed && filter(entry, payload))
+	git_vector_foreach(&bld->entries, i, entry) {
+		if (!entry->removed && filter(entry, payload)) {
 			entry->removed = 1;
+			bld->entrycount--;
+		}
 	}
 }
 
 void git_treebuilder_clear(git_treebuilder *bld)
 {
-	unsigned int i;
+	size_t i;
+	git_tree_entry *e;
+
 	assert(bld);
 
-	for (i = 0; i < bld->entries.length; ++i) {
-		git_tree_entry *e = bld->entries.contents[i];
-		git__free(e->filename);
-		git__free(e);
-	}
+	git_vector_foreach(&bld->entries, i, e)
+		git_tree_entry_free(e);
 
 	git_vector_clear(&bld->entries);
+	bld->entrycount = 0;
 }
 
 void git_treebuilder_free(git_treebuilder *bld)
 {
+	if (bld == NULL)
+		return;
+
 	git_treebuilder_clear(bld);
 	git_vector_free(&bld->entries);
 	git__free(bld);
 }
 
-static int tree_frompath(
-	git_tree **parent_out,
+static size_t subpath_len(const char *path)
+{
+	const char *slash_pos = strchr(path, '/');
+	if (slash_pos == NULL)
+		return strlen(path);
+
+	return slash_pos - path;
+}
+
+int git_tree_entry_bypath(
+	git_tree_entry **entry_out,
 	git_tree *root,
-	git_buf *treeentry_path,
-	size_t offset)
+	const char *path)
 {
-	char *slash_pos = NULL;
-	const git_tree_entry* entry;
 	int error = 0;
 	git_tree *subtree;
-
-	if (!*(treeentry_path->ptr + offset)) {
-		giterr_set(GITERR_INVALID,
-			"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
-		return -1;
-	}
-
-	slash_pos = (char *)strchr(treeentry_path->ptr + offset, '/');
+	const git_tree_entry *entry;
+	size_t filename_len;
 
-	if (slash_pos == NULL)
-		return git_tree_lookup(
-			parent_out,
-			root->object.repo,
-			git_object_id((const git_object *)root)
-		);
+	/* Find how long is the current path component (i.e.
+	 * the filename between two slashes */
+	filename_len = subpath_len(path);
 
-	if (slash_pos == treeentry_path->ptr + offset) {
-		giterr_set(GITERR_INVALID,
-			"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
-		return -1;
+	if (filename_len == 0) {
+		giterr_set(GITERR_TREE, "Invalid tree path given");
+		return GIT_ENOTFOUND;
 	}
 
-	*slash_pos = '\0';
-
-	entry = git_tree_entry_byname(root, treeentry_path->ptr + offset);
-
-	if (slash_pos != NULL)
-		*slash_pos = '/';
+	entry = entry_fromname(root, path, filename_len);
 
 	if (entry == NULL) {
 		giterr_set(GITERR_TREE,
-			"No tree entry can be found from "
-			"the given tree and relative path '%s'.", treeentry_path->ptr);
+			"The path '%s' does not exist in the given tree", path);
 		return GIT_ENOTFOUND;
 	}
 
+	switch (path[filename_len]) {
+	case '/':
+		/* If there are more components in the path...
+		 * then this entry *must* be a tree */
+		if (!git_tree_entry__is_tree(entry)) {
+			giterr_set(GITERR_TREE,
+				"The path '%s' does not exist in the given tree", path);
+			return GIT_ENOTFOUND;
+		}
 
+		/* If there's only a slash left in the path, we 
+		 * return the current entry; otherwise, we keep
+		 * walking down the path */
+		if (path[filename_len + 1] != '\0')
+			break;
+
+	case '\0':
+		/* If there are no more components in the path, return
+		 * this entry */
+		*entry_out = git_tree_entry_dup(entry);
+		return 0;
+	}
+
 	if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
-		return error;
+		return -1;
 
-	error = tree_frompath(
-		parent_out,
+	error = git_tree_entry_bypath(
+		entry_out,
 		subtree,
-		treeentry_path,
-		(slash_pos - treeentry_path->ptr) + 1
+		path + filename_len + 1
 	);
 
 	git_tree_free(subtree);
 	return error;
 }
 
-int git_tree_get_subtree(
-	git_tree **subtree,
-	git_tree *root,
-	const char *subtree_path)
-{
-	int error;
-	git_buf buffer = GIT_BUF_INIT;
-
-	assert(subtree && root && subtree_path);
-
-	if ((error = git_buf_sets(&buffer, subtree_path)) == 0)
-		error = tree_frompath(subtree, root, &buffer, 0);
-
-	git_buf_free(&buffer);
-
-	return error;
-}
-
-static int tree_walk_post(
-	git_tree *tree,
+static int tree_walk(
+	const git_tree *tree,
 	git_treewalk_cb callback,
 	git_buf *path,
-	void *payload)
+	void *payload,
+	bool preorder)
 {
 	int error = 0;
-	unsigned int i;
-
-	for (i = 0; i < tree->entries.length; ++i) {
-		git_tree_entry *entry = tree->entries.contents[i];
+	size_t i;
+	const git_tree_entry *entry;
 
-		if (callback(path->ptr, entry, payload) < 0)
-			continue;
+	git_vector_foreach(&tree->entries, i, entry) {
+		if (preorder) {
+			error = callback(path->ptr, entry, payload);
+			if (error > 0)
+				continue;
+			if (error < 0) {
+				giterr_clear();
+				return GIT_EUSER;
+			}
+		}
 
 		if (git_tree_entry__is_tree(entry)) {
 			git_tree *subtree;
@@ -761,35 +906,40 @@
 			if (git_buf_oom(path))
 				return -1;
 
-			if (tree_walk_post(subtree, callback, path, payload) < 0)
-				return -1;
+			error = tree_walk(subtree, callback, path, payload, preorder);
+			if (error != 0)
+				break;
 
 			git_buf_truncate(path, path_len);
 			git_tree_free(subtree);
 		}
+
+		if (!preorder && callback(path->ptr, entry, payload) < 0) {
+			giterr_clear();
+			error = GIT_EUSER;
+			break;
+		}
 	}
 
-	return 0;
+	return error;
 }
 
-int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload)
+int git_tree_walk(
+	const git_tree *tree,
+	git_treewalk_mode mode,
+	git_treewalk_cb callback,
+	void *payload)
 {
 	int error = 0;
 	git_buf root_path = GIT_BUF_INIT;
 
-	switch (mode) {
-		case GIT_TREEWALK_POST:
-			error = tree_walk_post(tree, callback, &root_path, payload);
-			break;
-
-		case GIT_TREEWALK_PRE:
-			tree_error("Preorder tree walking is still not implemented");
-			return -1;
-
-		default:
-			giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
-			return -1;
+	if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
+		giterr_set(GITERR_INVALID, "Invalid walking mode for tree walk");
+		return -1;
 	}
+
+	error = tree_walk(
+		tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE));
 
 	git_buf_free(&root_path);
 
diff --git a/libgit2/src/tree.h b/libgit2/src/tree.h
--- a/libgit2/src/tree.h
+++ b/libgit2/src/tree.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -13,11 +13,11 @@
 #include "vector.h"
 
 struct git_tree_entry {
-	unsigned int attr;
-	char *filename;
+	uint16_t removed;
+	uint16_t attr;
 	git_oid oid;
 	size_t filename_len;
-	int removed;
+	char filename[1];
 };
 
 struct git_tree {
@@ -27,14 +27,16 @@
 
 struct git_treebuilder {
 	git_vector entries;
+	size_t entrycount; /* vector may contain "removed" entries */
 };
 
-
 GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
 {
 	return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
 }
 
+extern int git_tree_entry_icmp(const git_tree_entry *e1, const git_tree_entry *e2);
+
 void git_tree__free(git_tree *tree);
 int git_tree__parse(git_tree *tree, git_odb_object *obj);
 
@@ -47,5 +49,16 @@
  */
 int git_tree__prefix_position(git_tree *tree, const char *prefix);
 
+
+/**
+ * Write a tree to the given repository
+ */
+int git_tree__write_index(
+	git_oid *oid, git_index *index, git_repository *repo);
+
+/**
+ * Obsolete mode kept for compatibility reasons
+ */
+#define GIT_FILEMODE_BLOB_GROUP_WRITABLE 0100664
 
 #endif
diff --git a/libgit2/src/tsort.c b/libgit2/src/tsort.c
--- a/libgit2/src/tsort.c
+++ b/libgit2/src/tsort.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -23,9 +23,8 @@
 #	define MIN(x,y) (((x) < (y) ? (x) : (y)))
 #endif
 
-typedef int (*cmp_ptr_t)(const void *, const void *);
-
-static int binsearch(void **dst, const void *x, size_t size, cmp_ptr_t cmp)
+static int binsearch(
+	void **dst, const void *x, size_t size, git__sort_r_cmp cmp, void *payload)
 {
 	int l, c, r;
 	void *lx, *cx;
@@ -38,12 +37,12 @@
 	lx = dst[l];
 
 	/* check for beginning conditions */
-	if (cmp(x, lx) < 0)
+	if (cmp(x, lx, payload) < 0)
 		return 0;
 
-	else if (cmp(x, lx) == 0) {
+	else if (cmp(x, lx, payload) == 0) {
 		int i = 1;
-		while (cmp(x, dst[i]) == 0)
+		while (cmp(x, dst[i], payload) == 0)
 			i++;
 		return i;
 	}
@@ -51,7 +50,7 @@
 	/* guaranteed not to be >= rx */
 	cx = dst[c];
 	while (1) {
-		const int val = cmp(x, cx);
+		const int val = cmp(x, cx, payload);
 		if (val < 0) {
 			if (c - l <= 1) return c;
 			r = c;
@@ -62,7 +61,7 @@
 		} else {
 			do {
 				cx = dst[++c];
-			} while (cmp(x, cx) == 0);
+			} while (cmp(x, cx, payload) == 0);
 			return c;
 		}
 		c = l + ((r - l) >> 1);
@@ -71,7 +70,8 @@
 }
 
 /* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
-static void bisort(void **dst, size_t start, size_t size, cmp_ptr_t cmp)
+static void bisort(
+	void **dst, size_t start, size_t size, git__sort_r_cmp cmp, void *payload)
 {
 	size_t i;
 	void *x;
@@ -80,12 +80,12 @@
 	for (i = start; i < size; i++) {
 		int j;
 		/* If this entry is already correct, just move along */
-		if (cmp(dst[i - 1], dst[i]) <= 0)
+		if (cmp(dst[i - 1], dst[i], payload) <= 0)
 			continue;
 
 		/* Else we need to find the right place, shift everything over, and squeeze in */
 		x = dst[i];
-		location = binsearch(dst, x, i, cmp);
+		location = binsearch(dst, x, i, cmp, payload);
 		for (j = (int)i - 1; j >= location; j--) {
 			dst[j + 1] = dst[j];
 		}
@@ -102,7 +102,8 @@
 
 struct tsort_store {
 	size_t alloc;
-	cmp_ptr_t cmp;
+	git__sort_r_cmp cmp;
+	void *payload;
 	void **storage;
 };
 
@@ -118,7 +119,8 @@
 	}
 }
 
-static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+static ssize_t count_run(
+	void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
 {
 	ssize_t curr = start + 2;
 
@@ -126,7 +128,7 @@
 		return 1;
 
 	if (start >= size - 2) {
-		if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {
+		if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) {
 			void *tmp = dst[size - 1];
 			dst[size - 1] = dst[size - 2];
 			dst[size - 2] = tmp;
@@ -135,13 +137,15 @@
 		return 2;
 	}
 
-	if (store->cmp(dst[start], dst[start + 1]) <= 0) {
-		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)
+	if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) {
+		while (curr < size - 1 &&
+				store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0)
 			curr++;
 
 		return curr - start;
 	} else {
-		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)
+		while (curr < size - 1 &&
+				store->cmp(dst[curr - 1], dst[curr], store->payload) > 0)
 			curr++;
 
 		/* reverse in-place */
@@ -219,7 +223,7 @@
 
 		for (k = curr; k < curr + A + B; k++) {
 			if ((i < A) && (j < curr + A + B)) {
-				if (store->cmp(storage[i], dst[j]) <= 0)
+				if (store->cmp(storage[i], dst[j], store->payload) <= 0)
 					dst[k] = storage[i++];
 				else
 					dst[k] = dst[j++];
@@ -235,7 +239,7 @@
 
 		for (k = curr + A + B - 1; k >= curr; k--) {
 			if ((i >= 0) && (j >= curr)) {
-				if (store->cmp(dst[j], storage[i]) > 0)
+				if (store->cmp(dst[j], storage[i], store->payload) > 0)
 					dst[k] = dst[j--];
 				else
 					dst[k] = storage[i--];
@@ -307,7 +311,7 @@
 	if (run < minrun) run = minrun;\
 	if (run > (ssize_t)size - curr) run = size - curr;\
 	if (run > len) {\
-		bisort(&dst[curr], len, run, cmp);\
+		bisort(&dst[curr], len, run, cmp, payload);\
 		len = run;\
 	}\
 	run_stack[stack_curr].start = curr;\
@@ -329,7 +333,8 @@
 }\
 while (0)
 
-void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
+void git__tsort_r(
+	void **dst, size_t size, git__sort_r_cmp cmp, void *payload)
 {
 	struct tsort_store _store, *store = &_store;
 	struct tsort_run run_stack[128];
@@ -340,7 +345,7 @@
 	ssize_t minrun;
 
 	if (size < 64) {
-		bisort(dst, 1, size, cmp);
+		bisort(dst, 1, size, cmp, payload);
 		return;
 	}
 
@@ -351,6 +356,7 @@
 	store->alloc = 0;
 	store->storage = NULL;
 	store->cmp = cmp;
+	store->payload = payload;
 
 	PUSH_NEXT();
 	PUSH_NEXT();
@@ -364,4 +370,14 @@
 
 		PUSH_NEXT();
 	}
+}
+
+static int tsort_r_cmp(const void *a, const void *b, void *payload)
+{
+	return ((git__tsort_cmp)payload)(a, b);
+}
+
+void git__tsort(void **dst, size_t size, git__tsort_cmp cmp)
+{
+	git__tsort_r(dst, size, tsort_r_cmp, cmp);
 }
diff --git a/libgit2/src/unix/map.c b/libgit2/src/unix/map.c
--- a/libgit2/src/unix/map.c
+++ b/libgit2/src/unix/map.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -31,8 +31,11 @@
 		mflag = MAP_SHARED;
 	else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
 		mflag = MAP_PRIVATE;
+	else
+		mflag = MAP_SHARED;
 
 	out->data = mmap(NULL, len, mprot, mflag, fd, offset);
+
 	if (!out->data || out->data == MAP_FAILED) {
 		giterr_set(GITERR_OS, "Failed to mmap. Could not write data");
 		return -1;
@@ -47,6 +50,7 @@
 {
 	assert(map != NULL);
 	munmap(map->data, map->len);
+
 	return 0;
 }
 
diff --git a/libgit2/src/unix/posix.h b/libgit2/src/unix/posix.h
--- a/libgit2/src/unix/posix.h
+++ b/libgit2/src/unix/posix.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,25 +7,29 @@
 #ifndef INCLUDE_posix__w32_h__
 #define INCLUDE_posix__w32_h__
 
-#ifndef __sun
-#	include <fnmatch.h>
-#	define p_fnmatch(p, s, f) fnmatch(p, s, f)
-#else
-#	include "compat/fnmatch.h"
-#endif
-
 #include <stdio.h>
+#include <sys/param.h>
 
 #define p_lstat(p,b) lstat(p,b)
 #define p_readlink(a, b, c) readlink(a, b, c)
+#define p_symlink(o,n) symlink(o, n)
 #define p_link(o,n) link(o, n)
 #define p_unlink(p) unlink(p)
 #define p_mkdir(p,m) mkdir(p, m)
 #define p_fsync(fd) fsync(fd)
-#define p_realpath(p, po) realpath(p, po)
+
+/* The OpenBSD realpath function behaves differently */
+#if !defined(__OpenBSD__)
+# define p_realpath(p, po) realpath(p, po)
+#endif
+
 #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
 #define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__)
 #define p_mkstemp(p) mkstemp(p)
 #define p_setenv(n,v,o) setenv(n,v,o)
+#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+
+/* see win32/posix.h for explanation about why this exists */
+#define p_lstat_posixly(p,b) lstat(p,b)
 
 #endif
diff --git a/libgit2/src/unix/realpath.c b/libgit2/src/unix/realpath.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/unix/realpath.c
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include <git2/common.h>
+
+#ifdef __OpenBSD__
+
+#include <limits.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+char *p_realpath(const char *pathname, char *resolved)
+{
+	char *ret;
+
+	if ((ret = realpath(pathname, resolved)) == NULL)
+		return NULL;
+
+	/* Figure out if the file exists */
+	if (!access(ret, F_OK))
+		return ret;
+
+	return NULL;
+}
+
+#endif
diff --git a/libgit2/src/util.c b/libgit2/src/util.c
--- a/libgit2/src/util.c
+++ b/libgit2/src/util.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,6 +10,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include "posix.h"
+#include "fileops.h"
 
 #ifdef _MSC_VER
 # include <Shlwapi.h>
@@ -22,6 +23,91 @@
 	*rev = LIBGIT2_VER_REVISION;
 }
 
+int git_libgit2_capabilities()
+{
+	return 0
+#ifdef GIT_THREADS
+		| GIT_CAP_THREADS
+#endif
+#if defined(GIT_SSL) || defined(GIT_WINHTTP)
+		| GIT_CAP_HTTPS
+#endif
+	;
+}
+
+/* Declarations for tuneable settings */
+extern size_t git_mwindow__window_size;
+extern size_t git_mwindow__mapped_limit;
+extern size_t git_odb__cache_size;
+
+static int config_level_to_futils_dir(int config_level)
+{
+	int val = -1;
+
+	switch (config_level) {
+	case GIT_CONFIG_LEVEL_SYSTEM: val = GIT_FUTILS_DIR_SYSTEM; break;
+	case GIT_CONFIG_LEVEL_XDG:    val = GIT_FUTILS_DIR_XDG; break;
+	case GIT_CONFIG_LEVEL_GLOBAL: val = GIT_FUTILS_DIR_GLOBAL; break;
+	default:
+		giterr_set(
+			GITERR_INVALID, "Invalid config path selector %d", config_level);
+	}
+
+	return val;
+}
+
+int git_libgit2_opts(int key, ...)
+{
+	int error = 0;
+	va_list ap;
+
+	va_start(ap, key);
+
+	switch (key) {
+	case GIT_OPT_SET_MWINDOW_SIZE:
+		git_mwindow__window_size = va_arg(ap, size_t);
+		break;
+
+	case GIT_OPT_GET_MWINDOW_SIZE:
+		*(va_arg(ap, size_t *)) = git_mwindow__window_size;
+		break;
+
+	case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
+		git_mwindow__mapped_limit = va_arg(ap, size_t);
+		break;
+
+	case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
+		*(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
+		break;
+
+	case GIT_OPT_GET_SEARCH_PATH:
+		if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) {
+			char *out = va_arg(ap, char *);
+			size_t outlen = va_arg(ap, size_t);
+
+			error = git_futils_dirs_get_str(out, outlen, error);
+		}
+		break;
+
+	case GIT_OPT_SET_SEARCH_PATH:
+		if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0)
+			error = git_futils_dirs_set(error, va_arg(ap, const char *));
+		break;
+
+	case GIT_OPT_GET_ODB_CACHE_SIZE:
+		*(va_arg(ap, size_t *)) = git_odb__cache_size;
+		break;
+
+	case GIT_OPT_SET_ODB_CACHE_SIZE:
+		git_odb__cache_size = va_arg(ap, size_t);
+		break;
+	}
+
+	va_end(ap);
+
+	return error;
+}
+
 void git_strarray_free(git_strarray *array)
 {
 	size_t i;
@@ -29,6 +115,8 @@
 		git__free(array->strings[i]);
 
 	git__free(array->strings);
+
+	memset(array, 0, sizeof(*array));
 }
 
 int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
@@ -46,8 +134,10 @@
 	GITERR_CHECK_ALLOC(tgt->strings);
 
 	for (i = 0; i < src->count; ++i) {
-		tgt->strings[tgt->count] = git__strdup(src->strings[i]);
+		if (!src->strings[i])
+			continue;
 
+		tgt->strings[tgt->count] = git__strdup(src->strings[i]);
 		if (!tgt->strings[tgt->count]) {
 			git_strarray_free(tgt);
 			memset(tgt, 0, sizeof(*tgt));
@@ -162,6 +252,42 @@
 	return error;
 }
 
+int git__strcmp(const char *a, const char *b)
+{
+	while (*a && *b && *a == *b)
+		++a, ++b;
+	return (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b);
+}
+
+int git__strcasecmp(const char *a, const char *b)
+{
+	while (*a && *b && tolower(*a) == tolower(*b))
+		++a, ++b;
+	return (tolower(*a) - tolower(*b));
+}
+
+int git__strncmp(const char *a, const char *b, size_t sz)
+{
+	while (sz && *a && *b && *a == *b)
+		--sz, ++a, ++b;
+	if (!sz)
+		return 0;
+	return (int)(*(const unsigned char *)a) - (int)(*(const unsigned char *)b);
+}
+
+int git__strncasecmp(const char *a, const char *b, size_t sz)
+{
+	int al, bl;
+
+	do {
+		al = (unsigned char)tolower(*a);
+		bl = (unsigned char)tolower(*b);
+		++a, ++b;
+	} while (--sz && al && al == bl);
+
+	return al - bl;
+}
+
 void git__strntolower(char *str, size_t len)
 {
 	size_t i;
@@ -179,7 +305,7 @@
 int git__prefixcmp(const char *str, const char *prefix)
 {
 	for (;;) {
-		char p = *(prefix++), s;
+		unsigned char p = *(prefix++), s;
 		if (!p)
 			return 0;
 		if ((s = *(str++)) != p)
@@ -187,6 +313,11 @@
 	}
 }
 
+int git__prefixcmp_icase(const char *str, const char *prefix)
+{
+	return strncasecmp(str, prefix, strlen(prefix));
+}
+
 int git__suffixcmp(const char *str, const char *suffix)
 {
 	size_t a = strlen(str);
@@ -221,6 +352,24 @@
 	return NULL;
 }
 
+/* Similar to strtok, but does not collapse repeated tokens. */
+char *git__strsep(char **end, const char *sep)
+{
+	char *start = *end, *ptr = *end;
+
+	while (*ptr && !strchr(sep, *ptr))
+		++ptr;
+
+	if (*ptr) {
+		*end = ptr + 1;
+		*ptr = '\0';
+
+		return start;
+	}
+
+	return NULL;
+}
+
 void git__hexdump(const char *buffer, size_t len)
 {
 	static const size_t LINE_WIDTH = 16;
@@ -367,6 +516,30 @@
  *
  * Copyright (c) 1990 Regents of the University of California.
  * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [rescinded 22 July 1999]
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 int git__bsearch(
 	void **array,
@@ -375,11 +548,11 @@
 	int (*compare)(const void *, const void *),
 	size_t *position)
 {
-	unsigned int lim;
+	size_t lim;
 	int cmp = -1;
 	void **part, **base = array;
 
-	for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
+	for (lim = array_len; lim != 0; lim >>= 1) {
 		part = base + (lim >> 1);
 		cmp = (*compare)(key, *part);
 		if (cmp == 0) {
@@ -395,12 +568,43 @@
 	if (position)
 		*position = (base - array);
 
-	return (cmp == 0) ? 0 : -1;
+	return (cmp == 0) ? 0 : GIT_ENOTFOUND;
 }
 
+int git__bsearch_r(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare_r)(const void *, const void *, void *),
+	void *payload,
+	size_t *position)
+{
+	size_t lim;
+	int cmp = -1;
+	void **part, **base = array;
+
+	for (lim = array_len; lim != 0; lim >>= 1) {
+		part = base + (lim >> 1);
+		cmp = (*compare_r)(key, *part, payload);
+		if (cmp == 0) {
+			base = part;
+			break;
+		}
+		if (cmp > 0) { /* key > p; take right partition */
+			base = part + 1;
+			lim--;
+		} /* else take left partition */
+	}
+
+	if (position)
+		*position = (base - array);
+
+	return (cmp == 0) ? 0 : GIT_ENOTFOUND;
+}
+
 /**
  * A strcmp wrapper
- * 
+ *
  * We don't want direct pointers to the CRT on Windows, we may
  * get stdcall conflicts.
  */
@@ -415,12 +619,8 @@
 int git__parse_bool(int *out, const char *value)
 {
 	/* A missing value means true */
-	if (value == NULL) {
-		*out = 1;
-		return 0;
-	}
-
-	if (!strcasecmp(value, "true") ||
+	if (value == NULL ||
+		!strcasecmp(value, "true") ||
 		!strcasecmp(value, "yes") ||
 		!strcasecmp(value, "on")) {
 		*out = 1;
@@ -428,10 +628,82 @@
 	}
 	if (!strcasecmp(value, "false") ||
 		!strcasecmp(value, "no") ||
-		!strcasecmp(value, "off")) {
+		!strcasecmp(value, "off") ||
+		value[0] == '\0') {
 		*out = 0;
 		return 0;
 	}
 
 	return -1;
+}
+
+size_t git__unescape(char *str)
+{
+	char *scan, *pos = str;
+
+	for (scan = str; *scan; pos++, scan++) {
+		if (*scan == '\\' && *(scan + 1) != '\0')
+			scan++; /* skip '\' but include next char */
+		if (pos != scan)
+			*pos = *scan;
+	}
+
+	if (pos != scan) {
+		*pos = '\0';
+	}
+
+	return (pos - str);
+}
+
+#if defined(GIT_WIN32) || defined(BSD)
+typedef struct {
+	git__sort_r_cmp cmp;
+	void *payload;
+} git__qsort_r_glue;
+
+static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
+	void *payload, const void *a, const void *b)
+{
+	git__qsort_r_glue *glue = payload;
+	return glue->cmp(a, b, glue->payload);
+}
+#endif
+
+void git__qsort_r(
+	void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
+{
+#if defined(__MINGW32__) || defined(__OpenBSD__)
+	git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
+#elif defined(GIT_WIN32)
+	git__qsort_r_glue glue = { cmp, payload };
+	qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
+#elif defined(BSD)
+	git__qsort_r_glue glue = { cmp, payload };
+	qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
+#else
+	qsort_r(els, nel, elsize, cmp, payload);
+#endif
+}
+
+void git__insertsort_r(
+	void *els, size_t nel, size_t elsize, void *swapel,
+	git__sort_r_cmp cmp, void *payload)
+{
+	uint8_t *base = els;
+	uint8_t *end = base + nel * elsize;
+	uint8_t *i, *j;
+	bool freeswap = !swapel;
+
+	if (freeswap)
+		swapel = git__malloc(elsize);
+
+	for (i = base + elsize; i < end; i += elsize)
+		for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize) {
+			memcpy(swapel, j, elsize);
+			memcpy(j, j - elsize, elsize);
+			memcpy(j - elsize, swapel, elsize);
+		}
+
+	if (freeswap)
+		git__free(swapel);
 }
diff --git a/libgit2/src/util.h b/libgit2/src/util.h
--- a/libgit2/src/util.h
+++ b/libgit2/src/util.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -13,6 +13,9 @@
 #ifndef min
 # define min(a,b) ((a) < (b) ? (a) : (b))
 #endif
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
 
 /*
  * Custom memory allocation wrappers
@@ -42,25 +45,31 @@
 
 GIT_INLINE(char *) git__strndup(const char *str, size_t n)
 {
-	size_t length;
+	size_t length = 0;
 	char *ptr;
 
-	length = strlen(str);
-	if (n < length)
-		length = n;
+	while (length < n && str[length])
+		++length;
 
-	ptr = (char*)malloc(length + 1);
-	if (!ptr) {
-		giterr_set_oom();
-		return NULL;
-	}
+	ptr = (char*)git__malloc(length + 1);
 
-	memcpy(ptr, str, length);
+	if (length)
+		memcpy(ptr, str, length);
+
 	ptr[length] = '\0';
 
 	return ptr;
 }
 
+/* NOTE: This doesn't do null or '\0' checking.  Watch those boundaries! */
+GIT_INLINE(char *) git__substrdup(const char *start, size_t n)
+{
+	char *ptr = (char*)git__malloc(n+1);
+	memcpy(ptr, start, n);
+	ptr[n] = '\0';
+	return ptr;
+}
+
 GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
 {
 	void *new_ptr = realloc(ptr, size);
@@ -70,9 +79,21 @@
 
 #define git__free(ptr) free(ptr)
 
+#define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
+	((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
+
+#define CASESELECT(IGNORE_CASE, ICASE, CASE) \
+	((IGNORE_CASE) ? (ICASE) : (CASE))
+
 extern int git__prefixcmp(const char *str, const char *prefix);
+extern int git__prefixcmp_icase(const char *str, const char *prefix);
 extern int git__suffixcmp(const char *str, const char *suffix);
 
+GIT_INLINE(int) git__signum(int val)
+{
+	return ((val > 0) - (val < 0));
+}
+
 extern int git__strtol32(int32_t *n, const char *buff, const char **end_buf, int base);
 extern int git__strtol64(int64_t *n, const char *buff, const char **end_buf, int base);
 
@@ -94,6 +115,7 @@
 #endif
 
 extern char *git__strtok(char **end, const char *sep);
+extern char *git__strsep(char **end, const char *sep);
 
 extern void git__strntolower(char *str, size_t len);
 extern void git__strtolower(char *str);
@@ -105,22 +127,64 @@
 	return s;
 }
 
-extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
+GIT_INLINE(const void *) git__memrchr(const void *s, int c, size_t n)
+{
+	const unsigned char *cp;
 
+	if (n != 0) {
+		cp = (unsigned char *)s + n;
+		do {
+			if (*(--cp) == (unsigned char)c)
+				return cp;
+		} while (--n != 0);
+	}
+
+	return NULL;
+}
+
+typedef int (*git__tsort_cmp)(const void *a, const void *b);
+
+extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
+
+typedef int (*git__sort_r_cmp)(const void *a, const void *b, void *payload);
+
+extern void git__tsort_r(
+	void **dst, size_t size, git__sort_r_cmp cmp, void *payload);
+
+extern void git__qsort_r(
+	void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload);
+
+extern void git__insertsort_r(
+	void *els, size_t nel, size_t elsize, void *swapel,
+	git__sort_r_cmp cmp, void *payload);
+
 /**
  * @param position If non-NULL, this will be set to the position where the
  * 		element is or would be inserted if not found.
- * @return pos (>=0) if found or -1 if not found
+ * @return 0 if found; GIT_ENOTFOUND if not found
  */
 extern int git__bsearch(
 	void **array,
 	size_t array_len,
 	const void *key,
-	int (*compare)(const void *, const void *),
+	int (*compare)(const void *key, const void *element),
 	size_t *position);
 
+extern int git__bsearch_r(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare_r)(const void *key, const void *element, void *payload),
+	void *payload,
+	size_t *position);
+
 extern int git__strcmp_cb(const void *a, const void *b);
 
+extern int git__strcmp(const char *a, const char *b);
+extern int git__strcasecmp(const char *a, const char *b);
+extern int git__strncmp(const char *a, const char *b, size_t sz);
+extern int git__strncasecmp(const char *a, const char *b, size_t sz);
+
 typedef struct {
 	short refcount;
 	void *owner;
@@ -204,9 +268,14 @@
     return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
 }
 
+GIT_INLINE(bool) git__isdigit(int c)
+{
+    return (c >= '0' && c <= '9');
+}
+
 GIT_INLINE(bool) git__isspace(int c)
 {
-    return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+    return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */);
 }
 
 GIT_INLINE(bool) git__iswildcard(int c)
@@ -222,5 +291,24 @@
  * Valid values for false are: 'false', 'no', 'off'
  */
 extern int git__parse_bool(int *out, const char *value);
+
+/*
+ * Parse a string into a value as a git_time_t.
+ *
+ * Sample valid input:
+ * - "yesterday"
+ * - "July 17, 2003"
+ * - "2003-7-17 08:23"
+ */
+int git__date_parse(git_time_t *out, const char *date);
+
+/*
+ * Unescapes a string in-place.
+ * 
+ * Edge cases behavior:
+ * - "jackie\" -> "jacky\"
+ * - "chan\\" -> "chan\"
+ */
+extern size_t git__unescape(char *str);
 
 #endif /* INCLUDE_util_h__ */
diff --git a/libgit2/src/vector.c b/libgit2/src/vector.c
--- a/libgit2/src/vector.c
+++ b/libgit2/src/vector.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -9,18 +9,60 @@
 #include "repository.h"
 #include "vector.h"
 
-static const double resize_factor = 1.75;
-static const unsigned int minimum_size = 8;
+/* In elements, not bytes */
+#define MIN_ALLOCSIZE	8
 
-static int resize_vector(git_vector *v)
+GIT_INLINE(size_t) compute_new_size(git_vector *v)
 {
-	v->_alloc_size = ((unsigned int)(v->_alloc_size * resize_factor)) + 1;
-	if (v->_alloc_size < minimum_size)
-		v->_alloc_size = minimum_size;
+	size_t new_size = v->_alloc_size;
 
-	v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *));
+	/* Use a resize factor of 1.5, which is quick to compute using integer
+	 * instructions and less than the golden ratio (1.618...) */
+	if (new_size < MIN_ALLOCSIZE)
+		new_size = MIN_ALLOCSIZE;
+	else if (new_size <= (SIZE_MAX / 3) * 2)
+		new_size += new_size / 2;
+	else
+		new_size = SIZE_MAX;
+
+	return new_size;
+}
+
+GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
+{
+	size_t new_bytes = new_size * sizeof(void *);
+	void *new_contents;
+
+	/* Check for overflow */
+	if (new_bytes / sizeof(void *) != new_size)
+		GITERR_CHECK_ALLOC(NULL);
+
+	new_contents = git__realloc(v->contents, new_bytes);
+	GITERR_CHECK_ALLOC(new_contents);
+
+	v->_alloc_size = new_size;
+	v->contents = new_contents;
+
+	return 0;
+}
+
+int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
+{
+	size_t bytes;
+
+	assert(v && src);
+
+	bytes = src->length * sizeof(void *);
+
+	v->_alloc_size = src->length;
+	v->_cmp = cmp;
+	v->length = src->length;
+	v->sorted = src->sorted && cmp == src->_cmp;
+	v->contents = git__malloc(bytes);
 	GITERR_CHECK_ALLOC(v->contents);
 
+	memcpy(v->contents, src->contents, bytes);
+
 	return 0;
 }
 
@@ -35,25 +77,17 @@
 	v->_alloc_size = 0;
 }
 
-int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp)
+int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
 {
 	assert(v);
 
-	memset(v, 0x0, sizeof(git_vector));
-
-	if (initial_size == 0)
-		initial_size = minimum_size;
-
-	v->_alloc_size = initial_size;
+	v->_alloc_size = 0;
 	v->_cmp = cmp;
-
 	v->length = 0;
 	v->sorted = 1;
-
-	v->contents = git__malloc(v->_alloc_size * sizeof(void *));
-	GITERR_CHECK_ALLOC(v->contents);
+	v->contents = NULL;
 
-	return 0;
+	return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
 }
 
 int git_vector_insert(git_vector *v, void *element)
@@ -61,7 +95,7 @@
 	assert(v);
 
 	if (v->length >= v->_alloc_size &&
-		resize_vector(v) < 0)
+		resize_vector(v, compute_new_size(v)) < 0)
 		return -1;
 
 	v->contents[v->length++] = element;
@@ -82,26 +116,25 @@
 		git_vector_sort(v);
 
 	if (v->length >= v->_alloc_size &&
-		resize_vector(v) < 0)
+		resize_vector(v, compute_new_size(v)) < 0)
 		return -1;
 
 	/* If we find the element and have a duplicate handler callback,
 	 * invoke it.  If it returns non-zero, then cancel insert, otherwise
 	 * proceed with normal insert.
 	 */
-	if (git__bsearch(v->contents, v->length, element, v->_cmp, &pos) >= 0 &&
-		on_dup != NULL &&
-		(result = on_dup(&v->contents[pos], element)) < 0)
+	if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) &&
+		on_dup && (result = on_dup(&v->contents[pos], element)) < 0)
 		return result;
 
 	/* shift elements to the right */
-	if (pos < v->length) {
+	if (pos < v->length)
 		memmove(v->contents + pos + 1, v->contents + pos,
 		        (v->length - pos) * sizeof(void *));
-	}
 
 	v->contents[pos] = element;
 	v->length++;
+
 	return 0;
 }
 
@@ -109,47 +142,44 @@
 {
 	assert(v);
 
-	if (v->sorted || v->_cmp == NULL)
+	if (v->sorted || !v->_cmp)
 		return;
 
 	git__tsort(v->contents, v->length, v->_cmp);
 	v->sorted = 1;
 }
 
-int git_vector_bsearch3(
-	unsigned int *at_pos,
+int git_vector_bsearch2(
+	size_t *at_pos,
 	git_vector *v,
 	git_vector_cmp key_lookup,
 	const void *key)
 {
-	int rval;
-	size_t pos;
-
 	assert(v && key && key_lookup);
 
 	/* need comparison function to sort the vector */
-	assert(v->_cmp != NULL);
+	if (!v->_cmp)
+		return -1;
 
 	git_vector_sort(v);
 
-	rval = git__bsearch(v->contents, v->length, key, key_lookup, &pos);
-
-	if (at_pos != NULL)
-		*at_pos = (unsigned int)pos;
-
-	return (rval >= 0) ? (int)pos : GIT_ENOTFOUND;
+	return git__bsearch(v->contents, v->length, key, key_lookup, at_pos);
 }
 
 int git_vector_search2(
-	git_vector *v, git_vector_cmp key_lookup, const void *key)
+	size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key)
 {
-	unsigned int i;
+	size_t i;
 
 	assert(v && key && key_lookup);
 
 	for (i = 0; i < v->length; ++i) {
-		if (key_lookup(key, v->contents[i]) == 0)
-			return i;
+		if (key_lookup(key, v->contents[i]) == 0) {
+			if (at_pos)
+				*at_pos = i;
+
+			return 0;
+		}
 	}
 
 	return GIT_ENOTFOUND;
@@ -160,23 +190,26 @@
 	return (a == b) ? 0 : -1;
 }
 
-int git_vector_search(git_vector *v, const void *entry)
+int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry)
 {
-	return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);
+	return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry);
 }
 
-int git_vector_remove(git_vector *v, unsigned int idx)
+int git_vector_remove(git_vector *v, size_t idx)
 {
-	unsigned int i;
+	size_t shift_count;
 
 	assert(v);
 
-	if (idx >= v->length || v->length == 0)
+	if (idx >= v->length)
 		return GIT_ENOTFOUND;
 
-	for (i = idx; i < v->length - 1; ++i)
-		v->contents[i] = v->contents[i + 1];
+	shift_count = v->length - idx - 1;
 
+	if (shift_count)
+		memmove(&v->contents[idx], &v->contents[idx + 1],
+			shift_count * sizeof(void *));
+
 	v->length--;
 	return 0;
 }
@@ -190,7 +223,7 @@
 void git_vector_uniq(git_vector *v)
 {
 	git_vector_cmp cmp;
-	unsigned int i, j;
+	size_t i, j;
 
 	if (v->length <= 1)
 		return;
@@ -207,6 +240,21 @@
 	v->length -= j - i - 1;
 }
 
+void git_vector_remove_matching(
+	git_vector *v, int (*match)(const git_vector *v, size_t idx))
+{
+	size_t i, j;
+
+	for (i = 0, j = 0; j < v->length; ++j) {
+		v->contents[i] = v->contents[j];
+
+		if (!match(v, i))
+			i++;
+	}
+
+	v->length = i;
+}
+
 void git_vector_clear(git_vector *v)
 {
 	assert(v);
@@ -218,10 +266,41 @@
 {
 	git_vector t;
 
-	if (!a || !b || a == b)
-		return;
+	assert(a && b);
 
-	memcpy(&t, a, sizeof(t));
-	memcpy(a, b, sizeof(t));
-	memcpy(b, &t, sizeof(t));
+	if (a != b) {
+		memcpy(&t, a, sizeof(t));
+		memcpy(a, b, sizeof(t));
+		memcpy(b, &t, sizeof(t));
+	}
+}
+
+int git_vector_resize_to(git_vector *v, size_t new_length)
+{
+	if (new_length <= v->length)
+		return 0;
+
+	if (new_length > v->_alloc_size &&
+		resize_vector(v, new_length) < 0)
+		return -1;
+
+	memset(&v->contents[v->length], 0,
+		sizeof(void *) * (new_length - v->length));
+
+	v->length = new_length;
+
+	return 0;
+}
+
+int git_vector_set(void **old, git_vector *v, size_t position, void *value)
+{
+	if (git_vector_resize_to(v, position + 1) < 0)
+		return -1;
+
+	if (old != NULL)
+		*old = v->contents[position];
+
+	v->contents[position] = value;
+
+	return 0;
 }
diff --git a/libgit2/src/vector.h b/libgit2/src/vector.h
--- a/libgit2/src/vector.h
+++ b/libgit2/src/vector.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -12,52 +12,50 @@
 typedef int (*git_vector_cmp)(const void *, const void *);
 
 typedef struct git_vector {
-	unsigned int _alloc_size;
+	size_t _alloc_size;
 	git_vector_cmp _cmp;
 	void **contents;
-	unsigned int length;
+	size_t length;
 	int sorted;
 } git_vector;
 
 #define GIT_VECTOR_INIT {0}
 
-int git_vector_init(git_vector *v, unsigned int initial_size, git_vector_cmp cmp);
+int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp);
 void git_vector_free(git_vector *v);
 void git_vector_clear(git_vector *v);
+int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp);
 void git_vector_swap(git_vector *a, git_vector *b);
 
 void git_vector_sort(git_vector *v);
 
-int git_vector_search(git_vector *v, const void *entry);
-int git_vector_search2(git_vector *v, git_vector_cmp cmp, const void *key);
-
-int git_vector_bsearch3(
-	unsigned int *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
+/** Linear search for matching entry using internal comparison function */
+int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry);
 
-GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)
-{
-	return git_vector_bsearch3(NULL, v, v->_cmp, key);
-}
+/** Linear search for matching entry using explicit comparison function */
+int git_vector_search2(size_t *at_pos, const git_vector *v, git_vector_cmp cmp, const void *key);
 
-GIT_INLINE(int) git_vector_bsearch2(
-	git_vector *v, git_vector_cmp cmp, const void *key)
-{
-	return git_vector_bsearch3(NULL, v, cmp, key);
-}
+/**
+ * Binary search for matching entry using explicit comparison function that
+ * returns position where item would go if not found.
+ */
+int git_vector_bsearch2(
+	size_t *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
 
-GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
+/** Binary search for matching entry using internal comparison function */
+GIT_INLINE(int) git_vector_bsearch(size_t *at_pos, git_vector *v, const void *key)
 {
-	return (position < v->length) ? v->contents[position] : NULL;
+	return git_vector_bsearch2(at_pos, v, v->_cmp, key);
 }
 
-GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int position)
+GIT_INLINE(void *) git_vector_get(const git_vector *v, size_t position)
 {
 	return (position < v->length) ? v->contents[position] : NULL;
 }
 
 #define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
 
-GIT_INLINE(void *) git_vector_last(git_vector *v)
+GIT_INLINE(void *) git_vector_last(const git_vector *v)
 {
 	return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
 }
@@ -66,13 +64,18 @@
 	for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
 
 #define git_vector_rforeach(v, iter, elem)	\
-	for ((iter) = (v)->length; (iter) > 0 && ((elem) = (v)->contents[(iter)-1], 1); (iter)-- )
+	for ((iter) = (v)->length - 1; (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
 
 int git_vector_insert(git_vector *v, void *element);
 int git_vector_insert_sorted(git_vector *v, void *element,
 	int (*on_dup)(void **old, void *new));
-int git_vector_remove(git_vector *v, unsigned int idx);
+int git_vector_remove(git_vector *v, size_t idx);
 void git_vector_pop(git_vector *v);
 void git_vector_uniq(git_vector *v);
+void git_vector_remove_matching(
+	git_vector *v, int (*match)(const git_vector *v, size_t idx));
+
+int git_vector_resize_to(git_vector *v, size_t new_length);
+int git_vector_set(void **old, git_vector *v, size_t position, void *value);
 
 #endif
diff --git a/libgit2/src/win32/dir.c b/libgit2/src/win32/dir.c
--- a/libgit2/src/win32/dir.c
+++ b/libgit2/src/win32/dir.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,7 +7,6 @@
 #define GIT__WIN32_NO_WRAP_DIR
 #include "dir.h"
 #include "utf-conv.h"
-#include "git2/windows.h"
 
 static int init_filter(char *filter, size_t n, const char *dir)
 {
@@ -26,8 +25,8 @@
 
 git__DIR *git__opendir(const char *dir)
 {
-	char filter[4096];
-	wchar_t* filter_w = NULL;
+	char filter[GIT_WIN_PATH];
+	wchar_t filter_w[GIT_WIN_PATH];
 	git__DIR *new = NULL;
 
 	if (!dir || !init_filter(filter, sizeof(filter), dir))
@@ -41,12 +40,8 @@
 	if (!new->dir)
 		goto fail;
 
-	filter_w = gitwin_to_utf16(filter);
-	if (!filter_w)
-		goto fail;
-
+	git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
 	new->h = FindFirstFileW(filter_w, &new->f);
-	git__free(filter_w);
 
 	if (new->h == INVALID_HANDLE_VALUE) {
 		giterr_set(GITERR_OS, "Could not open directory '%s'", dir);
@@ -85,16 +80,9 @@
 	if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
 		return -1;
 
+	git__utf16_to_8(entry->d_name, d->f.cFileName);
 	entry->d_ino = 0;
 
-	if (WideCharToMultiByte(
-		gitwin_get_codepage(), 0, d->f.cFileName, -1,
-		entry->d_name, GIT_PATH_MAX, NULL, NULL) == 0)
-	{
-		giterr_set(GITERR_OS, "Could not convert filename to UTF-8");
-		return -1;
-	}
-
 	*result = entry;
 
 	if (is_dir != NULL)
@@ -113,8 +101,8 @@
 
 void git__rewinddir(git__DIR *d)
 {
-	char filter[4096];
-	wchar_t* filter_w;
+	char filter[GIT_WIN_PATH];
+	wchar_t filter_w[GIT_WIN_PATH];
 
 	if (!d)
 		return;
@@ -125,12 +113,11 @@
 		d->first = 0;
 	}
 
-	if (!init_filter(filter, sizeof(filter), d->dir) ||
-		(filter_w = gitwin_to_utf16(filter)) == NULL)
+	if (!init_filter(filter, sizeof(filter), d->dir))
 		return;
 
+	git__utf8_to_16(filter_w, GIT_WIN_PATH, filter);
 	d->h = FindFirstFileW(filter_w, &d->f);
-	git__free(filter_w);
 
 	if (d->h == INVALID_HANDLE_VALUE)
 		giterr_set(GITERR_OS, "Could not open directory '%s'", d->dir);
diff --git a/libgit2/src/win32/dir.h b/libgit2/src/win32/dir.h
--- a/libgit2/src/win32/dir.h
+++ b/libgit2/src/win32/dir.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/win32/error.c b/libgit2/src/win32/error.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/error.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "error.h"
+
+#ifdef GIT_WINHTTP
+# include <winhttp.h>
+#endif
+
+#define WC_ERR_INVALID_CHARS	0x80
+
+char *git_win32_get_error_message(DWORD error_code)
+{
+	LPWSTR lpMsgBuf = NULL;
+	HMODULE hModule = NULL;
+	char *utf8_msg = NULL;
+	int utf8_size;
+	DWORD dwFlags =
+		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
+
+	if (!error_code)
+		return NULL;
+
+#ifdef GIT_WINHTTP
+	/* Errors raised by WinHTTP are not in the system resource table */
+	if (error_code >= WINHTTP_ERROR_BASE &&
+		error_code <= WINHTTP_ERROR_LAST)
+		hModule = GetModuleHandleW(L"winhttp");
+#endif
+
+	GIT_UNUSED(hModule);
+
+	if (hModule)
+		dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+	else
+		dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
+
+	if (FormatMessageW(dwFlags, hModule, error_code,
+		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+		(LPWSTR)&lpMsgBuf, 0, NULL)) {
+
+		/* Invalid code point check supported on Vista+ only */
+		if (git_has_win32_version(6, 0))
+			dwFlags = WC_ERR_INVALID_CHARS;
+		else
+			dwFlags = 0;
+
+		utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags,
+			lpMsgBuf, -1, NULL, 0, NULL, NULL);
+
+		if (!utf8_size) {
+			assert(0);
+			goto on_error;
+		}
+
+		utf8_msg = git__malloc(utf8_size);
+
+		if (!utf8_msg)
+			goto on_error;
+
+		if (!WideCharToMultiByte(CP_UTF8, dwFlags,
+			lpMsgBuf, -1, utf8_msg, utf8_size, NULL, NULL)) {
+			git__free(utf8_msg);
+			goto on_error;
+		}
+
+on_error:
+		LocalFree(lpMsgBuf);
+	}
+
+	return utf8_msg;
+}
diff --git a/libgit2/src/win32/error.h b/libgit2/src/win32/error.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/error.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_git_win32_error_h__
+#define INCLUDE_git_win32_error_h__
+
+extern char *git_win32_get_error_message(DWORD error_code);
+
+#endif
diff --git a/libgit2/src/win32/findfile.c b/libgit2/src/win32/findfile.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/findfile.c
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "utf-conv.h"
+#include "path.h"
+#include "findfile.h"
+
+#define REG_MSYSGIT_INSTALL_LOCAL L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+
+#ifndef _WIN64
+#define REG_MSYSGIT_INSTALL REG_MSYSGIT_INSTALL_LOCAL
+#else
+#define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+#endif
+
+int git_win32__expand_path(struct git_win32__path *s_root, const wchar_t *templ)
+{
+	s_root->len = ExpandEnvironmentStringsW(templ, s_root->path, MAX_PATH);
+	return s_root->len ? 0 : -1;
+}
+
+static int win32_path_utf16_to_8(git_buf *path_utf8, const wchar_t *path_utf16)
+{
+	char temp_utf8[GIT_PATH_MAX];
+
+	git__utf16_to_8(temp_utf8, path_utf16);
+	git_path_mkposix(temp_utf8);
+
+	return git_buf_sets(path_utf8, temp_utf8);
+}
+
+int git_win32__find_file(
+	git_buf *path, const struct git_win32__path *root, const char *filename)
+{
+	size_t len, alloc_len;
+	wchar_t *file_utf16 = NULL;
+
+	if (!root || !filename || (len = strlen(filename)) == 0)
+		return GIT_ENOTFOUND;
+
+	/* allocate space for wchar_t path to file */
+	alloc_len = root->len + len + 2;
+	file_utf16 = git__calloc(alloc_len, sizeof(wchar_t));
+	GITERR_CHECK_ALLOC(file_utf16);
+
+	/* append root + '\\' + filename as wchar_t */
+	memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
+
+	if (*filename == '/' || *filename == '\\')
+		filename++;
+
+	git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename);
+
+	/* check access */
+	if (_waccess(file_utf16, F_OK) < 0) {
+		git__free(file_utf16);
+		return GIT_ENOTFOUND;
+	}
+
+	win32_path_utf16_to_8(path, file_utf16);
+	git__free(file_utf16);
+
+	return 0;
+}
+
+static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen)
+{
+	wchar_t term, *base = path;
+
+	assert(path && buf && buflen);
+
+	term = (*path == L'"') ? *path++ : L';';
+
+	for (buflen--; *path && *path != term && buflen; buflen--)
+		*buf++ = *path++;
+
+	*buf = L'\0'; /* reserved a byte via initial subtract */
+
+	while (*path == term || *path == L';')
+		path++;
+
+	return (path != base) ? path : NULL;
+}
+
+static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe)
+{
+	wchar_t *env = _wgetenv(L"PATH"), lastch;
+	struct git_win32__path root;
+	size_t gitexe_len = wcslen(gitexe);
+
+	if (!env)
+		return -1;
+
+	while ((env = win32_walkpath(env, root.path, MAX_PATH-1)) && *root.path) {
+		root.len = (DWORD)wcslen(root.path);
+		lastch = root.path[root.len - 1];
+
+		/* ensure trailing slash (MAX_PATH-1 to walkpath guarantees space) */
+		if (lastch != L'/' && lastch != L'\\') {
+			root.path[root.len++] = L'\\';
+			root.path[root.len]   = L'\0';
+		}
+
+		if (root.len + gitexe_len >= MAX_PATH)
+			continue;
+		wcscpy(&root.path[root.len], gitexe);
+
+		if (_waccess(root.path, F_OK) == 0 && root.len > 5) {
+			/* replace "bin\\" or "cmd\\" with "etc\\" */
+			wcscpy(&root.path[root.len - 4], L"etc\\");
+
+			win32_path_utf16_to_8(buf, root.path);
+			return 0;
+		}
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static int win32_find_git_in_registry(
+	git_buf *buf, const HKEY hieve, const wchar_t *key)
+{
+	HKEY hKey;
+	DWORD dwType = REG_SZ;
+	struct git_win32__path path16;
+
+	assert(buf);
+
+	path16.len = 0;
+
+	if (RegOpenKeyExW(hieve, key, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS) {
+		if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType,
+			(LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS)
+		{
+			/* InstallLocation points to the root of the git directory */
+
+			if (path16.len + 4 > MAX_PATH) { /* 4 = wcslen(L"etc\\") */
+				giterr_set(GITERR_OS, "Cannot locate git - path too long");
+				return -1;
+			}
+
+			wcscat(path16.path, L"etc\\");
+			path16.len += 4;
+
+			win32_path_utf16_to_8(buf, path16.path);
+		}
+
+		RegCloseKey(hKey);
+	}
+
+	return path16.len ? 0 : GIT_ENOTFOUND;
+}
+
+static int win32_find_existing_dirs(
+	git_buf *out, const wchar_t *tmpl[], char *temp[])
+{
+	struct git_win32__path path16;
+	git_buf buf = GIT_BUF_INIT;
+
+	git_buf_clear(out);
+
+	for (; *tmpl != NULL; tmpl++) {
+		if (!git_win32__expand_path(&path16, *tmpl) &&
+			path16.path[0] != L'%' &&
+			!_waccess(path16.path, F_OK))
+		{
+			win32_path_utf16_to_8(&buf, path16.path);
+
+			if (buf.size)
+				git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+		}
+	}
+
+	git_buf_free(&buf);
+
+	return (git_buf_oom(out) ? -1 : 0);
+}
+
+int git_win32__find_system_dirs(git_buf *out)
+{
+	git_buf buf = GIT_BUF_INIT;
+
+	/* directories where git.exe & git.cmd are found */
+	if (!win32_find_git_in_path(&buf, L"git.exe") && buf.size)
+		git_buf_set(out, buf.ptr, buf.size);
+	else
+		git_buf_clear(out);
+
+	if (!win32_find_git_in_path(&buf, L"git.cmd") && buf.size)
+		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+
+	/* directories where git is installed according to registry */
+	if (!win32_find_git_in_registry(
+			&buf, HKEY_CURRENT_USER, REG_MSYSGIT_INSTALL_LOCAL) && buf.size)
+		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+
+	if (!win32_find_git_in_registry(
+			&buf, HKEY_LOCAL_MACHINE, REG_MSYSGIT_INSTALL) && buf.size)
+		git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+
+	git_buf_free(&buf);
+
+	return (git_buf_oom(out) ? -1 : 0);
+}
+
+int git_win32__find_global_dirs(git_buf *out)
+{
+	char *temp[3];
+	static const wchar_t *global_tmpls[4] = {
+		L"%HOME%\\",
+		L"%HOMEDRIVE%%HOMEPATH%\\",
+		L"%USERPROFILE%\\",
+		NULL,
+	};
+
+	return win32_find_existing_dirs(out, global_tmpls, temp);
+}
+
+int git_win32__find_xdg_dirs(git_buf *out)
+{
+	char *temp[6];
+	static const wchar_t *global_tmpls[7] = {
+		L"%XDG_CONFIG_HOME%\\git",
+		L"%APPDATA%\\git",
+		L"%LOCALAPPDATA%\\git",
+		L"%HOME%\\.config\\git",
+		L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
+		L"%USERPROFILE%\\.config\\git",
+		NULL,
+	};
+
+	return win32_find_existing_dirs(out, global_tmpls, temp);
+}
+
diff --git a/libgit2/src/win32/findfile.h b/libgit2/src/win32/findfile.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/findfile.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_git_findfile_h__
+#define INCLUDE_git_findfile_h__
+
+struct git_win32__path {
+	wchar_t path[MAX_PATH];
+	DWORD len;
+};
+
+extern int git_win32__expand_path(
+	struct git_win32__path *s_root, const wchar_t *templ);
+
+extern int git_win32__find_file(
+	git_buf *path, const struct git_win32__path *root, const char *filename);
+
+extern int git_win32__find_system_dirs(git_buf *out);
+extern int git_win32__find_global_dirs(git_buf *out);
+extern int git_win32__find_xdg_dirs(git_buf *out);
+
+#endif
+
diff --git a/libgit2/src/win32/map.c b/libgit2/src/win32/map.c
--- a/libgit2/src/win32/map.c
+++ b/libgit2/src/win32/map.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/win32/mingw-compat.h b/libgit2/src/win32/mingw-compat.h
--- a/libgit2/src/win32/mingw-compat.h
+++ b/libgit2/src/win32/mingw-compat.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
diff --git a/libgit2/src/win32/msvc-compat.h b/libgit2/src/win32/msvc-compat.h
--- a/libgit2/src/win32/msvc-compat.h
+++ b/libgit2/src/win32/msvc-compat.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -21,6 +21,7 @@
 /* stat: file mode type testing macros */
 #	define _S_IFLNK 0120000
 #	define S_IFLNK _S_IFLNK
+#	define S_IXUSR 00100
 
 #	define S_ISDIR(m)	(((m) & _S_IFMT) == _S_IFDIR)
 #	define S_ISREG(m)	(((m) & _S_IFMT) == _S_IFREG)
@@ -36,6 +37,15 @@
 /* MSVC doesn't define ssize_t at all */
 typedef SSIZE_T ssize_t;
 
+/* define snprintf using variadic macro support if available */
+#if _MSC_VER >= 1400
+# define snprintf(BUF, SZ, FMT, ...) _snprintf_s(BUF, SZ, _TRUNCATE, FMT, __VA_ARGS__)
+#else
+# define snprintf _snprintf
 #endif
+
+#endif
+
+#define GIT_STDLIB_CALL __cdecl
 
 #endif /* INCLUDE_msvc_compat__ */
diff --git a/libgit2/src/win32/posix.h b/libgit2/src/win32/posix.h
--- a/libgit2/src/win32/posix.h
+++ b/libgit2/src/win32/posix.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -8,7 +8,6 @@
 #define INCLUDE_posix__w32_h__
 
 #include "common.h"
-#include "compat/fnmatch.h"
 #include "utf-conv.h"
 
 GIT_INLINE(int) p_link(const char *old, const char *new)
@@ -21,18 +20,16 @@
 
 GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
 {
-	wchar_t* buf = gitwin_to_utf16(path);
-	int ret = _wmkdir(buf);
-
+	wchar_t buf[GIT_WIN_PATH];
 	GIT_UNUSED(mode);
-
-	git__free(buf);
-	return ret;
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _wmkdir(buf);
 }
 
 extern int p_unlink(const char *path);
 extern int p_lstat(const char *file_name, struct stat *buf);
 extern int p_readlink(const char *link, char *target, size_t target_len);
+extern int p_symlink(const char *old, const char *new);
 extern int p_hide_directory__w32(const char *path);
 extern char *p_realpath(const char *orig_path, char *buffer);
 extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
@@ -51,5 +48,14 @@
 extern int p_rename(const char *from, const char *to);
 extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
 extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
+extern int p_inet_pton(int af, const char* src, void* dst);
+
+/* p_lstat is almost but not quite POSIX correct.  Specifically, the use of
+ * ENOTDIR is wrong, in that it does not mean precisely that a non-directory
+ * entry was encountered.  Making it correct is potentially expensive,
+ * however, so this is a separate version of p_lstat to use when correct
+ * POSIX ENOTDIR semantics is required.
+ */
+extern int p_lstat_posixly(const char *filename, struct stat *buf);
 
 #endif
diff --git a/libgit2/src/win32/posix_w32.c b/libgit2/src/win32/posix_w32.c
--- a/libgit2/src/win32/posix_w32.c
+++ b/libgit2/src/win32/posix_w32.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,23 +7,18 @@
 #include "../posix.h"
 #include "path.h"
 #include "utf-conv.h"
+#include "repository.h"
 #include <errno.h>
 #include <io.h>
 #include <fcntl.h>
-
+#include <ws2tcpip.h>
 
 int p_unlink(const char *path)
 {
-	int ret = 0;
-	wchar_t* buf;
-
-	if ((buf = gitwin_to_utf16(path)) != NULL) {
-		_wchmod(buf, 0666);
-		ret = _wunlink(buf);
-		git__free(buf);
-	}
-
-	return ret;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	_wchmod(buf, 0666);
+	return _wunlink(buf);
 }
 
 int p_fsync(int fd)
@@ -57,16 +52,32 @@
 	return (time_t)winTime;
 }
 
-static int do_lstat(const char *file_name, struct stat *buf)
+#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
+
+static int do_lstat(
+	const char *file_name, struct stat *buf, int posix_enotdir)
 {
 	WIN32_FILE_ATTRIBUTE_DATA fdata;
-	wchar_t* fbuf = gitwin_to_utf16(file_name);
-	if (!fbuf)
-		return -1;
+	wchar_t fbuf[GIT_WIN_PATH], lastch;
+	int flen;
 
+	flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name);
+
+	/* truncate trailing slashes */
+	for (; flen > 0; --flen) {
+		lastch = fbuf[flen - 1];
+		if (WIN32_IS_WSEP(lastch))
+			fbuf[flen - 1] = L'\0';
+		else if (lastch != L'\0')
+			break;
+	}
+
 	if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
 		int fMode = S_IREAD;
 
+		if (!buf)
+			return 0;
+
 		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 			fMode |= S_IFDIR;
 		else
@@ -89,53 +100,68 @@
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
 
-		git__free(fbuf);
-		return 0;
-	}
+		/* Windows symlinks have zero file size, call readlink to determine
+		 * the length of the path pointed to, which we expect everywhere else
+		 */
+		if (S_ISLNK(fMode)) {
+			char target[GIT_WIN_PATH];
+			int readlink_result;
 
-	git__free(fbuf);
-	return -1;
-}
+			readlink_result = p_readlink(file_name, target, GIT_WIN_PATH);
 
-int p_lstat(const char *file_name, struct stat *buf)
-{
-	int error;
-	size_t namelen;
-	char *alt_name;
+			if (readlink_result == -1)
+				return -1;
 
-	if (do_lstat(file_name, buf) == 0)
+			buf->st_size = strlen(target);
+		}
+
 		return 0;
+	}
 
-	/* if file_name ended in a '/', Windows returned ENOENT;
-	 * try again without trailing slashes
+	errno = ENOENT;
+
+	/* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
+	 * file path is a regular file,otherwise ENOENT must be set.
 	 */
-	namelen = strlen(file_name);
-	if (namelen && file_name[namelen-1] != '/')
-		return -1;
+	if (posix_enotdir) {
+		/* scan up path until we find an existing item */
+		while (1) {
+			/* remove last directory component */
+			for (--flen; flen > 0 && !WIN32_IS_WSEP(fbuf[flen]); --flen);
 
-	while (namelen && file_name[namelen-1] == '/')
-		--namelen;
+			if (flen <= 0)
+				break;
 
-	if (!namelen)
-		return -1;
+			fbuf[flen] = L'\0';
 
-	alt_name = git__strndup(file_name, namelen);
-	if (!alt_name)
-		return -1;
+			if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
+				if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+					errno = ENOTDIR;
+				break;
+			}
+		}
+	}
 
-	error = do_lstat(alt_name, buf);
+	return -1;
+}
 
-	git__free(alt_name);
-	return error;
+int p_lstat(const char *filename, struct stat *buf)
+{
+	return do_lstat(filename, buf, 0);
 }
 
+int p_lstat_posixly(const char *filename, struct stat *buf)
+{
+	return do_lstat(filename, buf, 1);
+}
+
 int p_readlink(const char *link, char *target, size_t target_len)
 {
 	typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD);
 	static fpath_func pGetFinalPath = NULL;
 	HANDLE hFile;
 	DWORD dwRet;
-	wchar_t* link_w;
+	wchar_t link_w[GIT_WIN_PATH];
 	wchar_t* target_w;
 	int error = 0;
 
@@ -146,10 +172,10 @@
 	 * it is not available in platforms older than Vista
 	 */
 	if (pGetFinalPath == NULL) {
-		HINSTANCE library = LoadLibrary("kernel32");
+		HMODULE module = GetModuleHandle("kernel32");
 
-		if (library != NULL)
-			pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
+		if (module != NULL)
+			pGetFinalPath = (fpath_func)GetProcAddress(module, "GetFinalPathNameByHandleW");
 
 		if (pGetFinalPath == NULL) {
 			giterr_set(GITERR_OS,
@@ -158,8 +184,7 @@
 		}
 	}
 
-	link_w = gitwin_to_utf16(link);
-	GITERR_CHECK_ALLOC(link_w);
+	git__utf8_to_16(link_w, GIT_WIN_PATH, link);
 
 	hFile = CreateFileW(link_w,			// file to open
 			GENERIC_READ,			// open for reading
@@ -169,8 +194,6 @@
 			FILE_FLAG_BACKUP_SEMANTICS, // normal file
 			NULL);					// no attr. template
 
-	git__free(link_w);
-
 	if (hFile == INVALID_HANDLE_VALUE) {
 		giterr_set(GITERR_OS, "Cannot open '%s' for reading", link);
 		return -1;
@@ -217,46 +240,43 @@
 	return dwRet;
 }
 
+int p_symlink(const char *old, const char *new)
+{
+	/* Real symlinks on NTFS require admin privileges. Until this changes,
+	 * libgit2 just creates a text file with the link target in the contents.
+	 */
+	return git_futils_fake_symlink(old, new);
+}
+
 int p_open(const char *path, int flags, ...)
 {
-	int fd;
-	wchar_t* buf;
+	wchar_t buf[GIT_WIN_PATH];
 	mode_t mode = 0;
 
-	buf = gitwin_to_utf16(path);
-	if (!buf)
-		return -1;
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
 
-	if (flags & O_CREAT)
-	{
+	if (flags & O_CREAT) {
 		va_list arg_list;
 
 		va_start(arg_list, flags);
-		mode = va_arg(arg_list, mode_t);
+		mode = (mode_t)va_arg(arg_list, int);
 		va_end(arg_list);
 	}
 
-	fd = _wopen(buf, flags | _O_BINARY, mode);
-
-	git__free(buf);
-	return fd;
+	return _wopen(buf, flags | _O_BINARY, mode);
 }
 
 int p_creat(const char *path, mode_t mode)
 {
-	int fd;
-	wchar_t* buf = gitwin_to_utf16(path);
-	if (!buf)
-		return -1;
-	fd = _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
-	git__free(buf);
-	return fd;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode);
 }
 
 int p_getcwd(char *buffer_out, size_t size)
 {
 	int ret;
-	wchar_t* buf;
+	wchar_t *buf;
 
 	if ((size_t)((int)size) != size)
 		return -1;
@@ -275,104 +295,78 @@
 
 int p_stat(const char* path, struct stat* buf)
 {
-	return do_lstat(path, buf);
+	return do_lstat(path, buf, 0);
 }
 
 int p_chdir(const char* path)
 {
-	wchar_t* buf = gitwin_to_utf16(path);
-	int ret;
-	if (!buf)
-		return -1;
-	ret = _wchdir(buf);
-	git__free(buf);
-	return ret;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _wchdir(buf);
 }
 
 int p_chmod(const char* path, mode_t mode)
 {
-	wchar_t* buf = gitwin_to_utf16(path);
-	int ret;
-	if (!buf)
-		return -1;
-	ret = _wchmod(buf, mode);
-	git__free(buf);
-	return ret;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _wchmod(buf, mode);
 }
 
 int p_rmdir(const char* path)
 {
-	wchar_t* buf = gitwin_to_utf16(path);
-	int ret;
-	if (!buf)
-		return -1;
-	ret = _wrmdir(buf);
-	git__free(buf);
-	return ret;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _wrmdir(buf);
 }
 
 int p_hide_directory__w32(const char *path)
 {
-	int res;
-	wchar_t* buf = gitwin_to_utf16(path);
-	if (!buf)
-		return -1;
-
-	res = SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN);
-	git__free(buf);
-
-	return (res != 0) ? 0 : -1; /* MSDN states a "non zero" value indicates a success */
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1;
 }
 
 char *p_realpath(const char *orig_path, char *buffer)
 {
-	int ret, buffer_sz = 0;
-	wchar_t* orig_path_w = gitwin_to_utf16(orig_path);
-	wchar_t* buffer_w = (wchar_t*)git__malloc(GIT_PATH_MAX * sizeof(wchar_t));
+	int ret;
+	wchar_t orig_path_w[GIT_WIN_PATH];
+	wchar_t buffer_w[GIT_WIN_PATH];
 
-	if (!orig_path_w || !buffer_w)
-		return NULL;
+	git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path);
 
-	ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
-	git__free(orig_path_w);
+	/* Implicitly use GetCurrentDirectory which can be a threading issue */
+	ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL);
 
 	/* According to MSDN, a return value equals to zero means a failure. */
-	if (ret == 0 || ret > GIT_PATH_MAX) {
+	if (ret == 0 || ret > GIT_WIN_PATH)
 		buffer = NULL;
-		goto done;
+
+	else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
+		buffer = NULL;
+		errno = ENOENT;
 	}
 
-	if (buffer == NULL) {
-		buffer_sz = WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
+	else if (buffer == NULL) {
+		int buffer_sz = WideCharToMultiByte(
+			CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL);
 
 		if (!buffer_sz ||
 			!(buffer = (char *)git__malloc(buffer_sz)) ||
-			!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))
+			!WideCharToMultiByte(
+				CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL))
 		{
 			git__free(buffer);
 			buffer = NULL;
-			goto done;
 		}
-	} else {
-		if (!WideCharToMultiByte(CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) {
-			buffer = NULL;
-			goto done;
-		}
 	}
 
-	if (!git_path_exists(buffer))
-	{
-		if (buffer_sz > 0)
-			git__free(buffer);
-
+	else if (!WideCharToMultiByte(
+		CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL))
 		buffer = NULL;
-		errno = ENOENT;
-	}
 
-done:
-	git__free(buffer_w);
 	if (buffer)
 		git_path_mkposix(buffer);
+
 	return buffer;
 }
 
@@ -381,7 +375,8 @@
 #ifdef _MSC_VER
 	int len;
 
-	if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0)
+	if (count == 0 ||
+		(len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr)) < 0)
 		return _vscprintf(format, argptr);
 
 	return len;
@@ -414,7 +409,7 @@
 		return -1;
 #endif
 
-	return p_creat(tmp_path, 0744);
+	return p_creat(tmp_path, 0744); //-V536
 }
 
 int p_setenv(const char* name, const char* value, int overwrite)
@@ -427,32 +422,19 @@
 
 int p_access(const char* path, mode_t mode)
 {
-	wchar_t *buf = gitwin_to_utf16(path);
-	int ret;
-	if (!buf)
-		return -1;
-
-	ret = _waccess(buf, mode);
-	git__free(buf);
-
-	return ret;
+	wchar_t buf[GIT_WIN_PATH];
+	git__utf8_to_16(buf, GIT_WIN_PATH, path);
+	return _waccess(buf, mode);
 }
 
 int p_rename(const char *from, const char *to)
 {
-	wchar_t *wfrom = gitwin_to_utf16(from);
-	wchar_t *wto = gitwin_to_utf16(to);
-	int ret;
-
-	if (!wfrom || !wto)
-		return -1;
-
-	ret = MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
-
-	git__free(wfrom);
-	git__free(wto);
+	wchar_t wfrom[GIT_WIN_PATH];
+	wchar_t wto[GIT_WIN_PATH];
 
-	return ret;
+	git__utf8_to_16(wfrom, GIT_WIN_PATH, from);
+	git__utf8_to_16(wto, GIT_WIN_PATH, to);
+	return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1;
 }
 
 int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
@@ -469,4 +451,125 @@
 		return -1; /* giterr_set will be done by caller */
 
 	return send(socket, buffer, (int)length, flags);
+}
+
+/**
+ * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
+ * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
+ */
+struct tm * 
+p_localtime_r (const time_t *timer, struct tm *result) 
+{ 
+   struct tm *local_result; 
+   local_result = localtime (timer); 
+
+   if (local_result == NULL || result == NULL) 
+      return NULL; 
+
+   memcpy (result, local_result, sizeof (struct tm)); 
+   return result; 
+} 
+struct tm * 
+p_gmtime_r (const time_t *timer, struct tm *result) 
+{ 
+   struct tm *local_result; 
+   local_result = gmtime (timer); 
+
+   if (local_result == NULL || result == NULL) 
+      return NULL; 
+
+   memcpy (result, local_result, sizeof (struct tm)); 
+   return result; 
+}
+
+#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
+#define DELTA_EPOCH_IN_MICROSECS  11644473600000000Ui64
+#else
+#define DELTA_EPOCH_IN_MICROSECS  11644473600000000ULL
+#endif
+ 
+#ifndef _TIMEZONE_DEFINED
+#define _TIMEZONE_DEFINED
+struct timezone 
+{
+   int  tz_minuteswest; /* minutes W of Greenwich */
+   int  tz_dsttime;     /* type of dst correction */
+};
+#endif
+ 
+int p_gettimeofday(struct timeval *tv, struct timezone *tz)
+{
+   FILETIME ft;
+   unsigned __int64 tmpres = 0;
+   static int tzflag;
+ 
+   if (NULL != tv)
+      {
+         GetSystemTimeAsFileTime(&ft);
+ 
+         tmpres |= ft.dwHighDateTime;
+         tmpres <<= 32;
+         tmpres |= ft.dwLowDateTime;
+ 
+         /*converting file time to unix epoch*/
+         tmpres /= 10;  /*convert into microseconds*/
+         tmpres -= DELTA_EPOCH_IN_MICROSECS; 
+         tv->tv_sec = (long)(tmpres / 1000000UL);
+         tv->tv_usec = (long)(tmpres % 1000000UL);
+      }
+ 
+   if (NULL != tz)
+      {
+         if (!tzflag)
+            {
+               _tzset();
+               tzflag++;
+            }
+         tz->tz_minuteswest = _timezone / 60;
+         tz->tz_dsttime = _daylight;
+      }
+ 
+   return 0;
+}
+
+int p_inet_pton(int af, const char* src, void* dst)
+{
+	union {
+		struct sockaddr_in6 sin6;
+		struct sockaddr_in sin;
+	} sa;
+	int srcsize;
+
+	switch(af)
+	{
+		case AF_INET:
+			sa.sin.sin_family = AF_INET;
+			srcsize = (int)sizeof(sa.sin);
+		break;
+		case AF_INET6:
+			sa.sin6.sin6_family = AF_INET6;
+			srcsize = (int)sizeof(sa.sin6);
+		break;
+		default:
+			errno = WSAEPFNOSUPPORT;
+			return -1;
+	}
+
+	if (WSAStringToAddress((LPSTR)src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0)
+	{
+		errno = WSAGetLastError();
+		return -1;
+	}
+
+	switch(af)
+	{
+		case AF_INET:
+			memcpy(dst, &sa.sin.sin_addr, sizeof(sa.sin.sin_addr));
+		break;
+		case AF_INET6:
+			memcpy(dst, &sa.sin6.sin6_addr, sizeof(sa.sin6.sin6_addr));
+		break;
+	}
+
+	return 1;
 }
diff --git a/libgit2/src/win32/precompiled.c b/libgit2/src/win32/precompiled.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/precompiled.c
@@ -0,0 +1,1 @@
+#include "precompiled.h"
diff --git a/libgit2/src/win32/precompiled.h b/libgit2/src/win32/precompiled.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/precompiled.h
@@ -0,0 +1,19 @@
+#include "git2.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <regex.h>
+
+#include <io.h>
+#include <direct.h>
+#ifdef GIT_THREADS
+ #include "win32/pthread.h"
+#endif
diff --git a/libgit2/src/win32/pthread.c b/libgit2/src/win32/pthread.c
--- a/libgit2/src/win32/pthread.c
+++ b/libgit2/src/win32/pthread.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -53,6 +53,74 @@
 	LeaveCriticalSection(mutex);
 	return 0;
 }
+
+int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
+{
+	/* We don't support non-default attributes. */
+	if (attr)
+		return EINVAL;
+
+	/* This is an auto-reset event. */
+	*cond = CreateEventW(NULL, FALSE, FALSE, NULL);
+	assert(*cond);
+
+	/* If we can't create the event, claim that the reason was out-of-memory.
+	 * The actual reason can be fetched with GetLastError(). */
+	return *cond ? 0 : ENOMEM;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+	BOOL closed;
+
+	if (!cond)
+		return EINVAL;
+
+	closed = CloseHandle(*cond);
+	assert(closed);
+	GIT_UNUSED(closed);
+
+	*cond = NULL;
+	return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
+{
+	int error;
+	DWORD wait_result;
+
+	if (!cond || !mutex)
+		return EINVAL;
+
+	/* The caller must be holding the mutex. */
+	error = pthread_mutex_unlock(mutex);
+
+	if (error)
+		return error;
+
+	wait_result = WaitForSingleObject(*cond, INFINITE);
+	assert(WAIT_OBJECT_0 == wait_result);
+	GIT_UNUSED(wait_result);
+
+	return pthread_mutex_lock(mutex);
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+	BOOL signaled;
+
+	if (!cond)
+		return EINVAL;
+
+	signaled = SetEvent(*cond);
+	assert(signaled);
+	GIT_UNUSED(signaled);
+
+	return 0;
+}
+
+/* pthread_cond_broadcast is not implemented because doing so with just Win32 events
+ * is quite complicated, and no caller in libgit2 uses it yet. */
 
 int pthread_num_processors_np(void)
 {
diff --git a/libgit2/src/win32/pthread.h b/libgit2/src/win32/pthread.h
--- a/libgit2/src/win32/pthread.h
+++ b/libgit2/src/win32/pthread.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -21,6 +21,7 @@
 typedef int pthread_attr_t;
 typedef CRITICAL_SECTION pthread_mutex_t;
 typedef HANDLE pthread_t;
+typedef HANDLE pthread_cond_t;
 
 #define PTHREAD_MUTEX_INITIALIZER {(void*)-1};
 
@@ -34,6 +35,12 @@
 int pthread_mutex_destroy(pthread_mutex_t *);
 int pthread_mutex_lock(pthread_mutex_t *);
 int pthread_mutex_unlock(pthread_mutex_t *);
+
+int pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *);
+int pthread_cond_destroy(pthread_cond_t *);
+int pthread_cond_wait(pthread_cond_t *, pthread_mutex_t *);
+int pthread_cond_signal(pthread_cond_t *);
+/* pthread_cond_broadcast is not supported on Win32 yet. */
 
 int pthread_num_processors_np(void);
 
diff --git a/libgit2/src/win32/utf-conv.c b/libgit2/src/win32/utf-conv.c
--- a/libgit2/src/win32/utf-conv.c
+++ b/libgit2/src/win32/utf-conv.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -7,89 +7,75 @@
 
 #include "common.h"
 #include "utf-conv.h"
-#include "git2/windows.h"
 
-/*
- * Default codepage value
- */
-static int _active_codepage = CP_UTF8;
-
-void gitwin_set_codepage(unsigned int codepage)
-{
-	_active_codepage = codepage;
-}
-
-unsigned int gitwin_get_codepage(void)
-{
-	return _active_codepage;
-}
-
-void gitwin_set_utf8(void)
-{
-	_active_codepage = CP_UTF8;
-}
+#define U16_LEAD(c) (wchar_t)(((c)>>10)+0xd7c0)
+#define U16_TRAIL(c) (wchar_t)(((c)&0x3ff)|0xdc00)
 
-wchar_t* gitwin_to_utf16(const char* str)
+#if 0
+void git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
 {
-	wchar_t* ret;
-	size_t cb;
+	wchar_t *pDest = dest;
+	uint32_t ch;
+	const uint8_t* pSrc = (uint8_t*) src;
 
-	if (!str)
-		return NULL;
+	assert(dest && src && length);
 
-	cb = strlen(str) * sizeof(wchar_t);
-	if (cb == 0)
-		return (wchar_t *)git__calloc(1, sizeof(wchar_t));
+	length--;
 
-	/* Add space for null terminator */
-	cb += sizeof(wchar_t);
+	while(*pSrc && length > 0) {
+		ch = *pSrc++;
+		length--;
 
-	ret = (wchar_t *)git__malloc(cb);
-	if (!ret)
-		return NULL;
+		if(ch < 0xc0) {
+			/*
+			 * ASCII, or a trail byte in lead position which is treated like
+			 * a single-byte sequence for better character boundary
+			 * resynchronization after illegal sequences.
+			 */
+			*pDest++ = (wchar_t)ch;
+			continue;
+		} else if(ch < 0xe0) { /* U+0080..U+07FF */
+			if (pSrc[0]) {
+				/* 0x3080 = (0xc0 << 6) + 0x80 */
+				*pDest++ = (wchar_t)((ch << 6) + *pSrc++ - 0x3080);
+				continue;
+			}
+		} else if(ch < 0xf0) { /* U+0800..U+FFFF */
+			if (pSrc[0] && pSrc[1]) {
+				/* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */
+				/* 0x2080 = (0x80 << 6) + 0x80 */
+				ch = (ch << 12) + (*pSrc++ << 6);
+				*pDest++ = (wchar_t)(ch + *pSrc++ - 0x2080);
+				continue;
+			}
+		} else /* f0..f4 */ { /* U+10000..U+10FFFF */
+			if (length >= 1 && pSrc[0] && pSrc[1] && pSrc[2]) {
+				/* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */
+				ch = (ch << 18) + (*pSrc++ << 12);
+				ch += *pSrc++ << 6;
+				ch += *pSrc++ - 0x3c82080;
+				*(pDest++) = U16_LEAD(ch);
+				*(pDest++) = U16_TRAIL(ch);
+				length--; /* two bytes for this character */
+				continue;
+			}
+		}
 
-	if (MultiByteToWideChar(_active_codepage, 0, str, -1, ret, (int)cb) == 0) {
-		giterr_set(GITERR_OS, "Could not convert string to UTF-16");
-		git__free(ret);
-		ret = NULL;
+		/* truncated character at the end */
+		*pDest++ = 0xfffd;
+		break;
 	}
 
-	return ret;
+	*pDest++ = 0x0;
 }
+#endif
 
-int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
+int git__utf8_to_16(wchar_t *dest, size_t length, const char *src)
 {
-	int result = MultiByteToWideChar(_active_codepage, 0, str, -1, buffer, (int)len);
-	if (result == 0)
-		giterr_set(GITERR_OS, "Could not convert string to UTF-16");
-	return result;
+	return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length);
 }
 
-char* gitwin_from_utf16(const wchar_t* str)
+int git__utf16_to_8(char *out, const wchar_t *input)
 {
-	char* ret;
-	size_t cb;
-
-	if (!str)
-		return NULL;
-
-	cb = wcslen(str) * sizeof(char);
-	if (cb == 0)
-		return (char *)git__calloc(1, sizeof(char));
-
-	/* Add space for null terminator */
-	cb += sizeof(char);
-
-	ret = (char*)git__malloc(cb);
-	if (!ret)
-		return NULL;
-
-	if (WideCharToMultiByte(_active_codepage, 0, str, -1, ret, (int)cb, NULL, NULL) == 0) {
-		giterr_set(GITERR_OS, "Could not convert string to UTF-8");
-		git__free(ret);
-		ret = NULL;
-	}
-
-	return ret;
-
+	return WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL);
 }
diff --git a/libgit2/src/win32/utf-conv.h b/libgit2/src/win32/utf-conv.h
--- a/libgit2/src/win32/utf-conv.h
+++ b/libgit2/src/win32/utf-conv.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 the libgit2 contributors
+ * Copyright (C) the libgit2 contributors. All rights reserved.
  *
  * This file is part of libgit2, distributed under the GNU GPL v2 with
  * a Linking Exception. For full terms see the included COPYING file.
@@ -10,9 +10,10 @@
 #ifndef INCLUDE_git_utfconv_h__
 #define INCLUDE_git_utfconv_h__
 
-wchar_t* gitwin_to_utf16(const char* str);
-int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len);
-char* gitwin_from_utf16(const wchar_t* str);
+#define GIT_WIN_PATH (260 + 1)
+
+int git__utf8_to_16(wchar_t *dest, size_t length, const char *src);
+int git__utf16_to_8(char *dest, const wchar_t *src);
 
 #endif
 
diff --git a/libgit2/src/win32/version.h b/libgit2/src/win32/version.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/version.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_version_h__
+#define INCLUDE_win32_version_h__
+
+#include <windows.h>
+
+GIT_INLINE(int) git_has_win32_version(int major, int minor)
+{
+	WORD wVersion = LOWORD(GetVersion());
+
+	return LOBYTE(wVersion) > major ||
+		(LOBYTE(wVersion) == major && HIBYTE(wVersion) >= minor);
+}
+
+#endif
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -15,10 +15,12 @@
   _ <- system "git init smoke.git"
 
   putStrLn "Accessing directly..."
+  c'git_threads_init
   alloca $ \ptr -> do
     withCString "smoke.git/.git" $ \str -> do
       r <- c'git_repository_open ptr str
       when (r < 0) $ exitWith (ExitFailure 1)
       peek ptr >>= c'git_repository_free
+  c'git_threads_shutdown
 
 -- Main.hs ends here
