diff --git a/Bindings/Libgit2/Windows.hsc b/Bindings/Libgit2/Windows.hsc
--- a/Bindings/Libgit2/Windows.hsc
+++ b/Bindings/Libgit2/Windows.hsc
@@ -4,6 +4,8 @@
 #strict_import
 
 import Bindings.Libgit2.Common
+#ifdef GIT_WIN32
 #ccall gitwin_set_codepage , CUInt -> IO ()
 #ccall gitwin_get_codepage , IO (CUInt)
 #ccall gitwin_set_utf8 , IO ()
+#endif
diff --git a/hlibgit2.cabal b/hlibgit2.cabal
--- a/hlibgit2.cabal
+++ b/hlibgit2.cabal
@@ -1,45 +1,48 @@
 Name:                hlibgit2
-Version:             0.6.0
+Version:             0.17.0.1
 Synopsis:            Low-level bindings to libgit2.
-Description:         Bindings to libgit2 v0.16.0-471-g5b9fac3
+Description:         Bindings to libgit2 v0.17.0
 License-file:        LICENSE
 License:             MIT
-Author:              Sakari Jokinen, Jacob Stanley, John Wiegley
+Author:              John Wiegley, Sakari Jokinen, Jacob Stanleyyeah,
 Maintainer:          johnw@newartisans.com
 Build-Type:          Simple
 Cabal-Version:       >=1.10
 Category:            FFI
 
 Extra-source-files:
+  libgit2/deps/http-parser/*.h
   libgit2/include/git2.h
   libgit2/include/git2/*.h
+  libgit2/src/*.h
+  libgit2/src/unix/*.h
+  libgit2/src/win32/*.h
+  libgit2/src/xdiff/*.h
 
 Source-repository head
   type: git
   location: git://github.com/jwiegley/hlibgit2.git
 
-Source-repository this
-  type: git
-  location: git://github.com/jwiegley/hlibgit2.git
-  tag: hlibgit2-0.1-17-gd7b22d6
-
 Test-suite smoke
   default-language: Haskell98
   type: exitcode-stdio-1.0
   main-is: Main.hs
-  hs-source-dirs: tests
+  hs-source-dirs: test
   build-depends:
       base >=3
     , hlibgit2
     , process
 
 Library
+  hs-source-dirs: .
   default-language: Haskell98
   default-extensions:
     ForeignFunctionInterface
   build-depends:
       base            >= 3 && < 5
     , bindings-DSL    >= 1.0.11
+    , zlib            >= 0.5.4
+
   exposed-modules:
     Bindings.Libgit2
     Bindings.Libgit2.Attr
@@ -74,6 +77,7 @@
     Bindings.Libgit2.Types
     Bindings.Libgit2.Version
     Bindings.Libgit2.Windows
+
   c-sources:
     Bindings/Libgit2/Attr.hsc.helper.c
     Bindings/Libgit2/Blob.hsc.helper.c
@@ -81,8 +85,96 @@
     Bindings/Libgit2/Common.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/buffer.c
+    libgit2/src/cache.c
+    libgit2/src/commit.c
+    libgit2/src/config.c
+    libgit2/src/config_cache.c
+    libgit2/src/config_file.c
+    libgit2/src/crlf.c
+    libgit2/src/delta-apply.c
+    libgit2/src/diff.c
+    libgit2/src/diff_output.c
+    libgit2/src/errors.c
+    libgit2/src/fetch.c
+    libgit2/src/filebuf.c
+    libgit2/src/fileops.c
+    libgit2/src/filter.c
+    libgit2/src/global.c
+    libgit2/src/hash.c
+    libgit2/src/ignore.c
+    libgit2/src/index.c
+    libgit2/src/indexer.c
+    libgit2/src/iterator.c
+    libgit2/src/message.c
+    libgit2/src/mwindow.c
+    libgit2/src/netops.c
+    libgit2/src/notes.c
+    libgit2/src/object.c
+    libgit2/src/odb.c
+    libgit2/src/odb_loose.c
+    libgit2/src/odb_pack.c
+    libgit2/src/oid.c
+    libgit2/src/pack.c
+    libgit2/src/path.c
+    libgit2/src/pkt.c
+    libgit2/src/pool.c
+    libgit2/src/posix.c
+    libgit2/src/pqueue.c
+    libgit2/src/protocol.c
+    libgit2/src/reflog.c
+    libgit2/src/refs.c
+    libgit2/src/refspec.c
+    libgit2/src/remote.c
+    libgit2/src/repository.c
+    libgit2/src/revwalk.c
+    libgit2/src/sha1.c
+    libgit2/src/sha1_lookup.c
+    libgit2/src/signature.c
+    libgit2/src/status.c
+    libgit2/src/submodule.c
+    libgit2/src/tag.c
+    libgit2/src/thread-utils.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/git.c
+    libgit2/src/transports/http.c
+    libgit2/src/transports/local.c
+    libgit2/src/xdiff/xdiffi.c
+    libgit2/src/xdiff/xemit.c
+    libgit2/src/xdiff/xhistogram.c
+    libgit2/src/xdiff/xmerge.c
+    libgit2/src/xdiff/xpatience.c
+    libgit2/src/xdiff/xprepare.c
+    libgit2/src/xdiff/xutils.c
+    libgit2/deps/http-parser/http_parser.c
 
-  include-dirs: libgit2/include
-  extra-libraries: git2
+  include-dirs:
+    libgit2/include
+    libgit2/src
+    libgit2/deps/http-parser
+
+  cc-options: -DGIT_THREADS -D_FILE_OFFSET_BITS=64
   if os(windows)
-    cc-options: -DGIT_WIN32 -DNO_VIZ
+    cc-options: -DGIT_WIN32 -DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501
+    c-sources:
+      libgit2/src/win32/dir.c
+      libgit2/src/win32/pthread.c
+      libgit2/src/win32/utf-conv.c
+      libgit2/src/win32/map.c
+      libgit2/src/win32/posix_w32.c
+    include-dirs:
+      libgit2/src/win32
+  else
+    c-sources:
+      libgit2/src/unix/map.c
+    include-dirs:
+      libgit2/src/unix
diff --git a/libgit2/deps/http-parser/http_parser.c b/libgit2/deps/http-parser/http_parser.c
new file mode 100644
--- /dev/null
+++ b/libgit2/deps/http-parser/http_parser.c
@@ -0,0 +1,1778 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#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;
+}
diff --git a/libgit2/deps/http-parser/http_parser.h b/libgit2/deps/http-parser/http_parser.h
new file mode 100644
--- /dev/null
+++ b/libgit2/deps/http-parser/http_parser.h
@@ -0,0 +1,267 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define HTTP_PARSER_VERSION_MAJOR 1
+#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"
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# 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
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * 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"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Request Methods */
+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
+  };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+  { F_CHUNKED               = 1 << 0
+  , F_CONNECTION_KEEP_ALIVE = 1 << 1
+  , F_CONNECTION_CLOSE      = 1 << 2
+  , F_TRAILING              = 1 << 3
+  , F_UPGRADE               = 1 << 4
+  , F_SKIPBODY              = 1 << 5
+  };
+
+
+/* Map for errno-related constants
+ * 
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX)                                           \
+  /* No error */                                                     \
+  XX(OK, "success")                                                  \
+                                                                     \
+  /* 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") \
+  XX(CB_body, "the on_body callback failed")                         \
+  XX(CB_message_complete, "the on_message_complete callback failed") \
+                                                                     \
+  /* Parsing-related errors */                                       \
+  XX(INVALID_EOF_STATE, "stream ended at an unexpected time")        \
+  XX(HEADER_OVERFLOW,                                                \
+     "too many header bytes seen; overflow detected")                \
+  XX(CLOSED_CONNECTION,                                              \
+     "data received after completed connection: close message")      \
+  XX(INVALID_VERSION, "invalid HTTP version")                        \
+  XX(INVALID_STATUS, "invalid HTTP status code")                     \
+  XX(INVALID_METHOD, "invalid HTTP method")                          \
+  XX(INVALID_URL, "invalid URL")                                     \
+  XX(INVALID_HOST, "invalid host")                                   \
+  XX(INVALID_PORT, "invalid port")                                   \
+  XX(INVALID_PATH, "invalid path")                                   \
+  XX(INVALID_QUERY_STRING, "invalid query string")                   \
+  XX(INVALID_FRAGMENT, "invalid fragment")                           \
+  XX(LF_EXPECTED, "LF character expected")                           \
+  XX(INVALID_HEADER_TOKEN, "invalid character in header")            \
+  XX(INVALID_CONTENT_LENGTH,                                         \
+     "invalid character in content-length header")                   \
+  XX(INVALID_CHUNK_SIZE,                                             \
+     "invalid character in chunk size header")                       \
+  XX(INVALID_CONSTANT, "invalid constant string")                    \
+  XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+  XX(STRICT, "strict mode assertion failed")                         \
+  XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+  HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* 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;
+
+  size_t nread;
+  int64_t content_length;
+
+  /** READ-ONLY **/
+  unsigned short http_major;
+  unsigned short http_minor;
+  unsigned short status_code; /* responses only */
+  unsigned char method;    /* requests only */
+  unsigned char http_errno : 7;
+
+  /* 1 = Upgrade header was present and the parser has exited because of that.
+   * 0 = No upgrade header present.
+   * Should be checked when http_parser_execute() returns in addition to
+   * error checking.
+   */
+  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 */
+};
+
+
+struct http_parser_settings {
+  http_cb      on_message_begin;
+  http_data_cb on_url;
+  http_data_cb on_header_field;
+  http_data_cb on_header_value;
+  http_cb      on_headers_complete;
+  http_data_cb on_body;
+  http_cb      on_message_complete;
+};
+
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+size_t http_parser_execute(http_parser *parser,
+                           const http_parser_settings *settings,
+                           const char *data,
+                           size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns true, then this will be 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);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
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
@@ -8,7 +8,6 @@
 #define INCLUDE_git_branch_h__
 
 #include "common.h"
-#include "oid.h"
 #include "types.h"
 
 /**
diff --git a/libgit2/src/attr.c b/libgit2/src/attr.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/attr.c
@@ -0,0 +1,672 @@
+#include "repository.h"
+#include "fileops.h"
+#include "config.h"
+#include <ctype.h>
+
+GIT__USE_STRMAP;
+
+static int collect_attr_files(
+	git_repository *repo,
+	uint32_t flags,
+	const char *path,
+	git_vector *files);
+
+
+int git_attr_get(
+	const char **value,
+    git_repository *repo,
+	uint32_t flags,
+	const char *pathname,
+	const char *name)
+{
+	int error;
+	git_attr_path path;
+	git_vector files = GIT_VECTOR_INIT;
+	unsigned int i, j;
+	git_attr_file *file;
+	git_attr_name attr;
+	git_attr_rule *rule;
+
+	*value = NULL;
+
+	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+		return -1;
+
+	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+		goto cleanup;
+
+	attr.name = name;
+	attr.name_hash = git_attr_file__name_hash(name);
+
+	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) {
+				*value = ((git_attr_assignment *)git_vector_get(
+							  &rule->assigns, pos))->value;
+				goto cleanup;
+			}
+		}
+	}
+
+cleanup:
+	git_vector_free(&files);
+	git_attr_path__free(&path);
+
+	return error;
+}
+
+
+typedef struct {
+	git_attr_name name;
+	git_attr_assignment *found;
+} attr_get_many_info;
+
+int git_attr_get_many(
+	const char **values,
+    git_repository *repo,
+	uint32_t flags,
+	const char *pathname,
+    size_t num_attr,
+	const char **names)
+{
+	int error;
+	git_attr_path path;
+	git_vector files = GIT_VECTOR_INIT;
+	unsigned int i, j, k;
+	git_attr_file *file;
+	git_attr_rule *rule;
+	attr_get_many_info *info = NULL;
+	size_t num_found = 0;
+
+	memset((void *)values, 0, sizeof(const char *) * num_attr);
+
+	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+		return -1;
+
+	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+		goto cleanup;
+
+	info = git__calloc(num_attr, sizeof(attr_get_many_info));
+	GITERR_CHECK_ALLOC(info);
+
+	git_vector_foreach(&files, i, file) {
+
+		git_attr_file__foreach_matching_rule(file, &path, j, rule) {
+
+			for (k = 0; k < num_attr; k++) {
+				int pos;
+
+				if (info[k].found != NULL) /* already found assignment */
+					continue;
+
+				if (!info[k].name.name) {
+					info[k].name.name = names[k];
+					info[k].name.name_hash = git_attr_file__name_hash(names[k]);
+				}
+
+				pos = git_vector_bsearch(&rule->assigns, &info[k].name);
+				if (pos >= 0) {
+					info[k].found = (git_attr_assignment *)
+						git_vector_get(&rule->assigns, pos);
+					values[k] = info[k].found->value;
+
+					if (++num_found == num_attr)
+						goto cleanup;
+				}
+			}
+		}
+	}
+
+cleanup:
+	git_vector_free(&files);
+	git_attr_path__free(&path);
+	git__free(info);
+
+	return error;
+}
+
+
+int git_attr_foreach(
+    git_repository *repo,
+	uint32_t flags,
+	const char *pathname,
+	int (*callback)(const char *name, const char *value, void *payload),
+	void *payload)
+{
+	int error;
+	git_attr_path path;
+	git_vector files = GIT_VECTOR_INIT;
+	unsigned int i, j, k;
+	git_attr_file *file;
+	git_attr_rule *rule;
+	git_attr_assignment *assign;
+	git_strmap *seen = NULL;
+
+	if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
+		return -1;
+
+	if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
+		goto cleanup;
+
+	seen = git_strmap_alloc();
+	GITERR_CHECK_ALLOC(seen);
+
+	git_vector_foreach(&files, i, file) {
+
+		git_attr_file__foreach_matching_rule(file, &path, j, rule) {
+
+			git_vector_foreach(&rule->assigns, k, assign) {
+				/* skip if higher priority assignment was already seen */
+				if (git_strmap_exists(seen, assign->name))
+					continue;
+
+				git_strmap_insert(seen, assign->name, assign, error);
+				if (error >= 0)
+					error = callback(assign->name, assign->value, payload);
+
+				if (error != 0)
+					goto cleanup;
+			}
+		}
+	}
+
+cleanup:
+	git_strmap_free(seen);
+	git_vector_free(&files);
+	git_attr_path__free(&path);
+
+	return error;
+}
+
+
+int git_attr_add_macro(
+	git_repository *repo,
+	const char *name,
+	const char *values)
+{
+	int error;
+	git_attr_rule *macro = NULL;
+	git_pool *pool;
+
+	if (git_attr_cache__init(repo) < 0)
+		return -1;
+
+	macro = git__calloc(1, sizeof(git_attr_rule));
+	GITERR_CHECK_ALLOC(macro);
+
+	pool = &git_repository_attr_cache(repo)->pool;
+
+	macro->match.pattern = git_pool_strdup(pool, name);
+	GITERR_CHECK_ALLOC(macro->match.pattern);
+
+	macro->match.length = strlen(macro->match.pattern);
+	macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
+
+	error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
+
+	if (!error)
+		error = git_attr_cache__insert_macro(repo, macro);
+
+	if (error < 0)
+		git_attr_rule__free(macro);
+
+	return error;
+}
+
+bool git_attr_cache__is_cached(
+	git_repository *repo, git_attr_file_source source, const char *path)
+{
+	git_buf cache_key = GIT_BUF_INIT;
+	git_strmap *files = git_repository_attr_cache(repo)->files;
+	const char *workdir = git_repository_workdir(repo);
+	bool rval;
+
+	if (workdir && git__prefixcmp(path, workdir) == 0)
+		path += strlen(workdir);
+	if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0)
+		return false;
+
+	rval = git_strmap_exists(files, git_buf_cstr(&cache_key));
+
+	git_buf_free(&cache_key);
+
+	return rval;
+}
+
+static int load_attr_file(
+	const char **data,
+	git_attr_file_stat_sig *sig,
+	const char *filename)
+{
+	int error;
+	git_buf content = GIT_BUF_INIT;
+	struct stat st;
+
+	if (p_stat(filename, &st) < 0)
+		return GIT_ENOTFOUND;
+
+	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)
+		return GIT_ENOTFOUND;
+
+	error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
+	if (error < 0)
+		return error;
+
+	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;
+	}
+
+	*data = git_buf_detach(&content);
+
+	return 0;
+}
+
+static int load_attr_blob_from_index(
+	const char **content,
+	git_blob **blob,
+	git_repository *repo,
+	const git_oid *old_oid,
+	const char *relfile)
+{
+	int error;
+	git_index *index;
+	git_index_entry *entry;
+
+	if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+		(error = git_index_find(index, relfile)) < 0)
+		return error;
+
+	entry = git_index_get(index, error);
+
+	if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
+		return GIT_ENOTFOUND;
+
+	if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
+		return error;
+
+	*content = git_blob_rawcontent(*blob);
+	return 0;
+}
+
+static int load_attr_from_cache(
+	git_attr_file **file,
+	git_attr_cache *cache,
+	git_attr_file_source source,
+	const char *relative_path)
+{
+	git_buf  cache_key = GIT_BUF_INIT;
+	khiter_t cache_pos;
+
+	*file = NULL;
+
+	if (!cache || !cache->files)
+		return 0;
+
+	if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)
+		return -1;
+
+	cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
+
+	git_buf_free(&cache_key);
+
+	if (git_strmap_valid_index(cache->files, cache_pos))
+		*file = git_strmap_value_at(cache->files, cache_pos);
+
+	return 0;
+}
+
+int git_attr_cache__internal_file(
+	git_repository *repo,
+	const char *filename,
+	git_attr_file **file)
+{
+	int error = 0;
+	git_attr_cache *cache = git_repository_attr_cache(repo);
+	khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
+
+	if (git_strmap_valid_index(cache->files, cache_pos)) {
+		*file = git_strmap_value_at(cache->files, cache_pos);
+		return 0;
+	}
+
+	if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
+		return -1;
+
+	git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
+	if (error > 0)
+		error = 0;
+
+	return error;
+}
+
+int git_attr_cache__push_file(
+	git_repository *repo,
+	const char *base,
+	const char *filename,
+	git_attr_file_source source,
+	git_attr_file_parser parse,
+	git_vector *stack)
+{
+	int error = 0;
+	git_buf path = GIT_BUF_INIT;
+	const char *workdir = git_repository_workdir(repo);
+	const char *relfile, *content = NULL;
+	git_attr_cache *cache = git_repository_attr_cache(repo);
+	git_attr_file *file = NULL;
+	git_blob *blob = NULL;
+	git_attr_file_stat_sig st;
+
+	assert(filename && stack);
+
+	/* join base and path as needed */
+	if (base != NULL && git_path_root(filename) < 0) {
+		if (git_buf_joinpath(&path, base, filename) < 0)
+			return -1;
+		filename = path.ptr;
+	}
+
+	relfile = filename;
+	if (workdir && git__prefixcmp(relfile, workdir) == 0)
+		relfile += strlen(workdir);
+
+	/* check cache */
+	if (load_attr_from_cache(&file, cache, source, relfile) < 0)
+		return -1;
+
+	/* 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));
+
+		error = load_attr_file(&content, &st, filename);
+	} else {
+		error = load_attr_blob_from_index(&content, &blob,
+			repo, file ? &file->cache_data.oid : NULL, relfile);
+	}
+
+	if (error) {
+		/* not finding a file is not an error for this function */
+		if (error == GIT_ENOTFOUND) {
+			giterr_clear();
+			error = 0;
+		}
+		goto finish;
+	}
+
+	if (!file &&
+		(error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
+		goto finish;
+
+	if (parse && (error = parse(repo, content, file)) < 0)
+		goto finish;
+
+	git_strmap_insert(cache->files, file->key, file, error);
+	if (error > 0)
+		error = 0;
+
+	/* remember "cache buster" file signature */
+	if (blob)
+		git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
+	else
+		memcpy(&file->cache_data.st, &st, sizeof(st));
+
+finish:
+	/* push file onto vector if we found one*/
+	if (!error && file != NULL)
+		error = git_vector_insert(stack, file);
+
+	if (error != 0)
+		git_attr_file__free(file);
+
+	if (blob)
+		git_blob_free(blob);
+	else
+		git__free((void *)content);
+
+	git_buf_free(&path);
+
+	return error;
+}
+
+#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))
+
+typedef struct {
+	git_repository *repo;
+	uint32_t flags;
+	const char *workdir;
+	git_index *index;
+	git_vector *files;
+} attr_walk_up_info;
+
+int git_attr_cache__decide_sources(
+	uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
+{
+	int count = 0;
+
+	switch (flags & 0x03) {
+	case GIT_ATTR_CHECK_FILE_THEN_INDEX:
+		if (has_wd)
+			srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+		if (has_index)
+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+		break;
+	case GIT_ATTR_CHECK_INDEX_THEN_FILE:
+		if (has_index)
+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+		if (has_wd)
+			srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
+		break;
+	case GIT_ATTR_CHECK_INDEX_ONLY:
+		if (has_index)
+			srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
+		break;
+	}
+
+	return count;
+}
+
+static int push_one_attr(void *ref, git_buf *path)
+{
+	int error = 0, n_src, i;
+	attr_walk_up_info *info = (attr_walk_up_info *)ref;
+	git_attr_file_source src[2];
+
+	n_src = git_attr_cache__decide_sources(
+		info->flags, info->workdir != NULL, info->index != NULL, src);
+
+	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);
+
+	return error;
+}
+
+static int collect_attr_files(
+	git_repository *repo,
+	uint32_t flags,
+	const char *path,
+	git_vector *files)
+{
+	int error;
+	git_buf dir = GIT_BUF_INIT;
+	const char *workdir = git_repository_workdir(repo);
+	attr_walk_up_info info;
+
+	if (git_attr_cache__init(repo) < 0 ||
+		git_vector_init(files, 4, NULL) < 0)
+		return -1;
+
+	/* Resolve path in a non-bare repo */
+	if (workdir != NULL)
+		error = git_path_find_dir(&dir, path, workdir);
+	else
+		error = git_path_dirname_r(&dir, path);
+	if (error < 0)
+		goto cleanup;
+
+	/* in precendence order highest to lowest:
+	 * - $GIT_DIR/info/attributes
+	 * - path components with .gitattributes
+	 * - config core.attributesfile
+	 * - $GIT_PREFIX/etc/gitattributes
+	 */
+
+	error = push_attr_file(
+		repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
+	if (error < 0)
+		goto cleanup;
+
+	info.repo  = repo;
+	info.flags = flags;
+	info.workdir = workdir;
+	if (git_repository_index__weakptr(&info.index, repo) < 0)
+		giterr_clear(); /* no error even if there is no index */
+	info.files = files;
+
+	error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
+	if (error < 0)
+		goto cleanup;
+
+	if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
+		error = push_attr_file(
+			repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+		if (error < 0)
+			goto cleanup;
+	}
+
+	if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+		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)
+			error = 0;
+	}
+
+ cleanup:
+	if (error < 0)
+		git_vector_free(files);
+	git_buf_free(&dir);
+
+	return error;
+}
+
+
+int git_attr_cache__init(git_repository *repo)
+{
+	int ret;
+	git_attr_cache *cache = git_repository_attr_cache(repo);
+	git_config *cfg;
+
+	if (cache->initialized)
+		return 0;
+
+	/* cache config settings for attributes and ignores */
+	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)
+		return ret;
+
+	ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);
+	if (ret < 0 && ret != GIT_ENOTFOUND)
+		return ret;
+
+	giterr_clear();
+
+	/* allocate hashtable for attribute and ignore file contents */
+	if (cache->files == NULL) {
+		cache->files = git_strmap_alloc();
+		GITERR_CHECK_ALLOC(cache->files);
+	}
+
+	/* allocate hashtable for attribute macros */
+	if (cache->macros == NULL) {
+		cache->macros = git_strmap_alloc();
+		GITERR_CHECK_ALLOC(cache->macros);
+	}
+
+	/* allocate string pool */
+	if (git_pool_init(&cache->pool, 1, 0) < 0)
+		return -1;
+
+	cache->initialized = 1;
+
+	/* insert default macros */
+	return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
+}
+
+void git_attr_cache_flush(
+	git_repository *repo)
+{
+	git_attr_cache *cache;
+
+	if (!repo)
+		return;
+
+	cache = git_repository_attr_cache(repo);
+
+	if (cache->files != NULL) {
+		git_attr_file *file;
+
+		git_strmap_foreach_value(cache->files, file, {
+			git_attr_file__free(file);
+		});
+
+		git_strmap_free(cache->files);
+	}
+
+	if (cache->macros != NULL) {
+		git_attr_rule *rule;
+
+		git_strmap_foreach_value(cache->macros, rule, {
+			git_attr_rule__free(rule);
+		});
+
+		git_strmap_free(cache->macros);
+	}
+
+	git_pool_clear(&cache->pool);
+
+	cache->initialized = 0;
+}
+
+int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
+{
+	git_strmap *macros = git_repository_attr_cache(repo)->macros;
+	int error;
+
+	/* TODO: generate warning log if (macro->assigns.length == 0) */
+	if (macro->assigns.length == 0)
+		return 0;
+
+	git_strmap_insert(macros, macro->match.pattern, macro, error);
+	return (error < 0) ? -1 : 0;
+}
+
+git_attr_rule *git_attr_cache__lookup_macro(
+	git_repository *repo, const char *name)
+{
+	git_strmap *macros = git_repository_attr_cache(repo)->macros;
+	khiter_t pos;
+
+	pos = git_strmap_lookup_index(macros, name);
+
+	if (!git_strmap_valid_index(macros, pos))
+		return NULL;
+
+	return (git_attr_rule *)git_strmap_value_at(macros, pos);
+}
+
diff --git a/libgit2/src/attr.h b/libgit2/src/attr.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/attr.h
@@ -0,0 +1,56 @@
+/*
+ * 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_attr_h__
+#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;
+
+typedef int (*git_attr_file_parser)(
+	git_repository *, const char *, git_attr_file *);
+
+extern int git_attr_cache__init(git_repository *repo);
+
+extern int git_attr_cache__insert_macro(
+	git_repository *repo, git_attr_rule *macro);
+
+extern git_attr_rule *git_attr_cache__lookup_macro(
+	git_repository *repo, const char *name);
+
+extern int git_attr_cache__push_file(
+	git_repository *repo,
+	const char *base,
+	const char *filename,
+	git_attr_file_source source,
+	git_attr_file_parser parse,
+	git_vector *stack);
+
+extern int git_attr_cache__internal_file(
+	git_repository *repo,
+	const char *key,
+	git_attr_file **file_ptr);
+
+/* returns true if path is in cache */
+extern bool git_attr_cache__is_cached(
+	git_repository *repo, git_attr_file_source source, const char *path);
+
+extern int git_attr_cache__decide_sources(
+	uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);
+
+#endif
diff --git a/libgit2/src/attr_file.c b/libgit2/src/attr_file.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/attr_file.c
@@ -0,0 +1,601 @@
+#include "common.h"
+#include "repository.h"
+#include "filebuf.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);
+
+int git_attr_file__new(
+	git_attr_file **attrs_ptr,
+	git_attr_file_source from,
+	const char *path,
+	git_pool *pool)
+{
+	git_attr_file *attrs = NULL;
+
+	attrs = git__calloc(1, sizeof(git_attr_file));
+	GITERR_CHECK_ALLOC(attrs);
+
+	if (pool)
+		attrs->pool = pool;
+	else {
+		attrs->pool = git__calloc(1, sizeof(git_pool));
+		if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
+			goto fail;
+		attrs->pool_is_allocated = true;
+	}
+
+	if (path) {
+		size_t len = strlen(path);
+
+		attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
+		GITERR_CHECK_ALLOC(attrs->key);
+
+		attrs->key[0] = '0' + from;
+		attrs->key[1] = '#';
+		memcpy(&attrs->key[2], path, len);
+		attrs->key[len + 2] = '\0';
+	}
+
+	if (git_vector_init(&attrs->rules, 4, NULL) < 0)
+		goto fail;
+
+	*attrs_ptr = attrs;
+	return 0;
+
+fail:
+	git_attr_file__free(attrs);
+	attrs_ptr = NULL;
+	return -1;
+}
+
+int git_attr_file__parse_buffer(
+	git_repository *repo, const char *buffer, git_attr_file *attrs)
+{
+	int error = 0;
+	const char *scan = NULL;
+	char *context = NULL;
+	git_attr_rule *rule = NULL;
+
+	assert(buffer && attrs);
+
+	scan = buffer;
+
+	/* if subdir file path, convert context for file paths */
+	if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {
+		context = attrs->key + 2;
+		context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';
+	}
+
+	while (!error && *scan) {
+		/* allocate rule if needed */
+		if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
+			error = -1;
+			break;
+		}
+
+		/* parse the next "pattern attr attr attr" line */
+		if (!(error = git_attr_fnmatch__parse(
+				&rule->match, attrs->pool, context, &scan)) &&
+			!(error = git_attr_assignment__parse(
+				repo, attrs->pool, &rule->assigns, &scan)))
+		{
+			if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
+				/* should generate error/warning if this is coming from any
+				 * file other than .gitattributes at repo root.
+				 */
+				error = git_attr_cache__insert_macro(repo, rule);
+			else
+				error = git_vector_insert(&attrs->rules, rule);
+		}
+
+		/* if the rule wasn't a pattern, on to the next */
+		if (error < 0) {
+			git_attr_rule__clear(rule); /* reset rule contents */
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+		} else {
+			rule = NULL; /* vector now "owns" the rule */
+		}
+	}
+
+	git_attr_rule__free(rule);
+
+	/* restore file path used for context */
+	if (context)
+		context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
+
+	return error;
+}
+
+int git_attr_file__new_and_load(
+	git_attr_file **attrs_ptr,
+	const char *path)
+{
+	int error;
+	git_buf content = GIT_BUF_INIT;
+
+	if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
+		return error;
+
+	if (!(error = git_futils_readbuffer(&content, path)))
+		error = git_attr_file__parse_buffer(
+			NULL, git_buf_cstr(&content), *attrs_ptr);
+
+	git_buf_free(&content);
+
+	if (error) {
+		git_attr_file__free(*attrs_ptr);
+		*attrs_ptr = NULL;
+	}
+
+	return error;
+}
+
+void git_attr_file__free(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);
+
+	if (file->pool_is_allocated) {
+		git_pool_clear(file->pool);
+		git__free(file->pool);
+	}
+	file->pool = NULL;
+
+	git__free(file);
+}
+
+uint32_t git_attr_file__name_hash(const char *name)
+{
+	uint32_t h = 5381;
+	int c;
+	assert(name);
+	while ((c = (int)*name++) != 0)
+		h = ((h << 5) + h) + c;
+	return h;
+}
+
+
+int git_attr_file__lookup_one(
+	git_attr_file *file,
+	const git_attr_path *path,
+	const char *attr,
+	const char **value)
+{
+	unsigned int i;
+	git_attr_name name;
+	git_attr_rule *rule;
+
+	*value = NULL;
+
+	name.name = attr;
+	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);
+
+		if (pos >= 0) {
+			*value = ((git_attr_assignment *)
+					  git_vector_get(&rule->assigns, pos))->value;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
+bool git_attr_fnmatch__match(
+	git_attr_fnmatch *match,
+	const git_attr_path *path)
+{
+	int fnm;
+
+	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);
+	else if (path->is_dir)
+		fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
+	else
+		fnm = p_fnmatch(match->pattern, path->basename, 0);
+
+	return (fnm == FNM_NOMATCH) ? false : true;
+}
+
+bool git_attr_rule__match(
+	git_attr_rule *rule,
+	const git_attr_path *path)
+{
+	bool matched = git_attr_fnmatch__match(&rule->match, path);
+
+	if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
+		matched = !matched;
+
+	return matched;
+}
+
+
+git_attr_assignment *git_attr_rule__lookup_assignment(
+	git_attr_rule *rule, const char *name)
+{
+	int pos;
+	git_attr_name key;
+	key.name = name;
+	key.name_hash = git_attr_file__name_hash(name);
+
+	pos = git_vector_bsearch(&rule->assigns, &key);
+
+	return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;
+}
+
+int git_attr_path__init(
+	git_attr_path *info, const char *path, const char *base)
+{
+	/* 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;
+	}
+
+	/* remove trailing slashes */
+	while (info->full.size > 0) {
+		if (info->full.ptr[info->full.size - 1] != '/')
+			break;
+		info->full.size--;
+	}
+	info->full.ptr[info->full.size] = '\0';
+
+	/* skip leading slashes in path */
+	while (*info->path == '/')
+		info->path++;
+
+	/* find trailing basename component */
+	info->basename = strrchr(info->path, '/');
+	if (info->basename)
+		info->basename++;
+	if (!info->basename || !*info->basename)
+		info->basename = info->path;
+
+	info->is_dir = (int)git_path_isdir(info->full.ptr);
+
+	return 0;
+}
+
+void git_attr_path__free(git_attr_path *info)
+{
+	git_buf_free(&info->full);
+	info->path = NULL;
+	info->basename = NULL;
+}
+
+
+/*
+ * From gitattributes(5):
+ *
+ * Patterns have the following format:
+ *
+ * - A blank line matches no files, so it can serve as a separator for
+ *   readability.
+ *
+ * - A line starting with # serves as a comment.
+ *
+ * - An optional prefix ! which negates the pattern; any matching file
+ *   excluded by a previous pattern will become included again. If a negated
+ *   pattern matches, this will override lower precedence patterns sources.
+ *
+ * - If the pattern ends with a slash, it is removed for the purpose of the
+ *   following description, but it would only find a match with a directory. In
+ *   other words, foo/ will match a directory foo and paths underneath it, but
+ *   will not match a regular file or a symbolic link foo (this is consistent
+ *   with the way how pathspec works in general in git).
+ *
+ * - If the pattern does not contain a slash /, git treats it as a shell glob
+ *   pattern and checks for a match against the pathname without leading
+ *   directories.
+ *
+ * - Otherwise, git treats the pattern as a shell glob suitable for consumption
+ *   by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will
+ *   not match a / in the pathname. For example, "Documentation/\*.html" matches
+ *   "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading
+ *   slash matches the beginning of the pathname; for example, "/\*.c" matches
+ *   "cat-file.c" but not "mozilla-sha1/sha1.c".
+ */
+
+/*
+ * This will return 0 if the spec was filled out,
+ * GIT_ENOTFOUND if the fnmatch does not require matching, or
+ * another error code there was an actual problem.
+ */
+int git_attr_fnmatch__parse(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *source,
+	const char **base)
+{
+	const char *pattern, *scan;
+	int slash_count;
+
+	assert(spec && base && *base);
+
+	pattern = *base;
+
+	while (git__isspace(*pattern)) pattern++;
+	if (!*pattern || *pattern == '#') {
+		*base = git__next_line(pattern);
+		return GIT_ENOTFOUND;
+	}
+
+	spec->flags = 0;
+
+	if (*pattern == '[') {
+		if (strncmp(pattern, "[attr]", 6) == 0) {
+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
+			pattern += 6;
+		}
+		/* else a character range like [a-e]* which is accepted */
+	}
+
+	if (*pattern == '!') {
+		spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
+		pattern++;
+	}
+
+	slash_count = 0;
+	for (scan = pattern; *scan != '\0'; ++scan) {
+		/* scan until (non-escaped) white space */
+		if (git__isspace(*scan) && *(scan - 1) != '\\')
+			break;
+
+		if (*scan == '/') {
+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
+			slash_count++;
+			if (pattern == scan)
+				pattern++;
+		}
+		/* remember if we see an unescaped wildcard in pattern */
+		else if (git__iswildcard(*scan) &&
+			(scan == pattern || (*(scan - 1) != '\\')))
+			spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
+	}
+
+	*base = scan;
+
+	spec->length = scan - pattern;
+
+	if (pattern[spec->length - 1] == '/') {
+		spec->length--;
+		spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
+		if (--slash_count <= 0)
+			spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
+	}
+
+	if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
+		source != NULL && git_path_root(pattern) < 0)
+	{
+		size_t sourcelen = strlen(source);
+		/* given an unrooted fullpath match from a file inside a repo,
+		 * prefix the pattern with the relative directory of the source file
+		 */
+		spec->pattern = git_pool_malloc(
+			pool, (uint32_t)(sourcelen + spec->length + 1));
+		if (spec->pattern) {
+			memcpy(spec->pattern, source, sourcelen);
+			memcpy(spec->pattern + sourcelen, pattern, spec->length);
+			spec->length += sourcelen;
+			spec->pattern[spec->length] = '\0';
+		}
+	} else {
+		spec->pattern = git_pool_strndup(pool, pattern, spec->length);
+	}
+
+	if (!spec->pattern) {
+		*base = git__next_line(pattern);
+		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);
+		}
+	}
+
+	return 0;
+}
+
+static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
+{
+	const git_attr_name *a = a_raw;
+	const git_attr_name *b = b_raw;
+
+	if (b->name_hash < a->name_hash)
+		return 1;
+	else if (b->name_hash > a->name_hash)
+		return -1;
+	else
+		return strcmp(b->name, a->name);
+}
+
+static void git_attr_assignment__free(git_attr_assignment *assign)
+{
+	/* name and value are stored in a git_pool associated with the
+	 * git_attr_file, so they do not need to be freed here
+	 */
+	assign->name = NULL;
+	assign->value = NULL;
+	git__free(assign);
+}
+
+static int merge_assignments(void **old_raw, void *new_raw)
+{
+	git_attr_assignment **old = (git_attr_assignment **)old_raw;
+	git_attr_assignment *new = (git_attr_assignment *)new_raw;
+
+	GIT_REFCOUNT_DEC(*old, git_attr_assignment__free);
+	*old = new;
+	return GIT_EEXISTS;
+}
+
+int git_attr_assignment__parse(
+	git_repository *repo,
+	git_pool *pool,
+	git_vector *assigns,
+	const char **base)
+{
+	int error;
+	const char *scan = *base;
+	git_attr_assignment *assign = NULL;
+
+	assert(assigns && !assigns->length);
+
+	assigns->_cmp = sort_by_hash_and_name;
+
+	while (*scan && *scan != '\n') {
+		const char *name_start, *value_start;
+
+		/* skip leading blanks */
+		while (git__isspace(*scan) && *scan != '\n') scan++;
+
+		/* allocate assign if needed */
+		if (!assign) {
+			assign = git__calloc(1, sizeof(git_attr_assignment));
+			GITERR_CHECK_ALLOC(assign);
+			GIT_REFCOUNT_INC(assign);
+		}
+
+		assign->name_hash = 5381;
+		assign->value = git_attr__true;
+
+		/* look for magic name prefixes */
+		if (*scan == '-') {
+			assign->value = git_attr__false;
+			scan++;
+		} else if (*scan == '!') {
+			assign->value = git_attr__unset; /* explicit unspecified state */
+			scan++;
+		} else if (*scan == '#') /* comment rest of line */
+			break;
+
+		/* find the name */
+		name_start = scan;
+		while (*scan && !git__isspace(*scan) && *scan != '=') {
+			assign->name_hash =
+				((assign->name_hash << 5) + assign->name_hash) + *scan;
+			scan++;
+		}
+		if (scan == name_start) {
+			/* must have found lone prefix (" - ") or leading = ("=foo")
+			 * or end of buffer -- advance until whitespace and continue
+			 */
+			while (*scan && !git__isspace(*scan)) scan++;
+			continue;
+		}
+
+		/* allocate permanent storage for name */
+		assign->name = git_pool_strndup(pool, name_start, scan - name_start);
+		GITERR_CHECK_ALLOC(assign->name);
+
+		/* if there is an equals sign, find the value */
+		if (*scan == '=') {
+			for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);
+
+			/* if we found a value, allocate permanent storage for it */
+			if (scan > value_start) {
+				assign->value = git_pool_strndup(pool, value_start, scan - value_start);
+				GITERR_CHECK_ALLOC(assign->value);
+			}
+		}
+
+		/* expand macros (if given a repo with a macro cache) */
+		if (repo != NULL && assign->value == git_attr__true) {
+			git_attr_rule *macro =
+				git_attr_cache__lookup_macro(repo, assign->name);
+
+			if (macro != NULL) {
+				unsigned int i;
+				git_attr_assignment *massign;
+
+				git_vector_foreach(&macro->assigns, i, massign) {
+					GIT_REFCOUNT_INC(massign);
+
+					error = git_vector_insert_sorted(
+						assigns, massign, &merge_assignments);
+					if (error < 0 && error != GIT_EEXISTS)
+						return error;
+				}
+			}
+		}
+
+		/* insert allocated assign into vector */
+		error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
+		if (error < 0 && error != GIT_EEXISTS)
+			return error;
+
+		/* clear assign since it is now "owned" by the vector */
+		assign = NULL;
+	}
+
+	if (assign != NULL)
+		git_attr_assignment__free(assign);
+
+	*base = git__next_line(scan);
+
+	return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
+}
+
+static void git_attr_rule__clear(git_attr_rule *rule)
+{
+	unsigned int i;
+	git_attr_assignment *assign;
+
+	if (!rule)
+		return;
+
+	if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) {
+		git_vector_foreach(&rule->assigns, i, assign)
+			GIT_REFCOUNT_DEC(assign, git_attr_assignment__free);
+		git_vector_free(&rule->assigns);
+	}
+
+	/* match.pattern is stored in a git_pool, so no need to free */
+	rule->match.pattern = NULL;
+	rule->match.length = 0;
+}
+
+void git_attr_rule__free(git_attr_rule *rule)
+{
+	git_attr_rule__clear(rule);
+	git__free(rule);
+}
+
diff --git a/libgit2/src/attr_file.h b/libgit2/src/attr_file.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/attr_file.h
@@ -0,0 +1,142 @@
+/*
+ * 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_attr_file_h__
+#define INCLUDE_attr_file_h__
+
+#include "git2/attr.h"
+#include "vector.h"
+#include "pool.h"
+#include "buffer.h"
+
+#define GIT_ATTR_FILE			".gitattributes"
+#define GIT_ATTR_FILE_INREPO	"info/attributes"
+#define GIT_ATTR_FILE_SYSTEM	"gitattributes"
+
+#define GIT_ATTR_FNMATCH_NEGATIVE	(1U << 0)
+#define GIT_ATTR_FNMATCH_DIRECTORY	(1U << 1)
+#define GIT_ATTR_FNMATCH_FULLPATH	(1U << 2)
+#define GIT_ATTR_FNMATCH_MACRO		(1U << 3)
+#define GIT_ATTR_FNMATCH_IGNORE		(1U << 4)
+#define GIT_ATTR_FNMATCH_HASWILD	(1U << 5)
+
+typedef struct {
+	char *pattern;
+	size_t length;
+	unsigned int flags;
+} git_attr_fnmatch;
+
+typedef struct {
+	git_attr_fnmatch match;
+	git_vector assigns;		/* vector of <git_attr_assignment*> */
+} git_attr_rule;
+
+typedef struct {
+	git_refcount unused;
+	const char *name;
+    uint32_t name_hash;
+} git_attr_name;
+
+typedef struct {
+	git_refcount rc;		/* for macros */
+	char *name;
+    uint32_t name_hash;
+    const char *value;
+} 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;
+	} cache_data;
+} git_attr_file;
+
+typedef struct {
+	git_buf     full;
+	const char *path;
+	const char *basename;
+	int         is_dir;
+} git_attr_path;
+
+typedef enum {
+	GIT_ATTR_FILE_FROM_FILE = 0,
+	GIT_ATTR_FILE_FROM_INDEX = 1
+} git_attr_file_source;
+
+/*
+ * git_attr_file API
+ */
+
+extern int git_attr_file__new(
+	git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
+
+extern int git_attr_file__new_and_load(
+	git_attr_file **attrs_ptr, const char *path);
+
+extern void git_attr_file__free(git_attr_file *file);
+
+extern int git_attr_file__parse_buffer(
+	git_repository *repo, const char *buf, git_attr_file *file);
+
+extern int git_attr_file__lookup_one(
+	git_attr_file *file,
+	const git_attr_path *path,
+	const char *attr,
+	const char **value);
+
+/* loop over rules in file from bottom to top */
+#define git_attr_file__foreach_matching_rule(file, path, iter, rule)	\
+	git_vector_rforeach(&(file)->rules, (iter), (rule)) \
+		if (git_attr_rule__match((rule), (path)))
+
+extern uint32_t git_attr_file__name_hash(const char *name);
+
+
+/*
+ * other utilities
+ */
+
+extern int git_attr_fnmatch__parse(
+	git_attr_fnmatch *spec,
+	git_pool *pool,
+	const char *source,
+	const char **base);
+
+extern bool git_attr_fnmatch__match(
+	git_attr_fnmatch *rule,
+	const git_attr_path *path);
+
+extern void git_attr_rule__free(git_attr_rule *rule);
+
+extern bool git_attr_rule__match(
+	git_attr_rule *rule,
+	const git_attr_path *path);
+
+extern git_attr_assignment *git_attr_rule__lookup_assignment(
+	git_attr_rule *rule, const char *name);
+
+extern int git_attr_path__init(
+	git_attr_path *info, const char *path, const char *base);
+
+extern void git_attr_path__free(git_attr_path *info);
+
+extern int git_attr_assignment__parse(
+	git_repository *repo, /* needed to expand macros */
+	git_pool *pool,
+	git_vector *assigns,
+	const char **scan);
+
+#endif
diff --git a/libgit2/src/blob.c b/libgit2/src/blob.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/blob.c
@@ -0,0 +1,239 @@
+/*
+ * 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/common.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+
+#include "common.h"
+#include "blob.h"
+#include "filter.h"
+
+const void *git_blob_rawcontent(git_blob *blob)
+{
+	assert(blob);
+	return blob->odb_object->raw.data;
+}
+
+size_t git_blob_rawsize(git_blob *blob)
+{
+	assert(blob);
+	return blob->odb_object->raw.len;
+}
+
+int git_blob__getbuf(git_buf *buffer, git_blob *blob)
+{
+	return git_buf_set(
+		buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
+}
+
+void git_blob__free(git_blob *blob)
+{
+	git_odb_object_free(blob->odb_object);
+	git__free(blob);
+}
+
+int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
+{
+	assert(blob);
+	git_cached_obj_incref((git_cached_obj *)odb_obj);
+	blob->odb_object = odb_obj;
+	return 0;
+}
+
+int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
+{
+	int error;
+	git_odb *odb;
+	git_odb_stream *stream;
+
+	if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+		(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
+		return error;
+
+	if ((error = stream->write(stream, buffer, len)) == 0)
+		error = stream->finalize_write(oid, stream);
+
+	stream->free(stream);
+	return error;
+}
+
+static int write_file_stream(
+	git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
+{
+	int fd, error;
+	char buffer[4096];
+	git_odb_stream *stream = NULL;
+
+	if ((error = git_odb_open_wstream(
+			&stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
+		return error;
+
+	if ((fd = git_futils_open_ro(path)) < 0) {
+		stream->free(stream);
+		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;
+	}
+
+	p_close(fd);
+
+	if (!error)
+		error = stream->finalize_write(oid, stream);
+
+	stream->free(stream);
+	return error;
+}
+
+static int write_file_filtered(
+	git_oid *oid,
+	git_odb *odb,
+	const char *full_path,
+	git_vector *filters)
+{
+	int error;
+	git_buf source = GIT_BUF_INIT;
+	git_buf dest = GIT_BUF_INIT;
+
+	if ((error = git_futils_readbuffer(&source, full_path)) < 0)
+		return error;
+
+	error = git_filters_apply(&dest, &source, filters);
+
+	/* Free the source as soon as possible. This can be big in memory,
+	 * and we don't want to ODB write to choke */
+	git_buf_free(&source);
+
+	/* Write the file to disk if it was properly filtered */
+	if (!error)
+		error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
+
+	git_buf_free(&dest);
+	return error;
+}
+
+static int write_symlink(
+	git_oid *oid, git_odb *odb, const char *path, size_t link_size)
+{
+	char *link_data;
+	ssize_t read_len;
+	int error;
+
+	link_data = git__malloc(link_size);
+	GITERR_CHECK_ALLOC(link_data);
+
+	read_len = p_readlink(path, link_data, link_size);
+	if (read_len != (ssize_t)link_size) {
+		giterr_set(GITERR_OS, "Failed to create blob.  Can't read symlink '%s'", path);
+		git__free(link_data);
+		return -1;
+	}
+
+	error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
+	git__free(link_data);
+	return error;
+}
+
+static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
+{
+	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)
+		return error;
+
+	size = st.st_size;
+
+	if (S_ISLNK(st.st_mode)) {
+		error = write_symlink(oid, odb, path, (size_t)size);
+	} else {
+		git_vector write_filters = GIT_VECTOR_INIT;
+		int filter_count;
+
+		/* Load the filters for writing this file to the ODB */
+		filter_count = git_filters_load(
+			&write_filters, repo, path, GIT_FILTER_TO_ODB);
+
+		if (filter_count < 0) {
+			/* Negative value means there was a critical error */
+			error = filter_count;
+		} else if (filter_count == 0) {
+			/* No filters need to be applied to the document: we can stream
+			 * directly from disk */
+			error = write_file_stream(oid, odb, path, size);
+		} else {
+			/* We need to apply one or more filters */
+			error = write_file_filtered(oid, odb, path, &write_filters);
+		}
+
+		git_filters_free(&write_filters);
+
+		/*
+		 * TODO: eventually support streaming filtered files, for files
+		 * which are bigger than a given threshold. This is not a priority
+		 * because applying a filter in streaming mode changes the final
+		 * size of the blob, and without knowing its final size, the blob
+		 * cannot be written in stream mode to the ODB.
+		 *
+		 * The plan is to do streaming writes to a tempfile on disk and then
+		 * opening streaming that file to the ODB, using
+		 * `write_file_stream`.
+		 *
+		 * CAREFULLY DESIGNED APIS YO
+		 */
+	}
+
+	return error;
+}
+
+int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
+{
+	git_buf full_path = GIT_BUF_INIT;
+	const char *workdir;
+	int 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));
+
+	git_buf_free(&full_path);
+	return error;
+}
+
+int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
+{
+	int error;
+	git_buf full_path = GIT_BUF_INIT;
+
+	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));
+
+	git_buf_free(&full_path);
+	return error;
+}
diff --git a/libgit2/src/blob.h b/libgit2/src/blob.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/blob.h
@@ -0,0 +1,24 @@
+/*
+ * 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_blob_h__
+#define INCLUDE_blob_h__
+
+#include "git2/blob.h"
+#include "repository.h"
+#include "odb.h"
+#include "fileops.h"
+
+struct git_blob {
+	git_object object;
+	git_odb_object *odb_object;
+};
+
+void git_blob__free(git_blob *blob);
+int git_blob__parse(git_blob *blob, git_odb_object *obj);
+int git_blob__getbuf(git_buf *buffer, git_blob *blob);
+
+#endif
diff --git a/libgit2/src/branch.c b/libgit2/src/branch.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/branch.c
@@ -0,0 +1,208 @@
+/*
+ * 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 "commit.h"
+#include "branch.h"
+#include "tag.h"
+
+static int retrieve_branch_reference(
+	git_reference **branch_reference_out,
+	git_repository *repo,
+	const char *branch_name,
+	int is_remote)
+{
+	git_reference *branch;
+	int error = -1;
+	char *prefix;
+	git_buf ref_name = GIT_BUF_INIT;
+
+	*branch_reference_out = NULL;
+
+	prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
+
+	if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
+		goto cleanup;
+
+	if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
+		giterr_set(GITERR_REFERENCE,
+			"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
+		goto cleanup;
+	}
+
+	*branch_reference_out = branch;
+
+cleanup:
+	git_buf_free(&ref_name);
+	return error;
+}
+
+static int create_error_invalid(const char *msg)
+{
+	giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
+	return -1;
+}
+
+int git_branch_create(
+		git_oid *oid_out,
+		git_repository *repo,
+		const char *branch_name,
+		const git_object *target,
+		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.");
+	}
+
+	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;
+
+	git_oid_cpy(oid_out, git_reference_oid(branch));
+	error = 0;
+
+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)
+{
+	git_reference *branch = NULL;
+	git_reference *head = NULL;
+	int error;
+
+	assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
+
+	if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
+		return error;
+
+	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
+		giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
+		goto on_error;
+	}
+
+	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);
+			goto on_error;
+	}
+
+	if (git_reference_delete(branch) < 0)
+		goto on_error;
+
+	git_reference_free(head);
+	return 0;
+
+on_error:
+	git_reference_free(head);
+	git_reference_free(branch);
+	return -1;
+}
+
+typedef struct {
+	git_vector *branchlist;
+	unsigned int branch_type;
+} branch_filter_data;
+
+static int branch_list_cb(const char *branch_name, void *payload)
+{
+	branch_filter_data *filter = (branch_filter_data *)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));
+
+	return 0;
+}
+
+int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
+{
+	int error;
+	branch_filter_data filter;
+	git_vector branchlist;
+
+	assert(branch_names && repo);
+
+	if (git_vector_init(&branchlist, 8, NULL) < 0)
+		return -1;
+
+	filter.branchlist = &branchlist;
+	filter.branch_type = list_flags;
+
+	error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter);
+	if (error < 0) {
+		git_vector_free(&branchlist);
+		return -1;
+	}
+
+	branch_names->strings = (char **)branchlist.contents;
+	branch_names->count = branchlist.length;
+	return 0;
+}
+
+int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
+{
+	git_reference *reference = NULL;
+	git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
+	int error = 0;
+
+	if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
+		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)
+		goto cleanup;
+
+	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
+		goto cleanup;
+
+	error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
+
+cleanup:
+	git_reference_free(reference);
+	git_buf_free(&old_reference_name);
+	git_buf_free(&new_reference_name);
+
+	return error;
+}
diff --git a/libgit2/src/branch.h b/libgit2/src/branch.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/branch.h
@@ -0,0 +1,17 @@
+/*
+ * 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_branch_h__
+#define INCLUDE_branch_h__
+
+#include "git2/branch.h"
+
+struct git_branch {
+	char *remote; /* TODO: Make this a git_remote */
+	char *merge;
+};
+
+#endif
diff --git a/libgit2/src/bswap.h b/libgit2/src/bswap.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/bswap.h
@@ -0,0 +1,97 @@
+/*
+ * 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"
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+GIT_INLINE(uint32_t) default_swab32(uint32_t val)
+{
+	return (((val & 0xff000000) >> 24) |
+		((val & 0x00ff0000) >> 8) |
+		((val & 0x0000ff00) << 8) |
+		((val & 0x000000ff) << 24));
+}
+
+#undef bswap32
+
+GIT_INLINE(uint16_t) default_swab16(uint16_t val)
+{
+	return (((val & 0xff00) >> 8) |
+		((val & 0x00ff) << 8));
+}
+
+#undef bswap16
+
+#if defined(__GNUC__) && defined(__i386__)
+
+#define bswap32(x) ({ \
+	uint32_t __res; \
+	if (__builtin_constant_p(x)) { \
+		__res = default_swab32(x); \
+	} else { \
+		__asm__("bswap %0" : "=r" (__res) : "0" ((uint32_t)(x))); \
+	} \
+	__res; })
+
+#define bswap16(x) ({ \
+	uint16_t __res; \
+	if (__builtin_constant_p(x)) { \
+		__res = default_swab16(x); \
+	} else { \
+		__asm__("xchgb %b0,%h0" : "=q" (__res) : "0" ((uint16_t)(x))); \
+	} \
+	__res; })
+
+#elif defined(__GNUC__) && defined(__x86_64__)
+
+#define bswap32(x) ({ \
+	uint32_t __res; \
+	if (__builtin_constant_p(x)) { \
+		__res = default_swab32(x); \
+	} else { \
+		__asm__("bswapl %0" : "=r" (__res) : "0" ((uint32_t)(x))); \
+	} \
+	__res; })
+
+#define bswap16(x) ({ \
+	uint16_t __res; \
+	if (__builtin_constant_p(x)) { \
+		__res = default_swab16(x); \
+	} else { \
+		__asm__("xchgb %b0,%h0" : "=Q" (__res) : "0" ((uint16_t)(x))); \
+	} \
+	__res; })
+
+#elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
+
+#include <stdlib.h>
+
+#define bswap32(x) _byteswap_ulong(x)
+#define bswap16(x) _byteswap_ushort(x)
+
+#endif
+
+#ifdef bswap32
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
+
+#ifdef bswap16
+
+#undef ntohs
+#undef htons
+#define ntohs(x) bswap16(x)
+#define htons(x) bswap16(x)
+
+#endif
diff --git a/libgit2/src/buffer.c b/libgit2/src/buffer.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/buffer.c
@@ -0,0 +1,461 @@
+/*
+ * 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 "buffer.h"
+#include "posix.h"
+#include <stdarg.h>
+#include <ctype.h>
+
+/* Used as default value for git_buf->ptr so that people can always
+ * assume ptr is non-NULL and zero terminated even for new git_bufs.
+ */
+char git_buf__initbuf[1];
+
+char git_buf__oom[1];
+
+#define ENSURE_SIZE(b, d) \
+	if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
+		return -1;
+
+
+void git_buf_init(git_buf *buf, size_t initial_size)
+{
+	buf->asize = 0;
+	buf->size = 0;
+	buf->ptr = git_buf__initbuf;
+
+	if (initial_size)
+		git_buf_grow(buf, initial_size);
+}
+
+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)
+{
+	char *new_ptr;
+	size_t new_size;
+
+	if (buf->ptr == git_buf__oom)
+		return -1;
+
+	if (target_size <= buf->asize)
+		return 0;
+
+	if (buf->asize == 0) {
+		new_size = target_size;
+		new_ptr = NULL;
+	} else {
+		new_size = buf->asize;
+		new_ptr = buf->ptr;
+	}
+
+	/* grow the buffer size by 1.5, until it's big enough
+	 * to fit our target size */
+	while (new_size < target_size)
+		new_size = (new_size << 1) - (new_size >> 1);
+
+	/* round allocation up to multiple of 8 */
+	new_size = (new_size + 7) & ~7;
+
+	new_ptr = git__realloc(new_ptr, new_size);
+	if (!new_ptr)
+		return -1;
+
+	buf->asize = new_size;
+	buf->ptr   = new_ptr;
+
+	/* truncate the existing buffer size if necessary */
+	if (buf->size >= buf->asize)
+		buf->size = buf->asize - 1;
+	buf->ptr[buf->size] = '\0';
+
+	return 0;
+}
+
+void git_buf_free(git_buf *buf)
+{
+	if (!buf) return;
+
+	if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
+		git__free(buf->ptr);
+
+	git_buf_init(buf, 0);
+}
+
+void git_buf_clear(git_buf *buf)
+{
+	buf->size = 0;
+	if (buf->asize > 0)
+		buf->ptr[0] = '\0';
+}
+
+int git_buf_set(git_buf *buf, const char *data, size_t len)
+{
+	if (len == 0 || data == NULL) {
+		git_buf_clear(buf);
+	} else {
+		if (data != buf->ptr) {
+			ENSURE_SIZE(buf, len + 1);
+			memmove(buf->ptr, data, len);
+		}
+		buf->size = len;
+		buf->ptr[buf->size] = '\0';
+	}
+	return 0;
+}
+
+int git_buf_sets(git_buf *buf, const char *string)
+{
+	return git_buf_set(buf, string, string ? strlen(string) : 0);
+}
+
+int git_buf_putc(git_buf *buf, char c)
+{
+	ENSURE_SIZE(buf, buf->size + 2);
+	buf->ptr[buf->size++] = c;
+	buf->ptr[buf->size] = '\0';
+	return 0;
+}
+
+int git_buf_put(git_buf *buf, const char *data, size_t len)
+{
+	ENSURE_SIZE(buf, buf->size + len + 1);
+	memmove(buf->ptr + buf->size, data, len);
+	buf->size += len;
+	buf->ptr[buf->size] = '\0';
+	return 0;
+}
+
+int git_buf_puts(git_buf *buf, const char *string)
+{
+	assert(string);
+	return git_buf_put(buf, string, strlen(string));
+}
+
+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
+{
+	int len;
+
+	ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
+
+	while (1) {
+		va_list args;
+		va_copy(args, ap);
+
+		len = p_vsnprintf(
+			buf->ptr + buf->size,
+			buf->asize - buf->size,
+			format, args
+		);
+
+		if (len < 0) {
+			git__free(buf->ptr);
+			buf->ptr = git_buf__oom;
+			return -1;
+		}
+
+		if ((size_t)len + 1 <= buf->asize - buf->size) {
+			buf->size += len;
+			break;
+		}
+
+		ENSURE_SIZE(buf, buf->size + len + 1);
+	}
+
+	return 0;
+}
+
+int git_buf_printf(git_buf *buf, const char *format, ...)
+{
+	int r;
+	va_list ap;
+
+	va_start(ap, format);
+	r = git_buf_vprintf(buf, format, ap);
+	va_end(ap);
+
+	return r;
+}
+
+void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
+{
+	size_t copylen;
+
+	assert(data && datasize && buf);
+
+	data[0] = '\0';
+
+	if (buf->size == 0 || buf->asize <= 0)
+		return;
+
+	copylen = buf->size;
+	if (copylen > datasize - 1)
+		copylen = datasize - 1;
+	memmove(data, buf->ptr, copylen);
+	data[copylen] = '\0';
+}
+
+void git_buf_consume(git_buf *buf, const char *end)
+{
+	if (end > buf->ptr && end <= buf->ptr + buf->size) {
+		size_t consumed = end - buf->ptr;
+		memmove(buf->ptr, end, buf->size - consumed);
+		buf->size -= consumed;
+		buf->ptr[buf->size] = '\0';
+	}
+}
+
+void git_buf_truncate(git_buf *buf, size_t len)
+{
+	if (len < buf->size) {
+		buf->size = len;
+		buf->ptr[buf->size] = '\0';
+	}
+}
+
+void git_buf_rtruncate_at_char(git_buf *buf, char separator)
+{
+	ssize_t idx = git_buf_rfind_next(buf, separator);
+	git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
+}
+
+void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
+{
+	git_buf t = *buf_a;
+	*buf_a = *buf_b;
+	*buf_b = t;
+}
+
+char *git_buf_detach(git_buf *buf)
+{
+	char *data = buf->ptr;
+
+	if (buf->asize == 0 || buf->ptr == git_buf__oom)
+		return NULL;
+
+	git_buf_init(buf, 0);
+
+	return data;
+}
+
+void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
+{
+	git_buf_free(buf);
+
+	if (ptr) {
+		buf->ptr = ptr;
+		buf->size = strlen(ptr);
+		if (asize)
+			buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
+		else /* pass 0 to fall back on strlen + 1 */
+			buf->asize = buf->size + 1;
+	} else {
+		git_buf_grow(buf, asize);
+	}
+}
+
+int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
+{
+	va_list ap;
+	int i;
+	size_t total_size = 0, original_size = buf->size;
+	char *out, *original = buf->ptr;
+
+	if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
+		++total_size; /* space for initial separator */
+
+	/* Make two passes to avoid multiple reallocation */
+
+	va_start(ap, nbuf);
+	for (i = 0; i < nbuf; ++i) {
+		const char* segment;
+		size_t segment_len;
+
+		segment = va_arg(ap, const char *);
+		if (!segment)
+			continue;
+
+		segment_len = strlen(segment);
+		total_size += segment_len;
+		if (segment_len == 0 || segment[segment_len - 1] != separator)
+			++total_size; /* space for separator */
+	}
+	va_end(ap);
+
+	/* expand buffer if needed */
+	if (total_size == 0)
+		return 0;
+	if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
+		return -1;
+
+	out = buf->ptr + buf->size;
+
+	/* append separator to existing buf if needed */
+	if (buf->size > 0 && out[-1] != separator)
+		*out++ = separator;
+
+	va_start(ap, nbuf);
+	for (i = 0; i < nbuf; ++i) {
+		const char* segment;
+		size_t segment_len;
+
+		segment = va_arg(ap, const char *);
+		if (!segment)
+			continue;
+
+		/* deal with join that references buffer's original content */
+		if (segment >= original && segment < original + original_size) {
+			size_t offset = (segment - original);
+			segment = buf->ptr + offset;
+			segment_len = original_size - offset;
+		} else {
+			segment_len = strlen(segment);
+		}
+
+		/* skip leading separators */
+		if (out > buf->ptr && out[-1] == separator)
+			while (segment_len > 0 && *segment == separator) {
+				segment++;
+				segment_len--;
+			}
+
+		/* copy over next buffer */
+		if (segment_len > 0) {
+			memmove(out, segment, segment_len);
+			out += segment_len;
+		}
+
+		/* append trailing separator (except for last item) */
+		if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
+			*out++ = separator;
+	}
+	va_end(ap);
+
+	/* set size based on num characters actually written */
+	buf->size = out - buf->ptr;
+	buf->ptr[buf->size] = '\0';
+
+	return 0;
+}
+
+int git_buf_join(
+	git_buf *buf,
+	char separator,
+	const char *str_a,
+	const char *str_b)
+{
+	size_t strlen_a = str_a ? strlen(str_a) : 0;
+	size_t strlen_b = strlen(str_b);
+	int need_sep = 0;
+	ssize_t offset_a = -1;
+
+	/* not safe to have str_b point internally to the buffer */
+	assert(str_b < buf->ptr || str_b > buf->ptr + buf->size);
+
+	/* figure out if we need to insert a separator */
+	if (separator && strlen_a) {
+		while (*str_b == separator) { str_b++; strlen_b--; }
+		if (str_a[strlen_a - 1] != separator)
+			need_sep = 1;
+	}
+
+	/* str_a could be part of the buffer */
+	if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
+		offset_a = str_a - buf->ptr;
+
+	if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
+		return -1;
+
+	/* fix up internal pointers */
+	if (offset_a >= 0)
+		str_a = buf->ptr + offset_a;
+
+	/* do the actual copying */
+	if (offset_a != 0)
+		memmove(buf->ptr, str_a, strlen_a);
+	if (need_sep)
+		buf->ptr[strlen_a] = separator;
+	memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
+
+	buf->size = strlen_a + strlen_b + need_sep;
+	buf->ptr[buf->size] = '\0';
+
+	return 0;
+}
+
+void git_buf_rtrim(git_buf *buf)
+{
+	while (buf->size > 0) {
+		if (!git__isspace(buf->ptr[buf->size - 1]))
+			break;
+
+		buf->size--;
+	}
+
+	buf->ptr[buf->size] = '\0';
+}
+
+int git_buf_cmp(const git_buf *a, const git_buf *b)
+{
+	int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
+	return (result != 0) ? result :
+		(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
+}
+
+int git_buf_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_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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/buffer.h
@@ -0,0 +1,135 @@
+/*
+ * 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_buffer_h__
+#define INCLUDE_buffer_h__
+
+#include "common.h"
+#include <stdarg.h>
+
+typedef struct {
+	char *ptr;
+	size_t asize, size;
+} git_buf;
+
+extern char git_buf__initbuf[];
+extern char git_buf__oom[];
+
+#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
+
+/**
+ * Initialize a git_buf structure.
+ *
+ * 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);
+
+/**
+ * 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
+ */
+int git_buf_grow(git_buf *buf, size_t target_size);
+
+/**
+ * Attempt to 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.
+ */
+int git_buf_try_grow(git_buf *buf, size_t target_size);
+
+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);
+
+/**
+ * Test if there have been any reallocation failures with this git_buf.
+ *
+ * Any function that writes to a git_buf can fail due to memory allocation
+ * issues.  If one fails, the git_buf will be marked with an OOM error and
+ * further calls to modify the buffer will fail.  Check git_buf_oom() at the
+ * end of your sequence and it will be true if you ran out of memory at any
+ * point with that buffer.
+ *
+ * @return false if no error, true if allocation error
+ */
+GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
+{
+	return (buf->ptr == git_buf__oom);
+}
+
+/*
+ * Functions below that return int value error codes will return 0 on
+ * success or -1 on failure (which generally means an allocation failed).
+ * Using a git_buf where the allocation has failed with result in -1 from
+ * all further calls using that buffer.  As a result, you can ignore the
+ * return code of these functions and call them in a series then just call
+ * git_buf_oom at the end.
+ */
+int git_buf_set(git_buf *buf, const char *data, size_t len);
+int git_buf_sets(git_buf *buf, const char *string);
+int git_buf_putc(git_buf *buf, char c);
+int git_buf_put(git_buf *buf, const char *data, size_t len);
+int git_buf_puts(git_buf *buf, const char *string);
+int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
+void git_buf_clear(git_buf *buf);
+void git_buf_consume(git_buf *buf, const char *end);
+void git_buf_truncate(git_buf *buf, size_t len);
+void git_buf_rtruncate_at_char(git_buf *path, char separator);
+
+int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
+int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
+
+/**
+ * Join two strings as paths, inserting a slash between as needed.
+ * @return 0 on success, -1 on failure
+ */
+GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
+{
+	return git_buf_join(buf, '/', a, b);
+}
+
+GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
+{
+	return buf->ptr;
+}
+
+GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
+{
+	return buf->size;
+}
+
+void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
+
+#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)
+{
+	ssize_t idx = (ssize_t)buf->size - 1;
+	while (idx >= 0 && buf->ptr[idx] == ch) idx--;
+	while (idx >= 0 && buf->ptr[idx] != ch) idx--;
+	return idx;
+}
+
+/* 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);
+
+/* Check if buffer looks like it contains binary data */
+bool git_buf_is_binary(const git_buf *buf);
+
+#endif
diff --git a/libgit2/src/cache.c b/libgit2/src/cache.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/cache.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "repository.h"
+#include "commit.h"
+#include "thread-utils.h"
+#include "util.h"
+#include "cache.h"
+
+int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
+{
+	if (size < 8)
+		size = 8;
+	size = git__size_t_powerof2(size);
+
+	cache->size_mask = size - 1;
+	cache->lru_count = 0;
+	cache->free_obj = free_ptr;
+
+	git_mutex_init(&cache->lock);
+
+	cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
+	GITERR_CHECK_ALLOC(cache->nodes);
+
+	memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
+	return 0;
+}
+
+void git_cache_free(git_cache *cache)
+{
+	size_t i;
+
+	for (i = 0; i < (cache->size_mask + 1); ++i) {
+		if (cache->nodes[i] != NULL)
+			git_cached_obj_decref(cache->nodes[i], cache->free_obj);
+	}
+
+	git__free(cache->nodes);
+}
+
+void *git_cache_get(git_cache *cache, const git_oid *oid)
+{
+	uint32_t hash;
+	git_cached_obj *node = NULL, *result = NULL;
+
+	memcpy(&hash, oid->id, sizeof(hash));
+
+	git_mutex_lock(&cache->lock);
+	{
+		node = cache->nodes[hash & cache->size_mask];
+
+		if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
+			git_cached_obj_incref(node);
+			result = node;
+		}
+	}
+	git_mutex_unlock(&cache->lock);
+
+	return result;
+}
+
+void *git_cache_try_store(git_cache *cache, void *_entry)
+{
+	git_cached_obj *entry = _entry;
+	uint32_t hash;
+
+	memcpy(&hash, &entry->oid, sizeof(hash));
+
+	/* increase the refcount on this object, because
+	 * the cache now owns it */
+	git_cached_obj_incref(entry);
+
+	git_mutex_lock(&cache->lock);
+	{
+		git_cached_obj *node = cache->nodes[hash & cache->size_mask];
+
+		if (node == NULL) {
+			cache->nodes[hash & cache->size_mask] = entry;
+		} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
+			git_cached_obj_decref(entry, cache->free_obj);
+			entry = node;
+		} else {
+			git_cached_obj_decref(node, cache->free_obj);
+			cache->nodes[hash & cache->size_mask] = 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/cache.h
@@ -0,0 +1,54 @@
+/*
+ * 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_cache_h__
+#define INCLUDE_cache_h__
+
+#include "git2/common.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+
+#include "thread-utils.h"
+
+#define GIT_DEFAULT_CACHE_SIZE 128
+
+typedef void (*git_cached_obj_freeptr)(void *);
+
+typedef struct {
+	git_oid oid;
+	git_atomic refcount;
+} git_cached_obj;
+
+typedef struct {
+	git_cached_obj **nodes;
+	git_mutex lock;
+
+	unsigned int lru_count;
+	size_t size_mask;
+	git_cached_obj_freeptr free_obj;
+} git_cache;
+
+int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
+void git_cache_free(git_cache *cache);
+
+void *git_cache_try_store(git_cache *cache, void *entry);
+void *git_cache_get(git_cache *cache, const git_oid *oid);
+
+GIT_INLINE(void) git_cached_obj_incref(void *_obj)
+{
+	git_cached_obj *obj = _obj;
+	git_atomic_inc(&obj->refcount);
+}
+
+GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj)
+{
+	git_cached_obj *obj = _obj;
+
+	if (git_atomic_dec(&obj->refcount) == 0)
+		free_obj(obj);
+}
+
+#endif
diff --git a/libgit2/src/cc-compat.h b/libgit2/src/cc-compat.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/cc-compat.h
@@ -0,0 +1,69 @@
+/*
+ * 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_compat_h__
+#define INCLUDE_compat_h__
+
+/*
+ * See if our compiler is known to support flexible array members.
+ */
+#ifndef GIT_FLEX_ARRAY
+#	if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#		define GIT_FLEX_ARRAY /* empty */
+#	elif defined(__GNUC__)
+#		if (__GNUC__ >= 3)
+#			define GIT_FLEX_ARRAY /* empty */
+#		else
+#			define GIT_FLEX_ARRAY 0 /* older GNU extension */
+#		endif
+#	endif
+
+/* Default to safer but a bit wasteful traditional style */
+#	ifndef GIT_FLEX_ARRAY
+#		define GIT_FLEX_ARRAY 1
+#	endif
+#endif
+
+#ifdef __GNUC__
+#	define GIT_TYPEOF(x) (__typeof__(x))
+#else
+#	define GIT_TYPEOF(x)
+#endif
+
+#define GIT_UNUSED(x) ((void)(x))
+
+/* Define the printf format specifer to use for size_t output */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#	define PRIuZ "Iu"
+#else
+#	define PRIuZ "zu"
+#endif
+
+/* Micosoft Visual C/C++ */
+#if defined(_MSC_VER)
+/* disable "deprecated function" warnings */
+#	pragma warning ( disable : 4996 )
+/* disable "conditional expression is constant" level 4 warnings */
+#	pragma warning ( disable : 4127 )
+#endif
+
+#if defined (_MSC_VER)
+	typedef unsigned char bool;
+#	define true 1
+#	define false 0
+#else
+#	include <stdbool.h>
+#endif
+
+#ifndef va_copy
+#	ifdef __va_copy
+#		define va_copy(dst, src) __va_copy(dst, src)
+#	else
+#		define va_copy(dst, src) ((dst) = (src))
+#	endif
+#endif
+
+#endif /* INCLUDE_compat_h__ */
diff --git a/libgit2/src/commit.c b/libgit2/src/commit.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/commit.c
@@ -0,0 +1,334 @@
+/*
+ * 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/common.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+#include "git2/signature.h"
+
+#include "common.h"
+#include "odb.h"
+#include "commit.h"
+#include "signature.h"
+#include "message.h"
+
+#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;
+
+	for (i = 0; i < commit->parent_oids.length; ++i) {
+		git_oid *parent = git_vector_get(&commit->parent_oids, i);
+		git__free(parent);
+	}
+
+	git_vector_clear(&commit->parent_oids);
+}
+
+void git_commit__free(git_commit *commit)
+{
+	clear_parents(commit);
+	git_vector_free(&commit->parent_oids);
+
+	git_signature_free(commit->author);
+	git_signature_free(commit->committer);
+
+	git__free(commit->message);
+	git__free(commit->message_encoding);
+	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,
+		const char *update_ref,
+		const git_signature *author,
+		const git_signature *committer,
+		const char *message_encoding,
+		const char *message,
+		const git_tree *tree,
+		int parent_count,
+		...)
+{
+	va_list ap;
+	int i, res;
+	const git_commit **parents;
+
+	parents = git__malloc(parent_count * sizeof(git_commit *));
+	GITERR_CHECK_ALLOC(parents);
+
+	va_start(ap, parent_count);
+	for (i = 0; i < parent_count; ++i)
+		parents[i] = va_arg(ap, const git_commit *);
+	va_end(ap);
+
+	res = git_commit_create(
+		oid, repo, update_ref, author, committer,
+		message_encoding, message,
+		tree, parent_count, parents);
+
+	git__free((void *)parents);
+	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(
+		git_oid *oid,
+		git_repository *repo,
+		const char *update_ref,
+		const git_signature *author,
+		const git_signature *committer,
+		const char *message_encoding,
+		const char *message,
+		const git_tree *tree,
+		int parent_count,
+		const git_commit *parents[])
+{
+	git_buf commit = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
+	int i;
+	git_odb *odb;
+
+	assert(git_object_owner((const git_object *)tree) == repo);
+
+	git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
+
+	for (i = 0; i < parent_count; ++i) {
+		assert(git_object_owner((const git_object *)parents[i]) == repo);
+		git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
+	}
+
+	git_signature__writebuf(&commit, "author ", author);
+	git_signature__writebuf(&commit, "committer ", committer);
+
+	if (message_encoding != NULL)
+		git_buf_printf(&commit, "encoding %s\n", message_encoding);
+
+	git_buf_putc(&commit, '\n');
+
+	/* 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);
+
+	if (git_repository_odb__weakptr(&odb, repo) < 0)
+		goto on_error;
+
+	if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
+		goto on_error;
+
+	git_buf_free(&commit);
+
+	if (update_ref != NULL)
+		return update_reference(repo, oid, update_ref);
+
+	return 0;
+
+on_error:
+	git_buf_free(&commit);
+	git_buf_free(&cleaned_message);
+	giterr_set(GITERR_OBJECT, "Failed to create commit.");
+	return -1;
+}
+
+int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
+{
+	const char *buffer = data;
+	const char *buffer_end = (const char *)data + len;
+
+	git_oid parent_oid;
+
+	git_vector_init(&commit->parent_oids, 4, NULL);
+
+	if (git_oid__parse(&commit->tree_oid, &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);
+
+		git_oid_cpy(new_oid, &parent_oid);
+
+		if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
+			return -1;
+	}
+
+	commit->author = git__malloc(sizeof(git_signature));
+	GITERR_CHECK_ALLOC(commit->author);
+
+	if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
+		return -1;
+
+	/* Always parse the committer; we need the commit time */
+	commit->committer = git__malloc(sizeof(git_signature));
+	GITERR_CHECK_ALLOC(commit->committer);
+
+	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 ");
+
+		encoding_end = buffer;
+		while (encoding_end < buffer_end && *encoding_end != '\n')
+			encoding_end++;
+
+		commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
+		GITERR_CHECK_ALLOC(commit->message_encoding);
+
+		buffer = encoding_end;
+	}
+
+	/* parse commit message */
+	while (buffer < buffer_end - 1 && *buffer == '\n')
+		buffer++;
+
+	if (buffer <= buffer_end) {
+		commit->message = git__strndup(buffer, buffer_end - buffer);
+		GITERR_CHECK_ALLOC(commit->message);
+	}
+
+	return 0;
+
+bad_buffer:
+	giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
+	return -1;
+}
+
+int git_commit__parse(git_commit *commit, git_odb_object *obj)
+{
+	assert(commit);
+	return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len);
+}
+
+#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
+	_rvalue git_commit_##_name(git_commit *commit) \
+	{\
+		assert(commit); \
+		return _return; \
+	}
+
+GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
+GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
+GIT_COMMIT_GETTER(const char *, message, commit->message)
+GIT_COMMIT_GETTER(const char *, message_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);
+
+
+int git_commit_tree(git_tree **tree_out, git_commit *commit)
+{
+	assert(commit);
+	return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_oid);
+}
+
+int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
+{
+	git_oid *parent_oid;
+	assert(commit);
+
+	parent_oid = git_vector_get(&commit->parent_oids, n);
+	if (parent_oid == NULL) {
+		giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
+		return GIT_ENOTFOUND;
+	}
+
+	return git_commit_lookup(parent, commit->object.repo, parent_oid);
+}
+
+const git_oid *git_commit_parent_oid(git_commit *commit, unsigned int n)
+{
+	assert(commit);
+
+	return git_vector_get(&commit->parent_oids, n);
+}
diff --git a/libgit2/src/commit.h b/libgit2/src/commit.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/commit.h
@@ -0,0 +1,34 @@
+/*
+ * 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_commit_h__
+#define INCLUDE_commit_h__
+
+#include "git2/commit.h"
+#include "tree.h"
+#include "repository.h"
+#include "vector.h"
+
+#include <time.h>
+
+struct git_commit {
+	git_object object;
+
+	git_vector parent_oids;
+	git_oid tree_oid;
+
+	git_signature *author;
+	git_signature *committer;
+
+	char *message_encoding;
+	char *message;
+};
+
+void git_commit__free(git_commit *c);
+int git_commit__parse(git_commit *commit, git_odb_object *obj);
+
+int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len);
+#endif
diff --git a/libgit2/src/common.h b/libgit2/src/common.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/common.h
@@ -0,0 +1,72 @@
+/*
+ * 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_common_h__
+#define INCLUDE_common_h__
+
+#include "git2/common.h"
+#include "cc-compat.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>
+
+#ifdef GIT_WIN32
+
+# include <io.h>
+# include <direct.h>
+# include <windows.h>
+# include "win32/msvc-compat.h"
+# include "win32/mingw-compat.h"
+# ifdef GIT_THREADS
+#	include "win32/pthread.h"
+#endif
+
+# define snprintf _snprintf
+
+#else
+# include <unistd.h>
+
+# ifdef GIT_THREADS
+#	include <pthread.h>
+# endif
+#endif
+
+#include "git2/types.h"
+#include "git2/errors.h"
+#include "thread-utils.h"
+#include "bswap.h"
+
+#include <regex.h>
+
+extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
+#define git__throw(error, ...) \
+	(git___throw(__VA_ARGS__), error)
+
+extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
+#define git__rethrow(error, ...) \
+	(git___rethrow(__VA_ARGS__), error)
+
+
+#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
+
+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);
+
+
+#include "util.h"
+
+
+#endif /* INCLUDE_common_h__ */
diff --git a/libgit2/src/config.c b/libgit2/src/config.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/config.c
@@ -0,0 +1,508 @@
+/*
+ * 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 "fileops.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#if GIT_WIN32
+# include <windows.h>
+#endif
+
+#include <ctype.h>
+
+typedef struct {
+	git_config_file *file;
+	int priority;
+} file_internal;
+
+static void config_free(git_config *cfg)
+{
+	unsigned int i;
+	git_config_file *file;
+	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_vector_free(&cfg->files);
+	git__free(cfg);
+}
+
+void git_config_free(git_config *cfg)
+{
+	if (cfg == NULL)
+		return;
+
+	GIT_REFCOUNT_DEC(cfg, config_free);
+}
+
+static int config_backend_cmp(const void *a, const void *b)
+{
+	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;
+}
+
+int git_config_new(git_config **out)
+{
+	git_config *cfg;
+
+	cfg = git__malloc(sizeof(git_config));
+	GITERR_CHECK_ALLOC(cfg);
+
+	memset(cfg, 0x0, sizeof(git_config));
+
+	if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
+		git__free(cfg);
+		return -1;
+	}
+
+	*out = cfg;
+	GIT_REFCOUNT_INC(cfg);
+	return 0;
+}
+
+int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
+{
+	git_config_file *file = NULL;
+
+	if (git_config_file__ondisk(&file, path) < 0)
+		return -1;
+
+	if (git_config_add_file(cfg, file, priority) < 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 0;
+}
+
+int git_config_open_ondisk(git_config **cfg, const char *path)
+{
+	if (git_config_new(cfg) < 0)
+		return -1;
+
+	if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
+		git_config_free(*cfg);
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
+{
+	file_internal *internal;
+	int result;
+
+	assert(cfg && file);
+
+	if ((result = file->open(file)) < 0)
+		return result;
+
+	internal = git__malloc(sizeof(file_internal));
+	GITERR_CHECK_ALLOC(internal);
+
+	internal->file = file;
+	internal->priority = priority;
+
+	if (git_vector_insert(&cfg->files, internal) < 0) {
+		git__free(internal);
+		return -1;
+	}
+
+	git_vector_sort(&cfg->files);
+	internal->file->cfg = cfg;
+
+	return 0;
+}
+
+/*
+ * Loop over all the variables
+ */
+
+int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
+{
+	int ret = 0;
+	unsigned int i;
+	file_internal *internal;
+	git_config_file *file;
+
+	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);
+	}
+
+	return ret;
+}
+
+int git_config_delete(git_config *cfg, const char *name)
+{
+	file_internal *internal;
+	git_config_file *file;
+
+	assert(cfg->files.length);
+
+	internal = git_vector_get(&cfg->files, 0);
+	file = internal->file;
+
+	return file->del(file, name);
+}
+
+/**************
+ * Setters
+ **************/
+
+int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
+{
+	char str_value[32]; /* All numbers should fit in here */
+	p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
+	return git_config_set_string(cfg, name, str_value);
+}
+
+int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
+{
+	return git_config_set_int64(cfg, name, (int64_t)value);
+}
+
+int git_config_set_bool(git_config *cfg, const char *name, int value)
+{
+	return git_config_set_string(cfg, name, value ? "true" : "false");
+}
+
+int git_config_set_string(git_config *cfg, const char *name, const char *value)
+{
+	file_internal *internal;
+	git_config_file *file;
+
+	assert(cfg->files.length);
+
+	internal = git_vector_get(&cfg->files, 0);
+	file = internal->file;
+
+	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 char *name,
+	git_cvar_map *maps,
+	size_t map_n)
+{
+	const char *value;
+	int ret;
+
+	ret = git_config_get_string(&value, cfg, name);
+	if (ret < 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;
+}
+
+int git_config_get_int64(int64_t *out, git_config *cfg, const char *name)
+{
+	const char *value;
+	int ret;
+
+	ret = git_config_get_string(&value, cfg, name);
+	if (ret < 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;
+}
+
+int git_config_get_int32(int32_t *out, git_config *cfg, const char *name)
+{
+	const char *value;
+	int ret;
+
+	ret = git_config_get_string(&value, cfg, name);
+	if (ret < 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 0;
+}
+
+int git_config_get_bool(int *out, git_config *cfg, const char *name)
+{
+	const char *value;
+	int ret;
+
+	ret = git_config_get_string(&value, cfg, name);
+	if (ret < 0)
+		return ret;
+
+	if (git__parse_bool(out, value) == 0)
+		return 0;
+
+	if (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_get_string(const char **out, 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;
+		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)
+{
+	file_internal *internal;
+	git_config_file *file;
+	int ret = GIT_ENOTFOUND;
+	unsigned int i;
+
+	assert(cfg->files.length);
+
+	/*
+	 * This loop runs the "wrong" way 'round because we need to
+	 * look at every value from the most general to most specific
+	 */
+	for (i = cfg->files.length; i > 0; --i) {
+		internal = git_vector_get(&cfg->files, i - 1);
+		file = internal->file;
+		ret = file->get_multivar(file, name, regexp, fn, data);
+		if (ret < 0 && ret != GIT_ENOTFOUND)
+			return ret;
+	}
+
+	return 0;
+}
+
+int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+{
+	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;
+	}
+
+	return 0;
+}
+
+int git_config_find_global_r(git_buf *path)
+{
+	return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
+}
+
+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;
+	}
+
+	if (path.size >= length) {
+		git_buf_free(&path);
+		giterr_set(GITERR_NOMEMORY,
+			"Path is to long to fit on the given buffer");
+		return -1;
+	}
+
+	git_buf_copy_cstr(global_config_path, length, &path);
+	git_buf_free(&path);
+	return 0;
+}
+
+int git_config_find_system_r(git_buf *path)
+{
+	return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
+}
+
+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);
+
+	if (ret < 0) {
+		git_buf_free(&path);
+		return ret;
+	}
+
+	if (path.size >= length) {
+		git_buf_free(&path);
+		giterr_set(GITERR_NOMEMORY,
+			"Path is to long to fit on the given buffer");
+		return -1;
+	}
+
+	git_buf_copy_cstr(system_config_path, length, &path);
+	git_buf_free(&path);
+	return 0;
+}
+
+int git_config_open_global(git_config **out)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+
+	if ((error = git_config_find_global_r(&path)) < 0)
+		return error;
+
+	error = git_config_open_ondisk(out, git_buf_cstr(&path));
+	git_buf_free(&path);
+
+	return error;
+}
+
diff --git a/libgit2/src/config.h b/libgit2/src/config.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/config.h
@@ -0,0 +1,33 @@
+/*
+ * 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_config_h__
+#define INCLUDE_config_h__
+
+#include "git2.h"
+#include "git2/config.h"
+#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_FILE_MODE 0666
+
+struct git_config {
+	git_refcount rc;
+	git_vector files;
+};
+
+extern int git_config_find_global_r(git_buf *global_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_lookup_map_value(
+	git_cvar_map *maps, size_t map_n, const char *value, int *out);
+
+#endif
diff --git a/libgit2/src/config_cache.c b/libgit2/src/config_cache.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/config_cache.c
@@ -0,0 +1,94 @@
+/*
+ * 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 "fileops.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "filter.h"
+#include "repository.h"
+
+struct map_data {
+	const char *cvar_name;
+	git_cvar_map *maps;
+	size_t map_count;
+	int default_value;
+};
+
+/*
+ *	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
+ *	value is native. See gitattributes(5) for more information on
+ *	end-of-line conversion. 
+ */
+static git_cvar_map _cvar_map_eol[] = {
+	{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
+	{GIT_CVAR_STRING, "lf", GIT_EOL_LF},
+	{GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
+	{GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
+};
+
+/*
+ *	core.autocrlf
+ *		Setting this variable to "true" is almost the same as setting 
+ *	the text attribute to "auto" on all files except that text files are
+ *	not guaranteed to be normalized: files that contain CRLF in the
+ *	repository will not be touched. Use this setting if you want to have
+ *	CRLF line endings in your working directory even though the repository
+ *	does not have normalized line endings. This variable can be set to input,
+ *	in which case no output conversion is performed.
+ */
+static git_cvar_map _cvar_map_autocrlf[] = {
+	{GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+	{GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+	{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
+};
+
+static struct map_data _cvar_maps[] = {
+	{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
+	{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
+};
+
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
+{
+	*out = repo->cvar_cache[(int)cvar];
+
+	if (*out == GIT_CVAR_NOT_CACHED) {
+		struct map_data *data = &_cvar_maps[(int)cvar];
+		git_config *config;
+		int error;
+
+		error = git_repository_config__weakptr(&config, repo);
+		if (error < 0)
+			return error;
+
+		error = git_config_get_mapped(out,
+			config, data->cvar_name, data->maps, data->map_count);
+
+		if (error == GIT_ENOTFOUND)
+			*out = data->default_value;
+
+		else if (error < 0)
+			return error;
+
+		repo->cvar_cache[(int)cvar] = *out;
+	}
+
+	return 0;
+}
+
+void git_repository__cvar_cache_clear(git_repository *repo)
+{
+	int i;
+
+	for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
+		repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
+}
+
diff --git a/libgit2/src/config_file.c b/libgit2/src/config_file.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/config_file.c
@@ -0,0 +1,1318 @@
+/*
+ * 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 "config.h"
+#include "fileops.h"
+#include "filebuf.h"
+#include "buffer.h"
+#include "git2/config.h"
+#include "git2/types.h"
+#include "strmap.h"
+
+#include <ctype.h>
+#include <sys/types.h>
+#include <regex.h>
+
+GIT__USE_STRMAP;
+
+typedef struct cvar_t {
+	struct cvar_t *next;
+	char *key; /* TODO: we might be able to get rid of this */
+	char *value;
+} 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)
+
+#define CVAR_LIST_NEXT(var) ((var)->next)
+
+#define CVAR_LIST_EMPTY(list) ((list)->head == NULL)
+
+#define CVAR_LIST_APPEND(list, var) do {\
+	if (CVAR_LIST_EMPTY(list)) {\
+		CVAR_LIST_HEAD(list) = CVAR_LIST_TAIL(list) = var;\
+	} else {\
+		CVAR_LIST_NEXT(CVAR_LIST_TAIL(list)) = var;\
+		CVAR_LIST_TAIL(list) = var;\
+	}\
+} while(0)
+
+#define CVAR_LIST_REMOVE_HEAD(list) do {\
+	CVAR_LIST_HEAD(list) = CVAR_LIST_NEXT(CVAR_LIST_HEAD(list));\
+} while(0)
+
+#define CVAR_LIST_REMOVE_AFTER(var) do {\
+	CVAR_LIST_NEXT(var) = CVAR_LIST_NEXT(CVAR_LIST_NEXT(var));\
+} while(0)
+
+#define CVAR_LIST_FOREACH(list, iter)\
+	for ((iter) = CVAR_LIST_HEAD(list);\
+		 (iter) != NULL;\
+		 (iter) = CVAR_LIST_NEXT(iter))
+
+/*
+ * Inspired by the FreeBSD functions
+ */
+#define CVAR_LIST_FOREACH_SAFE(start, iter, tmp)\
+	for ((iter) = CVAR_LIST_HEAD(vars);\
+		 (iter) && (((tmp) = CVAR_LIST_NEXT(iter) || 1));\
+		 (iter) = (tmp))
+
+typedef struct {
+	git_config_file parent;
+
+	git_strmap *values;
+
+	struct {
+		git_buf buffer;
+		char *read_ptr;
+		int line_number;
+		int eof;
+	} reader;
+
+	char *file_path;
+} diskfile_backend;
+
+static int config_parse(diskfile_backend *cfg_file);
+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 void set_parse_error(diskfile_backend *backend, int col, const char *error_str)
+{
+	giterr_set(GITERR_CONFIG, "Failed to parse config file: %s (in %s:%d, column %d)",
+		error_str, backend->file_path, backend->reader.line_number, col);
+}
+
+static void cvar_free(cvar_t *var)
+{
+	if (var == NULL)
+		return;
+
+	git__free(var->key);
+	git__free(var->value);
+	git__free(var);
+}
+
+/* Take something the user gave us and make it nice for our hash function */
+static int normalize_name(const char *in, char **out)
+{
+	char *name, *fdot, *ldot;
+
+	assert(in && out);
+
+	name = git__strdup(in);
+	GITERR_CHECK_ALLOC(name);
+
+	fdot = strchr(name, '.');
+	ldot = strrchr(name, '.');
+
+	if (fdot == NULL || ldot == NULL) {
+		git__free(name);
+		giterr_set(GITERR_CONFIG,
+			"Invalid variable name: '%s'", in);
+		return -1;
+	}
+
+	/* Downcase up to the first dot and after the last one */
+	git__strntolower(name, fdot - name);
+	git__strtolower(ldot);
+
+	*out = name;
+	return 0;
+}
+
+static void free_vars(git_strmap *values)
+{
+	cvar_t *var = NULL;
+
+	if (values == NULL)
+		return;
+
+	git_strmap_foreach_value(values, var,
+		while (var != NULL) {
+			cvar_t *next = CVAR_LIST_NEXT(var);
+			cvar_free(var);
+			var = next;
+		});
+
+	git_strmap_free(values);
+}
+
+static int config_open(git_config_file *cfg)
+{
+	int res;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+
+	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);
+
+	/* It's fine if the file doesn't exist */
+	if (res == GIT_ENOTFOUND)
+		return 0;
+
+	if (res < 0 || config_parse(b) <  0) {
+		free_vars(b->values);
+		b->values = NULL;
+		git_buf_free(&b->reader.buffer);
+		return -1;
+	}
+
+	git_buf_free(&b->reader.buffer);
+	return 0;
+}
+
+static void backend_free(git_config_file *_backend)
+{
+	diskfile_backend *backend = (diskfile_backend *)_backend;
+
+	if (backend == NULL)
+		return;
+
+	git__free(backend->file_path);
+	free_vars(backend->values);
+	git__free(backend);
+}
+
+static int file_foreach(git_config_file *backend, int (*fn)(const char *, const char *, void *), void *data)
+{
+	diskfile_backend *b = (diskfile_backend *)backend;
+	cvar_t *var;
+	const char *key;
+
+	if (!b->values)
+		return 0;
+
+	git_strmap_foreach(b->values, key, var,
+		do {
+			if (fn(key, var->value, data) < 0)
+				break;
+
+			var = CVAR_LIST_NEXT(var);
+		} while (var != NULL);
+	);
+
+	return 0;
+}
+
+static int config_set(git_config_file *cfg, const char *name, const char *value)
+{
+	cvar_t *var = NULL, *old_var;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	khiter_t pos;
+	int rval;
+
+	if (normalize_name(name, &key) < 0)
+		return -1;
+
+	/*
+	 * Try to find it in the existing values and update it if it
+	 * only has one value.
+	 */
+	pos = git_strmap_lookup_index(b->values, key);
+	if (git_strmap_valid_index(b->values, pos)) {
+		cvar_t *existing = git_strmap_value_at(b->values, pos);
+		char *tmp = NULL;
+
+		git__free(key);
+		if (existing->next != NULL) {
+			giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set");
+			return -1;
+		}
+
+		if (value) {
+			tmp = git__strdup(value);
+			GITERR_CHECK_ALLOC(tmp);
+		}
+
+		git__free(existing->value);
+		existing->value = tmp;
+
+		return config_write(b, existing->key, NULL, value);
+	}
+
+	var = git__malloc(sizeof(cvar_t));
+	GITERR_CHECK_ALLOC(var);
+
+	memset(var, 0x0, sizeof(cvar_t));
+
+	var->key = key;
+	var->value = NULL;
+
+	if (value) {
+		var->value = git__strdup(value);
+		GITERR_CHECK_ALLOC(var->value);
+	}
+
+	if (config_write(b, key, NULL, value) < 0) {
+		cvar_free(var);
+		return -1;
+	}
+
+	git_strmap_insert2(b->values, key, var, old_var, rval);
+	if (rval < 0)
+		return -1;
+	if (old_var != NULL)
+		cvar_free(old_var);
+
+	return 0;
+}
+
+/*
+ * Internal function that actually gets the value in string form
+ */
+static int config_get(git_config_file *cfg, const char *name, const char **out)
+{
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	khiter_t pos;
+
+	if (normalize_name(name, &key) < 0)
+		return -1;
+
+	pos = git_strmap_lookup_index(b->values, key);
+	git__free(key);
+
+	/* no error message; the config system will write one */
+	if (!git_strmap_valid_index(b->values, pos))
+		return GIT_ENOTFOUND;
+
+	*out = ((cvar_t *)git_strmap_value_at(b->values, pos))->value;
+
+	return 0;
+}
+
+static int config_get_multivar(
+	git_config_file *cfg,
+	const char *name,
+	const char *regex_str,
+	int (*fn)(const char *, void *),
+	void *data)
+{
+	cvar_t *var;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	khiter_t pos;
+
+	if (normalize_name(name, &key) < 0)
+		return -1;
+
+	pos = git_strmap_lookup_index(b->values, key);
+	git__free(key);
+
+	if (!git_strmap_valid_index(b->values, pos))
+		return GIT_ENOTFOUND;
+
+	var = git_strmap_value_at(b->values, pos);
+
+	if (regex_str != NULL) {
+		regex_t regex;
+		int result;
+
+		/* regex matching; build the regex */
+		result = regcomp(&regex, regex_str, REG_EXTENDED);
+		if (result < 0) {
+			giterr_set_regex(&regex, result);
+			return -1;
+		}
+
+		/* and throw the callback only on the variables that
+		 * match the regex */
+		do {
+			if (regexec(&regex, var->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)
+					break;
+			}
+
+			var = var->next;
+		} while (var != NULL);
+		regfree(&regex);
+	} else {
+		/* no regex; go through all the variables */
+		do {
+			/* early termination by the user is not an error;
+			 * just break and return successfully */
+			if (fn(var->value, data) < 0)
+				break;
+
+			var = var->next;
+		} while (var != NULL);
+	}
+
+	return 0;
+}
+
+static int config_set_multivar(
+	git_config_file *cfg, const char *name, const char *regexp, const char *value)
+{
+	int replaced = 0;
+	cvar_t *var, *newvar;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	regex_t preg;
+	int result;
+	khiter_t pos;
+
+	assert(regexp);
+
+	if (normalize_name(name, &key) < 0)
+		return -1;
+
+	pos = git_strmap_lookup_index(b->values, key);
+	if (!git_strmap_valid_index(b->values, pos)) {
+		git__free(key);
+		return GIT_ENOTFOUND;
+	}
+
+	var = git_strmap_value_at(b->values, pos);
+
+	result = regcomp(&preg, regexp, REG_EXTENDED);
+	if (result < 0) {
+		git__free(key);
+		giterr_set_regex(&preg, result);
+		return -1;
+	}
+
+	for (;;) {
+		if (regexec(&preg, var->value, 0, NULL, 0) == 0) {
+			char *tmp = git__strdup(value);
+			GITERR_CHECK_ALLOC(tmp);
+
+			git__free(var->value);
+			var->value = tmp;
+			replaced = 1;
+		}
+
+		if (var->next == NULL)
+			break;
+
+		var = var->next;
+	}
+
+	/* If we've reached the end of the variables and we haven't found it yet, we need to append it */
+	if (!replaced) {
+		newvar = git__malloc(sizeof(cvar_t));
+		GITERR_CHECK_ALLOC(newvar);
+
+		memset(newvar, 0x0, sizeof(cvar_t));
+
+		newvar->key = git__strdup(var->key);
+		GITERR_CHECK_ALLOC(newvar->key);
+
+		newvar->value = git__strdup(value);
+		GITERR_CHECK_ALLOC(newvar->value);
+
+		var->next = newvar;
+	}
+
+	result = config_write(b, key, &preg, value);
+
+	git__free(key);
+	regfree(&preg);
+
+	return result;
+}
+
+static int config_delete(git_config_file *cfg, const char *name)
+{
+	cvar_t *var;
+	diskfile_backend *b = (diskfile_backend *)cfg;
+	char *key;
+	int result;
+	khiter_t pos;
+
+	if (normalize_name(name, &key) < 0)
+		return -1;
+
+	pos = git_strmap_lookup_index(b->values, key);
+	git__free(key);
+
+	if (!git_strmap_valid_index(b->values, pos))
+		return GIT_ENOTFOUND;
+
+	var = git_strmap_value_at(b->values, pos);
+
+	if (var->next != NULL) {
+		giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete");
+		return -1;
+	}
+
+	git_strmap_delete_at(b->values, pos);
+
+	result = config_write(b, var->key, NULL, NULL);
+
+	cvar_free(var);
+	return result;
+}
+
+int git_config_file__ondisk(git_config_file **out, const char *path)
+{
+	diskfile_backend *backend;
+
+	backend = git__malloc(sizeof(diskfile_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	memset(backend, 0x0, sizeof(diskfile_backend));
+
+	backend->file_path = git__strdup(path);
+	GITERR_CHECK_ALLOC(backend->file_path);
+
+	backend->parent.open = config_open;
+	backend->parent.get = config_get;
+	backend->parent.get_multivar = config_get_multivar;
+	backend->parent.set = config_set;
+	backend->parent.set_multivar = config_set_multivar;
+	backend->parent.del = config_delete;
+	backend->parent.foreach = file_foreach;
+	backend->parent.free = backend_free;
+
+	*out = (git_config_file *)backend;
+
+	return 0;
+}
+
+static int cfg_getchar_raw(diskfile_backend *cfg)
+{
+	int c;
+
+	c = *cfg->reader.read_ptr++;
+
+	/*
+	Win 32 line breaks: if we find a \r\n sequence,
+	return only the \n as a newline
+	*/
+	if (c == '\r' && *cfg->reader.read_ptr == '\n') {
+		cfg->reader.read_ptr++;
+		c = '\n';
+	}
+
+	if (c == '\n')
+		cfg->reader.line_number++;
+
+	if (c == 0) {
+		cfg->reader.eof = 1;
+		c = '\n';
+	}
+
+	return c;
+}
+
+#define SKIP_WHITESPACE (1 << 1)
+#define SKIP_COMMENTS (1 << 2)
+
+static int cfg_getchar(diskfile_backend *cfg_file, int flags)
+{
+	const int skip_whitespace = (flags & SKIP_WHITESPACE);
+	const int skip_comments = (flags & SKIP_COMMENTS);
+	int c;
+
+	assert(cfg_file->reader.read_ptr);
+
+	do c = cfg_getchar_raw(cfg_file);
+	while (skip_whitespace && git__isspace(c) &&
+	       !cfg_file->reader.eof);
+
+	if (skip_comments && (c == '#' || c == ';')) {
+		do c = cfg_getchar_raw(cfg_file);
+		while (c != '\n');
+	}
+
+	return c;
+}
+
+/*
+ * Read the next char, but don't move the reading pointer.
+ */
+static int cfg_peek(diskfile_backend *cfg, int flags)
+{
+	void *old_read_ptr;
+	int old_lineno, old_eof;
+	int ret;
+
+	assert(cfg->reader.read_ptr);
+
+	old_read_ptr = cfg->reader.read_ptr;
+	old_lineno = cfg->reader.line_number;
+	old_eof = cfg->reader.eof;
+
+	ret = cfg_getchar(cfg, flags);
+
+	cfg->reader.read_ptr = old_read_ptr;
+	cfg->reader.line_number = old_lineno;
+	cfg->reader.eof = old_eof;
+
+	return ret;
+}
+
+/*
+ * Read and consume a line, returning it in newly-allocated memory.
+ */
+static char *cfg_readline(diskfile_backend *cfg, bool skip_whitespace)
+{
+	char *line = NULL;
+	char *line_src, *line_end;
+	size_t line_len;
+
+	line_src = cfg->reader.read_ptr;
+
+	if (skip_whitespace) {
+		/* Skip empty empty lines */
+		while (git__isspace(*line_src))
+			++line_src;
+	}
+
+	line_end = strchr(line_src, '\n');
+
+	/* no newline at EOF */
+	if (line_end == NULL)
+		line_end = strchr(line_src, 0);
+
+	line_len = line_end - line_src;
+
+	line = git__malloc(line_len + 1);
+	if (line == NULL)
+		return NULL;
+
+	memcpy(line, line_src, line_len);
+
+	do line[line_len] = '\0';
+	while (line_len-- > 0 && git__isspace(line[line_len]));
+
+	if (*line_end == '\n')
+		line_end++;
+
+	if (*line_end == '\0')
+		cfg->reader.eof = 1;
+
+	cfg->reader.line_number++;
+	cfg->reader.read_ptr = line_end;
+
+	return line;
+}
+
+/*
+ * Consume a line, without storing it anywhere
+ */
+static void cfg_consume_line(diskfile_backend *cfg)
+{
+	char *line_start, *line_end;
+
+	line_start = cfg->reader.read_ptr;
+	line_end = strchr(line_start, '\n');
+	/* No newline at EOF */
+	if(line_end == NULL){
+		line_end = strchr(line_start, '\0');
+	}
+
+	if (*line_end == '\n')
+		line_end++;
+
+	if (*line_end == '\0')
+		cfg->reader.eof = 1;
+
+	cfg->reader.line_number++;
+	cfg->reader.read_ptr = line_end;
+}
+
+GIT_INLINE(int) config_keychar(int c)
+{
+	return isalnum(c) || c == '-';
+}
+
+static int parse_section_header_ext(diskfile_backend *cfg, const char *line, const char *base_name, char **section_name)
+{
+	int c, rpos;
+	char *first_quote, *last_quote;
+	git_buf buf = GIT_BUF_INIT;
+	int quote_marks;
+	/*
+	 * base_name is what came before the space. We should be at the
+	 * first quotation mark, except for now, line isn't being kept in
+	 * sync so we only really use it to calculate the length.
+	 */
+
+	first_quote = strchr(line, '"');
+	last_quote = strrchr(line, '"');
+
+	if (last_quote - first_quote == 0) {
+		set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
+		return -1;
+	}
+
+	git_buf_grow(&buf, strlen(base_name) + last_quote - first_quote + 2);
+	git_buf_printf(&buf, "%s.", base_name);
+
+	rpos = 0;
+	quote_marks = 0;
+
+	line = first_quote;
+	c = line[rpos++];
+
+	/*
+	 * At the end of each iteration, whatever is stored in c will be
+	 * added to the string. In case of error, jump to out
+	 */
+	do {
+		if (quote_marks == 2) {
+			set_parse_error(cfg, rpos, "Unexpected text after closing quotes");
+			git_buf_free(&buf);
+			return -1;
+		}
+
+		switch (c) {
+		case '"':
+			++quote_marks;
+			continue;
+
+		case '\\':
+			c = line[rpos++];
+
+			switch (c) {
+			case '"':
+			case '\\':
+				break;
+
+			default:
+				set_parse_error(cfg, rpos, "Unsupported escape sequence");
+				git_buf_free(&buf);
+				return -1;
+			}
+
+		default:
+			break;
+		}
+
+		git_buf_putc(&buf, c);
+	} while ((c = line[rpos++]) != ']');
+
+	*section_name = git_buf_detach(&buf);
+	return 0;
+}
+
+static int parse_section_header(diskfile_backend *cfg, char **section_out)
+{
+	char *name, *name_end;
+	int name_length, c, pos;
+	int result;
+	char *line;
+
+	line = cfg_readline(cfg, true);
+	if (line == NULL)
+		return -1;
+
+	/* find the end of the variable's name */
+	name_end = strchr(line, ']');
+	if (name_end == NULL) {
+		git__free(line);
+		set_parse_error(cfg, 0, "Missing ']' in section header");
+		return -1;
+	}
+
+	name = (char *)git__malloc((size_t)(name_end - line) + 1);
+	GITERR_CHECK_ALLOC(name);
+
+	name_length = 0;
+	pos = 0;
+
+	/* Make sure we were given a section header */
+	c = line[pos++];
+	assert(c == '[');
+
+	c = line[pos++];
+
+	do {
+		if (git__isspace(c)){
+			name[name_length] = '\0';
+			result = parse_section_header_ext(cfg, line, name, section_out);
+			git__free(line);
+			git__free(name);
+			return result;
+		}
+
+		if (!config_keychar(c) && c != '.') {
+			set_parse_error(cfg, pos, "Unexpected character in header");
+			goto fail_parse;
+		}
+
+		name[name_length++] = (char) tolower(c);
+
+	} while ((c = line[pos++]) != ']');
+
+	if (line[pos - 1] != ']') {
+		set_parse_error(cfg, pos, "Unexpected end of file");
+		goto fail_parse;
+	}
+
+	git__free(line);
+
+	name[name_length] = 0;
+	*section_out = name;
+
+	return 0;
+
+fail_parse:
+	git__free(line);
+	git__free(name);
+	return -1;
+}
+
+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;
+
+	if (memcmp(cfg->reader.read_ptr, utf8_bom, sizeof(utf8_bom)) == 0)
+		cfg->reader.read_ptr += sizeof(utf8_bom);
+
+	/* TODO: the reference implementation does pretty stupid
+		shit with the BoM
+	*/
+
+	return 0;
+}
+
+/*
+	(* basic types *)
+	digit = "0".."9"
+	integer = digit { digit }
+	alphabet = "a".."z" + "A" .. "Z"
+
+	section_char = alphabet | "." | "-"
+	extension_char = (* any character except newline *)
+	any_char = (* any character *)
+	variable_char = "alphabet" | "-"
+
+
+	(* actual grammar *)
+	config = { section }
+
+	section = header { definition }
+
+	header = "[" section [subsection | subsection_ext] "]"
+
+	subsection = "." section
+	subsection_ext = "\"" extension "\""
+
+	section = section_char { section_char }
+	extension = extension_char { extension_char }
+
+	definition = variable_name ["=" variable_value] "\n"
+
+	variable_name = variable_char { variable_char }
+	variable_value = string | boolean | integer
+
+	string = quoted_string | plain_string
+	quoted_string = "\"" plain_string "\""
+	plain_string = { any_char }
+
+	boolean = boolean_true | boolean_false
+	boolean_true = "yes" | "1" | "true" | "on"
+	boolean_false = "no" | "0" | "false" | "off"
+*/
+
+static int strip_comments(char *line, int in_quotes)
+{
+	int quote_count = in_quotes;
+	char *ptr;
+
+	for (ptr = line; *ptr; ++ptr) {
+		if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
+			quote_count++;
+
+		if ((ptr[0] == ';' || ptr[0] == '#') && (quote_count % 2) == 0) {
+			ptr[0] = '\0';
+			break;
+		}
+	}
+
+	/* skip any space at the end */
+	if (git__isspace(ptr[-1])) {
+		ptr--;
+	}
+	ptr[0] = '\0';
+
+	return quote_count;
+}
+
+static int config_parse(diskfile_backend *cfg_file)
+{
+	int c;
+	char *current_section = NULL;
+	char *var_name;
+	char *var_value;
+	cvar_t *var, *existing;
+	git_buf buf = GIT_BUF_INIT;
+	int result = 0;
+	khiter_t pos;
+
+	/* Initialize the reading position */
+	cfg_file->reader.read_ptr = cfg_file->reader.buffer.ptr;
+	cfg_file->reader.eof = 0;
+
+	/* If the file is empty, there's nothing for us to do */
+	if (*cfg_file->reader.read_ptr == '\0')
+		return 0;
+
+	skip_bom(cfg_file);
+
+	while (result == 0 && !cfg_file->reader.eof) {
+
+		c = cfg_peek(cfg_file, SKIP_WHITESPACE);
+
+		switch (c) {
+		case '\n': /* EOF when peeking, set EOF in the reader to exit the loop */
+			cfg_file->reader.eof = 1;
+			break;
+
+		case '[': /* section header, new section begins */
+			git__free(current_section);
+			current_section = NULL;
+			result = parse_section_header(cfg_file, &current_section);
+			break;
+
+		case ';':
+		case '#':
+			cfg_consume_line(cfg_file);
+			break;
+
+		default: /* assume variable declaration */
+			result = parse_variable(cfg_file, &var_name, &var_value);
+			if (result < 0)
+				break;
+
+			var = git__malloc(sizeof(cvar_t));
+			GITERR_CHECK_ALLOC(var);
+
+			memset(var, 0x0, sizeof(cvar_t));
+
+			git__strtolower(var_name);
+			git_buf_printf(&buf, "%s.%s", current_section, var_name);
+			git__free(var_name);
+
+			if (git_buf_oom(&buf))
+				return -1;
+
+			var->key = git_buf_detach(&buf);
+			var->value = var_value;
+
+			/* Add or append the new config option */
+			pos = git_strmap_lookup_index(cfg_file->values, var->key);
+			if (!git_strmap_valid_index(cfg_file->values, pos)) {
+				git_strmap_insert(cfg_file->values, var->key, var, result);
+				if (result < 0)
+					break;
+				result = 0;
+			} else {
+				existing = git_strmap_value_at(cfg_file->values, pos);
+				while (existing->next != NULL) {
+					existing = existing->next;
+				}
+				existing->next = var;
+			}
+
+			break;
+		}
+	}
+
+	git__free(current_section);
+	return result;
+}
+
+static int write_section(git_filebuf *file, const char *key)
+{
+	int result;
+	const char *dot;
+	git_buf buf = GIT_BUF_INIT;
+
+	/* All of this just for [section "subsection"] */
+	dot = strchr(key, '.');
+	git_buf_putc(&buf, '[');
+	if (dot == NULL) {
+		git_buf_puts(&buf, key);
+	} else {
+		git_buf_put(&buf, key, dot - key);
+		/* TODO: escape  */
+		git_buf_printf(&buf, " \"%s\"", dot + 1);
+	}
+	git_buf_puts(&buf, "]\n");
+
+	if (git_buf_oom(&buf))
+		return -1;
+
+	result = git_filebuf_write(file, git_buf_cstr(&buf), buf.size);
+	git_buf_free(&buf);
+
+	return result;
+}
+
+/*
+ * This is pretty much the parsing, except we write out anything we don't have
+ */
+static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char* value)
+{
+	int result, c;
+	int section_matches = 0, last_section_matched = 0, preg_replaced = 0, write_trailer = 0;
+	const char *pre_end = NULL, *post_start = NULL, *data_start;
+	char *current_section = NULL, *section, *name, *ldot;
+	git_filebuf file = GIT_FILEBUF_INIT;
+
+	/* We need to read in our own config file */
+	result = git_futils_readbuffer(&cfg->reader.buffer, cfg->file_path);
+
+	/* Initialise the reading position */
+	if (result == GIT_ENOTFOUND) {
+		cfg->reader.read_ptr = NULL;
+		cfg->reader.eof = 1;
+		data_start = NULL;
+		git_buf_clear(&cfg->reader.buffer);
+	} else if (result == 0) {
+		cfg->reader.read_ptr = cfg->reader.buffer.ptr;
+		cfg->reader.eof = 0;
+		data_start = cfg->reader.read_ptr;
+	} else {
+		return -1; /* OS error when reading the file */
+	}
+
+	/* Lock the file */
+	if (git_filebuf_open(&file, cfg->file_path, 0) < 0)
+		return -1;
+
+	skip_bom(cfg);
+	ldot = strrchr(key, '.');
+	name = ldot + 1;
+	section = git__strndup(key, ldot - key);
+
+	while (!cfg->reader.eof) {
+		c = cfg_peek(cfg, SKIP_WHITESPACE);
+
+		if (c == '\0') { /* We've arrived at the end of the file */
+			break;
+
+		} else if (c == '[') { /* section header, new section begins */
+			/*
+			 * We set both positions to the current one in case we
+			 * need to add a variable to the end of a section. In that
+			 * case, we want both variables to point just before the
+			 * new section. If we actually want to replace it, the
+			 * default case will take care of updating them.
+			 */
+			pre_end = post_start = cfg->reader.read_ptr;
+
+			git__free(current_section);
+			current_section = NULL;
+			if (parse_section_header(cfg, &current_section) < 0)
+				goto rewrite_fail;
+
+			/* Keep track of when it stops matching */
+			last_section_matched = section_matches;
+			section_matches = !strcmp(current_section, section);
+		}
+
+		else if (c == ';' || c == '#') {
+			cfg_consume_line(cfg);
+		}
+
+		else {
+			/*
+			 * If the section doesn't match, but the last section did,
+			 * it means we need to add a variable (so skip the line
+			 * otherwise). If both the section and name match, we need
+			 * to overwrite the variable (so skip the line
+			 * otherwise). pre_end needs to be updated each time so we
+			 * don't loose that information, but we only need to
+			 * update post_start if we're going to use it in this
+			 * iteration.
+			 */
+			if (!section_matches) {
+				if (!last_section_matched) {
+					cfg_consume_line(cfg);
+					continue;
+				}
+			} else {
+				int has_matched = 0;
+				char *var_name, *var_value;
+
+				pre_end = cfg->reader.read_ptr;
+				if (parse_variable(cfg, &var_name, &var_value) < 0)
+					goto rewrite_fail;
+
+				/* First try to match the name of the variable */
+				if (strcasecmp(name, var_name) == 0)
+					has_matched = 1;
+
+				/* If the name matches, and we have a regex to match the
+				 * value, try to match it */
+				if (has_matched && preg != NULL)
+					has_matched = (regexec(preg, var_value, 0, NULL, 0) == 0);
+
+				git__free(var_name);
+				git__free(var_value);
+
+				/* if there is no match, keep going */
+				if (!has_matched)
+					continue;
+
+				post_start = cfg->reader.read_ptr;
+			}
+
+			/* We've found the variable we wanted to change, so
+			 * write anything up to it */
+			git_filebuf_write(&file, data_start, pre_end - data_start);
+			preg_replaced = 1;
+
+			/* Then replace the variable. If the value is NULL, it
+			 * means we want to delete it, so don't write anything. */
+			if (value != NULL) {
+				git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+			}
+
+			/* multiline variable? we need to keep reading lines to match */
+			if (preg != NULL) {
+				data_start = post_start;
+				continue;
+			}
+
+			write_trailer = 1;
+			break; /* break from the loop */
+		}
+	}
+
+	/*
+	 * Being here can mean that
+	 *
+	 * 1) our section is the last one in the file and we're
+	 * adding a variable
+	 *
+	 * 2) we didn't find a section for us so we need to create it
+	 * ourselves.
+	 *
+	 * 3) we're setting a multivar with a regex, which means we
+	 * continue to search for matching values
+	 *
+	 * In the last case, if we've already replaced a value, we
+	 * want to write the rest of the file. Otherwise we need to write
+	 * out the whole file and then the new variable.
+	 */
+	if (write_trailer) {
+		/* Write out rest of the file */
+		git_filebuf_write(&file, post_start, cfg->reader.buffer.size - (post_start - data_start));
+	} else {
+		if (preg_replaced) {
+			git_filebuf_printf(&file, "\n%s", data_start);
+		} else {
+			git_filebuf_write(&file, cfg->reader.buffer.ptr, cfg->reader.buffer.size);
+
+			/* And now if we just need to add a variable */
+			if (!section_matches && write_section(&file, section) < 0)
+				goto rewrite_fail;
+
+			/* Sanity check: if we are here, and value is NULL, that means that somebody
+			 * touched the config file after our intial read. We should probably assert()
+			 * this, but instead we'll handle it gracefully with an error. */
+			if (value == NULL) {
+				giterr_set(GITERR_CONFIG,
+					"Race condition when writing a config file (a cvar has been removed)");
+				goto rewrite_fail;
+			}
+
+			git_filebuf_printf(&file, "\t%s = %s\n", name, value);
+		}
+	}
+
+	git__free(section);
+	git__free(current_section);
+
+	result = git_filebuf_commit(&file, GIT_CONFIG_FILE_MODE);
+	git_buf_free(&cfg->reader.buffer);
+	return result;
+
+rewrite_fail:
+	git__free(section);
+	git__free(current_section);
+
+	git_filebuf_cleanup(&file);
+	git_buf_free(&cfg->reader.buffer);
+	return -1;
+}
+
+/* '\"' -> '"' 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;
+
+	while (*ptr != '\0') {
+		if (*ptr == '"') {
+			quote_count++;
+		} else if (*ptr != '\\') {
+			*out++ = *ptr;
+		} else {
+			/* backslash, check the next char */
+			ptr++;
+			/* if we're at the end, it's a multiline, so keep the backslash */
+			if (*ptr == '\0') {
+				*out++ = '\\';
+				goto out;
+			}
+			if ((esc = strchr(escapes, *ptr)) != NULL) {
+				*out++ = escaped[esc - escapes];
+			} else {
+				git__free(str);
+				giterr_set(GITERR_CONFIG, "Invalid escape at %s", ptr);
+				return NULL;
+			}
+		}
+		ptr++;
+	}
+
+out:
+	*out = '\0';
+
+	return str;
+}
+
+static int is_multiline_var(const char *str)
+{
+	const char *end = str + strlen(str);
+	return (end > str) && (end[-1] == '\\');
+}
+
+static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int in_quotes)
+{
+	char *line = NULL, *proc_line = NULL;
+	int quote_count;
+
+	/* Check that the next line exists */
+	line = cfg_readline(cfg, false);
+	if (line == NULL)
+		return -1;
+
+	/* We've reached the end of the file, there is input missing */
+	if (line[0] == '\0') {
+		set_parse_error(cfg, 0, "Unexpected end of file while parsing multine var");
+		git__free(line);
+		return -1;
+	}
+
+	quote_count = strip_comments(line, !!in_quotes);
+
+	/* If it was just a comment, pretend it didn't exist */
+	if (line[0] == '\0') {
+		git__free(line);
+		return parse_multiline_variable(cfg, value, quote_count);
+		/* TODO: unbounded recursion. This **could** be exploitable */
+	}
+
+	/* Drop the continuation character '\': to closely follow the UNIX
+	 * standard, this character **has** to be last one in the buf, with
+	 * no whitespace after it */
+	assert(is_multiline_var(value->ptr));
+	git_buf_truncate(value, git_buf_len(value) - 1);
+
+	proc_line = fixup_line(line, in_quotes);
+	if (proc_line == NULL) {
+		git__free(line);
+		return -1;
+	}
+	/* add this line to the multiline var */
+	git_buf_puts(value, proc_line);
+	git__free(line);
+	git__free(proc_line);
+
+	/*
+	 * If we need to continue reading the next line, let's just
+	 * keep putting stuff in the buffer
+	 */
+	if (is_multiline_var(value->ptr))
+		return parse_multiline_variable(cfg, value, quote_count);
+
+	return 0;
+}
+
+static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value)
+{
+	const char *var_end = NULL;
+	const char *value_start = NULL;
+	char *line;
+	int quote_count;
+
+	line = cfg_readline(cfg, true);
+	if (line == NULL)
+		return -1;
+
+	quote_count = strip_comments(line, 0);
+
+	var_end = strchr(line, '=');
+
+	if (var_end == NULL)
+		var_end = strchr(line, '\0');
+	else
+		value_start = var_end + 1;
+
+	if (git__isspace(var_end[-1])) {
+		do var_end--;
+		while (git__isspace(var_end[0]));
+	}
+
+	*var_name = git__strndup(line, var_end - line + 1);
+	GITERR_CHECK_ALLOC(*var_name);
+
+	/* If there is no value, boolean true is assumed */
+	*var_value = NULL;
+
+	/*
+	 * Now, let's try to parse the value
+	 */
+	if (value_start != NULL) {
+		while (git__isspace(value_start[0]))
+			value_start++;
+
+		if (is_multiline_var(value_start)) {
+			git_buf multi_value = GIT_BUF_INIT;
+			char *proc_line = fixup_line(value_start, 0);
+			GITERR_CHECK_ALLOC(proc_line);
+			git_buf_puts(&multi_value, proc_line);
+			git__free(proc_line);
+			if (parse_multiline_variable(cfg, &multi_value, quote_count) < 0 || git_buf_oom(&multi_value)) {
+				git__free(*var_name);
+				git__free(line);
+				git_buf_free(&multi_value);
+				return -1;
+			}
+
+			*var_value = git_buf_detach(&multi_value);
+
+		}
+		else if (value_start[0] != '\0') {
+			*var_value = fixup_line(value_start, 0);
+			GITERR_CHECK_ALLOC(*var_value);
+		}
+
+	}
+
+	git__free(line);
+	return 0;
+}
diff --git a/libgit2/src/config_file.h b/libgit2/src/config_file.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/config_file.h
@@ -0,0 +1,31 @@
+/*
+ * 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_config_file_h__
+#define INCLUDE_config_file_h__
+
+#include "git2/config.h"
+
+GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
+{
+	return cfg->open(cfg);
+}
+
+GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
+{
+	cfg->free(cfg);
+}
+
+GIT_INLINE(int) git_config_file_foreach(
+	git_config_file *cfg,
+	int (*fn)(const char *key, const char *value, void *data),
+	void *data)
+{
+	return cfg->foreach(cfg, fn, data);
+}
+
+#endif
+
diff --git a/libgit2/src/crlf.c b/libgit2/src/crlf.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/crlf.c
@@ -0,0 +1,228 @@
+/*
+ * 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 "fileops.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+
+#include "git2/attr.h"
+
+struct crlf_attrs {
+	int crlf_action;
+	int eol;
+};
+
+struct crlf_filter {
+	git_filter f;
+	struct crlf_attrs attrs;
+};
+
+static int check_crlf(const char *value)
+{
+	if (GIT_ATTR_TRUE(value))
+		return GIT_CRLF_TEXT;
+
+	if (GIT_ATTR_FALSE(value))
+		return GIT_CRLF_BINARY;
+
+	if (GIT_ATTR_UNSPECIFIED(value))
+		return GIT_CRLF_GUESS;
+
+	if (strcmp(value, "input") == 0)
+		return GIT_CRLF_INPUT;
+
+	if (strcmp(value, "auto") == 0)
+		return GIT_CRLF_AUTO;
+
+	return GIT_CRLF_GUESS;
+}
+
+static int check_eol(const char *value)
+{
+	if (GIT_ATTR_UNSPECIFIED(value))
+		return GIT_EOL_UNSET;
+
+	if (strcmp(value, "lf") == 0)
+		return GIT_EOL_LF;
+
+	if (strcmp(value, "crlf") == 0)
+		return GIT_EOL_CRLF;
+
+	return GIT_EOL_UNSET;
+}
+
+static int crlf_input_action(struct crlf_attrs *ca)
+{
+	if (ca->crlf_action == GIT_CRLF_BINARY)
+		return GIT_CRLF_BINARY;
+
+	if (ca->eol == GIT_EOL_LF)
+		return GIT_CRLF_INPUT;
+
+	if (ca->eol == GIT_EOL_CRLF)
+		return GIT_CRLF_CRLF;
+
+	return ca->crlf_action;
+}
+
+static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
+{
+#define NUM_CONV_ATTRS 3
+
+	static const char *attr_names[NUM_CONV_ATTRS] = {
+		"crlf", "eol", "text",
+	};
+
+	const char *attr_vals[NUM_CONV_ATTRS];
+	int error;
+
+	error = git_attr_get_many(attr_vals,
+		repo, 0, path, NUM_CONV_ATTRS, attr_names);
+
+	if (error == GIT_ENOTFOUND) {
+		ca->crlf_action = GIT_CRLF_GUESS;
+		ca->eol = GIT_EOL_UNSET;
+		return 0;
+	}
+
+	if (error == 0) {
+		ca->crlf_action = check_crlf(attr_vals[2]); /* text */
+		if (ca->crlf_action == GIT_CRLF_GUESS)
+			ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
+
+		ca->eol = check_eol(attr_vals[1]); /* eol */
+		return 0;
+	}
+
+	return -1;
+}
+
+static int drop_crlf(git_buf *dest, const git_buf *source)
+{
+	const char *scan = source->ptr, *next;
+	const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
+
+	/* 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);
+
+		/* Do not drop \r unless it is followed by \n */
+		if (*(next + 1) != '\n')
+			git_buf_putc(dest, '\r');
+
+		scan = next + 1;
+	}
+
+	/* If there was no \r, then tell the library to skip this filter */
+	if (scan == source->ptr)
+		return -1;
+
+	/* Copy remaining input into dest */
+	git_buf_put(dest, scan, scan_end - scan);
+	return 0;
+}
+
+static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
+{
+	struct crlf_filter *filter = (struct crlf_filter *)self;
+
+	assert(self && dest && source);
+
+	/* Empty file? Nothing to do */
+	if (git_buf_len(source) == 0)
+		return 0;
+
+	/* Heuristics to see if we can skip the conversion.
+	 * Straight from Core Git.
+	 */
+	if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
+		filter->attrs.crlf_action == GIT_CRLF_GUESS) {
+
+		git_text_stats stats;
+		git_text_gather_stats(&stats, source);
+
+		/*
+		 * We're currently not going to even try to convert stuff
+		 * that has bare CR characters. Does anybody do that crazy
+		 * stuff?
+		 */
+		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 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;
+		}
+#endif
+
+		if (!stats.cr)
+			return -1;
+	}
+
+	/* Actually drop the carriage returns */
+	return drop_crlf(dest, source);
+}
+
+int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
+{
+	struct crlf_attrs ca;
+	struct crlf_filter *filter;
+	int error;
+
+	/* Load gitattributes for the path */
+	if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
+		return error;
+
+	/*
+	 * 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`
+	 */
+	ca.crlf_action = crlf_input_action(&ca);
+
+	if (ca.crlf_action == GIT_CRLF_BINARY)
+		return 0;
+
+	if (ca.crlf_action == GIT_CRLF_GUESS) {
+		int auto_crlf;
+
+		if ((error = git_repository__cvar(
+			&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
+			return error;
+
+		if (auto_crlf == GIT_AUTO_CRLF_FALSE)
+			return 0;
+	}
+
+	/* If we're good, we create a new filter object and push it
+	 * into the filters array */
+	filter = git__malloc(sizeof(struct crlf_filter));
+	GITERR_CHECK_ALLOC(filter);
+
+	filter->f.apply = &crlf_apply_to_odb;
+	filter->f.do_free = NULL;
+	memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
+
+	return git_vector_insert(filters, filter);
+}
+
diff --git a/libgit2/src/delta-apply.c b/libgit2/src/delta-apply.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/delta-apply.c
@@ -0,0 +1,121 @@
+/*
+ * 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/odb.h"
+#include "delta-apply.h"
+
+/*
+ * This file was heavily cribbed from BinaryDelta.java in JGit, which
+ * itself was heavily cribbed from <code>patch-delta.c</code> in the
+ * GIT project.	The original delta patching code was written by
+ * Nicolas Pitre <nico@cam.org>.
+ */
+
+static int hdr_sz(
+	size_t *size,
+	const unsigned char **delta,
+	const unsigned char *end)
+{
+	const unsigned char *d = *delta;
+	size_t r = 0;
+	unsigned int c, shift = 0;
+
+	do {
+		if (d == end)
+			return -1;
+		c = *d++;
+		r |= (c & 0x7f) << shift;
+		shift += 7;
+	} while (c & 0x80);
+	*delta = d;
+	*size = r;
+	return 0;
+}
+
+int git__delta_apply(
+	git_rawobj *out,
+	const unsigned char *base,
+	size_t base_len,
+	const unsigned char *delta,
+	size_t delta_len)
+{
+	const unsigned char *delta_end = delta + delta_len;
+	size_t base_sz, res_sz;
+	unsigned char *res_dp;
+
+	/* Check that the base size matches the data we were given;
+	 * if not we would underflow while accessing data from the
+	 * base object, resulting in data corruption or segfault.
+	 */
+	if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
+		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+		return -1;
+	}
+
+	if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
+		giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
+		return -1;
+	}
+
+	res_dp = git__malloc(res_sz + 1);
+	GITERR_CHECK_ALLOC(res_dp);
+
+	res_dp[res_sz] = '\0';
+	out->data = res_dp;
+	out->len = res_sz;
+
+	while (delta < delta_end) {
+		unsigned char cmd = *delta++;
+		if (cmd & 0x80) {
+			/* cmd is a copy instruction; copy from the base.
+			 */
+			size_t off = 0, len = 0;
+
+			if (cmd & 0x01) off = *delta++;
+			if (cmd & 0x02) off |= *delta++ << 8;
+			if (cmd & 0x04) off |= *delta++ << 16;
+			if (cmd & 0x08) off |= *delta++ << 24;
+
+			if (cmd & 0x10) len = *delta++;
+			if (cmd & 0x20) len |= *delta++ << 8;
+			if (cmd & 0x40) len |= *delta++ << 16;
+			if (!len)		len = 0x10000;
+
+			if (base_len < off + len || res_sz < len)
+				goto fail;
+			memcpy(res_dp, base + off, len);
+			res_dp += len;
+			res_sz -= len;
+
+		} else if (cmd) {
+			/* cmd is a literal insert instruction; copy from
+			 * the delta stream itself.
+			 */
+			if (delta_end - delta < cmd || res_sz < cmd)
+				goto fail;
+			memcpy(res_dp, delta, cmd);
+			delta += cmd;
+			res_dp += cmd;
+			res_sz -= cmd;
+
+		} else {
+			/* cmd == 0 is reserved for future encodings.
+			 */
+			goto fail;
+		}
+	}
+
+	if (delta != delta_end || res_sz)
+		goto fail;
+	return 0;
+
+fail:
+	git__free(out->data);
+	out->data = NULL;
+	giterr_set(GITERR_INVALID, "Failed to apply delta");
+	return -1;
+}
diff --git a/libgit2/src/delta-apply.h b/libgit2/src/delta-apply.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/delta-apply.h
@@ -0,0 +1,33 @@
+/*
+ * 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_delta_apply_h__
+#define INCLUDE_delta_apply_h__
+
+#include "odb.h"
+
+/**
+ * Apply a git binary delta to recover the original content.
+ *
+ * @param out the output buffer to receive the original data.
+ *		Only out->data and out->len are populated, as this is
+ *		the only information available in the delta.
+ * @param base the base to copy from during copy instructions.
+ * @param base_len number of bytes available at base.
+ * @param delta the delta to execute copy/insert instructions from.
+ * @param delta_len total number of bytes in the delta.
+ * @return
+ * - 0 on a successful delta unpack.
+ * - GIT_ERROR if the delta is corrupt or doesn't match the base.
+ */
+extern int git__delta_apply(
+	git_rawobj *out,
+	const unsigned char *base,
+	size_t base_len,
+	const unsigned char *delta,
+	size_t delta_len);
+
+#endif
diff --git a/libgit2/src/diff.c b/libgit2/src/diff.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/diff.c
@@ -0,0 +1,783 @@
+/*
+ * 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 "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;
+	}
+
+	return false;
+}
+
+static git_diff_delta *diff_delta__alloc(
+	git_diff_list *diff,
+	git_delta_t status,
+	const char *path)
+{
+	git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
+	if (!delta)
+		return NULL;
+
+	delta->old_file.path = git_pool_strdup(&diff->pool, path);
+	if (delta->old_file.path == NULL) {
+		git__free(delta);
+		return NULL;
+	}
+
+	delta->new_file.path = delta->old_file.path;
+
+	if (diff->opts.flags & GIT_DIFF_REVERSE) {
+		switch (status) {
+		case GIT_DELTA_ADDED:   status = GIT_DELTA_DELETED; break;
+		case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
+		default: break; /* leave other status values alone */
+		}
+	}
+	delta->status = status;
+
+	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)
+{
+	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;
+
+	return dup;
+}
+
+static int diff_delta__from_one(
+	git_diff_list *diff,
+	git_delta_t   status,
+	const git_index_entry *entry)
+{
+	git_diff_delta *delta;
+
+	if (status == GIT_DELTA_IGNORED &&
+		(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+		return 0;
+
+	if (status == GIT_DELTA_UNTRACKED &&
+		(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+		return 0;
+
+	if (!diff_path_matches_pathspec(diff, entry->path))
+		return 0;
+
+	delta = diff_delta__alloc(diff, status, entry->path);
+	GITERR_CHECK_ALLOC(delta);
+
+	/* This fn is just for single-sided diffs */
+	assert(status != GIT_DELTA_MODIFIED);
+
+	if (delta->status == GIT_DELTA_DELETED) {
+		delta->old_file.mode = entry->mode;
+		delta->old_file.size = entry->file_size;
+		git_oid_cpy(&delta->old_file.oid, &entry->oid);
+	} else /* ADDED, IGNORED, UNTRACKED */ {
+		delta->new_file.mode = entry->mode;
+		delta->new_file.size = entry->file_size;
+		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;
+
+	if (git_vector_insert(&diff->deltas, delta) < 0) {
+		git__free(delta);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int diff_delta__from_two(
+	git_diff_list *diff,
+	git_delta_t   status,
+	const git_index_entry *old_entry,
+	const git_index_entry *new_entry,
+	git_oid *new_oid)
+{
+	git_diff_delta *delta;
+
+	if (status == GIT_DELTA_UNMODIFIED &&
+		(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+		return 0;
+
+	if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
+		const git_index_entry *temp = old_entry;
+		old_entry = new_entry;
+		new_entry = temp;
+	}
+
+	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->new_file.mode = new_entry->mode;
+	git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
+	if (new_oid || !git_oid_iszero(&new_entry->oid))
+		delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
+
+	if (git_vector_insert(&diff->deltas, delta) < 0) {
+		git__free(delta);
+		return -1;
+	}
+
+	return 0;
+}
+
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
+{
+	size_t len = strlen(prefix);
+
+	/* append '/' at end if needed */
+	if (len > 0 && prefix[len - 1] != '/')
+		return git_pool_strcat(pool, prefix, "/");
+	else
+		return git_pool_strndup(pool, prefix, len + 1);
+}
+
+static int 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);
+}
+
+static int config_bool(git_config *cfg, const char *name, int defvalue)
+{
+	int val = defvalue;
+
+	if (git_config_get_bool(&val, cfg, name) < 0)
+		giterr_clear();
+
+	return val;
+}
+
+static git_diff_list *git_diff_list_alloc(
+	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;
+
+	diff->repo = repo;
+
+	if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
+		git_pool_init(&diff->pool, 1, 0) < 0)
+		goto fail;
+
+	/* load config values that affect diff behavior */
+	if (git_repository_config__weakptr(&cfg, repo) < 0)
+		goto fail;
+	if (config_bool(cfg, "core.symlinks", 1))
+		diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
+	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;
+	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)
+		return diff;
+
+	memcpy(&diff->opts, opts, sizeof(git_diff_options));
+	memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
+
+	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,
+		opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
+
+	if (!diff->opts.old_prefix || !diff->opts.new_prefix)
+		goto fail;
+
+	if (diff->opts.flags & GIT_DIFF_REVERSE) {
+		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;
+	}
+
+	return diff;
+
+fail:
+	git_diff_list_free(diff);
+	return NULL;
+}
+
+void git_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_pool_clear(&diff->pool);
+	git__free(diff);
+}
+
+static int oid_for_workdir_item(
+	git_repository *repo,
+	const git_index_entry *item,
+	git_oid *oid)
+{
+	int result;
+	git_buf full_path = GIT_BUF_INIT;
+
+	if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
+		return -1;
+
+	/* calculate OID for file if possible*/
+	if (S_ISLNK(item->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");
+		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_buf_free(&full_path);
+
+	return result;
+}
+
+#define EXEC_BIT_MASK 0000111
+
+static int maybe_modified(
+	git_iterator *old_iter,
+	const git_index_entry *oitem,
+	git_iterator *new_iter,
+	const git_index_entry *nitem,
+	git_diff_list *diff)
+{
+	git_oid noid, *use_noid = NULL;
+	git_delta_t status = GIT_DELTA_MODIFIED;
+	unsigned int omode = oitem->mode;
+	unsigned int nmode = nitem->mode;
+
+	GIT_UNUSED(old_iter);
+
+	if (!diff_path_matches_pathspec(diff, oitem->path))
+		return 0;
+
+	/* on platforms with no symlinks, promote plain files to symlinks */
+	if (S_ISLNK(omode) && S_ISREG(nmode) &&
+		!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+		nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
+
+	/* 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;
+	}
+
+	/* support "assume unchanged" (badly, 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;
+
+	/* support "skip worktree" index bit */
+	else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
+		status = GIT_DELTA_UNMODIFIED;
+
+	/* 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 oids and modes match, then file is unmodified */
+	else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
+			 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) {
+		/* 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 &&
+			(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
+			 (oitem->ctime.seconds == nitem->ctime.seconds)) &&
+			oitem->mtime.seconds == nitem->mtime.seconds &&
+			(!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
+			 (oitem->dev == nitem->dev)) &&
+			oitem->ino == nitem->ino &&
+			oitem->uid == nitem->uid &&
+			oitem->gid == nitem->gid)
+			status = GIT_DELTA_UNMODIFIED;
+
+		else if (S_ISGITLINK(nmode)) {
+			git_submodule *sub;
+
+			if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
+				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)
+				status = GIT_DELTA_UNMODIFIED;
+			else {
+				/* TODO: support other GIT_SUBMODULE_IGNORE values */
+				status = GIT_DELTA_UNMODIFIED;
+			}
+		}
+
+		/* TODO: check git attributes so we will not have to read the file
+		 * in if it is marked binary.
+		 */
+
+		else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
+			return -1;
+
+		else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
+				 omode == nmode)
+			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, nitem, use_noid);
+}
+
+static int diff_from_iterators(
+	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_index_entry *oitem, *nitem;
+	git_buf ignore_prefix = GIT_BUF_INIT;
+	git_diff_list *diff = git_diff_list_alloc(repo, opts);
+	if (!diff)
+		goto fail;
+
+	diff->old_src = old_iter->type;
+	diff->new_src = new_iter->type;
+
+	if (git_iterator_current(old_iter, &oitem) < 0 ||
+		git_iterator_current(new_iter, &nitem) < 0)
+		goto fail;
+
+	/* run iterators building diffs */
+	while (oitem || nitem) {
+
+		/* 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)
+				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)) {
+			git_delta_t delta_type = GIT_DELTA_UNTRACKED;
+
+			/* check if contained in ignored parent directory */
+			if (git_buf_len(&ignore_prefix) &&
+				git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
+				delta_type = GIT_DELTA_IGNORED;
+
+			if (S_ISDIR(nitem->mode)) {
+				/* recurse into directory only if there are tracked items in
+				 * 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) ||
+					(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);
+
+					if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
+						goto fail;
+
+					continue;
+				}
+			}
+
+			/* In core git, the next two "else if" clauses are effectively
+			 * reversed -- i.e. when an untracked file contained in an
+			 * ignored directory is individually ignored, it shows up as an
+			 * ignored file in the diff list, even though other untracked
+			 * files in the same directory are skipped completely.
+			 *
+			 * To me, this is odd.  If the directory is ignored and the file
+			 * is untracked, we should skip it consistently, regardless of
+			 * whether it happens to match a pattern in the ignore file.
+			 *
+			 * To match the core git behavior, just reverse the following
+			 * two "else if" cases so that individual file ignores are
+			 * 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)
+					goto fail;
+				continue; /* ignored parent directory, so skip completely */
+			}
+
+			else if (git_iterator_current_is_ignored(new_iter))
+				delta_type = GIT_DELTA_IGNORED;
+
+			else if (new_iter->type != GIT_ITERATOR_WORKDIR)
+				delta_type = GIT_DELTA_ADDED;
+
+			if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
+				git_iterator_advance(new_iter, &nitem) < 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);
+
+			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)
+				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);
+	git_buf_free(&ignore_prefix);
+
+	git_diff_list_free(diff);
+	*diff_ptr = NULL;
+	return -1;
+}
+
+
+int git_diff_tree_to_tree(
+	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)
+{
+	git_iterator *a = NULL, *b = NULL;
+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+	assert(repo && old_tree && new_tree && diff);
+
+	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;
+
+	git__free(prefix);
+
+	return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_index_to_tree(
+	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 && diff);
+
+	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;
+
+	git__free(prefix);
+
+	return diff_from_iterators(repo, opts, a, b, diff);
+}
+
+int git_diff_workdir_to_index(
+	git_repository *repo,
+	const git_diff_options *opts,
+	git_diff_list **diff)
+{
+	git_iterator *a = NULL, *b = NULL;
+	char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
+
+	assert(repo && diff);
+
+	if (git_iterator_for_index_range(&a, repo, 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_workdir_to_tree(
+	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)
+{
+	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;
+	}
+
+	git_vector_foreach(&onto_new, i, delta)
+		git__free(delta);
+	git_vector_free(&onto_new);
+	git_pool_clear(&onto_pool);
+
+	return error;
+}
+
diff --git a/libgit2/src/diff.h b/libgit2/src/diff.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/diff.h
@@ -0,0 +1,40 @@
+/*
+ * 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_diff_h__
+#define INCLUDE_diff_h__
+
+#include <stdio.h>
+#include "vector.h"
+#include "buffer.h"
+#include "iterator.h"
+#include "repository.h"
+#include "pool.h"
+
+#define DIFF_OLD_PREFIX_DEFAULT "a/"
+#define DIFF_NEW_PREFIX_DEFAULT "b/"
+
+enum {
+	GIT_DIFFCAPS_HAS_SYMLINKS     = (1 << 0), /* symlinks on platform? */
+	GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
+	GIT_DIFFCAPS_TRUST_EXEC_BIT   = (1 << 2), /* use st_mode exec bit? */
+	GIT_DIFFCAPS_TRUST_CTIME      = (1 << 3), /* use st_ctime? */
+	GIT_DIFFCAPS_USE_DEV          = (1 << 4), /* use st_dev? */
+};
+
+struct git_diff_list {
+	git_repository   *repo;
+	git_diff_options opts;
+	git_vector       pathspec;
+	git_vector       deltas;    /* vector of git_diff_file_delta */
+	git_pool pool;
+	git_iterator_type_t old_src;
+	git_iterator_type_t new_src;
+	uint32_t diffcaps;
+};
+
+#endif
+
diff --git a/libgit2/src/diff_output.c b/libgit2/src/diff_output.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/diff_output.c
@@ -0,0 +1,786 @@
+/*
+ * 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);
+
+	return 0;
+}
diff --git a/libgit2/src/errors.c b/libgit2/src/errors.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/errors.c
@@ -0,0 +1,119 @@
+/*
+ * 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 "global.h"
+#include "posix.h"
+#include "buffer.h"
+#include <stdarg.h>
+
+/********************************************
+ * New error handling
+ ********************************************/
+
+static git_error g_git_oom_error = {
+	"Out of memory",
+	GITERR_NOMEMORY
+};
+
+static void set_error(int error_class, char *string)
+{
+	git_error *error = &GIT_GLOBAL->error_t;
+
+	git__free(error->message);
+
+	error->message = string;
+	error->klass = error_class;
+
+	GIT_GLOBAL->last_error = error;
+}
+
+void giterr_set_oom(void)
+{
+	GIT_GLOBAL->last_error = &g_git_oom_error;
+}
+
+void giterr_set(int error_class, const char *string, ...)
+{
+	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);
+#endif
+	}
+
+	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);
+
+			if (lpMsgBuf) {
+				git_buf_PUTS(&buf, ": ");
+				git_buf_puts(&buf, lpMsgBuf);
+				LocalFree(lpMsgBuf);
+			}
+		}
+#endif
+	}
+
+	if (!git_buf_oom(&buf))
+		set_error(error_class, git_buf_detach(&buf));
+}
+
+void giterr_set_str(int error_class, const char *string)
+{
+	char *message = git__strdup(string);
+
+	if (message)
+		set_error(error_class, message);
+}
+
+void giterr_set_regex(const regex_t *regex, int error_code)
+{
+	char error_buf[1024];
+	regerror(error_code, regex, error_buf, sizeof(error_buf));
+	giterr_set_str(GITERR_REGEX, error_buf);
+}
+
+void giterr_clear(void)
+{
+	GIT_GLOBAL->last_error = NULL;
+}
+
+const git_error *giterr_last(void)
+{
+	return GIT_GLOBAL->last_error;
+}
+
diff --git a/libgit2/src/fetch.c b/libgit2/src/fetch.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fetch.c
@@ -0,0 +1,200 @@
+/*
+ * 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/oid.h"
+#include "git2/refs.h"
+#include "git2/revwalk.h"
+#include "git2/indexer.h"
+
+#include "common.h"
+#include "transport.h"
+#include "remote.h"
+#include "refspec.h"
+#include "pack.h"
+#include "fetch.h"
+#include "netops.h"
+
+struct filter_payload {
+	git_remote *remote;
+	const git_refspec *spec;
+	git_odb *odb;
+	int found_head;
+};
+
+static int filter_ref__cb(git_remote_head *head, void *payload)
+{
+	struct filter_payload *p = payload;
+
+	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;
+
+		/* Don't even try to ask for the annotation target */
+		if (!git__suffixcmp(head->name, "^{}"))
+			return 0;
+	}
+
+	/* If we have the object, mark it so we don't ask for it */
+	if (git_odb_exists(p->odb, &head->oid))
+		head->local = 1;
+	else
+		p->remote->need_pack = 1;
+
+	return git_vector_insert(&p->remote->refs, head);
+}
+
+static int filter_wants(git_remote *remote)
+{
+	struct filter_payload p;
+
+	git_vector_clear(&remote->refs);
+
+	/*
+	 * The fetch refspec can be NULL, and what this means is that the
+	 * user didn't specify one. This is fine, as it means that we're
+	 * not interested in any particular branch but just the remote's
+	 * HEAD, which will be stored in FETCH_HEAD after the fetch.
+	 */
+	p.spec = git_remote_fetchspec(remote);
+	p.found_head = 0;
+	p.remote = remote;
+
+	if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
+		return -1;
+
+	return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
+}
+
+/*
+ * In this first version, we push all our refs in and start sending
+ * them out. When we get an ACK we hide that commit and continue
+ * traversing until we're done
+ */
+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;
+	}
+
+	/* Don't try to negotiate when we don't want anything */
+	if (remote->refs.length == 0 || !remote->need_pack)
+		return 0;
+
+	/*
+	 * 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);
+}
+
+int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
+{
+	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;
+}
diff --git a/libgit2/src/fetch.h b/libgit2/src/fetch.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fetch.h
@@ -0,0 +1,19 @@
+/*
+ * 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_fetch_h__
+#define INCLUDE_fetch_h__
+
+#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_setup_walk(git_revwalk **out, git_repository *repo);
+
+#endif
diff --git a/libgit2/src/filebuf.c b/libgit2/src/filebuf.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/filebuf.c
@@ -0,0 +1,452 @@
+/*
+ * 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 "common.h"
+#include "filebuf.h"
+#include "fileops.h"
+
+#define GIT_LOCK_FILE_MODE 0644
+
+static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
+
+enum buferr_t {
+	BUFERR_OK = 0,
+	BUFERR_WRITE,
+	BUFERR_ZLIB,
+	BUFERR_MEM
+};
+
+#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
+
+static int verify_last_error(git_filebuf *file)
+{
+	switch (file->last_error) {
+	case BUFERR_WRITE:
+		giterr_set(GITERR_OS, "Failed to write out file");
+		return -1;
+
+	case BUFERR_MEM:
+		giterr_set_oom();
+		return -1;
+
+	case BUFERR_ZLIB:
+		giterr_set(GITERR_ZLIB,
+			"Buffer error when writing out ZLib data");
+		return -1;
+
+	default:
+		return 0;
+	}
+}
+
+static int lock_file(git_filebuf *file, int flags)
+{
+	if (git_path_exists(file->path_lock) == true) {
+		if (flags & GIT_FILEBUF_FORCE)
+			p_unlink(file->path_lock);
+		else {
+			giterr_set(GITERR_OS,
+				"Failed to lock file '%s' for writing", file->path_lock);
+			return -1;
+		}
+	}
+
+	/* create path to the file buffer is required */
+	if (flags & GIT_FILEBUF_FORCE) {
+		/* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
+		file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, GIT_LOCK_FILE_MODE);
+	} else {
+		file->fd = git_futils_creat_locked(file->path_lock, GIT_LOCK_FILE_MODE);
+	}
+
+	if (file->fd < 0)
+		return -1;
+
+	file->fd_is_open = true;
+
+	if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
+		git_file source;
+		char buffer[2048];
+		size_t read_bytes;
+
+		source = p_open(file->path_original, O_RDONLY);
+		if (source < 0) {
+			giterr_set(GITERR_OS,
+				"Failed to open file '%s' for reading",
+				file->path_original);
+			return -1;
+		}
+
+		while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
+			p_write(file->fd, buffer, read_bytes);
+			if (file->digest)
+				git_hash_update(file->digest, buffer, read_bytes);
+		}
+
+		p_close(source);
+	}
+
+	return 0;
+}
+
+void git_filebuf_cleanup(git_filebuf *file)
+{
+	if (file->fd_is_open && file->fd >= 0)
+		p_close(file->fd);
+
+	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->buffer)
+		git__free(file->buffer);
+
+	/* use the presence of z_buf to decide if we need to deflateEnd */
+	if (file->z_buf) {
+		git__free(file->z_buf);
+		deflateEnd(&file->zs);
+	}
+
+	if (file->path_original)
+		git__free(file->path_original);
+	if (file->path_lock)
+		git__free(file->path_lock);
+
+	memset(file, 0x0, sizeof(git_filebuf));
+	file->fd = -1;
+}
+
+GIT_INLINE(int) flush_buffer(git_filebuf *file)
+{
+	int result = file->write(file, file->buffer, file->buf_pos);
+	file->buf_pos = 0;
+	return result;
+}
+
+static int write_normal(git_filebuf *file, void *source, size_t len)
+{
+	if (len > 0) {
+		if (p_write(file->fd, (void *)source, len) < 0) {
+			file->last_error = BUFERR_WRITE;
+			return -1;
+		}
+
+		if (file->digest)
+			git_hash_update(file->digest, source, len);
+	}
+
+	return 0;
+}
+
+static int write_deflate(git_filebuf *file, void *source, size_t len)
+{
+	z_stream *zs = &file->zs;
+
+	if (len > 0 || file->flush_mode == Z_FINISH) {
+		zs->next_in = source;
+		zs->avail_in = (uInt)len;
+
+		do {
+			size_t have;
+
+			zs->next_out = file->z_buf;
+			zs->avail_out = (uInt)file->buf_size;
+
+			if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
+				file->last_error = BUFERR_ZLIB;
+				return -1;
+			}
+
+			have = file->buf_size - (size_t)zs->avail_out;
+
+			if (p_write(file->fd, file->z_buf, have) < 0) {
+				file->last_error = BUFERR_WRITE;
+				return -1;
+			}
+
+		} while (zs->avail_out == 0);
+
+		assert(zs->avail_in == 0);
+
+		if (file->digest)
+			git_hash_update(file->digest, source, len);
+	}
+
+	return 0;
+}
+
+int git_filebuf_open(git_filebuf *file, const char *path, int flags)
+{
+	int compression;
+	size_t path_len;
+
+	/* opening an already open buffer is a programming error;
+	 * assert that this never happens instead of returning
+	 * an error code */
+	assert(file && path && file->buffer == NULL);
+
+	memset(file, 0x0, sizeof(git_filebuf));
+
+	if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
+		file->do_not_buffer = true;
+
+	file->buf_size = WRITE_BUFFER_SIZE;
+	file->buf_pos = 0;
+	file->fd = -1;
+	file->last_error = BUFERR_OK;
+
+	/* Allocate the main cache buffer */
+	if (!file->do_not_buffer) {
+		file->buffer = git__malloc(file->buf_size);
+		GITERR_CHECK_ALLOC(file->buffer);
+	}
+
+	/* 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);
+	}
+
+	compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
+
+	/* If we are deflating on-write, */
+	if (compression != 0) {
+		/* Initialize the ZLib stream */
+		if (deflateInit(&file->zs, compression) != Z_OK) {
+			giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
+			goto cleanup;
+		}
+
+		/* Allocate the Zlib cache buffer */
+		file->z_buf = git__malloc(file->buf_size);
+		GITERR_CHECK_ALLOC(file->z_buf);
+
+		/* Never flush */
+		file->flush_mode = Z_NO_FLUSH;
+		file->write = &write_deflate;
+	} else {
+		file->write = &write_normal;
+	}
+
+	/* If we are writing to a temp file */
+	if (flags & GIT_FILEBUF_TEMPORARY) {
+		git_buf tmp_path = GIT_BUF_INIT;
+
+		/* Open the file as temporary for locking */
+		file->fd = git_futils_mktmp(&tmp_path, path);
+
+		if (file->fd < 0) {
+			git_buf_free(&tmp_path);
+			goto cleanup;
+		}
+		file->fd_is_open = true;
+
+		/* No original path */
+		file->path_original = NULL;
+		file->path_lock = git_buf_detach(&tmp_path);
+		GITERR_CHECK_ALLOC(file->path_lock);
+	} else {
+		path_len = strlen(path);
+
+		/* Save the original path of the file */
+		file->path_original = git__strdup(path);
+		GITERR_CHECK_ALLOC(file->path_original);
+
+		/* create the locking path by appending ".lock" to the original */
+		file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
+		GITERR_CHECK_ALLOC(file->path_lock);
+
+		memcpy(file->path_lock, file->path_original, path_len);
+		memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
+
+		/* open the file for locking */
+		if (lock_file(file, flags) < 0)
+			goto cleanup;
+	}
+
+	return 0;
+
+cleanup:
+	git_filebuf_cleanup(file);
+	return -1;
+}
+
+int git_filebuf_hash(git_oid *oid, git_filebuf *file)
+{
+	assert(oid && file && file->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;
+
+	return 0;
+}
+
+int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
+{
+	git__free(file->path_original);
+	file->path_original = git__strdup(path);
+	GITERR_CHECK_ALLOC(file->path_original);
+
+	return git_filebuf_commit(file, mode);
+}
+
+int git_filebuf_commit(git_filebuf *file, mode_t mode)
+{
+	/* temporary files cannot be committed */
+	assert(file && file->path_original);
+
+	file->flush_mode = Z_FINISH;
+	flush_buffer(file);
+
+	if (verify_last_error(file) < 0)
+		goto on_error;
+
+	p_close(file->fd);
+	file->fd = -1;
+	file->fd_is_open = false;
+
+	if (p_chmod(file->path_lock, mode)) {
+		giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
+		goto on_error;
+	}
+
+	p_unlink(file->path_original);
+
+	if (p_rename(file->path_lock, file->path_original) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
+		goto on_error;
+	}
+
+	git_filebuf_cleanup(file);
+	return 0;
+
+on_error:
+	git_filebuf_cleanup(file);
+	return -1;
+}
+
+GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
+{
+	memcpy(file->buffer + file->buf_pos, buf, len);
+	file->buf_pos += len;
+}
+
+int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
+{
+	const unsigned char *buf = buff;
+
+	ENSURE_BUF_OK(file);
+
+	if (file->do_not_buffer)
+		return file->write(file, (void *)buff, len);
+
+	for (;;) {
+		size_t space_left = file->buf_size - file->buf_pos;
+
+		/* cache if it's small */
+		if (space_left > len) {
+			add_to_cache(file, buf, len);
+			return 0;
+		}
+
+		add_to_cache(file, buf, space_left);
+		if (flush_buffer(file) < 0)
+			return -1;
+
+		len -= space_left;
+		buf += space_left;
+	}
+}
+
+int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
+{
+	size_t space_left = file->buf_size - file->buf_pos;
+
+	*buffer = NULL;
+
+	ENSURE_BUF_OK(file);
+
+	if (len > file->buf_size) {
+		file->last_error = BUFERR_MEM;
+		return -1;
+	}
+
+	if (space_left <= len) {
+		if (flush_buffer(file) < 0)
+			return -1;
+	}
+
+	*buffer = (file->buffer + file->buf_pos);
+	file->buf_pos += len;
+
+	return 0;
+}
+
+int git_filebuf_printf(git_filebuf *file, const char *format, ...)
+{
+	va_list arglist;
+	size_t space_left;
+	int len, res;
+	char *tmp_buffer;
+
+	ENSURE_BUF_OK(file);
+
+	space_left = file->buf_size - file->buf_pos;
+
+	do {
+		va_start(arglist, format);
+		len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
+		va_end(arglist);
+
+		if (len < 0) {
+			file->last_error = BUFERR_MEM;
+			return -1;
+		}
+
+		if ((size_t)len + 1 <= space_left) {
+			file->buf_pos += len;
+			return 0;
+		}
+
+		if (flush_buffer(file) < 0)
+			return -1;
+
+		space_left = file->buf_size - file->buf_pos;
+
+	} while ((size_t)len + 1 <= space_left);
+
+	tmp_buffer = git__malloc(len + 1);
+	if (!tmp_buffer) {
+		file->last_error = BUFERR_MEM;
+		return -1;
+	}
+
+	va_start(arglist, format);
+	len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
+	va_end(arglist);
+
+	if (len < 0) {
+		git__free(tmp_buffer);
+		file->last_error = BUFERR_MEM;
+		return -1;
+	}
+
+	res = git_filebuf_write(file, tmp_buffer, len);
+	git__free(tmp_buffer);
+
+	return res;
+}
+
diff --git a/libgit2/src/filebuf.h b/libgit2/src/filebuf.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/filebuf.h
@@ -0,0 +1,85 @@
+/*
+ * 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_filebuf_h__
+#define INCLUDE_filebuf_h__
+
+#include "fileops.h"
+#include "hash.h"
+#include <zlib.h>
+
+#ifdef GIT_THREADS
+#	define GIT_FILEBUF_THREADS
+#endif
+
+#define GIT_FILEBUF_HASH_CONTENTS		(1 << 0)
+#define GIT_FILEBUF_APPEND				(1 << 2)
+#define GIT_FILEBUF_FORCE				(1 << 3)
+#define GIT_FILEBUF_TEMPORARY			(1 << 4)
+#define GIT_FILEBUF_DO_NOT_BUFFER		(1 << 5)
+#define GIT_FILEBUF_DEFLATE_SHIFT		(6)
+
+#define GIT_FILELOCK_EXTENSION ".lock\0"
+#define GIT_FILELOCK_EXTLENGTH 6
+
+struct git_filebuf {
+	char *path_original;
+	char *path_lock;
+
+	int (*write)(struct git_filebuf *file, void *source, size_t len);
+
+	git_hash_ctx *digest;
+
+	unsigned char *buffer;
+	unsigned char *z_buf;
+
+	z_stream zs;
+	int flush_mode;
+
+	size_t buf_size, buf_pos;
+	git_file fd;
+	bool fd_is_open;
+	bool do_not_buffer;
+	int last_error;
+};
+
+typedef struct git_filebuf git_filebuf;
+
+#define GIT_FILEBUF_INIT {0}
+
+/*
+ * The git_filebuf object lifecycle is:
+ * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
+ *
+ * - Call git_filebuf_open() to initialize the filebuf for use.
+ *
+ * - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
+ *   git_filebuf_reserve() as you like. The error codes for these
+ *   functions don't need to be checked. They are stored internally
+ *   by the file buffer.
+ *
+ * - While you are writing, you may call git_filebuf_hash() to get
+ *   the hash of all you have written so far. This function will
+ *   fail if any of the previous writes to the buffer failed.
+ *
+ * - To close the git_filebuf, you may call git_filebuf_commit() or
+ *   git_filebuf_commit_at() to save the file, or
+ *   git_filebuf_cleanup() to abandon the file.  All of these will
+ *   free the git_filebuf object. Likewise, all of these will fail
+ *   if any of the previous writes to the buffer failed, and set
+ *   an error code accordingly.
+ */
+int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
+int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
+int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
+
+int git_filebuf_open(git_filebuf *lock, const char *path, int flags);
+int git_filebuf_commit(git_filebuf *lock, mode_t mode);
+int git_filebuf_commit_at(git_filebuf *lock, const char *path, mode_t mode);
+void git_filebuf_cleanup(git_filebuf *lock);
+int git_filebuf_hash(git_oid *oid, git_filebuf *file);
+
+#endif
diff --git a/libgit2/src/fileops.c b/libgit2/src/fileops.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fileops.c
@@ -0,0 +1,475 @@
+/*
+ * 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 "fileops.h"
+#include <ctype.h>
+
+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;
+}
+
+int git_futils_mktmp(git_buf *path_out, const char *filename)
+{
+	int fd;
+
+	git_buf_sets(path_out, filename);
+	git_buf_puts(path_out, "_git2_XXXXXX");
+
+	if (git_buf_oom(path_out))
+		return -1;
+
+	if ((fd = p_mkstemp(path_out->ptr)) < 0) {
+		giterr_set(GITERR_OS,
+			"Failed to create temporary file '%s'", path_out->ptr);
+		return -1;
+	}
+
+	return fd;
+}
+
+int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
+{
+	int fd;
+
+	if (git_futils_mkpath2file(path, dirmode) < 0)
+		return -1;
+
+	fd = p_creat(path, mode);
+	if (fd < 0) {
+		giterr_set(GITERR_OS, "Failed to create file '%s'", path);
+		return -1;
+	}
+
+	return fd;
+}
+
+int git_futils_creat_locked(const char *path, const mode_t mode)
+{
+	int fd;
+
+#ifdef GIT_WIN32
+	wchar_t* buf;
+
+	buf = gitwin_to_utf16(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
+
+	if (fd < 0) {
+		giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
+		return -1;
+	}
+
+	return fd;
+}
+
+int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
+{
+	if (git_futils_mkpath2file(path, dirmode) < 0)
+		return -1;
+
+	return git_futils_creat_locked(path, mode);
+}
+
+int git_futils_open_ro(const char *path)
+{
+	int fd = p_open(path, O_RDONLY);
+	if (fd < 0) {
+		if (errno == ENOENT)
+			fd = GIT_ENOTFOUND;
+		giterr_set(GITERR_OS, "Failed to open '%s'", path);
+	}
+	return fd;
+}
+
+git_off_t git_futils_filesize(git_file fd)
+{
+	struct stat sb;
+
+	if (p_fstat(fd, &sb)) {
+		giterr_set(GITERR_OS, "Failed to stat file descriptor");
+		return -1;
+	}
+
+	return sb.st_size;
+}
+
+mode_t git_futils_canonical_mode(mode_t raw_mode)
+{
+	if (S_ISREG(raw_mode))
+		return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
+	else if (S_ISLNK(raw_mode))
+		return S_IFLNK;
+	else if (S_ISGITLINK(raw_mode))
+		return S_IFGITLINK;
+	else if (S_ISDIR(raw_mode))
+		return S_IFDIR;
+	else
+		return 0;
+}
+
+int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
+{
+	git_file fd;
+	size_t len;
+	struct stat st;
+
+	assert(buf && path && *path);
+
+	if (updated != NULL)
+		*updated = 0;
+
+	if ((fd = git_futils_open_ro(path)) < 0)
+		return fd;
+
+	if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
+		p_close(fd);
+		giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
+		return -1;
+	}
+
+	/*
+	 * If we were given a time, we only want to read the file if it
+	 * has been modified.
+	 */
+	if (mtime != NULL && *mtime >= st.st_mtime) {
+		p_close(fd);
+		return 0;
+	}
+
+	if (mtime != NULL)
+		*mtime = st.st_mtime;
+
+	len = (size_t) st.st_size;
+
+	git_buf_clear(buf);
+
+	if (git_buf_grow(buf, len + 1) < 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)
+		*updated = 1;
+
+	return 0;
+}
+
+int git_futils_readbuffer(git_buf *buf, const char *path)
+{
+	return git_futils_readbuffer_updated(buf, path, NULL, NULL);
+}
+
+int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
+{
+	if (git_futils_mkpath2file(to, dirmode) < 0)
+		return -1;
+
+	if (p_rename(from, to) < 0) {
+		giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
+{
+	return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
+}
+
+int git_futils_mmap_ro_file(git_map *out, const char *path)
+{
+	git_file fd = git_futils_open_ro(path);
+	git_off_t len;
+	int result;
+
+	if (fd < 0)
+		return fd;
+
+	len = git_futils_filesize(fd);
+	if (!git__is_sizet(len)) {
+		giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
+		return -1;
+	}
+
+	result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+	p_close(fd);
+	return result;
+}
+
+void git_futils_mmap_free(git_map *out)
+{
+	p_munmap(out);
+}
+
+int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
+{
+	int root_path_offset;
+	git_buf make_path = GIT_BUF_INIT;
+	size_t start;
+	char *pp, *sp;
+	bool failed = false;
+
+	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;
+	}
+
+	pp = make_path.ptr + start;
+
+	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:) */
+
+	while (!failed && (sp = strchr(pp, '/')) != NULL) {
+		if (sp != pp && git_path_isdir(make_path.ptr) == false) {
+			*sp = 0;
+
+			/* Do not choke while trying to recreate an existing directory */
+			if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+				failed = true;
+
+			*sp = '/';
+		}
+
+		pp = sp + 1;
+	}
+
+	if (*pp != '\0' && !failed) {
+		if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
+			failed = true;
+	}
+
+	git_buf_free(&make_path);
+
+	if (failed) {
+		giterr_set(GITERR_OS,
+			"Failed to create directory structure at '%s'", path);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
+{
+	git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
+
+	assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
+		|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
+		|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
+
+	if (git_path_isdir(path->ptr) == true) {
+		if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
+			return -1;
+
+		if (p_rmdir(path->ptr) < 0) {
+			if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
+				return 0;
+
+			giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
+			return -1;
+		}
+
+		return 0;
+	}
+
+	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;
+		}
+
+		return 0;
+	}
+
+	if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
+		giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
+		return -1;
+	}
+
+	return 0;
+}
+
+int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
+{
+	int error;
+	git_buf p = GIT_BUF_INIT;
+
+	error = git_buf_sets(&p, path);
+	if (!error)
+		error = _rmdir_recurs_foreach(&removal_type, &p);
+	git_buf_free(&p);
+	return error;
+}
+
+int git_futils_find_global_file(git_buf *path, const char *filename)
+{
+	const char *home = getenv("HOME");
+
+#ifdef GIT_WIN32
+	if (home == NULL)
+		home = getenv("USERPROFILE");
+#endif
+
+	if (home == NULL) {
+		giterr_set(GITERR_OS, "Global file lookup failed. "
+			"Cannot locate the user's home directory");
+		return -1;
+	}
+
+	if (git_buf_joinpath(path, home, filename) < 0)
+		return -1;
+
+	if (git_path_exists(path->ptr) == false) {
+		git_buf_clear(path);
+		return GIT_ENOTFOUND;
+	}
+
+	return 0;
+}
+
+#ifdef GIT_WIN32
+typedef struct {
+	wchar_t *path;
+	DWORD len;
+} win32_path;
+
+static const win32_path *win32_system_root(void)
+{
+	static win32_path s_root = { 0, 0 };
+
+	if (s_root.path == NULL) {
+		const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
+
+		s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
+		if (s_root.len <= 0) {
+			giterr_set(GITERR_OS, "Failed to expand environment strings");
+			return NULL;
+		}
+
+		s_root.path = git__calloc(s_root.len, sizeof(wchar_t));
+		if (s_root.path == NULL)
+			return NULL;
+
+		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;
+		}
+	}
+
+	return &s_root;
+}
+
+static int win32_find_system_file(git_buf *path, const char *filename)
+{
+	int error = 0;
+	const win32_path *root = win32_system_root();
+	size_t len;
+	wchar_t *file_utf16 = NULL, *scan;
+	char *file_utf8 = NULL;
+
+	if (!root || !filename || (len = strlen(filename)) == 0)
+		return GIT_ENOTFOUND;
+
+	/* allocate space for wchar_t path to file */
+	file_utf16 = git__calloc(root->len + len + 2, 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++;
+
+	if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
+		(int)len + 1) {
+		error = -1;
+		goto cleanup;
+	}
+
+	for (scan = file_utf16; *scan; scan++)
+		if (*scan == L'/')
+			*scan = L'\\';
+
+	/* check access */
+	if (_waccess(file_utf16, F_OK) < 0) {
+		error = GIT_ENOTFOUND;
+		goto cleanup;
+	}
+
+	/* convert to utf8 */
+	if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
+		error = -1;
+	else {
+		git_path_mkposix(file_utf8);
+		git_buf_attach(path, file_utf8, 0);
+	}
+
+cleanup:
+	git__free(file_utf16);
+
+	return error;
+}
+#endif
+
+int git_futils_find_system_file(git_buf *path, const char *filename)
+{
+	if (git_buf_joinpath(path, "/etc", filename) < 0)
+		return -1;
+
+	if (git_path_exists(path->ptr) == true)
+		return 0;
+
+	git_buf_clear(path);
+
+#ifdef GIT_WIN32
+	return win32_find_system_file(path, filename);
+#else
+	return GIT_ENOTFOUND;
+#endif
+}
diff --git a/libgit2/src/fileops.h b/libgit2/src/fileops.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/fileops.h
@@ -0,0 +1,179 @@
+/*
+ * 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_fileops_h__
+#define INCLUDE_fileops_h__
+
+#include "common.h"
+#include "map.h"
+#include "posix.h"
+#include "path.h"
+
+/**
+ * Filebuffer methods
+ *
+ * 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);
+
+/**
+ * File utils
+ *
+ * These are custom filesystem-related helper methods. They are
+ * rather high level, and wrap the underlying POSIX methods
+ *
+ * All these methods return 0 on success,
+ * or an error code on failure and an error message is set.
+ */
+
+/**
+ * Create and open a file, while also
+ * creating all the folders in its path
+ */
+extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);
+
+/**
+ * Create an open a process-locked file
+ */
+extern int git_futils_creat_locked(const char *path, const mode_t mode);
+
+/**
+ * Create an open a process-locked file, while
+ * also creating all the folders in its path
+ */
+extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
+
+/**
+ * Create a path recursively
+ */
+extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode);
+
+/**
+ * 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);
+
+typedef enum {
+	GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
+	GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
+	GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
+} git_directory_removal_type;
+
+/**
+ * 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).
+ *
+ * @return 0 on success; -1 on error.
+ */
+extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
+
+/**
+ * Create and open a temporary file with a `_git2_` suffix.
+ * Writes the filename into path_out.
+ * @return On success, an open file descriptor, else an error code < 0.
+ */
+extern int git_futils_mktmp(git_buf *path_out, const char *filename);
+
+/**
+ * Move a file on the filesystem, create the
+ * destination path if it doesn't exist
+ */
+extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
+
+/**
+ * Open a file readonly and set error if needed.
+ */
+extern int git_futils_open_ro(const char *path);
+
+/**
+ * Get the filesize in bytes of a file
+ */
+extern git_off_t git_futils_filesize(git_file fd);
+
+#define GIT_MODE_PERMS_MASK			0777
+#define GIT_CANONICAL_PERMS(MODE)	(((MODE) & 0100) ? 0755 : 0644)
+#define GIT_MODE_TYPE(MODE)			((MODE) & ~GIT_MODE_PERMS_MASK)
+
+/**
+ * Convert a mode_t from the OS to a legal git mode_t value.
+ */
+extern mode_t git_futils_canonical_mode(mode_t raw_mode);
+
+
+/**
+ * Read-only map all or part of a file into memory.
+ * When possible this function should favor a virtual memory
+ * style mapping over some form of malloc()+read(), as the
+ * data access will be random and is not likely to touch the
+ * majority of the region requested.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param fd open descriptor to configure the mapping from.
+ * @param begin first byte to map, this should be page aligned.
+ * @param end number of bytes to map.
+ * @return
+ * - 0 on success;
+ * - -1 on error.
+ */
+extern int git_futils_mmap_ro(
+	git_map *out,
+	git_file fd,
+	git_off_t begin,
+	size_t len);
+
+/**
+ * Read-only map an entire file.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param path path to file to be opened.
+ * @return
+ * - 0 on success;
+ * - GIT_ENOTFOUND if not found;
+ * - -1 on an unspecified OS related error.
+ */
+extern int git_futils_mmap_ro_file(
+	git_map *out,
+	const char *path);
+
+/**
+ * Release the memory associated with a previous memory mapping.
+ * @param map the mapping description previously configured.
+ */
+extern void git_futils_mmap_free(git_map *map);
+
+/**
+ * Find a "global" file (i.e. one in a user's home directory).
+ *
+ * @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.
+ */
+extern int git_futils_find_global_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.
+ */
+extern int git_futils_find_system_file(git_buf *path, const char *filename);
+
+#endif /* INCLUDE_fileops_h__ */
diff --git a/libgit2/src/filter.c b/libgit2/src/filter.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/filter.c
@@ -0,0 +1,165 @@
+/*
+ * 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 "fileops.h"
+#include "hash.h"
+#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;
+}
+
+int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
+{
+	int error;
+
+	if (mode == GIT_FILTER_TO_ODB) {
+		/* Load the CRLF cleanup filter when writing to the ODB */
+		error = git_filter_add__crlf_to_odb(filters, repo, path);
+		if (error < 0)
+			return error;
+	} else {
+		giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
+		return -1;
+	}
+
+	return (int)filters->length;
+}
+
+void git_filters_free(git_vector *filters)
+{
+	size_t i;
+	git_filter *filter;
+
+	git_vector_foreach(filters, i, filter) {
+		if (filter->do_free != NULL)
+			filter->do_free(filter);
+		else
+			git__free(filter);
+	}
+
+	git_vector_free(filters);
+}
+
+int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
+{
+	unsigned int i, src;
+	git_buf *dbuffer[2];
+
+	dbuffer[0] = source;
+	dbuffer[1] = dest;
+
+	src = 0;
+
+	if (git_buf_len(source) == 0) {
+		git_buf_clear(dest);
+		return 0;
+	}
+
+	/* Pre-grow the destination buffer to more or less the size
+	 * we expect it to have */
+	if (git_buf_grow(dest, git_buf_len(source)) < 0)
+		return -1;
+
+	for (i = 0; i < filters->length; ++i) {
+		git_filter *filter = git_vector_get(filters, i);
+		unsigned int dst = 1 - src;
+
+		git_buf_clear(dbuffer[dst]);
+
+		/* Apply the filter from dbuffer[src] to the other buffer;
+		 * if the filtering is canceled by the user mid-filter,
+		 * we skip to the next filter without changing the source
+		 * of the double buffering (so that the text goes through
+		 * cleanly).
+		 */
+		if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
+			src = dst;
+
+		if (git_buf_oom(dbuffer[dst]))
+			return -1;
+	}
+
+	/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
+	if (src != 1)
+		git_buf_swap(dest, source);
+
+	return 0;
+}
+
diff --git a/libgit2/src/filter.h b/libgit2/src/filter.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/filter.h
@@ -0,0 +1,119 @@
+/*
+ * 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_filter_h__
+#define INCLUDE_filter_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+
+typedef struct git_filter {
+	int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
+	void (*do_free)(struct git_filter *self);
+} git_filter;
+
+typedef enum {
+	GIT_FILTER_TO_WORKTREE,
+	GIT_FILTER_TO_ODB
+} git_filter_mode;
+
+typedef enum {
+	GIT_CRLF_GUESS = -1,
+	GIT_CRLF_BINARY = 0,
+	GIT_CRLF_TEXT,
+	GIT_CRLF_INPUT,
+	GIT_CRLF_CRLF,
+	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
+ */
+
+/*
+ * For any given path in the working directory, fill the `filters`
+ * array with the relevant filters that need to be applied.
+ *
+ * Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
+ * filters that will be used when checking out a file to the working
+ * directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
+ * a file to the ODB.
+ *
+ * @param filters Vector where to store all the loaded filters
+ * @param repo Repository object that contains `path`
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @return the number of filters loaded for the file (0 if the file
+ *	doesn't need filtering), or a negative error code
+ */
+extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
+
+/*
+ * Apply one or more filters to a file.
+ *
+ * The file must have been loaded as a `git_buf` object. Both the `source`
+ * and `dest` buffers are owned by the caller and must be freed once
+ * they are no longer needed.
+ *
+ * NOTE: Because of the double-buffering schema, the `source` buffer that contains
+ * the original file may be tampered once the filtering is complete. Regardless, 
+ * the `dest` buffer will always contain the final result of the filtering
+ *
+ * @param dest Buffer to store the result of the filtering
+ * @param source Buffer containing the document to filter
+ * @param filters A non-empty vector of filters as supplied by `git_filters_load`
+ * @return 0 on success, an error code otherwise
+ */
+extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
+
+/*
+ * Free the `filters` array generated by `git_filters_load`.
+ *
+ * Note that this frees both the array and its contents. The array will
+ * be clean/reusable after this call.
+ *
+ * @param filters A filters array as supplied by `git_filters_load`
+ */
+extern void git_filters_free(git_vector *filters);
+
+/*
+ * Available filters
+ */
+
+/* Strip CRLF, from Worktree to ODB */
+extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
+
+
+/*
+ * 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);
+
+#endif
diff --git a/libgit2/src/global.c b/libgit2/src/global.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/global.c
@@ -0,0 +1,134 @@
+/*
+ * 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 "global.h"
+#include "git2/threads.h" 
+#include "thread-utils.h"
+
+/**
+ * Handle the global state with TLS
+ *
+ * If libgit2 is built with GIT_THREADS enabled,
+ * the `git_threads_init()` function must be called
+ * before calling any other function of the library.
+ *
+ * This function allocates a TLS index (using pthreads
+ * or the native Win32 API) to store the global state
+ * on a per-thread basis.
+ *
+ * Any internal method that requires global state will
+ * then call `git__global_state()` which returns a pointer
+ * to the global state structure; this pointer is lazily
+ * allocated on each thread.
+ *
+ * Before shutting down the library, the
+ * `git_threads_shutdown` method must be called to free
+ * the previously reserved TLS index.
+ *
+ * If libgit2 is built without threading support, the
+ * `git__global_statestate()` call returns a pointer to a single,
+ * statically allocated global state. The `git_thread_`
+ * functions are not available in that case.
+ */
+
+#if defined(GIT_THREADS) && defined(GIT_WIN32)
+
+static DWORD _tls_index;
+static int _tls_init = 0;
+
+void git_threads_init(void)
+{
+	if (_tls_init)
+		return;
+
+	_tls_index = TlsAlloc();
+	_tls_init = 1;
+}
+
+void git_threads_shutdown(void)
+{
+	TlsFree(_tls_index);
+	_tls_init = 0;
+}
+
+git_global_st *git__global_state(void)
+{
+	void *ptr;
+
+	if ((ptr = TlsGetValue(_tls_index)) != NULL)
+		return ptr;
+
+	ptr = git__malloc(sizeof(git_global_st));
+	if (!ptr)
+		return NULL;
+
+	memset(ptr, 0x0, sizeof(git_global_st));
+	TlsSetValue(_tls_index, ptr);
+	return ptr;
+}
+
+#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
+
+static pthread_key_t _tls_key;
+static int _tls_init = 0;
+
+static void cb__free_status(void *st)
+{
+	git__free(st);
+}
+
+void git_threads_init(void)
+{
+	if (_tls_init)
+		return;
+
+	pthread_key_create(&_tls_key, &cb__free_status);
+	_tls_init = 1;
+}
+
+void git_threads_shutdown(void)
+{
+	pthread_key_delete(_tls_key);
+	_tls_init = 0;
+}
+
+git_global_st *git__global_state(void)
+{
+	void *ptr;
+
+	if ((ptr = pthread_getspecific(_tls_key)) != NULL)
+		return ptr;
+
+	ptr = git__malloc(sizeof(git_global_st));
+	if (!ptr)
+		return NULL;
+
+	memset(ptr, 0x0, sizeof(git_global_st));
+	pthread_setspecific(_tls_key, ptr);
+	return ptr;
+}
+
+#else
+
+static git_global_st __state;
+
+void git_threads_init(void)
+{
+	/* noop */ 
+}
+
+void git_threads_shutdown(void)
+{
+	/* noop */
+}
+
+git_global_st *git__global_state(void)
+{
+	return &__state;
+}
+
+#endif /* GIT_THREADS */
diff --git a/libgit2/src/global.h b/libgit2/src/global.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/global.h
@@ -0,0 +1,27 @@
+/*
+ * 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_global_h__
+#define INCLUDE_global_h__
+
+#include "mwindow.h"
+
+typedef struct {
+	struct {
+		char last[1024];
+	} error;
+
+	git_error *last_error;
+	git_error error_t;
+
+	git_mwindow_ctl mem_ctl;
+} git_global_st;
+
+git_global_st *git__global_state(void);
+
+#define GIT_GLOBAL (git__global_state())
+
+#endif
diff --git a/libgit2/src/hash.c b/libgit2/src/hash.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/hash.c
@@ -0,0 +1,74 @@
+/*
+ * 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 "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)
+{
+	git_hash_ctx *ctx = git__malloc(sizeof(*ctx));
+
+	if (!ctx)
+		return NULL;
+
+	SHA1_Init(&ctx->c);
+
+	return ctx;
+}
+
+void git_hash_free_ctx(git_hash_ctx *ctx)
+{
+	git__free(ctx);
+}
+
+void git_hash_init(git_hash_ctx *ctx)
+{
+	assert(ctx);
+	SHA1_Init(&ctx->c);
+}
+
+void git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+	assert(ctx);
+	SHA1_Update(&ctx->c, data, len);
+}
+
+void git_hash_final(git_oid *out, git_hash_ctx *ctx)
+{
+	assert(ctx);
+	SHA1_Final(out->id, &ctx->c);
+}
+
+void git_hash_buf(git_oid *out, const void *data, size_t len)
+{
+	SHA_CTX c;
+
+	SHA1_Init(&c);
+	SHA1_Update(&c, data, len);
+	SHA1_Final(out->id, &c);
+}
+
+void git_hash_vec(git_oid *out, git_buf_vec *vec, size_t n)
+{
+	SHA_CTX c;
+	size_t i;
+
+	SHA1_Init(&c);
+	for (i = 0; i < n; i++)
+		SHA1_Update(&c, vec[i].data, vec[i].len);
+	SHA1_Final(out->id, &c);
+}
diff --git a/libgit2/src/hash.h b/libgit2/src/hash.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/hash.h
@@ -0,0 +1,29 @@
+/*
+ * 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_hash_h__
+#define INCLUDE_hash_h__
+
+#include "git2/oid.h"
+
+typedef struct git_hash_ctx git_hash_ctx;
+
+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);
+
+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);
+
+#endif /* INCLUDE_hash_h__ */
diff --git a/libgit2/src/ignore.c b/libgit2/src/ignore.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/ignore.c
@@ -0,0 +1,203 @@
+#include "ignore.h"
+#include "path.h"
+
+#define GIT_IGNORE_INTERNAL		"[internal]exclude"
+#define GIT_IGNORE_FILE_INREPO	"info/exclude"
+#define GIT_IGNORE_FILE			".gitignore"
+
+static int parse_ignore_file(
+	git_repository *repo, const char *buffer, git_attr_file *ignores)
+{
+	int error = 0;
+	git_attr_fnmatch *match = NULL;
+	const char *scan = NULL;
+	char *context = NULL;
+
+	GIT_UNUSED(repo);
+
+	if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) {
+		context = ignores->key + 2;
+		context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0';
+	}
+
+	scan = buffer;
+
+	while (!error && *scan) {
+		if (!match) {
+			match = git__calloc(1, sizeof(*match));
+			GITERR_CHECK_ALLOC(match);
+		}
+
+		if (!(error = git_attr_fnmatch__parse(
+			match, ignores->pool, context, &scan)))
+		{
+			match->flags = match->flags | GIT_ATTR_FNMATCH_IGNORE;
+			scan = git__next_line(scan);
+			error = git_vector_insert(&ignores->rules, match);
+		}
+
+		if (error != 0) {
+			git__free(match->pattern);
+			match->pattern = NULL;
+
+			if (error == GIT_ENOTFOUND)
+				error = 0;
+		} else {
+			match = NULL; /* vector now "owns" the match */
+		}
+	}
+
+	git__free(match);
+	/* restore file path used for context */
+	if (context)
+		context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */
+
+	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))
+
+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);
+}
+
+int git_ignore__for_path(
+	git_repository *repo,
+	const char *path,
+	git_ignores *ignores)
+{
+	int error = 0;
+	const char *workdir = git_repository_workdir(repo);
+
+	assert(ignores);
+
+	ignores->repo = repo;
+	git_buf_init(&ignores->dir, 0);
+	ignores->ign_internal = NULL;
+
+	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)
+		goto cleanup;
+
+	/* given a unrooted path in a non-bare repo, resolve it */
+	if (workdir && git_path_root(path) < 0)
+		error = git_path_find_dir(&ignores->dir, path, workdir);
+	else
+		error = git_buf_sets(&ignores->dir, path);
+	if (error < 0)
+		goto cleanup;
+
+	/* set up internals */
+	error = git_attr_cache__internal_file(
+		repo, GIT_IGNORE_INTERNAL, &ignores->ign_internal);
+	if (error < 0)
+		goto cleanup;
+
+	/* load .gitignore up the path */
+	if (workdir != NULL) {
+		error = git_path_walk_up(
+			&ignores->dir, workdir, push_one_ignore, ignores);
+		if (error < 0)
+			goto cleanup;
+	}
+
+	/* load .git/info/exclude */
+	error = push_ignore_file(repo, &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,
+			git_repository_attr_cache(repo)->cfg_excl_file);
+
+cleanup:
+	if (error < 0)
+		git_ignore__free(ignores);
+
+	return error;
+}
+
+int git_ignore__push_dir(git_ignores *ign, const char *dir)
+{
+	if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
+		return -1;
+	else
+		return push_ignore_file(
+			ign->repo, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+}
+
+int git_ignore__pop_dir(git_ignores *ign)
+{
+	if (ign->ign_path.length > 0) {
+		git_attr_file *file = git_vector_last(&ign->ign_path);
+		if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
+			git_vector_pop(&ign->ign_path);
+		git_buf_rtruncate_at_char(&ign->dir, '/');
+	}
+	return 0;
+}
+
+void git_ignore__free(git_ignores *ignores)
+{
+	/* don't need to free ignores->ign_internal since it is in cache */
+	git_vector_free(&ignores->ign_path);
+	git_vector_free(&ignores->ign_global);
+	git_buf_free(&ignores->dir);
+}
+
+static bool ignore_lookup_in_rules(
+	git_vector *rules, git_attr_path *path, int *ignored)
+{
+	unsigned int j;
+	git_attr_fnmatch *match;
+
+	git_vector_rforeach(rules, j, match) {
+		if (git_attr_fnmatch__match(match, path)) {
+			*ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+int git_ignore__lookup(
+	git_ignores *ignores, const char *pathname, int *ignored)
+{
+	unsigned int i;
+	git_attr_file *file;
+	git_attr_path path;
+
+	if (git_attr_path__init(
+		&path, pathname, git_repository_workdir(ignores->repo)) < 0)
+		return -1;
+
+	/* 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;
+	}
+
+	*ignored = 0;
+
+cleanup:
+	git_attr_path__free(&path);
+	return 0;
+}
diff --git a/libgit2/src/ignore.h b/libgit2/src/ignore.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/ignore.h
@@ -0,0 +1,38 @@
+/*
+ * 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_ignore_h__
+#define INCLUDE_ignore_h__
+
+#include "repository.h"
+#include "vector.h"
+
+/* The git_ignores structure maintains three sets of ignores:
+ * - internal ignores
+ * - per directory ignores
+ * - global ignores (at lower priority than the others)
+ * As you traverse from one directory to another, you can push and pop
+ * directories onto git_ignores list efficiently.
+ */
+typedef struct {
+	git_repository *repo;
+	git_buf dir;
+	git_attr_file *ign_internal;
+	git_vector ign_path;
+	git_vector ign_global;
+} git_ignores;
+
+extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
+
+extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
+
+extern int git_ignore__pop_dir(git_ignores *ign);
+
+extern void git_ignore__free(git_ignores *ign);
+
+extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
+
+#endif
diff --git a/libgit2/src/index.c b/libgit2/src/index.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/index.c
@@ -0,0 +1,969 @@
+/*
+ * 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);
+}
diff --git a/libgit2/src/index.h b/libgit2/src/index.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/index.h
@@ -0,0 +1,38 @@
+/*
+ * 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_index_h__
+#define INCLUDE_index_h__
+
+#include "fileops.h"
+#include "filebuf.h"
+#include "vector.h"
+#include "tree-cache.h"
+#include "git2/odb.h"
+#include "git2/index.h"
+
+#define GIT_INDEX_FILE "index"
+#define GIT_INDEX_FILE_MODE 0666
+
+struct git_index {
+	git_refcount rc;
+
+	char *index_file_path;
+
+	time_t last_modified;
+	git_vector entries;
+
+	unsigned int on_disk:1;
+	git_tree_cache *tree;
+
+	git_vector unmerged;
+};
+
+extern void git_index__init_entry_from_stat(struct stat *st, git_index_entry *entry);
+
+extern unsigned int git_index__prefix_position(git_index *index, const char *path);
+
+#endif
diff --git a/libgit2/src/indexer.c b/libgit2/src/indexer.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/indexer.c
@@ -0,0 +1,899 @@
+/*
+ * 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 <zlib.h>
+
+#include "git2/indexer.h"
+#include "git2/object.h"
+#include "git2/oid.h"
+
+#include "common.h"
+#include "pack.h"
+#include "mwindow.h"
+#include "posix.h"
+#include "pack.h"
+#include "filebuf.h"
+#include "sha1.h"
+
+#define UINT31_MAX (0x7FFFFFFF)
+
+struct entry {
+	git_oid oid;
+	uint32_t crc;
+	uint32_t offset;
+	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;
+	struct git_pack_file *pack;
+	git_filebuf pack_file;
+	git_filebuf index_file;
+	git_off_t off;
+	size_t nr_objects;
+	git_vector objects;
+	git_vector deltas;
+	unsigned int fanout[256];
+	git_oid hash;
+};
+
+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)
+{
+	return &idx->hash;
+}
+
+static int open_pack(struct git_pack_file **out, const char *filename)
+{
+	size_t namelen;
+	struct git_pack_file *pack;
+	struct stat st;
+	int fd;
+
+	namelen = strlen(filename);
+	pack = git__calloc(1, sizeof(struct git_pack_file) + namelen + 1);
+	GITERR_CHECK_ALLOC(pack);
+
+	memcpy(pack->pack_name, filename, namelen + 1);
+
+	if (p_stat(filename, &st) < 0) {
+		giterr_set(GITERR_OS, "Failed to stat packfile.");
+		goto cleanup;
+	}
+
+	if ((fd = p_open(pack->pack_name, O_RDONLY)) < 0) {
+		giterr_set(GITERR_OS, "Failed to open packfile.");
+		goto cleanup;
+	}
+
+	pack->mwf.fd = fd;
+	pack->mwf.size = (git_off_t)st.st_size;
+
+	*out = pack;
+	return 0;
+
+cleanup:
+	git__free(pack);
+	return -1;
+}
+
+static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
+{
+	int error;
+
+	/* Verify we recognize this pack file format. */
+	if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) {
+		giterr_set(GITERR_OS, "Failed to read in pack header");
+		return error;
+	}
+
+	if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
+		giterr_set(GITERR_INDEXER, "Wrong pack signature");
+		return -1;
+	}
+
+	if (!pack_version_ok(hdr->hdr_version)) {
+		giterr_set(GITERR_INDEXER, "Wrong pack version");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int objects_cmp(const void *a, const void *b)
+{
+	const struct entry *entrya = a;
+	const struct entry *entryb = b;
+
+	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)
+{
+	git_indexer_stream *idx;
+	git_buf path = GIT_BUF_INIT;
+	static const char suff[] = "/objects/pack/pack-received";
+	int error;
+
+	idx = git__calloc(1, sizeof(git_indexer_stream));
+	GITERR_CHECK_ALLOC(idx);
+
+	error = git_buf_joinpath(&path, prefix, suff);
+	if (error < 0)
+		goto cleanup;
+
+	error = git_filebuf_open(&idx->pack_file, path.ptr,
+				 GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER);
+	git_buf_free(&path);
+	if (error < 0)
+		goto cleanup;
+
+	*out = idx;
+	return 0;
+
+cleanup:
+	git_buf_free(&path);
+	git_filebuf_cleanup(&idx->pack_file);
+	git__free(idx);
+	return -1;
+}
+
+/* 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)
+		return -1;
+
+	git_mwindow_close(&w);
+
+	/* 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;
+
+	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_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;
+	}
+
+	delta = git__calloc(1, sizeof(struct delta_info));
+	GITERR_CHECK_ALLOC(delta);
+	delta->delta_off = entry_start;
+
+	git__free(obj.data);
+
+	if (git_vector_insert(&idx->deltas, delta) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
+{
+	int i;
+	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));
+	GITERR_CHECK_ALLOC(entry);
+
+	if (entry_start > UINT31_MAX) {
+		entry->offset = UINT32_MAX;
+		entry->offset_long = entry_start;
+	} else {
+		entry->offset = (uint32_t)entry_start;
+	}
+
+	/* FIXME: Parse the object instead of hashing it */
+	if (git_odb__hashobj(&oid, obj) < 0) {
+		giterr_set(GITERR_INDEXER, "Failed to hash object");
+		return -1;
+	}
+
+	pentry = git__malloc(sizeof(struct git_pack_entry));
+	GITERR_CHECK_ALLOC(pentry);
+
+	git_oid_cpy(&pentry->sha1, &oid);
+	pentry->offset = entry_start;
+	if (git_vector_insert(&idx->pack->cache, pentry) < 0)
+		goto on_error;
+
+	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)
+		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;
+
+	for (i = oid.id[0]; i < 256; ++i) {
+		idx->fanout[i]++;
+	}
+
+	return 0;
+
+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)
+{
+	int error;
+	struct git_pack_header hdr;
+	size_t processed = stats->processed;
+	git_mwindow_file *mwf = &idx->pack->mwf;
+
+	assert(idx && data && stats);
+
+	if (git_filebuf_write(&idx->pack_file, data, size) < 0)
+		return -1;
+
+	/* Make sure we set the new size of the pack */
+	if (idx->opened_pack) {
+		idx->pack->mwf.size += size;
+		//printf("\nadding %zu for %zu\n", size, idx->pack->mwf.size);
+	} else {
+		if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0)
+			return -1;
+		idx->opened_pack = 1;
+		mwf = &idx->pack->mwf;
+		if (git_mwindow_file_register(&idx->pack->mwf) < 0)
+			return -1;
+
+		return 0;
+	}
+
+	if (!idx->parsed_header) {
+		if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
+			return 0;
+
+		if (parse_header(&hdr, idx->pack) < 0)
+			return -1;
+
+		idx->parsed_header = 1;
+		idx->nr_objects = ntohl(hdr.hdr_entries);
+		idx->off = sizeof(struct git_pack_header);
+
+		/* for now, limit to 2^32 objects */
+		assert(idx->nr_objects == (size_t)((unsigned int)idx->nr_objects));
+
+		if (git_vector_init(&idx->pack->cache, (unsigned int)idx->nr_objects, cache_cmp) < 0)
+			return -1;
+
+		idx->pack->has_cache = 1;
+		if (git_vector_init(&idx->objects, (unsigned int)idx->nr_objects, objects_cmp) < 0)
+			return -1;
+
+		if (git_vector_init(&idx->deltas, (unsigned int)(idx->nr_objects / 2), NULL) < 0)
+			return -1;
+
+		stats->total = (unsigned int)idx->nr_objects;
+		stats->processed = 0;
+	}
+
+	/* Now that we have data in the pack, let's try to parse it */
+
+	/* 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_off_t entry_start = idx->off;
+
+		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)
+				return 0;
+			if (error < 0)
+				return error;
+
+			continue;
+		}
+
+		if (hash_and_save(idx, &obj, entry_start) < 0)
+			goto on_error;
+
+		git__free(obj.data);
+
+		stats->processed = (unsigned int)++processed;
+	}
+
+	return 0;
+
+on_error:
+	git_mwindow_free_all(mwf);
+	return -1;
+}
+
+static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char *suffix)
+{
+	const char prefix[] = "pack-";
+	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;
+}
+
+static int resolve_deltas(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+	unsigned int i;
+	struct delta_info *delta;
+
+	git_vector_foreach(&idx->deltas, i, delta) {
+		git_rawobj obj;
+
+		idx->off = delta->delta_off;
+		if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
+			return -1;
+
+		if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+			return -1;
+
+		git__free(obj.data);
+		stats->processed++;
+	}
+
+	return 0;
+}
+
+int git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats)
+{
+	git_mwindow *w = NULL;
+	unsigned int i, long_offsets = 0, left;
+	struct git_pack_idx_header hdr;
+	git_buf filename = GIT_BUF_INIT;
+	struct entry *entry;
+	void *packfile_hash;
+	git_oid file_hash;
+	SHA_CTX ctx;
+
+	/* 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");
+		return -1;
+	}
+
+	if (idx->deltas.length > 0)
+		if (resolve_deltas(idx, stats) < 0)
+			return -1;
+
+	if (stats->processed != stats->total) {
+		giterr_set(GITERR_INDEXER, "Indexing error: early EOF");
+		return -1;
+	}
+
+	git_vector_sort(&idx->objects);
+
+	git_buf_sets(&filename, idx->pack->pack_name);
+	git_buf_truncate(&filename, filename.size - strlen("pack"));
+	git_buf_puts(&filename, "idx");
+	if (git_buf_oom(&filename))
+		return -1;
+
+	if (git_filebuf_open(&idx->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));
+
+	/* 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));
+	}
+
+	/* 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);
+	}
+	SHA1_Final(idx->hash.id, &ctx);
+
+	/* Write out the CRC32 values */
+	git_vector_foreach(&idx->objects, i, entry) {
+		git_filebuf_write(&idx->index_file, &entry->crc, sizeof(uint32_t));
+	}
+
+	/* 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);
+
+		git_filebuf_write(&idx->index_file, &n, sizeof(uint32_t));
+	}
+
+	/* 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);
+
+		git_filebuf_write(&idx->index_file, &split, sizeof(uint32_t) * 2);
+	}
+
+	/* Write out the packfile trailer */
+	packfile_hash = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ, &left);
+	if (packfile_hash == NULL) {
+		git_mwindow_close(&w);
+		goto on_error;
+	}
+
+	memcpy(&file_hash, packfile_hash, GIT_OID_RAWSZ);
+	git_mwindow_close(&w);
+
+	git_filebuf_write(&idx->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)
+		goto on_error;
+
+	git_filebuf_write(&idx->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)
+		goto on_error;
+
+	git_mwindow_free_all(&idx->pack->mwf);
+	p_close(idx->pack->mwf.fd);
+
+	if (index_path_stream(&filename, idx, ".pack") < 0)
+		goto on_error;
+	/* And don't forget to rename the packfile to its new place. */
+	if (git_filebuf_commit_at(&idx->pack_file, filename.ptr, GIT_PACK_FILE_MODE) < 0)
+		return -1;
+
+	git_buf_free(&filename);
+	return 0;
+
+on_error:
+	git_mwindow_free_all(&idx->pack->mwf);
+	p_close(idx->pack->mwf.fd);
+	git_filebuf_cleanup(&idx->index_file);
+	git_buf_free(&filename);
+	return -1;
+}
+
+void git_indexer_stream_free(git_indexer_stream *idx)
+{
+	unsigned int i;
+	struct entry *e;
+	struct git_pack_entry *pe;
+	struct delta_info *delta;
+
+	if (idx == NULL)
+		return;
+
+	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]++;
+		}
+
+		git__free(obj.data);
+
+		stats->processed = ++processed;
+	}
+
+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__free(idx);
+}
+
diff --git a/libgit2/src/iterator.c b/libgit2/src/iterator.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/iterator.c
@@ -0,0 +1,748 @@
+/*
+ * 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);
+}
+
diff --git a/libgit2/src/iterator.h b/libgit2/src/iterator.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/iterator.h
@@ -0,0 +1,151 @@
+/*
+ * 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_iterator_h__
+#define INCLUDE_iterator_h__
+
+#include "common.h"
+#include "git2/index.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_t;
+
+struct git_iterator {
+	git_iterator_type_t type;
+	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 *);
+};
+
+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);
+
+GIT_INLINE(int) git_iterator_for_index(
+	git_iterator **iter, git_repository *repo)
+{
+	return git_iterator_for_index_range(iter, repo, NULL, NULL);
+}
+
+extern int git_iterator_for_workdir_range(
+	git_iterator **iter, git_repository *repo,
+	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);
+}
+
+
+/* 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.
+ *
+ * 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.
+ */
+GIT_INLINE(int) git_iterator_current(
+	git_iterator *iter, const git_index_entry **entry)
+{
+	return iter->current(iter, entry);
+}
+
+GIT_INLINE(int) git_iterator_at_end(git_iterator *iter)
+{
+	return iter->at_end(iter);
+}
+
+GIT_INLINE(int) git_iterator_advance(
+	git_iterator *iter, const git_index_entry **entry)
+{
+	return iter->advance(iter, entry);
+}
+
+GIT_INLINE(int) git_iterator_seek(
+	git_iterator *iter, const char *prefix)
+{
+	return iter->seek(iter, prefix);
+}
+
+GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
+{
+	return iter->reset(iter);
+}
+
+GIT_INLINE(void) git_iterator_free(git_iterator *iter)
+{
+	if (iter == NULL)
+		return;
+
+	iter->free(iter);
+
+	git__free(iter->start);
+	git__free(iter->end);
+
+	memset(iter, 0, sizeof(*iter));
+
+	git__free(iter);
+}
+
+GIT_INLINE(git_iterator_type_t) git_iterator_type(git_iterator *iter)
+{
+	return iter->type;
+}
+
+extern int git_iterator_current_tree_entry(
+	git_iterator *iter, const git_tree_entry **tree_entry);
+
+extern int git_iterator_current_is_ignored(git_iterator *iter);
+
+/**
+ * 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 int git_iterator_cmp(
+	git_iterator *iter, const char *path_prefix);
+
+#endif
diff --git a/libgit2/src/khash.h b/libgit2/src/khash.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/khash.h
@@ -0,0 +1,608 @@
+/* The MIT License
+
+   Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+   Permission is hereby granted, free of charge, to any person obtaining
+   a copy of this software and associated documentation files (the
+   "Software"), to deal in the Software without restriction, including
+   without limitation the rights to use, copy, modify, merge, publish,
+   distribute, sublicense, and/or sell copies of the Software, and to
+   permit persons to whom the Software is furnished to do so, subject to
+   the following conditions:
+
+   The above copyright notice and this permission notice shall be
+   included in all copies or substantial portions of the Software.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+   SOFTWARE.
+*/
+
+/*
+  An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+	int ret, is_missing;
+	khiter_t k;
+	khash_t(32) *h = kh_init(32);
+	k = kh_put(32, h, 5, &ret);
+	kh_value(h, k) = 10;
+	k = kh_get(32, h, 10);
+	is_missing = (k == kh_end(h));
+	k = kh_get(32, h, 5);
+	kh_del(32, h, k);
+	for (k = kh_begin(h); k != kh_end(h); ++k)
+		if (kh_exist(h, k)) kh_value(h, k) = 1;
+	kh_destroy(32, h);
+	return 0;
+}
+*/
+
+/*
+  2011-12-29 (0.2.7):
+
+    * Minor code clean up; no actual effect.
+
+  2011-09-16 (0.2.6):
+
+	* The capacity is a power of 2. This seems to dramatically improve the
+	  speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+	   - http://code.google.com/p/ulib/
+	   - http://nothings.org/computer/judy/
+
+	* Allow to optionally use linear probing which usually has better
+	  performance for random input. Double hashing is still the default as it
+	  is more robust to certain non-random input.
+
+	* Added Wang's integer hash function (not used by default). This hash
+	  function is more robust to certain non-random input.
+
+  2011-02-14 (0.2.5):
+
+    * Allow to declare global functions.
+
+  2009-09-26 (0.2.4):
+
+    * Improve portability
+
+  2008-09-19 (0.2.3):
+
+	* Corrected the example
+	* Improved interfaces
+
+  2008-09-11 (0.2.2):
+
+	* Improved speed a little in kh_put()
+
+  2008-09-10 (0.2.1):
+
+	* Added kh_clear()
+	* Fixed a compiling error
+
+  2008-09-02 (0.2.0):
+
+	* Changed to token concatenation which increases flexibility.
+
+  2008-08-31 (0.1.2):
+
+	* Fixed a bug in kh_get(), which has not been tested previously.
+
+  2008-08-31 (0.1.1):
+
+	* Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+  @header
+
+  Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.6"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compipler specific configuration */
+
+#if UINT_MAX == 0xffffffffu
+typedef unsigned int khint32_t;
+#elif ULONG_MAX == 0xffffffffu
+typedef unsigned long khint32_t;
+#endif
+
+#if ULONG_MAX == ULLONG_MAX
+typedef unsigned long khint64_t;
+#else
+typedef unsigned long long khint64_t;
+#endif
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#ifdef KHASH_LINEAR
+#define __ac_inc(k, m) 1
+#else
+#define __ac_inc(k, m) (((k)>>3 ^ (k)<<3) | 1) & (m)
+#endif
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+	typedef struct { \
+		khint_t n_buckets, size, n_occupied, upper_bound; \
+		khint32_t *flags; \
+		khkey_t *keys; \
+		khval_t *vals; \
+	} kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t)	 					\
+	extern kh_##name##_t *kh_init_##name(void);							\
+	extern void kh_destroy_##name(kh_##name##_t *h);					\
+	extern void kh_clear_##name(kh_##name##_t *h);						\
+	extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); 	\
+	extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+	extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+	extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+	SCOPE kh_##name##_t *kh_init_##name(void) {							\
+		return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));		\
+	}																	\
+	SCOPE void kh_destroy_##name(kh_##name##_t *h)						\
+	{																	\
+		if (h) {														\
+			kfree((void *)h->keys); kfree(h->flags);					\
+			kfree((void *)h->vals);										\
+			kfree(h);													\
+		}																\
+	}																	\
+	SCOPE void kh_clear_##name(kh_##name##_t *h)						\
+	{																	\
+		if (h && h->flags) {											\
+			memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+			h->size = h->n_occupied = 0;								\
+		}																\
+	}																	\
+	SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) 	\
+	{																	\
+		if (h->n_buckets) {												\
+			khint_t inc, k, i, last, mask;								\
+			mask = h->n_buckets - 1;									\
+			k = __hash_func(key); i = k & mask;							\
+			inc = __ac_inc(k, mask); last = i; /* inc==1 for linear probing */ \
+			while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+				i = (i + inc) & mask; 									\
+				if (i == last) return h->n_buckets;						\
+			}															\
+			return __ac_iseither(h->flags, i)? h->n_buckets : i;		\
+		} else return 0;												\
+	}																	\
+	SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+	{ /* This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+		khint32_t *new_flags = 0;										\
+		khint_t j = 1;													\
+		{																\
+			kroundup32(new_n_buckets); 									\
+			if (new_n_buckets < 4) new_n_buckets = 4;					\
+			if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;	/* requested size is too small */ \
+			else { /* hash table size to be changed (shrink or expand); rehash */ \
+				new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));	\
+				if (!new_flags) return -1;								\
+				memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+				if (h->n_buckets < new_n_buckets) {	/* expand */		\
+					khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+					if (!new_keys) return -1;							\
+					h->keys = new_keys;									\
+					if (kh_is_map) {									\
+						khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+						if (!new_vals) return -1;						\
+						h->vals = new_vals;								\
+					}													\
+				} /* otherwise shrink */								\
+			}															\
+		}																\
+		if (j) { /* rehashing is needed */								\
+			for (j = 0; j != h->n_buckets; ++j) {						\
+				if (__ac_iseither(h->flags, j) == 0) {					\
+					khkey_t key = h->keys[j];							\
+					khval_t val;										\
+					khint_t new_mask;									\
+					new_mask = new_n_buckets - 1; 						\
+					if (kh_is_map) val = h->vals[j];					\
+					__ac_set_isdel_true(h->flags, j);					\
+					while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+						khint_t inc, k, i;								\
+						k = __hash_func(key);							\
+						i = k & new_mask;								\
+						inc = __ac_inc(k, new_mask);					\
+						while (!__ac_isempty(new_flags, i)) i = (i + inc) & new_mask; \
+						__ac_set_isempty_false(new_flags, i);			\
+						if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+							{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+							if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+							__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+						} else { /* write the element and jump out of the loop */ \
+							h->keys[i] = key;							\
+							if (kh_is_map) h->vals[i] = val;			\
+							break;										\
+						}												\
+					}													\
+				}														\
+			}															\
+			if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+				h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \
+				if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \
+			}															\
+			kfree(h->flags); /* free the working space */				\
+			h->flags = new_flags;										\
+			h->n_buckets = new_n_buckets;								\
+			h->n_occupied = h->size;									\
+			h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+		}																\
+		return 0;														\
+	}																	\
+	SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+	{																	\
+		khint_t x;														\
+		if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+			if (h->n_buckets > (h->size<<1)) {							\
+				if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+					*ret = -1; return h->n_buckets;						\
+				}														\
+			} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+				*ret = -1; return h->n_buckets;							\
+			}															\
+		} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+		{																\
+			khint_t inc, k, i, site, last, mask = h->n_buckets - 1;		\
+			x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+			if (__ac_isempty(h->flags, i)) x = i; /* for speed up */	\
+			else {														\
+				inc = __ac_inc(k, mask); last = i;						\
+				while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+					if (__ac_isdel(h->flags, i)) site = i;				\
+					i = (i + inc) & mask; 								\
+					if (i == last) { x = site; break; }					\
+				}														\
+				if (x == h->n_buckets) {								\
+					if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+					else x = i;											\
+				}														\
+			}															\
+		}																\
+		if (__ac_isempty(h->flags, x)) { /* not present at all */		\
+			h->keys[x] = key;											\
+			__ac_set_isboth_false(h->flags, x);							\
+			++h->size; ++h->n_occupied;									\
+			*ret = 1;													\
+		} else if (__ac_isdel(h->flags, x)) { /* deleted */				\
+			h->keys[x] = key;											\
+			__ac_set_isboth_false(h->flags, x);							\
+			++h->size;													\
+			*ret = 2;													\
+		} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+		return x;														\
+	}																	\
+	SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)				\
+	{																	\
+		if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {			\
+			__ac_set_isdel_true(h->flags, x);							\
+			--h->size;													\
+		}																\
+	}
+
+#define KHASH_DECLARE(name, khkey_t, khval_t)		 					\
+	__KHASH_TYPE(name, khkey_t, khval_t) 								\
+	__KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+	__KHASH_TYPE(name, khkey_t, khval_t) 								\
+	__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)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+  @abstract     Integer hash function
+  @param  key   The integer [khint32_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+  @abstract     Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     64-bit integer hash function
+  @param  key   The integer [khint64_t]
+  @return       The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+  @abstract     64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+  @abstract     const char* hash function
+  @param  s     Pointer to a null terminated string
+  @return       The hash value
+ */
+static 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;
+	return h;
+}
+/*! @function
+  @abstract     Another interface to const char* hash function
+  @param  key   Pointer to a null terminated string [const char*]
+  @return       The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+  @abstract     Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static inline khint_t __ac_Wang_hash(khint_t key)
+{
+    key += ~(key << 15);
+    key ^=  (key >> 10);
+    key +=  (key << 3);
+    key ^=  (key >> 6);
+    key += ~(key << 11);
+    key ^=  (key >> 16);
+    return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+  @abstract Type of the hash table.
+  @param  name  Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+  @abstract     Initiate a hash table.
+  @param  name  Name of the hash table [symbol]
+  @return       Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+  @abstract     Destroy a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+  @abstract     Reset a hash table without deallocating memory.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+  @abstract     Resize a hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  s     New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+  @abstract     Insert a key to the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @param  r     Extra return code: 0 if the key is present in the hash table;
+                1 if the bucket is empty (never used); 2 if the element in
+				the bucket has been deleted [int*]
+  @return       Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+  @abstract     Retrieve a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Key [type of keys]
+  @return       Iterator to the found element, or kh_end(h) is the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+  @abstract     Remove a key from the hash table.
+  @param  name  Name of the hash table [symbol]
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  k     Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+  @abstract     Test whether a bucket contains data.
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+  @abstract     Get key given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+  @abstract     Get value given an iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  x     Iterator to the bucket [khint_t]
+  @return       Value [type of values]
+  @discussion   For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+  @abstract     Get the start iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+  @abstract     Get the end iterator
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Get the number of elements in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+  @abstract     Get the number of buckets in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @return       Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+  @abstract     Iterate over the entries in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  kvar  Variable to which key will be assigned
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i;		\
+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+		if (!kh_exist(h,__i)) continue;						\
+		(kvar) = kh_key(h,__i);								\
+		(vvar) = kh_val(h,__i);								\
+		code;												\
+	} }
+
+/*! @function
+  @abstract     Iterate over the values in the hash table
+  @param  h     Pointer to the hash table [khash_t(name)*]
+  @param  vvar  Variable to which value will be assigned
+  @param  code  Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i;		\
+	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\
+		if (!kh_exist(h,__i)) continue;						\
+		(vvar) = kh_val(h,__i);								\
+		code;												\
+	} }
+
+/* More conenient interfaces */
+
+/*! @function
+  @abstract     Instantiate a hash set containing integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name)										\
+	KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t)								\
+	KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name)										\
+	KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing 64-bit integer keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t)								\
+	KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name)										\
+	KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+  @abstract     Instantiate a hash map containing const char* keys
+  @param  name  Name of the hash table [symbol]
+  @param  khval_t  Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t)								\
+	KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/libgit2/src/map.h b/libgit2/src/map.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/map.h
@@ -0,0 +1,42 @@
+/*
+ * 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_map_h__
+#define INCLUDE_map_h__
+
+#include "common.h"
+
+
+/* p_mmap() prot values */
+#define GIT_PROT_NONE 0x0
+#define GIT_PROT_READ 0x1
+#define GIT_PROT_WRITE 0x2
+#define GIT_PROT_EXEC 0x4
+
+/* git__mmmap() flags values */
+#define GIT_MAP_FILE	0
+#define GIT_MAP_SHARED 1
+#define GIT_MAP_PRIVATE 2
+#define GIT_MAP_TYPE	0xf
+#define GIT_MAP_FIXED	0x10
+
+typedef struct { /* memory mapped buffer	*/
+	void *data; /* data bytes			*/
+	size_t len; /* data length			*/
+#ifdef GIT_WIN32
+	HANDLE fmh; /* file mapping handle */
+#endif
+} git_map;
+
+#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
+	assert(out != NULL && len > 0); \
+	assert((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
+	assert((flags & GIT_MAP_FIXED) == 0); } while (0)
+
+extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset);
+extern int p_munmap(git_map *map);
+
+#endif /* INCLUDE_map_h__ */
diff --git a/libgit2/src/message.c b/libgit2/src/message.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/message.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "message.h"
+#include <ctype.h>
+
+static size_t line_length_without_trailing_spaces(const char *line, size_t len)
+{
+	while (len) {
+		unsigned char c = line[len - 1];
+		if (!git__isspace(c))
+			break;
+		len--;
+	}
+
+	return len;
+}
+
+/* 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)
+{
+	const size_t message_len = strlen(message);
+
+	int consecutive_empty_lines = 0;
+	size_t i, line_length, rtrimmed_line_length;
+	char *next_newline;
+
+	for (i = 0; i < strlen(message); i += line_length) {
+		next_newline = memchr(message + i, '\n', message_len - i);
+
+		if (next_newline != NULL) {
+			line_length = next_newline - (message + i) + 1;
+		} else {
+			line_length = message_len - i;
+		}
+
+		if (strip_comments && line_length && message[i] == '#')
+			continue;
+
+		rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
+
+		if (!rtrimmed_line_length) {
+			consecutive_empty_lines++;
+			continue;
+		}
+
+		if (consecutive_empty_lines > 0 && message_out->size > 0)
+			git_buf_putc(message_out, '\n');
+
+		consecutive_empty_lines = 0;
+		git_buf_put(message_out, message + i, rtrimmed_line_length);
+		git_buf_putc(message_out, '\n');
+	}
+
+	return git_buf_oom(message_out) ? -1 : 0;
+}
diff --git a/libgit2/src/message.h b/libgit2/src/message.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/message.h
@@ -0,0 +1,14 @@
+/*
+ * 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_message_h__
+#define INCLUDE_message_h__
+
+#include "buffer.h"
+
+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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/mwindow.c
@@ -0,0 +1,272 @@
+/*
+ * 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 "mwindow.h"
+#include "vector.h"
+#include "fileops.h"
+#include "map.h"
+#include "global.h"
+
+#define DEFAULT_WINDOW_SIZE \
+	(sizeof(void*) >= 8 \
+		? 1 * 1024 * 1024 * 1024 \
+		: 32 * 1024 * 1024)
+
+#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,
+};
+
+/*
+ * 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;
+	/*
+	 * Remove these windows from the global list
+	 */
+	for (i = 0; i < ctl->windowfiles.length; ++i){
+		if (git_vector_get(&ctl->windowfiles, i) == mwf) {
+			git_vector_remove(&ctl->windowfiles, i);
+			break;
+		}
+	}
+
+	if (ctl->windowfiles.length == 0) {
+		git_vector_free(&ctl->windowfiles);
+		ctl->windowfiles.contents = NULL;
+	}
+
+	while (mwf->windows) {
+		git_mwindow *w = mwf->windows;
+		assert(w->inuse_cnt == 0);
+
+		ctl->mapped -= w->window_map.len;
+		ctl->open_windows--;
+
+		git_futils_mmap_free(&w->window_map);
+
+		mwf->windows = w->next;
+		git__free(w);
+	}
+}
+
+/*
+ * Check if a window 'win' contains the address 'offset'
+ */
+int git_mwindow_contains(git_mwindow *win, git_off_t offset)
+{
+	git_off_t win_off = win->offset;
+	return win_off <= offset
+		&& offset <= (git_off_t)(win_off + win->window_map.len);
+}
+
+/*
+ * Find the least-recently-used window in a file
+ */
+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) {
+			/*
+			 * If the current one is more recent than the last one,
+			 * store it in the output parameter. If lru_w is NULL,
+			 * it's the first loop, so store it as well.
+			 */
+			if (!*lru_w || w->last_used < (*lru_w)->last_used) {
+				*lru_w = w;
+				*lru_l = w_l;
+			}
+		}
+		w_l = w;
+	}
+}
+
+/*
+ * Close the least recently used window. You should check to see if
+ * the file descriptors need closing from time to time.
+ */
+static int git_mwindow_close_lru(git_mwindow_file *mwf)
+{
+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
+	unsigned int i;
+	git_mwindow *lru_w = NULL, *lru_l = NULL, **list = &mwf->windows;
+
+	/* FIXME: Does this give us any advantage? */
+	if(mwf->windows)
+		git_mwindow_scan_lru(mwf, &lru_w, &lru_l);
+
+	for (i = 0; i < ctl->windowfiles.length; ++i) {
+		git_mwindow *last = lru_w;
+		git_mwindow_file *cur = git_vector_get(&ctl->windowfiles, i);
+		git_mwindow_scan_lru(cur, &lru_w, &lru_l);
+		if (lru_w != last)
+			list = &cur->windows;
+	}
+
+	if (!lru_w) {
+		giterr_set(GITERR_OS, "Failed to close memory window. Couldn't find LRU");
+		return -1;
+	}
+
+	ctl->mapped -= lru_w->window_map.len;
+	git_futils_mmap_free(&lru_w->window_map);
+
+	if (lru_l)
+		lru_l->next = lru_w->next;
+	else
+		*list = lru_w->next;
+
+	git__free(lru_w);
+	ctl->open_windows--;
+
+	return 0;
+}
+
+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_off_t len;
+	git_mwindow *w;
+
+	w = git__malloc(sizeof(*w));
+	if (w == NULL)
+		return NULL;
+
+	memset(w, 0x0, sizeof(*w));
+	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;
+
+	ctl->mapped += (size_t)len;
+
+	while (_mw_options.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
+	 * window to close and are above the limit, we still mmap the new
+	 * window.
+	 */
+
+	if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+		git__free(w);
+		return NULL;
+	}
+
+	ctl->mmap_calls++;
+	ctl->open_windows++;
+
+	if (ctl->mapped > ctl->peak_mapped)
+		ctl->peak_mapped = ctl->mapped;
+
+	if (ctl->open_windows > ctl->peak_open_windows)
+		ctl->peak_open_windows = ctl->open_windows;
+
+	return w;
+}
+
+/*
+ * Open a new window, closing the least recenty used until we have
+ * enough space. Don't forget to add it to your list
+ */
+unsigned char *git_mwindow_open(
+	git_mwindow_file *mwf,
+	git_mwindow **cursor,
+	git_off_t offset,
+	size_t extra,
+	unsigned int *left)
+{
+	git_mwindow_ctl *ctl = &GIT_GLOBAL->mem_ctl;
+	git_mwindow *w = *cursor;
+
+	if (!w || !(git_mwindow_contains(w, offset) && git_mwindow_contains(w, offset + extra))) {
+		if (w) {
+			w->inuse_cnt--;
+		}
+
+		for (w = mwf->windows; w; w = w->next) {
+			if (git_mwindow_contains(w, offset) &&
+				git_mwindow_contains(w, offset + extra))
+				break;
+		}
+
+		/*
+		 * If there isn't a suitable window, we need to create a new
+		 * one.
+		 */
+		if (!w) {
+			w = new_window(mwf, mwf->fd, mwf->size, offset);
+			if (w == NULL)
+				return NULL;
+			w->next = mwf->windows;
+			mwf->windows = w;
+		}
+	}
+
+	/* If we changed w, store it in the cursor */
+	if (w != *cursor) {
+		w->last_used = ctl->used_ctr++;
+		w->inuse_cnt++;
+		*cursor = w;
+	}
+
+	offset -= w->offset;
+
+	if (left)
+		*left = (unsigned int)(w->window_map.len - offset);
+
+	fflush(stdout);
+	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;
+
+	if (ctl->windowfiles.length == 0 &&
+		git_vector_init(&ctl->windowfiles, 8, NULL) < 0)
+		return -1;
+
+	return git_vector_insert(&ctl->windowfiles, mwf);
+}
+
+void git_mwindow_close(git_mwindow **window)
+{
+	git_mwindow *w = *window;
+	if (w) {
+		w->inuse_cnt--;
+		*window = NULL;
+	}
+}
diff --git a/libgit2/src/mwindow.h b/libgit2/src/mwindow.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/mwindow.h
@@ -0,0 +1,45 @@
+/*
+ * 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_mwindow__
+#define INCLUDE_mwindow__
+
+#include "map.h"
+#include "vector.h"
+
+typedef struct git_mwindow {
+	struct git_mwindow *next;
+	git_map window_map;
+	git_off_t offset;
+	size_t last_used;
+	size_t inuse_cnt;
+} git_mwindow;
+
+typedef struct git_mwindow_file {
+	git_mwindow *windows;
+	int fd;
+	git_off_t size;
+} git_mwindow_file;
+
+typedef struct git_mwindow_ctl {
+	size_t mapped;
+	unsigned int open_windows;
+	unsigned int mmap_calls;
+	unsigned int peak_open_windows;
+	size_t peak_mapped;
+	size_t used_ctr;
+	git_vector windowfiles;
+} git_mwindow_ctl;
+
+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_close(git_mwindow **w_cursor);
+
+#endif
diff --git a/libgit2/src/netops.c b/libgit2/src/netops.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/netops.c
@@ -0,0 +1,209 @@
+/*
+ * 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 _WIN32
+#	include <sys/types.h>
+#	include <sys/socket.h>
+#	include <sys/select.h>
+#	include <sys/time.h>
+#	include <netdb.h>
+#else
+#	include <winsock2.h>
+#	include <Ws2tcpip.h>
+#	ifdef _MSC_VER
+#		pragma comment(lib, "Ws2_32.lib")
+#	endif
+#endif
+
+
+#include "git2/errors.h"
+
+#include "common.h"
+#include "netops.h"
+#include "posix.h"
+#include "buffer.h"
+
+#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);
+
+	giterr_set(GITERR_NET, "%s: %s", str, err_str);
+	LocalFree(err_str);
+}
+#else
+static void net_set_error(const char *str)
+{
+	giterr_set(GITERR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd)
+{
+	memset(buf, 0x0, sizeof(gitno_buffer));
+	memset(data, 0x0, len);
+	buf->data = data;
+	buf->len = len;
+	buf->offset = 0;
+	buf->fd = fd;
+}
+
+int gitno_recv(gitno_buffer *buf)
+{
+	int ret;
+
+	ret = p_recv(buf->fd, 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;
+}
+
+/* Consume up to ptr and move the rest of the buffer to the beginning */
+void gitno_consume(gitno_buffer *buf, const char *ptr)
+{
+	size_t consumed;
+
+	assert(ptr - buf->data >= 0);
+	assert(ptr - buf->data <= (int) buf->len);
+
+	consumed = ptr - buf->data;
+
+	memmove(buf->data, ptr, buf->offset - consumed);
+	memset(buf->data + buf->offset, 0x0, buf->len - buf->offset);
+	buf->offset -= consumed;
+}
+
+/* Consume const bytes and move the rest of the buffer to the beginning */
+void gitno_consume_n(gitno_buffer *buf, size_t cons)
+{
+	memmove(buf->data, buf->data + cons, buf->len - buf->offset);
+	memset(buf->data + cons, 0x0, buf->len - buf->offset);
+	buf->offset -= cons;
+}
+
+int gitno_connect(GIT_SOCKET *sock, const char *host, const char *port)
+{
+	struct addrinfo *info = NULL, *p;
+	struct addrinfo hints;
+	int ret;
+	GIT_SOCKET s = INVALID_SOCKET;
+
+	memset(&hints, 0x0, sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+
+	if ((ret = getaddrinfo(host, port, &hints, &info)) < 0) {
+		giterr_set(GITERR_NET, "Failed to resolve address for %s: %s", host, 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;
+		}
+
+		if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0)
+			break;
+
+		/* If we can't connect, try the next one */
+		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);
+		return -1;
+	}
+
+	freeaddrinfo(info);
+	*sock = s;
+	return 0;
+}
+
+int gitno_send(GIT_SOCKET s, const char *msg, size_t len, int flags)
+{
+	int ret;
+	size_t off = 0;
+
+	while (off < len) {
+		errno = 0;
+
+		ret = p_send(s, msg + off, len - off, flags);
+		if (ret < 0) {
+			net_set_error("Error sending data");
+			return -1;
+		}
+
+		off += ret;
+	}
+
+	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)
+{
+	return close(s);
+}
+#endif
+
+int gitno_select_in(gitno_buffer *buf, long int sec, long int usec)
+{
+	fd_set fds;
+	struct timeval tv;
+
+	tv.tv_sec = sec;
+	tv.tv_usec = usec;
+
+	FD_ZERO(&fds);
+	FD_SET(buf->fd, &fds);
+
+	/* The select(2) interface is silly */
+	return select((int)buf->fd + 1, &fds, NULL, NULL, &tv);
+}
+
+int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port)
+{
+	char *colon, *slash, *delim;
+
+	colon = strchr(url, ':');
+	slash = strchr(url, '/');
+
+	if (slash == NULL) {
+		giterr_set(GITERR_NET, "Malformed URL: missing /");
+		return -1;
+	}
+
+	if (colon == NULL) {
+		*port = git__strdup(default_port);
+	} else {
+		*port = git__strndup(colon + 1, slash - colon - 1);
+	}
+	GITERR_CHECK_ALLOC(*port);
+
+	delim = colon == NULL ? slash : colon;
+	*host = git__strndup(url, delim - url);
+	GITERR_CHECK_ALLOC(*host);
+
+	return 0;
+}
diff --git a/libgit2/src/netops.h b/libgit2/src/netops.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/netops.h
@@ -0,0 +1,32 @@
+/*
+ * 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_netops_h__
+#define INCLUDE_netops_h__
+
+#include "posix.h"
+
+typedef struct gitno_buffer {
+	char *data;
+	size_t len;
+	size_t offset;
+	GIT_SOCKET fd;
+} gitno_buffer;
+
+void gitno_buffer_setup(gitno_buffer *buf, char *data, unsigned int len, GIT_SOCKET fd);
+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_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);
+
+#endif
diff --git a/libgit2/src/notes.c b/libgit2/src/notes.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/notes.c
@@ -0,0 +1,548 @@
+/*
+ * 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 "notes.h"
+
+#include "git2.h"
+#include "refs.h"
+#include "config.h"
+#include "iterator.h"
+
+static int find_subtree(git_tree **subtree, const git_oid *root,
+			git_repository *repo, const char *target, int *fanout)
+{
+	int error;
+	unsigned int i;
+	git_tree *tree;
+	const git_tree_entry *entry;
+
+	*subtree = NULL;
+
+	error = git_tree_lookup(&tree, repo, root);
+	if (error < 0)
+		return error;
+
+	for (i=0; i<git_tree_entrycount(tree); i++) {
+		entry = git_tree_entry_byindex(tree, 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))
+			&& strlen(git_tree_entry_name(entry)) == 2
+			&& !strncmp(git_tree_entry_name(entry), target + *fanout, 2)) {
+
+			/* found matching subtree - unpack and resume lookup */
+
+			git_oid subtree_sha;
+			git_oid_cpy(&subtree_sha, git_tree_entry_id(entry));
+			git_tree_free(tree);
+
+			*fanout += 2;
+
+			return find_subtree(subtree, &subtree_sha, repo,
+					    target, fanout);
+		}
+	}
+
+	*subtree = tree;
+	return 0;
+}
+
+static int find_blob(git_oid *blob, git_tree *tree, const char *target)
+{
+	unsigned int i;
+	const git_tree_entry *entry;
+
+	for (i=0; i<git_tree_entrycount(tree); i++) {
+		entry = git_tree_entry_byindex(tree, i);
+
+		if (!strcmp(git_tree_entry_name(entry), target)) {
+			/* found matching note object - return */
+
+			git_oid_cpy(blob, git_tree_entry_id(entry));
+			return 0;
+		}
+	}
+	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)
+{
+	int error, fanout = 0;
+	git_oid oid;
+	git_tree *tree = NULL;
+	git_tree_entry *entry;
+	git_treebuilder *tb;
+
+	/* check for existing notes tree */
+
+	if (tree_sha) {
+		error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+		if (error < 0)
+			return error;
+
+		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;
+		}
+	}
+
+	/* no matching tree entry - add note object to target tree */
+
+	error = git_treebuilder_create(&tb, tree);
+	git_tree_free(tree);
+
+	if (error < 0)
+		return error;
+
+	if (!tree_sha)
+		/* no notes tree yet - create fanout */
+		fanout += 2;
+
+	/* create note object */
+	error = git_blob_create_frombuffer(&oid, repo, note, strlen(note));
+	if (error < 0) {
+		git_treebuilder_free(tb);
+		return error;
+	}
+
+	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 */
+
+		git_treebuilder_free(tb);
+		return error;
+	}
+
+	if (out)
+		git_oid_cpy(out, git_tree_entry_id(entry));
+
+	error = git_treebuilder_write(&oid, repo, tb);
+	git_treebuilder_free(tb);
+
+	if (error < 0)
+		return 0;
+
+	if (!tree_sha) {
+		/* create fanout subtree */
+
+		char subtree[3];
+		strncpy(subtree, target, 2);
+		subtree[2] = '\0';
+
+		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;
+		}
+
+		error = git_treebuilder_write(&oid, repo, tb);
+
+		git_treebuilder_free(tb);
+
+		if (error < 0)
+			return error;
+	}
+
+	/* create new notes commit */
+
+	error = git_tree_lookup(&tree, repo, &oid);
+	if (error < 0)
+		return error;
+
+	error = git_commit_create(&oid, repo, notes_ref, author, committer,
+				  NULL, GIT_NOTES_DEFAULT_MSG_ADD,
+				  tree, nparents, (const git_commit **) parents);
+
+	git_tree_free(tree);
+
+	return error;
+}
+
+static int note_lookup(git_note **out, git_repository *repo,
+		       const git_oid *tree_sha, const char *target)
+{
+	int error, fanout = 0;
+	git_oid oid;
+	git_blob *blob;
+	git_tree *tree;
+	git_note *note;
+
+	error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+	if (error < 0)
+		return error;
+
+	error = find_blob(&oid, tree, target + fanout);
+
+	git_tree_free(tree);
+	if (error < 0)
+		return error;
+
+	error = git_blob_lookup(&blob, repo, &oid);
+	if (error < 0)
+		return error;
+
+	note = git__malloc(sizeof(git_note));
+	GITERR_CHECK_ALLOC(note);
+
+	git_oid_cpy(&note->oid, &oid);
+	note->message = git__strdup(git_blob_rawcontent(blob));
+	GITERR_CHECK_ALLOC(note->message);
+
+	*out = note;
+
+	git_blob_free(blob);
+	return error;
+}
+
+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)
+{
+	int error, fanout = 0;
+	git_oid oid;
+	git_tree *tree;
+	git_treebuilder *tb;
+
+	error = find_subtree(&tree, tree_sha, repo, target, &fanout);
+	if (error < 0)
+		return error;
+
+	error = find_blob(&oid, tree, target + fanout);
+	if (!error)
+		error = git_treebuilder_create(&tb, tree);
+
+	git_tree_free(tree);
+	if (error < 0)
+		return error;
+
+	error = git_treebuilder_remove(tb, target + fanout);
+	if (!error)
+		error = git_treebuilder_write(&oid, repo, tb);
+
+	git_treebuilder_free(tb);
+	if (error < 0)
+		return error;
+
+	/* create new notes commit */
+
+	error = git_tree_lookup(&tree, repo, &oid);
+	if (error < 0)
+		return error;
+
+	error = git_commit_create(&oid, repo, notes_ref, author, committer,
+				  NULL, GIT_NOTES_DEFAULT_MSG_RM,
+				  tree, nparents, (const git_commit **) parents);
+
+	git_tree_free(tree);
+
+	return error;
+}
+
+static int note_get_default_ref(const char **out, git_repository *repo)
+{
+	int ret;
+	git_config *cfg;
+
+	*out = NULL;
+
+	if (git_repository_config__weakptr(&cfg, repo) < 0)
+		return -1;
+
+	ret = git_config_get_string(out, cfg, "core.notesRef");
+	if (ret == GIT_ENOTFOUND) {
+		*out = GIT_NOTES_DEFAULT_REF;
+		return 0;
+	}
+
+	return ret;
+}
+
+static int normalize_namespace(const char **notes_ref, git_repository *repo)
+{
+	if (*notes_ref)
+		return 0;
+
+	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)
+{
+	int error = -1;
+	git_commit *commit = NULL;
+	git_oid oid;
+
+	if ((error = git_reference_name_to_oid(&oid, repo, notes_ref)) < 0)
+		goto cleanup;
+
+	if (git_commit_lookup(&commit, repo, &oid) < 0)
+		goto cleanup;
+
+	git_oid_cpy(tree_oid_out, git_commit_tree_oid(commit));
+
+	error = 0;
+
+cleanup:
+	git_commit_free(commit);
+	return error;
+}
+
+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;
+
+	target = git_oid_allocfmt(oid);
+	GITERR_CHECK_ALLOC(target);
+
+	error = note_lookup(out, repo, &sha, target);
+
+	git__free(target);
+	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)
+{
+	int error, nparents = 0;
+	char *target;
+	git_oid sha;
+	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++;
+	}
+
+	target = git_oid_allocfmt(oid);
+	GITERR_CHECK_ALLOC(target);
+
+	error = note_write(out, repo, author, committer, notes_ref,
+			   note, nparents ? &sha : NULL, target,
+			   nparents, &commit);
+
+	git__free(target);
+	git_commit_free(commit);
+	return error;
+}
+
+int git_note_remove(git_repository *repo, const char *notes_ref,
+		    git_signature *author, 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));
+
+	target = git_oid_allocfmt(oid);
+	GITERR_CHECK_ALLOC(target);
+
+	error = note_remove(repo, author, committer, notes_ref,
+			    &sha, target, 1, &commit);
+
+	git__free(target);
+	git_commit_free(commit);
+	return error;
+}
+
+int git_note_default_ref(const char **out, git_repository *repo)
+{
+	assert(repo);
+	return note_get_default_ref(out, repo);
+}
+
+const char * git_note_message(git_note *note)
+{
+	assert(note);
+	return note->message;
+}
+
+const git_oid * git_note_oid(git_note *note)
+{
+	assert(note);
+	return &note->oid;
+}
+
+void git_note_free(git_note *note)
+{
+	if (note == NULL)
+		return;
+
+	git__free(note->message);
+	git__free(note);
+}
+
+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)
+{
+	int i = 0, j = 0, error = -1, len;
+	git_buf buf = GIT_BUF_INIT;
+	git_note_data note_data;
+
+	if (git_buf_puts(&buf, entry_path) < 0)
+		goto cleanup;
+	
+	len = git_buf_len(&buf);
+
+	while (i < len) {
+		if (buf.ptr[i] == '/') {
+			i++;
+			continue;
+		}
+		
+		if (git__fromhex(buf.ptr[i]) < 0) {
+			/* This is not a note entry */
+			error = 0;
+			goto cleanup;
+		}
+
+		if (i != j)
+			buf.ptr[j] = buf.ptr[i];
+
+		i++;
+		j++;
+	}
+
+	buf.ptr[j] = '\0';
+	buf.size = j;
+
+	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);
+
+cleanup:
+	git_buf_free(&buf);
+	return error;
+}
+
+int git_note_foreach(
+	git_repository *repo,
+	const char *notes_ref,
+	int (*note_cb)(git_note_data *note_data, void *payload),
+	void *payload)
+{
+	int error = -1;
+	git_oid tree_oid;
+	git_iterator *iter = NULL;
+	git_tree *tree = NULL;
+	const git_index_entry *item;
+
+	if (normalize_namespace(&notes_ref, repo) < 0)
+		return -1;
+
+	if ((error = retrieve_note_tree_oid(&tree_oid, repo, notes_ref)) < 0)
+		goto cleanup;
+
+	if (git_tree_lookup(&tree, repo, &tree_oid) < 0)
+		goto cleanup;
+
+	if (git_iterator_for_tree(&iter, repo, tree) < 0)
+		goto cleanup;
+
+	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;
+
+		if (git_iterator_advance(iter, &item) < 0)
+			goto cleanup;
+	}
+
+	error = 0;
+
+cleanup:
+	git_iterator_free(iter);
+	git_tree_free(tree);
+	return error;
+}
diff --git a/libgit2/src/notes.h b/libgit2/src/notes.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/notes.h
@@ -0,0 +1,28 @@
+/*
+ * 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_note_h__
+#define INCLUDE_note_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+
+#define GIT_NOTES_DEFAULT_MSG_ADD \
+	"Notes added by 'git_note_create' from libgit2"
+
+#define GIT_NOTES_DEFAULT_MSG_RM \
+	"Notes removed by 'git_note_remove' from libgit2"
+
+struct git_note {
+	git_oid oid;
+
+	char *message;
+};
+
+#endif /* INCLUDE_notes_h__ */
diff --git a/libgit2/src/object.c b/libgit2/src/object.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/object.c
@@ -0,0 +1,333 @@
+/*
+ * 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 "git2/object.h"
+
+#include "common.h"
+#include "repository.h"
+
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tag.h"
+
+static const int OBJECT_BASE_SIZE = 4096;
+
+static struct {
+	const char	*str;	/* type name string */
+	int			loose; /* valid loose object type flag */
+	size_t		size;	/* size in bytes of the object structure */
+} git_objects_table[] = {
+	/* 0 = GIT_OBJ__EXT1 */
+	{ "", 0, 0},
+
+	/* 1 = GIT_OBJ_COMMIT */
+	{ "commit", 1, sizeof(struct git_commit)},
+
+	/* 2 = GIT_OBJ_TREE */
+	{ "tree", 1, sizeof(struct git_tree) },
+
+	/* 3 = GIT_OBJ_BLOB */
+	{ "blob", 1, sizeof(struct git_blob) },
+
+	/* 4 = GIT_OBJ_TAG */
+	{ "tag", 1, sizeof(struct git_tag) },
+
+	/* 5 = GIT_OBJ__EXT2 */
+	{ "", 0, 0 },
+
+	/* 6 = GIT_OBJ_OFS_DELTA */
+	{ "OFS_DELTA", 0, 0 },
+
+	/* 7 = GIT_OBJ_REF_DELTA */
+	{ "REF_DELTA", 0, 0	}
+};
+
+static int create_object(git_object **object_out, git_otype type)
+{
+	git_object *object = NULL;
+
+	assert(object_out);
+
+	*object_out = NULL;
+
+	switch (type) {
+	case GIT_OBJ_COMMIT:
+	case GIT_OBJ_TAG:
+	case GIT_OBJ_BLOB:
+	case GIT_OBJ_TREE:
+		object = git__malloc(git_object__size(type));
+		GITERR_CHECK_ALLOC(object);
+		memset(object, 0x0, git_object__size(type));
+		break;
+
+	default:
+		giterr_set(GITERR_INVALID, "The given type is invalid");
+		return -1;
+	}
+
+	object->type = type;
+
+	*object_out = object;
+	return 0;
+}
+
+int git_object_lookup_prefix(
+	git_object **object_out,
+	git_repository *repo,
+	const git_oid *id,
+	unsigned int len,
+	git_otype type)
+{
+	git_object *object = NULL;
+	git_odb *odb = NULL;
+	git_odb_object *odb_obj;
+	int error = 0;
+
+	assert(repo && object_out && id);
+
+	if (len < GIT_OID_MINPREFIXLEN)
+		return GIT_EAMBIGUOUS;
+
+	error = git_repository_odb__weakptr(&odb, repo);
+	if (error < 0)
+		return error;
+
+	if (len > GIT_OID_HEXSZ)
+		len = GIT_OID_HEXSZ;
+
+	if (len == GIT_OID_HEXSZ) {
+		/* We want to match the full id : we can first look up in the cache,
+		 * since there is no need to check for non ambiguousity
+		 */
+		object = git_cache_get(&repo->objects, id);
+		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");
+				return GIT_ENOTFOUND;
+			}
+
+			*object_out = object;
+			return 0;
+		}
+
+		/* Object was not found in the cache, let's explore the backends.
+		 * We could just use git_odb_read_unique_short_oid,
+		 * it is the same cost for packed and loose object backends,
+		 * but it may be much more costly for sqlite and hiredis.
+		 */
+		error = git_odb_read(&odb_obj, odb, id);
+	} else {
+		git_oid short_oid;
+
+		/* We copy the first len*4 bits from id and fill the remaining with 0s */
+		memcpy(short_oid.id, id->id, (len + 1) / 2);
+		if (len % 2)
+			short_oid.id[len / 2] &= 0xF0;
+		memset(short_oid.id + (len + 1) / 2, 0, (GIT_OID_HEXSZ - len) / 2);
+
+		/* If len < GIT_OID_HEXSZ (a strict short oid was given), we have
+		 * 2 options :
+		 * - We always search in the cache first. If we find that short oid is
+		 *	ambiguous, we can stop. But in all the other cases, we must then
+		 *	explore all the backends (to find an object if there was match,
+		 *	or to check that oid is not ambiguous if we have found 1 match in
+		 *	the cache)
+		 * - We never explore the cache, go right to exploring the backends
+		 * We chose the latter : we explore directly the backends.
+		 */
+		error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
+	}
+
+	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;
+	}
+
+	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;
+}
+
+int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type) {
+	return git_object_lookup_prefix(object_out, repo, id, GIT_OID_HEXSZ, type);
+}
+
+void git_object__free(void *_obj)
+{
+	git_object *object = (git_object *)_obj;
+
+	assert(object);
+
+	switch (object->type) {
+	case GIT_OBJ_COMMIT:
+		git_commit__free((git_commit *)object);
+		break;
+
+	case GIT_OBJ_TREE:
+		git_tree__free((git_tree *)object);
+		break;
+
+	case GIT_OBJ_TAG:
+		git_tag__free((git_tag *)object);
+		break;
+
+	case GIT_OBJ_BLOB:
+		git_blob__free((git_blob *)object);
+		break;
+
+	default:
+		git__free(object);
+		break;
+	}
+}
+
+void git_object_free(git_object *object)
+{
+	if (object == NULL)
+		return;
+
+	git_cached_obj_decref((git_cached_obj *)object, git_object__free);
+}
+
+const git_oid *git_object_id(const git_object *obj)
+{
+	assert(obj);
+	return &obj->cached.oid;
+}
+
+git_otype git_object_type(const git_object *obj)
+{
+	assert(obj);
+	return obj->type;
+}
+
+git_repository *git_object_owner(const git_object *obj)
+{
+	assert(obj);
+	return obj->repo;
+}
+
+const char *git_object_type2string(git_otype type)
+{
+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+		return "";
+
+	return git_objects_table[type].str;
+}
+
+git_otype git_object_string2type(const char *str)
+{
+	size_t i;
+
+	if (!str || !*str)
+		return GIT_OBJ_BAD;
+
+	for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
+		if (!strcmp(str, git_objects_table[i].str))
+			return (git_otype)i;
+
+	return GIT_OBJ_BAD;
+}
+
+int git_object_typeisloose(git_otype type)
+{
+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+		return 0;
+
+	return git_objects_table[type].loose;
+}
+
+size_t git_object__size(git_otype type)
+{
+	if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+		return 0;
+
+	return git_objects_table[type].size;
+}
+
+int git_object__resolve_to_type(git_object **obj, git_otype type)
+{
+	int error = 0;
+	git_object *scan, *next;
+
+	if (type == GIT_OBJ_ANY)
+		return 0;
+
+	scan = *obj;
+
+	while (!error && scan && git_object_type(scan) != type) {
+
+		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_TAG:
+			error = git_tag_target(&next, (git_tag *)scan);
+			break;
+
+		default:
+			giterr_set(GITERR_REFERENCE, "Object does not resolve to type");
+			error = -1;
+			next = NULL;
+			break;
+		}
+
+		git_object_free(scan);
+		scan = next;
+	}
+
+	*obj = scan;
+	return error;
+}
diff --git a/libgit2/src/odb.c b/libgit2/src/odb.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/odb.c
@@ -0,0 +1,709 @@
+/*
+ * 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 <zlib.h>
+#include "git2/object.h"
+#include "fileops.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta-apply.h"
+
+#include "git2/odb_backend.h"
+
+#define GIT_ALTERNATES_FILE "info/alternates"
+
+/* TODO: is this correct? */
+#define GIT_LOOSE_PRIORITY 2
+#define GIT_PACKED_PRIORITY 1
+
+typedef struct
+{
+	git_odb_backend *backend;
+	int priority;
+	int is_alternate;
+} backend_internal;
+
+static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
+{
+	const char *type_str = git_object_type2string(obj_type);
+	int len = p_snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
+	assert(len > 0 && len <= (int)n);
+	return len+1;
+}
+
+int git_odb__hashobj(git_oid *id, git_rawobj *obj)
+{
+	git_buf_vec vec[2];
+	char header[64];
+	int hdrlen;
+
+	assert(id && obj);
+
+	if (!git_object_typeisloose(obj->type))
+		return -1;
+	if (!obj->data && obj->len != 0)
+		return -1;
+
+	hdrlen = format_object_header(header, sizeof(header), obj->len, obj->type);
+
+	vec[0].data = header;
+	vec[0].len = hdrlen;
+	vec[1].data = obj->data;
+	vec[1].len = obj->len;
+
+	git_hash_vec(id, vec, 2);
+
+	return 0;
+}
+
+
+static git_odb_object *new_odb_object(const git_oid *oid, git_rawobj *source)
+{
+	git_odb_object *object = git__malloc(sizeof(git_odb_object));
+	memset(object, 0x0, sizeof(git_odb_object));
+
+	git_oid_cpy(&object->cached.oid, oid);
+	memcpy(&object->raw, source, sizeof(git_rawobj));
+
+	return object;
+}
+
+static void free_odb_object(void *o)
+{
+	git_odb_object *object = (git_odb_object *)o;
+
+	if (object != NULL) {
+		git__free(object->raw.data);
+		git__free(object);
+	}
+}
+
+const git_oid *git_odb_object_id(git_odb_object *object)
+{
+	return &object->cached.oid;
+}
+
+const void *git_odb_object_data(git_odb_object *object)
+{
+	return object->raw.data;
+}
+
+size_t git_odb_object_size(git_odb_object *object)
+{
+	return object->raw.len;
+}
+
+git_otype git_odb_object_type(git_odb_object *object)
+{
+	return object->raw.type;
+}
+
+void git_odb_object_free(git_odb_object *object)
+{
+	git_cached_obj_decref((git_cached_obj *)object, &free_odb_object);
+}
+
+int git_odb__hashfd(git_oid *out, git_file fd, size_t size, git_otype type)
+{
+	int hdr_len;
+	char hdr[64], buffer[2048];
+	git_hash_ctx *ctx;
+
+	hdr_len = format_object_header(hdr, sizeof(hdr), size, type);
+
+	ctx = git_hash_new_ctx();
+
+	git_hash_update(ctx, hdr, hdr_len);
+
+	while (size > 0) {
+		ssize_t read_len = read(fd, buffer, sizeof(buffer));
+
+		if (read_len < 0) {
+			git_hash_free_ctx(ctx);
+			giterr_set(GITERR_OS, "Error reading file");
+			return -1;
+		}
+
+		git_hash_update(ctx, buffer, read_len);
+		size -= read_len;
+	}
+
+	git_hash_final(out, ctx);
+	git_hash_free_ctx(ctx);
+
+	return 0;
+}
+
+int git_odb__hashlink(git_oid *out, const char *path)
+{
+	struct stat st;
+	git_off_t size;
+	int result;
+
+	if (git_path_lstat(path, &st) < 0)
+		return -1;
+
+	size = st.st_size;
+
+	if (!git__is_sizet(size)) {
+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+		return -1;
+	}
+
+	if (S_ISLNK(st.st_mode)) {
+		char *link_data;
+		ssize_t read_len;
+
+		link_data = git__malloc((size_t)size);
+		GITERR_CHECK_ALLOC(link_data);
+
+		read_len = p_readlink(path, link_data, (size_t)(size + 1));
+		if (read_len != (ssize_t)size) {
+			giterr_set(GITERR_OS, "Failed to read symlink data for '%s'", path);
+			return -1;
+		}
+
+		result = git_odb_hash(out, link_data, (size_t)size, GIT_OBJ_BLOB);
+		git__free(link_data);
+	} else { 
+		int fd = git_futils_open_ro(path);
+		if (fd < 0)
+			return -1;
+		result = git_odb__hashfd(out, fd, (size_t)size, GIT_OBJ_BLOB);
+		p_close(fd);
+	}
+
+	return result;
+}
+
+int git_odb_hashfile(git_oid *out, const char *path, git_otype type)
+{
+	git_off_t size;
+	int result, fd = git_futils_open_ro(path);
+	if (fd < 0)
+		return fd;
+
+	if ((size = git_futils_filesize(fd)) < 0 || !git__is_sizet(size)) {
+		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
+		p_close(fd);
+		return -1;
+	}
+
+	result = git_odb__hashfd(out, fd, (size_t)size, type);
+	p_close(fd);
+	return result;
+}
+
+int git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type)
+{
+	git_rawobj raw;
+
+	assert(id);
+
+	raw.data = (void *)data;
+	raw.len = len;
+	raw.type = type;
+
+	return git_odb__hashobj(id, &raw);
+}
+
+/**
+ * FAKE WSTREAM
+ */
+
+typedef struct {
+	git_odb_stream stream;
+	char *buffer;
+	size_t size, written;
+	git_otype type;
+} fake_wstream;
+
+static int fake_wstream__fwrite(git_oid *oid, git_odb_stream *_stream)
+{
+	fake_wstream *stream = (fake_wstream *)_stream;
+	return _stream->backend->write(oid, _stream->backend, stream->buffer, stream->size, stream->type);
+}
+
+static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
+{
+	fake_wstream *stream = (fake_wstream *)_stream;
+
+	if (stream->written + len > stream->size)
+		return -1;
+
+	memcpy(stream->buffer + stream->written, data, len);
+	stream->written += len;
+	return 0;
+}
+
+static void fake_wstream__free(git_odb_stream *_stream)
+{
+	fake_wstream *stream = (fake_wstream *)_stream;
+
+	git__free(stream->buffer);
+	git__free(stream);
+}
+
+static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, size_t size, git_otype type)
+{
+	fake_wstream *stream;
+
+	stream = git__calloc(1, sizeof(fake_wstream));
+	GITERR_CHECK_ALLOC(stream);
+
+	stream->size = size;
+	stream->type = type;
+	stream->buffer = git__malloc(size);
+	if (stream->buffer == NULL) {
+		git__free(stream);
+		return -1;
+	}
+
+	stream->stream.backend = backend;
+	stream->stream.read = NULL; /* read only */
+	stream->stream.write = &fake_wstream__write;
+	stream->stream.finalize_write = &fake_wstream__fwrite;
+	stream->stream.free = &fake_wstream__free;
+	stream->stream.mode = GIT_STREAM_WRONLY;
+
+	*stream_p = (git_odb_stream *)stream;
+	return 0;
+}
+
+/***********************************************************
+ *
+ * OBJECT DATABASE PUBLIC API
+ *
+ * Public calls for the ODB functionality
+ *
+ ***********************************************************/
+
+static int backend_sort_cmp(const void *a, const void *b)
+{
+	const backend_internal *backend_a = (const backend_internal *)(a);
+	const backend_internal *backend_b = (const backend_internal *)(b);
+
+	if (backend_a->is_alternate == backend_b->is_alternate)
+		return (backend_b->priority - backend_a->priority);
+
+	return backend_a->is_alternate ? 1 : -1;
+}
+
+int git_odb_new(git_odb **out)
+{
+	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 ||
+		git_vector_init(&db->backends, 4, backend_sort_cmp) < 0)
+	{
+		git__free(db);
+		return -1;
+	}
+
+	*out = db;
+	GIT_REFCOUNT_INC(db);
+	return 0;
+}
+
+static int add_backend_internal(git_odb *odb, git_odb_backend *backend, int priority, int is_alternate)
+{
+	backend_internal *internal;
+
+	assert(odb && backend);
+
+	/* Check if the backend is already owned by another ODB */
+	assert(!backend->odb || backend->odb == odb);
+
+	internal = git__malloc(sizeof(backend_internal));
+	GITERR_CHECK_ALLOC(internal);
+
+	internal->backend = backend;
+	internal->priority = priority;
+	internal->is_alternate = is_alternate;
+
+	if (git_vector_insert(&odb->backends, internal) < 0) {
+		git__free(internal);
+		return -1;
+	}
+
+	git_vector_sort(&odb->backends);
+	internal->backend->odb = odb;
+	return 0;
+}
+
+int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
+{
+	return add_backend_internal(odb, backend, priority, 0);
+}
+
+int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
+{
+	return add_backend_internal(odb, backend, priority, 1);
+}
+
+static int add_default_backends(git_odb *db, const char *objects_dir, int as_alternates)
+{
+	git_odb_backend *loose, *packed;
+
+	/* add the loose object backend */
+	if (git_odb_backend_loose(&loose, objects_dir, -1, 0) < 0 ||
+		add_backend_internal(db, loose, GIT_LOOSE_PRIORITY, as_alternates) < 0)
+		return -1;
+
+	/* add the packed file backend */
+	if (git_odb_backend_pack(&packed, objects_dir) < 0 ||
+		add_backend_internal(db, packed, GIT_PACKED_PRIORITY, as_alternates) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int load_alternates(git_odb *odb, const char *objects_dir)
+{
+	git_buf alternates_path = GIT_BUF_INIT;
+	git_buf alternates_buf = GIT_BUF_INIT;
+	char *buffer;
+	const char *alternate;
+	int result = 0;
+
+	if (git_buf_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
+		return -1;
+
+	if (git_path_exists(alternates_path.ptr) == false) {
+		git_buf_free(&alternates_path);
+		return 0;
+	}
+
+	if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
+		git_buf_free(&alternates_path);
+		return -1;
+	}
+
+	buffer = (char *)alternates_buf.ptr;
+
+	/* add each alternate as a new backend; one alternate per line */
+	while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
+		if (*alternate == '\0' || *alternate == '#')
+			continue;
+
+		/* relative path: build based on the current `objects` folder */
+		if (*alternate == '.') {
+			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)
+			break;
+	}
+
+	git_buf_free(&alternates_path);
+	git_buf_free(&alternates_buf);
+
+	return result;
+}
+
+int git_odb_open(git_odb **out, const char *objects_dir)
+{
+	git_odb *db;
+
+	assert(out && objects_dir);
+
+	*out = NULL;
+
+	if (git_odb_new(&db) < 0)
+		return -1;
+
+	if (add_default_backends(db, objects_dir, 0) < 0 ||
+		load_alternates(db, objects_dir) < 0)
+	{
+		git_odb_free(db);
+		return -1;
+	}
+
+	*out = db;
+	return 0;
+}
+
+static void odb_free(git_odb *db)
+{
+	unsigned int i;
+
+	for (i = 0; i < db->backends.length; ++i) {
+		backend_internal *internal = git_vector_get(&db->backends, i);
+		git_odb_backend *backend = internal->backend;
+
+		if (backend->free) backend->free(backend);
+		else git__free(backend);
+
+		git__free(internal);
+	}
+
+	git_vector_free(&db->backends);
+	git_cache_free(&db->cache);
+	git__free(db);
+}
+
+void git_odb_free(git_odb *db)
+{
+	if (db == NULL)
+		return;
+
+	GIT_REFCOUNT_DEC(db, odb_free);
+}
+
+int git_odb_exists(git_odb *db, const git_oid *id)
+{
+	git_odb_object *object;
+	unsigned int i;
+	bool found = false;
+
+	assert(db && id);
+
+	if ((object = git_cache_get(&db->cache, id)) != NULL) {
+		git_odb_object_free(object);
+		return (int)true;
+	}
+
+	for (i = 0; i < db->backends.length && !found; ++i) {
+		backend_internal *internal = git_vector_get(&db->backends, i);
+		git_odb_backend *b = internal->backend;
+
+		if (b->exists != NULL)
+			found = b->exists(b, id);
+	}
+
+	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_ENOTFOUND;
+	git_odb_object *object;
+
+	assert(db && id);
+
+	if ((object = git_cache_get(&db->cache, id)) != NULL) {
+		*len_p = object->raw.len;
+		*type_p = object->raw.type;
+		git_odb_object_free(object);
+		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;
+
+		if (b->read_header != NULL)
+			error = b->read_header(len_p, type_p, b, id);
+	}
+
+	if (!error || error == GIT_PASSTHROUGH)
+		return 0;
+
+	/*
+	 * no backend could read only the header.
+	 * try reading the whole object and freeing the contents
+	 */
+	if ((error = git_odb_read(&object, db, id)) < 0)
+		return error; /* error already set - pass along */
+
+	*len_p = object->raw.len;
+	*type_p = object->raw.type;
+	git_odb_object_free(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;
+	git_rawobj raw;
+
+	assert(out && db && id);
+
+	*out = git_cache_get(&db->cache, id);
+	if (*out != NULL)
+		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;
+
+		if (b->read != NULL)
+			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 && error != GIT_PASSTHROUGH)
+		return error;
+
+	*out = git_cache_try_store(&db->cache, new_odb_object(id, &raw));
+	return 0;
+}
+
+int git_odb_read_prefix(
+	git_odb_object **out, git_odb *db, const git_oid *short_id, unsigned int len)
+{
+	unsigned int i;
+	int error = GIT_ENOTFOUND;
+	git_oid found_full_oid = {{0}};
+	git_rawobj raw;
+	bool found = false;
+
+	assert(out && db);
+
+	if (len < GIT_OID_MINPREFIXLEN)
+		return git_odb__error_ambiguous("prefix length too short");
+
+	if (len > GIT_OID_HEXSZ)
+		len = GIT_OID_HEXSZ;
+
+	if (len == GIT_OID_HEXSZ) {
+		*out = git_cache_get(&db->cache, short_id);
+		if (*out != NULL)
+			return 0;
+	}
+
+	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) {
+			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)
+				continue;
+
+			if (error)
+				return error;
+
+			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)
+		return git_odb__error_notfound("no match for prefix", short_id);
+
+	*out = git_cache_try_store(&db->cache, new_odb_object(&found_full_oid, &raw));
+	return 0;
+}
+
+int git_odb_write(
+	git_oid *oid, git_odb *db, const void *data, size_t len, git_otype type)
+{
+	unsigned int i;
+	int error = GIT_ERROR;
+	git_odb_stream *stream;
+
+	assert(oid && 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->write != NULL)
+			error = b->write(oid, b, data, len, type);
+	}
+
+	if (!error || error == GIT_PASSTHROUGH)
+		return 0;
+
+	/* if no backends were able to write the object directly, we try a streaming
+	 * write to the backends; just write the whole object into the stream in one
+	 * push */
+
+	if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
+		return error;
+
+	stream->write(stream, data, len);
+	error = stream->finalize_write(oid, stream);
+	stream->free(stream);
+
+	return error;
+}
+
+int git_odb_open_wstream(
+	git_odb_stream **stream, git_odb *db, size_t size, git_otype type)
+{
+	unsigned int i;
+	int error = GIT_ERROR;
+
+	assert(stream && 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->writestream != NULL)
+			error = b->writestream(stream, b, size, type);
+		else if (b->write != NULL)
+			error = init_fake_wstream(stream, b, size, type);
+	}
+
+	if (error == GIT_PASSTHROUGH)
+		error = 0;
+
+	return error;
+}
+
+int git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const git_oid *oid)
+{
+	unsigned int i;
+	int error = GIT_ERROR;
+
+	assert(stream && 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;
+
+		if (b->readstream != NULL)
+			error = b->readstream(stream, b, oid);
+	}
+
+	if (error == GIT_PASSTHROUGH)
+		error = 0;
+
+	return error;
+}
+
+int git_odb__error_notfound(const char *message, const git_oid *oid)
+{
+	if (oid != NULL) {
+		char oid_str[GIT_OID_HEXSZ + 1];
+		git_oid_tostr(oid_str, sizeof(oid_str), oid);
+		giterr_set(GITERR_ODB, "Object not found - %s (%s)", message, oid_str);
+	} else
+		giterr_set(GITERR_ODB, "Object not found - %s", message);
+
+	return GIT_ENOTFOUND;
+}
+
+int git_odb__error_ambiguous(const char *message)
+{
+	giterr_set(GITERR_ODB, "Ambiguous SHA1 prefix - %s", message);
+	return GIT_EAMBIGUOUS;
+}
+
diff --git a/libgit2/src/odb.h b/libgit2/src/odb.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/odb.h
@@ -0,0 +1,80 @@
+/*
+ * 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_odb_h__
+#define INCLUDE_odb_h__
+
+#include "git2/odb.h"
+#include "git2/oid.h"
+#include "git2/types.h"
+
+#include "vector.h"
+#include "cache.h"
+#include "posix.h"
+
+#define GIT_OBJECTS_DIR "objects/"
+#define GIT_OBJECT_DIR_MODE 0777
+#define GIT_OBJECT_FILE_MODE 0444
+
+/* DO NOT EXPORT */
+typedef struct {
+	void *data;			/**< Raw, decompressed object data. */
+	size_t len;			/**< Total number of bytes in data. */
+	git_otype type;		/**< Type of this object. */
+} git_rawobj;
+
+/* EXPORT */
+struct git_odb_object {
+	git_cached_obj cached;
+	git_rawobj raw;
+};
+
+/* EXPORT */
+struct git_odb {
+	git_refcount rc;
+	git_vector backends;
+	git_cache cache;
+};
+
+/*
+ * Hash a git_rawobj internally.
+ * The `git_rawobj` is supposed to be previously initialized
+ */
+int git_odb__hashobj(git_oid *id, git_rawobj *obj);
+
+/*
+ * 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.
+ *
+ * Saves us some `stat` calls.
+ *
+ * The fd is never closed, not even on error. It must be opened and closed
+ * by the caller
+ */
+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`.
+ *
+ * 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);
+
+/*
+ * Generate a GIT_ENOTFOUND error for the ODB.
+ */
+int git_odb__error_notfound(const char *message, const git_oid *oid);
+
+/*
+ * Generate a GIT_EAMBIGUOUS error for the ODB.
+ */
+int git_odb__error_ambiguous(const char *message);
+
+#endif
diff --git a/libgit2/src/odb_loose.c b/libgit2/src/odb_loose.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/odb_loose.c
@@ -0,0 +1,852 @@
+/*
+ * 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 <zlib.h>
+#include "git2/object.h"
+#include "git2/oid.h"
+#include "fileops.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta-apply.h"
+#include "filebuf.h"
+
+#include "git2/odb_backend.h"
+#include "git2/types.h"
+
+typedef struct { /* object header data */
+	git_otype type; /* object type */
+	size_t	size; /* object size */
+} obj_hdr;
+
+typedef struct {
+	git_odb_stream stream;
+	git_filebuf fbuf;
+} loose_writestream;
+
+typedef struct loose_backend {
+	git_odb_backend parent;
+
+	int object_zlib_level; /** loose object zlib compression level. */
+	int fsync_object_files; /** loose object file fsync flag. */
+	char *objects_dir;
+} loose_backend;
+
+/* State structure for exploring directories,
+ * in order to locate objects matching a short oid.
+ */
+typedef struct {
+	size_t dir_len;
+	unsigned char short_oid[GIT_OID_HEXSZ]; /* hex formatted oid to match */
+	unsigned int short_oid_len;
+	int found;				/* number of matching
+						 * objects already found */
+	unsigned char res_oid[GIT_OID_HEXSZ];	/* hex formatted oid of
+						 * the object found */
+} loose_locate_object_state;
+
+
+/***********************************************************
+ *
+ * MISCELANEOUS HELPER FUNCTIONS
+ *
+ ***********************************************************/
+
+static int object_file_name(git_buf *name, const char *dir, const git_oid *id)
+{
+	git_buf_sets(name, dir);
+
+	/* expand length for 40 hex sha1 chars + 2 * '/' + '\0' */
+	if (git_buf_grow(name, git_buf_len(name) + GIT_OID_HEXSZ + 3) < 0)
+		return -1;
+
+	git_path_to_dir(name);
+
+	/* loose object filename: aa/aaa... (41 bytes) */
+	git_oid_pathfmt(name->ptr + git_buf_len(name), id);
+	name->size += GIT_OID_HEXSZ + 1;
+	name->ptr[name->size] = '\0';
+
+	return 0;
+}
+
+
+static size_t get_binary_object_header(obj_hdr *hdr, git_buf *obj)
+{
+	unsigned char c;
+	unsigned char *data = (unsigned char *)obj->ptr;
+	size_t shift, size, used = 0;
+
+	if (git_buf_len(obj) == 0)
+		return 0;
+
+	c = data[used++];
+	hdr->type = (c >> 4) & 7;
+
+	size = c & 15;
+	shift = 4;
+	while (c & 0x80) {
+		if (git_buf_len(obj) <= used)
+			return 0;
+		if (sizeof(size_t) * 8 <= shift)
+			return 0;
+		c = data[used++];
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+	hdr->size = size;
+
+	return used;
+}
+
+static size_t get_object_header(obj_hdr *hdr, unsigned char *data)
+{
+	char c, typename[10];
+	size_t size, used = 0;
+
+	/*
+	 * type name string followed by space.
+	 */
+	while ((c = data[used]) != ' ') {
+		typename[used++] = c;
+		if (used >= sizeof(typename))
+			return 0;
+	}
+	typename[used] = 0;
+	if (used == 0)
+		return 0;
+	hdr->type = git_object_string2type(typename);
+	used++; /* consume the space */
+
+	/*
+	 * length follows immediately in decimal (without
+	 * leading zeros).
+	 */
+	size = data[used++] - '0';
+	if (size > 9)
+		return 0;
+	if (size) {
+		while ((c = data[used]) != '\0') {
+			size_t d = c - '0';
+			if (d > 9)
+				break;
+			used++;
+			size = size * 10 + d;
+		}
+	}
+	hdr->size = size;
+
+	/*
+	 * the length must be followed by a zero byte
+	 */
+	if (data[used++] != '\0')
+		return 0;
+
+	return used;
+}
+
+
+
+/***********************************************************
+ *
+ * ZLIB RELATED FUNCTIONS
+ *
+ ***********************************************************/
+
+static void init_stream(z_stream *s, void *out, size_t len)
+{
+	memset(s, 0, sizeof(*s));
+	s->next_out = out;
+	s->avail_out = (uInt)len;
+}
+
+static void set_stream_input(z_stream *s, void *in, size_t len)
+{
+	s->next_in = in;
+	s->avail_in = (uInt)len;
+}
+
+static void set_stream_output(z_stream *s, void *out, size_t len)
+{
+	s->next_out = out;
+	s->avail_out = (uInt)len;
+}
+
+
+static int start_inflate(z_stream *s, git_buf *obj, void *out, size_t len)
+{
+	int status;
+
+	init_stream(s, out, len);
+	set_stream_input(s, obj->ptr, git_buf_len(obj));
+
+	if ((status = inflateInit(s)) < Z_OK)
+		return status;
+
+	return inflate(s, 0);
+}
+
+static int finish_inflate(z_stream *s)
+{
+	int status = Z_OK;
+
+	while (status == Z_OK)
+		status = inflate(s, Z_FINISH);
+
+	inflateEnd(s);
+
+	if ((status != Z_STREAM_END) || (s->avail_in != 0)) {
+		giterr_set(GITERR_ZLIB, "Failed to finish ZLib inflation. Stream aborted prematurely");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int is_zlib_compressed_data(unsigned char *data)
+{
+	unsigned int w;
+
+	w = ((unsigned int)(data[0]) << 8) + data[1];
+	return (data[0] & 0x8F) == 0x08 && !(w % 31);
+}
+
+static int inflate_buffer(void *in, size_t inlen, void *out, size_t outlen)
+{
+	z_stream zs;
+	int status = Z_OK;
+
+	memset(&zs, 0x0, sizeof(zs));
+
+	zs.next_out = out;
+	zs.avail_out = (uInt)outlen;
+
+	zs.next_in = in;
+	zs.avail_in = (uInt)inlen;
+
+	if (inflateInit(&zs) < Z_OK) {
+		giterr_set(GITERR_ZLIB, "Failed to inflate buffer");
+		return -1;
+	}
+
+	while (status == Z_OK)
+		status = inflate(&zs, Z_FINISH);
+
+	inflateEnd(&zs);
+
+	if (status != Z_STREAM_END /* || zs.avail_in != 0 */ ||
+		zs.total_out != outlen)
+	{
+		giterr_set(GITERR_ZLIB, "Failed to inflate buffer. Stream aborted prematurely");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void *inflate_tail(z_stream *s, void *hb, size_t used, obj_hdr *hdr)
+{
+	unsigned char *buf, *head = hb;
+	size_t tail;
+
+	/*
+	 * allocate a buffer to hold the inflated data and copy the
+	 * initial sequence of inflated data from the tail of the
+	 * head buffer, if any.
+	 */
+	if ((buf = git__malloc(hdr->size + 1)) == NULL) {
+		inflateEnd(s);
+		return NULL;
+	}
+	tail = s->total_out - used;
+	if (used > 0 && tail > 0) {
+		if (tail > hdr->size)
+			tail = hdr->size;
+		memcpy(buf, head + used, tail);
+	}
+	used = tail;
+
+	/*
+	 * inflate the remainder of the object data, if any
+	 */
+	if (hdr->size < used)
+		inflateEnd(s);
+	else {
+		set_stream_output(s, buf + used, hdr->size - used);
+		if (finish_inflate(s)) {
+			git__free(buf);
+			return NULL;
+		}
+	}
+
+	return buf;
+}
+
+/*
+ * At one point, there was a loose object format that was intended to
+ * mimic the format used in pack-files. This was to allow easy copying
+ * of loose object data into packs. This format is no longer used, but
+ * we must still read it.
+ */
+static int inflate_packlike_loose_disk_obj(git_rawobj *out, git_buf *obj)
+{
+	unsigned char *in, *buf;
+	obj_hdr hdr;
+	size_t len, used;
+
+	/*
+	 * read the object header, which is an (uncompressed)
+	 * binary encoding of the object type and size.
+	 */
+	if ((used = get_binary_object_header(&hdr, obj)) == 0 ||
+		!git_object_typeisloose(hdr.type)) {
+		giterr_set(GITERR_ODB, "Failed to inflate loose object.");
+		return -1;
+	}
+
+	/*
+	 * allocate a buffer and inflate the data into it
+	 */
+	buf = git__malloc(hdr.size + 1);
+	GITERR_CHECK_ALLOC(buf);
+
+	in = ((unsigned char *)obj->ptr) + used;
+	len = obj->size - used;
+	if (inflate_buffer(in, len, buf, hdr.size) < 0) {
+		git__free(buf);
+		return -1;
+	}
+	buf[hdr.size] = '\0';
+
+	out->data = buf;
+	out->len = hdr.size;
+	out->type = hdr.type;
+
+	return 0;
+}
+
+static int inflate_disk_obj(git_rawobj *out, git_buf *obj)
+{
+	unsigned char head[64], *buf;
+	z_stream zs;
+	obj_hdr hdr;
+	size_t used;
+
+	/*
+	 * check for a pack-like loose object
+	 */
+	if (!is_zlib_compressed_data((unsigned char *)obj->ptr))
+		return inflate_packlike_loose_disk_obj(out, obj);
+
+	/*
+	 * inflate the initial part of the io buffer in order
+	 * to parse the object header (type and size).
+	 */
+	if (start_inflate(&zs, obj, head, sizeof(head)) < Z_OK ||
+		(used = get_object_header(&hdr, head)) == 0 ||
+		!git_object_typeisloose(hdr.type))
+	{
+		giterr_set(GITERR_ODB, "Failed to inflate disk object.");
+		return -1;
+	}
+
+	/*
+	 * allocate a buffer and inflate the object data into it
+	 * (including the initial sequence in the head buffer).
+	 */
+	if ((buf = inflate_tail(&zs, head, used, &hdr)) == NULL)
+		return -1;
+	buf[hdr.size] = '\0';
+
+	out->data = buf;
+	out->len = hdr.size;
+	out->type = hdr.type;
+
+	return 0;
+}
+
+
+
+
+
+
+/***********************************************************
+ *
+ * ODB OBJECT READING & WRITING
+ *
+ * Backend for the public API; read headers and full objects
+ * from the ODB. Write raw data to the ODB.
+ *
+ ***********************************************************/
+
+static int read_loose(git_rawobj *out, git_buf *loc)
+{
+	int error;
+	git_buf obj = GIT_BUF_INIT;
+
+	assert(out && loc);
+
+	if (git_buf_oom(loc))
+		return -1;
+
+	out->data = NULL;
+	out->len = 0;
+	out->type = GIT_OBJ_BAD;
+
+	if (!(error = git_futils_readbuffer(&obj, loc->ptr)))
+		error = inflate_disk_obj(out, &obj);
+
+	git_buf_free(&obj);
+
+	return error;
+}
+
+static int read_header_loose(git_rawobj *out, git_buf *loc)
+{
+	int error = 0, z_return = Z_ERRNO, read_bytes;
+	git_file fd;
+	z_stream zs;
+	obj_hdr header_obj;
+	unsigned char raw_buffer[16], inflated_buffer[64];
+
+	assert(out && loc);
+
+	if (git_buf_oom(loc))
+		return -1;
+
+	out->data = NULL;
+
+	if ((fd = git_futils_open_ro(loc->ptr)) < 0)
+		return fd;
+
+	init_stream(&zs, inflated_buffer, sizeof(inflated_buffer));
+
+	z_return = inflateInit(&zs);
+
+	while (z_return == Z_OK) {
+		if ((read_bytes = p_read(fd, raw_buffer, sizeof(raw_buffer))) > 0) {
+			set_stream_input(&zs, raw_buffer, read_bytes);
+			z_return = inflate(&zs, 0);
+		} else
+			z_return = Z_STREAM_END;
+	}
+
+	if ((z_return != Z_STREAM_END && z_return != Z_BUF_ERROR)
+		|| get_object_header(&header_obj, inflated_buffer) == 0
+		|| git_object_typeisloose(header_obj.type) == 0)
+	{
+		giterr_set(GITERR_ZLIB, "Failed to read loose object header");
+		error = -1;
+	} else {
+		out->len = header_obj.size;
+		out->type = header_obj.type;
+	}
+
+	finish_inflate(&zs);
+	p_close(fd);
+
+	return error;
+}
+
+static int locate_object(
+	git_buf *object_location,
+	loose_backend *backend,
+	const git_oid *oid)
+{
+	int error = object_file_name(object_location, backend->objects_dir, oid);
+
+	if (!error && !git_path_exists(object_location->ptr))
+		return GIT_ENOTFOUND;
+
+	return error;
+}
+
+/* Explore an entry of a directory and see if it matches a short oid */
+static int fn_locate_object_short_oid(void *state, git_buf *pathbuf) {
+	loose_locate_object_state *sstate = (loose_locate_object_state *)state;
+
+	if (git_buf_len(pathbuf) - sstate->dir_len != GIT_OID_HEXSZ - 2) {
+		/* Entry cannot be an object. Continue to next entry */
+		return 0;
+	}
+
+	if (git_path_isdir(pathbuf->ptr) == false) {
+		/* We are already in the directory matching the 2 first hex characters,
+		 * compare the first ncmp characters of the oids */
+		if (!memcmp(sstate->short_oid + 2,
+			(unsigned char *)pathbuf->ptr + sstate->dir_len,
+			sstate->short_oid_len - 2)) {
+
+			if (!sstate->found) {
+				sstate->res_oid[0] = sstate->short_oid[0];
+				sstate->res_oid[1] = sstate->short_oid[1];
+				memcpy(sstate->res_oid+2, pathbuf->ptr+sstate->dir_len, GIT_OID_HEXSZ-2);
+			}
+			sstate->found++;
+		}
+	}
+
+	if (sstate->found > 1)
+		return git_odb__error_ambiguous("multiple matches in loose objects");
+
+	return 0;
+}
+
+/* Locate an object matching a given short oid */
+static int locate_object_short_oid(
+	git_buf *object_location,
+	git_oid *res_oid,
+	loose_backend *backend,
+	const git_oid *short_oid,
+	unsigned int len)
+{
+	char *objects_dir = backend->objects_dir;
+	size_t dir_len = strlen(objects_dir);
+	loose_locate_object_state state;
+	int error;
+
+	/* prealloc memory for OBJ_DIR/xx/ */
+	if (git_buf_grow(object_location, dir_len + 5) < 0)
+		return -1;
+
+	git_buf_sets(object_location, objects_dir);
+	git_path_to_dir(object_location);
+
+	/* save adjusted position at end of dir so it can be restored later */
+	dir_len = git_buf_len(object_location);
+
+	/* Convert raw oid to hex formatted oid */
+	git_oid_fmt((char *)state.short_oid, short_oid);
+
+	/* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
+	if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0)
+		return -1;
+
+	/* Check that directory exists */
+	if (git_path_isdir(object_location->ptr) == false)
+		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+
+	state.dir_len = git_buf_len(object_location);
+	state.short_oid_len = len;
+	state.found = 0;
+
+	/* Explore directory to find a unique object matching short_oid */
+	error = git_path_direach(
+		object_location, fn_locate_object_short_oid, &state);
+	if (error)
+		return error;
+
+	if (!state.found)
+		return git_odb__error_notfound("no matching loose object for prefix", short_oid);
+
+	/* Convert obtained hex formatted oid to raw */
+	error = git_oid_fromstr(res_oid, (char *)state.res_oid);
+	if (error)
+		return error;
+
+	/* Update the location according to the oid obtained */
+
+	git_buf_truncate(object_location, dir_len);
+	if (git_buf_grow(object_location, dir_len + GIT_OID_HEXSZ + 2) < 0)
+		return -1;
+
+	git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
+
+	object_location->size += GIT_OID_HEXSZ + 1;
+	object_location->ptr[object_location->size] = '\0';
+
+	return 0;
+}
+
+
+
+
+
+
+
+
+
+/***********************************************************
+ *
+ * LOOSE BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+
+static int loose_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+	git_buf object_path = GIT_BUF_INIT;
+	git_rawobj raw;
+	int error;
+
+	assert(backend && oid);
+
+	raw.len = 0;
+	raw.type = GIT_OBJ_BAD;
+
+	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
+		error = git_odb__error_notfound("no matching loose object", oid);
+	else if ((error = read_header_loose(&raw, &object_path)) == 0) {
+		*len_p = raw.len;
+		*type_p = raw.type;
+	}
+
+	git_buf_free(&object_path);
+
+	return error;
+}
+
+static int loose_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+	git_buf object_path = GIT_BUF_INIT;
+	git_rawobj raw;
+	int error = 0;
+
+	assert(backend && oid);
+
+	if (locate_object(&object_path, (loose_backend *)backend, oid) < 0)
+		error = git_odb__error_notfound("no matching loose object", oid);
+	else if ((error = read_loose(&raw, &object_path)) == 0) {
+		*buffer_p = raw.data;
+		*len_p = raw.len;
+		*type_p = raw.type;
+	}
+
+	git_buf_free(&object_path);
+
+	return error;
+}
+
+static int loose_backend__read_prefix(
+	git_oid *out_oid,
+	void **buffer_p,
+	size_t *len_p,
+	git_otype *type_p,
+	git_odb_backend *backend,
+	const git_oid *short_oid,
+	unsigned int len)
+{
+	int error = 0;
+
+	if (len < GIT_OID_MINPREFIXLEN)
+		error = git_odb__error_ambiguous("prefix length too short");
+
+	else if (len >= GIT_OID_HEXSZ) {
+		/* We can fall back to regular read method */
+		error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+		if (!error)
+			git_oid_cpy(out_oid, short_oid);
+	} else {
+		git_buf object_path = GIT_BUF_INIT;
+		git_rawobj raw;
+
+		assert(backend && short_oid);
+
+		if ((error = locate_object_short_oid(&object_path, out_oid,
+				(loose_backend *)backend, short_oid, len)) == 0 &&
+			(error = read_loose(&raw, &object_path)) == 0)
+		{
+			*buffer_p = raw.data;
+			*len_p = raw.len;
+			*type_p = raw.type;
+		}
+
+		git_buf_free(&object_path);
+	}
+
+	return error;
+}
+
+static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+	git_buf object_path = GIT_BUF_INIT;
+	int error;
+
+	assert(backend && oid);
+
+	error = locate_object(&object_path, (loose_backend *)backend, oid);
+
+	git_buf_free(&object_path);
+
+	return !error;
+}
+
+static int loose_backend__stream_fwrite(git_oid *oid, git_odb_stream *_stream)
+{
+	loose_writestream *stream = (loose_writestream *)_stream;
+	loose_backend *backend = (loose_backend *)_stream->backend;
+	git_buf final_path = GIT_BUF_INIT;
+	int error = 0;
+
+	if (git_filebuf_hash(oid, &stream->fbuf) < 0 ||
+		object_file_name(&final_path, backend->objects_dir, oid) < 0 ||
+		git_futils_mkpath2file(final_path.ptr, GIT_OBJECT_DIR_MODE) < 0)
+		error = -1;
+	/*
+	 * Don't try to add an existing object to the repository. This
+	 * is what git does and allows us to sidestep the fact that
+	 * we're not allowed to overwrite a read-only file on Windows.
+	 */
+	else if (git_path_exists(final_path.ptr) == true)
+		git_filebuf_cleanup(&stream->fbuf);
+	else
+		error = git_filebuf_commit_at(
+			&stream->fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE);
+
+	git_buf_free(&final_path);
+
+	return error;
+}
+
+static int loose_backend__stream_write(git_odb_stream *_stream, const char *data, size_t len)
+{
+	loose_writestream *stream = (loose_writestream *)_stream;
+	return git_filebuf_write(&stream->fbuf, data, len);
+}
+
+static void loose_backend__stream_free(git_odb_stream *_stream)
+{
+	loose_writestream *stream = (loose_writestream *)_stream;
+
+	git_filebuf_cleanup(&stream->fbuf);
+	git__free(stream);
+}
+
+static int format_object_header(char *hdr, size_t n, size_t obj_len, git_otype obj_type)
+{
+	const char *type_str = git_object_type2string(obj_type);
+	int len = snprintf(hdr, n, "%s %"PRIuZ, type_str, obj_len);
+
+	assert(len > 0);				/* otherwise snprintf() is broken */
+	assert(((size_t)len) < n);		/* otherwise the caller is broken! */
+
+	return len+1;
+}
+
+static int loose_backend__stream(git_odb_stream **stream_out, git_odb_backend *_backend, size_t length, git_otype type)
+{
+	loose_backend *backend;
+	loose_writestream *stream = NULL;
+	char hdr[64];
+	git_buf tmp_path = GIT_BUF_INIT;
+	int hdrlen;
+
+	assert(_backend);
+
+	backend = (loose_backend *)_backend;
+	*stream_out = NULL;
+
+	hdrlen = format_object_header(hdr, sizeof(hdr), length, type);
+
+	stream = git__calloc(1, sizeof(loose_writestream));
+	GITERR_CHECK_ALLOC(stream);
+
+	stream->stream.backend = _backend;
+	stream->stream.read = NULL; /* read only */
+	stream->stream.write = &loose_backend__stream_write;
+	stream->stream.finalize_write = &loose_backend__stream_fwrite;
+	stream->stream.free = &loose_backend__stream_free;
+	stream->stream.mode = GIT_STREAM_WRONLY;
+
+	if (git_buf_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
+		git_filebuf_open(&stream->fbuf, tmp_path.ptr,
+			GIT_FILEBUF_HASH_CONTENTS |
+			GIT_FILEBUF_TEMPORARY |
+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0 ||
+		stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
+	{
+		git_filebuf_cleanup(&stream->fbuf);
+		git__free(stream);
+		stream = NULL;
+	}
+	git_buf_free(&tmp_path);
+	*stream_out = (git_odb_stream *)stream;
+
+	return !stream ? -1 : 0;
+}
+
+static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type)
+{
+	int error = 0, header_len;
+	git_buf final_path = GIT_BUF_INIT;
+	char header[64];
+	git_filebuf fbuf = GIT_FILEBUF_INIT;
+	loose_backend *backend;
+
+	backend = (loose_backend *)_backend;
+
+	/* prepare the header for the file */
+	header_len = format_object_header(header, sizeof(header), len, type);
+
+	if (git_buf_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
+		git_filebuf_open(&fbuf, final_path.ptr,
+			GIT_FILEBUF_HASH_CONTENTS |
+			GIT_FILEBUF_TEMPORARY |
+			(backend->object_zlib_level << GIT_FILEBUF_DEFLATE_SHIFT)) < 0)
+	{
+		error = -1;
+		goto cleanup;
+	}
+
+	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 ||
+		git_filebuf_commit_at(&fbuf, final_path.ptr, GIT_OBJECT_FILE_MODE) < 0)
+		error = -1;
+
+cleanup:
+	if (error < 0)
+		git_filebuf_cleanup(&fbuf);
+	git_buf_free(&final_path);
+	return error;
+}
+
+static void loose_backend__free(git_odb_backend *_backend)
+{
+	loose_backend *backend;
+	assert(_backend);
+	backend = (loose_backend *)_backend;
+
+	git__free(backend->objects_dir);
+	git__free(backend);
+}
+
+int git_odb_backend_loose(
+	git_odb_backend **backend_out,
+	const char *objects_dir,
+	int compression_level,
+	int do_fsync)
+{
+	loose_backend *backend;
+
+	backend = git__calloc(1, sizeof(loose_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	backend->objects_dir = git__strdup(objects_dir);
+	GITERR_CHECK_ALLOC(backend->objects_dir);
+
+	if (compression_level < 0)
+		compression_level = Z_BEST_SPEED;
+
+	backend->object_zlib_level = compression_level;
+	backend->fsync_object_files = do_fsync;
+
+	backend->parent.read = &loose_backend__read;
+	backend->parent.write = &loose_backend__write;
+	backend->parent.read_prefix = &loose_backend__read_prefix;
+	backend->parent.read_header = &loose_backend__read_header;
+	backend->parent.writestream = &loose_backend__stream;
+	backend->parent.exists = &loose_backend__exists;
+	backend->parent.free = &loose_backend__free;
+
+	*backend_out = (git_odb_backend *)backend;
+	return 0;
+}
diff --git a/libgit2/src/odb_pack.c b/libgit2/src/odb_pack.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/odb_pack.c
@@ -0,0 +1,473 @@
+/*
+ * 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 <zlib.h>
+#include "git2/repository.h"
+#include "git2/oid.h"
+#include "fileops.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta-apply.h"
+#include "sha1_lookup.h"
+#include "mwindow.h"
+#include "pack.h"
+
+#include "git2/odb_backend.h"
+
+struct pack_backend {
+	git_odb_backend parent;
+	git_vector packs;
+	struct git_pack_file *last_found;
+	char *pack_folder;
+	time_t pack_folder_mtime;
+};
+
+/**
+ * The wonderful tale of a Packed Object lookup query
+ * ===================================================
+ *	A riveting and epic story of epicness and ASCII
+ *			art, presented by yours truly,
+ *				Sir Vicent of Marti
+ *
+ *
+ *	Chapter 1: Once upon a time...
+ *	Initialization of the Pack Backend
+ *	--------------------------------------------------
+ *
+ *	# git_odb_backend_pack
+ *	| Creates the pack backend structure, initializes the
+ *	| callback pointers to our default read() and exist() methods,
+ *	| and tries to preload all the known packfiles in the ODB.
+ * |
+ *	|-# packfile_load_all
+ *	 | Tries to find the `pack` folder, if it exists. ODBs without
+ *	 | a pack folder are ignored altogether. If there's a `pack` folder
+ *	 | we run a `dirent` callback through every file in the pack folder
+ *	 | to find our packfiles. The packfiles are then sorted according
+ *	 | to a sorting callback.
+ * 	 |
+ *	 |-# packfile_load__cb
+ *	 | | This callback is called from `dirent` with every single file
+ *	 | | inside the pack folder. We find the packs by actually locating
+ *	 | | their index (ends in ".idx"). From that index, we verify that
+ *	 | | the corresponding packfile exists and is valid, and if so, we
+ *	| | add it to the pack list.
+ *	 | |
+ *	 | |-# packfile_check
+ *	 |		Make sure that there's a packfile to back this index, and store
+ *	 |		some very basic information regarding the packfile itself,
+ *	 |		such as the full path, the size, and the modification time.
+ *	 |		We don't actually open the packfile to check for internal consistency.
+ *	|
+ *	|-# packfile_sort__cb
+ *		Sort all the preloaded packs according to some specific criteria:
+ *		we prioritize the "newer" packs because it's more likely they
+ *		contain the objects we are looking for, and we prioritize local
+ *		packs over remote ones.
+ *
+ *
+ *
+ *	Chapter 2: To be, or not to be...
+ *	A standard packed `exist` query for an OID
+ *	--------------------------------------------------
+ *
+ * # pack_backend__exists
+ * | Check if the given SHA1 oid exists in any of the packs
+ * | that have been loaded for our ODB.
+ * |
+ * |-# pack_entry_find
+ *	| Iterate through all the packs that have been preloaded
+ *	| (starting by the pack where the latest object was found)
+ *	| to try to find the OID in one of them.
+ *	|
+ *	|-# pack_entry_find1
+ *		| Check the index of an individual pack to see if the SHA1
+ *		| OID can be found. If we can find the offset to that SHA1
+ *		| inside of the index, that means the object is contained
+ *		| inside of the packfile and we can stop searching.
+ *		| Before returning, we verify that the packfile behing the
+ *		| index we are searching still exists on disk.
+ *		|
+ *		|-# pack_entry_find_offset
+ *		| | Mmap the actual index file to disk if it hasn't been opened
+ *		| | yet, and run a binary search through it to find the OID.
+ *		| | See <http://book.git-scm.com/7_the_packfile.html> for specifics
+ *		| | on the Packfile Index format and how do we find entries in it.
+ *		| |
+ *		| |-# pack_index_open
+ *		|	| Guess the name of the index based on the full path to the
+ *		|	| packfile, open it and verify its contents. Only if the index
+ *		|	| has not been opened already.
+ *		|	|
+ *		|	|-# pack_index_check
+ *		|		Mmap the index file and do a quick run through the header
+ *		|		to guess the index version (right now we support v1 and v2),
+ *		|		and to verify that the size of the index makes sense.
+ *		|
+ *		|-# packfile_open
+ *			See `packfile_open` in Chapter 3
+ *
+ *
+ *
+ *	Chapter 3: The neverending story...
+ *	A standard packed `lookup` query for an OID
+ *	--------------------------------------------------
+ *	TODO
+ *
+ */
+
+
+/***********************************************************
+ *
+ * FORWARD DECLARATIONS
+ *
+ ***********************************************************/
+
+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);
+
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
+ */
+static int pack_entry_find_prefix(
+	struct git_pack_entry *e,
+	struct pack_backend *backend,
+	const git_oid *short_oid,
+	unsigned int len);
+
+
+
+/***********************************************************
+ *
+ * PACK WINDOW MANAGEMENT
+ *
+ ***********************************************************/
+
+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_;
+	const struct git_pack_file *b = b_;
+	int st;
+
+	/*
+	 * Local packs tend to contain objects specific to our
+	 * variant of the project than remote ones. In addition,
+	 * remote ones could be on a network mounted filesystem.
+	 * Favor local ones for these reasons.
+	 */
+	st = a->pack_local - b->pack_local;
+	if (st)
+		return -st;
+
+	/*
+	 * Younger packs tend to contain more recent objects,
+	 * and more recent objects tend to get accessed more
+	 * often.
+	 */
+	if (a->mtime < b->mtime)
+		return 1;
+	else if (a->mtime == b->mtime)
+		return 0;
+
+	return -1;
+}
+
+
+
+static int packfile_load__cb(void *_data, git_buf *path)
+{
+	struct pack_backend *backend = (struct pack_backend *)_data;
+	struct git_pack_file *pack;
+	int error;
+	unsigned int i;
+
+	if (git__suffixcmp(path->ptr, ".idx") != 0)
+		return 0; /* not an index */
+
+	for (i = 0; i < backend->packs.length; ++i) {
+		struct git_pack_file *p = git_vector_get(&backend->packs, i);
+		if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0)
+			return 0;
+	}
+
+	error = git_packfile_check(&pack, path->ptr);
+	if (error == GIT_ENOTFOUND)
+		/* ignore missing .pack file as git does */
+		return 0;
+	else if (error < 0)
+		return error;
+
+	return git_vector_insert(&backend->packs, pack);
+}
+
+static int packfile_refresh_all(struct pack_backend *backend)
+{
+	int error;
+	struct stat st;
+
+	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);
+
+	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);
+
+		if (error < 0)
+			return error;
+
+		git_vector_sort(&backend->packs);
+		backend->pack_folder_mtime = st.st_mtime;
+	}
+
+	return 0;
+}
+
+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;
+
+	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;
+		}
+	}
+
+	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)
+{
+	int error;
+	unsigned int 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 (error == GIT_EAMBIGUOUS)
+			return error;
+		if (!error)
+			found = 1;
+	}
+
+	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;
+
+		error = git_pack_entry_find(e, p, short_oid, len);
+		if (error == GIT_EAMBIGUOUS)
+			return error;
+		if (!error) {
+			if (++found > 1)
+				break;
+			backend->last_found = p;
+		}
+	}
+
+	if (!found)
+		return git_odb__error_notfound("no matching pack entry for prefix", short_oid);
+	else if (found > 1)
+		return git_odb__error_ambiguous("found multiple pack entries");
+	else
+		return 0;
+}
+
+
+/***********************************************************
+ *
+ * PACKED BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+
+/*
+int pack_backend__read_header(git_rawobj *obj, git_odb_backend *backend, const git_oid *oid)
+{
+	pack_location location;
+
+	assert(obj && backend && oid);
+
+	if (locate_packfile(&location, (struct pack_backend *)backend, oid) < 0)
+		return GIT_ENOTFOUND;
+
+	return read_header_packed(obj, &location);
+}
+*/
+
+static int pack_backend__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+	struct git_pack_entry e;
+	git_rawobj raw;
+	int error;
+
+	if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
+		(error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
+		return error;
+
+	*buffer_p = raw.data;
+	*len_p = raw.len;
+	*type_p = raw.type;
+
+	return 0;
+}
+
+static int pack_backend__read_prefix(
+	git_oid *out_oid,
+	void **buffer_p,
+	size_t *len_p,
+	git_otype *type_p,
+	git_odb_backend *backend,
+	const git_oid *short_oid,
+	unsigned int len)
+{
+	int error = 0;
+
+	if (len < GIT_OID_MINPREFIXLEN)
+		error = git_odb__error_ambiguous("prefix length too short");
+
+	else if (len >= GIT_OID_HEXSZ) {
+		/* We can fall back to regular read method */
+		error = pack_backend__read(buffer_p, len_p, type_p, backend, short_oid);
+		if (!error)
+			git_oid_cpy(out_oid, short_oid);
+	} else {
+		struct git_pack_entry e;
+		git_rawobj raw;
+
+		if ((error = pack_entry_find_prefix(
+				&e, (struct pack_backend *)backend, short_oid, len)) == 0 &&
+			(error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+		{
+			*buffer_p = raw.data;
+			*len_p = raw.len;
+			*type_p = raw.type;
+			git_oid_cpy(out_oid, &e.sha1);
+		}
+	}
+
+	return error;
+}
+
+static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+	struct git_pack_entry e;
+	return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
+}
+
+static void pack_backend__free(git_odb_backend *_backend)
+{
+	struct pack_backend *backend;
+	unsigned int 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_vector_free(&backend->packs);
+	git__free(backend->pack_folder);
+	git__free(backend);
+}
+
+int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
+{
+	struct pack_backend *backend = NULL;
+	git_buf path = GIT_BUF_INIT;
+
+	backend = git__calloc(1, sizeof(struct pack_backend));
+	GITERR_CHECK_ALLOC(backend);
+
+	if (git_vector_init(&backend->packs, 8, packfile_sort__cb) < 0 ||
+		git_buf_joinpath(&path, objects_dir, "pack") < 0)
+	{
+		git__free(backend);
+		return -1;
+	}
+
+	if (git_path_isdir(git_buf_cstr(&path)) == true) {
+		backend->pack_folder = git_buf_detach(&path);
+		backend->pack_folder_mtime = 0;
+	}
+
+	backend->parent.read = &pack_backend__read;
+	backend->parent.read_prefix = &pack_backend__read_prefix;
+	backend->parent.read_header = NULL;
+	backend->parent.exists = &pack_backend__exists;
+	backend->parent.free = &pack_backend__free;
+
+	*backend_out = (git_odb_backend *)backend;
+
+	git_buf_free(&path);
+
+	return 0;
+}
diff --git a/libgit2/src/oid.c b/libgit2/src/oid.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/oid.c
@@ -0,0 +1,387 @@
+/*
+ * 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/oid.h"
+#include "repository.h"
+#include <string.h>
+#include <limits.h>
+
+static char to_hex[] = "0123456789abcdef";
+
+static int oid_error_invalid(const char *msg)
+{
+	giterr_set(GITERR_INVALID, "Unable to parse OID - %s", msg);
+	return -1;
+}
+
+int git_oid_fromstrn(git_oid *out, const char *str, size_t length)
+{
+	size_t p;
+	int v;
+
+	if (length < 4)
+		return oid_error_invalid("input too short");
+
+	if (length > GIT_OID_HEXSZ)
+		length = GIT_OID_HEXSZ;
+
+	for (p = 0; p < length - 1; p += 2) {
+		v = (git__fromhex(str[p + 0]) << 4)
+				| git__fromhex(str[p + 1]);
+
+		if (v < 0)
+			return oid_error_invalid("contains invalid characters");
+
+		out->id[p / 2] = (unsigned char)v;
+	}
+
+	if (length % 2) {
+		v = (git__fromhex(str[p + 0]) << 4);
+		if (v < 0)
+			return oid_error_invalid("contains invalid characters");
+
+		out->id[p / 2] = (unsigned char)v;
+		p += 2;
+	}
+
+	memset(out->id + p / 2, 0, (GIT_OID_HEXSZ - p) / 2);
+
+	return 0;
+}
+
+int git_oid_fromstr(git_oid *out, const char *str)
+{
+	return git_oid_fromstrn(out, str, GIT_OID_HEXSZ);
+}
+
+GIT_INLINE(char) *fmt_one(char *str, unsigned int val)
+{
+	*str++ = to_hex[val >> 4];
+	*str++ = to_hex[val & 0xf];
+	return str;
+}
+
+void git_oid_fmt(char *str, const git_oid *oid)
+{
+	size_t i;
+
+	for (i = 0; i < sizeof(oid->id); i++)
+		str = fmt_one(str, oid->id[i]);
+}
+
+void git_oid_pathfmt(char *str, const git_oid *oid)
+{
+	size_t i;
+
+	str = fmt_one(str, oid->id[0]);
+	*str++ = '/';
+	for (i = 1; i < sizeof(oid->id); i++)
+		str = fmt_one(str, oid->id[i]);
+}
+
+char *git_oid_allocfmt(const git_oid *oid)
+{
+	char *str = git__malloc(GIT_OID_HEXSZ + 1);
+	if (!str)
+		return NULL;
+	git_oid_fmt(str, oid);
+	str[GIT_OID_HEXSZ] = '\0';
+	return str;
+}
+
+char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
+{
+	char str[GIT_OID_HEXSZ];
+
+	if (!out || n == 0 || !oid)
+		return "";
+
+	n--; /* allow room for terminating NUL */
+
+	if (n > 0) {
+		git_oid_fmt(str, oid);
+		if (n > GIT_OID_HEXSZ)
+			n = GIT_OID_HEXSZ;
+		memcpy(out, str, n);
+	}
+
+	out[n] = '\0';
+
+	return out;
+}
+
+int git_oid__parse(
+	git_oid *oid, const char **buffer_out,
+	const char *buffer_end, const char *header)
+{
+	const size_t sha_len = GIT_OID_HEXSZ;
+	const size_t header_len = strlen(header);
+
+	const char *buffer = *buffer_out;
+
+	if (buffer + (header_len + sha_len + 1) > buffer_end)
+		return -1;
+
+	if (memcmp(buffer, header, header_len) != 0)
+		return -1;
+
+	if (buffer[header_len + sha_len] != '\n')
+		return -1;
+
+	if (git_oid_fromstr(oid, buffer + header_len) < 0)
+		return -1;
+
+	*buffer_out = buffer + (header_len + sha_len + 1);
+
+	return 0;
+}
+
+void git_oid__writebuf(git_buf *buf, const char *header, const git_oid *oid)
+{
+	char hex_oid[GIT_OID_HEXSZ];
+
+	git_oid_fmt(hex_oid, oid);
+	git_buf_puts(buf, header);
+	git_buf_put(buf, hex_oid, GIT_OID_HEXSZ);
+	git_buf_putc(buf, '\n');
+}
+
+void git_oid_fromraw(git_oid *out, const unsigned char *raw)
+{
+	memcpy(out->id, raw, sizeof(out->id));
+}
+
+void git_oid_cpy(git_oid *out, const git_oid *src)
+{
+	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)
+{
+	const unsigned char *a = oid_a->id;
+	const unsigned char *b = oid_b->id;
+
+	do {
+		if (*a != *b)
+			return 1;
+		a++;
+		b++;
+		len -= 2;
+	} while (len > 1);
+
+	if (len)
+		if ((*a ^ *b) & 0xf0)
+			return 1;
+
+	return 0;
+}
+
+int git_oid_streq(const git_oid *a, const char *str)
+{
+	git_oid id;
+
+	if (git_oid_fromstr(&id, str) < 0)
+		return -1;
+
+	return git_oid_cmp(a, &id) == 0 ? 0 : -1;
+}
+
+int git_oid_iszero(const git_oid *oid_a)
+{
+	const unsigned char *a = oid_a->id;
+	unsigned int i;
+	for (i = 0; i < GIT_OID_RAWSZ; ++i, ++a)
+		if (*a != 0)
+			return 0;
+	return 1;
+}
+
+typedef short node_index;
+
+typedef union {
+	const char *tail;
+	node_index children[16];
+} trie_node;
+
+struct git_oid_shorten {
+	trie_node *nodes;
+	size_t node_count, size;
+	int min_length, full;
+};
+
+static int resize_trie(git_oid_shorten *self, size_t new_size)
+{
+	self->nodes = git__realloc(self->nodes, new_size * sizeof(trie_node));
+	GITERR_CHECK_ALLOC(self->nodes);
+
+	if (new_size > self->size) {
+		memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
+	}
+
+	self->size = new_size;
+	return 0;
+}
+
+static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
+{
+	trie_node *node, *leaf;
+	node_index idx_leaf;
+
+	if (os->node_count >= os->size) {
+		if (resize_trie(os, os->size * 2) < 0)
+			return NULL;
+	}
+
+	idx_leaf = (node_index)os->node_count++;
+
+	if (os->node_count == SHRT_MAX)
+		os->full = 1;
+
+	node = &os->nodes[idx];
+	node->children[push_at] = -idx_leaf;
+
+	leaf = &os->nodes[idx_leaf];
+	leaf->tail = oid;
+
+	return node;
+}
+
+git_oid_shorten *git_oid_shorten_new(size_t min_length)
+{
+	git_oid_shorten *os;
+
+	assert((size_t)((int)min_length) == min_length);
+
+	os = git__calloc(1, sizeof(git_oid_shorten));
+	if (os == NULL)
+		return NULL;
+
+	if (resize_trie(os, 16) < 0) {
+		git__free(os);
+		return NULL;
+	}
+
+	os->node_count = 1;
+	os->min_length = (int)min_length;
+
+	return os;
+}
+
+void git_oid_shorten_free(git_oid_shorten *os)
+{
+	git__free(os->nodes);
+	git__free(os);
+}
+
+
+/*
+ * What wizardry is this?
+ *
+ * This is just a memory-optimized trie: basically a very fancy
+ * 16-ary tree, which is used to store the prefixes of the OID
+ * strings.
+ *
+ * Read more: http://en.wikipedia.org/wiki/Trie
+ *
+ * Magic that happens in this method:
+ *
+ *	- Each node in the trie is an union, so it can work both as
+ *	a normal node, or as a leaf.
+ *
+ *	- 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
+ *	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
+ *	fit in the structure to about 20000 (assuming a more or less uniform
+ *	distribution).
+ *
+ *	- All the nodes in om->index array are stored contiguously in
+ *	memory, and each of them is 32 bytes, so we fit 2x nodes per
+ *	cache line. Convenient for speed.
+ *
+ *	- To differentiate the leafs from the normal nodes, we store all
+ *	the indexes towards a leaf as a negative index (indexes to normal
+ *	nodes are positives). When we find that one of the children for
+ *	a node has a negative value, that means it's going to be a leaf.
+ *	This reduces the amount of indexes we have by two, but also reduces
+ *	the size of each node by 1-4 bytes (the amount we would need to
+ *	add a `is_leaf` field): this is good because it allows the nodes
+ *	to fit cleanly in cache lines.
+ *
+ *	- Once we reach an empty children, instead of continuing to insert
+ *	new nodes for each remaining character of the OID, we store a pointer
+ *	to the tail in the leaf; if the leaf is reached again, we turn it
+ *	into a normal node and use the tail to create a new leaf.
+ *
+ *	This is a pretty good balance between performance and memory usage.
+ */
+int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
+{
+	int i;
+	bool is_leaf;
+	node_index idx;
+
+	if (os->full)
+		return -1;
+
+	if (text_oid == NULL)
+		return os->min_length;
+
+	idx = 0;
+	is_leaf = false;
+
+	for (i = 0; i < GIT_OID_HEXSZ; ++i) {
+		int c = git__fromhex(text_oid[i]);
+		trie_node *node;
+
+		if (c == -1) {
+			giterr_set(GITERR_INVALID, "Unable to shorten OID - invalid hex value");
+			return -1;
+		}
+
+		node = &os->nodes[idx];
+
+		if (is_leaf) {
+			const char *tail;
+
+			tail = node->tail;
+			node->tail = NULL;
+
+			node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
+			GITERR_CHECK_ALLOC(node);
+		}
+
+		if (node->children[c] == 0) {
+			if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL)
+				return -1;
+			break;
+		}
+
+		idx = node->children[c];
+		is_leaf = false;
+
+		if (idx < 0) {
+			node->children[c] = idx = -idx;
+			is_leaf = true;
+		}
+	}
+
+	if (++i > os->min_length)
+		os->min_length = i;
+
+	return os->min_length;
+}
+
diff --git a/libgit2/src/oidmap.h b/libgit2/src/oidmap.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/oidmap.h
@@ -0,0 +1,42 @@
+/*
+ * 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_oidmap_h__
+#define INCLUDE_oidmap_h__
+
+#include "common.h"
+#include "git2/oid.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(oid, const git_oid *, void *);
+typedef khash_t(oid) git_oidmap;
+
+GIT_INLINE(khint_t) hash_git_oid(const git_oid *oid)
+{
+	int i;
+	khint_t h = 0;
+	for (i = 0; i < 20; ++i)
+		h = (h << 5) - h + oid->id[i];
+	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)
+
+#define git_oidmap_alloc() kh_init(oid)
+#define git_oidmap_free(h) kh_destroy(oid,h), h = NULL
+
+#endif
diff --git a/libgit2/src/pack.c b/libgit2/src/pack.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pack.c
@@ -0,0 +1,816 @@
+/*
+ * 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 "odb.h"
+#include "pack.h"
+#include "delta-apply.h"
+#include "sha1_lookup.h"
+#include "mwindow.h"
+#include "fileops.h"
+
+#include "git2/oid.h"
+#include <zlib.h>
+
+static int packfile_open(struct git_pack_file *p);
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n);
+int packfile_unpack_compressed(
+		git_rawobj *obj,
+		struct git_pack_file *p,
+		git_mwindow **w_curs,
+		git_off_t *curpos,
+		size_t size,
+		git_otype type);
+
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
+ * is ambiguous within the pack.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and GIT_OID_HEXSZ.
+ */
+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);
+
+static int packfile_error(const char *message)
+{
+	giterr_set(GITERR_ODB, "Invalid pack file - %s", message);
+	return -1;
+}
+
+/***********************************************************
+ *
+ * PACK INDEX METHODS
+ *
+ ***********************************************************/
+
+static void pack_index_free(struct git_pack_file *p)
+{
+	if (p->index_map.data) {
+		git_futils_mmap_free(&p->index_map);
+		p->index_map.data = NULL;
+	}
+}
+
+static int pack_index_check(const char *path, struct git_pack_file *p)
+{
+	struct git_pack_idx_header *hdr;
+	uint32_t version, nr, i, *index;
+	void *idx_map;
+	size_t idx_size;
+	struct stat st;
+	int error;
+	/* TODO: properly open the file without access time using O_NOATIME */
+	git_file fd = git_futils_open_ro(path);
+	if (fd < 0)
+		return fd;
+
+	if (p_fstat(fd, &st) < 0 ||
+		!S_ISREG(st.st_mode) ||
+		!git__is_sizet(st.st_size) ||
+		(idx_size = (size_t)st.st_size) < 4 * 256 + 20 + 20)
+	{
+		p_close(fd);
+		giterr_set(GITERR_OS, "Failed to check pack index.");
+		return -1;
+	}
+
+	error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
+
+	p_close(fd);
+
+	if (error < 0)
+		return error;
+
+	hdr = idx_map = p->index_map.data;
+
+	if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
+		version = ntohl(hdr->idx_version);
+
+		if (version < 2 || version > 2) {
+			git_futils_mmap_free(&p->index_map);
+			return packfile_error("unsupported index version");
+		}
+
+	} else
+		version = 1;
+
+	nr = 0;
+	index = idx_map;
+
+	if (version > 1)
+		index += 2; /* skip index header */
+
+	for (i = 0; i < 256; i++) {
+		uint32_t n = ntohl(index[i]);
+		if (n < nr) {
+			git_futils_mmap_free(&p->index_map);
+			return packfile_error("index is non-monotonic");
+		}
+		nr = n;
+	}
+
+	if (version == 1) {
+		/*
+		 * Total size:
+		 * - 256 index entries 4 bytes each
+		 * - 24-byte entries * nr (20-byte sha1 + 4-byte offset)
+		 * - 20-byte SHA1 of the packfile
+		 * - 20-byte SHA1 file checksum
+		 */
+		if (idx_size != 4*256 + nr * 24 + 20 + 20) {
+			git_futils_mmap_free(&p->index_map);
+			return packfile_error("index is corrupted");
+		}
+	} else if (version == 2) {
+		/*
+		 * Minimum size:
+		 * - 8 bytes of header
+		 * - 256 index entries 4 bytes each
+		 * - 20-byte sha1 entry * nr
+		 * - 4-byte crc entry * nr
+		 * - 4-byte offset entry * nr
+		 * - 20-byte SHA1 of the packfile
+		 * - 20-byte SHA1 file checksum
+		 * And after the 4-byte offset table might be a
+		 * variable sized table containing 8-byte entries
+		 * for offsets larger than 2^31.
+		 */
+		unsigned long min_size = 8 + 4*256 + nr*(20 + 4 + 4) + 20 + 20;
+		unsigned long max_size = min_size;
+
+		if (nr)
+			max_size += (nr - 1)*8;
+
+		if (idx_size < min_size || idx_size > max_size) {
+			git_futils_mmap_free(&p->index_map);
+			return packfile_error("wrong index size");
+		}
+	}
+
+	p->index_version = version;
+	p->num_objects = nr;
+	return 0;
+}
+
+static int pack_index_open(struct git_pack_file *p)
+{
+	char *idx_name;
+	int error;
+	size_t name_len, offset;
+
+	if (p->index_map.data)
+		return 0;
+
+	idx_name = git__strdup(p->pack_name);
+	GITERR_CHECK_ALLOC(idx_name);
+
+	name_len = strlen(idx_name);
+	offset = name_len - strlen(".pack");
+	assert(offset < name_len); /* make sure no underflow */
+
+	strncpy(idx_name + offset, ".idx", name_len - offset);
+
+	error = pack_index_check(idx_name, p);
+	git__free(idx_name);
+
+	return error;
+}
+
+static unsigned char *pack_window_open(
+		struct git_pack_file *p,
+		git_mwindow **w_cursor,
+		git_off_t offset,
+		unsigned int *left)
+{
+	if (p->mwf.fd == -1 && packfile_open(p) < 0)
+		return NULL;
+
+	/* Since packfiles end in a hash of their content and it's
+	 * pointless to ask for an offset into the middle of that
+	 * hash, and the pack_window_contains function above wouldn't match
+	 * don't allow an offset too close to the end of the file.
+	 */
+	if (offset > (p->mwf.size - 20))
+		return NULL;
+
+	return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
+ }
+
+static int packfile_unpack_header1(
+		unsigned long *usedp,
+		size_t *sizep,
+		git_otype *type,
+		const unsigned char *buf,
+		unsigned long len)
+{
+	unsigned shift;
+	unsigned long size, c;
+	unsigned long used = 0;
+
+	c = buf[used++];
+	*type = (c >> 4) & 7;
+	size = c & 15;
+	shift = 4;
+	while (c & 0x80) {
+		if (len <= used)
+			return GIT_EBUFS;
+
+		if (bitsizeof(long) <= shift) {
+			*usedp = 0;
+			return -1;
+		}
+
+		c = buf[used++];
+		size += (c & 0x7f) << shift;
+		shift += 7;
+	}
+
+	*sizep = (size_t)size;
+	*usedp = used;
+	return 0;
+}
+
+int git_packfile_unpack_header(
+		size_t *size_p,
+		git_otype *type_p,
+		git_mwindow_file *mwf,
+		git_mwindow **w_curs,
+		git_off_t *curpos)
+{
+	unsigned char *base;
+	unsigned int left;
+	unsigned long used;
+	int ret;
+
+	/* pack_window_open() assures us we have [base, base + 20) available
+	 * as a range that we can look at at. (Its actually the hash
+	 * size that is assured.) With our object header encoding
+	 * the maximum deflated object size is 2^137, which is just
+	 * insane, so we know won't exceed what we have been given.
+	 */
+//	base = pack_window_open(p, w_curs, *curpos, &left);
+	base = git_mwindow_open(mwf, w_curs, *curpos, 20, &left);
+	if (base == NULL)
+		return GIT_EBUFS;
+
+	ret = packfile_unpack_header1(&used, size_p, type_p, base, left);
+	git_mwindow_close(w_curs);
+	if (ret == GIT_EBUFS)
+		return ret;
+	else if (ret < 0)
+		return packfile_error("header length is zero");
+
+	*curpos += used;
+	return 0;
+}
+
+static int packfile_unpack_delta(
+		git_rawobj *obj,
+		struct git_pack_file *p,
+		git_mwindow **w_curs,
+		git_off_t *curpos,
+		size_t delta_size,
+		git_otype delta_type,
+		git_off_t obj_offset)
+{
+	git_off_t base_offset;
+	git_rawobj base, delta;
+	int error;
+
+	base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset);
+	git_mwindow_close(w_curs);
+	if (base_offset == 0)
+		return packfile_error("delta offset is zero");
+	if (base_offset < 0) /* must actually be an error code */
+		return (int)base_offset;
+
+	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);
+		return error;
+	}
+
+	obj->type = base.type;
+	error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len);
+
+	git__free(base.data);
+	git__free(delta.data);
+
+	/* TODO: we might want to cache this shit. eventually */
+	//add_delta_base_cache(p, base_offset, base, base_size, *type);
+
+	return error; /* error set by git__delta_apply */
+}
+
+int git_packfile_unpack(
+	git_rawobj *obj,
+	struct git_pack_file *p,
+	git_off_t *obj_offset)
+{
+	git_mwindow *w_curs = NULL;
+	git_off_t curpos = *obj_offset;
+	int error;
+
+	size_t size = 0;
+	git_otype type;
+
+	/*
+	 * TODO: optionally check the CRC on the packfile
+	 */
+
+	obj->data = NULL;
+	obj->len = 0;
+	obj->type = GIT_OBJ_BAD;
+
+	error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos);
+	git_mwindow_close(&w_curs);
+
+	if (error < 0)
+		return error;
+
+	switch (type) {
+	case GIT_OBJ_OFS_DELTA:
+	case GIT_OBJ_REF_DELTA:
+		error = packfile_unpack_delta(
+				obj, p, &w_curs, &curpos,
+				size, type, *obj_offset);
+		break;
+
+	case GIT_OBJ_COMMIT:
+	case GIT_OBJ_TREE:
+	case GIT_OBJ_BLOB:
+	case GIT_OBJ_TAG:
+		error = packfile_unpack_compressed(
+				obj, p, &w_curs, &curpos,
+				size, type);
+		break;
+
+	default:
+		error = packfile_error("invalid packfile type in header");;
+		break;
+	}
+
+	*obj_offset = curpos;
+	return error;
+}
+
+static void *use_git_alloc(void *opaq, unsigned int count, unsigned int size)
+{
+	GIT_UNUSED(opaq);
+	return git__calloc(count, size);
+}
+
+static void use_git_free(void *opaq, void *ptr)
+{
+	GIT_UNUSED(opaq);
+	git__free(ptr);
+}
+
+int packfile_unpack_compressed(
+	git_rawobj *obj,
+	struct git_pack_file *p,
+	git_mwindow **w_curs,
+	git_off_t *curpos,
+	size_t size,
+	git_otype type)
+{
+	int st;
+	z_stream stream;
+	unsigned char *buffer, *in;
+
+	buffer = git__calloc(1, size + 1);
+	GITERR_CHECK_ALLOC(buffer);
+
+	memset(&stream, 0, sizeof(stream));
+	stream.next_out = buffer;
+	stream.avail_out = (uInt)size + 1;
+	stream.zalloc = use_git_alloc;
+	stream.zfree = use_git_free;
+
+	st = inflateInit(&stream);
+	if (st != Z_OK) {
+		git__free(buffer);
+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+
+		return -1;
+	}
+
+	do {
+		in = pack_window_open(p, w_curs, *curpos, &stream.avail_in);
+		stream.next_in = in;
+		st = inflate(&stream, Z_FINISH);
+		git_mwindow_close(w_curs);
+
+		if (!stream.avail_out)
+			break; /* the payload is larger than it should be */
+
+		if (st == Z_BUF_ERROR && in == NULL) {
+			inflateEnd(&stream);
+			git__free(buffer);
+			return GIT_EBUFS;
+		}
+
+		*curpos += stream.next_in - in;
+	} while (st == Z_OK || st == Z_BUF_ERROR);
+
+	inflateEnd(&stream);
+
+	if ((st != Z_STREAM_END) || stream.total_out != size) {
+		git__free(buffer);
+		giterr_set(GITERR_ZLIB, "Failed to inflate packfile");
+		return -1;
+	}
+
+	obj->type = type;
+	obj->len = size;
+	obj->data = buffer;
+	return 0;
+}
+
+/*
+ * curpos is where the data starts, delta_obj_offset is the where the
+ * header starts
+ */
+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)
+{
+	unsigned int left = 0;
+	unsigned char *base_info;
+	git_off_t base_offset;
+	git_oid unused;
+
+	base_info = pack_window_open(p, w_curs, *curpos, &left);
+	/* Assumption: the only reason this would fail is because the file is too small */
+	if (base_info == NULL)
+		return GIT_EBUFS;
+	/* pack_window_open() assured us we have [base_info, base_info + 20)
+	 * as a range that we can look at without walking off the
+	 * end of the mapped window. Its actually the hash size
+	 * that is assured. An OFS_DELTA longer than the hash size
+	 * is stupid, as then a REF_DELTA would be smaller to store.
+	 */
+	if (type == GIT_OBJ_OFS_DELTA) {
+		unsigned used = 0;
+		unsigned char c = base_info[used++];
+		base_offset = c & 127;
+		while (c & 128) {
+			if (left <= used)
+				return GIT_EBUFS;
+			base_offset += 1;
+			if (!base_offset || MSB(base_offset, 7))
+				return 0; /* overflow */
+			c = base_info[used++];
+			base_offset = (base_offset << 7) + (c & 127);
+		}
+		base_offset = delta_obj_offset - base_offset;
+		if (base_offset <= 0 || base_offset >= delta_obj_offset)
+			return 0; /* out of bound */
+		*curpos += used;
+	} 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;
+
+			git_oid_fromraw(&key.sha1, base_info);
+			pos = git_vector_bsearch(&p->cache, &key);
+			if (pos >= 0) {
+				*curpos += 20;
+				return ((struct git_pack_entry *)git_vector_get(&p->cache, pos))->offset;
+			}
+		}
+		/* The base entry _must_ be in the same pack */
+		if (pack_entry_find_offset(&base_offset, &unused, p, (git_oid *)base_info, GIT_OID_HEXSZ) < 0)
+			return packfile_error("base entry delta is not in the same pack");
+		*curpos += 20;
+	} else
+		return 0;
+
+	return base_offset;
+}
+
+/***********************************************************
+ *
+ * PACKFILE METHODS
+ *
+ ***********************************************************/
+
+static struct git_pack_file *packfile_alloc(size_t extra)
+{
+	struct git_pack_file *p = git__calloc(1, sizeof(*p) + extra);
+	if (p != NULL)
+		p->mwf.fd = -1;
+	return p;
+}
+
+
+void packfile_free(struct git_pack_file *p)
+{
+	assert(p);
+
+	/* clear_delta_base_cache(); */
+	git_mwindow_free_all(&p->mwf);
+
+	if (p->mwf.fd != -1)
+		p_close(p->mwf.fd);
+
+	pack_index_free(p);
+
+	git__free(p->bad_object_sha1);
+	git__free(p);
+}
+
+static int packfile_open(struct git_pack_file *p)
+{
+	struct stat st;
+	struct git_pack_header hdr;
+	git_oid sha1;
+	unsigned char *idx_sha1;
+
+	assert(p->index_map.data);
+
+	if (!p->index_map.data && pack_index_open(p) < 0)
+		return git_odb__error_notfound("failed to open packfile", NULL);
+
+	/* 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_fstat(p->mwf.fd, &st) < 0 ||
+		git_mwindow_file_register(&p->mwf) < 0)
+		goto cleanup;
+
+	/* If we created the struct before we had the pack we lack size. */
+	if (!p->mwf.size) {
+		if (!S_ISREG(st.st_mode))
+			goto cleanup;
+		p->mwf.size = (git_off_t)st.st_size;
+	} else if (p->mwf.size != st.st_size)
+		goto cleanup;
+
+#if 0
+	/* We leave these file descriptors open with sliding mmap;
+	 * there is no point keeping them open across exec(), though.
+	 */
+	fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
+	if (fd_flag < 0)
+		goto cleanup;
+
+	fd_flag |= FD_CLOEXEC;
+	if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
+		goto cleanup;
+#endif
+
+	/* Verify we recognize this pack file format. */
+	if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
+		hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
+		!pack_version_ok(hdr.hdr_version))
+		goto cleanup;
+
+	/* Verify the pack matches its index. */
+	if (p->num_objects != ntohl(hdr.hdr_entries) ||
+		p_lseek(p->mwf.fd, p->mwf.size - GIT_OID_RAWSZ, SEEK_SET) == -1 ||
+		p_read(p->mwf.fd, sha1.id, GIT_OID_RAWSZ) < 0)
+		goto cleanup;
+
+	idx_sha1 = ((unsigned char *)p->index_map.data) + p->index_map.len - 40;
+
+	if (git_oid_cmp(&sha1, (git_oid *)idx_sha1) == 0)
+		return 0;
+
+cleanup:
+	giterr_set(GITERR_OS, "Invalid packfile '%s'", p->pack_name);
+	p_close(p->mwf.fd);
+	p->mwf.fd = -1;
+	return -1;
+}
+
+int git_packfile_check(struct git_pack_file **pack_out, const char *path)
+{
+	struct stat st;
+	struct git_pack_file *p;
+	size_t path_len;
+
+	*pack_out = NULL;
+	path_len = strlen(path);
+	p = packfile_alloc(path_len + 2);
+	GITERR_CHECK_ALLOC(p);
+
+	/*
+	 * Make sure a corresponding .pack file exists and that
+	 * the index looks sane.
+	 */
+	path_len -= strlen(".idx");
+	if (path_len < 1) {
+		git__free(p);
+		return git_odb__error_notfound("invalid packfile path", NULL);
+	}
+
+	memcpy(p->pack_name, path, path_len);
+
+	strcpy(p->pack_name + path_len, ".keep");
+	if (git_path_exists(p->pack_name) == true)
+		p->pack_keep = 1;
+
+	strcpy(p->pack_name + path_len, ".pack");
+	if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
+		git__free(p);
+		return git_odb__error_notfound("packfile not found", NULL);
+	}
+
+	/* ok, it looks sane as far as we can check without
+	 * actually mapping the pack file.
+	 */
+	p->mwf.size = st.st_size;
+	p->pack_local = 1;
+	p->mtime = (git_time_t)st.st_mtime;
+
+	/* see if we can parse the sha1 oid in the packfile name */
+	if (path_len < 40 ||
+		git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0)
+		memset(&p->sha1, 0x0, GIT_OID_RAWSZ);
+
+	*pack_out = p;
+
+	return 0;
+}
+
+/***********************************************************
+ *
+ * PACKFILE ENTRY SEARCH INTERNALS
+ *
+ ***********************************************************/
+
+static git_off_t nth_packed_object_offset(const struct git_pack_file *p, uint32_t n)
+{
+	const unsigned char *index = p->index_map.data;
+	index += 4 * 256;
+	if (p->index_version == 1) {
+		return ntohl(*((uint32_t *)(index + 24 * n)));
+	} else {
+		uint32_t off;
+		index += 8 + p->num_objects * (20 + 4);
+		off = ntohl(*((uint32_t *)(index + 4 * n)));
+		if (!(off & 0x80000000))
+			return off;
+		index += p->num_objects * 4 + (off & 0x7fffffff) * 8;
+		return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+					ntohl(*((uint32_t *)(index + 4)));
+	}
+}
+
+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)
+{
+	const uint32_t *level1_ofs = p->index_map.data;
+	const unsigned char *index = p->index_map.data;
+	unsigned hi, lo, stride;
+	int pos, found = 0;
+	const unsigned char *current = 0;
+
+	*offset_out = 0;
+
+	if (index == NULL) {
+		int error;
+
+		if ((error = pack_index_open(p)) < 0)
+			return error;
+
+		assert(p->index_map.data);
+
+		index = p->index_map.data;
+		level1_ofs = p->index_map.data;
+	}
+
+	if (p->index_version > 1) {
+		level1_ofs += 2;
+		index += 8;
+	}
+
+	index += 4 * 256;
+	hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
+	lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
+
+	if (p->index_version > 1) {
+		stride = 20;
+	} else {
+		stride = 24;
+		index += 4;
+	}
+
+#ifdef INDEX_DEBUG_LOOKUP
+	printf("%02x%02x%02x... lo %u hi %u nr %d\n",
+		short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
+#endif
+
+	/* Use git.git lookup code */
+	pos = sha1_entry_pos(index, stride, 0, lo, hi, p->num_objects, short_oid->id);
+
+	if (pos >= 0) {
+		/* An object matching exactly the oid was found */
+		found = 1;
+		current = index + pos * stride;
+	} else {
+		/* No object was found */
+		/* pos refers to the object with the "closest" oid to short_oid */
+		pos = - 1 - pos;
+		if (pos < (int)p->num_objects) {
+			current = index + pos * stride;
+
+			if (!git_oid_ncmp(short_oid, (const git_oid *)current, len))
+				found = 1;
+		}
+	}
+
+	if (found && len != GIT_OID_HEXSZ && pos + 1 < (int)p->num_objects) {
+		/* Check for ambiguousity */
+		const unsigned char *next = current + stride;
+
+		if (!git_oid_ncmp(short_oid, (const git_oid *)next, len)) {
+			found = 2;
+		}
+	}
+
+	if (!found)
+		return git_odb__error_notfound("failed to find offset for pack entry", short_oid);
+	if (found > 1)
+		return git_odb__error_ambiguous("found multiple offsets for pack entry");
+	*offset_out = nth_packed_object_offset(p, pos);
+	git_oid_fromraw(found_oid, current);
+
+#ifdef INDEX_DEBUG_LOOKUP
+	{
+		unsigned char hex_sha1[GIT_OID_HEXSZ + 1];
+		git_oid_fmt(hex_sha1, found_oid);
+		hex_sha1[GIT_OID_HEXSZ] = '\0';
+		printf("found lo=%d %s\n", lo, hex_sha1);
+	}
+#endif
+	return 0;
+}
+
+int git_pack_entry_find(
+		struct git_pack_entry *e,
+		struct git_pack_file *p,
+		const git_oid *short_oid,
+		unsigned int len)
+{
+	git_off_t offset;
+	git_oid found_oid;
+	int error;
+
+	assert(p);
+
+	if (len == GIT_OID_HEXSZ && p->num_bad_objects) {
+		unsigned i;
+		for (i = 0; i < p->num_bad_objects; i++)
+			if (git_oid_cmp(short_oid, &p->bad_object_sha1[i]) == 0)
+				return packfile_error("bad object found in packfile");
+	}
+
+	error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
+	if (error < 0)
+		return error;
+
+	/* we found a unique entry in the index;
+	 * make sure the packfile backing the index
+	 * still exists on disk */
+	if (p->mwf.fd == -1 && (error = packfile_open(p)) < 0)
+		return error;
+
+	e->offset = offset;
+	e->p = p;
+
+	git_oid_cpy(&e->sha1, &found_oid);
+	return 0;
+}
diff --git a/libgit2/src/pack.h b/libgit2/src/pack.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pack.h
@@ -0,0 +1,106 @@
+/*
+ * 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_pack_h__
+#define INCLUDE_pack_h__
+
+#include "git2/oid.h"
+
+#include "common.h"
+#include "map.h"
+#include "mwindow.h"
+#include "odb.h"
+
+#define GIT_PACK_FILE_MODE 0444
+
+#define PACK_SIGNATURE 0x5041434b	/* "PACK" */
+#define PACK_VERSION 2
+#define pack_version_ok(v) ((v) == htonl(2) || (v) == htonl(3))
+struct git_pack_header {
+	uint32_t hdr_signature;
+	uint32_t hdr_version;
+	uint32_t hdr_entries;
+};
+
+/*
+ * The first four bytes of index formats later than version 1 should
+ * start with this signature, as all older git binaries would find this
+ * value illegal and abort reading the file.
+ *
+ * This is the case because the number of objects in a packfile
+ * cannot exceed 1,431,660,000 as every object would need at least
+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
+ * version 1 of the index file due to the offsets limited to 32 bits.
+ * Clearly the signature exceeds this maximum.
+ *
+ * Very old git binaries will also compare the first 4 bytes to the
+ * next 4 bytes in the index and abort with a "non-monotonic index"
+ * error if the second 4 byte word is smaller than the first 4
+ * byte word. This would be true in the proposed future index
+ * format as idx_signature would be greater than idx_version.
+ */
+
+#define PACK_IDX_SIGNATURE 0xff744f63	/* "\377tOc" */
+
+struct git_pack_idx_header {
+	uint32_t idx_signature;
+	uint32_t idx_version;
+};
+
+struct git_pack_file {
+	git_mwindow_file mwf;
+	git_map index_map;
+
+	uint32_t num_objects;
+	uint32_t num_bad_objects;
+	git_oid *bad_object_sha1; /* array of git_oid */
+
+	int index_version;
+	git_time_t mtime;
+	unsigned pack_local:1, pack_keep:1, has_cache:1;
+	git_oid sha1;
+	git_vector cache;
+
+	/* something like ".git/objects/pack/xxxxx.pack" */
+	char pack_name[GIT_FLEX_ARRAY]; /* more */
+};
+
+struct git_pack_entry {
+	git_off_t offset;
+	git_oid sha1;
+	struct git_pack_file *p;
+};
+
+int git_packfile_unpack_header(
+		size_t *size_p,
+		git_otype *type_p,
+		git_mwindow_file *mwf,
+		git_mwindow **w_curs,
+		git_off_t *curpos);
+
+int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, git_off_t *obj_offset);
+int packfile_unpack_compressed(
+	git_rawobj *obj,
+	struct git_pack_file *p,
+	git_mwindow **w_curs,
+	git_off_t *curpos,
+	size_t size,
+	git_otype type);
+
+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);
+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);
+
+#endif
diff --git a/libgit2/src/path.c b/libgit2/src/path.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/path.c
@@ -0,0 +1,656 @@
+/*
+ * 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 "path.h"
+#include "posix.h"
+#ifdef GIT_WIN32
+#include "win32/dir.h"
+#include "win32/posix.h"
+#else
+#include <dirent.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * Based on the Android implementation, BSD licensed.
+ * Check http://android.git.kernel.org/
+ */
+int git_path_basename_r(git_buf *buffer, const char *path)
+{
+	const char *endp, *startp;
+	int len, result;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		startp = ".";
+		len		= 1;
+		goto Exit;
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* All slashes becomes "/" */
+	if (endp == path && *endp == '/') {
+		startp = "/";
+		len	= 1;
+		goto Exit;
+	}
+
+	/* Find the start of the base */
+	startp = endp;
+	while (startp > path && *(startp - 1) != '/')
+		startp--;
+
+	/* Cast is safe because max path < max int */
+	len = (int)(endp - startp + 1);
+
+Exit:
+	result = len;
+
+	if (buffer != NULL && git_buf_set(buffer, startp, len) < 0)
+		return -1;
+
+	return result;
+}
+
+/*
+ * Based on the Android implementation, BSD licensed.
+ * Check http://android.git.kernel.org/
+ */
+int git_path_dirname_r(git_buf *buffer, const char *path)
+{
+	const char *endp;
+	int result, len;
+
+	/* Empty or NULL string gets treated as "." */
+	if (path == NULL || *path == '\0') {
+		path = ".";
+		len = 1;
+		goto Exit;
+	}
+
+	/* Strip trailing slashes */
+	endp = path + strlen(path) - 1;
+	while (endp > path && *endp == '/')
+		endp--;
+
+	/* Find the start of the dir */
+	while (endp > path && *endp != '/')
+		endp--;
+
+	/* Either the dir is "/" or there are no slashes */
+	if (endp == path) {
+		path = (*endp == '/') ? "/" : ".";
+		len = 1;
+		goto Exit;
+	}
+
+	do {
+		endp--;
+	} while (endp > path && *endp == '/');
+
+	/* Cast is safe because max path < max int */
+	len = (int)(endp - path + 1);
+
+#ifdef GIT_WIN32
+	/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
+		'C:/' here */
+
+	if (len == 2 && isalpha(path[0]) && path[1] == ':') {
+		len = 3;
+		goto Exit;
+	}
+#endif
+
+Exit:
+	result = len;
+
+	if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
+		return -1;
+
+	return result;
+}
+
+
+char *git_path_dirname(const char *path)
+{
+	git_buf buf = GIT_BUF_INIT;
+	char *dirname;
+
+	git_path_dirname_r(&buf, path);
+	dirname = git_buf_detach(&buf);
+	git_buf_free(&buf); /* avoid memleak if error occurs */
+
+	return dirname;
+}
+
+char *git_path_basename(const char *path)
+{
+	git_buf buf = GIT_BUF_INIT;
+	char *basename;
+
+	git_path_basename_r(&buf, path);
+	basename = git_buf_detach(&buf);
+	git_buf_free(&buf); /* avoid memleak if error occurs */
+
+	return basename;
+}
+
+
+const char *git_path_topdir(const char *path)
+{
+	size_t len;
+	ssize_t i;
+
+	assert(path);
+	len = strlen(path);
+
+	if (!len || path[len - 1] != '/')
+		return NULL;
+
+	for (i = (ssize_t)len - 2; i >= 0; --i)
+		if (path[i] == '/')
+			break;
+
+	return &path[i + 1];
+}
+
+int git_path_root(const char *path)
+{
+	int offset = 0;
+
+#ifdef GIT_WIN32
+	/* Does the root of the path look like a windows drive ? */
+	if (isalpha(path[0]) && (path[1] == ':'))
+		offset += 2;
+
+	/* Are we dealing with a windows network path? */
+	else if ((path[0] == '/' && path[1] == '/') ||
+		(path[0] == '\\' && path[1] == '\\'))
+	{
+		offset += 2;
+
+		/* Skip the computer name segment */
+		while (path[offset] && path[offset] != '/' && path[offset] != '\\')
+			offset++;
+	}
+#endif
+
+	if (path[offset] == '/' || path[offset] == '\\')
+		return offset;
+
+	return -1;	/* Not a real error - signals that path is not rooted */
+}
+
+int git_path_prettify(git_buf *path_out, const char *path, const char *base)
+{
+	char buf[GIT_PATH_MAX];
+
+	assert(path && path_out);
+
+	/* construct path if needed */
+	if (base != NULL && git_path_root(path) < 0) {
+		if (git_buf_joinpath(path_out, base, path) < 0)
+			return -1;
+		path = path_out->ptr;
+	}
+
+	if (p_realpath(path, buf) == NULL) {
+		/* giterr_set resets the errno when dealing with a GITERR_OS kind of error */
+		int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
+		giterr_set(GITERR_OS, "Failed to resolve path '%s'", path);
+
+		git_buf_clear(path_out);
+		
+		return error;
+	}
+
+	return git_buf_sets(path_out, buf);
+}
+
+int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base)
+{
+	int error = git_path_prettify(path_out, path, base);
+	return (error < 0) ? error : git_path_to_dir(path_out);
+}
+
+int git_path_to_dir(git_buf *path)
+{
+	if (path->asize > 0 &&
+		git_buf_len(path) > 0 &&
+		path->ptr[git_buf_len(path) - 1] != '/')
+		git_buf_putc(path, '/');
+
+	return git_buf_oom(path) ? -1 : 0;
+}
+
+void git_path_string_to_dir(char* path, size_t size)
+{
+	size_t end = strlen(path);
+
+	if (end && path[end - 1] != '/' && end < size) {
+		path[end] = '/';
+		path[end + 1] = '\0';
+	}
+}
+
+int git__percent_decode(git_buf *decoded_out, const char *input)
+{
+	int len, hi, lo, i;
+	assert(decoded_out && input);
+
+	len = (int)strlen(input);
+	git_buf_clear(decoded_out);
+
+	for(i = 0; i < len; i++)
+	{
+		char c = input[i];
+
+		if (c != '%')
+			goto append;
+
+		if (i >= len - 2)
+			goto append;
+
+		hi = git__fromhex(input[i + 1]);
+		lo = git__fromhex(input[i + 2]);
+
+		if (hi < 0 || lo < 0)
+			goto append;
+
+		c = (char)(hi << 4 | lo);
+		i += 2;
+
+append:
+		if (git_buf_putc(decoded_out, c) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+static int error_invalid_local_file_uri(const char *uri)
+{
+	giterr_set(GITERR_CONFIG, "'%s' is not a valid local file URI", uri);
+	return -1;
+}
+
+int git_path_fromurl(git_buf *local_path_out, const char *file_url)
+{
+	int offset = 0, len;
+
+	assert(local_path_out && file_url);
+
+	if (git__prefixcmp(file_url, "file://") != 0)
+		return error_invalid_local_file_uri(file_url);
+
+	offset += 7;
+	len = (int)strlen(file_url);
+
+	if (offset < len && file_url[offset] == '/')
+		offset++;
+	else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0)
+		offset += 10;
+	else
+		return error_invalid_local_file_uri(file_url);
+
+	if (offset >= len || file_url[offset] == '/')
+		return error_invalid_local_file_uri(file_url);
+
+#ifndef _MSC_VER
+	offset--;	/* A *nix absolute path starts with a forward slash */
+#endif
+
+	git_buf_clear(local_path_out);
+
+	return git__percent_decode(local_path_out, file_url + offset);
+}
+
+int git_path_walk_up(
+	git_buf *path,
+	const char *ceiling,
+	int (*cb)(void *data, git_buf *),
+	void *data)
+{
+	int error = 0;
+	git_buf iter;
+	ssize_t stop = 0, scan;
+	char oldc = '\0';
+
+	assert(path && cb);
+
+	if (ceiling != NULL) {
+		if (git__prefixcmp(path->ptr, ceiling) == 0)
+			stop = (ssize_t)strlen(ceiling);
+		else
+			stop = git_buf_len(path);
+	}
+	scan = git_buf_len(path);
+
+	iter.ptr = path->ptr;
+	iter.size = git_buf_len(path);
+	iter.asize = path->asize;
+
+	while (scan >= stop) {
+		if ((error = cb(data, &iter)) < 0)
+			break;
+		iter.ptr[scan] = oldc;
+		scan = git_buf_rfind_next(&iter, '/');
+		if (scan >= 0) {
+			scan++;
+			oldc = iter.ptr[scan];
+			iter.size = scan;
+			iter.ptr[scan] = '\0';
+		}
+	}
+
+	if (scan >= 0)
+		iter.ptr[scan] = oldc;
+
+	return error;
+}
+
+bool git_path_exists(const char *path)
+{
+	assert(path);
+	return p_access(path, F_OK) == 0;
+}
+
+bool git_path_isdir(const char *path)
+{
+	struct stat st;
+	if (p_stat(path, &st) < 0)
+		return false;
+
+	return S_ISDIR(st.st_mode) != 0;
+}
+
+bool git_path_isfile(const char *path)
+{
+	struct stat st;
+
+	assert(path);
+	if (p_stat(path, &st) < 0)
+		return false;
+
+	return S_ISREG(st.st_mode) != 0;
+}
+
+int git_path_lstat(const char *path, struct stat *st)
+{
+	int err = 0;
+
+	if (p_lstat(path, st) < 0) {
+		err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
+		giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
+	}
+
+	return err;
+}
+
+static bool _check_dir_contents(
+	git_buf *dir,
+	const char *sub,
+	bool (*predicate)(const char *))
+{
+	bool result;
+	size_t dir_size = git_buf_len(dir);
+	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)
+		return false;
+
+	/* save excursion */
+	git_buf_joinpath(dir, dir->ptr, sub);
+
+	result = predicate(dir->ptr);
+
+	/* restore path */
+	git_buf_truncate(dir, dir_size);
+	return result;
+}
+
+bool git_path_contains(git_buf *dir, const char *item)
+{
+	return _check_dir_contents(dir, item, &git_path_exists);
+}
+
+bool git_path_contains_dir(git_buf *base, const char *subdir)
+{
+	return _check_dir_contents(base, subdir, &git_path_isdir);
+}
+
+bool git_path_contains_file(git_buf *base, const char *file)
+{
+	return _check_dir_contents(base, file, &git_path_isfile);
+}
+
+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);
+
+	if (!error) {
+		char buf[GIT_PATH_MAX];
+		if (p_realpath(dir->ptr, buf) != NULL)
+			error = git_buf_sets(dir, buf);
+	}
+
+	/* call dirname if this is not a directory */
+	if (!error && git_path_isdir(dir->ptr) == false)
+		error = git_path_dirname_r(dir, dir->ptr);
+
+	if (!error)
+		error = git_path_to_dir(dir);
+
+	return error;
+}
+
+int git_path_cmp(
+	const char *name1, size_t len1, int isdir1,
+	const char *name2, size_t len2, int isdir2)
+{
+	size_t len = len1 < len2 ? len1 : len2;
+	int cmp;
+
+	cmp = memcmp(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')));
+}
+
+int git_path_direach(
+	git_buf *path,
+	int (*fn)(void *, git_buf *),
+	void *arg)
+{
+	ssize_t wd_len;
+	DIR *dir;
+	struct dirent *de, *de_buf;
+
+	if (git_path_to_dir(path) < 0)
+		return -1;
+
+	wd_len = git_buf_len(path);
+
+	if ((dir = opendir(path->ptr)) == NULL) {
+		giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr);
+		return -1;
+	}
+
+#ifdef __sun
+	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+	de_buf = git__malloc(sizeof(struct dirent));
+#endif
+
+	while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
+		int result;
+
+		if (is_dot_or_dotdot(de->d_name))
+			continue;
+
+		if (git_buf_puts(path, de->d_name) < 0) {
+			closedir(dir);
+			git__free(de_buf);
+			return -1;
+		}
+
+		result = fn(arg, path);
+
+		git_buf_truncate(path, wd_len); /* restore path */
+
+		if (result < 0) {
+			closedir(dir);
+			git__free(de_buf);
+			return -1;
+		}
+	}
+
+	closedir(dir);
+	git__free(de_buf);
+	return 0;
+}
+
+int git_path_dirload(
+	const char *path,
+	size_t prefix_len,
+	size_t alloc_extra,
+	git_vector *contents)
+{
+	int error, need_slash;
+	DIR *dir;
+	struct dirent *de, *de_buf;
+	size_t path_len;
+
+	assert(path != NULL && contents != NULL);
+	path_len = strlen(path);
+	assert(path_len > 0 && path_len >= prefix_len);
+
+	if ((dir = opendir(path)) == NULL) {
+		giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
+		return -1;
+	}
+
+#ifdef __sun
+	de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
+#else
+	de_buf = git__malloc(sizeof(struct dirent));
+#endif
+
+	path += prefix_len;
+	path_len -= prefix_len;
+	need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
+
+	while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
+		char *entry_path;
+		size_t entry_len;
+
+		if (is_dot_or_dotdot(de->d_name))
+			continue;
+
+		entry_len = strlen(de->d_name);
+
+		entry_path = git__malloc(
+			path_len + need_slash + entry_len + 1 + alloc_extra);
+		GITERR_CHECK_ALLOC(entry_path);
+
+		if (path_len)
+			memcpy(entry_path, path, path_len);
+		if (need_slash)
+			entry_path[path_len] = '/';
+		memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
+		entry_path[path_len + need_slash + entry_len] = '\0';
+
+		if (git_vector_insert(contents, entry_path) < 0) {
+			closedir(dir);
+			git__free(de_buf);
+			return -1;
+		}
+	}
+
+	closedir(dir);
+	git__free(de_buf);
+
+	if (error != 0)
+		giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
+
+	return error;
+}
+
+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);
+}
+
+int git_path_dirload_with_stat(
+	const char *path,
+	size_t prefix_len,
+	git_vector *contents)
+{
+	int error;
+	unsigned int i;
+	git_path_with_stat *ps;
+	git_buf full = GIT_BUF_INIT;
+
+	if (git_buf_set(&full, path, prefix_len) < 0)
+		return -1;
+
+	error = git_path_dirload(
+		path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
+	if (error < 0) {
+		git_buf_free(&full);
+		return error;
+	}
+
+	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;
+
+		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';
+		}
+	}
+
+	git_buf_free(&full);
+
+	return error;
+}
diff --git a/libgit2/src/path.h b/libgit2/src/path.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/path.h
@@ -0,0 +1,278 @@
+/*
+ * 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_path_h__
+#define INCLUDE_path_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "vector.h"
+
+/**
+ * Path manipulation utils
+ *
+ * These are path utilities that munge paths without actually
+ * looking at the real filesystem.
+ */
+
+/*
+ * The dirname() function shall take a pointer to a character string
+ * that contains a pathname, and return a pointer to a string that is a
+ * pathname of the parent directory of that file. Trailing '/' characters
+ * in the path are not counted as part of the path.
+ *
+ * If path does not contain a '/', then dirname() shall return a pointer to
+ * the string ".". If path is a null pointer or points to an empty string,
+ * dirname() shall return a pointer to the string "." .
+ *
+ * The `git_path_dirname` implementation is thread safe. The returned
+ * string must be manually free'd.
+ *
+ * The `git_path_dirname_r` implementation writes the dirname to a `git_buf`
+ * if the buffer pointer is not NULL.
+ * It returns an error code < 0 if there is an allocation error, otherwise
+ * the length of the dirname (which will be > 0).
+ */
+extern char *git_path_dirname(const char *path);
+extern int git_path_dirname_r(git_buf *buffer, const char *path);
+
+/*
+ * This function returns the basename of the file, which is the last
+ * part of its full name given by fname, with the drive letter and
+ * leading directories stripped off. For example, the basename of
+ * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo.
+ *
+ * Trailing slashes and backslashes are significant: the basename of
+ * c:/foo/bar/ is an empty string after the rightmost slash.
+ *
+ * The `git_path_basename` implementation is thread safe. The returned
+ * string must be manually free'd.
+ *
+ * The `git_path_basename_r` implementation writes the basename to a `git_buf`.
+ * It returns an error code < 0 if there is an allocation error, otherwise
+ * the length of the basename (which will be >= 0).
+ */
+extern char *git_path_basename(const char *path);
+extern int git_path_basename_r(git_buf *buffer, const char *path);
+
+extern const char *git_path_topdir(const char *path);
+
+/**
+ * Find offset to root of path if path has one.
+ *
+ * This will return a number >= 0 which is the offset to the start of the
+ * path, if the path is rooted (i.e. "/rooted/path" returns 0 and
+ * "c:/windows/rooted/path" returns 2).  If the path is not rooted, this
+ * returns < 0.
+ */
+extern int git_path_root(const char *path);
+
+/**
+ * Ensure path has a trailing '/'.
+ */
+extern int git_path_to_dir(git_buf *path);
+
+/**
+ * Ensure string has a trailing '/' if there is space for it.
+ */
+extern void git_path_string_to_dir(char* path, size_t size);
+
+#ifdef GIT_WIN32
+/**
+ * Convert backslashes in path to forward slashes.
+ */
+GIT_INLINE(void) git_path_mkposix(char *path)
+{
+	while (*path) {
+		if (*path == '\\')
+			*path = '/';
+
+		path++;
+	}
+}
+#else
+#	define git_path_mkposix(p) /* blank */
+#endif
+
+extern int git__percent_decode(git_buf *decoded_out, const char *input);
+
+/**
+ * Extract path from file:// URL.
+ */
+extern int git_path_fromurl(git_buf *local_path_out, const char *file_url);
+
+
+/**
+ * Path filesystem utils
+ *
+ * These are path utilities that actually access the filesystem.
+ */
+
+/**
+ * Check if a file exists and can be accessed.
+ * @return true or false
+ */
+extern bool git_path_exists(const char *path);
+
+/**
+ * Check if the given path points to a directory.
+ * @return true or false
+ */
+extern bool git_path_isdir(const char *path);
+
+/**
+ * Check if the given path points to a regular file.
+ * @return true or false
+ */
+extern bool git_path_isfile(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);
+
+/**
+ * Check if the parent directory contains the item.
+ *
+ * @param dir Directory to check.
+ * @param item Item that might be in the directory.
+ * @return 0 if item exists in directory, <0 otherwise.
+ */
+extern bool git_path_contains(git_buf *dir, const char *item);
+
+/**
+ * Check if the given path contains the given subdirectory.
+ *
+ * @param parent Directory path that might contain subdir
+ * @param subdir Subdirectory name to look for in parent
+ * @param append_if_exists If true, then subdir will be appended to the parent path if it does exist
+ * @return true if subdirectory exists, false otherwise.
+ */
+extern bool git_path_contains_dir(git_buf *parent, const char *subdir);
+
+/**
+ * Check if the given path contains the given file.
+ *
+ * @param dir Directory path that might contain file
+ * @param file File name to look for in parent
+ * @param append_if_exists If true, then file will be appended to the path if it does exist
+ * @return true if file exists, false otherwise.
+ */
+extern bool git_path_contains_file(git_buf *dir, const char *file);
+
+/**
+ * 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);
+
+/**
+ * Clean up path, prepending base if it is not already rooted and
+ * appending a slash.
+ */
+extern int git_path_prettify_dir(git_buf *path_out, const char *path, const char *base);
+
+/**
+ * Get a directory from a path.
+ *
+ * If path is a directory, this acts like `git_path_prettify_dir`
+ * (cleaning up path and appending a '/').  If path is a normal file,
+ * this prettifies it, then removed the filename a la dirname and
+ * appends the trailing '/'.  If the path does not exist, it is
+ * treated like a regular filename.
+ */
+extern int git_path_find_dir(git_buf *dir, const char *path, const char *base);
+
+/**
+ * Walk each directory entry, except '.' and '..', calling fn(state).
+ *
+ * @param pathbuf buffer the function reads the initial directory
+ * 		path from, and updates with each successive entry's name.
+ * @param fn function to invoke with each entry. The first arg is
+ *		the input state and the second arg is pathbuf. The function
+ *		may modify the pathbuf, but only by appending new text.
+ * @param state to pass to fn as the first arg.
+ */
+extern int git_path_direach(
+	git_buf *pathbuf,
+	int (*fn)(void *, git_buf *),
+	void *state);
+
+/**
+ * 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);
+
+/**
+ * Invoke callback up path directory by directory until the ceiling is
+ * reached (inclusive of a final call at the root_path).
+ *
+ * Returning anything other than 0 from the callback function
+ * will stop the iteration and propogate the error to the caller.
+ *
+ * @param pathbuf Buffer the function reads the directory from and
+ *		and updates with each successive name.
+ * @param ceiling Prefix of path at which to stop walking up.  If NULL,
+ *      this will walk all the way up to the root.  If not a prefix of
+ *      pathbuf, the callback will be invoked a single time on the
+ *      original input path.
+ * @param fn Function to invoke on each path.  The first arg is the
+ *		input satte and the second arg is the pathbuf.  The function
+ *		should not modify the pathbuf.
+ * @param state Passed to fn as the first ath.
+ */
+extern int git_path_walk_up(
+	git_buf *pathbuf,
+	const char *ceiling,
+	int (*fn)(void *state, git_buf *),
+	void *state);
+
+/**
+ * Load all directory entries (except '.' and '..') into a vector.
+ *
+ * For cases where `git_path_direach()` is not appropriate, this
+ * allows you to load the filenames in a directory into a vector
+ * of strings. That vector can then be sorted, iterated, or whatever.
+ * Remember to free alloc of the allocated strings when you are done.
+ *
+ * @param path The directory to read from.
+ * @param prefix_len When inserting entries, the trailing part of path
+ * 		will be prefixed after this length.  I.e. given path "/a/b" and
+ * 		prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
+ * @param alloc_extra Extra bytes to add to each string allocation in
+ * 		case you want to append anything funny.
+ * @param contents Vector to fill with directory entry names.
+ */
+extern int git_path_dirload(
+	const char *path,
+	size_t prefix_len,
+	size_t alloc_extra,
+	git_vector *contents);
+
+
+typedef struct {
+	struct stat st;
+	size_t      path_len;
+	char        path[GIT_FLEX_ARRAY];
+} git_path_with_stat;
+
+extern int git_path_with_stat_cmp(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.
+ */
+extern int git_path_dirload_with_stat(
+	const char *path,
+	size_t prefix_len,
+	git_vector *contents);
+
+#endif
diff --git a/libgit2/src/pkt.c b/libgit2/src/pkt.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pkt.c
@@ -0,0 +1,361 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pkt.h
@@ -0,0 +1,81 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pool.c
@@ -0,0 +1,294 @@
+#include "pool.h"
+#ifndef GIT_WIN32
+#include <unistd.h>
+#endif
+
+struct git_pool_page {
+	git_pool_page *next;
+	uint32_t size;
+	uint32_t avail;
+	char data[GIT_FLEX_ARRAY];
+};
+
+#define GIT_POOL_MIN_USABLE	4
+#define GIT_POOL_MIN_PAGESZ	2 * sizeof(void*)
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size);
+static void pool_insert_page(git_pool *pool, git_pool_page *page);
+
+int git_pool_init(
+	git_pool *pool, uint32_t item_size, uint32_t items_per_page)
+{
+	assert(pool);
+
+	if (!item_size)
+		item_size = 1;
+	/* round up item_size for decent object alignment */
+	if (item_size > 4)
+		item_size = (item_size + 7) & ~7;
+	else if (item_size == 3)
+		item_size = 4;
+
+	if (!items_per_page)
+		items_per_page = git_pool__suggest_items_per_page(item_size);
+	if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
+		items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;
+
+	memset(pool, 0, sizeof(git_pool));
+	pool->item_size = item_size;
+	pool->page_size = item_size * items_per_page;
+
+	return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+	git_pool_page *scan, *next;
+
+	for (scan = pool->open; scan != NULL; scan = next) {
+		next = scan->next;
+		git__free(scan);
+	}
+	pool->open = NULL;
+
+	for (scan = pool->full; scan != NULL; scan = next) {
+		next = scan->next;
+		git__free(scan);
+	}
+	pool->full = NULL;
+
+	pool->free_list = NULL;
+
+	pool->items = 0;
+
+	pool->has_string_alloc     = 0;
+	pool->has_multi_item_alloc = 0;
+	pool->has_large_page_alloc = 0;
+}
+
+void git_pool_swap(git_pool *a, git_pool *b)
+{
+	git_pool temp;
+
+	if (a == b)
+		return;
+
+	memcpy(&temp, a, sizeof(temp));
+	memcpy(a, b, sizeof(temp));
+	memcpy(b, &temp, sizeof(temp));
+}
+
+static void pool_insert_page(git_pool *pool, git_pool_page *page)
+{
+	git_pool_page *scan;
+
+	/* If there are no open pages or this page has the most open space,
+	 * insert it at the beginning of the list.  This is the common case.
+	 */
+	if (pool->open == NULL || pool->open->avail < page->avail) {
+		page->next = pool->open;
+		pool->open = page;
+		return;
+	}
+
+	/* Otherwise insert into sorted position. */
+	for (scan = pool->open;
+		 scan->next && scan->next->avail > page->avail;
+		 scan = scan->next);
+	page->next = scan->next;
+	scan->next = page;
+}
+
+static void *pool_alloc_page(git_pool *pool, uint32_t size)
+{
+	git_pool_page *page;
+	uint32_t alloc_size;
+
+	if (size <= pool->page_size)
+		alloc_size = pool->page_size;
+	else {
+		alloc_size = size;
+		pool->has_large_page_alloc = 1;
+	}
+
+	page = git__calloc(1, alloc_size + sizeof(git_pool_page));
+	if (!page)
+		return NULL;
+
+	page->size  = alloc_size;
+	page->avail = alloc_size - size;
+
+	if (page->avail > 0)
+		pool_insert_page(pool, page);
+	else {
+		page->next = pool->full;
+		pool->full = page;
+	}
+
+	pool->items++;
+
+	return page->data;
+}
+
+GIT_INLINE(void) pool_remove_page(
+	git_pool *pool, git_pool_page *page, git_pool_page *prev)
+{
+	if (prev == NULL)
+		pool->open = page->next;
+	else
+		prev->next = page->next;
+}
+
+void *git_pool_malloc(git_pool *pool, uint32_t items)
+{
+	git_pool_page *scan = pool->open, *prev;
+	uint32_t size = items * pool->item_size;
+	void *ptr = NULL;
+
+	pool->has_string_alloc = 0;
+	if (items > 1)
+		pool->has_multi_item_alloc = 1;
+	else if (pool->free_list != NULL) {
+		ptr = pool->free_list;
+		pool->free_list = *((void **)pool->free_list);
+		return ptr;
+	}
+
+	/* just add a block if there is no open one to accomodate this */
+	if (size >= pool->page_size || !scan || scan->avail < size)
+		return pool_alloc_page(pool, size);
+
+	pool->items++;
+
+	/* find smallest block in free list with space */
+	for (scan = pool->open, prev = NULL;
+		 scan->next && scan->next->avail >= size;
+		 prev = scan, scan = scan->next);
+
+	/* allocate space from the block */
+	ptr = &scan->data[scan->size - scan->avail];
+	scan->avail -= size;
+
+	/* move to full list if there is almost no space left */
+	if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
+		pool_remove_page(pool, scan, prev);
+		scan->next = pool->full;
+		pool->full = scan;
+	}
+	/* reorder list if block is now smaller than the one after it */
+	else if (scan->next != NULL && scan->next->avail > scan->avail) {
+		pool_remove_page(pool, scan, prev);
+		pool_insert_page(pool, scan);
+	}
+
+	return ptr;
+}
+
+char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
+{
+	void *ptr = NULL;
+
+	assert(pool && str && pool->item_size == sizeof(char));
+
+	if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) {
+		memcpy(ptr, str, n);
+		*(((char *)ptr) + n) = '\0';
+	}
+	pool->has_string_alloc = 1;
+
+	return ptr;
+}
+
+char *git_pool_strdup(git_pool *pool, const char *str)
+{
+	assert(pool && str && pool->item_size == sizeof(char));
+
+	return git_pool_strndup(pool, str, strlen(str));
+}
+
+char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
+{
+	void *ptr;
+	size_t len_a, len_b;
+
+	assert(pool && a && b && pool->item_size == sizeof(char));
+
+	len_a = a ? strlen(a) : 0;
+	len_b = b ? strlen(b) : 0;
+
+	if ((ptr = git_pool_malloc(pool, (uint32_t)(len_a + len_b + 1))) != NULL) {
+		if (len_a)
+			memcpy(ptr, a, len_a);
+		if (len_b)
+			memcpy(((char *)ptr) + len_a, b, len_b);
+		*(((char *)ptr) + len_a + len_b) = '\0';
+	}
+	pool->has_string_alloc = 1;
+
+	return ptr;
+}
+
+void git_pool_free(git_pool *pool, void *ptr)
+{
+	assert(pool && ptr && pool->item_size >= sizeof(void*));
+
+	*((void **)ptr) = pool->free_list;
+	pool->free_list = ptr;
+}
+
+uint32_t git_pool__open_pages(git_pool *pool)
+{
+	uint32_t ct = 0;
+	git_pool_page *scan;
+	for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
+	return ct;
+}
+
+uint32_t git_pool__full_pages(git_pool *pool)
+{
+	uint32_t ct = 0;
+	git_pool_page *scan;
+	for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
+	return ct;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+	git_pool_page *scan;
+	for (scan = pool->open; scan != NULL; scan = scan->next)
+		if ((void *)scan->data <= ptr &&
+			(void *)(((char *)scan->data) + scan->size) > ptr)
+			return true;
+	for (scan = pool->full; scan != NULL; scan = scan->next)
+		if ((void *)scan->data <= ptr &&
+			(void *)(((char *)scan->data) + scan->size) > ptr)
+			return true;
+	return false;
+}
+
+uint32_t git_pool__system_page_size(void)
+{
+	static uint32_t size = 0;
+
+	if (!size) {
+#ifdef GIT_WIN32
+		SYSTEM_INFO info;
+		GetSystemInfo(&info);
+		size = (uint32_t)info.dwPageSize;
+#else
+		size = (uint32_t)sysconf(_SC_PAGE_SIZE);
+#endif
+
+		size -= 2 * sizeof(void *); /* allow space for malloc overhead */
+	}
+
+	return size;
+}
+
+uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
+{
+	uint32_t page_bytes =
+		git_pool__system_page_size() - sizeof(git_pool_page);
+	return page_bytes / item_size;
+}
+
diff --git a/libgit2/src/pool.h b/libgit2/src/pool.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pool.h
@@ -0,0 +1,125 @@
+/*
+ * 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_pool_h__
+#define INCLUDE_pool_h__
+
+#include "common.h"
+
+typedef struct git_pool_page git_pool_page;
+
+/**
+ * Chunked allocator.
+ *
+ * A `git_pool` can be used when you want to cheaply allocate
+ * multiple items of the same type and are willing to free them
+ * all together with a single call.  The two most common cases
+ * are a set of fixed size items (such as lots of OIDs) or a
+ * bunch of strings.
+ *
+ * Internally, a `git_pool` allocates pages of memory and then
+ * deals out blocks from the trailing unused portion of each page.
+ * The pages guarantee that the number of actual allocations done
+ * will be much smaller than the number of items needed.
+ *
+ * For examples of how to set up a `git_pool` see `git_pool_init`.
+ */
+typedef struct {
+	git_pool_page *open; /* pages with space left */
+	git_pool_page *full; /* pages with no space left */
+	void *free_list;     /* optional: list of freed blocks */
+	uint32_t item_size;  /* size of single alloc unit in bytes */
+	uint32_t page_size;  /* size of page in bytes */
+	uint32_t items;
+	unsigned has_string_alloc : 1; /* was the strdup function used */
+	unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */
+	unsigned has_large_page_alloc : 1; /* are any pages > page_size */
+} git_pool;
+
+#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }
+
+/**
+ * Initialize a pool.
+ *
+ * To allocation strings, use like this:
+ *
+ *     git_pool_init(&string_pool, 1, 0);
+ *     my_string = git_pool_strdup(&string_pool, your_string);
+ *
+ * To allocate items of fixed size, use like this:
+ *
+ *     git_pool_init(&pool, sizeof(item), 0);
+ *     my_item = git_pool_malloc(&pool, 1);
+ *
+ * Of course, you can use this in other ways, but those are the
+ * two most common patterns.
+ */
+extern int git_pool_init(
+	git_pool *pool, uint32_t item_size, uint32_t items_per_page);
+
+/**
+ * Free all items in pool
+ */
+extern void git_pool_clear(git_pool *pool);
+
+/**
+ * Swap two pools with one another
+ */
+extern void git_pool_swap(git_pool *a, git_pool *b);
+
+/**
+ * Allocate space for one or more items from a pool.
+ */
+extern void *git_pool_malloc(git_pool *pool, uint32_t items);
+
+/**
+ * Allocate space and duplicate string data into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
+
+/**
+ * Allocate space and duplicate a string into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup(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)
+ */
+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
+
+/**
+ * Push a block back onto the free list for the pool.
+ *
+ * This is allowed only if the item_size is >= sizeof(void*).
+ *
+ * In some cases, it is helpful to "release" an allocated block
+ * for reuse.  Pools don't support a general purpose free, but
+ * they will keep a simple free blocks linked list provided the
+ * native block size is large enough to hold a void pointer
+ */
+extern void git_pool_free(git_pool *pool, void *ptr);
+
+/*
+ * Misc utilities
+ */
+
+extern uint32_t git_pool__open_pages(git_pool *pool);
+
+extern uint32_t git_pool__full_pages(git_pool *pool);
+
+extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
+
+extern uint32_t git_pool__system_page_size(void);
+
+extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);
+
+#endif
diff --git a/libgit2/src/posix.c b/libgit2/src/posix.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/posix.c
@@ -0,0 +1,116 @@
+/*
+ * 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 "posix.h"
+#include "path.h"
+#include <stdio.h>
+#include <ctype.h>
+
+#ifndef GIT_WIN32
+
+int p_open(const char *path, int flags, ...)
+{
+	mode_t mode = 0;
+
+	if (flags & O_CREAT)
+	{
+		va_list arg_list;
+
+		va_start(arg_list, flags);
+		mode = (mode_t)va_arg(arg_list, int);
+		va_end(arg_list);
+	}
+
+	return open(path, flags | O_BINARY, mode);
+}
+
+int p_creat(const char *path, mode_t mode)
+{
+	return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
+}
+
+int p_getcwd(char *buffer_out, size_t size)
+{
+	char *cwd_buffer;
+
+	assert(buffer_out && size > 0);
+
+	cwd_buffer = getcwd(buffer_out, size);
+
+	if (cwd_buffer == NULL)
+		return -1;
+
+	git_path_mkposix(buffer_out);
+	git_path_string_to_dir(buffer_out, size); /* append trailing slash */
+
+	return 0;
+}
+
+int p_rename(const char *from, const char *to)
+{
+	if (!link(from, to)) {
+		p_unlink(from);
+		return 0;
+	}
+
+	if (!rename(from, to))
+		return 0;
+
+	return -1;
+}
+
+#endif
+
+int p_read(git_file fd, void *buf, size_t cnt)
+{
+	char *b = buf;
+	while (cnt) {
+		ssize_t r;
+#ifdef GIT_WIN32
+		assert((size_t)((unsigned int)cnt) == cnt);
+		r = read(fd, b, (unsigned int)cnt);
+#else
+		r = read(fd, b, cnt);
+#endif
+		if (r < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!r)
+			break;
+		cnt -= r;
+		b += r;
+	}
+	return (int)(b - (char *)buf);
+}
+
+int p_write(git_file fd, const void *buf, size_t cnt)
+{
+	const char *b = buf;
+	while (cnt) {
+		ssize_t r;
+#ifdef GIT_WIN32
+		assert((size_t)((unsigned int)cnt) == cnt);
+		r = write(fd, b, (unsigned int)cnt);
+#else
+		r = write(fd, b, cnt);
+#endif
+		if (r < 0) {
+			if (errno == EINTR || errno == EAGAIN)
+				continue;
+			return -1;
+		}
+		if (!r) {
+			errno = EPIPE;
+			return -1;
+		}
+		cnt -= r;
+		b += r;
+	}
+	return 0;
+}
diff --git a/libgit2/src/posix.h b/libgit2/src/posix.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/posix.h
@@ -0,0 +1,79 @@
+/*
+ * 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_posix_h__
+#define INCLUDE_posix_h__
+
+#include "common.h"
+#include <fcntl.h>
+#include <time.h>
+
+#define S_IFGITLINK 0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+
+#if !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+
+typedef int git_file;
+
+/**
+ * Standard POSIX Methods
+ *
+ * All the methods starting with the `p_` prefix are
+ * 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);
+extern int p_write(git_file fd, const void *buf, size_t cnt);
+
+#define p_fstat(f,b) fstat(f, b)
+#define p_lseek(f,n,w) lseek(f, n, w)
+#define p_close(fd) close(fd)
+#define p_umask(m) umask(m)
+
+extern int p_open(const char *path, int flags, ...);
+extern int p_creat(const char *path, mode_t mode);
+extern int p_getcwd(char *buffer_out, size_t size);
+extern int p_rename(const char *from, const char *to);
+
+#ifndef GIT_WIN32
+
+#define p_stat(p,b) stat(p, b)
+#define p_chdir(p) chdir(p)
+#define p_rmdir(p) rmdir(p)
+#define p_chmod(p,m) chmod(p, m)
+#define p_access(p,m) access(p,m)
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#else
+
+typedef SOCKET GIT_SOCKET;
+
+#endif
+
+/**
+ * Platform-dependent methods
+ */
+#ifdef GIT_WIN32
+#	include "win32/posix.h"
+#else
+#	include "unix/posix.h"
+#endif
+
+#define p_readdir_r(d,e,r) readdir_r(d,e,r)
+
+#endif
diff --git a/libgit2/src/pqueue.c b/libgit2/src/pqueue.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pqueue.c
@@ -0,0 +1,141 @@
+/*
+ * 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 "pqueue.h"
+
+#define left(i)	((i) << 1)
+#define right(i) (((i) << 1) + 1)
+#define parent(i) ((i) >> 1)
+
+int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri)
+{
+	assert(q);
+
+	/* Need to allocate n+1 elements since element 0 isn't used. */
+	q->d = git__malloc((n + 1) * sizeof(void *));
+	GITERR_CHECK_ALLOC(q->d);
+
+	q->size = 1;
+	q->avail = q->step = (n + 1); /* see comment above about n+1 */
+	q->cmppri = cmppri;
+
+	return 0;
+}
+
+
+void git_pqueue_free(git_pqueue *q)
+{
+	git__free(q->d);
+	q->d = NULL;
+}
+
+void git_pqueue_clear(git_pqueue *q)
+{
+	q->size = 1;
+}
+
+size_t git_pqueue_size(git_pqueue *q)
+{
+	/* queue element 0 exists but doesn't count since it isn't used. */
+	return (q->size - 1);
+}
+
+
+static void bubble_up(git_pqueue *q, size_t i)
+{
+	size_t parent_node;
+	void *moving_node = q->d[i];
+
+	for (parent_node = parent(i);
+			((i > 1) && q->cmppri(q->d[parent_node], moving_node));
+			i = parent_node, parent_node = parent(i)) {
+		q->d[i] = q->d[parent_node];
+	}
+
+	q->d[i] = moving_node;
+}
+
+
+static size_t maxchild(git_pqueue *q, size_t i)
+{
+	size_t child_node = left(i);
+
+	if (child_node >= q->size)
+		return 0;
+
+	if ((child_node + 1) < q->size &&
+		q->cmppri(q->d[child_node], q->d[child_node + 1]))
+		child_node++; /* use right child instead of left */
+
+	return child_node;
+}
+
+
+static void percolate_down(git_pqueue *q, size_t i)
+{
+	size_t child_node;
+	void *moving_node = q->d[i];
+
+	while ((child_node = maxchild(q, i)) != 0 &&
+			q->cmppri(moving_node, q->d[child_node])) {
+		q->d[i] = q->d[child_node];
+		i = child_node;
+	}
+
+	q->d[i] = moving_node;
+}
+
+
+int git_pqueue_insert(git_pqueue *q, void *d)
+{
+	void *tmp;
+	size_t i;
+	size_t newsize;
+
+	if (!q) return 1;
+
+	/* allocate more memory if necessary */
+	if (q->size >= q->avail) {
+		newsize = q->size + q->step;
+		tmp = git__realloc(q->d, sizeof(void *) * newsize);
+		GITERR_CHECK_ALLOC(tmp);
+
+		q->d = tmp;
+		q->avail = newsize;
+	}
+
+	/* insert item */
+	i = q->size++;
+	q->d[i] = d;
+	bubble_up(q, i);
+
+	return 0;
+}
+
+
+void *git_pqueue_pop(git_pqueue *q)
+{
+	void *head;
+
+	if (!q || q->size == 1)
+		return NULL;
+
+	head = q->d[1];
+	q->d[1] = q->d[--q->size];
+	percolate_down(q, 1);
+
+	return head;
+}
+
+
+void *git_pqueue_peek(git_pqueue *q)
+{
+	if (!q || q->size == 1)
+		return NULL;
+	return q->d[1];
+}
diff --git a/libgit2/src/pqueue.h b/libgit2/src/pqueue.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/pqueue.h
@@ -0,0 +1,81 @@
+/*
+ * 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_pqueue_h__
+#define INCLUDE_pqueue_h__
+
+/** callback functions to get/set/compare the priority of an element */
+typedef int (*git_pqueue_cmp)(void *a, void *b);
+
+/** the priority queue handle */
+typedef struct {
+	size_t size, avail, step;
+	git_pqueue_cmp cmppri;
+	void **d;
+} git_pqueue;
+
+
+/**
+ * initialize the queue
+ *
+ * @param n the initial estimate of the number of queue items for which memory
+ *			should be preallocated
+ * @param cmppri the callback function to compare two nodes of the queue
+ *
+ * @Return the handle or NULL for insufficent memory
+ */
+int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri);
+
+
+/**
+ * free all memory used by the queue
+ * @param q the queue
+ */
+void git_pqueue_free(git_pqueue *q);
+
+/**
+ * clear all the elements in the queue
+ * @param q the queue
+ */
+void git_pqueue_clear(git_pqueue *q);
+
+/**
+ * return the size of the queue.
+ * @param q the queue
+ */
+size_t git_pqueue_size(git_pqueue *q);
+
+
+/**
+ * insert an item into the queue.
+ * @param q the queue
+ * @param d the item
+ * @return 0 on success
+ */
+int git_pqueue_insert(git_pqueue *q, void *d);
+
+
+/**
+ * pop the highest-ranking item from the queue.
+ * @param p the queue
+ * @param d where to copy the entry to
+ * @return NULL on error, otherwise the entry
+ */
+void *git_pqueue_pop(git_pqueue *q);
+
+
+/**
+ * access highest-ranking item without removing it.
+ * @param q the queue
+ * @param d the entry
+ * @return NULL on error, otherwise the entry
+ */
+void *git_pqueue_peek(git_pqueue *q);
+
+#endif /* PQUEUE_H */
+/** @} */
+
diff --git a/libgit2/src/protocol.c b/libgit2/src/protocol.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/protocol.c
@@ -0,0 +1,58 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/protocol.h
@@ -0,0 +1,23 @@
+/*
+ * 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/reflog.c b/libgit2/src/reflog.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/reflog.c
@@ -0,0 +1,340 @@
+/*
+ * 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 "reflog.h"
+#include "repository.h"
+#include "filebuf.h"
+#include "signature.h"
+
+static int reflog_init(git_reflog **reflog, git_reference *ref)
+{
+	git_reflog *log;
+
+	*reflog = NULL;
+
+	log = git__calloc(1, sizeof(git_reflog));
+	GITERR_CHECK_ALLOC(log);
+
+	log->ref_name = git__strdup(ref->name);
+	GITERR_CHECK_ALLOC(log->ref_name);
+
+	if (git_vector_init(&log->entries, 0, NULL) < 0) {
+		git__free(log->ref_name);
+		git__free(log);
+		return -1;
+	}
+
+	*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)
+{
+	int error;
+	git_buf log = GIT_BUF_INIT;
+	git_filebuf fbuf = GIT_FILEBUF_INIT;
+	bool trailing_newline = false;
+
+	assert(log_path && oid_old && oid_new && committer);
+
+	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_puts(&log, oid_old);
+	git_buf_putc(&log, ' ');
+
+	git_buf_puts(&log, oid_new);
+
+	git_signature__writebuf(&log, " ", committer);
+	git_buf_truncate(&log, log.size - 1); /* drop LF */
+
+	if (msg) {
+		git_buf_putc(&log, '\t');
+		git_buf_puts(&log, msg);
+	}
+
+	if (!trailing_newline)
+		git_buf_putc(&log, '\n');
+
+	if (git_buf_oom(&log)) {
+		git_buf_free(&log);
+		return -1;
+	}
+
+	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);
+	}
+
+	git_buf_free(&log);
+
+	return error;
+}
+
+static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
+{
+	const char *ptr;
+	git_reflog_entry *entry;
+
+#define seek_forward(_increase) do { \
+	if (_increase >= buf_size) { \
+		giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
+		goto fail; \
+	} \
+	buf += _increase; \
+	buf_size -= _increase; \
+	} while (0)
+
+	while (buf_size > GIT_REFLOG_SIZE_MIN) {
+		entry = git__malloc(sizeof(git_reflog_entry));
+		GITERR_CHECK_ALLOC(entry);
+
+		entry->committer = git__malloc(sizeof(git_signature));
+		GITERR_CHECK_ALLOC(entry->committer);
+
+		if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
+			goto fail;
+		seek_forward(GIT_OID_HEXSZ + 1);
+
+		if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
+			goto fail;
+		seek_forward(GIT_OID_HEXSZ + 1);
+
+		ptr = buf;
+
+		/* Seek forward to the end of the signature. */
+		while (*buf && *buf != '\t' && *buf != '\n')
+			seek_forward(1);
+
+		if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
+			goto fail;
+
+		if (*buf == '\t') {
+			/* We got a message. Read everything till we reach LF. */
+			seek_forward(1);
+			ptr = buf;
+
+			while (*buf && *buf != '\n')
+				seek_forward(1);
+
+			entry->msg = git__strndup(ptr, buf - ptr);
+			GITERR_CHECK_ALLOC(entry->msg);
+		} else
+			entry->msg = NULL;
+
+		while (*buf && *buf == '\n' && buf_size > 1)
+			seek_forward(1);
+
+		if (git_vector_insert(&log->entries, entry) < 0)
+			goto fail;
+	}
+
+	return 0;
+
+#undef seek_forward
+
+fail:
+	if (entry) {
+		git__free(entry->committer);
+		git__free(entry);
+	}
+	return -1;
+}
+
+void git_reflog_free(git_reflog *reflog)
+{
+	unsigned int i;
+	git_reflog_entry *entry;
+
+	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);
+	}
+
+	git_vector_free(&reflog->entries);
+	git__free(reflog->ref_name);
+	git__free(reflog);
+}
+
+int git_reflog_read(git_reflog **reflog, git_reference *ref)
+{
+	int error;
+	git_buf log_path = GIT_BUF_INIT;
+	git_buf log_file = GIT_BUF_INIT;
+	git_reflog *log = NULL;
+
+	*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 (!error)
+		error = git_futils_readbuffer(&log_file, log_path.ptr);
+
+	if (!error)
+		error = reflog_parse(log, log_file.ptr, log_file.size);
+
+	if (!error)
+		*reflog = log;
+	else
+		git_reflog_free(log);
+
+	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 error;
+	char old[GIT_OID_HEXSZ+1];
+	char new[GIT_OID_HEXSZ+1];
+	git_buf log_path = GIT_BUF_INIT;
+	git_reference *r;
+	const git_oid *oid;
+
+	if ((error = git_reference_resolve(&r, ref)) < 0)
+		return error;
+
+	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);
+		return -1;
+	}
+
+	git_oid_tostr(new, GIT_OID_HEXSZ+1, oid);
+
+	git_reference_free(r);
+
+	error = git_buf_join_n(&log_path, '/', 3,
+		ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+	if (error < 0)
+		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 (error < 0)
+		goto cleanup;
+
+	if (oid_old)
+		git_oid_tostr(old, sizeof(old), oid_old);
+	else
+		p_snprintf(old, sizeof(old), "%0*d", GIT_OID_HEXSZ, 0);
+
+	error = reflog_write(log_path.ptr, old, new, committer, msg);
+
+cleanup:
+	git_buf_free(&log_path);
+	return error;
+}
+
+int git_reflog_rename(git_reference *ref, const char *new_name)
+{
+	int error;
+	git_buf old_path = GIT_BUF_INIT;
+	git_buf new_path = 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
+		error = -1;
+
+	git_buf_free(&old_path);
+	git_buf_free(&new_path);
+
+	return error;
+}
+
+int git_reflog_delete(git_reference *ref)
+{
+	int error;
+	git_buf path = GIT_BUF_INIT;
+
+	error = git_buf_join_n(
+		&path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name);
+
+	if (!error && git_path_exists(path.ptr))
+		error = p_unlink(path.ptr);
+
+	git_buf_free(&path);
+
+	return error;
+}
+
+unsigned int 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)
+{
+	assert(reflog);
+	return git_vector_get(&reflog->entries, idx);
+}
+
+const git_oid * git_reflog_entry_oidold(const git_reflog_entry *entry)
+{
+	assert(entry);
+	return &entry->oid_old;
+}
+
+const git_oid * git_reflog_entry_oidnew(const git_reflog_entry *entry)
+{
+	assert(entry);
+	return &entry->oid_cur;
+}
+
+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)
+{
+	assert(entry);
+	return entry->msg;
+}
diff --git a/libgit2/src/reflog.h b/libgit2/src/reflog.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/reflog.h
@@ -0,0 +1,34 @@
+/*
+ * 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_reflog_h__
+#define INCLUDE_reflog_h__
+
+#include "common.h"
+#include "git2/reflog.h"
+#include "vector.h"
+
+#define GIT_REFLOG_DIR "logs/"
+#define GIT_REFLOG_DIR_MODE 0777
+#define GIT_REFLOG_FILE_MODE 0666
+
+#define GIT_REFLOG_SIZE_MIN (2*GIT_OID_HEXSZ+2+17)
+
+struct git_reflog_entry {
+	git_oid oid_old;
+	git_oid oid_cur;
+
+	git_signature *committer;
+
+	char *msg;
+};
+
+struct git_reflog {
+	char *ref_name;
+	git_vector entries;
+};
+
+#endif /* INCLUDE_reflog_h__ */
diff --git a/libgit2/src/refs.c b/libgit2/src/refs.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refs.c
@@ -0,0 +1,1707 @@
+/*
+ * 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);
+}
+
diff --git a/libgit2/src/refs.h b/libgit2/src/refs.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refs.h
@@ -0,0 +1,81 @@
+/*
+ * 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_refs_h__
+#define INCLUDE_refs_h__
+
+#include "common.h"
+#include "git2/oid.h"
+#include "git2/refs.h"
+#include "strmap.h"
+
+#define GIT_REFS_DIR "refs/"
+#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
+#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
+#define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"
+#define GIT_REFS_DIR_MODE 0777
+#define GIT_REFS_FILE_MODE 0666
+
+#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF"
+
+#define GIT_SYMREF "ref: "
+#define GIT_PACKEDREFS_FILE "packed-refs"
+#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled "
+#define GIT_PACKEDREFS_FILE_MODE 0666
+
+#define GIT_HEAD_FILE "HEAD"
+#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
+#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
+#define GIT_REFS_HEADS_MASTER_FILE GIT_REFS_HEADS_DIR "master"
+
+#define GIT_REFNAME_MAX 1024
+
+struct git_reference {
+	unsigned int flags;
+	git_repository *owner;
+	char *name;
+	time_t mtime;
+
+	union {
+		git_oid oid;
+		char *symbolic;
+	} target;
+};
+
+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);
+
+/**
+ * Lookup a reference by name and try to resolve to an OID.
+ *
+ * You can control how many dereferences this will attempt to resolve the
+ * reference with the `max_deref` parameter, or pass -1 to use a sane
+ * default.  If you pass 0 for `max_deref`, this will not attempt to resolve
+ * the reference.  For any value of `max_deref` other than 0, not
+ * successfully resolving the reference will be reported as an error.
+
+ * The generated reference must be freed by the user.
+ *
+ * @param reference_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, ...)
+ * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value
+ * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref
+ */
+int git_reference_lookup_resolved(
+	git_reference **reference_out,
+	git_repository *repo,
+	const char *name,
+	int max_deref);
+
+#endif
diff --git a/libgit2/src/refspec.c b/libgit2/src/refspec.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refspec.c
@@ -0,0 +1,122 @@
+/*
+ * 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/errors.h"
+
+#include "common.h"
+#include "refspec.h"
+#include "util.h"
+#include "posix.h"
+
+int git_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) {
+		refspec->src = git__strdup(str);
+		GITERR_CHECK_ALLOC(refspec->src);
+		return 0;
+	}
+
+	refspec->src = git__strndup(str, delim - str);
+	GITERR_CHECK_ALLOC(refspec->src);
+
+	refspec->dst = git__strdup(delim + 1);
+	if (refspec->dst == NULL) {
+		git__free(refspec->src);
+		refspec->src = NULL;
+		return -1;
+	}
+
+	return 0;
+}
+
+const char *git_refspec_src(const git_refspec *refspec)
+{
+	return refspec == NULL ? NULL : refspec->src;
+}
+
+const char *git_refspec_dst(const git_refspec *refspec)
+{
+	return refspec == NULL ? NULL : refspec->dst;
+}
+
+int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
+{
+	if (refspec == NULL || refspec->src == NULL)
+		return false;
+
+	return (p_fnmatch(refspec->src, refname, 0) == 0);
+}
+
+int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name)
+{
+	size_t baselen, namelen;
+
+	baselen = strlen(spec->dst);
+	if (outlen <= baselen) {
+		giterr_set(GITERR_INVALID, "Reference name too long");
+		return GIT_EBUFS;
+	}
+
+	/*
+	 * 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' */
+		return 0;
+	}
+
+	/* There's a '*' at the end, so remove its length */
+	baselen--;
+
+	/* skip the prefix, -1 is for the '*' */
+	name += strlen(spec->src) - 1;
+
+	namelen = strlen(name);
+
+	if (outlen <= baselen + namelen) {
+		giterr_set(GITERR_INVALID, "Reference name too long");
+		return GIT_EBUFS;
+	}
+
+	memcpy(out, spec->dst, baselen);
+	memcpy(out + baselen, name, namelen + 1);
+
+	return 0;
+}
+
+int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name)
+{
+	if (git_buf_sets(out, spec->dst) < 0)
+		return -1;
+
+	/*
+	 * No '*' at the end means that it's mapped to one specific local
+	 * 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);
+
+	if (git_buf_oom(out))
+		return -1;
+
+	return 0;
+}
+
diff --git a/libgit2/src/refspec.h b/libgit2/src/refspec.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/refspec.h
@@ -0,0 +1,35 @@
+/*
+ * 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_refspec_h__
+#define INCLUDE_refspec_h__
+
+#include "git2/refspec.h"
+#include "buffer.h"
+
+struct git_refspec {
+	struct git_refspec *next;
+	char *src;
+	char *dst;
+	unsigned int force :1,
+		pattern :1,
+		matching :1;
+};
+
+int git_refspec_parse(struct git_refspec *refspec, const char *str);
+
+/**
+ * Transform a reference to its target following the refspec's rules,
+ * and writes the results into a git_buf.
+ *
+ * @param out where to store the target 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_r(git_buf *out, const git_refspec *spec, const char *name);
+
+#endif
diff --git a/libgit2/src/remote.c b/libgit2/src/remote.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/remote.c
@@ -0,0 +1,510 @@
+/*
+ * 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;
+}
diff --git a/libgit2/src/remote.h b/libgit2/src/remote.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/remote.h
@@ -0,0 +1,25 @@
+/*
+ * 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_remote_h__
+#define INCLUDE_remote_h__
+
+#include "refspec.h"
+#include "transport.h"
+#include "repository.h"
+
+struct git_remote {
+	char *name;
+	char *url;
+	git_vector refs;
+	struct git_refspec fetch;
+	struct git_refspec push;
+	git_transport *transport;
+	git_repository *repo;
+	unsigned int need_pack:1;
+};
+
+#endif
diff --git a/libgit2/src/repository.c b/libgit2/src/repository.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/repository.c
@@ -0,0 +1,951 @@
+/*
+ * 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;
+}
+
+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;
+}
diff --git a/libgit2/src/repository.h b/libgit2/src/repository.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/repository.h
@@ -0,0 +1,138 @@
+/*
+ * 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_repository_h__
+#define INCLUDE_repository_h__
+
+#include "git2/common.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+
+#include "index.h"
+#include "cache.h"
+#include "refs.h"
+#include "buffer.h"
+#include "odb.h"
+#include "attr.h"
+#include "strmap.h"
+
+#define DOT_GIT ".git"
+#define GIT_DIR DOT_GIT "/"
+#define GIT_DIR_MODE 0755
+#define GIT_BARE_DIR_MODE 0777
+
+/** Cvar cache identifiers */
+typedef enum {
+	GIT_CVAR_AUTO_CRLF = 0, /* core.autocrlf */
+	GIT_CVAR_EOL, /* core.eol */
+	GIT_CVAR_CACHE_MAX
+} git_cvar_cached;
+
+/**
+ * CVAR value enumerations
+ *
+ * These are the values that are actually stored in the cvar cache, instead
+ * of their string equivalents. These values are internal and symbolic;
+ * make sure that none of them is set to `-1`, since that is the unique
+ * identifier for "not cached"
+ */
+typedef enum {
+	/* The value hasn't been loaded from the cache yet */
+	GIT_CVAR_NOT_CACHED = -1,
+
+	/* core.safecrlf: false, 'fail', 'warn' */
+	GIT_SAFE_CRLF_FALSE = 0,
+	GIT_SAFE_CRLF_FAIL = 1,
+	GIT_SAFE_CRLF_WARN = 2,
+
+	/* core.autocrlf: false, true, 'input; */
+	GIT_AUTO_CRLF_FALSE = 0,
+	GIT_AUTO_CRLF_TRUE = 1,
+	GIT_AUTO_CRLF_INPUT = 2,
+	GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
+
+	/* core.eol: unset, 'crlf', 'lf', 'native' */
+	GIT_EOL_UNSET = 0,
+	GIT_EOL_CRLF = 1,
+	GIT_EOL_LF = 2,
+#ifdef GIT_WIN32
+	GIT_EOL_NATIVE = GIT_EOL_CRLF,
+#else
+	GIT_EOL_NATIVE = GIT_EOL_LF,
+#endif
+	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;
+};
+
+struct git_repository {
+	git_odb *_odb;
+	git_config *_config;
+	git_index *_index;
+
+	git_cache objects;
+	git_refcache references;
+	git_attr_cache attrcache;
+	git_strmap *submodules;
+
+	char *path_repository;
+	char *workdir;
+
+	unsigned is_bare:1;
+	unsigned int lru_counter;
+
+	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;
+}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo);
+
+/*
+ * Weak pointers to repository internals.
+ *
+ * The returned pointers do not need to be freed. Do not keep
+ * permanent references to these (i.e. between API calls), since they may
+ * become invalidated if the user replaces a repository internal.
+ */
+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_index__weakptr(git_index **out, git_repository *repo);
+
+/*
+ * CVAR cache 
+ *
+ * Efficient access to the most used config variables of a repository.
+ * The cache is cleared everytime the config backend is replaced.
+ */
+int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar);
+void git_repository__cvar_cache_clear(git_repository *repo);
+
+/*
+ * Submodule cache
+ */
+extern void git_submodule_config_free(git_repository *repo);
+
+#endif
diff --git a/libgit2/src/revwalk.c b/libgit2/src/revwalk.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/revwalk.c
@@ -0,0 +1,845 @@
+/*
+ * 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 "commit.h"
+#include "odb.h"
+#include "pqueue.h"
+#include "pool.h"
+#include "oidmap.h"
+
+#include "git2/revwalk.h"
+#include "git2/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)
+{
+	commit_object *commit;
+	khiter_t pos;
+	int ret;
+
+	/* lookup and reserve space if not already present */
+	pos = kh_get(oid, walk->commits, oid);
+	if (pos != kh_end(walk->commits))
+		return kh_value(walk->commits, pos);
+
+	commit = alloc_commit(walk);
+	if (commit == NULL)
+		return NULL;
+
+	git_oid_cpy(&commit->oid, oid);
+
+	pos = kh_put(oid, walk->commits, &commit->oid, &ret);
+	assert(ret != 0);
+	kh_value(walk->commits, pos) = commit;
+
+	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)
+{
+	unsigned short i;
+	assert(commit);
+
+	commit->uninteresting = 1;
+
+	/* This means we've reached a merge base, so there's no need to walk any more */
+	if ((commit->flags & (RESULT | STALE)) == RESULT)
+		return;
+
+	for (i = 0; i < commit->out_degree; ++i)
+		if (!commit->parents[i]->uninteresting)
+			mark_uninteresting(commit->parents[i]);
+}
+
+static int process_commit(git_revwalk *walk, commit_object *commit, int hide)
+{
+	int error;
+
+	if (hide)
+		mark_uninteresting(commit);
+
+	if (commit->seen)
+		return 0;
+
+	commit->seen = 1;
+
+	if ((error = commit_parse(walk, commit)) < 0)
+		return error;
+
+	return walk->enqueue(walk, commit);
+}
+
+static int process_commit_parents(git_revwalk *walk, commit_object *commit)
+{
+	unsigned short i;
+	int error = 0;
+
+	for (i = 0; i < commit->out_degree && !error; ++i)
+		error = process_commit(walk, commit->parents[i], commit->uninteresting);
+
+	return error;
+}
+
+static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting)
+{
+	commit_object *commit;
+
+	commit = commit_lookup(walk, oid);
+	if (commit == NULL)
+		return -1; /* error already reported by failed lookup */
+
+	commit->uninteresting = uninteresting;
+	if (walk->one == NULL && !uninteresting) {
+		walk->one = commit;
+	} else {
+		if (git_vector_insert(&walk->twos, commit) < 0)
+			return -1;
+	}
+
+	return 0;
+}
+
+int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
+{
+	assert(walk && oid);
+	return push_commit(walk, oid, 0);
+}
+
+
+int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
+{
+	assert(walk && oid);
+	return push_commit(walk, oid, 1);
+}
+
+static int push_ref(git_revwalk *walk, const char *refname, int hide)
+{
+	git_oid oid;
+
+	if (git_reference_name_to_oid(&oid, walk->repo, refname) < 0)
+		return -1;
+
+	return push_commit(walk, &oid, hide);
+}
+
+struct push_cb_data {
+	git_revwalk *walk;
+	const char *glob;
+	int hide;
+};
+
+static int push_glob_cb(const char *refname, void *data_)
+{
+	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;
+}
+
+static int push_glob(git_revwalk *walk, const char *glob, int hide)
+{
+	git_buf buf = GIT_BUF_INIT;
+	struct push_cb_data data;
+	regex_t preg;
+
+	assert(walk && glob);
+
+	/* refs/ is implied if not given in the glob */
+	if (strncmp(glob, GIT_REFS_DIR, strlen(GIT_REFS_DIR))) {
+		git_buf_printf(&buf, GIT_REFS_DIR "%s", glob);
+	} else {
+		git_buf_puts(&buf, glob);
+	}
+
+	/* If no '?', '*' or '[' exist, we append '/ *' to the glob */
+	memset(&preg, 0x0, sizeof(regex_t));
+	if (regcomp(&preg, "[?*[]", REG_EXTENDED)) {
+		giterr_set(GITERR_OS, "Regex failed to compile");
+		git_buf_free(&buf);
+		return -1;
+	}
+
+	if (regexec(&preg, glob, 0, NULL, 0))
+		git_buf_puts(&buf, "/*");
+
+	if (git_buf_oom(&buf))
+		goto on_error;
+
+	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)
+		goto on_error;
+
+	regfree(&preg);
+	git_buf_free(&buf);
+	return 0;
+
+on_error:
+	regfree(&preg);
+	git_buf_free(&buf);
+	return -1;
+}
+
+int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
+{
+	assert(walk && glob);
+	return push_glob(walk, glob, 0);
+}
+
+int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
+{
+	assert(walk && glob);
+	return push_glob(walk, glob, 1);
+}
+
+int git_revwalk_push_head(git_revwalk *walk)
+{
+	assert(walk);
+	return push_ref(walk, GIT_HEAD_FILE, 0);
+}
+
+int git_revwalk_hide_head(git_revwalk *walk)
+{
+	assert(walk);
+	return push_ref(walk, GIT_HEAD_FILE, 1);
+}
+
+int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
+{
+	assert(walk && refname);
+	return push_ref(walk, refname, 0);
+}
+
+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)
+{
+	return git_pqueue_insert(&walk->iterator_time, commit);
+}
+
+static int revwalk_enqueue_unsorted(git_revwalk *walk, commit_object *commit)
+{
+	return commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
+}
+
+static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
+{
+	int error;
+	commit_object *next;
+
+	while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
+		if ((error = process_commit_parents(walk, next)) < 0)
+			return error;
+
+		if (!next->uninteresting) {
+			*object_out = next;
+			return 0;
+		}
+	}
+
+	return GIT_REVWALKOVER;
+}
+
+static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
+{
+	int error;
+	commit_object *next;
+
+	while ((next = commit_list_pop(&walk->iterator_rand)) != NULL) {
+		if ((error = process_commit_parents(walk, next)) < 0)
+			return error;
+
+		if (!next->uninteresting) {
+			*object_out = next;
+			return 0;
+		}
+	}
+
+	return GIT_REVWALKOVER;
+}
+
+static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
+{
+	commit_object *next;
+	unsigned short i;
+
+	for (;;) {
+		next = commit_list_pop(&walk->iterator_topo);
+		if (next == NULL)
+			return GIT_REVWALKOVER;
+
+		if (next->in_degree > 0) {
+			next->topo_delay = 1;
+			continue;
+		}
+
+		for (i = 0; i < next->out_degree; ++i) {
+			commit_object *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)
+					return -1;
+			}
+		}
+
+		*object_out = next;
+		return 0;
+	}
+}
+
+static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)
+{
+	*object_out = commit_list_pop(&walk->iterator_reverse);
+	return *object_out ? 0 : GIT_REVWALKOVER;
+}
+
+
+static int prepare_walk(git_revwalk *walk)
+{
+	int error;
+	unsigned int i;
+	commit_object *next, *two;
+	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;
+
+	/* first figure out what the merge bases are */
+	if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
+		return -1;
+
+	commit_list_free(&bases);
+	if (process_commit(walk, walk->one, walk->one->uninteresting) < 0)
+		return -1;
+
+	git_vector_foreach(&walk->twos, i, two) {
+		if (process_commit(walk, two, two->uninteresting) < 0)
+			return -1;
+	}
+
+	if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
+		unsigned short i;
+
+		while ((error = walk->get_next(&next, walk)) == 0) {
+			for (i = 0; i < next->out_degree; ++i) {
+				commit_object *parent = next->parents[i];
+				parent->in_degree++;
+			}
+
+			if (commit_list_insert(next, &walk->iterator_topo) == NULL)
+				return -1;
+		}
+
+		if (error != GIT_REVWALKOVER)
+			return error;
+
+		walk->get_next = &revwalk_next_toposort;
+	}
+
+	if (walk->sorting & GIT_SORT_REVERSE) {
+
+		while ((error = walk->get_next(&next, walk)) == 0)
+			if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
+				return -1;
+
+		if (error != GIT_REVWALKOVER)
+			return error;
+
+		walk->get_next = &revwalk_next_reverse;
+	}
+
+	walk->walking = 1;
+	return 0;
+}
+
+
+
+
+
+int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
+{
+	git_revwalk *walk;
+
+	walk = git__malloc(sizeof(git_revwalk));
+	GITERR_CHECK_ALLOC(walk);
+
+	memset(walk, 0x0, sizeof(git_revwalk));
+
+	walk->commits = git_oidmap_alloc();
+	GITERR_CHECK_ALLOC(walk->commits);
+
+	if (git_pqueue_init(&walk->iterator_time, 8, commit_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)
+		return -1;
+
+	walk->get_next = &revwalk_next_unsorted;
+	walk->enqueue = &revwalk_enqueue_unsorted;
+
+	walk->repo = repo;
+
+	if (git_repository_odb(&walk->odb, repo) < 0) {
+		git_revwalk_free(walk);
+		return -1;
+	}
+
+	*revwalk_out = walk;
+	return 0;
+}
+
+void git_revwalk_free(git_revwalk *walk)
+{
+	if (walk == NULL)
+		return;
+
+	git_revwalk_reset(walk);
+	git_odb_free(walk->odb);
+
+	git_oidmap_free(walk->commits);
+	git_pool_clear(&walk->commit_pool);
+	git_pqueue_free(&walk->iterator_time);
+	git_vector_free(&walk->twos);
+	git__free(walk);
+}
+
+git_repository *git_revwalk_repository(git_revwalk *walk)
+{
+	assert(walk);
+	return walk->repo;
+}
+
+void git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
+{
+	assert(walk);
+
+	if (walk->walking)
+		git_revwalk_reset(walk);
+
+	walk->sorting = sort_mode;
+
+	if (walk->sorting & GIT_SORT_TIME) {
+		walk->get_next = &revwalk_next_timesort;
+		walk->enqueue = &revwalk_enqueue_timesort;
+	} else {
+		walk->get_next = &revwalk_next_unsorted;
+		walk->enqueue = &revwalk_enqueue_unsorted;
+	}
+}
+
+int git_revwalk_next(git_oid *oid, git_revwalk *walk)
+{
+	int error;
+	commit_object *next;
+
+	assert(walk && oid);
+
+	if (!walk->walking) {
+		if ((error = prepare_walk(walk)) < 0)
+			return error;
+	}
+
+	error = walk->get_next(&next, walk);
+
+	if (error == GIT_REVWALKOVER) {
+		git_revwalk_reset(walk);
+		return GIT_REVWALKOVER;
+	}
+
+	if (!error)
+		git_oid_cpy(oid, &next->oid);
+
+	return error;
+}
+
+void git_revwalk_reset(git_revwalk *walk)
+{
+	commit_object *commit;
+
+	assert(walk);
+
+	kh_foreach_value(walk->commits, commit, {
+		commit->seen = 0;
+		commit->in_degree = 0;
+		commit->topo_delay = 0;
+		commit->uninteresting = 0;
+		});
+
+	git_pqueue_clear(&walk->iterator_time);
+	commit_list_free(&walk->iterator_topo);
+	commit_list_free(&walk->iterator_rand);
+	commit_list_free(&walk->iterator_reverse);
+	walk->walking = 0;
+
+	walk->one = NULL;
+	git_vector_clear(&walk->twos);
+}
+
diff --git a/libgit2/src/sha1.c b/libgit2/src/sha1.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/sha1.c
@@ -0,0 +1,280 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/sha1.h
@@ -0,0 +1,21 @@
+/*
+ * 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
new file mode 100644
--- /dev/null
+++ b/libgit2/src/sha1_lookup.c
@@ -0,0 +1,178 @@
+/*
+ * 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 <stdio.h>
+
+#include "sha1_lookup.h"
+#include "common.h"
+
+/*
+ * Conventional binary search loop looks like this:
+ *
+ *	unsigned lo, hi;
+ *		do {
+ *				unsigned mi = (lo + hi) / 2;
+ *				int cmp = "entry pointed at by mi" minus "target";
+ *				if (!cmp)
+ *						return (mi is the wanted one)
+ *				if (cmp > 0)
+ *						hi = mi; "mi is larger than target"
+ *				else
+ *						lo = mi+1; "mi is smaller than target"
+ *		} while (lo < hi);
+ *
+ * The invariants are:
+ *
+ * - When entering the loop, lo points at a slot that is never
+ *	above the target (it could be at the target), hi points at a
+ *	slot that is guaranteed to be above the target (it can never
+ *	be at the target).
+ *
+ * - We find a point 'mi' between lo and hi (mi could be the same
+ *	as lo, but never can be as same as hi), and check if it hits
+ *	the target. There are three cases:
+ *
+ *	- if it is a hit, we are happy.
+ *
+ *	- if it is strictly higher than the target, we set it to hi,
+ *		and repeat the search.
+ *
+ *	- if it is strictly lower than the target, we update lo to
+ *		one slot after it, because we allow lo to be at the target.
+ *
+ *	If the loop exits, there is no matching entry.
+ *
+ * When choosing 'mi', we do not have to take the "middle" but
+ * anywhere in between lo and hi, as long as lo <= mi < hi is
+ * satisfied. When we somehow know that the distance between the
+ * target and lo is much shorter than the target and hi, we could
+ * pick mi that is much closer to lo than the midway.
+ *
+ * Now, we can take advantage of the fact that SHA-1 is a good hash
+ * function, and as long as there are enough entries in the table, we
+ * can expect uniform distribution. An entry that begins with for
+ * example "deadbeef..." is much likely to appear much later than in
+ * the midway of the table. It can reasonably be expected to be near
+ * 87% (222/256) from the top of the table.
+ *
+ * However, we do not want to pick "mi" too precisely. If the entry at
+ * the 87% in the above example turns out to be higher than the target
+ * we are looking for, we would end up narrowing the search space down
+ * only by 13%, instead of 50% we would get if we did a simple binary
+ * search. So we would want to hedge our bets by being less aggressive.
+ *
+ * The table at "table" holds at least "nr" entries of "elem_size"
+ * bytes each. Each entry has the SHA-1 key at "key_offset". The
+ * table is sorted by the SHA-1 key of the entries. The caller wants
+ * to find the entry with "key", and knows that the entry at "lo" is
+ * not higher than the entry it is looking for, and that the entry at
+ * "hi" is higher than the entry it is looking for.
+ */
+int sha1_entry_pos(const void *table,
+			size_t elem_size,
+			size_t key_offset,
+			unsigned lo, unsigned hi, unsigned nr,
+			const unsigned char *key)
+{
+	const unsigned char *base = (const unsigned char*)table;
+	const unsigned char *hi_key, *lo_key;
+	unsigned ofs_0;
+
+	if (!nr || lo >= hi)
+		return -1;
+
+	if (nr == hi)
+		hi_key = NULL;
+	else
+		hi_key = base + elem_size * hi + key_offset;
+	lo_key = base + elem_size * lo + key_offset;
+
+	ofs_0 = 0;
+	do {
+		int cmp;
+		unsigned ofs, mi, range;
+		unsigned lov, hiv, kyv;
+		const unsigned char *mi_key;
+
+		range = hi - lo;
+		if (hi_key) {
+			for (ofs = ofs_0; ofs < 20; ofs++)
+				if (lo_key[ofs] != hi_key[ofs])
+					break;
+			ofs_0 = ofs;
+			/*
+			 * byte 0 thru (ofs-1) are the same between
+			 * lo and hi; ofs is the first byte that is
+			 * different.
+			 */
+			hiv = hi_key[ofs_0];
+			if (ofs_0 < 19)
+				hiv = (hiv << 8) | hi_key[ofs_0+1];
+		} else {
+			hiv = 256;
+			if (ofs_0 < 19)
+				hiv <<= 8;
+		}
+		lov = lo_key[ofs_0];
+		kyv = key[ofs_0];
+		if (ofs_0 < 19) {
+			lov = (lov << 8) | lo_key[ofs_0+1];
+			kyv = (kyv << 8) | key[ofs_0+1];
+		}
+		assert(lov < hiv);
+
+		if (kyv < lov)
+			return -1 - lo;
+		if (hiv < kyv)
+			return -1 - hi;
+
+		/*
+		 * Even if we know the target is much closer to 'hi'
+		 * than 'lo', if we pick too precisely and overshoot
+		 * (e.g. when we know 'mi' is closer to 'hi' than to
+		 * 'lo', pick 'mi' that is higher than the target), we
+		 * end up narrowing the search space by a smaller
+		 * amount (i.e. the distance between 'mi' and 'hi')
+		 * than what we would have (i.e. about half of 'lo'
+		 * and 'hi'). Hedge our bets to pick 'mi' less
+		 * aggressively, i.e. make 'mi' a bit closer to the
+		 * middle than we would otherwise pick.
+		 */
+		kyv = (kyv * 6 + lov + hiv) / 8;
+		if (lov < hiv - 1) {
+			if (kyv == lov)
+				kyv++;
+			else if (kyv == hiv)
+				kyv--;
+		}
+		mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
+
+#ifdef INDEX_DEBUG_LOOKUP
+		printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
+		printf("ofs %u lov %x, hiv %x, kyv %x\n",
+				ofs_0, lov, hiv, kyv);
+#endif
+
+		if (!(lo <= mi && mi < hi)) {
+			giterr_set(GITERR_INVALID, "Assertion failure. Binary search invariant is false");
+			return -1;
+		}
+
+		mi_key = base + elem_size * mi + key_offset;
+		cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
+		if (!cmp)
+			return mi;
+		if (cmp > 0) {
+			hi = mi;
+			hi_key = mi_key;
+		} else {
+			lo = mi + 1;
+			lo_key = mi_key + elem_size;
+		}
+	} while (lo < hi);
+	return -((int)lo)-1;
+}
diff --git a/libgit2/src/sha1_lookup.h b/libgit2/src/sha1_lookup.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/sha1_lookup.h
@@ -0,0 +1,18 @@
+/*
+ * 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_sha1_lookup_h__
+#define INCLUDE_sha1_lookup_h__
+
+#include <stdlib.h>
+
+int sha1_entry_pos(const void *table,
+			size_t elem_size,
+			size_t key_offset,
+			unsigned lo, unsigned hi, unsigned nr,
+			const unsigned char *key);
+
+#endif
diff --git a/libgit2/src/signature.c b/libgit2/src/signature.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/signature.c
@@ -0,0 +1,336 @@
+/*
+ * 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 "signature.h"
+#include "repository.h"
+#include "git2/common.h"
+
+void git_signature_free(git_signature *sig)
+{
+	if (sig == NULL)
+		return;
+
+	git__free(sig->name);
+	sig->name = NULL;
+	git__free(sig->email);
+	sig->email = NULL;
+	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)
+{
+	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");
+
+		right = left - 1;
+	}
+
+	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;
+
+	return 0;
+}
+
+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;
+
+	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)
+	{
+		git_signature_free(p);
+		return error;
+	}
+
+	p->when.time = time;
+	p->when.offset = offset;
+
+	*sig_out = p;
+
+	return 0;
+}
+
+git_signature *git_signature_dup(const git_signature *sig)
+{
+	git_signature *new;
+	if (git_signature_new(&new, sig->name, sig->email, sig->when.time, sig->when.offset) < 0)
+		return NULL;
+	return new;
+}
+
+int git_signature_now(git_signature **sig_out, const char *name, const char *email)
+{
+	time_t now;
+	time_t offset;
+	struct tm *utc_tm, *local_tm;
+	git_signature *sig;
+
+#ifndef GIT_WIN32
+	struct tm _utc, _local;
+#endif
+
+	*sig_out = NULL;
+
+	time(&now);
+
+	/**
+	 * On Win32, `gmtime_r` doesn't exist but
+	 * `gmtime` is threadsafe, so we can use that
+	 */
+#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);
+	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;
+
+	*sig_out = sig;
+
+	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;
+
+	memset(sig, 0x0, sizeof(git_signature));
+
+	if ((line_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)
+			return signature_error("expected prefix doesn't match actual");
+
+		buffer += header_len;
+	}
+
+	if (buffer > line_end)
+		return signature_error("signature too short");
+
+	if ((name_end = strchr(buffer, '<')) == NULL)
+		return signature_error("character '<' not allowed in signature");
+
+	if ((email_end = strchr(name_end, '>')) == NULL)
+		return signature_error("character '>' not allowed in signature");
+
+	if (email_end < name_end)
+		return signature_error("malformed e-mail");
+
+	error = process_next_token(&buffer, &sig->name, name_end, line_end);
+	if (error < 0)
+		return error;
+
+	error = process_next_token(&buffer, &sig->email, email_end, line_end);
+	if (error < 0)
+		return error;
+
+	tz_start = scan_for_previous_token(line_end - 1, buffer);
+
+	if (tz_start == NULL)
+		goto clean_exit;	/* No timezone nor date */
+
+	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;
+	}
+
+	if (parse_timezone_offset(tz_start, &sig->when.offset) < 0) {
+		sig->when.time = 0; /* Bogus timezone, we reset the time */
+	}
+
+clean_exit:
+	*buffer_out = line_end + 1;
+	return 0;
+}
+
+void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig)
+{
+	int offset, hours, mins;
+	char sign;
+
+	offset = sig->when.offset;
+	sign = (sig->when.offset < 0) ? '-' : '+';
+
+	if (offset < 0)
+		offset = -offset;
+
+	hours = offset / 60;
+	mins = offset % 60;
+
+	git_buf_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
+			header ? header : "", sig->name, sig->email,
+			(unsigned)sig->when.time, sign, hours, mins);
+}
+
diff --git a/libgit2/src/signature.h b/libgit2/src/signature.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/signature.h
@@ -0,0 +1,18 @@
+/*
+ * 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_signature_h__
+#define INCLUDE_signature_h__
+
+#include "git2/common.h"
+#include "git2/signature.h"
+#include "repository.h"
+#include <time.h>
+
+int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
+void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig);
+
+#endif
diff --git a/libgit2/src/status.c b/libgit2/src/status.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/status.c
@@ -0,0 +1,242 @@
+/*
+ * 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.h"
+#include "fileops.h"
+#include "hash.h"
+#include "vector.h"
+#include "tree.h"
+#include "git2/status.h"
+#include "repository.h"
+#include "ignore.h"
+
+#include "git2/diff.h"
+#include "diff.h"
+
+static unsigned int index_delta2status(git_delta_t index_status)
+{
+	unsigned int st = GIT_STATUS_CURRENT;
+
+	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:
+		st = GIT_STATUS_INDEX_DELETED;
+		break;
+	case GIT_DELTA_MODIFIED:
+		st = GIT_STATUS_INDEX_MODIFIED;
+		break;
+	default:
+		break;
+	}
+
+	return st;
+}
+
+static unsigned int workdir_delta2status(git_delta_t workdir_status)
+{
+	unsigned int st = GIT_STATUS_CURRENT;
+
+	switch (workdir_status) {
+	case GIT_DELTA_ADDED:
+	case GIT_DELTA_COPIED:
+	case GIT_DELTA_RENAMED:
+	case GIT_DELTA_UNTRACKED:
+		st = GIT_STATUS_WT_NEW;
+		break;
+	case GIT_DELTA_DELETED:
+		st = GIT_STATUS_WT_DELETED;
+		break;
+	case GIT_DELTA_MODIFIED:
+		st = GIT_STATUS_WT_MODIFIED;
+		break;
+	case GIT_DELTA_IGNORED:
+		st = GIT_STATUS_IGNORED;
+		break;
+	default:
+		break;
+	}
+
+	return st;
+}
+
+int git_status_foreach_ext(
+	git_repository *repo,
+	const git_status_options *opts,
+	int (*cb)(const char *, unsigned int, void *),
+	void *cbdata)
+{
+	int err = 0, cmp;
+	git_diff_options diffopt;
+	git_diff_list *idx2head = NULL, *wd2idx = 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;
+
+	assert(show <= GIT_STATUS_SHOW_INDEX_THEN_WORKDIR);
+
+	if ((err = git_repository_head_tree(&head, repo)) < 0)
+		return err;
+
+	memset(&diffopt, 0, sizeof(diffopt));
+	memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+
+	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)
+		diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+	if ((opts->flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+		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 (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;
+	}
+
+	i_max = idx2head ? idx2head->deltas.length : 0;
+	j_max = wd2idx   ? wd2idx->deltas.length   : 0;
+
+	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;
+
+		cmp = !w2i ? -1 : !i2h ? 1 : strcmp(i2h->old_file.path, w2i->old_file.path);
+
+		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++;
+		}
+	}
+
+cleanup:
+	git_tree_free(head);
+	git_diff_list_free(idx2head);
+	git_diff_list_free(wd2idx);
+	return err;
+}
+
+int git_status_foreach(
+	git_repository *repo,
+	int (*callback)(const char *, unsigned int, void *),
+	void *payload)
+{
+	git_status_options opts;
+
+	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 |
+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+	return git_status_foreach_ext(repo, &opts, callback, payload);
+}
+
+struct status_file_info {
+	unsigned int count;
+	unsigned int status;
+	char *expected;
+};
+
+static int get_one_status(const char *path, unsigned int status, void *data)
+{
+	struct status_file_info *sfi = data;
+
+	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;
+	}
+
+	return 0;
+}
+
+int git_status_file(
+	unsigned int *status_flags,
+	git_repository *repo,
+	const char *path)
+{
+	int error;
+	git_status_options opts;
+	struct status_file_info sfi;
+
+	assert(status_flags && repo && path);
+
+	memset(&sfi, 0, sizeof(sfi));
+	if ((sfi.expected = git__strdup(path)) == NULL)
+		return -1;
+
+	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 |
+		GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+		GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+	opts.pathspec.count = 1;
+	opts.pathspec.strings = &sfi.expected;
+
+	error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
+
+	if (!error && !sfi.count) {
+		giterr_set(GITERR_INVALID,
+			"Attempt to get status of nonexistent file '%s'", path);
+		error = GIT_ENOTFOUND;
+	}
+
+	*status_flags = sfi.status;
+
+	git__free(sfi.expected);
+
+	return error;
+}
+
+int git_status_should_ignore(
+	int *ignored,
+	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;
+}
+
diff --git a/libgit2/src/strmap.h b/libgit2/src/strmap.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/strmap.h
@@ -0,0 +1,64 @@
+/*
+ * 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_strmap_h__
+#define INCLUDE_strmap_h__
+
+#include "common.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(str, const char *, void *);
+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)
+
+#define git_strmap_alloc()  kh_init(str)
+#define git_strmap_free(h)  kh_destroy(str, h), h = NULL
+#define git_strmap_clear(h) kh_clear(str, h)
+
+#define git_strmap_num_entries(h) kh_size(h)
+
+#define git_strmap_lookup_index(h, k)  kh_get(str, h, k)
+#define git_strmap_valid_index(h, idx) (idx != kh_end(h))
+
+#define git_strmap_exists(h, k) (kh_get(str, h, k) != kh_end(h))
+
+#define git_strmap_value_at(h, idx)        kh_val(h, idx)
+#define git_strmap_set_value_at(h, idx, v) kh_val(h, idx) = v
+#define git_strmap_delete_at(h, idx)       kh_del(str, h, idx)
+
+#define git_strmap_insert(h, key, val, rval) do { \
+	khiter_t __pos = kh_put(str, h, key, &rval); \
+	if (rval >= 0) { \
+		if (rval == 0) kh_key(h, __pos) = key; \
+		kh_val(h, __pos) = val; \
+	} } while (0)
+
+#define git_strmap_insert2(h, key, val, oldv, rval) do { \
+	khiter_t __pos = kh_put(str, h, key, &rval); \
+	if (rval >= 0) { \
+		if (rval == 0) { \
+			oldv = kh_val(h, __pos); \
+			kh_key(h, __pos) = key; \
+		} else { oldv = NULL; } \
+		kh_val(h, __pos) = val; \
+	} } while (0)
+
+#define git_strmap_delete(h, key) do { \
+	khiter_t __pos = git_strmap_lookup_index(h, key); \
+	if (git_strmap_valid_index(h, __pos)) \
+		git_strmap_delete_at(h, __pos); } while (0)
+
+#define git_strmap_foreach		kh_foreach
+#define git_strmap_foreach_value	kh_foreach_value
+
+#endif
diff --git a/libgit2/src/submodule.c b/libgit2/src/submodule.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/submodule.c
@@ -0,0 +1,387 @@
+/*
+ * 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;
+}
diff --git a/libgit2/src/tag.c b/libgit2/src/tag.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tag.c
@@ -0,0 +1,471 @@
+/*
+ * 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 "commit.h"
+#include "tag.h"
+#include "signature.h"
+#include "message.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+#include "git2/signature.h"
+
+void git_tag__free(git_tag *tag)
+{
+	git_signature_free(tag->tagger);
+	git__free(tag->message);
+	git__free(tag->tag_name);
+	git__free(tag);
+}
+
+const git_oid *git_tag_id(git_tag *c)
+{
+	return git_object_id((git_object *)c);
+}
+
+int git_tag_target(git_object **target, 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)
+{
+	assert(t);
+	return &t->target;
+}
+
+git_otype git_tag_type(git_tag *t)
+{
+	assert(t);
+	return t->type;
+}
+
+const char *git_tag_name(git_tag *t)
+{
+	assert(t);
+	return t->tag_name;
+}
+
+const git_signature *git_tag_tagger(git_tag *t)
+{
+	return t->tagger;
+}
+
+const char *git_tag_message(git_tag *t)
+{
+	assert(t);
+	return t->message;
+}
+
+static int tag_error(const char *str)
+{
+	giterr_set(GITERR_TAG, "Failed to parse tag. %s", str);
+	return -1;
+}
+
+int git_tag__parse_buffer(git_tag *tag, const char *buffer, size_t length)
+{
+	static const char *tag_types[] = {
+		NULL, "commit\n", "tree\n", "blob\n", "tag\n"
+	};
+
+	unsigned int i;
+	size_t text_len;
+	char *search;
+
+	const char *buffer_end = buffer + length;
+
+	if (git_oid__parse(&tag->target, &buffer, buffer_end, "object ") < 0)
+		return tag_error("Object field invalid");
+
+	if (buffer + 5 >= buffer_end)
+		return tag_error("Object too short");
+
+	if (memcmp(buffer, "type ", 5) != 0)
+		return tag_error("Type field not found");
+	buffer += 5;
+
+	tag->type = GIT_OBJ_BAD;
+
+	for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
+		size_t type_length = strlen(tag_types[i]);
+
+		if (buffer + type_length >= buffer_end)
+			return tag_error("Object too short");
+
+		if (memcmp(buffer, tag_types[i], type_length) == 0) {
+			tag->type = i;
+			buffer += type_length;
+			break;
+		}
+	}
+
+	if (tag->type == GIT_OBJ_BAD)
+		return tag_error("Invalid object type");
+
+	if (buffer + 4 >= buffer_end)
+		return tag_error("Object too short");
+
+	if (memcmp(buffer, "tag ", 4) != 0)
+		return tag_error("Tag field not found");
+
+	buffer += 4;
+
+	search = memchr(buffer, '\n', buffer_end - buffer);
+	if (search == NULL)
+		return tag_error("Object too short");
+
+	text_len = search - buffer;
+
+	tag->tag_name = git__malloc(text_len + 1);
+	GITERR_CHECK_ALLOC(tag->tag_name);
+
+	memcpy(tag->tag_name, buffer, text_len);
+	tag->tag_name[text_len] = '\0';
+
+	buffer = search + 1;
+
+	tag->tagger = NULL;
+	if (*buffer != '\n') {
+		tag->tagger = git__malloc(sizeof(git_signature));
+		GITERR_CHECK_ALLOC(tag->tagger);
+
+		if (git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n') < 0)
+			return -1;
+	}
+
+	if( *buffer != '\n' )
+		return tag_error("No new line before message");
+
+	text_len = buffer_end - ++buffer;
+
+	tag->message = git__malloc(text_len + 1);
+	GITERR_CHECK_ALLOC(tag->message);
+
+	memcpy(tag->message, buffer, text_len);
+	tag->message[text_len] = '\0';
+
+	return 0;
+}
+
+static int retrieve_tag_reference(
+	git_reference **tag_reference_out,
+	git_buf *ref_name_out,
+	git_repository *repo,
+	const char *tag_name)
+{
+	git_reference *tag_ref;
+	int error;
+
+	*tag_reference_out = NULL;
+
+	if (git_buf_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+		return -1;
+
+	error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
+	if (error < 0)
+		return error; /* Be it not foundo or corrupted */
+
+	*tag_reference_out = tag_ref;
+
+	return 0;
+}
+
+static int retrieve_tag_reference_oid(
+	git_oid *oid,
+	git_buf *ref_name_out,
+	git_repository *repo,
+	const char *tag_name)
+{
+	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);
+}
+
+static int write_tag_annotation(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		const git_signature *tagger,
+		const char *message)
+{
+	git_buf tag = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
+	git_odb *odb;
+
+	git_oid__writebuf(&tag, "object ", git_object_id(target));
+	git_buf_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
+	git_buf_printf(&tag, "tag %s\n", tag_name);
+	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)
+		goto on_error;
+
+	git_buf_free(&cleaned_message);
+
+	if (git_repository_odb__weakptr(&odb, repo) < 0)
+		goto on_error;
+
+	if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJ_TAG) < 0)
+		goto on_error;
+
+	git_buf_free(&tag);
+	return 0;
+
+on_error:
+	git_buf_free(&tag);
+	git_buf_free(&cleaned_message);
+	giterr_set(GITERR_OBJECT, "Failed to create tag annotation.");
+	return -1;
+}
+
+static int git_tag_create__internal(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		const git_signature *tagger,
+		const char *message,
+		int allow_ref_overwrite,
+		int create_tag_annotation)
+{
+	git_reference *new_ref = NULL;
+	git_buf ref_name = GIT_BUF_INIT;
+
+	int error;
+
+	assert(repo && tag_name && target);
+	assert(!create_tag_annotation || (tagger && message));
+
+	if (git_object_owner(target) != repo) {
+		giterr_set(GITERR_INVALID, "The given target does not belong to this repository");
+		return -1;
+	}
+
+	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
+	if (error < 0 && error != GIT_ENOTFOUND)
+		return -1;
+
+	/** Ensure the tag name doesn't conflict with an already existing
+	 *	reference unless overwriting has explictly been requested **/
+	if (error == 0 && !allow_ref_overwrite) {
+		git_buf_free(&ref_name);
+		giterr_set(GITERR_TAG, "Tag already exists");
+		return GIT_EEXISTS;
+	}
+
+	if (create_tag_annotation) {
+		if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0)
+			return -1;
+	} else
+		git_oid_cpy(oid, git_object_id(target));
+
+	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
+
+	git_reference_free(new_ref);
+	git_buf_free(&ref_name);
+	return error;
+}
+
+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 allow_ref_overwrite)
+{
+	return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
+}
+
+int git_tag_create_lightweight(
+		git_oid *oid,
+		git_repository *repo,
+		const char *tag_name,
+		const git_object *target,
+		int allow_ref_overwrite)
+{
+	return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
+}
+
+int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
+{
+	git_tag tag;
+	int error;
+	git_odb *odb;
+	git_odb_stream *stream;
+	git_odb_object *target_obj;
+
+	git_reference *new_ref = NULL;
+	git_buf ref_name = GIT_BUF_INIT;
+
+	assert(oid && buffer);
+
+	memset(&tag, 0, sizeof(tag));
+
+	if (git_repository_odb__weakptr(&odb, repo) < 0)
+		return -1;
+
+	/* validate the buffer */
+	if (git_tag__parse_buffer(&tag, buffer, strlen(buffer)) < 0)
+		return -1;
+
+	/* validate the target */
+	if (git_odb_read(&target_obj, odb, &tag.target) < 0)
+		goto on_error;
+
+	if (tag.type != target_obj->raw.type) {
+		giterr_set(GITERR_TAG, "The type for the given target is invalid");
+		goto on_error;
+	}
+
+	error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
+	if (error < 0 && error != GIT_ENOTFOUND)
+		goto on_error;
+
+	/* We don't need these objects after this */
+	git_signature_free(tag.tagger);
+	git__free(tag.tag_name);
+	git__free(tag.message);
+	git_odb_object_free(target_obj);
+
+	/** Ensure the tag name doesn't conflict with an already existing
+	 *	reference unless overwriting has explictly been requested **/
+	if (error == 0 && !allow_ref_overwrite) {
+		giterr_set(GITERR_TAG, "Tag already exists");
+		return GIT_EEXISTS;
+	}
+
+	/* write the buffer */
+	if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0)
+		return -1;
+
+	stream->write(stream, buffer, strlen(buffer));
+
+	error = stream->finalize_write(oid, stream);
+	stream->free(stream);
+
+	if (error < 0) {
+		git_buf_free(&ref_name);
+		return -1;
+	}
+
+	error = git_reference_create_oid(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite);
+
+	git_reference_free(new_ref);
+	git_buf_free(&ref_name);
+
+	return error;
+
+on_error:
+	git_signature_free(tag.tagger);
+	git__free(tag.tag_name);
+	git__free(tag.message);
+	git_odb_object_free(target_obj);
+	return -1;
+}
+
+int git_tag_delete(git_repository *repo, const char *tag_name)
+{
+	int error;
+	git_reference *tag_ref;
+	git_buf ref_name = GIT_BUF_INIT;
+
+	error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
+
+	git_buf_free(&ref_name);
+
+	if (error < 0)
+		return -1;
+
+	return git_reference_delete(tag_ref);
+}
+
+int git_tag__parse(git_tag *tag, git_odb_object *obj)
+{
+	assert(tag);
+	return git_tag__parse_buffer(tag, obj->raw.data, obj->raw.len);
+}
+
+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)
+{
+	tag_filter_data *filter;
+
+	if (git__prefixcmp(tag_name, GIT_REFS_TAGS_DIR) != 0)
+		return 0;
+
+	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 0;
+}
+
+int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
+{
+	int error;
+	tag_filter_data filter;
+	git_vector taglist;
+
+	assert(tag_names && repo && pattern);
+
+	if (git_vector_init(&taglist, 8, NULL) < 0)
+		return -1;
+
+	filter.taglist = &taglist;
+	filter.pattern = pattern;
+
+	error = git_reference_foreach(repo, GIT_REF_OID|GIT_REF_PACKED, &tag_list_cb, (void *)&filter);
+	if (error < 0) {
+		git_vector_free(&taglist);
+		return -1;
+	}
+
+	tag_names->strings = (char **)taglist.contents;
+	tag_names->count = taglist.length;
+	return 0;
+}
+
+int git_tag_list(git_strarray *tag_names, git_repository *repo)
+{
+	return git_tag_list_match(tag_names, "", repo);
+}
+
+int git_tag_peel(git_object **tag_target, 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;
+}
diff --git a/libgit2/src/tag.h b/libgit2/src/tag.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tag.h
@@ -0,0 +1,29 @@
+/*
+ * 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_tag_h__
+#define INCLUDE_tag_h__
+
+#include "git2/tag.h"
+#include "repository.h"
+#include "odb.h"
+
+struct git_tag {
+	git_object object;
+
+	git_oid target;
+	git_otype type;
+
+	char *tag_name;
+	git_signature *tagger;
+	char *message;
+};
+
+void git_tag__free(git_tag *tag);
+int git_tag__parse(git_tag *tag, git_odb_object *obj);
+int git_tag__parse_buffer(git_tag *tag, const char *data, size_t len);
+
+#endif
diff --git a/libgit2/src/thread-utils.c b/libgit2/src/thread-utils.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/thread-utils.c
@@ -0,0 +1,55 @@
+/*
+ * 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 "thread-utils.h"
+
+#ifdef _WIN32
+#	define WIN32_LEAN_AND_MEAN
+#	include <windows.h>
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+#	include <sys/pstat.h>
+#endif
+
+/*
+ * By doing this in two steps we can at least get
+ * the function to be somewhat coherent, even
+ * with this disgusting nest of #ifdefs.
+ */
+#ifndef _SC_NPROCESSORS_ONLN
+#	ifdef _SC_NPROC_ONLN
+#		define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
+#	elif defined _SC_CRAY_NCPU
+#		define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
+#	endif
+#endif
+
+int git_online_cpus(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+	long ncpus;
+#endif
+
+#ifdef _WIN32
+	SYSTEM_INFO info;
+	GetSystemInfo(&info);
+
+	if ((int)info.dwNumberOfProcessors > 0)
+		return (int)info.dwNumberOfProcessors;
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+	struct pst_dynamic psd;
+
+	if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
+		return (int)psd.psd_proc_cnt;
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+	if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
+		return (int)ncpus;
+#endif
+
+	return 1;
+}
diff --git a/libgit2/src/thread-utils.h b/libgit2/src/thread-utils.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/thread-utils.h
@@ -0,0 +1,108 @@
+/*
+ * 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_thread_utils_h__
+#define INCLUDE_thread_utils_h__
+
+#include "common.h"
+
+/* Common operations even if threading has been disabled */
+typedef struct {
+#if defined(GIT_WIN32)
+	volatile long val;
+#else
+	volatile int val;
+#endif
+} git_atomic;
+
+GIT_INLINE(void) git_atomic_set(git_atomic *a, int val)
+{
+	a->val = val;
+}
+
+#ifdef GIT_THREADS
+
+#define git_thread pthread_t
+#define git_thread_create(thread, attr, start_routine, arg) pthread_create(thread, attr, start_routine, arg)
+#define git_thread_kill(thread) pthread_cancel(thread)
+#define git_thread_exit(status)	pthread_exit(status)
+#define git_thread_join(id, status) pthread_join(id, status)
+
+/* Pthreads Mutex */
+#define git_mutex pthread_mutex_t
+#define git_mutex_init(a)	pthread_mutex_init(a, NULL)
+#define git_mutex_lock(a)	pthread_mutex_lock(a)
+#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)
+
+GIT_INLINE(int) git_atomic_inc(git_atomic *a)
+{
+#if defined(GIT_WIN32)
+	return InterlockedIncrement(&a->val);
+#elif defined(__GNUC__)
+	return __sync_add_and_fetch(&a->val, 1);
+#else
+#	error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(int) git_atomic_dec(git_atomic *a)
+{
+#if defined(GIT_WIN32)
+	return InterlockedDecrement(&a->val);
+#elif defined(__GNUC__)
+	return __sync_sub_and_fetch(&a->val, 1);
+#else
+#	error "Unsupported architecture for atomic operations"
+#endif
+}
+
+#else
+
+#define git_thread unsigned int
+#define git_thread_create(thread, attr, start_routine, arg) (void)0
+#define git_thread_kill(thread) (void)0
+#define git_thread_exit(status) (void)0
+#define git_thread_join(id, status) (void)0
+
+/* Pthreads Mutex */
+#define git_mutex unsigned int
+#define git_mutex_init(a) (void)0
+#define git_mutex_lock(a) (void)0
+#define git_mutex_unlock(a) (void)0
+#define git_mutex_free(a) (void)0
+
+/* Pthreads condition vars */
+#define git_cond unsigned int
+#define git_cond_init(c, a)	(void)0
+#define git_cond_free(c) (void)0
+#define git_cond_wait(c, l)	(void)0
+#define git_cond_signal(c) (void)0
+#define git_cond_broadcast(c) (void)0
+
+GIT_INLINE(int) git_atomic_inc(git_atomic *a)
+{
+	return ++a->val;
+}
+
+GIT_INLINE(int) git_atomic_dec(git_atomic *a)
+{
+	return --a->val;
+}
+
+#endif
+
+extern int git_online_cpus(void);
+
+#endif /* INCLUDE_thread_utils_h__ */
diff --git a/libgit2/src/transport.c b/libgit2/src/transport.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transport.c
@@ -0,0 +1,97 @@
+/*
+ * 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/remote.h"
+#include "git2/net.h"
+#include "transport.h"
+#include "path.h"
+
+static struct {
+	char *prefix;
+	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}
+};
+
+#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
+
+static git_transport_cb transport_find_fn(const char *url)
+{
+	size_t i = 0;
+
+	// 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;
+	}
+
+	/* still here? Check to see if the path points to a file on the local file system */
+	if ((git_path_exists(url) == 0) && git_path_isdir(url))
+		return &git_transport_local;
+
+	/* 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 */
+
+	return NULL;
+}
+
+/**************
+ * Public API *
+ **************/
+
+int git_transport_dummy(git_transport **transport)
+{
+	GIT_UNUSED(transport);
+	giterr_set(GITERR_NET, "This transport isn't implemented. Sorry");
+	return -1;
+}
+
+int git_transport_new(git_transport **out, const char *url)
+{
+	git_transport_cb fn;
+	git_transport *transport;
+	int error;
+
+	fn = transport_find_fn(url);
+
+	if (fn == NULL) {
+		giterr_set(GITERR_NET, "Unsupported URL protocol");
+		return -1;
+	}
+
+	error = fn(&transport);
+	if (error < 0)
+		return error;
+
+	transport->url = git__strdup(url);
+	GITERR_CHECK_ALLOC(transport->url);
+
+	*out = transport;
+
+	return 0;
+}
+
+/* from remote.h */
+int git_remote_valid_url(const char *url)
+{
+	return transport_find_fn(url) != NULL;
+}
+
+int git_remote_supported_url(const char* url)
+{
+	git_transport_cb transport_fn = transport_find_fn(url);
+
+	return ((transport_fn != NULL) && (transport_fn != &git_transport_dummy));
+}
diff --git a/libgit2/src/transport.h b/libgit2/src/transport.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transport.h
@@ -0,0 +1,108 @@
+/*
+ * 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/git.c b/libgit2/src/transports/git.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/git.c
@@ -0,0 +1,478 @@
+/*
+ * 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/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 "netops.h"
+#include "filebuf.h"
+#include "repository.h"
+#include "fetch.h"
+#include "protocol.h"
+
+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;
+
+/*
+ * Create a git procol 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;
+
+	delim = strchr(url, '/');
+	if (delim == NULL) {
+		giterr_set(GITERR_NET, "Malformed URL");
+		return -1;
+	}
+
+	repo = delim;
+
+	delim = strchr(url, ':');
+	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);
+	git_buf_printf(request, "%04x%s %s%c%s",
+		(unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
+	git_buf_put(request, url, delim - url);
+	git_buf_putc(request, '\0');
+
+	if (git_buf_oom(request))
+		return -1;
+
+	return 0;
+}
+
+static int send_request(GIT_SOCKET s, const char *cmd, const char *url)
+{
+	int error;
+	git_buf request = GIT_BUF_INIT;
+
+	error = gen_proto(&request, cmd, url);
+	if (error < 0)
+		goto cleanup;
+
+	error = gitno_send(s, request.ptr, request.size, 0);
+
+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)
+{
+	char *host, *port;
+	const char prefix[] = "git://";
+	int error;
+
+	t->socket = INVALID_SOCKET;
+
+	if (!git__prefixcmp(url, prefix))
+		url += strlen(prefix);
+
+	if (gitno_extract_host_and_port(&host, &port, url, GIT_DEFAULT_PORT) < 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;
+	}
+
+	if (t->socket == INVALID_SOCKET) {
+		giterr_set(GITERR_NET, "Failed to connect to the host");
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ * Read from the socket and store the references in the vector
+ */
+static int store_refs(transport_git *t)
+{
+	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;
+
+		gitno_consume_n(buf, buf->offset);
+
+		if (t->proto.flush) { /* No more refs */
+			t->proto.flush = 0;
+			return 0;
+		}
+	}
+}
+
+static int detect_caps(transport_git *t)
+{
+	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;
+
+	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;
+		}
+
+		/* We don't know this capability, so skip it */
+		ptr = strchr(ptr, ' ');
+	}
+
+	return 0;
+}
+
+/*
+ * 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)
+{
+	transport_git *t = (transport_git *) transport;
+
+	if (direction == GIT_DIR_PUSH) {
+		giterr_set(GITERR_NET, "Pushing over git:// is not supported");
+		return -1;
+	}
+
+	t->parent.direction = direction;
+	if (git_vector_init(&t->refs, 16, NULL) < 0)
+		return -1;
+
+	/* Connect and ask for the refs */
+	if (do_connect(t, transport->url) < 0)
+		goto cleanup;
+
+	gitno_buffer_setup(&t->buf, t->buff, sizeof(t->buff), t->socket);
+
+	t->parent.connected = 1;
+	if (store_refs(t) < 0)
+		goto cleanup;
+
+	if (detect_caps(t) < 0)
+		goto cleanup;
+
+	return 0;
+cleanup:
+	git_vector_free(&t->refs);
+	return -1;
+}
+
+static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque)
+{
+	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;
+
+		pkt = (git_pkt_ref *)p;
+
+		if (list_cb(&pkt->head, opaque) < 0) {
+			giterr_set(GITERR_NET, "User callback returned error");
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/* 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;
+
+	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 ((error = gitno_recv(buf)) < 0)
+			return -1;
+
+		error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
+		if (error == GIT_EBUFS)
+			continue;
+		if (error < 0)
+			return -1;
+	} while (error);
+
+	gitno_consume(buf, line_end);
+	pkt_type = pkt->type;
+	git__free(pkt);
+
+	return pkt_type;
+}
+
+static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
+{
+	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;
+
+	if (git_fetch_setup_walk(&walk, repo) < 0)
+		goto on_error;
+
+	if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+		goto on_error;
+
+	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;
+
+			git_pkt_buffer_flush(&data);
+			if (git_buf_oom(&data))
+				goto on_error;
+
+			if (gitno_send(t->socket, data.ptr, data.size, 0) < 0)
+				goto on_error;
+
+			pkt_type = recv_pkt(buf);
+
+			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;
+			}
+
+		}
+	}
+	if (error < 0 && error != GIT_REVWALKOVER)
+		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)
+		goto on_error;
+
+	git_buf_free(&data);
+	git_revwalk_free(walk);
+	return 0;
+
+on_error:
+	git_buf_free(&data);
+	git_revwalk_free(walk);
+	return -1;
+}
+
+static int git_download_pack(git_transport *transport, git_repository *repo, git_off_t *bytes, git_indexer_stats *stats)
+{
+	transport_git *t = (transport_git *) transport;
+	int error = 0, read_bytes;
+	gitno_buffer *buf = &t->buf;
+	git_pkt *pkt;
+	const char *line_end, *ptr;
+
+	/*
+	 * 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;
+			}
+
+			error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
+			if (error == GIT_EBUFS)
+				break;
+
+			if (error < 0)
+				return error;
+
+			if (pkt->type == GIT_PKT_PACK) {
+				git__free(pkt);
+				return git_fetch__download_pack(buf->data, buf->offset, t->socket, repo, bytes, stats);
+			}
+
+			/* For now we don't care about anything */
+			git__free(pkt);
+			gitno_consume(buf, line_end);
+		}
+
+		read_bytes = gitno_recv(buf);
+	} while (read_bytes);
+
+	return read_bytes;
+}
+
+static int git_close(git_transport *transport)
+{
+	transport_git *t = (transport_git*) transport;
+
+	/* 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;
+	}
+
+#ifdef GIT_WIN32
+	WSACleanup();
+#endif
+
+	return 0;
+}
+
+static void git_free(git_transport *transport)
+{
+	transport_git *t = (transport_git *) transport;
+	git_vector *refs = &t->refs;
+	unsigned int i;
+
+	for (i = 0; i < refs->length; ++i) {
+		git_pkt *p = git_vector_get(refs, i);
+		git_pkt_free(p);
+	}
+
+	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)
+{
+	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));
+
+	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;
+
+	*out = (git_transport *) 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
+
+	return 0;
+}
diff --git a/libgit2/src/transports/http.c b/libgit2/src/transports/http.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/http.c
@@ -0,0 +1,682 @@
+/*
+ * 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;
+}
diff --git a/libgit2/src/transports/local.c b/libgit2/src/transports/local.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/transports/local.c
@@ -0,0 +1,240 @@
+/*
+ * 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/net.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/tag.h"
+#include "refs.h"
+#include "transport.h"
+#include "posix.h"
+#include "path.h"
+#include "buffer.h"
+
+typedef struct {
+	git_transport parent;
+	git_repository *repo;
+	git_vector refs;
+} transport_local;
+
+static int add_ref(transport_local *t, const char *name)
+{
+	const char peeled[] = "^{}";
+	git_remote_head *head;
+	git_object *obj = NULL, *target = NULL;
+	git_buf buf = GIT_BUF_INIT;
+
+	head = git__malloc(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)
+	{
+		git__free(head->name);
+		git__free(head);
+		return -1;
+	}
+
+	/* If it's not a tag, we don't need to try to peel it */
+	if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
+		return 0;
+
+	if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0)
+		return -1;
+
+	head = NULL;
+
+	/* If it's not an annotated tag, just get out */
+	if (git_object_type(obj) != GIT_OBJ_TAG) {
+		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));
+	GITERR_CHECK_ALLOC(head);
+	if (git_buf_join(&buf, 0, name, peeled) < 0)
+		return -1;
+
+	head->name = git_buf_detach(&buf);
+
+	if (git_tag_peel(&target, (git_tag *) obj) < 0)
+		goto on_error;
+
+	git_oid_cpy(&head->oid, git_object_id(target));
+	git_object_free(obj);
+	git_object_free(target);
+
+	if (git_vector_insert(&t->refs, head) < 0)
+		return -1;
+
+	return 0;
+
+on_error:
+	git_object_free(obj);
+	git_object_free(target);
+	return -1;
+}
+
+static int store_refs(transport_local *t)
+{
+	unsigned int i;
+	git_strarray ref_names = {0};
+
+	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)
+		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)
+		goto on_error;
+
+	for (i = 0; i < ref_names.count; ++i) {
+		if (add_ref(t, ref_names.strings[i]) < 0)
+			goto on_error;
+	}
+
+	git_strarray_free(&ref_names);
+	return 0;
+
+on_error:
+	git_vector_free(&t->refs);
+	git_strarray_free(&ref_names);
+	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)
+{
+	git_repository *repo;
+	int error;
+	transport_local *t = (transport_local *) transport;
+	const char *path;
+	git_buf buf = GIT_BUF_INIT;
+
+	GIT_UNUSED(direction);
+
+	/* The repo layer doesn't want the prefix */
+	if (!git__prefixcmp(transport->url, "file://")) {
+		if (git_path_fromurl(&buf, transport->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;
+	}
+
+	error = git_repository_open(&repo, path);
+
+	git_buf_free(&buf);
+
+	if (error < 0)
+		return -1;
+
+	t->repo = repo;
+
+	if (store_refs(t) < 0)
+		return -1;
+
+	t->parent.connected = 1;
+
+	return 0;
+}
+
+static int local_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants)
+{
+	GIT_UNUSED(transport);
+	GIT_UNUSED(repo);
+	GIT_UNUSED(wants);
+
+	giterr_set(GITERR_NET, "Fetch via local transport isn't implemented. Sorry");
+	return -1;
+}
+
+static int local_close(git_transport *transport)
+{
+	transport_local *t = (transport_local *)transport;
+
+	git_repository_free(t->repo);
+	t->repo = 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;
+
+	assert(transport);
+
+	git_vector_foreach (vec, i, h) {
+		git__free(h->name);
+		git__free(h);
+	}
+	git_vector_free(vec);
+
+	git__free(t->parent.url);
+	git__free(t);
+}
+
+/**************
+ * Public API *
+ **************/
+
+int git_transport_local(git_transport **out)
+{
+	transport_local *t;
+
+	t = git__malloc(sizeof(transport_local));
+	GITERR_CHECK_ALLOC(t);
+
+	memset(t, 0x0, sizeof(transport_local));
+
+	t->parent.connect = local_connect;
+	t->parent.ls = local_ls;
+	t->parent.negotiate_fetch = local_negotiate_fetch;
+	t->parent.close = local_close;
+	t->parent.free = local_free;
+
+	*out = (git_transport *) t;
+
+	return 0;
+}
diff --git a/libgit2/src/tree-cache.c b/libgit2/src/tree-cache.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tree-cache.c
@@ -0,0 +1,184 @@
+/*
+ * 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 "tree-cache.h"
+
+static git_tree_cache *find_child(const git_tree_cache *tree, const char *path)
+{
+	size_t i, dirlen;
+	const char *end;
+
+	end = strchr(path, '/');
+	if (end == NULL) {
+		end = strrchr(path, '\0');
+	}
+
+	dirlen = end - path;
+
+	for (i = 0; i < tree->children_count; ++i) {
+		const char *childname = tree->children[i]->name;
+
+		if (strlen(childname) == dirlen && !memcmp(path, childname, dirlen))
+			return tree->children[i];
+	}
+
+	return NULL;
+}
+
+void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
+{
+	const char *ptr = path, *end;
+
+	if (tree == NULL)
+		return;
+
+	tree->entries = -1;
+
+	while (ptr != NULL) {
+		end = strchr(ptr, '/');
+
+		if (end == NULL) /* End of path */
+			break;
+
+		tree = find_child(tree, ptr);
+		if (tree == NULL) /* We don't have that tree */
+			return;
+
+		tree->entries = -1;
+		ptr = end + 1;
+	}
+}
+
+const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
+{
+	const char *ptr = path, *end;
+
+	if (tree == NULL) {
+		return NULL;
+	}
+
+	while (1) {
+		end = strchr(ptr, '/');
+
+		tree = find_child(tree, ptr);
+		if (tree == NULL) { /* Can't find it */
+			return NULL;
+		}
+
+		if (end == NULL || end + 1 == '\0')
+			return tree;
+
+		ptr = end + 1;
+	}
+}
+
+static int read_tree_internal(git_tree_cache **out,
+		const char **buffer_in, const char *buffer_end, git_tree_cache *parent)
+{
+	git_tree_cache *tree = NULL;
+	const char *name_start, *buffer;
+	int count;
+	size_t name_len;
+
+	buffer = name_start = *buffer_in;
+
+	if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
+		goto corrupted;
+
+	if (++buffer >= buffer_end)
+		goto corrupted;
+
+	name_len = strlen(name_start);
+	tree = git__malloc(sizeof(git_tree_cache) + name_len + 1);
+	GITERR_CHECK_ALLOC(tree);
+
+	memset(tree, 0x0, sizeof(git_tree_cache));
+	tree->parent = parent;
+
+	/* NUL-terminated tree name */
+	memcpy(tree->name, name_start, name_len);
+	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)
+		goto corrupted;
+
+	tree->entries = count;
+
+	if (*buffer != ' ' || ++buffer >= buffer_end)
+		goto corrupted;
+
+	 /* Number of children of the tree, newline-terminated */
+	if (git__strtol32(&count, buffer, &buffer, 10) < 0 || count < 0)
+		goto corrupted;
+
+	tree->children_count = count;
+
+	if (*buffer != '\n' || ++buffer > buffer_end)
+		goto corrupted;
+
+	/* The SHA1 is only there if it's not invalidated */
+	if (tree->entries >= 0) {
+		/* 160-bit SHA-1 for this tree and it's children */
+		if (buffer + GIT_OID_RAWSZ > buffer_end)
+			goto corrupted;
+
+		git_oid_fromraw(&tree->oid, (const unsigned char *)buffer);
+		buffer += GIT_OID_RAWSZ;
+	}
+
+	/* Parse children: */
+	if (tree->children_count > 0) {
+		unsigned int i;
+
+		tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *));
+		GITERR_CHECK_ALLOC(tree->children);
+
+		for (i = 0; i < tree->children_count; ++i) {
+			if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0)
+				return -1;
+		}
+	}
+
+	*buffer_in = buffer;
+	*out = tree;
+	return 0;
+
+ corrupted:
+	git_tree_cache_free(tree);
+	giterr_set(GITERR_INDEX, "Corruped TREE extension in index");
+	return -1;
+}
+
+int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size)
+{
+	const char *buffer_end = buffer + buffer_size;
+
+	if (read_tree_internal(tree, &buffer, buffer_end, NULL) < 0)
+		return -1;
+
+	if (buffer < buffer_end) {
+		giterr_set(GITERR_INDEX, "Corruped TREE extension in index (unexpected trailing data)");
+		return -1;
+	}
+
+	return 0;
+}
+
+void git_tree_cache_free(git_tree_cache *tree)
+{
+	unsigned int i;
+
+	if (tree == NULL)
+		return;
+
+	for (i = 0; i < tree->children_count; ++i)
+		git_tree_cache_free(tree->children[i]);
+
+	git__free(tree->children);
+	git__free(tree);
+}
diff --git a/libgit2/src/tree-cache.h b/libgit2/src/tree-cache.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tree-cache.h
@@ -0,0 +1,31 @@
+/*
+ * 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_tree_cache_h__
+#define INCLUDE_tree_cache_h__
+
+#include "common.h"
+#include "git2/oid.h"
+
+struct git_tree_cache {
+	struct git_tree_cache *parent;
+	struct git_tree_cache **children;
+	size_t children_count;
+
+	ssize_t entries;
+	git_oid oid;
+	char name[GIT_FLEX_ARRAY];
+};
+
+typedef struct git_tree_cache git_tree_cache;
+
+int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size);
+void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);
+const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);
+void git_tree_cache_free(git_tree_cache *tree);
+
+#endif
diff --git a/libgit2/src/tree.c b/libgit2/src/tree.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tree.c
@@ -0,0 +1,785 @@
+/*
+ * 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 "commit.h"
+#include "tree.h"
+#include "git2/repository.h"
+#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)
+{
+	return attributes >= 0 && attributes <= MAX_FILEMODE;
+}
+
+static int valid_entry_name(const char *filename)
+{
+	return strlen(filename) > 0 && strchr(filename, '/') == NULL;
+}
+
+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);
+
+	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));
+}
+
+
+struct tree_key_search {
+	const char *filename;
+	size_t filename_len;
+};
+
+static int homing_search_cmp(const void *key, const void *array_member)
+{
+	const struct tree_key_search *ksearch = key;
+	const git_tree_entry *entry = array_member;
+
+	const size_t len1 = ksearch->filename_len;
+	const size_t len2 = entry->filename_len;
+
+	return memcmp(
+		ksearch->filename,
+		entry->filename,
+		len1 < len2 ? len1 : len2
+	);
+}
+
+/*
+ * Search for an entry in a given tree.
+ *
+ * Note that this search is performed in two steps because
+ * of the way tree entries are sorted internally in git:
+ *
+ * Entries in a tree are not sorted alphabetically; two entries
+ * with the same root prefix will have different positions
+ * depending on whether they are folders (subtrees) or normal files.
+ *
+ * Consequently, it is not possible to find an entry on the tree
+ * with a binary search if you don't know whether the filename
+ * you're looking for is a folder or a normal file.
+ *
+ * To work around this, we first perform a homing binary search
+ * on the tree, using the minimal length root prefix of our filename.
+ * Once the comparisons for this homing search start becoming
+ * 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)
+{
+	struct tree_key_search ksearch;
+	const git_tree_entry *entry;
+
+	int homing, i;
+
+	ksearch.filename = filename;
+	ksearch.filename_len = strlen(filename);
+
+	/* 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;
+
+	/* 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) {
+		entry = entries->contents[i];
+
+		if (homing_search_cmp(&ksearch, entry) < 0)
+			break;
+
+		if (strcmp(filename, entry->filename) == 0)
+			return i;
+	}
+
+	/* 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_search_cmp(&ksearch, entry) > 0)
+			break;
+
+		if (strcmp(filename, entry->filename) == 0)
+			return i;
+	}
+
+	/* The filename doesn't exist at all */
+	return GIT_ENOTFOUND;
+}
+
+void git_tree__free(git_tree *tree)
+{
+	unsigned int i;
+
+	for (i = 0; i < tree->entries.length; ++i) {
+		git_tree_entry *e;
+		e = git_vector_get(&tree->entries, i);
+
+		git__free(e->filename);
+		git__free(e);
+	}
+
+	git_vector_free(&tree->entries);
+	git__free(tree);
+}
+
+const git_oid *git_tree_id(git_tree *c)
+{
+	return git_object_id((git_object *)c);
+}
+
+unsigned int git_tree_entry_attributes(const git_tree_entry *entry)
+{
+	return entry->attr;
+}
+
+const char *git_tree_entry_name(const git_tree_entry *entry)
+{
+	assert(entry);
+	return entry->filename;
+}
+
+const git_oid *git_tree_entry_id(const git_tree_entry *entry)
+{
+	assert(entry);
+	return &entry->oid;
+}
+
+git_otype git_tree_entry_type(const git_tree_entry *entry)
+{
+	assert(entry);
+
+	if (S_ISGITLINK(entry->attr))
+		return GIT_OBJ_COMMIT;
+	else if (S_ISDIR(entry->attr))
+		return GIT_OBJ_TREE;
+	else
+		return GIT_OBJ_BLOB;
+}
+
+int git_tree_entry_to_object(
+	git_object **object_out,
+	git_repository *repo,
+	const git_tree_entry *entry)
+{
+	assert(entry && object_out);
+	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)
+{
+	int idx;
+
+	assert(tree && filename);
+
+	idx = tree_key_search(&tree->entries, filename);
+	if (idx == GIT_ENOTFOUND)
+		return NULL;
+
+	return git_vector_get(&tree->entries, idx);
+}
+
+const git_tree_entry *git_tree_entry_byindex(git_tree *tree, unsigned int idx)
+{
+	assert(tree);
+	return git_vector_get(&tree->entries, idx);
+}
+
+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;
+
+	ksearch.filename = path;
+	ksearch.filename_len = strlen(path);
+
+	/* Find tree entry with appropriate prefix */
+	git_vector_bsearch3(&at_pos, entries, &homing_search_cmp, &ksearch);
+
+	for (; at_pos < entries->length; ++at_pos) {
+		const git_tree_entry *entry = entries->contents[at_pos];
+		if (homing_search_cmp(&ksearch, entry) < 0)
+			break;
+	}
+
+	for (; at_pos > 0; --at_pos) {
+		const git_tree_entry *entry = entries->contents[at_pos - 1];
+		if (homing_search_cmp(&ksearch, entry) > 0)
+			break;
+	}
+
+	return at_pos;
+}
+
+unsigned int git_tree_entrycount(git_tree *tree)
+{
+	assert(tree);
+	return tree->entries.length;
+}
+
+static int tree_error(const char *str)
+{
+	giterr_set(GITERR_TREE, "%s", str);
+	return -1;
+}
+
+static int tree_parse_buffer(git_tree *tree, const char *buffer, const char *buffer_end)
+{
+	if (git_vector_init(&tree->entries, DEFAULT_TREE_SIZE, entry_sort_cmp) < 0)
+		return -1;
+
+	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;
+
+		if (git__strtol32(&tmp, buffer, &buffer, 8) < 0 ||
+			!buffer || !valid_attributes(tmp))
+			return tree_error("Failed to parse tree. Can't parse attributes");
+
+		entry->attr = tmp;
+
+		if (*buffer++ != ' ')
+			return tree_error("Failed to parse tree. Object is corrupted");
+
+		if (memchr(buffer, 0, buffer_end - buffer) == NULL)
+			return tree_error("Failed to parse tree. Object is corrupted");
+
+		entry->filename = git__strdup(buffer);
+		entry->filename_len = strlen(buffer);
+
+		while (buffer < buffer_end && *buffer != 0)
+			buffer++;
+
+		buffer++;
+
+		git_oid_fromraw(&entry->oid, (const unsigned char *)buffer);
+		buffer += GIT_OID_RAWSZ;
+	}
+
+	return 0;
+}
+
+int git_tree__parse(git_tree *tree, git_odb_object *obj)
+{
+	assert(tree);
+	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)
+{
+	unsigned int i, entries = git_index_entrycount(index);
+	size_t dirlen;
+
+	dirlen = strlen(dirname);
+	for (i = start; i < entries; ++i) {
+		git_index_entry *entry = git_index_get(index, i);
+		if (strlen(entry->path) < dirlen ||
+		    memcmp(entry->path, dirname, dirlen) ||
+			(dirlen > 0 && entry->path[dirlen] != '/')) {
+			break;
+		}
+	}
+
+	return i;
+}
+
+static int append_entry(git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
+{
+	git_tree_entry *entry;
+
+	entry = git__calloc(1, sizeof(git_tree_entry));
+	GITERR_CHECK_ALLOC(entry);
+
+	entry->filename = git__strdup(filename);
+	entry->filename_len = strlen(entry->filename);
+
+	git_oid_cpy(&entry->oid, id);
+	entry->attr = attributes;
+
+	if (git_vector_insert(&bld->entries, entry) < 0)
+		return -1;
+
+	return 0;
+}
+
+static int write_tree(
+	git_oid *oid,
+	git_repository *repo,
+	git_index *index,
+	const char *dirname,
+	unsigned int start)
+{
+	git_treebuilder *bld = NULL;
+
+	unsigned int i, entries = git_index_entrycount(index);
+	int error;
+	size_t dirname_len = strlen(dirname);
+	const git_tree_cache *cache;
+
+	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);
+	}
+
+	error = git_treebuilder_create(&bld, NULL);
+	if (bld == NULL) {
+		return -1;
+	}
+
+	/*
+	 * This loop is unfortunate, but necessary. The index doesn't have
+	 * any directores, so we need to handle that manually, and we
+	 * 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;
+
+	/*
+	 * If we've left our (sub)tree, exit the loop and return. The
+	 * first check is an early out (and security for the
+	 * third). The second check is a simple prefix comparison. The
+	 * third check catches situations where there is a directory
+	 * win32/sys and a file win32mmap.c. Without it, the following
+	 * code believes there is a file win32/mmap.c
+	 */
+		if (strlen(entry->path) < dirname_len ||
+		    memcmp(entry->path, dirname, dirname_len) ||
+		    (dirname_len > 0 && entry->path[dirname_len] != '/')) {
+			break;
+		}
+
+		filename = entry->path + dirname_len;
+		if (*filename == '/')
+			filename++;
+		next_slash = strchr(filename, '/');
+		if (next_slash) {
+			git_oid sub_oid;
+			int written;
+			char *subdir, *last_comp;
+
+			subdir = git__strndup(entry->path, next_slash - entry->path);
+			GITERR_CHECK_ALLOC(subdir);
+
+			/* Write out the subtree */
+			written = write_tree(&sub_oid, repo, index, subdir, i);
+			if (written < 0) {
+				tree_error("Failed to write subtree");
+				goto on_error;
+			} else {
+				i = written - 1; /* -1 because of the loop increment */
+			}
+
+			/*
+			 * We need to figure out what we want toinsert
+			 * into this tree. If we're traversing
+			 * deps/zlib/, then we only want to write
+			 * 'zlib' into the tree.
+			 */
+			last_comp = strrchr(subdir, '/');
+			if (last_comp) {
+				last_comp++; /* Get rid of the '/' */
+			} 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");
+				goto on_error;
+			}
+		} else {
+			error = append_entry(bld, filename, &entry->oid, entry->mode);
+			if (error < 0) {
+				tree_error("Failed to insert file");
+				goto on_error;
+			}
+		}
+	}
+
+	if (git_treebuilder_write(oid, repo, bld) < 0)
+		goto on_error;
+
+	git_treebuilder_free(bld);
+	return i;
+
+on_error:
+	git_treebuilder_free(bld);
+	return -1;
+}
+
+int git_tree_create_fromindex(git_oid *oid, git_index *index)
+{
+	int ret;
+	git_repository *repo;
+
+	repo = (git_repository *)GIT_REFCOUNT_OWNER(index);
+
+	if (repo == NULL)
+		return tree_error("Failed to create tree. "
+		  "The index file is not backed up by an existing repository");
+
+	if (index->tree != NULL && index->tree->entries >= 0) {
+		git_oid_cpy(oid, &index->tree->oid);
+		return 0;
+	}
+
+	/* The tree cache didn't help us */
+	ret = write_tree(oid, repo, index, "", 0);
+	return ret < 0 ? ret : 0;
+}
+
+static void sort_entries(git_treebuilder *bld)
+{
+	git_vector_sort(&bld->entries);
+}
+
+int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source)
+{
+	git_treebuilder *bld;
+	unsigned int i, source_entries = DEFAULT_TREE_SIZE;
+
+	assert(builder_p);
+
+	bld = git__calloc(1, sizeof(git_treebuilder));
+	GITERR_CHECK_ALLOC(bld);
+
+	if (source != NULL)
+		source_entries = source->entries.length;
+
+	if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0)
+		goto on_error;
+
+	if (source != NULL) {
+		for (i = 0; i < source->entries.length; ++i) {
+			git_tree_entry *entry_src = source->entries.contents[i];
+
+			if (append_entry(bld, entry_src->filename, &entry_src->oid, entry_src->attr) < 0)
+				goto on_error;
+		}
+	}
+
+	*builder_p = bld;
+	return 0;
+
+on_error:
+	git_treebuilder_free(bld);
+	return -1;
+}
+
+int git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes)
+{
+	git_tree_entry *entry;
+	int pos;
+
+	assert(bld && id && filename);
+
+	if (!valid_attributes(attributes))
+		return tree_error("Failed to insert entry. Invalid attributes");
+
+	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);
+
+	if (pos >= 0) {
+		entry = git_vector_get(&bld->entries, pos);
+		if (entry->removed)
+			entry->removed = 0;
+	} else {
+		entry = git__calloc(1, sizeof(git_tree_entry));
+		GITERR_CHECK_ALLOC(entry);
+
+		entry->filename = git__strdup(filename);
+		entry->filename_len = strlen(entry->filename);
+	}
+
+	git_oid_cpy(&entry->oid, id);
+	entry->attr = attributes;
+
+	if (pos == GIT_ENOTFOUND) {
+		if (git_vector_insert(&bld->entries, entry) < 0)
+			return -1;
+	}
+
+	if (entry_out != NULL)
+		*entry_out = entry;
+
+	return 0;
+}
+
+static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
+{
+	int idx;
+	git_tree_entry *entry;
+
+	assert(bld && filename);
+
+	idx = tree_key_search(&bld->entries, filename);
+	if (idx < 0)
+		return NULL;
+
+	entry = git_vector_get(&bld->entries, idx);
+	if (entry->removed)
+		return NULL;
+
+	return entry;
+}
+
+const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)
+{
+	return treebuilder_get(bld, filename);
+}
+
+int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
+{
+	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");
+
+	remove_ptr->removed = 1;
+	return 0;
+}
+
+int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld)
+{
+	unsigned int i;
+	git_buf tree = GIT_BUF_INIT;
+	git_odb *odb;
+
+	assert(bld);
+
+	sort_entries(bld);
+
+	/* Grow the buffer beforehand to an estimated size */
+	git_buf_grow(&tree, bld->entries.length * 72);
+
+	for (i = 0; i < bld->entries.length; ++i) {
+		git_tree_entry *entry = bld->entries.contents[i];
+
+		if (entry->removed)
+			continue;
+
+		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_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE) < 0)
+		goto on_error;
+
+	git_buf_free(&tree);
+	return 0;
+
+on_error:
+	git_buf_free(&tree);
+	return -1;
+}
+
+void git_treebuilder_filter(git_treebuilder *bld, int (*filter)(const git_tree_entry *, void *), void *payload)
+{
+	unsigned int i;
+
+	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))
+			entry->removed = 1;
+	}
+}
+
+void git_treebuilder_clear(git_treebuilder *bld)
+{
+	unsigned int i;
+	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_clear(&bld->entries);
+}
+
+void git_treebuilder_free(git_treebuilder *bld)
+{
+	git_treebuilder_clear(bld);
+	git_vector_free(&bld->entries);
+	git__free(bld);
+}
+
+static int tree_frompath(
+	git_tree **parent_out,
+	git_tree *root,
+	git_buf *treeentry_path,
+	size_t offset)
+{
+	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, '/');
+
+	if (slash_pos == NULL)
+		return git_tree_lookup(
+			parent_out,
+			root->object.repo,
+			git_object_id((const git_object *)root)
+		);
+
+	if (slash_pos == treeentry_path->ptr + offset) {
+		giterr_set(GITERR_INVALID,
+			"Invalid relative path to a tree entry '%s'.", treeentry_path->ptr);
+		return -1;
+	}
+
+	*slash_pos = '\0';
+
+	entry = git_tree_entry_byname(root, treeentry_path->ptr + offset);
+
+	if (slash_pos != NULL)
+		*slash_pos = '/';
+
+	if (entry == NULL) {
+		giterr_set(GITERR_TREE,
+			"No tree entry can be found from "
+			"the given tree and relative path '%s'.", treeentry_path->ptr);
+		return GIT_ENOTFOUND;
+	}
+
+
+	if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
+		return error;
+
+	error = tree_frompath(
+		parent_out,
+		subtree,
+		treeentry_path,
+		(slash_pos - treeentry_path->ptr) + 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,
+	git_treewalk_cb callback,
+	git_buf *path,
+	void *payload)
+{
+	int error = 0;
+	unsigned int i;
+
+	for (i = 0; i < tree->entries.length; ++i) {
+		git_tree_entry *entry = tree->entries.contents[i];
+
+		if (callback(path->ptr, entry, payload) < 0)
+			continue;
+
+		if (git_tree_entry__is_tree(entry)) {
+			git_tree *subtree;
+			size_t path_len = git_buf_len(path);
+
+			if ((error = git_tree_lookup(
+				&subtree, tree->object.repo, &entry->oid)) < 0)
+				break;
+
+			/* append the next entry to the path */
+			git_buf_puts(path, entry->filename);
+			git_buf_putc(path, '/');
+
+			if (git_buf_oom(path))
+				return -1;
+
+			if (tree_walk_post(subtree, callback, path, payload) < 0)
+				return -1;
+
+			git_buf_truncate(path, path_len);
+			git_tree_free(subtree);
+		}
+	}
+
+	return 0;
+}
+
+int git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, 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;
+	}
+
+	git_buf_free(&root_path);
+
+	return error;
+}
+
diff --git a/libgit2/src/tree.h b/libgit2/src/tree.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tree.h
@@ -0,0 +1,51 @@
+/*
+ * 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_tree_h__
+#define INCLUDE_tree_h__
+
+#include "git2/tree.h"
+#include "repository.h"
+#include "odb.h"
+#include "vector.h"
+
+struct git_tree_entry {
+	unsigned int attr;
+	char *filename;
+	git_oid oid;
+	size_t filename_len;
+	int removed;
+};
+
+struct git_tree {
+	git_object object;
+	git_vector entries;
+};
+
+struct git_treebuilder {
+	git_vector entries;
+};
+
+
+GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
+{
+	return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
+}
+
+void git_tree__free(git_tree *tree);
+int git_tree__parse(git_tree *tree, git_odb_object *obj);
+
+/**
+ * Lookup the first position in the tree with a given prefix.
+ *
+ * @param tree a previously loaded tree.
+ * @param prefix the beginning of a path to find in the tree.
+ * @return index of the first item at or after the given prefix.
+ */
+int git_tree__prefix_position(git_tree *tree, const char *prefix);
+
+
+#endif
diff --git a/libgit2/src/tsort.c b/libgit2/src/tsort.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/tsort.c
@@ -0,0 +1,367 @@
+/*
+ * 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"
+
+/**
+ * An array-of-pointers implementation of Python's Timsort
+ * Based on code by Christopher Swenson under the MIT license
+ *
+ * Copyright (c) 2010 Christopher Swenson
+ * Copyright (c) 2011 Vicent Marti
+ */
+
+#ifndef MAX
+#	define MAX(x,y) (((x) > (y) ? (x) : (y)))
+#endif
+
+#ifndef MIN
+#	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)
+{
+	int l, c, r;
+	void *lx, *cx;
+
+	assert(size > 0);
+
+	l = 0;
+	r = (int)size - 1;
+	c = r >> 1;
+	lx = dst[l];
+
+	/* check for beginning conditions */
+	if (cmp(x, lx) < 0)
+		return 0;
+
+	else if (cmp(x, lx) == 0) {
+		int i = 1;
+		while (cmp(x, dst[i]) == 0)
+			i++;
+		return i;
+	}
+
+	/* guaranteed not to be >= rx */
+	cx = dst[c];
+	while (1) {
+		const int val = cmp(x, cx);
+		if (val < 0) {
+			if (c - l <= 1) return c;
+			r = c;
+		} else if (val > 0) {
+			if (r - c <= 1) return c + 1;
+			l = c;
+			lx = cx;
+		} else {
+			do {
+				cx = dst[++c];
+			} while (cmp(x, cx) == 0);
+			return c;
+		}
+		c = l + ((r - l) >> 1);
+		cx = dst[c];
+	}
+}
+
+/* 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)
+{
+	size_t i;
+	void *x;
+	int location;
+
+	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)
+			continue;
+
+		/* Else we need to find the right place, shift everything over, and squeeze in */
+		x = dst[i];
+		location = binsearch(dst, x, i, cmp);
+		for (j = (int)i - 1; j >= location; j--) {
+			dst[j + 1] = dst[j];
+		}
+		dst[location] = x;
+	}
+}
+
+
+/* timsort implementation, based on timsort.txt */
+struct tsort_run {
+	ssize_t start;
+	ssize_t length;
+};
+
+struct tsort_store {
+	size_t alloc;
+	cmp_ptr_t cmp;
+	void **storage;
+};
+
+static void reverse_elements(void **dst, ssize_t start, ssize_t end)
+{
+	while (start < end) {
+		void *tmp = dst[start];
+		dst[start] = dst[end];
+		dst[end] = tmp;
+
+		start++;
+		end--;
+	}
+}
+
+static ssize_t count_run(void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+{
+	ssize_t curr = start + 2;
+
+	if (size - start == 1)
+		return 1;
+
+	if (start >= size - 2) {
+		if (store->cmp(dst[size - 2], dst[size - 1]) > 0) {
+			void *tmp = dst[size - 1];
+			dst[size - 1] = dst[size - 2];
+			dst[size - 2] = tmp;
+		}
+
+		return 2;
+	}
+
+	if (store->cmp(dst[start], dst[start + 1]) <= 0) {
+		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) <= 0)
+			curr++;
+
+		return curr - start;
+	} else {
+		while (curr < size - 1 && store->cmp(dst[curr - 1], dst[curr]) > 0)
+			curr++;
+
+		/* reverse in-place */
+		reverse_elements(dst, start, curr - 1);
+		return curr - start;
+	}
+}
+
+static size_t compute_minrun(size_t n)
+{
+	int r = 0;
+	while (n >= 64) {
+		r |= n & 1;
+		n >>= 1;
+	}
+	return n + r;
+}
+
+static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
+{
+	if (stack_curr < 2)
+		return 1;
+
+	else if (stack_curr == 2) {
+		const ssize_t A = stack[stack_curr - 2].length;
+		const ssize_t B = stack[stack_curr - 1].length;
+		return (A > B);
+	} else {
+		const ssize_t A = stack[stack_curr - 3].length;
+		const ssize_t B = stack[stack_curr - 2].length;
+		const ssize_t C = stack[stack_curr - 1].length;
+		return !((A <= B + C) || (B <= C));
+	}
+}
+
+static int resize(struct tsort_store *store, size_t new_size)
+{
+	if (store->alloc < new_size) {
+		void **tempstore = git__realloc(store->storage, new_size * sizeof(void *));
+
+		/**
+		 * Do not propagate on OOM; this will abort the sort and
+		 * leave the array unsorted, but no error code will be
+		 * raised
+		 */
+		if (tempstore == NULL)
+			return -1;
+
+		store->storage = tempstore;
+		store->alloc = new_size;
+	}
+
+	return 0;
+}
+
+static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)
+{
+	const ssize_t A = stack[stack_curr - 2].length;
+	const ssize_t B = stack[stack_curr - 1].length;
+	const ssize_t curr = stack[stack_curr - 2].start;
+
+	void **storage;
+	ssize_t i, j, k;
+
+	if (resize(store, MIN(A, B)) < 0)
+		return;
+
+	storage = store->storage;
+
+	/* left merge */
+	if (A < B) {
+		memcpy(storage, &dst[curr], A * sizeof(void *));
+		i = 0;
+		j = curr + A;
+
+		for (k = curr; k < curr + A + B; k++) {
+			if ((i < A) && (j < curr + A + B)) {
+				if (store->cmp(storage[i], dst[j]) <= 0)
+					dst[k] = storage[i++];
+				else
+					dst[k] = dst[j++];
+			} else if (i < A) {
+				dst[k] = storage[i++];
+			} else
+				dst[k] = dst[j++];
+		}
+	} else {
+		memcpy(storage, &dst[curr + A], B * sizeof(void *));
+		i = B - 1;
+		j = curr + A - 1;
+
+		for (k = curr + A + B - 1; k >= curr; k--) {
+			if ((i >= 0) && (j >= curr)) {
+				if (store->cmp(dst[j], storage[i]) > 0)
+					dst[k] = dst[j--];
+				else
+					dst[k] = storage[i--];
+			} else if (i >= 0)
+				dst[k] = storage[i--];
+			else
+				dst[k] = dst[j--];
+		}
+	}
+}
+
+static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size)
+{
+	ssize_t A, B, C;
+
+	while (1) {
+		/* if the stack only has one thing on it, we are done with the collapse */
+		if (stack_curr <= 1)
+			break;
+
+		/* if this is the last merge, just do it */
+		if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
+			merge(dst, stack, stack_curr, store);
+			stack[0].length += stack[1].length;
+			stack_curr--;
+			break;
+		}
+
+		/* check if the invariant is off for a stack of 2 elements */
+		else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
+			merge(dst, stack, stack_curr, store);
+			stack[0].length += stack[1].length;
+			stack_curr--;
+			break;
+		}
+		else if (stack_curr == 2)
+			break;
+
+		A = stack[stack_curr - 3].length;
+		B = stack[stack_curr - 2].length;
+		C = stack[stack_curr - 1].length;
+
+		/* check first invariant */
+		if (A <= B + C) {
+			if (A < C) {
+				merge(dst, stack, stack_curr - 1, store);
+				stack[stack_curr - 3].length += stack[stack_curr - 2].length;
+				stack[stack_curr - 2] = stack[stack_curr - 1];
+				stack_curr--;
+			} else {
+				merge(dst, stack, stack_curr, store);
+				stack[stack_curr - 2].length += stack[stack_curr - 1].length;
+				stack_curr--;
+			}
+		} else if (B <= C) {
+			merge(dst, stack, stack_curr, store);
+			stack[stack_curr - 2].length += stack[stack_curr - 1].length;
+			stack_curr--;
+		} else
+			break;
+	}
+
+	return stack_curr;
+}
+
+#define PUSH_NEXT() do {\
+	len = count_run(dst, curr, size, store);\
+	run = minrun;\
+	if (run < minrun) run = minrun;\
+	if (run > (ssize_t)size - curr) run = size - curr;\
+	if (run > len) {\
+		bisort(&dst[curr], len, run, cmp);\
+		len = run;\
+	}\
+	run_stack[stack_curr].start = curr;\
+	run_stack[stack_curr++].length = len;\
+	curr += len;\
+	if (curr == (ssize_t)size) {\
+		/* finish up */ \
+		while (stack_curr > 1) { \
+			merge(dst, run_stack, stack_curr, store); \
+			run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \
+			stack_curr--; \
+		} \
+		if (store->storage != NULL) {\
+			git__free(store->storage);\
+			store->storage = NULL;\
+		}\
+		return;\
+	}\
+}\
+while (0)
+
+void git__tsort(void **dst, size_t size, cmp_ptr_t cmp)
+{
+	struct tsort_store _store, *store = &_store;
+	struct tsort_run run_stack[128];
+
+	ssize_t stack_curr = 0;
+	ssize_t len, run;
+	ssize_t curr = 0;
+	ssize_t minrun;
+
+	if (size < 64) {
+		bisort(dst, 1, size, cmp);
+		return;
+	}
+
+	/* compute the minimum run length */
+	minrun = (ssize_t)compute_minrun(size);
+
+	/* temporary storage for merges */
+	store->alloc = 0;
+	store->storage = NULL;
+	store->cmp = cmp;
+
+	PUSH_NEXT();
+	PUSH_NEXT();
+	PUSH_NEXT();
+
+	while (1) {
+		if (!check_invariant(run_stack, stack_curr)) {
+			stack_curr = collapse(dst, run_stack, stack_curr, store, size);
+			continue;
+		}
+
+		PUSH_NEXT();
+	}
+}
diff --git a/libgit2/src/unix/map.c b/libgit2/src/unix/map.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/unix/map.c
@@ -0,0 +1,54 @@
+/*
+ * 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/common.h>
+
+#ifndef GIT_WIN32
+
+#include "map.h"
+#include <sys/mman.h>
+#include <errno.h>
+
+int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
+{
+	int mprot = 0;
+	int mflag = 0;
+
+	GIT_MMAP_VALIDATE(out, len, prot, flags);
+
+	out->data = NULL;
+	out->len = 0;
+
+	if (prot & GIT_PROT_WRITE)
+		mprot = PROT_WRITE;
+	else if (prot & GIT_PROT_READ)
+		mprot = PROT_READ;
+
+	if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
+		mflag = MAP_SHARED;
+	else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
+		mflag = MAP_PRIVATE;
+
+	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;
+	}
+
+	out->len = len;
+
+	return 0;
+}
+
+int p_munmap(git_map *map)
+{
+	assert(map != NULL);
+	munmap(map->data, map->len);
+	return 0;
+}
+
+#endif
+
diff --git a/libgit2/src/unix/posix.h b/libgit2/src/unix/posix.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/unix/posix.h
@@ -0,0 +1,31 @@
+/*
+ * 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_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>
+
+#define p_lstat(p,b) lstat(p,b)
+#define p_readlink(a, b, c) readlink(a, b, c)
+#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)
+#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)
+
+#endif
diff --git a/libgit2/src/util.c b/libgit2/src/util.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/util.c
@@ -0,0 +1,437 @@
+/*
+ * 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.h>
+#include "common.h"
+#include <stdarg.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "posix.h"
+
+#ifdef _MSC_VER
+# include <Shlwapi.h>
+#endif
+
+void git_libgit2_version(int *major, int *minor, int *rev)
+{
+	*major = LIBGIT2_VER_MAJOR;
+	*minor = LIBGIT2_VER_MINOR;
+	*rev = LIBGIT2_VER_REVISION;
+}
+
+void git_strarray_free(git_strarray *array)
+{
+	size_t i;
+	for (i = 0; i < array->count; ++i)
+		git__free(array->strings[i]);
+
+	git__free(array->strings);
+}
+
+int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
+{
+	size_t i;
+
+	assert(tgt && src);
+
+	memset(tgt, 0, sizeof(*tgt));
+
+	if (!src->count)
+		return 0;
+
+	tgt->strings = git__calloc(src->count, sizeof(char *));
+	GITERR_CHECK_ALLOC(tgt->strings);
+
+	for (i = 0; i < src->count; ++i) {
+		tgt->strings[tgt->count] = git__strdup(src->strings[i]);
+
+		if (!tgt->strings[tgt->count]) {
+			git_strarray_free(tgt);
+			memset(tgt, 0, sizeof(*tgt));
+			return -1;
+		}
+
+		tgt->count++;
+	}
+
+	return 0;
+}
+
+int git__strtol64(int64_t *result, const char *nptr, const char **endptr, int base)
+{
+	const char *p;
+	int64_t n, nn;
+	int c, ovfl, v, neg, ndig;
+
+	p = nptr;
+	neg = 0;
+	n = 0;
+	ndig = 0;
+	ovfl = 0;
+
+	/*
+	 * White space
+	 */
+	while (git__isspace(*p))
+		p++;
+
+	/*
+	 * Sign
+	 */
+	if (*p == '-' || *p == '+')
+		if (*p++ == '-')
+			neg = 1;
+
+	/*
+	 * Base
+	 */
+	if (base == 0) {
+		if (*p != '0')
+			base = 10;
+		else {
+			base = 8;
+			if (p[1] == 'x' || p[1] == 'X') {
+				p += 2;
+				base = 16;
+			}
+		}
+	} else if (base == 16 && *p == '0') {
+		if (p[1] == 'x' || p[1] == 'X')
+			p += 2;
+	} else if (base < 0 || 36 < base)
+		goto Return;
+
+	/*
+	 * Non-empty sequence of digits
+	 */
+	for (;; p++,ndig++) {
+		c = *p;
+		v = base;
+		if ('0'<=c && c<='9')
+			v = c - '0';
+		else if ('a'<=c && c<='z')
+			v = c - 'a' + 10;
+		else if ('A'<=c && c<='Z')
+			v = c - 'A' + 10;
+		if (v >= base)
+			break;
+		nn = n*base + v;
+		if (nn < n)
+			ovfl = 1;
+		n = nn;
+	}
+
+Return:
+	if (ndig == 0) {
+		giterr_set(GITERR_INVALID, "Failed to convert string to long. Not a number");
+		return -1;
+	}
+
+	if (endptr)
+		*endptr = p;
+
+	if (ovfl) {
+		giterr_set(GITERR_INVALID, "Failed to convert string to long. Overflow error");
+		return -1;
+	}
+
+	*result = neg ? -n : n;
+	return 0;
+}
+
+int git__strtol32(int32_t *result, const char *nptr, const char **endptr, int base)
+{
+	int error;
+	int32_t tmp_int;
+	int64_t tmp_long;
+
+	if ((error = git__strtol64(&tmp_long, nptr, endptr, base)) < 0)
+		return error;
+
+	tmp_int = tmp_long & 0xFFFFFFFF;
+	if (tmp_int != tmp_long) {
+		giterr_set(GITERR_INVALID, "Failed to convert. '%s' is too large", nptr);
+		return -1;
+	}
+
+	*result = tmp_int;
+
+	return error;
+}
+
+void git__strntolower(char *str, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; ++i) {
+		str[i] = (char) tolower(str[i]);
+	}
+}
+
+void git__strtolower(char *str)
+{
+	git__strntolower(str, strlen(str));
+}
+
+int git__prefixcmp(const char *str, const char *prefix)
+{
+	for (;;) {
+		char p = *(prefix++), s;
+		if (!p)
+			return 0;
+		if ((s = *(str++)) != p)
+			return s - p;
+	}
+}
+
+int git__suffixcmp(const char *str, const char *suffix)
+{
+	size_t a = strlen(str);
+	size_t b = strlen(suffix);
+	if (a < b)
+		return -1;
+	return strcmp(str + (a - b), suffix);
+}
+
+char *git__strtok(char **end, const char *sep)
+{
+	char *ptr = *end;
+
+	while (*ptr && strchr(sep, *ptr))
+		++ptr;
+
+	if (*ptr) {
+		char *start = ptr;
+		*end = start + 1;
+
+		while (**end && !strchr(sep, **end))
+			++*end;
+
+		if (**end) {
+			**end = '\0';
+			++*end;
+		}
+
+		return start;
+	}
+
+	return NULL;
+}
+
+void git__hexdump(const char *buffer, size_t len)
+{
+	static const size_t LINE_WIDTH = 16;
+
+	size_t line_count, last_line, i, j;
+	const char *line;
+
+	line_count = (len / LINE_WIDTH);
+	last_line = (len % LINE_WIDTH);
+
+	for (i = 0; i < line_count; ++i) {
+		line = buffer + (i * LINE_WIDTH);
+		for (j = 0; j < LINE_WIDTH; ++j, ++line)
+			printf("%02X ", (unsigned char)*line & 0xFF);
+
+		printf("| ");
+
+		line = buffer + (i * LINE_WIDTH);
+		for (j = 0; j < LINE_WIDTH; ++j, ++line)
+			printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
+
+		printf("\n");
+	}
+
+	if (last_line > 0) {
+
+		line = buffer + (line_count * LINE_WIDTH);
+		for (j = 0; j < last_line; ++j, ++line)
+			printf("%02X ", (unsigned char)*line & 0xFF);
+
+		for (j = 0; j < (LINE_WIDTH - last_line); ++j)
+			printf("	");
+
+		printf("| ");
+
+		line = buffer + (line_count * LINE_WIDTH);
+		for (j = 0; j < last_line; ++j, ++line)
+			printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
+
+		printf("\n");
+	}
+
+	printf("\n");
+}
+
+#ifdef GIT_LEGACY_HASH
+uint32_t git__hash(const void *key, int len, unsigned int seed)
+{
+	const uint32_t m = 0x5bd1e995;
+	const int r = 24;
+	uint32_t h = seed ^ len;
+
+	const unsigned char *data = (const unsigned char *)key;
+
+	while(len >= 4) {
+		uint32_t k = *(uint32_t *)data;
+
+		k *= m;
+		k ^= k >> r;
+		k *= m;
+
+		h *= m;
+		h ^= k;
+
+		data += 4;
+		len -= 4;
+	}
+
+	switch(len) {
+	case 3: h ^= data[2] << 16;
+	case 2: h ^= data[1] << 8;
+	case 1: h ^= data[0];
+			h *= m;
+	};
+
+	h ^= h >> 13;
+	h *= m;
+	h ^= h >> 15;
+
+	return h;
+}
+#else
+/*
+	Cross-platform version of Murmurhash3
+	http://code.google.com/p/smhasher/wiki/MurmurHash3
+	by Austin Appleby (aappleby@gmail.com)
+
+	This code is on the public domain.
+*/
+uint32_t git__hash(const void *key, int len, uint32_t seed)
+{
+
+#define MURMUR_BLOCK() {\
+	k1 *= c1; \
+	k1 = git__rotl(k1,11);\
+	k1 *= c2;\
+	h1 ^= k1;\
+	h1 = h1*3 + 0x52dce729;\
+	c1 = c1*5 + 0x7b7d159c;\
+	c2 = c2*5 + 0x6bce6396;\
+}
+
+	const uint8_t *data = (const uint8_t*)key;
+	const int nblocks = len / 4;
+
+	const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
+	const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
+
+	uint32_t h1 = 0x971e137b ^ seed;
+	uint32_t k1;
+
+	uint32_t c1 = 0x95543787;
+	uint32_t c2 = 0x2ad7eb25;
+
+	int i;
+
+	for (i = -nblocks; i; i++) {
+		k1 = blocks[i];
+		MURMUR_BLOCK();
+	}
+
+	k1 = 0;
+
+	switch(len & 3) {
+	case 3: k1 ^= tail[2] << 16;
+	case 2: k1 ^= tail[1] << 8;
+	case 1: k1 ^= tail[0];
+			MURMUR_BLOCK();
+	}
+
+	h1 ^= len;
+	h1 ^= h1 >> 16;
+	h1 *= 0x85ebca6b;
+	h1 ^= h1 >> 13;
+	h1 *= 0xc2b2ae35;
+	h1 ^= h1 >> 16;
+
+	return h1;
+}
+#endif
+
+/**
+ * A modified `bsearch` from the BSD glibc.
+ *
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
+ */
+int git__bsearch(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare)(const void *, const void *),
+	size_t *position)
+{
+	unsigned int lim;
+	int cmp = -1;
+	void **part, **base = array;
+
+	for (lim = (unsigned int)array_len; lim != 0; lim >>= 1) {
+		part = base + (lim >> 1);
+		cmp = (*compare)(key, *part);
+		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 : -1;
+}
+
+/**
+ * A strcmp wrapper
+ * 
+ * We don't want direct pointers to the CRT on Windows, we may
+ * get stdcall conflicts.
+ */
+int git__strcmp_cb(const void *a, const void *b)
+{
+	const char *stra = (const char *)a;
+	const char *strb = (const char *)b;
+
+	return strcmp(stra, strb);
+}
+
+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") ||
+		!strcasecmp(value, "yes") ||
+		!strcasecmp(value, "on")) {
+		*out = 1;
+		return 0;
+	}
+	if (!strcasecmp(value, "false") ||
+		!strcasecmp(value, "no") ||
+		!strcasecmp(value, "off")) {
+		*out = 0;
+		return 0;
+	}
+
+	return -1;
+}
diff --git a/libgit2/src/util.h b/libgit2/src/util.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/util.h
@@ -0,0 +1,226 @@
+/*
+ * 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_util_h__
+#define INCLUDE_util_h__
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define bitsizeof(x) (CHAR_BIT * sizeof(x))
+#define MSB(x, bits) ((x) & (~0ULL << (bitsizeof(x) - (bits))))
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+/*
+ * Custom memory allocation wrappers
+ * that set error code and error message
+ * on allocation failure
+ */
+GIT_INLINE(void *) git__malloc(size_t len)
+{
+	void *ptr = malloc(len);
+	if (!ptr) giterr_set_oom();
+	return ptr;
+}
+
+GIT_INLINE(void *) git__calloc(size_t nelem, size_t elsize)
+{
+	void *ptr = calloc(nelem, elsize);
+	if (!ptr) giterr_set_oom();
+	return ptr;
+}
+
+GIT_INLINE(char *) git__strdup(const char *str)
+{
+	char *ptr = strdup(str);
+	if (!ptr) giterr_set_oom();
+	return ptr;
+}
+
+GIT_INLINE(char *) git__strndup(const char *str, size_t n)
+{
+	size_t length;
+	char *ptr;
+
+	length = strlen(str);
+	if (n < length)
+		length = n;
+
+	ptr = (char*)malloc(length + 1);
+	if (!ptr) {
+		giterr_set_oom();
+		return NULL;
+	}
+
+	memcpy(ptr, str, length);
+	ptr[length] = '\0';
+
+	return ptr;
+}
+
+GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
+{
+	void *new_ptr = realloc(ptr, size);
+	if (!new_ptr) giterr_set_oom();
+	return new_ptr;
+}
+
+#define git__free(ptr) free(ptr)
+
+extern int git__prefixcmp(const char *str, const char *prefix);
+extern int git__suffixcmp(const char *str, const char *suffix);
+
+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);
+
+extern void git__hexdump(const char *buffer, size_t n);
+extern uint32_t git__hash(const void *key, int len, uint32_t seed);
+
+/** @return true if p fits into the range of a size_t */
+GIT_INLINE(int) git__is_sizet(git_off_t p)
+{
+	size_t r = (size_t)p;
+	return p == (git_off_t)r;
+}
+
+/* 32-bit cross-platform rotl */
+#ifdef _MSC_VER /* use built-in method in MSVC */
+#	define git__rotl(v, s) (uint32_t)_rotl(v, s)
+#else /* use bitops in GCC; with o2 this gets optimized to a rotl instruction */
+#	define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))
+#endif
+
+extern char *git__strtok(char **end, const char *sep);
+
+extern void git__strntolower(char *str, size_t len);
+extern void git__strtolower(char *str);
+
+GIT_INLINE(const char *) git__next_line(const char *s)
+{
+	while (*s && *s != '\n') s++;
+	while (*s == '\n' || *s == '\r') s++;
+	return s;
+}
+
+extern void git__tsort(void **dst, size_t size, int (*cmp)(const void *, const void *));
+
+/**
+ * @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
+ */
+extern int git__bsearch(
+	void **array,
+	size_t array_len,
+	const void *key,
+	int (*compare)(const void *, const void *),
+	size_t *position);
+
+extern int git__strcmp_cb(const void *a, const void *b);
+
+typedef struct {
+	short refcount;
+	void *owner;
+} git_refcount;
+
+typedef void (*git_refcount_freeptr)(void *r);
+
+#define GIT_REFCOUNT_INC(r) { \
+	((git_refcount *)(r))->refcount++; \
+}
+
+#define GIT_REFCOUNT_DEC(_r, do_free) { \
+	git_refcount *r = (git_refcount *)(_r); \
+	r->refcount--; \
+	if (r->refcount <= 0 && r->owner == NULL) { do_free(_r); } \
+}
+
+#define GIT_REFCOUNT_OWN(r, o) { \
+	((git_refcount *)(r))->owner = o; \
+}
+
+#define GIT_REFCOUNT_OWNER(r) (((git_refcount *)(r))->owner)
+
+static signed char from_hex[] = {
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 30 */
+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 */
+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */
+};
+
+GIT_INLINE(int) git__fromhex(char h)
+{
+	return from_hex[(unsigned char) h];
+}
+
+GIT_INLINE(int) git__ishex(const char *str)
+{
+	unsigned i;
+	for (i=0; i<strlen(str); i++)
+		if (git__fromhex(str[i]) < 0)
+			return 0;
+	return 1;
+}
+
+GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
+{
+	v--;
+	v |= v >> 1;
+	v |= v >> 2;
+	v |= v >> 4;
+	v |= v >> 8;
+	v |= v >> 16;
+
+	return v;
+}
+
+GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
+{
+	return git__size_t_bitmask(v) + 1;
+}
+
+GIT_INLINE(bool) git__isupper(int c)
+{
+    return (c >= 'A' && c <= 'Z');
+}
+
+GIT_INLINE(bool) git__isalpha(int c)
+{
+    return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+GIT_INLINE(bool) git__isspace(int c)
+{
+    return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__iswildcard(int c)
+{
+	return (c == '*' || c == '?' || c == '[');
+}
+
+/*
+ * Parse a string value as a boolean, just like Core Git
+ * does.
+ *
+ * Valid values for true are: 'true', 'yes', 'on'
+ * Valid values for false are: 'false', 'no', 'off'
+ */
+extern int git__parse_bool(int *out, const char *value);
+
+#endif /* INCLUDE_util_h__ */
diff --git a/libgit2/src/vector.c b/libgit2/src/vector.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/vector.c
@@ -0,0 +1,227 @@
+/*
+ * 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 "repository.h"
+#include "vector.h"
+
+static const double resize_factor = 1.75;
+static const unsigned int minimum_size = 8;
+
+static int resize_vector(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;
+
+	v->contents = git__realloc(v->contents, v->_alloc_size * sizeof(void *));
+	GITERR_CHECK_ALLOC(v->contents);
+
+	return 0;
+}
+
+void git_vector_free(git_vector *v)
+{
+	assert(v);
+
+	git__free(v->contents);
+	v->contents = NULL;
+
+	v->length = 0;
+	v->_alloc_size = 0;
+}
+
+int git_vector_init(git_vector *v, unsigned int 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->_cmp = cmp;
+
+	v->length = 0;
+	v->sorted = 1;
+
+	v->contents = git__malloc(v->_alloc_size * sizeof(void *));
+	GITERR_CHECK_ALLOC(v->contents);
+
+	return 0;
+}
+
+int git_vector_insert(git_vector *v, void *element)
+{
+	assert(v);
+
+	if (v->length >= v->_alloc_size &&
+		resize_vector(v) < 0)
+		return -1;
+
+	v->contents[v->length++] = element;
+	v->sorted = 0;
+
+	return 0;
+}
+
+int git_vector_insert_sorted(
+	git_vector *v, void *element, int (*on_dup)(void **old, void *new))
+{
+	int result;
+	size_t pos;
+
+	assert(v && v->_cmp);
+
+	if (!v->sorted)
+		git_vector_sort(v);
+
+	if (v->length >= v->_alloc_size &&
+		resize_vector(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)
+		return result;
+
+	/* shift elements to the right */
+	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;
+}
+
+void git_vector_sort(git_vector *v)
+{
+	assert(v);
+
+	if (v->sorted || v->_cmp == NULL)
+		return;
+
+	git__tsort(v->contents, v->length, v->_cmp);
+	v->sorted = 1;
+}
+
+int git_vector_bsearch3(
+	unsigned int *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);
+
+	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;
+}
+
+int git_vector_search2(
+	git_vector *v, git_vector_cmp key_lookup, const void *key)
+{
+	unsigned int i;
+
+	assert(v && key && key_lookup);
+
+	for (i = 0; i < v->length; ++i) {
+		if (key_lookup(key, v->contents[i]) == 0)
+			return i;
+	}
+
+	return GIT_ENOTFOUND;
+}
+
+static int strict_comparison(const void *a, const void *b)
+{
+	return (a == b) ? 0 : -1;
+}
+
+int git_vector_search(git_vector *v, const void *entry)
+{
+	return git_vector_search2(v, v->_cmp ? v->_cmp : strict_comparison, entry);
+}
+
+int git_vector_remove(git_vector *v, unsigned int idx)
+{
+	unsigned int i;
+
+	assert(v);
+
+	if (idx >= v->length || v->length == 0)
+		return GIT_ENOTFOUND;
+
+	for (i = idx; i < v->length - 1; ++i)
+		v->contents[i] = v->contents[i + 1];
+
+	v->length--;
+	return 0;
+}
+
+void git_vector_pop(git_vector *v)
+{
+	if (v->length > 0)
+		v->length--;
+}
+
+void git_vector_uniq(git_vector *v)
+{
+	git_vector_cmp cmp;
+	unsigned int i, j;
+
+	if (v->length <= 1)
+		return;
+
+	git_vector_sort(v);
+	cmp = v->_cmp ? v->_cmp : strict_comparison;
+
+	for (i = 0, j = 1 ; j < v->length; ++j)
+		if (!cmp(v->contents[i], v->contents[j]))
+			v->contents[i] = v->contents[j];
+		else
+			v->contents[++i] = v->contents[j];
+
+	v->length -= j - i - 1;
+}
+
+void git_vector_clear(git_vector *v)
+{
+	assert(v);
+	v->length = 0;
+	v->sorted = 1;
+}
+
+void git_vector_swap(git_vector *a, git_vector *b)
+{
+	git_vector t;
+
+	if (!a || !b || a == b)
+		return;
+
+	memcpy(&t, a, sizeof(t));
+	memcpy(a, b, sizeof(t));
+	memcpy(b, &t, sizeof(t));
+}
diff --git a/libgit2/src/vector.h b/libgit2/src/vector.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/vector.h
@@ -0,0 +1,78 @@
+/*
+ * 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_vector_h__
+#define INCLUDE_vector_h__
+
+#include "git2/common.h"
+
+typedef int (*git_vector_cmp)(const void *, const void *);
+
+typedef struct git_vector {
+	unsigned int _alloc_size;
+	git_vector_cmp _cmp;
+	void **contents;
+	unsigned int 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);
+void git_vector_free(git_vector *v);
+void git_vector_clear(git_vector *v);
+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);
+
+GIT_INLINE(int) git_vector_bsearch(git_vector *v, const void *key)
+{
+	return git_vector_bsearch3(NULL, v, v->_cmp, 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);
+}
+
+GIT_INLINE(void *) git_vector_get(git_vector *v, unsigned int position)
+{
+	return (position < v->length) ? v->contents[position] : NULL;
+}
+
+GIT_INLINE(const void *) git_vector_get_const(const git_vector *v, unsigned int 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)
+{
+	return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
+}
+
+#define git_vector_foreach(v, iter, elem)	\
+	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)-- )
+
+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);
+void git_vector_pop(git_vector *v);
+void git_vector_uniq(git_vector *v);
+
+#endif
diff --git a/libgit2/src/win32/dir.c b/libgit2/src/win32/dir.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/dir.c
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+#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)
+{
+	size_t len = strlen(dir);
+
+	if (len+3 >= n)
+		return 0;
+
+	strcpy(filter, dir);
+	if (len && dir[len-1] != '/')
+		strcat(filter, "/");
+	strcat(filter, "*");
+
+	return 1;
+}
+
+git__DIR *git__opendir(const char *dir)
+{
+	char filter[4096];
+	wchar_t* filter_w = NULL;
+	git__DIR *new = NULL;
+
+	if (!dir || !init_filter(filter, sizeof(filter), dir))
+		return NULL;
+
+	new = git__malloc(sizeof(*new));
+	if (!new)
+		return NULL;
+
+	new->dir = git__strdup(dir);
+	if (!new->dir)
+		goto fail;
+
+	filter_w = gitwin_to_utf16(filter);
+	if (!filter_w)
+		goto fail;
+
+	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);
+		goto fail;
+	}
+
+	new->first = 1;
+	return new;
+
+fail:
+	git__free(new->dir);
+	git__free(new);
+	return NULL;
+}
+
+int git__readdir_ext(
+	git__DIR *d,
+	struct git__dirent *entry,
+	struct git__dirent **result,
+	int *is_dir)
+{
+	if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
+		return -1;
+
+	*result = NULL;
+
+	if (d->first)
+		d->first = 0;
+	else if (!FindNextFileW(d->h, &d->f)) {
+		if (GetLastError() == ERROR_NO_MORE_FILES)
+			return 0;
+		giterr_set(GITERR_OS, "Could not read from directory '%s'", d->dir);
+		return -1;
+	}
+
+	if (wcslen(d->f.cFileName) >= sizeof(entry->d_name))
+		return -1;
+
+	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)
+		*is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+
+	return 0;
+}
+
+struct git__dirent *git__readdir(git__DIR *d)
+{
+	struct git__dirent *result;
+	if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
+		return NULL;
+	return result;
+}
+
+void git__rewinddir(git__DIR *d)
+{
+	char filter[4096];
+	wchar_t* filter_w;
+
+	if (!d)
+		return;
+
+	if (d->h != INVALID_HANDLE_VALUE) {
+		FindClose(d->h);
+		d->h = INVALID_HANDLE_VALUE;
+		d->first = 0;
+	}
+
+	if (!init_filter(filter, sizeof(filter), d->dir) ||
+		(filter_w = gitwin_to_utf16(filter)) == NULL)
+		return;
+
+	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);
+	else
+		d->first = 1;
+}
+
+int git__closedir(git__DIR *d)
+{
+	if (!d)
+		return 0;
+
+	if (d->h != INVALID_HANDLE_VALUE) {
+		FindClose(d->h);
+		d->h = INVALID_HANDLE_VALUE;
+	}
+	git__free(d->dir);
+	d->dir = NULL;
+	git__free(d);
+	return 0;
+}
+
diff --git a/libgit2/src/win32/dir.h b/libgit2/src/win32/dir.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/dir.h
@@ -0,0 +1,42 @@
+/*
+ * 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_dir_h__
+#define INCLUDE_dir_h__
+
+#include "common.h"
+
+struct git__dirent {
+	int d_ino;
+	char d_name[261];
+};
+
+typedef struct {
+	HANDLE h;
+	WIN32_FIND_DATAW f;
+	struct git__dirent entry;
+	char *dir;
+	int first;
+} git__DIR;
+
+extern git__DIR *git__opendir(const char *);
+extern struct git__dirent *git__readdir(git__DIR *);
+extern int git__readdir_ext(
+	git__DIR *, struct git__dirent *, struct git__dirent **, int *);
+extern void git__rewinddir(git__DIR *);
+extern int git__closedir(git__DIR *);
+
+# ifndef GIT__WIN32_NO_WRAP_DIR
+#	define dirent git__dirent
+#	define DIR git__DIR
+#	define opendir	git__opendir
+#	define readdir	git__readdir
+#   define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
+#	define rewinddir git__rewinddir
+#	define closedir git__closedir
+# endif
+
+#endif /* INCLUDE_dir_h__ */
diff --git a/libgit2/src/win32/map.c b/libgit2/src/win32/map.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/map.c
@@ -0,0 +1,115 @@
+/*
+ * 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 "map.h"
+#include <errno.h>
+
+
+static DWORD get_page_size(void)
+{
+	static DWORD page_size;
+	SYSTEM_INFO sys;
+
+	if (!page_size) {
+		GetSystemInfo(&sys);
+		page_size = sys.dwAllocationGranularity;
+	}
+
+	return page_size;
+}
+
+int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
+{
+	HANDLE fh = (HANDLE)_get_osfhandle(fd);
+	DWORD page_size = get_page_size();
+	DWORD fmap_prot = 0;
+	DWORD view_prot = 0;
+	DWORD off_low = 0;
+	DWORD off_hi = 0;
+	git_off_t page_start;
+	git_off_t page_offset;
+
+	GIT_MMAP_VALIDATE(out, len, prot, flags);
+
+	out->data = NULL;
+	out->len = 0;
+	out->fmh = NULL;
+
+	if (fh == INVALID_HANDLE_VALUE) {
+		errno = EBADF;
+		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+		return -1;
+	}
+
+	if (prot & GIT_PROT_WRITE)
+		fmap_prot |= PAGE_READWRITE;
+	else if (prot & GIT_PROT_READ)
+		fmap_prot |= PAGE_READONLY;
+
+	if (prot & GIT_PROT_WRITE)
+		view_prot |= FILE_MAP_WRITE;
+	if (prot & GIT_PROT_READ)
+		view_prot |= FILE_MAP_READ;
+
+	page_start = (offset / page_size) * page_size;
+	page_offset = offset - page_start;
+
+	if (page_offset != 0) { /* offset must be multiple of page size */
+		errno = EINVAL;
+		giterr_set(GITERR_OS, "Failed to mmap. Offset must be multiple of page size");
+		return -1;
+	}
+
+	out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
+	if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
+		giterr_set(GITERR_OS, "Failed to mmap. Invalid handle value");
+		out->fmh = NULL;
+		return -1;
+	}
+
+	assert(sizeof(git_off_t) == 8);
+
+	off_low = (DWORD)(page_start);
+	off_hi = (DWORD)(page_start >> 32);
+	out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
+	if (!out->data) {
+		giterr_set(GITERR_OS, "Failed to mmap. No data written");
+		CloseHandle(out->fmh);
+		out->fmh = NULL;
+		return -1;
+	}
+	out->len = len;
+
+	return 0;
+}
+
+int p_munmap(git_map *map)
+{
+	int error = 0;
+
+	assert(map != NULL);
+
+	if (map->data) {
+		if (!UnmapViewOfFile(map->data)) {
+			giterr_set(GITERR_OS, "Failed to munmap. Could not unmap view of file");
+			error = -1;
+		}
+		map->data = NULL;
+	}
+
+	if (map->fmh) {
+		if (!CloseHandle(map->fmh)) {
+			giterr_set(GITERR_OS, "Failed to munmap. Could not close handle");
+			error = -1;
+		}
+		map->fmh = NULL;
+	}
+
+	return error;
+}
+
+
diff --git a/libgit2/src/win32/mingw-compat.h b/libgit2/src/win32/mingw-compat.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/mingw-compat.h
@@ -0,0 +1,24 @@
+/*
+ * 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_mingw_compat__
+#define INCLUDE_mingw_compat__
+
+#if defined(__MINGW32__)
+
+/* use a 64-bit file offset type */
+# define lseek _lseeki64
+# define stat _stati64
+# define fstat _fstati64
+
+/* stat: file mode type testing macros */
+# define _S_IFLNK 0120000
+# define S_IFLNK _S_IFLNK
+# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+
+#endif
+
+#endif /* INCLUDE_mingw_compat__ */
diff --git a/libgit2/src/win32/msvc-compat.h b/libgit2/src/win32/msvc-compat.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/msvc-compat.h
@@ -0,0 +1,41 @@
+/*
+ * 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_msvc_compat__
+#define INCLUDE_msvc_compat__
+
+#if defined(_MSC_VER)
+
+/* access() mode parameter #defines	*/
+#	define F_OK 0 /* existence check */
+#	define W_OK 2 /* write mode check */
+#	define R_OK 4 /* read mode check */
+
+#	define lseek _lseeki64
+#	define stat _stat64
+#	define fstat _fstat64
+
+/* stat: file mode type testing macros */
+#	define _S_IFLNK 0120000
+#	define S_IFLNK _S_IFLNK
+
+#	define S_ISDIR(m)	(((m) & _S_IFMT) == _S_IFDIR)
+#	define S_ISREG(m)	(((m) & _S_IFMT) == _S_IFREG)
+#	define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+#	define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+
+#	define mode_t unsigned short
+
+/* case-insensitive string comparison */
+#	define strcasecmp	_stricmp
+#	define strncasecmp _strnicmp
+
+/* MSVC doesn't define ssize_t at all */
+typedef SSIZE_T ssize_t;
+
+#endif
+
+#endif /* INCLUDE_msvc_compat__ */
diff --git a/libgit2/src/win32/posix.h b/libgit2/src/win32/posix.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/posix.h
@@ -0,0 +1,55 @@
+/*
+ * 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_posix__w32_h__
+#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)
+{
+	GIT_UNUSED(old);
+	GIT_UNUSED(new);
+	errno = ENOSYS;
+	return -1;
+}
+
+GIT_INLINE(int) p_mkdir(const char *path, mode_t mode)
+{
+	wchar_t* buf = gitwin_to_utf16(path);
+	int ret = _wmkdir(buf);
+
+	GIT_UNUSED(mode);
+
+	git__free(buf);
+	return ret;
+}
+
+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_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);
+extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
+extern int p_mkstemp(char *tmp_path);
+extern int p_setenv(const char* name, const char* value, int overwrite);
+extern int p_stat(const char* path, struct stat* buf);
+extern int p_chdir(const char* path);
+extern int p_chmod(const char* path, mode_t mode);
+extern int p_rmdir(const char* path);
+extern int p_access(const char* path, mode_t mode);
+extern int p_fsync(int fd);
+extern int p_open(const char *path, int flags, ...);
+extern int p_creat(const char *path, mode_t mode);
+extern int p_getcwd(char *buffer_out, size_t size);
+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);
+
+#endif
diff --git a/libgit2/src/win32/posix_w32.c b/libgit2/src/win32/posix_w32.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/posix_w32.c
@@ -0,0 +1,472 @@
+/*
+ * 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 "../posix.h"
+#include "path.h"
+#include "utf-conv.h"
+#include <errno.h>
+#include <io.h>
+#include <fcntl.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;
+}
+
+int p_fsync(int fd)
+{
+	HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+	if (fh == INVALID_HANDLE_VALUE) {
+		errno = EBADF;
+		return -1;
+	}
+
+	if (!FlushFileBuffers(fh)) {
+		DWORD code = GetLastError();
+
+		if (code == ERROR_INVALID_HANDLE)
+			errno = EINVAL;
+		else
+			errno = EIO;
+
+		return -1;
+	}
+
+	return 0;
+}
+
+GIT_INLINE(time_t) filetime_to_time_t(const FILETIME *ft)
+{
+	long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
+	winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
+	winTime /= 10000000;		 /* Nano to seconds resolution */
+	return (time_t)winTime;
+}
+
+static int do_lstat(const char *file_name, struct stat *buf)
+{
+	WIN32_FILE_ATTRIBUTE_DATA fdata;
+	wchar_t* fbuf = gitwin_to_utf16(file_name);
+	if (!fbuf)
+		return -1;
+
+	if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) {
+		int fMode = S_IREAD;
+
+		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+			fMode |= S_IFDIR;
+		else
+			fMode |= S_IFREG;
+
+		if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
+			fMode |= S_IWRITE;
+
+		if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+			fMode |= S_IFLNK;
+
+		buf->st_ino = 0;
+		buf->st_gid = 0;
+		buf->st_uid = 0;
+		buf->st_nlink = 1;
+		buf->st_mode = (mode_t)fMode;
+		buf->st_size = ((git_off_t)fdata.nFileSizeHigh << 32) + fdata.nFileSizeLow;
+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
+		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
+		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
+		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
+
+		git__free(fbuf);
+		return 0;
+	}
+
+	git__free(fbuf);
+	return -1;
+}
+
+int p_lstat(const char *file_name, struct stat *buf)
+{
+	int error;
+	size_t namelen;
+	char *alt_name;
+
+	if (do_lstat(file_name, buf) == 0)
+		return 0;
+
+	/* if file_name ended in a '/', Windows returned ENOENT;
+	 * try again without trailing slashes
+	 */
+	namelen = strlen(file_name);
+	if (namelen && file_name[namelen-1] != '/')
+		return -1;
+
+	while (namelen && file_name[namelen-1] == '/')
+		--namelen;
+
+	if (!namelen)
+		return -1;
+
+	alt_name = git__strndup(file_name, namelen);
+	if (!alt_name)
+		return -1;
+
+	error = do_lstat(alt_name, buf);
+
+	git__free(alt_name);
+	return error;
+}
+
+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* target_w;
+	int error = 0;
+
+	assert(link && target && target_len > 0);
+
+	/*
+	 * Try to load the pointer to pGetFinalPath dynamically, because
+	 * it is not available in platforms older than Vista
+	 */
+	if (pGetFinalPath == NULL) {
+		HINSTANCE library = LoadLibrary("kernel32");
+
+		if (library != NULL)
+			pGetFinalPath = (fpath_func)GetProcAddress(library, "GetFinalPathNameByHandleW");
+
+		if (pGetFinalPath == NULL) {
+			giterr_set(GITERR_OS,
+				"'GetFinalPathNameByHandleW' is not available in this platform");
+			return -1;
+		}
+	}
+
+	link_w = gitwin_to_utf16(link);
+	GITERR_CHECK_ALLOC(link_w);
+
+	hFile = CreateFileW(link_w,			// file to open
+			GENERIC_READ,			// open for reading
+			FILE_SHARE_READ,		// share for reading
+			NULL,					// default security
+			OPEN_EXISTING,			// existing file only
+			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;
+	}
+
+	target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t));
+	GITERR_CHECK_ALLOC(target_w);
+
+	dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0);
+	if (dwRet == 0 ||
+		dwRet >= target_len ||
+		!WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target,
+			(int)(target_len * sizeof(char)), NULL, NULL))
+		error = -1;
+
+	git__free(target_w);
+	CloseHandle(hFile);
+
+	if (error)
+		return error;
+
+	/* Skip first 4 characters if they are "\\?\" */
+	if (dwRet > 4 &&
+		target[0] == '\\' && target[1] == '\\' &&
+		target[2] == '?' && target[3] == '\\')
+	{
+		unsigned int offset = 4;
+		dwRet -= 4;
+
+		/* \??\UNC\ */
+		if (dwRet > 7 &&
+			target[4] == 'U' && target[5] == 'N' && target[6] == 'C')
+		{
+			offset += 2;
+			dwRet -= 2;
+			target[offset] = '\\';
+		}
+
+		memmove(target, target + offset, dwRet);
+	}
+
+	target[dwRet] = '\0';
+
+	return dwRet;
+}
+
+int p_open(const char *path, int flags, ...)
+{
+	int fd;
+	wchar_t* buf;
+	mode_t mode = 0;
+
+	buf = gitwin_to_utf16(path);
+	if (!buf)
+		return -1;
+
+	if (flags & O_CREAT)
+	{
+		va_list arg_list;
+
+		va_start(arg_list, flags);
+		mode = va_arg(arg_list, mode_t);
+		va_end(arg_list);
+	}
+
+	fd = _wopen(buf, flags | _O_BINARY, mode);
+
+	git__free(buf);
+	return fd;
+}
+
+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;
+}
+
+int p_getcwd(char *buffer_out, size_t size)
+{
+	int ret;
+	wchar_t* buf;
+
+	if ((size_t)((int)size) != size)
+		return -1;
+
+	buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size);
+	GITERR_CHECK_ALLOC(buf);
+
+	_wgetcwd(buf, (int)size);
+
+	ret = WideCharToMultiByte(
+		CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL);
+
+	git__free(buf);
+	return !ret ? -1 : 0;
+}
+
+int p_stat(const char* path, struct stat* buf)
+{
+	return do_lstat(path, buf);
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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 */
+}
+
+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));
+
+	if (!orig_path_w || !buffer_w)
+		return NULL;
+
+	ret = GetFullPathNameW(orig_path_w, GIT_PATH_MAX, buffer_w, NULL);
+	git__free(orig_path_w);
+
+	/* According to MSDN, a return value equals to zero means a failure. */
+	if (ret == 0 || ret > GIT_PATH_MAX) {
+		buffer = NULL;
+		goto done;
+	}
+
+	if (buffer == NULL) {
+		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))
+		{
+			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);
+
+		buffer = NULL;
+		errno = ENOENT;
+	}
+
+done:
+	git__free(buffer_w);
+	if (buffer)
+		git_path_mkposix(buffer);
+	return buffer;
+}
+
+int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
+{
+#ifdef _MSC_VER
+	int len;
+
+	if (count == 0 || (len = _vsnprintf(buffer, count, format, argptr)) < 0)
+		return _vscprintf(format, argptr);
+
+	return len;
+#else /* MinGW */
+	return vsnprintf(buffer, count, format, argptr);
+#endif
+}
+
+int p_snprintf(char *buffer, size_t count, const char *format, ...)
+{
+	va_list va;
+	int r;
+
+	va_start(va, format);
+	r = p_vsnprintf(buffer, count, format, va);
+	va_end(va);
+
+	return r;
+}
+
+extern int p_creat(const char *path, mode_t mode);
+
+int p_mkstemp(char *tmp_path)
+{
+#if defined(_MSC_VER)
+	if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0)
+		return -1;
+#else
+	if (_mktemp(tmp_path) == NULL)
+		return -1;
+#endif
+
+	return p_creat(tmp_path, 0744);
+}
+
+int p_setenv(const char* name, const char* value, int overwrite)
+{
+	if (overwrite != 1)
+		return -1;
+
+	return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0);
+}
+
+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;
+}
+
+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);
+
+	return ret;
+}
+
+int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
+{
+	if ((size_t)((int)length) != length)
+		return -1; /* giterr_set will be done by caller */
+
+	return recv(socket, buffer, (int)length, flags);
+}
+
+int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
+{
+	if ((size_t)((int)length) != length)
+		return -1; /* giterr_set will be done by caller */
+
+	return send(socket, buffer, (int)length, flags);
+}
diff --git a/libgit2/src/win32/pthread.c b/libgit2/src/win32/pthread.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/pthread.c
@@ -0,0 +1,68 @@
+/*
+ * 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 "pthread.h"
+
+int pthread_create(
+	pthread_t *GIT_RESTRICT thread,
+	const pthread_attr_t *GIT_RESTRICT attr,
+	void *(*start_routine)(void*),
+	void *GIT_RESTRICT arg)
+{
+	GIT_UNUSED(attr);
+	*thread = (pthread_t) CreateThread(
+		NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL);
+	return *thread ? 0 : -1;
+}
+
+int pthread_join(pthread_t thread, void **value_ptr)
+{
+	int ret;
+	ret = WaitForSingleObject(thread, INFINITE);
+	if (ret && value_ptr)
+		GetExitCodeThread(thread, (void*) value_ptr);
+	return -(!!ret);
+}
+
+int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT mutex,
+						const pthread_mutexattr_t *GIT_RESTRICT mutexattr)
+{
+	GIT_UNUSED(mutexattr);
+	InitializeCriticalSection(mutex);
+	return 0;
+}
+
+int pthread_mutex_destroy(pthread_mutex_t *mutex)
+{
+	DeleteCriticalSection(mutex);
+	return 0;
+}
+
+int pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+	EnterCriticalSection(mutex);
+	return 0;
+}
+
+int pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+	LeaveCriticalSection(mutex);
+	return 0;
+}
+
+int pthread_num_processors_np(void)
+{
+	DWORD_PTR p, s;
+	int n = 0;
+
+	if (GetProcessAffinityMask(GetCurrentProcess(), &p, &s))
+		for (; p; p >>= 1)
+			n += p&1;
+
+	return n ? n : 1;
+}
+
diff --git a/libgit2/src/win32/pthread.h b/libgit2/src/win32/pthread.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/pthread.h
@@ -0,0 +1,40 @@
+/*
+ * 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 GIT_PTHREAD_H
+#define GIT_PTHREAD_H
+
+#include "../common.h"
+
+#if defined (_MSC_VER)
+#	define GIT_RESTRICT __restrict
+#else
+#	define GIT_RESTRICT __restrict__
+#endif
+
+typedef int pthread_mutexattr_t;
+typedef int pthread_condattr_t;
+typedef int pthread_attr_t;
+typedef CRITICAL_SECTION pthread_mutex_t;
+typedef HANDLE pthread_t;
+
+#define PTHREAD_MUTEX_INITIALIZER {(void*)-1};
+
+int pthread_create(pthread_t *GIT_RESTRICT,
+					const pthread_attr_t *GIT_RESTRICT,
+					void *(*start_routine)(void*), void *__restrict);
+
+int pthread_join(pthread_t, void **);
+
+int pthread_mutex_init(pthread_mutex_t *GIT_RESTRICT, const pthread_mutexattr_t *GIT_RESTRICT);
+int pthread_mutex_destroy(pthread_mutex_t *);
+int pthread_mutex_lock(pthread_mutex_t *);
+int pthread_mutex_unlock(pthread_mutex_t *);
+
+int pthread_num_processors_np(void);
+
+#endif
diff --git a/libgit2/src/win32/utf-conv.c b/libgit2/src/win32/utf-conv.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/utf-conv.c
@@ -0,0 +1,95 @@
+/*
+ * 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 "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;
+}
+
+wchar_t* gitwin_to_utf16(const char* str)
+{
+	wchar_t* ret;
+	size_t cb;
+
+	if (!str)
+		return NULL;
+
+	cb = strlen(str) * sizeof(wchar_t);
+	if (cb == 0)
+		return (wchar_t *)git__calloc(1, sizeof(wchar_t));
+
+	/* Add space for null terminator */
+	cb += sizeof(wchar_t);
+
+	ret = (wchar_t *)git__malloc(cb);
+	if (!ret)
+		return NULL;
+
+	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;
+	}
+
+	return ret;
+}
+
+int gitwin_append_utf16(wchar_t *buffer, const char *str, size_t len)
+{
+	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;
+}
+
+char* gitwin_from_utf16(const wchar_t* str)
+{
+	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;
+
+}
diff --git a/libgit2/src/win32/utf-conv.h b/libgit2/src/win32/utf-conv.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/win32/utf-conv.h
@@ -0,0 +1,18 @@
+/*
+ * 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 <wchar.h>
+
+#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);
+
+#endif
+
diff --git a/libgit2/src/xdiff/xdiff.h b/libgit2/src/xdiff/xdiff.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xdiff.h
@@ -0,0 +1,135 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFF_H)
+#define XDIFF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* #ifdef __cplusplus */
+
+
+#define XDF_NEED_MINIMAL (1 << 1)
+#define XDF_IGNORE_WHITESPACE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 3)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 4)
+#define XDF_PATIENCE_DIFF (1 << 5)
+#define XDF_HISTOGRAM_DIFF (1 << 6)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | XDF_IGNORE_WHITESPACE_CHANGE | XDF_IGNORE_WHITESPACE_AT_EOL)
+
+#define XDL_PATCH_NORMAL '-'
+#define XDL_PATCH_REVERSE '+'
+#define XDL_PATCH_MODEMASK ((1 << 8) - 1)
+#define XDL_PATCH_IGNOREBSPACE (1 << 8)
+
+#define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_COMMON (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
+
+#define XDL_MMB_READONLY (1 << 0)
+
+#define XDL_MMF_ATOMIC (1 << 0)
+
+#define XDL_BDOP_INS 1
+#define XDL_BDOP_CPY 2
+#define XDL_BDOP_INSB 3
+
+/* merge simplification levels */
+#define XDL_MERGE_MINIMAL 0
+#define XDL_MERGE_EAGER 1
+#define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
+
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 1
+#define XDL_MERGE_FAVOR_THEIRS 2
+#define XDL_MERGE_FAVOR_UNION 3
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 1
+
+typedef struct s_mmfile {
+	char *ptr;
+	size_t size;
+} mmfile_t;
+
+typedef struct s_mmbuffer {
+	char *ptr;
+	size_t size;
+} mmbuffer_t;
+
+typedef struct s_xpparam {
+	unsigned long flags;
+} xpparam_t;
+
+typedef struct s_xdemitcb {
+	void *priv;
+	int (*outf)(void *, mmbuffer_t *, int);
+} xdemitcb_t;
+
+typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+
+typedef struct s_xdemitconf {
+	long ctxlen;
+	long interhunkctxlen;
+	unsigned long flags;
+	find_func_t find_func;
+	void *find_func_priv;
+	void (*emit_func)(void);
+} xdemitconf_t;
+
+typedef struct s_bdiffparam {
+	long bsize;
+} bdiffparam_t;
+
+
+#define xdl_malloc(x) malloc(x)
+#define xdl_free(ptr) free(ptr)
+#define xdl_realloc(ptr,x) realloc(ptr,x)
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size);
+long xdl_mmfile_size(mmfile_t *mmf);
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);
+
+typedef struct s_xmparam {
+	xpparam_t xpp;
+	int marker_size;
+	int level;
+	int favor;
+	int style;
+	const char *ancestor;	/* label for orig */
+	const char *file1;	/* label for mf1 */
+	const char *file2;	/* label for mf2 */
+} xmparam_t;
+
+#define DEFAULT_CONFLICT_MARKER_SIZE 7
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+		xmparam_t const *xmp, mmbuffer_t *result);
+
+#ifdef __cplusplus
+}
+#endif /* #ifdef __cplusplus */
+
+#endif /* #if !defined(XDIFF_H) */
diff --git a/libgit2/src/xdiff/xdiffi.c b/libgit2/src/xdiff/xdiffi.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xdiffi.c
@@ -0,0 +1,572 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003	Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+#define XDL_MAX_COST_MIN 256
+#define XDL_HEUR_MIN_COST 256
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
+#define XDL_SNAKE_CNT 20
+#define XDL_K_HEUR 4
+
+
+
+typedef struct s_xdpsplit {
+	long i1, i2;
+	int min_lo, min_hi;
+} xdpsplit_t;
+
+
+
+
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+		      unsigned long const *ha2, long off2, long lim2,
+		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+		      xdalgoenv_t *xenv);
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
+
+
+
+
+
+/*
+ * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
+ * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
+ * the forward diagonal starting from (off1, off2) and the backward diagonal
+ * starting from (lim1, lim2). If the K values on the same diagonal crosses
+ * returns the furthest point of reach. We might end up having to expensive
+ * cases using this algorithm is full, so a little bit of heuristic is needed
+ * to cut the search and to return a suboptimal point.
+ */
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+		      unsigned long const *ha2, long off2, long lim2,
+		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+		      xdalgoenv_t *xenv) {
+	long dmin = off1 - lim2, dmax = lim1 - off2;
+	long fmid = off1 - off2, bmid = lim1 - lim2;
+	long odd = (fmid - bmid) & 1;
+	long fmin = fmid, fmax = fmid;
+	long bmin = bmid, bmax = bmid;
+	long ec, d, i1, i2, prev1, best, dd, v, k;
+
+	/*
+	 * Set initial diagonal values for both forward and backward path.
+	 */
+	kvdf[fmid] = off1;
+	kvdb[bmid] = lim1;
+
+	for (ec = 1;; ec++) {
+		int got_snake = 0;
+
+		/*
+		 * We need to extent the diagonal "domain" by one. If the next
+		 * values exits the box boundaries we need to change it in the
+		 * opposite direction because (max - min) must be a power of two.
+		 * Also we initialize the external K value to -1 so that we can
+		 * avoid extra conditions check inside the core loop.
+		 */
+		if (fmin > dmin)
+			kvdf[--fmin - 1] = -1;
+		else
+			++fmin;
+		if (fmax < dmax)
+			kvdf[++fmax + 1] = -1;
+		else
+			--fmax;
+
+		for (d = fmax; d >= fmin; d -= 2) {
+			if (kvdf[d - 1] >= kvdf[d + 1])
+				i1 = kvdf[d - 1] + 1;
+			else
+				i1 = kvdf[d + 1];
+			prev1 = i1;
+			i2 = i1 - d;
+			for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
+			if (i1 - prev1 > xenv->snake_cnt)
+				got_snake = 1;
+			kvdf[d] = i1;
+			if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
+				spl->i1 = i1;
+				spl->i2 = i2;
+				spl->min_lo = spl->min_hi = 1;
+				return ec;
+			}
+		}
+
+		/*
+		 * We need to extent the diagonal "domain" by one. If the next
+		 * values exits the box boundaries we need to change it in the
+		 * opposite direction because (max - min) must be a power of two.
+		 * Also we initialize the external K value to -1 so that we can
+		 * avoid extra conditions check inside the core loop.
+		 */
+		if (bmin > dmin)
+			kvdb[--bmin - 1] = XDL_LINE_MAX;
+		else
+			++bmin;
+		if (bmax < dmax)
+			kvdb[++bmax + 1] = XDL_LINE_MAX;
+		else
+			--bmax;
+
+		for (d = bmax; d >= bmin; d -= 2) {
+			if (kvdb[d - 1] < kvdb[d + 1])
+				i1 = kvdb[d - 1];
+			else
+				i1 = kvdb[d + 1] - 1;
+			prev1 = i1;
+			i2 = i1 - d;
+			for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
+			if (prev1 - i1 > xenv->snake_cnt)
+				got_snake = 1;
+			kvdb[d] = i1;
+			if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
+				spl->i1 = i1;
+				spl->i2 = i2;
+				spl->min_lo = spl->min_hi = 1;
+				return ec;
+			}
+		}
+
+		if (need_min)
+			continue;
+
+		/*
+		 * If the edit cost is above the heuristic trigger and if
+		 * we got a good snake, we sample current diagonals to see
+		 * if some of the, have reached an "interesting" path. Our
+		 * measure is a function of the distance from the diagonal
+		 * corner (i1 + i2) penalized with the distance from the
+		 * mid diagonal itself. If this value is above the current
+		 * edit cost times a magic factor (XDL_K_HEUR) we consider
+		 * it interesting.
+		 */
+		if (got_snake && ec > xenv->heur_min) {
+			for (best = 0, d = fmax; d >= fmin; d -= 2) {
+				dd = d > fmid ? d - fmid: fmid - d;
+				i1 = kvdf[d];
+				i2 = i1 - d;
+				v = (i1 - off1) + (i2 - off2) - dd;
+
+				if (v > XDL_K_HEUR * ec && v > best &&
+				    off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
+				    off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
+					for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
+						if (k == xenv->snake_cnt) {
+							best = v;
+							spl->i1 = i1;
+							spl->i2 = i2;
+							break;
+						}
+				}
+			}
+			if (best > 0) {
+				spl->min_lo = 1;
+				spl->min_hi = 0;
+				return ec;
+			}
+
+			for (best = 0, d = bmax; d >= bmin; d -= 2) {
+				dd = d > bmid ? d - bmid: bmid - d;
+				i1 = kvdb[d];
+				i2 = i1 - d;
+				v = (lim1 - i1) + (lim2 - i2) - dd;
+
+				if (v > XDL_K_HEUR * ec && v > best &&
+				    off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
+				    off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
+					for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
+						if (k == xenv->snake_cnt - 1) {
+							best = v;
+							spl->i1 = i1;
+							spl->i2 = i2;
+							break;
+						}
+				}
+			}
+			if (best > 0) {
+				spl->min_lo = 0;
+				spl->min_hi = 1;
+				return ec;
+			}
+		}
+
+		/*
+		 * Enough is enough. We spent too much time here and now we collect
+		 * the furthest reaching path using the (i1 + i2) measure.
+		 */
+		if (ec >= xenv->mxcost) {
+			long fbest, fbest1, bbest, bbest1;
+
+			fbest = fbest1 = -1;
+			for (d = fmax; d >= fmin; d -= 2) {
+				i1 = XDL_MIN(kvdf[d], lim1);
+				i2 = i1 - d;
+				if (lim2 < i2)
+					i1 = lim2 + d, i2 = lim2;
+				if (fbest < i1 + i2) {
+					fbest = i1 + i2;
+					fbest1 = i1;
+				}
+			}
+
+			bbest = bbest1 = XDL_LINE_MAX;
+			for (d = bmax; d >= bmin; d -= 2) {
+				i1 = XDL_MAX(off1, kvdb[d]);
+				i2 = i1 - d;
+				if (i2 < off2)
+					i1 = off2 + d, i2 = off2;
+				if (i1 + i2 < bbest) {
+					bbest = i1 + i2;
+					bbest1 = i1;
+				}
+			}
+
+			if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
+				spl->i1 = fbest1;
+				spl->i2 = fbest - fbest1;
+				spl->min_lo = 1;
+				spl->min_hi = 0;
+			} else {
+				spl->i1 = bbest1;
+				spl->i2 = bbest - bbest1;
+				spl->min_lo = 0;
+				spl->min_hi = 1;
+			}
+			return ec;
+		}
+	}
+}
+
+
+/*
+ * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling
+ * the box splitting function. Note that the real job (marking changed lines)
+ * is done in the two boundary reaching checks.
+ */
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+		 diffdata_t *dd2, long off2, long lim2,
+		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
+	unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
+
+	/*
+	 * Shrink the box by walking through each diagonal snake (SW and NE).
+	 */
+	for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
+	for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
+
+	/*
+	 * If one dimension is empty, then all records on the other one must
+	 * be obviously changed.
+	 */
+	if (off1 == lim1) {
+		char *rchg2 = dd2->rchg;
+		long *rindex2 = dd2->rindex;
+
+		for (; off2 < lim2; off2++)
+			rchg2[rindex2[off2]] = 1;
+	} else if (off2 == lim2) {
+		char *rchg1 = dd1->rchg;
+		long *rindex1 = dd1->rindex;
+
+		for (; off1 < lim1; off1++)
+			rchg1[rindex1[off1]] = 1;
+	} else {
+		xdpsplit_t spl;
+		spl.i1 = spl.i2 = 0;
+
+		/*
+		 * Divide ...
+		 */
+		if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+			      need_min, &spl, xenv) < 0) {
+
+			return -1;
+		}
+
+		/*
+		 * ... et Impera.
+		 */
+		if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
+				 kvdf, kvdb, spl.min_lo, xenv) < 0 ||
+		    xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
+				 kvdf, kvdb, spl.min_hi, xenv) < 0) {
+
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *xe) {
+	long ndiags;
+	long *kvd, *kvdf, *kvdb;
+	xdalgoenv_t xenv;
+	diffdata_t dd1, dd2;
+
+	if (xpp->flags & XDF_PATIENCE_DIFF)
+		return xdl_do_patience_diff(mf1, mf2, xpp, xe);
+
+	if (xpp->flags & XDF_HISTOGRAM_DIFF)
+		return xdl_do_histogram_diff(mf1, mf2, xpp, xe);
+
+	if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) {
+
+		return -1;
+	}
+
+	/*
+	 * Allocate and setup K vectors to be used by the differential algorithm.
+	 * One is to store the forward path and one to store the backward path.
+	 */
+	ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
+	if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
+
+		xdl_free_env(xe);
+		return -1;
+	}
+	kvdf = kvd;
+	kvdb = kvdf + ndiags;
+	kvdf += xe->xdf2.nreff + 1;
+	kvdb += xe->xdf2.nreff + 1;
+
+	xenv.mxcost = xdl_bogosqrt(ndiags);
+	if (xenv.mxcost < XDL_MAX_COST_MIN)
+		xenv.mxcost = XDL_MAX_COST_MIN;
+	xenv.snake_cnt = XDL_SNAKE_CNT;
+	xenv.heur_min = XDL_HEUR_MIN_COST;
+
+	dd1.nrec = xe->xdf1.nreff;
+	dd1.ha = xe->xdf1.ha;
+	dd1.rchg = xe->xdf1.rchg;
+	dd1.rindex = xe->xdf1.rindex;
+	dd2.nrec = xe->xdf2.nreff;
+	dd2.ha = xe->xdf2.ha;
+	dd2.rchg = xe->xdf2.rchg;
+	dd2.rindex = xe->xdf2.rindex;
+
+	if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+			 kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) {
+
+		xdl_free(kvd);
+		xdl_free_env(xe);
+		return -1;
+	}
+
+	xdl_free(kvd);
+
+	return 0;
+}
+
+
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
+	xdchange_t *xch;
+
+	if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
+		return NULL;
+
+	xch->next = xscr;
+	xch->i1 = i1;
+	xch->i2 = i2;
+	xch->chg1 = chg1;
+	xch->chg2 = chg2;
+
+	return xch;
+}
+
+
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+	long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
+	char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
+	xrecord_t **recs = xdf->recs;
+
+	/*
+	 * This is the same of what GNU diff does. Move back and forward
+	 * change groups for a consistent and pretty diff output. This also
+	 * helps in finding joinable change groups and reduce the diff size.
+	 */
+	for (ix = ixo = 0;;) {
+		/*
+		 * Find the first changed line in the to-be-compacted file.
+		 * We need to keep track of both indexes, so if we find a
+		 * changed lines group on the other file, while scanning the
+		 * to-be-compacted file, we need to skip it properly. Note
+		 * that loops that are testing for changed lines on rchg* do
+		 * not need index bounding since the array is prepared with
+		 * a zero at position -1 and N.
+		 */
+		for (; ix < nrec && !rchg[ix]; ix++)
+			while (rchgo[ixo++]);
+		if (ix == nrec)
+			break;
+
+		/*
+		 * Record the start of a changed-group in the to-be-compacted file
+		 * and find the end of it, on both to-be-compacted and other file
+		 * indexes (ix and ixo).
+		 */
+		ixs = ix;
+		for (ix++; rchg[ix]; ix++);
+		for (; rchgo[ixo]; ixo++);
+
+		do {
+			grpsiz = ix - ixs;
+
+			/*
+			 * If the line before the current change group, is equal to
+			 * the last line of the current change group, shift backward
+			 * the group.
+			 */
+			while (ixs > 0 && recs[ixs - 1]->ha == recs[ix - 1]->ha &&
+			       xdl_recmatch(recs[ixs - 1]->ptr, recs[ixs - 1]->size, recs[ix - 1]->ptr, recs[ix - 1]->size, flags)) {
+				rchg[--ixs] = 1;
+				rchg[--ix] = 0;
+
+				/*
+				 * This change might have joined two change groups,
+				 * so we try to take this scenario in account by moving
+				 * the start index accordingly (and so the other-file
+				 * end-of-group index).
+				 */
+				for (; rchg[ixs - 1]; ixs--);
+				while (rchgo[--ixo]);
+			}
+
+			/*
+			 * Record the end-of-group position in case we are matched
+			 * with a group of changes in the other file (that is, the
+			 * change record before the end-of-group index in the other
+			 * file is set).
+			 */
+			ixref = rchgo[ixo - 1] ? ix: nrec;
+
+			/*
+			 * If the first line of the current change group, is equal to
+			 * the line next of the current change group, shift forward
+			 * the group.
+			 */
+			while (ix < nrec && recs[ixs]->ha == recs[ix]->ha &&
+			       xdl_recmatch(recs[ixs]->ptr, recs[ixs]->size, recs[ix]->ptr, recs[ix]->size, flags)) {
+				rchg[ixs++] = 0;
+				rchg[ix++] = 1;
+
+				/*
+				 * This change might have joined two change groups,
+				 * so we try to take this scenario in account by moving
+				 * the start index accordingly (and so the other-file
+				 * end-of-group index). Keep tracking the reference
+				 * index in case we are shifting together with a
+				 * corresponding group of changes in the other file.
+				 */
+				for (; rchg[ix]; ix++);
+				while (rchgo[++ixo])
+					ixref = ix;
+			}
+		} while (grpsiz != ix - ixs);
+
+		/*
+		 * Try to move back the possibly merged group of changes, to match
+		 * the recorded postion in the other file.
+		 */
+		while (ixref < ix) {
+			rchg[--ixs] = 1;
+			rchg[--ix] = 0;
+			while (rchgo[--ixo]);
+		}
+	}
+
+	return 0;
+}
+
+
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
+	xdchange_t *cscr = NULL, *xch;
+	char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
+	long i1, i2, l1, l2;
+
+	/*
+	 * Trivial. Collects "groups" of changes and creates an edit script.
+	 */
+	for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
+		if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
+			for (l1 = i1; rchg1[i1 - 1]; i1--);
+			for (l2 = i2; rchg2[i2 - 1]; i2--);
+
+			if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
+				xdl_free_script(cscr);
+				return -1;
+			}
+			cscr = xch;
+		}
+
+	*xscr = cscr;
+
+	return 0;
+}
+
+
+void xdl_free_script(xdchange_t *xscr) {
+	xdchange_t *xch;
+
+	while ((xch = xscr) != NULL) {
+		xscr = xscr->next;
+		xdl_free(xch);
+	}
+}
+
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+	     xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
+	xdchange_t *xscr;
+	xdfenv_t xe;
+	emit_func_t ef = xecfg->emit_func ?
+		(emit_func_t)xecfg->emit_func : xdl_emit_diff;
+
+	if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
+
+		return -1;
+	}
+	if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+	    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+	    xdl_build_script(&xe, &xscr) < 0) {
+
+		xdl_free_env(&xe);
+		return -1;
+	}
+	if (xscr) {
+		if (ef(&xe, xscr, ecb, xecfg) < 0) {
+
+			xdl_free_script(xscr);
+			xdl_free_env(&xe);
+			return -1;
+		}
+		xdl_free_script(xscr);
+	}
+	xdl_free_env(&xe);
+
+	return 0;
+}
diff --git a/libgit2/src/xdiff/xdiffi.h b/libgit2/src/xdiff/xdiffi.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xdiffi.h
@@ -0,0 +1,63 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFFI_H)
+#define XDIFFI_H
+
+
+typedef struct s_diffdata {
+	long nrec;
+	unsigned long const *ha;
+	long *rindex;
+	char *rchg;
+} diffdata_t;
+
+typedef struct s_xdalgoenv {
+	long mxcost;
+	long snake_cnt;
+	long heur_min;
+} xdalgoenv_t;
+
+typedef struct s_xdchange {
+	struct s_xdchange *next;
+	long i1, i2;
+	long chg1, chg2;
+} xdchange_t;
+
+
+
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+		 diffdata_t *dd2, long off2, long lim2,
+		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *xe);
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
+void xdl_free_script(xdchange_t *xscr);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+		  xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *env);
+int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		xdfenv_t *env);
+
+#endif /* #if !defined(XDIFFI_H) */
diff --git a/libgit2/src/xdiff/xemit.c b/libgit2/src/xdiff/xemit.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xemit.c
@@ -0,0 +1,253 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003	Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec);
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb);
+
+
+
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
+
+	*rec = xdf->recs[ri]->ptr;
+
+	return xdf->recs[ri]->size;
+}
+
+
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
+	long size, psize = (long)strlen(pre);
+	char const *rec;
+
+	size = xdl_get_rec(xdf, ri, &rec);
+	if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
+
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/*
+ * Starting at the passed change atom, find the latest change atom to be included
+ * inside the differential hunk according to the specified configuration.
+ */
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
+	xdchange_t *xch, *xchp;
+	long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+
+	for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
+		if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
+			break;
+
+	return xchp;
+}
+
+
+static long def_ff(const char *rec, long len, char *buf, long sz, void *priv)
+{
+	(void)priv;
+
+	if (len > 0 &&
+			(isalpha((unsigned char)*rec) || /* identifier? */
+			 *rec == '_' ||	/* also identifier? */
+			 *rec == '$')) { /* identifiers from VMS and other esoterico */
+		if (len > sz)
+			len = sz;
+		while (0 < len && isspace((unsigned char)rec[len - 1]))
+			len--;
+		memcpy(buf, rec, len);
+		return len;
+	}
+	return -1;
+}
+
+static int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+                           xdemitconf_t const *xecfg) {
+	xdfile_t *xdf = &xe->xdf2;
+	const char *rchg = xdf->rchg;
+	long ix;
+
+	(void)xscr;
+	(void)xecfg;
+
+	for (ix = 0; ix < xdf->nrec; ix++) {
+		if (rchg[ix])
+			continue;
+		if (xdl_emit_record(xdf, ix, "", ecb))
+			return -1;
+	}
+	return 0;
+}
+
+struct func_line {
+	long len;
+	char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+			  struct func_line *func_line, long start, long limit)
+{
+	find_func_t ff = xecfg->find_func ? xecfg->find_func : def_ff;
+	long l, size, step = (start > limit) ? -1 : 1;
+	char *buf, dummy[1];
+
+	buf = func_line ? func_line->buf : dummy;
+	size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+	for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+		const char *rec;
+		long reclen = xdl_get_rec(&xe->xdf1, l, &rec);
+		long len = ff(rec, reclen, buf, size, xecfg->find_func_priv);
+		if (len >= 0) {
+			if (func_line)
+				func_line->len = len;
+			return l;
+		}
+	}
+	return -1;
+}
+
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+		  xdemitconf_t const *xecfg) {
+	long s1, s2, e1, e2, lctx;
+	xdchange_t *xch, *xche;
+	long funclineprev = -1;
+	struct func_line func_line = { 0 };
+
+	if (xecfg->flags & XDL_EMIT_COMMON)
+		return xdl_emit_common(xe, xscr, ecb, xecfg);
+
+	for (xch = xscr; xch; xch = xche->next) {
+		xche = xdl_get_hunk(xch, xecfg);
+
+		s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+		s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+
+		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+			long fs1 = get_func_line(xe, xecfg, NULL, xch->i1, -1);
+			if (fs1 < 0)
+				fs1 = 0;
+			if (fs1 < s1) {
+				s2 -= s1 - fs1;
+				s1 = fs1;
+			}
+		}
+
+ again:
+		lctx = xecfg->ctxlen;
+		lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
+		lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
+
+		e1 = xche->i1 + xche->chg1 + lctx;
+		e2 = xche->i2 + xche->chg2 + lctx;
+
+		if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+			long fe1 = get_func_line(xe, xecfg, NULL,
+						 xche->i1 + xche->chg1,
+						 xe->xdf1.nrec);
+			if (fe1 < 0)
+				fe1 = xe->xdf1.nrec;
+			if (fe1 > e1) {
+				e2 += fe1 - e1;
+				e1 = fe1;
+			}
+
+			/*
+			 * Overlap with next change?  Then include it
+			 * in the current hunk and start over to find
+			 * its new end.
+			 */
+			if (xche->next) {
+				long l = xche->next->i1;
+				if (l <= e1 ||
+				    get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+					xche = xche->next;
+					goto again;
+				}
+			}
+		}
+
+		/*
+		 * Emit current hunk header.
+		 */
+
+		if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
+			get_func_line(xe, xecfg, &func_line,
+				      s1 - 1, funclineprev);
+			funclineprev = s1 - 1;
+		}
+		if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
+				      func_line.buf, func_line.len, ecb) < 0)
+			return -1;
+
+		/*
+		 * Emit pre-context.
+		 */
+		for (; s2 < xch->i2; s2++)
+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+				return -1;
+
+		for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
+			/*
+			 * Merge previous with current change atom.
+			 */
+			for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
+				if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+					return -1;
+
+			/*
+			 * Removes lines from the first file.
+			 */
+			for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
+				if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
+					return -1;
+
+			/*
+			 * Adds lines from the second file.
+			 */
+			for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
+				if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
+					return -1;
+
+			if (xch == xche)
+				break;
+			s1 = xch->i1 + xch->chg1;
+			s2 = xch->i2 + xch->chg2;
+		}
+
+		/*
+		 * Emit post-context.
+		 */
+		for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
+			if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+				return -1;
+	}
+
+	return 0;
+}
diff --git a/libgit2/src/xdiff/xemit.h b/libgit2/src/xdiff/xemit.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xemit.h
@@ -0,0 +1,36 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XEMIT_H)
+#define XEMIT_H
+
+
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+			   xdemitconf_t const *xecfg);
+
+xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+		  xdemitconf_t const *xecfg);
+
+
+
+#endif /* #if !defined(XEMIT_H) */
diff --git a/libgit2/src/xdiff/xhistogram.c b/libgit2/src/xdiff/xhistogram.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xhistogram.c
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * 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.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. 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 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.
+ */
+
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+#define MAX_PTR	UINT_MAX
+#define MAX_CNT	UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+	struct record {
+		unsigned int ptr, cnt;
+		struct record *next;
+	} **records, /* an ocurrence */
+	  **line_map; /* map of line to record chain */
+	chastore_t rcha;
+	unsigned int *next_ptrs;
+	unsigned int table_bits,
+		     records_size,
+		     line_map_size;
+
+	unsigned int max_chain_length,
+		     key_shift,
+		     ptr_shift;
+
+	unsigned int cnt,
+		     has_common;
+
+	xdfenv_t *env;
+	xpparam_t const *xpp;
+};
+
+struct region {
+	unsigned int begin1, end1;
+	unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+	(index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+	((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+	(env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xpparam_t const *xpp,
+	xrecord_t *r1, xrecord_t *r2)
+{
+	return r1->ha == r2->ha &&
+		xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size,
+			    xpp->flags);
+}
+
+#define CMP_ENV(xpp, env, s1, l1, s2, l2) \
+	(cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2)))
+
+#define CMP(i, s1, l1, s2, l2) \
+	(cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+	XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, unsigned int line1, unsigned int count1)
+{
+	unsigned int ptr;
+	unsigned int tbl_idx;
+	unsigned int chain_len;
+	struct record **rec_chain, *rec;
+
+	for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+		tbl_idx = TABLE_HASH(index, 1, ptr);
+		rec_chain = index->records + tbl_idx;
+		rec = *rec_chain;
+
+		chain_len = 0;
+		while (rec) {
+			if (CMP(index, 1, rec->ptr, 1, ptr)) {
+				/*
+				 * ptr is identical to another element. Insert
+				 * it onto the front of the existing element
+				 * chain.
+				 */
+				NEXT_PTR(index, ptr) = rec->ptr;
+				rec->ptr = ptr;
+				/* cap rec->cnt at MAX_CNT */
+				rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+				LINE_MAP(index, ptr) = rec;
+				goto continue_scan;
+			}
+
+			rec = rec->next;
+			chain_len++;
+		}
+
+		if (chain_len == index->max_chain_length)
+			return -1;
+
+		/*
+		 * This is the first time we have ever seen this particular
+		 * element in the sequence. Construct a new chain for it.
+		 */
+		if (!(rec = xdl_cha_alloc(&index->rcha)))
+			return -1;
+		rec->ptr = ptr;
+		rec->cnt = 1;
+		rec->next = *rec_chain;
+		*rec_chain = rec;
+		LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+		; /* no op */
+	}
+
+	return 0;
+}
+
+static int try_lcs(
+	struct histindex *index, struct region *lcs, unsigned int b_ptr,
+	unsigned int line1, unsigned int count1,
+	unsigned int line2, unsigned int count2)
+{
+	unsigned int b_next = b_ptr + 1;
+	struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+	unsigned int as, ae, bs, be, np, rc;
+	int should_break;
+
+	for (; rec; rec = rec->next) {
+		if (rec->cnt > index->cnt) {
+			if (!index->has_common)
+				index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+			continue;
+		}
+
+		as = rec->ptr;
+		if (!CMP(index, 1, as, 2, b_ptr))
+			continue;
+
+		index->has_common = 1;
+		for (;;) {
+			should_break = 0;
+			np = NEXT_PTR(index, as);
+			bs = b_ptr;
+			ae = as;
+			be = bs;
+			rc = rec->cnt;
+
+			while (line1 < as && line2 < bs
+				&& CMP(index, 1, as - 1, 2, bs - 1)) {
+				as--;
+				bs--;
+				if (1 < rc)
+					rc = XDL_MIN(rc, CNT(index, as));
+			}
+			while (ae < LINE_END(1) && be < LINE_END(2)
+				&& CMP(index, 1, ae + 1, 2, be + 1)) {
+				ae++;
+				be++;
+				if (1 < rc)
+					rc = XDL_MIN(rc, CNT(index, ae));
+			}
+
+			if (b_next <= be)
+				b_next = be + 1;
+			if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+				lcs->begin1 = as;
+				lcs->begin2 = bs;
+				lcs->end1 = ae;
+				lcs->end2 = be;
+				index->cnt = rc;
+			}
+
+			if (np == 0)
+				break;
+
+			while (np <= ae) {
+				np = NEXT_PTR(index, np);
+				if (np == 0) {
+					should_break = 1;
+					break;
+				}
+			}
+
+			if (should_break)
+				break;
+
+			as = np;
+		}
+	}
+	return b_next;
+}
+
+static int find_lcs(
+	struct histindex *index, struct region *lcs,
+	unsigned int line1, unsigned int count1,
+	unsigned int line2, unsigned int count2)
+{
+	unsigned int b_ptr;
+
+	if (scanA(index, line1, count1))
+		return -1;
+
+	index->cnt = index->max_chain_length + 1;
+
+	for (b_ptr = line2; b_ptr <= LINE_END(2); )
+		b_ptr = try_lcs(index, lcs, b_ptr, line1, count1, line2, count2);
+
+	return index->has_common && index->max_chain_length < index->cnt;
+}
+
+static int fall_back_to_classic_diff(struct histindex *index,
+		int line1, int count1, int line2, int count2)
+{
+	xpparam_t xpp;
+	xpp.flags = index->xpp->flags & ~XDF_HISTOGRAM_DIFF;
+
+	return xdl_fall_back_diff(index->env, &xpp,
+				  line1, count1, line2, count2);
+}
+
+static int histogram_diff(
+	xpparam_t const *xpp, xdfenv_t *env,
+	unsigned int line1, unsigned int count1,
+	unsigned int line2, unsigned int count2)
+{
+	struct histindex index;
+	struct region lcs;
+	unsigned int sz;
+	int result = -1;
+
+	if (count1 <= 0 && count2 <= 0)
+		return 0;
+
+	if (LINE_END(1) >= MAX_PTR)
+		return -1;
+
+	if (!count1) {
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		return 0;
+	} else if (!count2) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		return 0;
+	}
+
+	memset(&index, 0, sizeof(index));
+
+	index.env = env;
+	index.xpp = xpp;
+
+	index.records = NULL;
+	index.line_map = NULL;
+	/* in case of early xdl_cha_free() */
+	index.rcha.head = NULL;
+
+	index.table_bits = xdl_hashbits(count1);
+	sz = index.records_size = 1 << index.table_bits;
+	sz *= sizeof(struct record *);
+	if (!(index.records = (struct record **) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.records, 0, sz);
+
+	sz = index.line_map_size = count1;
+	sz *= sizeof(struct record *);
+	if (!(index.line_map = (struct record **) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.line_map, 0, sz);
+
+	sz = index.line_map_size;
+	sz *= sizeof(unsigned int);
+	if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz)))
+		goto cleanup;
+	memset(index.next_ptrs, 0, sz);
+
+	/* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+	if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+		goto cleanup;
+
+	index.ptr_shift = line1;
+	index.max_chain_length = 64;
+
+	memset(&lcs, 0, sizeof(lcs));
+	if (find_lcs(&index, &lcs, line1, count1, line2, count2))
+		result = fall_back_to_classic_diff(&index, line1, count1, line2, count2);
+	else {
+		if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+			while (count1--)
+				env->xdf1.rchg[line1++ - 1] = 1;
+			while (count2--)
+				env->xdf2.rchg[line2++ - 1] = 1;
+			result = 0;
+		} else {
+			result = histogram_diff(xpp, env,
+						line1, lcs.begin1 - line1,
+						line2, lcs.begin2 - line2);
+			if (result)
+				goto cleanup;
+			result = histogram_diff(xpp, env,
+						lcs.end1 + 1, LINE_END(1) - lcs.end1,
+						lcs.end2 + 1, LINE_END(2) - lcs.end2);
+			if (result)
+				goto cleanup;
+		}
+	}
+
+cleanup:
+	xdl_free(index.records);
+	xdl_free(index.line_map);
+	xdl_free(index.next_ptrs);
+	xdl_cha_free(&index.rcha);
+
+	return result;
+}
+
+int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2,
+	xpparam_t const *xpp, xdfenv_t *env)
+{
+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+		return -1;
+
+	return histogram_diff(xpp, env,
+		env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+		env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
diff --git a/libgit2/src/xdiff/xinclude.h b/libgit2/src/xdiff/xinclude.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xinclude.h
@@ -0,0 +1,46 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XINCLUDE_H)
+#define XINCLUDE_H
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifdef _WIN32
+#else
+#include <unistd.h>
+#endif
+
+#include "xmacros.h"
+#include "xdiff.h"
+#include "xtypes.h"
+#include "xutils.h"
+#include "xprepare.h"
+#include "xdiffi.h"
+#include "xemit.h"
+
+
+#endif /* #if !defined(XINCLUDE_H) */
diff --git a/libgit2/src/xdiff/xmacros.h b/libgit2/src/xdiff/xmacros.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xmacros.h
@@ -0,0 +1,54 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XMACROS_H)
+#define XMACROS_H
+
+
+
+
+#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
+#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
+#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
+#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
+#define XDL_ADDBITS(v,b)	((v) + ((v) >> (b)))
+#define XDL_MASKBITS(b)		((1UL << (b)) - 1)
+#define XDL_HASHLONG(v,b)	(XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
+#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0)
+#define XDL_LE32_PUT(p, v) \
+do { \
+	unsigned char *__p = (unsigned char *) (p); \
+	*__p++ = (unsigned char) (v); \
+	*__p++ = (unsigned char) ((v) >> 8); \
+	*__p++ = (unsigned char) ((v) >> 16); \
+	*__p = (unsigned char) ((v) >> 24); \
+} while (0)
+#define XDL_LE32_GET(p, v) \
+do { \
+	unsigned char const *__p = (unsigned char const *) (p); \
+	(v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
+		((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
+} while (0)
+
+
+#endif /* #if !defined(XMACROS_H) */
diff --git a/libgit2/src/xdiff/xmerge.c b/libgit2/src/xdiff/xmerge.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xmerge.c
@@ -0,0 +1,619 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+typedef struct s_xdmerge {
+	struct s_xdmerge *next;
+	/*
+	 * 0 = conflict,
+	 * 1 = no conflict, take first,
+	 * 2 = no conflict, take second.
+	 * 3 = no conflict, take both.
+	 */
+	int mode;
+	/*
+	 * These point at the respective postimages.  E.g. <i1,chg1> is
+	 * how side #1 wants to change the common ancestor; if there is no
+	 * overlap, lines before i1 in the postimage of side #1 appear
+	 * in the merge result as a region touched by neither side.
+	 */
+	long i1, i2;
+	long chg1, chg2;
+	/*
+	 * These point at the preimage; of course there is just one
+	 * preimage, that is from the shared common ancestor.
+	 */
+	long i0;
+	long chg0;
+} xdmerge_t;
+
+static int xdl_append_merge(xdmerge_t **merge, int mode,
+			    long i0, long chg0,
+			    long i1, long chg1,
+			    long i2, long chg2)
+{
+	xdmerge_t *m = *merge;
+	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+		if (mode != m->mode)
+			m->mode = 0;
+		m->chg0 = i0 + chg0 - m->i0;
+		m->chg1 = i1 + chg1 - m->i1;
+		m->chg2 = i2 + chg2 - m->i2;
+	} else {
+		m = xdl_malloc(sizeof(xdmerge_t));
+		if (!m)
+			return -1;
+		m->next = NULL;
+		m->mode = mode;
+		m->i0 = i0;
+		m->chg0 = chg0;
+		m->i1 = i1;
+		m->chg1 = chg1;
+		m->i2 = i2;
+		m->chg2 = chg2;
+		if (*merge)
+			(*merge)->next = m;
+		*merge = m;
+	}
+	return 0;
+}
+
+static int xdl_cleanup_merge(xdmerge_t *c)
+{
+	int count = 0;
+	xdmerge_t *next_c;
+
+	/* were there conflicts? */
+	for (; c; c = next_c) {
+		if (c->mode == 0)
+			count++;
+		next_c = c->next;
+		free(c);
+	}
+	return count;
+}
+
+static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
+		int line_count, long flags)
+{
+	int i;
+	xrecord_t **rec1 = xe1->xdf2.recs + i1;
+	xrecord_t **rec2 = xe2->xdf2.recs + i2;
+
+	for (i = 0; i < line_count; i++) {
+		int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
+			rec2[i]->ptr, rec2[i]->size, flags);
+		if (!result)
+			return -1;
+	}
+	return 0;
+}
+
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	xrecord_t **recs;
+	int size = 0;
+
+	recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
+	if (count < 1)
+		return 0;
+
+	for (i = 0; i < count; size += recs[i++]->size)
+		if (dest)
+			memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+	if (add_nl) {
+		i = recs[count - 1]->size;
+		if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+			if (dest)
+				dest[size] = '\n';
+			size++;
+		}
+	}
+	return size;
+}
+
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+}
+
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+			      xdfenv_t *xe2, const char *name2,
+			      const char *name3,
+			      int size, int i, int style,
+			      xdmerge_t *m, char *dest, int marker_size)
+{
+	int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
+	int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
+	int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
+
+	if (marker_size <= 0)
+		marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+
+	/* Before conflicting part */
+	size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+			      dest ? dest + size : NULL);
+
+	if (!dest) {
+		size += marker_size + 1 + marker1_size;
+	} else {
+		memset(dest + size, '<', marker_size);
+		size += marker_size;
+		if (marker1_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name1, marker1_size - 1);
+			size += marker1_size;
+		}
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #1 */
+	size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+			      dest ? dest + size : NULL);
+
+	if (style == XDL_MERGE_DIFF3) {
+		/* Shared preimage */
+		if (!dest) {
+			size += marker_size + 1 + marker3_size;
+		} else {
+			memset(dest + size, '|', marker_size);
+			size += marker_size;
+			if (marker3_size) {
+				dest[size] = ' ';
+				memcpy(dest + size + 1, name3, marker3_size - 1);
+				size += marker3_size;
+			}
+			dest[size++] = '\n';
+		}
+		size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
+				      dest ? dest + size : NULL);
+	}
+
+	if (!dest) {
+		size += marker_size + 1;
+	} else {
+		memset(dest + size, '=', marker_size);
+		size += marker_size;
+		dest[size++] = '\n';
+	}
+
+	/* Postimage from side #2 */
+	size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+			      dest ? dest + size : NULL);
+	if (!dest) {
+		size += marker_size + 1 + marker2_size;
+	} else {
+		memset(dest + size, '>', marker_size);
+		size += marker_size;
+		if (marker2_size) {
+			dest[size] = ' ';
+			memcpy(dest + size + 1, name2, marker2_size - 1);
+			size += marker2_size;
+		}
+		dest[size++] = '\n';
+	}
+	return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+				 xdfenv_t *xe2, const char *name2,
+				 const char *ancestor_name,
+				 int favor,
+				 xdmerge_t *m, char *dest, int style,
+				 int marker_size)
+{
+	int size, i;
+
+	for (size = i = 0; m; m = m->next) {
+		if (favor && !m->mode)
+			m->mode = favor;
+
+		if (m->mode == 0)
+			size = fill_conflict_hunk(xe1, name1, xe2, name2,
+						  ancestor_name,
+						  size, i, style, m, dest,
+						  marker_size);
+		else if (m->mode & 3) {
+			/* Before conflicting part */
+			size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
+					      dest ? dest + size : NULL);
+			/* Postimage from side #1 */
+			if (m->mode & 1)
+				size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+						      dest ? dest + size : NULL);
+			/* Postimage from side #2 */
+			if (m->mode & 2)
+				size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+						      dest ? dest + size : NULL);
+		} else
+			continue;
+		i = m->i1 + m->chg1;
+	}
+	size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
+			      dest ? dest + size : NULL);
+	return size;
+}
+
+/*
+ * Sometimes, changes are not quite identical, but differ in only a few
+ * lines. Try hard to show only these few lines as conflicting.
+ */
+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+		xpparam_t const *xpp)
+{
+	for (; m; m = m->next) {
+		mmfile_t t1, t2;
+		xdfenv_t xe;
+		xdchange_t *xscr, *x;
+		int i1 = m->i1, i2 = m->i2;
+
+		/* let's handle just the conflicts */
+		if (m->mode)
+			continue;
+
+		/* no sense refining a conflict when one side is empty */
+		if (m->chg1 == 0 || m->chg2 == 0)
+			continue;
+
+		/*
+		 * This probably does not work outside git, since
+		 * we have a very simple mmfile structure.
+		 */
+		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
+		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
+		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
+			return -1;
+		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+		    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+		    xdl_build_script(&xe, &xscr) < 0) {
+			xdl_free_env(&xe);
+			return -1;
+		}
+		if (!xscr) {
+			/* If this happens, the changes are identical. */
+			xdl_free_env(&xe);
+			m->mode = 4;
+			continue;
+		}
+		x = xscr;
+		m->i1 = xscr->i1 + i1;
+		m->chg1 = xscr->chg1;
+		m->i2 = xscr->i2 + i2;
+		m->chg2 = xscr->chg2;
+		while (xscr->next) {
+			xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
+			if (!m2) {
+				xdl_free_env(&xe);
+				xdl_free_script(x);
+				return -1;
+			}
+			xscr = xscr->next;
+			m2->next = m->next;
+			m->next = m2;
+			m = m2;
+			m->mode = 0;
+			m->i1 = xscr->i1 + i1;
+			m->chg1 = xscr->chg1;
+			m->i2 = xscr->i2 + i2;
+			m->chg2 = xscr->chg2;
+		}
+		xdl_free_env(&xe);
+		xdl_free_script(x);
+	}
+	return 0;
+}
+
+static int line_contains_alnum(const char *ptr, long size)
+{
+	while (size--)
+		if (isalnum((unsigned char)*(ptr++)))
+			return 1;
+	return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+	for (; chg; chg--, i++)
+		if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+				xe->xdf2.recs[i]->size))
+			return 1;
+	return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+	xdmerge_t *next_m = m->next;
+	m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+	m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+	m->next = next_m->next;
+	free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+				      int simplify_if_no_alnum)
+{
+	int result = 0;
+
+	if (!m)
+		return result;
+	for (;;) {
+		xdmerge_t *next_m = m->next;
+		int begin, end;
+
+		if (!next_m)
+			return result;
+
+		begin = m->i1 + m->chg1;
+		end = next_m->i1;
+
+		if (m->mode != 0 || next_m->mode != 0 ||
+		    (end - begin > 3 &&
+		     (!simplify_if_no_alnum ||
+		      lines_contain_alnum(xe1, begin, end - begin)))) {
+			m = next_m;
+		} else {
+			result++;
+			xdl_merge_two_conflicts(m);
+		}
+	}
+}
+
+/*
+ * level == 0: mark all overlapping changes as conflict
+ * level == 1: mark overlapping changes as conflict only if not identical
+ * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ *             treat hunks not containing any letter or number as conflicting
+ *
+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts
+ */
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+		xdfenv_t *xe2, xdchange_t *xscr2,
+		xmparam_t const *xmp, mmbuffer_t *result)
+{
+	xdmerge_t *changes, *c;
+	xpparam_t const *xpp = &xmp->xpp;
+	const char *const ancestor_name = xmp->ancestor;
+	const char *const name1 = xmp->file1;
+	const char *const name2 = xmp->file2;
+	int i0, i1, i2, chg0, chg1, chg2;
+	int level = xmp->level;
+	int style = xmp->style;
+	int favor = xmp->favor;
+
+	if (style == XDL_MERGE_DIFF3) {
+		/*
+		 * "diff3 -m" output does not make sense for anything
+		 * more aggressive than XDL_MERGE_EAGER.
+		 */
+		if (XDL_MERGE_EAGER < level)
+			level = XDL_MERGE_EAGER;
+	}
+
+	c = changes = NULL;
+
+	while (xscr1 && xscr2) {
+		if (!changes)
+			changes = c;
+		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+			i0 = xscr1->i1;
+			i1 = xscr1->i2;
+			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+			chg0 = xscr1->chg1;
+			chg1 = xscr1->chg2;
+			chg2 = xscr1->chg1;
+			if (xdl_append_merge(&c, 1,
+					     i0, chg0, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+			xscr1 = xscr1->next;
+			continue;
+		}
+		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+			i0 = xscr2->i1;
+			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
+			i2 = xscr2->i2;
+			chg0 = xscr2->chg1;
+			chg1 = xscr2->chg1;
+			chg2 = xscr2->chg2;
+			if (xdl_append_merge(&c, 2,
+					     i0, chg0, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+			xscr2 = xscr2->next;
+			continue;
+		}
+		if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
+				xscr1->chg1 != xscr2->chg1 ||
+				xscr1->chg2 != xscr2->chg2 ||
+				xdl_merge_cmp_lines(xe1, xscr1->i2,
+					xe2, xscr2->i2,
+					xscr1->chg2, xpp->flags)) {
+			/* conflict */
+			int off = xscr1->i1 - xscr2->i1;
+			int ffo = off + xscr1->chg1 - xscr2->chg1;
+
+			i0 = xscr1->i1;
+			i1 = xscr1->i2;
+			i2 = xscr2->i2;
+			if (off > 0) {
+				i0 -= off;
+				i1 -= off;
+			}
+			else
+				i2 += off;
+			chg0 = xscr1->i1 + xscr1->chg1 - i0;
+			chg1 = xscr1->i2 + xscr1->chg2 - i1;
+			chg2 = xscr2->i2 + xscr2->chg2 - i2;
+			if (ffo < 0) {
+				chg0 -= ffo;
+				chg1 -= ffo;
+			} else
+				chg2 += ffo;
+			if (xdl_append_merge(&c, 0,
+					     i0, chg0, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+		}
+
+		i1 = xscr1->i1 + xscr1->chg1;
+		i2 = xscr2->i1 + xscr2->chg1;
+
+		if (i1 >= i2)
+			xscr2 = xscr2->next;
+		if (i2 >= i1)
+			xscr1 = xscr1->next;
+	}
+	while (xscr1) {
+		if (!changes)
+			changes = c;
+		i0 = xscr1->i1;
+		i1 = xscr1->i2;
+		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+		chg0 = xscr1->chg1;
+		chg1 = xscr1->chg2;
+		chg2 = xscr1->chg1;
+		if (xdl_append_merge(&c, 1,
+				     i0, chg0, i1, chg1, i2, chg2)) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		xscr1 = xscr1->next;
+	}
+	while (xscr2) {
+		if (!changes)
+			changes = c;
+		i0 = xscr2->i1;
+		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+		i2 = xscr2->i2;
+		chg0 = xscr2->chg1;
+		chg1 = xscr2->chg1;
+		chg2 = xscr2->chg2;
+		if (xdl_append_merge(&c, 2,
+				     i0, chg0, i1, chg1, i2, chg2)) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		xscr2 = xscr2->next;
+	}
+	if (!changes)
+		changes = c;
+	/* refine conflicts */
+	if (XDL_MERGE_ZEALOUS <= level &&
+	    (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+	     xdl_simplify_non_conflicts(xe1, changes,
+					XDL_MERGE_ZEALOUS < level) < 0)) {
+		xdl_cleanup_merge(changes);
+		return -1;
+	}
+	/* output */
+	if (result) {
+		int marker_size = xmp->marker_size;
+		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+						 ancestor_name,
+						 favor, changes, NULL, style,
+						 marker_size);
+		result->ptr = xdl_malloc(size);
+		if (!result->ptr) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		result->size = size;
+		xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+				      ancestor_name, favor, changes,
+				      result->ptr, style, marker_size);
+	}
+	return xdl_cleanup_merge(changes);
+}
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+		xmparam_t const *xmp, mmbuffer_t *result)
+{
+	xdchange_t *xscr1, *xscr2;
+	xdfenv_t xe1, xe2;
+	int status;
+	xpparam_t const *xpp = &xmp->xpp;
+
+	result->ptr = NULL;
+	result->size = 0;
+
+	if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||
+			xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+		return -1;
+	}
+	if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
+	    xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
+	    xdl_build_script(&xe1, &xscr1) < 0) {
+		xdl_free_env(&xe1);
+		return -1;
+	}
+	if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
+	    xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
+	    xdl_build_script(&xe2, &xscr2) < 0) {
+		xdl_free_env(&xe2);
+		return -1;
+	}
+	status = 0;
+	if (!xscr1) {
+		result->ptr = xdl_malloc(mf2->size);
+		memcpy(result->ptr, mf2->ptr, mf2->size);
+		result->size = mf2->size;
+	} else if (!xscr2) {
+		result->ptr = xdl_malloc(mf1->size);
+		memcpy(result->ptr, mf1->ptr, mf1->size);
+		result->size = mf1->size;
+	} else {
+		status = xdl_do_merge(&xe1, xscr1,
+				      &xe2, xscr2,
+				      xmp, result);
+	}
+	xdl_free_script(xscr1);
+	xdl_free_script(xscr2);
+
+	xdl_free_env(&xe1);
+	xdl_free_env(&xe2);
+
+	return status;
+}
diff --git a/libgit2/src/xdiff/xpatience.c b/libgit2/src/xdiff/xpatience.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xpatience.c
@@ -0,0 +1,358 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003-2009 Davide Libenzi, Johannes E. Schindelin
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+#include "xtypes.h"
+#include "xdiff.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files.  These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+	int nr, alloc;
+	struct entry {
+		unsigned long hash;
+		/*
+		 * 0 = unused entry, 1 = first line, 2 = second, etc.
+		 * line2 is NON_UNIQUE if the line is not unique
+		 * in either the first or the second file.
+		 */
+		unsigned long line1, line2;
+		/*
+		 * "next" & "previous" are used for the longest common
+		 * sequence;
+		 * initially, "next" reflects only the order in file1.
+		 */
+		struct entry *next, *previous;
+	} *entries, *first, *last;
+	/* were common records found? */
+	unsigned long has_matches;
+	mmfile_t *file1, *file2;
+	xdfenv_t *env;
+	xpparam_t const *xpp;
+};
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(int line, struct hashmap *map, int pass)
+{
+	xrecord_t **records = pass == 1 ?
+		map->env->xdf1.recs : map->env->xdf2.recs;
+	xrecord_t *record = records[line - 1], *other;
+	/*
+	 * After xdl_prepare_env() (or more precisely, due to
+	 * xdl_classify_record()), the "ha" member of the records (AKA lines)
+	 * is _not_ the hash anymore, but a linearized version of it.  In
+	 * other words, the "ha" member is guaranteed to start with 0 and
+	 * the second record's ha can only be 0 or 1, etc.
+	 *
+	 * So we multiply ha by 2 in the hope that the hashing was
+	 * "unique enough".
+	 */
+	int index = (int)((record->ha << 1) % map->alloc);
+
+	while (map->entries[index].line1) {
+		other = map->env->xdf1.recs[map->entries[index].line1 - 1];
+		if (map->entries[index].hash != record->ha ||
+				!xdl_recmatch(record->ptr, record->size,
+					other->ptr, other->size,
+					map->xpp->flags)) {
+			if (++index >= map->alloc)
+				index = 0;
+			continue;
+		}
+		if (pass == 2)
+			map->has_matches = 1;
+		if (pass == 1 || map->entries[index].line2)
+			map->entries[index].line2 = NON_UNIQUE;
+		else
+			map->entries[index].line2 = line;
+		return;
+	}
+	if (pass == 2)
+		return;
+	map->entries[index].line1 = line;
+	map->entries[index].hash = record->ha;
+	if (!map->first)
+		map->first = map->entries + index;
+	if (map->last) {
+		map->last->next = map->entries + index;
+		map->entries[index].previous = map->last;
+	}
+	map->last = map->entries + index;
+	map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		struct hashmap *result,
+		int line1, int count1, int line2, int count2)
+{
+	result->file1 = file1;
+	result->file2 = file2;
+	result->xpp = xpp;
+	result->env = env;
+
+	/* We know exactly how large we want the hash map */
+	result->alloc = count1 * 2;
+	result->entries = (struct entry *)
+		xdl_malloc(result->alloc * sizeof(struct entry));
+	if (!result->entries)
+		return -1;
+	memset(result->entries, 0, result->alloc * sizeof(struct entry));
+
+	/* First, fill with entries from the first file */
+	while (count1--)
+		insert_record(line1++, result, 1);
+
+	/* Then search for matches in the second file */
+	while (count2--)
+		insert_record(line2++, result, 2);
+
+	return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+		struct entry *entry)
+{
+	int left = -1, right = longest;
+
+	while (left + 1 < right) {
+		int middle = (left + right) / 2;
+		/* by construction, no two entries can be equal */
+		if (sequence[middle]->line2 > entry->line2)
+			right = middle;
+		else
+			left = middle;
+	}
+	/* return the index in "sequence", _not_ the sequence length */
+	return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1.  For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static struct entry *find_longest_common_sequence(struct hashmap *map)
+{
+	struct entry **sequence = xdl_malloc(map->nr * sizeof(struct entry *));
+	int longest = 0, i;
+	struct entry *entry;
+
+	for (entry = map->first; entry; entry = entry->next) {
+		if (!entry->line2 || entry->line2 == NON_UNIQUE)
+			continue;
+		i = binary_search(sequence, longest, entry);
+		entry->previous = i < 0 ? NULL : sequence[i];
+		sequence[++i] = entry;
+		if (i == longest)
+			longest++;
+	}
+
+	/* No common unique lines were found */
+	if (!longest) {
+		xdl_free(sequence);
+		return NULL;
+	}
+
+	/* Iterate starting at the last element, adjusting the "next" members */
+	entry = sequence[longest - 1];
+	entry->next = NULL;
+	while (entry->previous) {
+		entry->previous->next = entry;
+		entry = entry->previous;
+	}
+	xdl_free(sequence);
+	return entry;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+	xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+	xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+	return xdl_recmatch(record1->ptr, record1->size,
+		record2->ptr, record2->size, map->xpp->flags);
+}
+
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+		int line1, int count1, int line2, int count2)
+{
+	int end1 = line1 + count1, end2 = line2 + count2;
+	int next1, next2;
+
+	for (;;) {
+		/* Try to grow the line ranges of common lines */
+		if (first) {
+			next1 = first->line1;
+			next2 = first->line2;
+			while (next1 > line1 && next2 > line2 &&
+					match(map, next1 - 1, next2 - 1)) {
+				next1--;
+				next2--;
+			}
+		} else {
+			next1 = end1;
+			next2 = end2;
+		}
+		while (line1 < next1 && line2 < next2 &&
+				match(map, line1, line2)) {
+			line1++;
+			line2++;
+		}
+
+		/* Recurse */
+		if (next1 > line1 || next2 > line2) {
+			struct hashmap submap;
+
+			memset(&submap, 0, sizeof(submap));
+			if (patience_diff(map->file1, map->file2,
+					map->xpp, map->env,
+					line1, next1 - line1,
+					line2, next2 - line2))
+				return -1;
+		}
+
+		if (!first)
+			return 0;
+
+		while (first->next &&
+				first->next->line1 == first->line1 + 1 &&
+				first->next->line2 == first->line2 + 1)
+			first = first->next;
+
+		line1 = first->line1 + 1;
+		line2 = first->line2 + 1;
+
+		first = first->next;
+	}
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+		int line1, int count1, int line2, int count2)
+{
+	xpparam_t xpp;
+	xpp.flags = map->xpp->flags & ~XDF_PATIENCE_DIFF;
+
+	return xdl_fall_back_diff(map->env, &xpp,
+				  line1, count1, line2, count2);
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env,
+		int line1, int count1, int line2, int count2)
+{
+	struct hashmap map;
+	struct entry *first;
+	int result = 0;
+
+	/* trivial case: one side is empty */
+	if (!count1) {
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		return 0;
+	} else if (!count2) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		return 0;
+	}
+
+	memset(&map, 0, sizeof(map));
+	if (fill_hashmap(file1, file2, xpp, env, &map,
+			line1, count1, line2, count2))
+		return -1;
+
+	/* are there any matching lines at all? */
+	if (!map.has_matches) {
+		while(count1--)
+			env->xdf1.rchg[line1++ - 1] = 1;
+		while(count2--)
+			env->xdf2.rchg[line2++ - 1] = 1;
+		xdl_free(map.entries);
+		return 0;
+	}
+
+	first = find_longest_common_sequence(&map);
+	if (first)
+		result = walk_common_sequence(&map, first,
+			line1, count1, line2, count2);
+	else
+		result = fall_back_to_classic_diff(&map,
+			line1, count1, line2, count2);
+
+	xdl_free(map.entries);
+	return result;
+}
+
+int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2,
+		xpparam_t const *xpp, xdfenv_t *env)
+{
+	if (xdl_prepare_env(file1, file2, xpp, env) < 0)
+		return -1;
+
+	/* environment is cleaned up in xdl_diff() */
+	return patience_diff(file1, file2, xpp, env,
+			1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
diff --git a/libgit2/src/xdiff/xprepare.c b/libgit2/src/xdiff/xprepare.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xprepare.c
@@ -0,0 +1,483 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+#define XDL_KPDIS_RUN 4
+#define XDL_MAX_EQLIMIT 1024
+#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
+
+
+typedef struct s_xdlclass {
+	struct s_xdlclass *next;
+	unsigned long ha;
+	char const *line;
+	long size;
+	long idx;
+	long len1, len2;
+} xdlclass_t;
+
+typedef struct s_xdlclassifier {
+	unsigned int hbits;
+	long hsize;
+	xdlclass_t **rchash;
+	chastore_t ncha;
+	xdlclass_t **rcrecs;
+	long alloc;
+	long count;
+	long flags;
+} xdlclassifier_t;
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
+static void xdl_free_classifier(xdlclassifier_t *cf);
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+			       unsigned int hbits, xrecord_t *rec);
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+			   xdlclassifier_t *cf, xdfile_t *xdf);
+static void xdl_free_ctx(xdfile_t *xdf);
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
+	cf->flags = flags;
+
+	cf->hbits = xdl_hashbits((unsigned int) size);
+	cf->hsize = 1 << cf->hbits;
+
+	if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
+
+		return -1;
+	}
+	if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) {
+
+		xdl_cha_free(&cf->ncha);
+		return -1;
+	}
+	memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *));
+
+	cf->alloc = size;
+	if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) {
+
+		xdl_free(cf->rchash);
+		xdl_cha_free(&cf->ncha);
+		return -1;
+	}
+
+	cf->count = 0;
+
+	return 0;
+}
+
+
+static void xdl_free_classifier(xdlclassifier_t *cf) {
+
+	xdl_free(cf->rcrecs);
+	xdl_free(cf->rchash);
+	xdl_cha_free(&cf->ncha);
+}
+
+
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+			       unsigned int hbits, xrecord_t *rec) {
+	long hi;
+	char const *line;
+	xdlclass_t *rcrec;
+	xdlclass_t **rcrecs;
+
+	line = rec->ptr;
+	hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
+	for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
+		if (rcrec->ha == rec->ha &&
+				xdl_recmatch(rcrec->line, rcrec->size,
+					rec->ptr, rec->size, cf->flags))
+			break;
+
+	if (!rcrec) {
+		if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
+
+			return -1;
+		}
+		rcrec->idx = cf->count++;
+		if (cf->count > cf->alloc) {
+			cf->alloc *= 2;
+			if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) {
+
+				return -1;
+			}
+			cf->rcrecs = rcrecs;
+		}
+		cf->rcrecs[rcrec->idx] = rcrec;
+		rcrec->line = line;
+		rcrec->size = rec->size;
+		rcrec->ha = rec->ha;
+		rcrec->len1 = rcrec->len2 = 0;
+		rcrec->next = cf->rchash[hi];
+		cf->rchash[hi] = rcrec;
+	}
+
+	(pass == 1) ? rcrec->len1++ : rcrec->len2++;
+
+	rec->ha = (unsigned long) rcrec->idx;
+
+	hi = (long) XDL_HASHLONG(rec->ha, hbits);
+	rec->next = rhash[hi];
+	rhash[hi] = rec;
+
+	return 0;
+}
+
+
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+			   xdlclassifier_t *cf, xdfile_t *xdf) {
+	unsigned int hbits;
+	long nrec, hsize, bsize;
+	unsigned long hav;
+	char const *blk, *cur, *top, *prev;
+	xrecord_t *crec;
+	xrecord_t **recs, **rrecs;
+	xrecord_t **rhash;
+	unsigned long *ha;
+	char *rchg;
+	long *rindex;
+
+	ha = NULL;
+	rindex = NULL;
+	rchg = NULL;
+	rhash = NULL;
+	recs = NULL;
+
+	if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+		goto abort;
+	if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *))))
+		goto abort;
+
+	if (xpp->flags & XDF_HISTOGRAM_DIFF)
+		hbits = hsize = 0;
+	else {
+		hbits = xdl_hashbits((unsigned int) narec);
+		hsize = 1 << hbits;
+		if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *))))
+			goto abort;
+		memset(rhash, 0, hsize * sizeof(xrecord_t *));
+	}
+
+	nrec = 0;
+	if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) {
+		for (top = blk + bsize; cur < top; ) {
+			prev = cur;
+			hav = xdl_hash_record(&cur, top, xpp->flags);
+			if (nrec >= narec) {
+				narec *= 2;
+				if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *))))
+					goto abort;
+				recs = rrecs;
+			}
+			if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+				goto abort;
+			crec->ptr = prev;
+			crec->size = (long) (cur - prev);
+			crec->ha = hav;
+			recs[nrec++] = crec;
+
+			if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+				xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+				goto abort;
+		}
+	}
+
+	if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char))))
+		goto abort;
+	memset(rchg, 0, (nrec + 2) * sizeof(char));
+
+	if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long))))
+		goto abort;
+	if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long))))
+		goto abort;
+
+	xdf->nrec = nrec;
+	xdf->recs = recs;
+	xdf->hbits = hbits;
+	xdf->rhash = rhash;
+	xdf->rchg = rchg + 1;
+	xdf->rindex = rindex;
+	xdf->nreff = 0;
+	xdf->ha = ha;
+	xdf->dstart = 0;
+	xdf->dend = nrec - 1;
+
+	return 0;
+
+abort:
+	xdl_free(ha);
+	xdl_free(rindex);
+	xdl_free(rchg);
+	xdl_free(rhash);
+	xdl_free(recs);
+	xdl_cha_free(&xdf->rcha);
+	return -1;
+}
+
+
+static void xdl_free_ctx(xdfile_t *xdf) {
+
+	xdl_free(xdf->rhash);
+	xdl_free(xdf->rindex);
+	xdl_free(xdf->rchg - 1);
+	xdl_free(xdf->ha);
+	xdl_free(xdf->recs);
+	xdl_cha_free(&xdf->rcha);
+}
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		    xdfenv_t *xe) {
+	long enl1, enl2, sample;
+	xdlclassifier_t cf;
+
+	memset(&cf, 0, sizeof(cf));
+
+	/*
+	 * For histogram diff, we can afford a smaller sample size and
+	 * thus a poorer estimate of the number of lines, as the hash
+	 * table (rhash) won't be filled up/grown. The number of lines
+	 * (nrecs) will be updated correctly anyway by
+	 * xdl_prepare_ctx().
+	 */
+	sample = xpp->flags & XDF_HISTOGRAM_DIFF ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1;
+
+	enl1 = xdl_guess_lines(mf1, sample) + 1;
+	enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+	if (!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+		xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) {
+
+		return -1;
+	}
+
+	if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+
+		xdl_free_classifier(&cf);
+		return -1;
+	}
+	if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+
+		xdl_free_ctx(&xe->xdf1);
+		xdl_free_classifier(&cf);
+		return -1;
+	}
+
+	if (!(xpp->flags & XDF_PATIENCE_DIFF) &&
+			!(xpp->flags & XDF_HISTOGRAM_DIFF) &&
+			xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+
+		xdl_free_ctx(&xe->xdf2);
+		xdl_free_ctx(&xe->xdf1);
+		return -1;
+	}
+
+	if (!(xpp->flags & XDF_HISTOGRAM_DIFF))
+		xdl_free_classifier(&cf);
+
+	return 0;
+}
+
+
+void xdl_free_env(xdfenv_t *xe) {
+
+	xdl_free_ctx(&xe->xdf2);
+	xdl_free_ctx(&xe->xdf1);
+}
+
+
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
+	long r, rdis0, rpdis0, rdis1, rpdis1;
+
+	/*
+	 * Limits the window the is examined during the similar-lines
+	 * scan. The loops below stops when dis[i - r] == 1 (line that
+	 * has no match), but there are corner cases where the loop
+	 * proceed all the way to the extremities by causing huge
+	 * performance penalties in case of big files.
+	 */
+	if (i - s > XDL_SIMSCAN_WINDOW)
+		s = i - XDL_SIMSCAN_WINDOW;
+	if (e - i > XDL_SIMSCAN_WINDOW)
+		e = i + XDL_SIMSCAN_WINDOW;
+
+	/*
+	 * Scans the lines before 'i' to find a run of lines that either
+	 * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
+	 * Note that we always call this function with dis[i] > 1, so the
+	 * current line (i) is already a multimatch line.
+	 */
+	for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
+		if (!dis[i - r])
+			rdis0++;
+		else if (dis[i - r] == 2)
+			rpdis0++;
+		else
+			break;
+	}
+	/*
+	 * If the run before the line 'i' found only multimatch lines, we
+	 * return 0 and hence we don't make the current line (i) discarded.
+	 * We want to discard multimatch lines only when they appear in the
+	 * middle of runs with nomatch lines (dis[j] == 0).
+	 */
+	if (rdis0 == 0)
+		return 0;
+	for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
+		if (!dis[i + r])
+			rdis1++;
+		else if (dis[i + r] == 2)
+			rpdis1++;
+		else
+			break;
+	}
+	/*
+	 * If the run after the line 'i' found only multimatch lines, we
+	 * return 0 and hence we don't make the current line (i) discarded.
+	 */
+	if (rdis1 == 0)
+		return 0;
+	rdis1 += rdis0;
+	rpdis1 += rpdis0;
+
+	return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
+}
+
+
+/*
+ * Try to reduce the problem complexity, discard records that have no
+ * matches on the other file. Also, lines that have multiple matches
+ * might be potentially discarded if they happear in a run of discardable.
+ */
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+	long i, nm, nreff, mlim;
+	xrecord_t **recs;
+	xdlclass_t *rcrec;
+	char *dis, *dis1, *dis2;
+
+	if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) {
+
+		return -1;
+	}
+	memset(dis, 0, xdf1->nrec + xdf2->nrec + 2);
+	dis1 = dis;
+	dis2 = dis1 + xdf1->nrec + 1;
+
+	if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+		mlim = XDL_MAX_EQLIMIT;
+	for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
+		rcrec = cf->rcrecs[(*recs)->ha];
+		nm = rcrec ? rcrec->len2 : 0;
+		dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+	}
+
+	if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+		mlim = XDL_MAX_EQLIMIT;
+	for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
+		rcrec = cf->rcrecs[(*recs)->ha];
+		nm = rcrec ? rcrec->len1 : 0;
+		dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+	}
+
+	for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
+	     i <= xdf1->dend; i++, recs++) {
+		if (dis1[i] == 1 ||
+		    (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
+			xdf1->rindex[nreff] = i;
+			xdf1->ha[nreff] = (*recs)->ha;
+			nreff++;
+		} else
+			xdf1->rchg[i] = 1;
+	}
+	xdf1->nreff = nreff;
+
+	for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
+	     i <= xdf2->dend; i++, recs++) {
+		if (dis2[i] == 1 ||
+		    (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
+			xdf2->rindex[nreff] = i;
+			xdf2->ha[nreff] = (*recs)->ha;
+			nreff++;
+		} else
+			xdf2->rchg[i] = 1;
+	}
+	xdf2->nreff = nreff;
+
+	xdl_free(dis);
+
+	return 0;
+}
+
+
+/*
+ * Early trim initial and terminal matching records.
+ */
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
+	long i, lim;
+	xrecord_t **recs1, **recs2;
+
+	recs1 = xdf1->recs;
+	recs2 = xdf2->recs;
+	for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
+	     i++, recs1++, recs2++)
+		if ((*recs1)->ha != (*recs2)->ha)
+			break;
+
+	xdf1->dstart = xdf2->dstart = i;
+
+	recs1 = xdf1->recs + xdf1->nrec - 1;
+	recs2 = xdf2->recs + xdf2->nrec - 1;
+	for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
+		if ((*recs1)->ha != (*recs2)->ha)
+			break;
+
+	xdf1->dend = xdf1->nrec - i - 1;
+	xdf2->dend = xdf2->nrec - i - 1;
+
+	return 0;
+}
+
+
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+
+	if (xdl_trim_ends(xdf1, xdf2) < 0 ||
+	    xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
+
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/libgit2/src/xdiff/xprepare.h b/libgit2/src/xdiff/xprepare.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xprepare.h
@@ -0,0 +1,34 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XPREPARE_H)
+#define XPREPARE_H
+
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+		    xdfenv_t *xe);
+void xdl_free_env(xdfenv_t *xe);
+
+
+
+#endif /* #if !defined(XPREPARE_H) */
diff --git a/libgit2/src/xdiff/xtypes.h b/libgit2/src/xdiff/xtypes.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xtypes.h
@@ -0,0 +1,67 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XTYPES_H)
+#define XTYPES_H
+
+
+
+typedef struct s_chanode {
+	struct s_chanode *next;
+	long icurr;
+} chanode_t;
+
+typedef struct s_chastore {
+	chanode_t *head, *tail;
+	long isize, nsize;
+	chanode_t *ancur;
+	chanode_t *sncur;
+	long scurr;
+} chastore_t;
+
+typedef struct s_xrecord {
+	struct s_xrecord *next;
+	char const *ptr;
+	long size;
+	unsigned long ha;
+} xrecord_t;
+
+typedef struct s_xdfile {
+	chastore_t rcha;
+	long nrec;
+	unsigned int hbits;
+	xrecord_t **rhash;
+	long dstart, dend;
+	xrecord_t **recs;
+	char *rchg;
+	long *rindex;
+	long nreff;
+	unsigned long *ha;
+} xdfile_t;
+
+typedef struct s_xdfenv {
+	xdfile_t xdf1, xdf2;
+} xdfenv_t;
+
+
+
+#endif /* #if !defined(XTYPES_H) */
diff --git a/libgit2/src/xdiff/xutils.c b/libgit2/src/xdiff/xutils.c
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xutils.c
@@ -0,0 +1,419 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003	Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+
+
+long xdl_bogosqrt(long n) {
+	long i;
+
+	/*
+	 * Classical integer square root approximation using shifts.
+	 */
+	for (i = 1; n > 0; n >>= 2)
+		i <<= 1;
+
+	return i;
+}
+
+
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+		     xdemitcb_t *ecb) {
+	int i = 2;
+	mmbuffer_t mb[3];
+
+	mb[0].ptr = (char *) pre;
+	mb[0].size = psize;
+	mb[1].ptr = (char *) rec;
+	mb[1].size = size;
+	if (size > 0 && rec[size - 1] != '\n') {
+		mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
+		mb[2].size = strlen(mb[2].ptr);
+		i++;
+	}
+	if (ecb->outf(ecb->priv, mb, i) < 0) {
+
+		return -1;
+	}
+
+	return 0;
+}
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size)
+{
+	*size = (long)mmf->size;
+	return mmf->ptr;
+}
+
+
+long xdl_mmfile_size(mmfile_t *mmf)
+{
+	return (long)mmf->size;
+}
+
+
+int xdl_cha_init(chastore_t *cha, long isize, long icount) {
+
+	cha->head = cha->tail = NULL;
+	cha->isize = isize;
+	cha->nsize = icount * isize;
+	cha->ancur = cha->sncur = NULL;
+	cha->scurr = 0;
+
+	return 0;
+}
+
+
+void xdl_cha_free(chastore_t *cha) {
+	chanode_t *cur, *tmp;
+
+	for (cur = cha->head; (tmp = cur) != NULL;) {
+		cur = cur->next;
+		xdl_free(tmp);
+	}
+}
+
+
+void *xdl_cha_alloc(chastore_t *cha) {
+	chanode_t *ancur;
+	void *data;
+
+	if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
+		if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
+
+			return NULL;
+		}
+		ancur->icurr = 0;
+		ancur->next = NULL;
+		if (cha->tail)
+			cha->tail->next = ancur;
+		if (!cha->head)
+			cha->head = ancur;
+		cha->tail = ancur;
+		cha->ancur = ancur;
+	}
+
+	data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
+	ancur->icurr += cha->isize;
+
+	return data;
+}
+
+
+void *xdl_cha_first(chastore_t *cha) {
+	chanode_t *sncur;
+
+	if (!(cha->sncur = sncur = cha->head))
+		return NULL;
+
+	cha->scurr = 0;
+
+	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+void *xdl_cha_next(chastore_t *cha) {
+	chanode_t *sncur;
+
+	if (!(sncur = cha->sncur))
+		return NULL;
+	cha->scurr += cha->isize;
+	if (cha->scurr == sncur->icurr) {
+		if (!(sncur = cha->sncur = sncur->next))
+			return NULL;
+		cha->scurr = 0;
+	}
+
+	return (char *) sncur + sizeof(chanode_t) + cha->scurr;
+}
+
+
+long xdl_guess_lines(mmfile_t *mf, long sample) {
+	long nl = 0, size, tsize = 0;
+	char const *data, *cur, *top;
+
+	if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) {
+		for (top = data + size; nl < sample && cur < top; ) {
+			nl++;
+			if (!(cur = memchr(cur, '\n', top - cur)))
+				cur = top;
+			else
+				cur++;
+		}
+		tsize += (long) (cur - data);
+	}
+
+	if (nl && tsize)
+		nl = xdl_mmfile_size(mf) / (tsize / nl);
+
+	return nl + 1;
+}
+
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
+{
+	int i1, i2;
+
+	if (s1 == s2 && !memcmp(l1, l2, s1))
+		return 1;
+	if (!(flags & XDF_WHITESPACE_FLAGS))
+		return 0;
+
+	i1 = 0;
+	i2 = 0;
+
+	/*
+	 * -w matches everything that matches with -b, and -b in turn
+	 * matches everything that matches with --ignore-space-at-eol.
+	 *
+	 * Each flavor of ignoring needs different logic to skip whitespaces
+	 * while we have both sides to compare.
+	 */
+	if (flags & XDF_IGNORE_WHITESPACE) {
+		goto skip_ws;
+		while (i1 < s1 && i2 < s2) {
+			if (l1[i1++] != l2[i2++])
+				return 0;
+		skip_ws:
+			while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+				i1++;
+			while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+				i2++;
+		}
+	} else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+		while (i1 < s1 && i2 < s2) {
+			if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
+				/* Skip matching spaces and try again */
+				while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+					i1++;
+				while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+					i2++;
+				continue;
+			}
+			if (l1[i1++] != l2[i2++])
+				return 0;
+		}
+	} else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+		while (i1 < s1 && i2 < s2 && l1[i1++] == l2[i2++])
+			; /* keep going */
+	}
+
+	/*
+	 * After running out of one side, the remaining side must have
+	 * nothing but whitespace for the lines to match.  Note that
+	 * ignore-whitespace-at-eol case may break out of the loop
+	 * while there still are characters remaining on both lines.
+	 */
+	if (i1 < s1) {
+		while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+			i1++;
+		if (s1 != i1)
+			return 0;
+	}
+	if (i2 < s2) {
+		while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+			i2++;
+		return (s2 == i2);
+	}
+	return 1;
+}
+
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+		char const *top, long flags) {
+	unsigned long ha = 5381;
+	char const *ptr = *data;
+
+	for (; ptr < top && *ptr != '\n'; ptr++) {
+		if (XDL_ISSPACE(*ptr)) {
+			const char *ptr2 = ptr;
+			int at_eol;
+			while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
+					&& ptr[1] != '\n')
+				ptr++;
+			at_eol = (top <= ptr + 1 || ptr[1] == '\n');
+			if (flags & XDF_IGNORE_WHITESPACE)
+				; /* already handled */
+			else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+				 && !at_eol) {
+				ha += (ha << 5);
+				ha ^= (unsigned long) ' ';
+			}
+			else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
+				 && !at_eol) {
+				while (ptr2 != ptr + 1) {
+					ha += (ha << 5);
+					ha ^= (unsigned long) *ptr2;
+					ptr2++;
+				}
+			}
+			continue;
+		}
+		ha += (ha << 5);
+		ha ^= (unsigned long) *ptr;
+	}
+	*data = ptr < top ? ptr + 1: ptr;
+
+	return ha;
+}
+
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+	unsigned long ha = 5381;
+	char const *ptr = *data;
+
+	if (flags & XDF_WHITESPACE_FLAGS)
+		return xdl_hash_record_with_whitespace(data, top, flags);
+
+	for (; ptr < top && *ptr != '\n'; ptr++) {
+		ha += (ha << 5);
+		ha ^= (unsigned long) *ptr;
+	}
+	*data = ptr < top ? ptr + 1: ptr;
+
+	return ha;
+}
+
+
+unsigned int xdl_hashbits(unsigned int size) {
+	unsigned int val = 1, bits = 0;
+
+	for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
+	return bits ? bits: 1;
+}
+
+
+int xdl_num_out(char *out, long val) {
+	char *ptr, *str = out;
+	char buf[32];
+
+	ptr = buf + sizeof(buf) - 1;
+	*ptr = '\0';
+	if (val < 0) {
+		*--ptr = '-';
+		val = -val;
+	}
+	for (; val && ptr > buf; val /= 10)
+		*--ptr = "0123456789"[val % 10];
+	if (*ptr)
+		for (; *ptr; ptr++, str++)
+			*str = *ptr;
+	else
+		*str++ = '0';
+	*str = '\0';
+
+	return (int)(str - out);
+}
+
+
+long xdl_atol(char const *str, char const **next) {
+	long val, base;
+	char const *top;
+
+	for (top = str; XDL_ISDIGIT(*top); top++);
+	if (next)
+		*next = top;
+	for (val = 0, base = 1, top--; top >= str; top--, base *= 10)
+		val += base * (long)(*top - '0');
+	return val;
+}
+
+
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+		      const char *func, long funclen, xdemitcb_t *ecb) {
+	int nb = 0;
+	mmbuffer_t mb;
+	char buf[128];
+
+	memcpy(buf, "@@ -", 4);
+	nb += 4;
+
+	nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
+
+	if (c1 != 1) {
+		memcpy(buf + nb, ",", 1);
+		nb += 1;
+
+		nb += xdl_num_out(buf + nb, c1);
+	}
+
+	memcpy(buf + nb, " +", 2);
+	nb += 2;
+
+	nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
+
+	if (c2 != 1) {
+		memcpy(buf + nb, ",", 1);
+		nb += 1;
+
+		nb += xdl_num_out(buf + nb, c2);
+	}
+
+	memcpy(buf + nb, " @@", 3);
+	nb += 3;
+	if (func && funclen) {
+		buf[nb++] = ' ';
+		if (funclen > (long)sizeof(buf) - nb - 1)
+			funclen = (long)sizeof(buf) - nb - 1;
+		memcpy(buf + nb, func, funclen);
+		nb += funclen;
+	}
+	buf[nb++] = '\n';
+
+	mb.ptr = buf;
+	mb.size = nb;
+	if (ecb->outf(ecb->priv, &mb, 1) < 0)
+		return -1;
+
+	return 0;
+}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+		int line1, int count1, int line2, int count2)
+{
+	/*
+	 * This probably does not work outside Git, since
+	 * we have a very simple mmfile structure.
+	 *
+	 * Note: ideally, we would reuse the prepared environment, but
+	 * the libxdiff interface does not (yet) allow for diffing only
+	 * ranges of lines instead of the whole files.
+	 */
+	mmfile_t subfile1, subfile2;
+	xdfenv_t env;
+
+	subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+	subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+		diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+	subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+	subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+		diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+	if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+		return -1;
+
+	memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+	memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+	xdl_free_env(&env);
+
+	return 0;
+}
diff --git a/libgit2/src/xdiff/xutils.h b/libgit2/src/xdiff/xutils.h
new file mode 100644
--- /dev/null
+++ b/libgit2/src/xdiff/xutils.h
@@ -0,0 +1,49 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003  Davide Libenzi
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XUTILS_H)
+#define XUTILS_H
+
+
+
+long xdl_bogosqrt(long n);
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+		     xdemitcb_t *ecb);
+int xdl_cha_init(chastore_t *cha, long isize, long icount);
+void xdl_cha_free(chastore_t *cha);
+void *xdl_cha_alloc(chastore_t *cha);
+void *xdl_cha_first(chastore_t *cha);
+void *xdl_cha_next(chastore_t *cha);
+long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
+unsigned long xdl_hash_record(char const **data, char const *top, long flags);
+unsigned int xdl_hashbits(unsigned int size);
+int xdl_num_out(char *out, long val);
+long xdl_atol(char const *str, char const **next);
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+		      const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+		       int line1, int count1, int line2, int count2);
+
+
+
+#endif /* #if !defined(XUTILS_H) */
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,24 @@
+module Main where
+
+import Bindings.Libgit2
+import Control.Monad
+import Foreign.C.String
+import Foreign.Marshal.Alloc
+import Foreign.Storable
+import Prelude
+import System.Exit
+import System.Process (system)
+
+main :: IO ()
+main = do
+  putStrLn "Creating Git repository..."
+  _ <- system "git init smoke.git"
+
+  putStrLn "Accessing directly..."
+  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
+
+-- Main.hs ends here
diff --git a/tests/Main.hs b/tests/Main.hs
deleted file mode 100644
--- a/tests/Main.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-module Main where
-
-import Bindings.Libgit2
-import Control.Monad
-import Foreign.C.String
-import Foreign.Marshal.Alloc
-import Foreign.Storable
-import Prelude
-import System.Exit
-import System.Process (system)
-
-main :: IO ()
-main = do
-  putStrLn "Creating Git repository..."
-  _ <- system "git init smoke.git"
-
-  putStrLn "Accessing directly..."
-  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
-
--- Main.hs ends here
