subversion-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhuij...@apache.org
Subject svn commit: r1717755 [2/4] - in /subversion/branches/ra-git/subversion/libsvn_ra_git: fetch.c ra_git.h ra_plugin.c reporter.c session.c
Date Thu, 03 Dec 2015 13:04:57 GMT

Copied: subversion/branches/ra-git/subversion/libsvn_ra_git/fetch.c (from r1717659, subversion/branches/ra-git/subversion/libsvn_ra_git/ra_plugin.c)
URL: http://svn.apache.org/viewvc/subversion/branches/ra-git/subversion/libsvn_ra_git/fetch.c?p2=subversion/branches/ra-git/subversion/libsvn_ra_git/fetch.c&p1=subversion/branches/ra-git/subversion/libsvn_ra_git/ra_plugin.c&r1=1717659&r2=1717755&rev=1717755&view=diff
==============================================================================
--- subversion/branches/ra-git/subversion/libsvn_ra_git/ra_plugin.c (original)
+++ subversion/branches/ra-git/subversion/libsvn_ra_git/fetch.c Thu Dec  3 13:04:57 2015
@@ -1,5 +1,6 @@
 /*
- * ra_plugin.c : the main RA module for git repository access
+ * fetch.c : Handles git repository url calculations and mirroring
+ *           git repositories into a libsvn_fs_git backend.
  *
  * ====================================================================
  *    Licensed to the Apache Software Foundation (ASF) under one
@@ -21,6 +22,11 @@
  * ====================================================================
  */
 
+ /* We compile in C89 mode, so the 'inline' keyword used by libgit2 isn't supported. */
+#define inline APR_INLINE
+#include <git2.h>
+#undef inline
+
 #include "svn_hash.h"
 #include "svn_ra.h"
 #include "svn_delta.h"
@@ -31,6 +37,7 @@
 #include "svn_path.h"
 #include "svn_version.h"
 #include "svn_sorts.h"
+#include "svn_repos.h"
 
 #include "svn_private_config.h"
 #include "../libsvn_ra/ra_loader.h"
@@ -42,60 +49,21 @@
 #define APR_WANT_STRFUNC
 #include <apr_want.h>
 
-#define RA_GIT_DEFAULT_REFSPEC      "+refs/heads/master:refs/remotes/origin/master"
+#define RA_GIT_DEFAULT_REFSPEC      "+refs/*:refs/*"
 #define RA_GIT_DEFAULT_REMOTE_NAME  "origin"
 #define RA_GIT_DEFAULT_REF          "refs/remotes/origin/master"
 
-typedef struct svn_ra_git__session_baton_t
-{
-  /* The URL of the session. */
-  const char *session_url;
-  const char *repos_root_url;
-
-  svn_ra_session_t *local_session;
-  const char *local_repos_root_url;
-
-  /* The user accessing the repository. */
-  const char *username;
-
-  /* Git repository data structures. */
-  git_repository *repos;
-  git_remote *remote;
-  git_revwalk *revwalk;
-
-  /* The URL of the remote. */
-  const char *remote_url;
-
-  /* The local abspath to the local git repository. */
-  const char *repos_abspath;
-
-  /* Wether we did 'git fetch' for this session already. */
-  svn_boolean_t fetch_done;
-
-  /* The relative path in the tree the session is rooted at. */
-  svn_stringbuf_t *fs_path;  /* URI-decoded, always without leading slash. */
-
-  /* The UUID associated with REPOS above (cached) */
-  const char *uuid;
-
-  /* Map revision numbers to git commit IDs. */
-  apr_hash_t *revmap;
-
-  /* Callbacks/baton passed to svn_ra_open. */
-  const svn_ra_callbacks2_t *callbacks;
-  void *callback_baton;
-
-  const char *useragent;
-
-  svn_ra__open_func_t svn_ra_open;
+/*----------------------------------------------------------------*/
 
-  /* Scratch pool for routines that cannot otherwise get one. */
-  apr_pool_t *scratch_pool;
+static volatile apr_uint32_t do_libgit2_init_called = 0;
 
-} svn_ra_git__session_baton_t;
+static svn_error_t *
+do_libgit2_init(void *baton, apr_pool_t *pool)
+{
+  git_libgit2_init();
+  return SVN_NO_ERROR;
+}
 
-/*----------------------------------------------------------------*/
-
 /*** Miscellaneous helper functions ***/
 
 svn_error_t *
@@ -107,12 +75,35 @@ svn_ra_git__wrap_git_error(void)
     SVN_ERR_MALFUNCTION();
 
   /* ### TODO: map error code */
-  return svn_error_createf(SVN_ERR_BASE, NULL,
+  return svn_error_createf(SVN_ERR_FS_GIT_LIBGIT2_ERROR, NULL,
                            _("git: %s"), git_err.message);
 }
 
+static apr_status_t
+cleanup_git_repos(void *baton)
+{
+  git_repository_free(baton);
+  return APR_SUCCESS;
+}
+
+static apr_status_t
+cleanup_git_remote(void *baton)
+{
+  git_remote_free(baton);
+  return APR_SUCCESS;
+}
+
+static svn_error_t *
+open_git_repos(git_repository **repos,
+               git_remote **remote,
+               git_remote_callbacks **callbacks,
+               svn_ra_git__session_t *session,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool);
+
 static const char *
-make_git_url(const char *session_url)
+make_git_url(const char *session_url,
+             apr_pool_t *result_pool)
 {
   if (strncmp(session_url, "git+", 4) == 0) /* git+file://, git+http://, git+https:// */
     return session_url + 4;
@@ -130,2401 +121,617 @@ make_svn_url(const char *git_url, apr_po
     return apr_pstrcat(result_pool, "git+", git_url, SVN_VA_NULL);
 }
 
-static svn_error_t *
-split_url(const char **remote_url,
-          svn_stringbuf_t *fs_path,
-          git_repository *repos,
-          const char *session_url,
-          git_remote_callbacks *callbacks,
-          apr_pool_t *result_pool,
-          apr_pool_t *scratch_pool)
+svn_error_t *
+svn_ra_git__split_url(const char **repos_root_url,
+                      const char **repos_relpath,
+                      const char **git_remote_url,
+                      apr_array_header_t **branches,
+                      svn_ra_git__session_t *session,
+                      const char *url,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
 {
   svn_boolean_t found_remote = FALSE;
   svn_stringbuf_t *remote_url_buf;
+  git_repository *repos;
+  git_remote *remote;
+  git_remote_callbacks *callbacks;
+  const char *remote_url_git;
+
+  if (branches)
+    *branches = NULL;
+
+  SVN_ERR(open_git_repos(&repos, &remote, &callbacks,
+                         session, scratch_pool, scratch_pool));
+
+  /* ### TODO: Optimize this by checking if there is some "path.git"
+               component, before starting at the end and working upwards.
 
-  remote_url_buf = svn_stringbuf_create(make_git_url(session_url), scratch_pool);
+               Perhaps starting at the root first, etc.
+               */
+
+  remote_url_git = make_git_url(url, scratch_pool);
+  remote_url_buf = svn_stringbuf_create(remote_url_git, scratch_pool);
   while (!found_remote)
     {
-      git_remote *remote;
       int git_err;
 
       SVN_DBG(("trying remote url '%s'", remote_url_buf->data));
 
       /* Create an in-memory remote... */
-      git_err = git_remote_create_anonymous(&remote, repos,
-                                            remote_url_buf->data);
-      if (git_err)
-        return svn_error_trace(svn_ra_git__wrap_git_error());
+      GIT2_ERR(git_remote_create_anonymous(&remote, repos,
+                                           remote_url_buf->data));
+
+      apr_pool_cleanup_register(scratch_pool, remote, cleanup_git_remote,
+                                apr_pool_cleanup_null);
 
       /* ... and try to connect to it. */
       git_err = git_remote_connect(remote, GIT_DIRECTION_FETCH, callbacks);
-      if (git_err)
+      if (!git_err)
         {
-          apr_size_t slash_pos;
-          const char *component;
+          found_remote = TRUE;
+          break;
+        }
 
-          giterr_clear();
+      giterr_clear();
 
-          slash_pos = svn_stringbuf_find_char_backward(remote_url_buf, '/');
-          if (slash_pos >= remote_url_buf->len)
-            break;
-
-          if (!svn_stringbuf_isempty(fs_path))
-            component = apr_pstrcat(scratch_pool, remote_url_buf->data + slash_pos + 1,
-                                   "/", SVN_VA_NULL); 
-          else
-            component = apr_pstrcat(scratch_pool, remote_url_buf->data + slash_pos + 1,
-                                    SVN_VA_NULL); 
-          svn_stringbuf_insert(fs_path, 0, component, strlen(component));
+      if (svn_uri_is_root(remote_url_buf->data, remote_url_buf->len))
+        break;
 
-          svn_stringbuf_chop(remote_url_buf, remote_url_buf->len - slash_pos); 
-        }
-      else
-        found_remote = TRUE;
+      svn_path_remove_component(remote_url_buf);
+
+      apr_pool_cleanup_run(scratch_pool, remote, cleanup_git_remote);
+      remote = NULL;
+    }
+
+  if (branches && found_remote)
+    {
+      git_remote_head** heads;
+      size_t head_count, i;
+    
+      /* This may look like it contacts the server, but this data is already
+         cached in libgit2 as it is always sent as part of the handshake */
+      GIT2_ERR(git_remote_ls(&heads, &head_count, remote));
+    
+      *branches = apr_array_make(result_pool, head_count,
+                                 sizeof(svn_ra_git_branch_t *));
+    
+      for (i = 0; i < head_count; i++)
+        {
+          git_remote_head *head = heads[i];
+          svn_ra_git_branch_t *br = apr_pcalloc(result_pool, sizeof(*br));
 
-      git_remote_free(remote);
+          br->name = apr_pstrdup(result_pool, head->name);
+          if (head->symref_target)
+            br->symref_target = apr_pstrdup(result_pool, head->symref_target);
+
+          APR_ARRAY_PUSH(*branches, svn_ra_git_branch_t *) = br;
+          SVN_DBG(("Noticed: %s -> %s", br->name, br->symref_target));
+        }
     }
 
+  apr_pool_cleanup_run(scratch_pool, remote, cleanup_git_remote);
+  remote = NULL;
+
   if (found_remote)
-    *remote_url = apr_pstrdup(result_pool, remote_url_buf->data);
+    {
+      *repos_root_url = make_svn_url(remote_url_buf->data, result_pool);
+      *repos_relpath = svn_uri_skip_ancestor(remote_url_buf->data,
+                                             remote_url_git, result_pool);
+      *git_remote_url = apr_pstrdup(result_pool, remote_url_buf->data);
+    }
   else
     return svn_error_compose_create(
-             svn_ra_git__wrap_git_error(),
-             svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
-                               _("No git repository found at URL '%s'"),
-                               session_url));
+      svn_ra_git__wrap_git_error(),
+      svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+                        _("No git repository found at URL '%s'"),
+                        url));
 
-  SVN_DBG(("found remote url '%s', fs_path: '%s'\n", *remote_url, fs_path->data));
+  SVN_DBG(("found remote url '%s', fs_path: '%s'\n",
+           *repos_root_url, *repos_relpath));
 
   return SVN_NO_ERROR;
 }
 
 static svn_error_t *
-do_git_fetch(svn_ra_git__session_baton_t *sess)
+do_update_head(svn_ra_session_t *session,
+               apr_pool_t *scratch_pool)
 {
-  int git_err;
-
-  /* Do one fetch per session.
-   * ### mutex? atomic_init? */
-  if (sess->fetch_done)
-    return SVN_NO_ERROR;
-
-  SVN_DBG(("fetching from %s\n", git_remote_url(sess->remote)));
-
-  git_err = git_remote_fetch(sess->remote, NULL, NULL, NULL);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-
-  sess->fetch_done = TRUE;
+#if 0
+  int error = 0;
+  size_t refs_len;
+  git_refspec *refspec;
+  const git_remote_head *remote_head, **refs;
+  const git_oid *remote_head_id;
+  git_buf remote_master_name = GIT_BUF_INIT;
+  git_buf branch = GIT_BUF_INIT;
+
+  if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
+    return error;
+
+  /* We cloned an empty repository or one with an unborn HEAD */
+  if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
+    return setup_tracking_config(
+      repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE);
+
+  /* We know we have HEAD, let's see where it points */
+  remote_head = refs[0];
+  assert(remote_head);
+
+  remote_head_id = &remote_head->oid;
+
+  error = git_remote_default_branch(&branch, remote);
+  if (error == GIT_ENOTFOUND) {
+    error = git_repository_set_head_detached(
+      repo, remote_head_id);
+    goto cleanup;
+  }
+
+  refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch));
+
+  if (refspec == NULL) {
+    giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration");
+    error = GIT_EINVALIDSPEC;
+    goto cleanup;
+  }
+
+  /* Determine the remote tracking reference name from the local master */
+  if ((error = git_refspec_transform(
+    &remote_master_name,
+    refspec,
+    git_buf_cstr(&branch))) < 0)
+    goto cleanup;
+
+  error = update_head_to_new_branch(
+    repo,
+    remote_head_id,
+    git_buf_cstr(&branch),
+    reflog_message);
+
+cleanup:
+  git_buf_free(&remote_master_name);
+  git_buf_free(&branch);
 
+  return error;
+#endif
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
-fill_revmap(git_revwalk *revwalk,
-            git_repository *repos,
-            apr_hash_t *revmap,
-            apr_pool_t *pool)
+svn_error_t *
+svn_ra_git__git_fetch(svn_ra_session_t *session,
+                      svn_boolean_t refresh,
+                      apr_pool_t *scratch_pool)
 {
-  svn_revnum_t rev;
-  int git_err;
+  svn_ra_git__session_t *sess = session->priv;
+  git_repository *repos;
+  git_remote *remote;
+  git_remote_callbacks *callbacks;
+  svn_revnum_t youngest;
+  apr_pool_t *subpool;
 
-  /* If the revmap has already been filled, there is nothing to do. */
-  if (apr_hash_count(revmap) > 0)
+
+  /* Do one fetch per session. */
+  if (sess->fetch_done && !refresh)
     return SVN_NO_ERROR;
 
-  git_revwalk_reset(revwalk);
-  git_revwalk_push_ref(revwalk, RA_GIT_DEFAULT_REF);
-  git_revwalk_simplify_first_parent(revwalk);
-  git_revwalk_sorting(revwalk, GIT_SORT_REVERSE);
-
-  SVN_DBG(("scanning git commits...\n"));
-  rev = 0;
-  do
-    {
-      git_oid oid;
+  /* Create subpool, to allow closing handles early on */
+  subpool = svn_pool_create(scratch_pool);
 
-      git_err = git_revwalk_next(&oid, revwalk);
-      if (git_err)
-        {
-          if (git_err != GIT_ITEROVER)
-            return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-      else
-        {
-          git_commit *commit;
-          svn_revnum_t *revp;
-          git_oid *oidp;
-          char rev_str[GIT_OID_HEXSZ + 1];
-
-          git_err = git_commit_lookup(&commit, repos, &oid);
-          if (git_err)
-            return svn_error_trace(svn_ra_git__wrap_git_error());
-
-          revp = apr_palloc(apr_hash_pool_get(revmap), sizeof(*revp));
-          *revp = ++rev;
-          oidp = apr_palloc(apr_hash_pool_get(revmap), sizeof(*oidp));
-          git_oid_cpy(oidp, &oid);
-          apr_hash_set(revmap, revp, sizeof(*revp), oidp);
+  SVN_ERR(open_git_repos(&repos, &remote, &callbacks, sess,
+                         subpool, subpool));
 
-          git_oid_tostr(rev_str, sizeof(rev_str), oidp);
-          SVN_DBG(("r%lu -> %s", rev, rev_str));
+  SVN_DBG(("Fetching from %s\n", sess->git_remote_url));
 
-          git_commit_free(commit);
-        }
-    }
-  while (git_err != GIT_ITEROVER);
+  {
+    git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
 
-  SVN_DBG(("done scanning git commits (%lu revisions)\n", rev));
+    fetch_opts.callbacks = *callbacks;
+    fetch_opts.prune = GIT_FETCH_PRUNE;
+    fetch_opts.update_fetchhead = TRUE;
+    fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
 
-  return SVN_NO_ERROR;
-}
+    GIT2_ERR(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
+  }
 
-/* Return the git tree, and the git commit pointing to it, corresponding
- * to Subverswion revision REVISION. If REVISION is SVN_INVALID_REVNUM
- * fetch the HEAD revision and store its revision number in *FETCHED_REV
- * if FETCHED_REV is not NULL.
- *
- * PATH is relative to the session url of SESS. Return the corresponding
- * repository-root-relative path in *REPOS_ROOT_RELPATH if REPOS_ROOT_RELPATH
- * is not NULL.
- *
- * Do all allocations in POOL. */
-static svn_error_t *
-fetch_revision_root(git_tree **tree,
-                    git_commit **commit,
-                    const char **repos_root_relpath,
-                    svn_revnum_t *fetched_rev,
-                    svn_ra_git__session_baton_t *sess,
-                    const char *path,
-                    svn_revnum_t revision,
-                    apr_pool_t *pool)
-{
-  git_oid *oid;
-  int git_err;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, pool);
-
-  if (!SVN_IS_VALID_REVNUM(revision))
-    revision = apr_hash_count(sess->revmap);
-
-  oid = apr_hash_get(sess->revmap, &revision, sizeof(revision));
-  if (oid == NULL)
-    return svn_error_create(SVN_ERR_FS_NO_SUCH_REVISION, NULL, NULL);
-
-  git_err = git_commit_lookup(commit, sess->repos, oid);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
+  sess->fetch_done = TRUE;
 
-  if (tree)
-    {
-      git_err = git_commit_tree(tree, *commit);
-      if (git_err)
-        {
-          git_commit_free(*commit);
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-    }
+  SVN_ERR(do_update_head(session, scratch_pool));
 
-  /* Handle reparented sessions and sessions not rooted at the git repos root. */
-  if (repos_root_relpath)
-    {
-      if (!svn_stringbuf_isempty(sess->fs_path))
-        *repos_root_relpath = svn_relpath_join(sess->fs_path->data, path, pool);
-      else
-        *repos_root_relpath = path;
-     }
+  /* This makes svn_fs_git() add new revisions to the mapping system */
+  SVN_ERR(svn_repos_recover4(sess->local_repos_abspath, FALSE,
+                             NULL, NULL,
+                             session->cancel_func, session->cancel_baton,
+                             subpool));
 
-  if (fetched_rev)
-    *fetched_rev = revision;
+  SVN_ERR(svn_ra_get_latest_revnum(sess->local_session, &youngest,
+                                   subpool));
+
+  SVN_DBG(("Latest revision r%ld\n", youngest));
+  svn_pool_destroy(subpool);
 
   return SVN_NO_ERROR;
 }
 
+
+
 /* Fetch a username for use with SESSION, and store it in SESSION->username.
  *
  * Allocate the username in SESSION->pool.  Use SCRATCH_POOL for temporary
  * allocations. */
 static svn_error_t *
-get_username(svn_ra_session_t *session,
+get_username(const char **username,
+             svn_ra_session_t *session,
+             apr_pool_t *result_pool,
              apr_pool_t *scratch_pool)
 {
-  svn_ra_git__session_baton_t *sess = session->priv;
+  svn_ra_git__session_t *sess = session->priv;
 
-  /* If we've already found the username don't ask for it again. */
-  if (! sess->username)
-    {
-      /* Get a username somehow, so we have some svn:author property to
-         attach to a commit. */
-      if (sess->callbacks->auth_baton)
-        {
-          void *creds;
-          svn_auth_cred_username_t *username_creds;
-          svn_auth_iterstate_t *iterstate;
-
-          SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
-                                             SVN_AUTH_CRED_USERNAME,
-                                             sess->uuid, /* realmstring */
-                                             sess->callbacks->auth_baton,
-                                             scratch_pool));
-
-          /* No point in calling next_creds(), since that assumes that the
-             first_creds() somehow failed to authenticate.  But there's no
-             challenge going on, so we use whatever creds we get back on
-             the first try. */
-          username_creds = creds;
-          if (username_creds && username_creds->username)
-            {
-              sess->username = apr_pstrdup(session->pool,
-                                           username_creds->username);
-              svn_error_clear(svn_auth_save_credentials(iterstate,
-                                                        scratch_pool));
-            }
-          else
-            sess->username = "";
+  *username = NULL;
+
+  /* Get a username somehow, so we have some svn:author property to
+      attach to a commit. */
+  if (sess->callbacks->auth_baton)
+    {
+      void *creds;
+      svn_auth_cred_username_t *username_creds;
+      svn_auth_iterstate_t *iterstate;
+
+      SVN_ERR(svn_auth_first_credentials(&creds, &iterstate,
+                                         SVN_AUTH_CRED_USERNAME,
+                                         sess->uuid, /* realmstring */
+                                         sess->callbacks->auth_baton,
+                                         scratch_pool));
+
+      /* No point in calling next_creds(), since that assumes that the
+          first_creds() somehow failed to authenticate.  But there's no
+          challenge going on, so we use whatever creds we get back on
+          the first try. */
+      username_creds = creds;
+      if (username_creds && username_creds->username)
+        {
+          *username = apr_pstrdup(result_pool,
+                                  username_creds->username);
+          svn_error_clear(svn_auth_save_credentials(iterstate,
+                                                    scratch_pool));
         }
-      else
-        sess->username = "";
     }
 
   return SVN_NO_ERROR;
 }
 
 /*----------------------------------------------------------------*/
-
-/*** The reporter vtable needed by do_update() and friends ***/
-
-typedef struct reporter_baton_t
-{
-  svn_ra_git__session_baton_t *sess;
-  void *report_baton;
-
-} reporter_baton_t;
-
-static svn_error_t *
-reporter_set_path(void *reporter_baton,
-                  const char *path,
-                  svn_revnum_t revision,
-                  svn_depth_t depth,
-                  svn_boolean_t start_empty,
-                  const char *lock_token,
-                  apr_pool_t *pool)
-{
-  reporter_baton_t *rbaton = reporter_baton;
-  return svn_error_trace(svn_ra_git__reporter_set_path(rbaton->report_baton,
-                                                       path, revision, depth,
-                                                       start_empty, lock_token,
-                                                       pool));
-}
-
-static svn_error_t *
-reporter_delete_path(void *reporter_baton,
-                     const char *path,
-                     apr_pool_t *pool)
-{
-  reporter_baton_t *rbaton = reporter_baton;
-  return svn_error_trace(svn_ra_git__reporter_delete_path(rbaton->report_baton,
-                                                          path, pool));
-}
-
-
-static svn_error_t *
-reporter_link_path(void *reporter_baton,
-                   const char *path,
-                   const char *url,
-                   svn_revnum_t revision,
-                   svn_depth_t depth,
-                   svn_boolean_t start_empty,
-                   const char *lock_token,
-                   apr_pool_t *pool)
-{
-  reporter_baton_t *rb = reporter_baton;
-  const char *linked_path;
-
-  linked_path = svn_uri_skip_ancestor(rb->sess->remote_url,
-                                      make_git_url(url), pool);
-  if (!linked_path)
-    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
-                             _("'%s'\n"
-                               "is not the same repository as\n"
-                               "'%s'"), url, rb->sess->session_url);
-
-  if (!svn_stringbuf_isempty(rb->sess->fs_path))
-    {
-      path = svn_relpath_join(rb->sess->fs_path->data, path, pool);
-      linked_path = svn_relpath_join(rb->sess->fs_path->data,
-                                        linked_path, pool);
-    }
-
-  return svn_error_trace(svn_ra_git__reporter_link_path(rb->report_baton,
-                                                        path, linked_path,
-                                                        revision,
-                                                        depth, start_empty,
-                                                        lock_token, pool));
-}
-
-static svn_error_t *
-reporter_finish_report(void *reporter_baton,
-                       apr_pool_t *pool)
-{
-  reporter_baton_t *rbaton = reporter_baton;
-  return svn_error_trace(svn_ra_git__reporter_finish_report(
-                           rbaton->report_baton, pool));
-}
-
-static svn_error_t *
-reporter_abort_report(void *reporter_baton,
-                      apr_pool_t *pool)
-{
-  reporter_baton_t *rbaton = reporter_baton;
-  return svn_error_trace(svn_ra_git__reporter_abort_report(
-                           rbaton->report_baton, pool));
-}
-
-
-static const svn_ra_reporter3_t ra_git_reporter =
-{
-  reporter_set_path,
-  reporter_delete_path,
-  reporter_link_path,
-  reporter_finish_report,
-  reporter_abort_report
-};
-
-
-/* ...
- *
- * Allocate @a *reporter and @a *report_baton in @a result_pool.  Use
- * @a scratch_pool for temporary allocations.
- */
-static svn_error_t *
-make_reporter(svn_ra_session_t *session,
-              const svn_ra_reporter3_t **reporter,
-              void **report_baton,
-              svn_revnum_t revision,
-              const char *target,
-              const char *other_url,
-              svn_boolean_t text_deltas,
-              svn_depth_t depth,
-              svn_boolean_t send_copyfrom_args,
-              svn_boolean_t ignore_ancestry,
-              const svn_delta_editor_t *editor,
-              void *edit_baton,
-              apr_pool_t *result_pool,
-              apr_pool_t *scratch_pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  struct reporter_baton_t *rb;
-  const char *other_fs_path = NULL;
-  void *wrapped_rb;
-
-  /* Get the HEAD revision if one is not supplied. */
-  if (! SVN_IS_VALID_REVNUM(revision))
-    revision = apr_hash_count(sess->revmap);
-
-  /* If OTHER_URL was provided, validate it and convert it into a
-     regular filesystem path. */
-  if (other_url)
-    {
-      const char *other_relpath
-        = svn_uri_skip_ancestor(sess->remote_url, make_git_url(other_url),
-                                scratch_pool);
-
-      /* Sanity check:  the other_url better be in the same repository as
-         the original session url! */
-      if (! other_relpath)
-        return svn_error_createf
-          (SVN_ERR_RA_ILLEGAL_URL, NULL,
-           _("'%s'\n"
-             "is not the same repository as\n"
-             "'%s'"), other_url, sess->session_url);
-
-      other_fs_path = other_relpath;
-    }
-
-  if (sess->callbacks)
-    SVN_ERR(svn_delta_get_cancellation_editor(sess->callbacks->cancel_func,
-                                              sess->callback_baton,
-                                              editor,
-                                              edit_baton,
-                                              &editor,
-                                              &edit_baton,
-                                              result_pool));
-
-  /* Build a reporter baton. */
-  SVN_ERR(svn_ra_git__reporter_begin_report(&wrapped_rb,
-                                            revision,
-                                            sess->repos,
-                                            sess->revmap,
-                                            sess->fs_path->data,
-                                            target,
-                                            other_fs_path,
-                                            text_deltas,
-                                            depth,
-                                            ignore_ancestry,
-                                            send_copyfrom_args,
-                                            editor,
-                                            edit_baton,
-                                            1024 * 1024,
-                                            result_pool));
-
-  /* Pass back our reporter */
-  *reporter = &ra_git_reporter;
-  rb = apr_palloc(result_pool, sizeof(*rb));
-  rb->sess = sess;
-  rb->report_baton = wrapped_rb;
-  *report_baton = rb;
-
-  return SVN_NO_ERROR;
-}
 
 static apr_status_t
 cleanup_temporary_repos(void *data)
 {
   svn_ra_session_t *session = data;
-  svn_ra_git__session_baton_t *sess = session->priv;
-  svn_error_t *err;
+  svn_ra_git__session_t *sess = session->priv;
 
-  err = svn_io_remove_dir2(sess->repos_abspath, TRUE, NULL, NULL, session->pool);
-  if (err)
-    {
-      apr_status_t apr_err = err->apr_err;
-      svn_error_clear(err);
-      return apr_err;
-    }
+  svn_error_clear(svn_io_remove_dir2(sess->local_repos_abspath, TRUE,
+                                     session->cancel_func,
+                                     session->cancel_baton,
+                                     session->pool));
 
   return APR_SUCCESS;
 }
 
+/*----------------------------------------------------------------*/
+/* git remote callbacks - Wrapped into Subversion like wrapper to
+                          allow using normal apis */
 
-static void
-check_cancel_stop_remote(svn_ra_git__session_baton_t *sess)
+typedef struct ra_git_remote_baton_t
 {
+  apr_pool_t *remote_pool;
+  git_remote *remote;
+
   svn_error_t *err;
 
-  if (sess->callbacks->cancel_func == NULL)
-    return;
+  svn_cancel_func_t cancel_func;
+  void *cancel_baton;
 
-  err = (sess->callbacks->cancel_func)(sess->callback_baton);
-  if (err)
-    {
-      if (err->apr_err == SVN_ERR_CANCELLED)
-        git_remote_stop(sess->remote);
-      svn_error_clear(err);
-    }
-}
+  apr_pool_t *scratch_pool;
+  svn_auth_iterstate_t *auth_iter;
 
-static int remote_sideband_progress_cb(const char *str, int len, void *data)
-{
-  svn_ra_git__session_baton_t *sess = data;
-  svn_string_t *s;
+  svn_boolean_t stopped;
+  svn_ra_git__session_t *sess;
+
+  apr_size_t last_received_bytes;
+} ra_git_remote_baton_t;
 
+static svn_error_t *
+remote_sideband_progress(ra_git_remote_baton_t *grb,
+                         const char *str, int len,
+                         apr_pool_t *scratch_pool)
+{
   if (len)
     {
-      svn_pool_clear(sess->scratch_pool);
-      s = svn_string_ncreate(str, len, sess->scratch_pool);
-      SVN_DBG(("%s\n", s->data));
+      str = apr_pstrmemdup(scratch_pool, str, len);
+    
+      SVN_DBG(("%s\n", str));
     }
 
-  check_cancel_stop_remote(sess);
-  return 0;
+  return SVN_NO_ERROR;
 }
 
-static int remote_transfer_progress_cb(const git_transfer_progress *stats,
-                                       void *data)
+static svn_error_t *
+remote_completion(ra_git_remote_baton_t *grb,
+                  git_remote_completion_type type,
+                  apr_pool_t *scratch_pool)
 {
-  svn_ra_git__session_baton_t *sess = data;
-
-  SVN_DBG(("objects: %u total %u indexed %u received %u local, "
-           "deltas: %u total %u indexed, %ld bytes received\n", 
-           stats->total_objects,
-           stats->indexed_objects,
-           stats->received_objects,
-           stats->local_objects,
-           stats->total_deltas,
-           stats->indexed_deltas,
-           (long)stats->received_bytes));
-
-  check_cancel_stop_remote(sess);
-  return 0;
+  return SVN_NO_ERROR;
 }
 
-static int remote_update_tips_cb(const char *refname,
-                                 const git_oid *a,
-                                 const git_oid *b,
-                                 void *data)
+static svn_error_t *
+remote_credentials_acquire(git_cred **cred,
+                           ra_git_remote_baton_t *grb,
+                           const char *url,
+                           const char *username_from_url,
+                           unsigned int allowed_types,
+                           apr_pool_t *result_pool,
+                           apr_pool_t *scratch_pool)
 {
-  svn_ra_git__session_baton_t *sess = data;
-
-  SVN_DBG(("update %s\n", refname));
 
-  check_cancel_stop_remote(sess);
-  return 0;
+  return SVN_NO_ERROR;
 }
 
+
 static svn_error_t *
-do_libgit_init(void *baton, apr_pool_t *pool)
+remote_transport_certificate_check(ra_git_remote_baton_t *grb,
+                                   git_cert *cert,
+                                   int valid,
+                                   const char *host,
+                                   void *payload)
 {
-  git_libgit2_init();
   return SVN_NO_ERROR;
 }
 
-/* Return the last-changed revision of the repos-root-relative
- * PATH@PEGREV in *LAST_CHANGED. */
-svn_error_t *
-svn_ra_git__find_last_changed(svn_revnum_t *last_changed,
-                              apr_hash_t *revmap,
-                              const char *path,
-                              svn_revnum_t pegrev,
-                              git_repository *repos,
-                              apr_pool_t *pool)
-{
-  int git_err;
-  const git_oid *oid;
-  git_oid last_oid;
-  git_commit *commit;
-  git_tree *tree;
-  git_tree_entry *entry;
-  svn_revnum_t rev;
-
-  oid = apr_hash_get(revmap, &pegrev, sizeof(pegrev));
-  if (oid == NULL)
-    return svn_error_create(SVN_ERR_FS_NO_SUCH_REVISION, NULL, NULL);
-
-  /* PATH has already been made relative to repos root by caller. */
-  if (path[0] == '\0')
-    {
-      /* The root directory of the repository was last changed in HEAD. */
-      *last_changed = apr_hash_count(revmap);
-      return SVN_NO_ERROR;
-    }
+static svn_error_t *
+remote_transfer_progress(ra_git_remote_baton_t *grb,
+                         const git_transfer_progress *stats,
+                         apr_pool_t *scratch_pool)
+{
+  svn_ra_git__session_t *sess = grb->sess;
 
-  git_err = git_commit_lookup(&commit, repos, oid);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-  git_err = git_commit_tree(&tree, commit);
-  if (git_err)
-    {
-      git_commit_free(commit);
-      return svn_error_trace(svn_ra_git__wrap_git_error());
-    }
+  if (stats->received_bytes < grb->last_received_bytes)
+    grb->last_received_bytes = 0;
 
-  git_err = git_tree_entry_bypath(&entry, tree, path);
-  if (git_err)
+  if (stats->received_bytes > grb->last_received_bytes)
     {
-      git_tree_free(tree);
-      git_commit_free(commit);
+      apr_size_t added = stats->received_bytes - grb->last_received_bytes;
 
-      if (git_err == GIT_ENOTFOUND)
-        return svn_error_createf(SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
-                                 _("No entry for %s@%lu\n"), path, pegrev);
+      sess->progress_bytes += added;
 
-      return svn_error_trace(svn_ra_git__wrap_git_error());
+      if (sess->callbacks->progress_func)
+        sess->callbacks->progress_func(sess->progress_bytes, -1,
+                                       sess->callbacks->progress_baton,
+                                       scratch_pool);
     }
 
-  git_oid_cpy(&last_oid, git_tree_entry_id(entry));
-  rev = apr_hash_count(revmap);
-
-  git_tree_free(tree);
-  git_commit_free(commit);
+  /*SVN_DBG(("objects: %u total %u indexed %u received %u local, "
+           "deltas: %u total %u indexed\n",
+           stats->total_objects, stats->indexed_objects,
+           stats->received_objects, stats->local_objects,
+           stats->total_deltas, stats->indexed_deltas));*/
 
-  while (rev >= 2)
-    {
-      oid = apr_hash_get(revmap, &rev, sizeof(rev));
-      if (oid == NULL)
-        return svn_error_create(SVN_ERR_FS_NO_SUCH_REVISION, NULL, NULL);
-      git_err = git_commit_lookup(&commit, repos, oid);
-      if (git_err)
-        return svn_error_trace(svn_ra_git__wrap_git_error());
-      git_err = git_commit_tree(&tree, commit);
-      if (git_err)
-        {
-          git_commit_free(commit);
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-      git_err = git_tree_entry_bypath(&entry, tree, path);
-      if (git_err)
-        {
-          git_tree_free(tree);
-          git_commit_free(commit);
+  return SVN_NO_ERROR;
+}
 
-          if (git_err == GIT_ENOTFOUND)
-            {
-              *last_changed = rev;
-              return SVN_NO_ERROR;
-            }
+static svn_error_t *
+remote_update_tips(svn_ra_git__session_t *sess,
+                   const char *refname,
+                   const git_oid *a,
+                   const git_oid *b,
+                   apr_pool_t *scratch_pool)
+{
+  SVN_DBG(("Updating: %s", refname));
 
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
+  return SVN_NO_ERROR;
+}
 
-      git_tree_free(tree);
-      git_commit_free(commit);
+static int
+remote_update_tips_cb(const char *refname,
+                      const git_oid *a,
+                      const git_oid *b,
+                      void *data)
+{
+  ra_git_remote_baton_t *grb = data;
 
-      oid = git_tree_entry_id(entry);
-      if (git_oid_cmp(oid, &last_oid) != 0)
-        {
-          git_tree_entry_free(entry);
-          break;
-        }
+  svn_pool_clear(grb->scratch_pool);
 
-      git_oid_cpy(&last_oid, git_tree_entry_id(entry));
-      git_tree_entry_free(entry);
-      rev--;
-    }
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-  *last_changed = rev;
+  if (!grb->err)
+    grb->err = remote_update_tips(grb->sess, refname,
+                                  a, b, grb->scratch_pool);
 
-  return SVN_NO_ERROR;
+  return (grb->err ? 1 : 0);
 }
 
-static svn_error_t *
-map_obj_to_dirent(svn_dirent_t **out,
-                  apr_hash_t *revmap, const char *path, svn_revnum_t pegrev,
-                  apr_uint32_t dirent_fields,
-                  git_repository *repos, git_commit *commit, git_object *obj,
-                  apr_pool_t *pool)
-{
-  svn_dirent_t *dirent = svn_dirent_create(pool);
-  git_otype type = git_object_type(obj);
-  svn_revnum_t last_changed_rev = SVN_INVALID_REVNUM;
-  git_commit *last_changed_commit = NULL;
 
-  if (dirent_fields & (SVN_DIRENT_CREATED_REV | SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
-    {
-      SVN_ERR(svn_ra_git__find_last_changed(&last_changed_rev, revmap, path,
-                                            pegrev, repos, pool));
+static int
+git_remote_sideband_progress_cb(const char *str, int len, void *data)
+{
+  ra_git_remote_baton_t *grb = data;
 
-      if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
-        {
-          git_oid *oid;
-          int git_err;
+  svn_pool_clear(grb->scratch_pool);
 
-          oid = apr_hash_get(revmap, &last_changed_rev, sizeof(last_changed_rev));
-          if (oid == NULL)
-            return svn_error_create(SVN_ERR_FS_NO_SUCH_REVISION, NULL, NULL);
-
-          git_err = git_commit_lookup(&last_changed_commit, repos, oid);
-          if (git_err)
-            return svn_error_trace(svn_ra_git__wrap_git_error());
-       } 
-    }
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-  if (dirent_fields & SVN_DIRENT_KIND)
-    {
-      if (type == GIT_OBJ_TREE)
-        dirent->kind = svn_node_dir;
-      else if (type == GIT_OBJ_BLOB)
-        dirent->kind = svn_node_file;
-      else
-        dirent->kind = svn_node_none;
-    }
+  if (!grb->err)
+    grb->err = remote_sideband_progress(grb, str, len, grb->scratch_pool);
 
-  if (dirent_fields & SVN_DIRENT_SIZE)
+  if (grb->err && !grb->stopped)
     {
-      if (type == GIT_OBJ_BLOB)
-        dirent->size = git_blob_rawsize((git_blob *)obj);
-      else
-        dirent->size = 0;
+      grb->stopped = TRUE;
+      git_remote_stop(grb->remote);
     }
 
-  if (dirent_fields & SVN_DIRENT_HAS_PROPS)
-    dirent->has_props = FALSE; /* ### TODO map svn: properties */
-
-  if (dirent_fields & SVN_DIRENT_CREATED_REV)
-    dirent->created_rev = last_changed_rev;
+  return (grb->err ? 1 : 0);
+}
 
-  if (dirent_fields & SVN_DIRENT_TIME)
-    dirent->time = git_commit_time(last_changed_commit) * 1000000;
+static int
+git_remote_completion_cb(git_remote_completion_type type, void *data)
+{
+  ra_git_remote_baton_t *grb = data;
 
-  if (dirent_fields & SVN_DIRENT_LAST_AUTHOR)
-    dirent->last_author = apr_pstrdup(pool, git_commit_author(last_changed_commit)->email);
+  svn_pool_clear(grb->scratch_pool);
 
-  *out = dirent;
-  return SVN_NO_ERROR;
-}
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-/*----------------------------------------------------------------*/
-
-/*** The RA vtable routines ***/
+  if (!grb->err)
+    grb->err = remote_completion(grb, type, grb->scratch_pool);
 
-#define RA_GIT_DESCRIPTION \
-        N_("Module for accessing a git repository.")
+  if (grb->err && !grb->stopped)
+    {
+      grb->stopped = TRUE;
+      git_remote_stop(grb->remote);
+    }
 
-static const char *
-svn_ra_git__get_description(apr_pool_t *pool)
-{
-  return _(RA_GIT_DESCRIPTION);
+  return (grb->err ? 1 : 0);
 }
 
-static const char * const *
-svn_ra_git__get_schemes(apr_pool_t *pool)
+static int
+git_remote_credentials_acquire_cb(git_cred **cred,
+                                  const char *url,
+                                  const char *username_from_url,
+                                  unsigned int allowed_types,
+                                  void *payload)
 {
-  /* TODO: git+ssh requires optional libssh dependency -- do we want that as well? */
-  static const char *schemes[] = { "git", "git+file", "git+http", "git+https", NULL };
+  ra_git_remote_baton_t *grb = payload;
 
-  return schemes;
-}
-
-#define USER_AGENT "SVN/" SVN_VER_NUMBER " (" SVN_BUILD_TARGET ")" \
-                   " ra_git"
+  svn_pool_clear(grb->scratch_pool);
 
-static svn_error_t *
-svn_ra_git__open(svn_ra_session_t *session,
-                 const char **corrected_url,
-                 const char *session_URL,
-                 const svn_ra_callbacks2_t *callbacks,
-                 void *callback_baton,
-                 svn_auth_baton_t *auth_baton,
-                 apr_hash_t *config,
-                 apr_pool_t *result_pool,
-                 apr_pool_t *scratch_pool)
-{
-  apr_pool_t *pool = result_pool;
-  const char *client_string;
-  svn_ra_git__session_baton_t *sess;
-  static volatile svn_atomic_t libgit_initialized = 0;
-  int git_err;
-  git_remote_callbacks *remote_callbacks;
-
-
-  /* We don't support redirections in ra-git. */
-  if (corrected_url)
-    *corrected_url = NULL;
-
-  /* Allocate and stash the session_sess args we have already. */
-  sess = apr_pcalloc(session->pool, sizeof(*sess));
-  sess->callbacks = callbacks;
-  sess->callback_baton = callback_baton;
-
-  /* Root the session at the root directory. */
-  sess->fs_path = svn_stringbuf_create_empty(session->pool);
-
-  /* Fake up the repository UUID. */
-   sess->uuid = RA_GIT_UUID;
-
-  /* Be sure username is NULL so we know to look it up / ask for it */
-  sess->username = NULL;
-
-  if (sess->callbacks->get_client_string != NULL)
-    SVN_ERR(sess->callbacks->get_client_string(sess->callback_baton,
-                                               &client_string, session->pool));
-  else
-    client_string = NULL;
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-  if (client_string)
-    sess->useragent = apr_pstrcat(session->pool, USER_AGENT " ",
-                                  client_string, SVN_VA_NULL);
-  else
-    sess->useragent = USER_AGENT;
+  if (!grb->err)
+    grb->err = remote_credentials_acquire(cred, grb, url, username_from_url,
+                                          allowed_types,
+                                          grb->scratch_pool, grb->scratch_pool);
 
-  sess->revmap = apr_hash_make(session->pool);
-  sess->fetch_done = FALSE;
-  sess->scratch_pool = svn_pool_create(session->pool);
-
-  sess->session_url = apr_pstrdup(pool, session_URL);
-  session->priv = sess;
-
-  /* Store the git repository within the working copy's admin area,
-   * if availble. Otherwise, create a temporary repository. */
-  if (sess->callbacks->get_wc_adm_subdir != NULL)
+  if (grb->err && !grb->stopped)
     {
-      SVN_ERR(sess->callbacks->get_wc_adm_subdir(sess->callback_baton,
-                                                 &sess->repos_abspath,
-                                                 "git",
-                                                 pool, pool));
+      grb->stopped = TRUE;
+      git_remote_stop(grb->remote);
     }
-  else
-    {
-      /* Use a temporary git repository. */
-      /* ### small race here, should be using mkdtemp() or similar */
-      SVN_ERR(svn_io_open_unique_file3(NULL, &sess->repos_abspath, NULL,
-                                       svn_io_file_del_none,
-                                       session->pool, pool));
-      SVN_ERR(svn_io_remove_file2(sess->repos_abspath, TRUE, pool));
 
-      /* Git repository is removed when the session pool gets destroyed. */
-      apr_pool_cleanup_register(session->pool, session, cleanup_temporary_repos,
-                                apr_pool_cleanup_null);
-    }
+  return (grb->err ? 1 : 0);
+}
+
+static int
+remote_transport_certificate_check_cb(git_cert *cert,
+                                      int valid,
+                                      const char *host,
+                                      void *payload)
+{
+  ra_git_remote_baton_t *grb = payload;
 
-  SVN_ERR(svn_atomic__init_once(&libgit_initialized, do_libgit_init, NULL,
-                                NULL));
+  svn_pool_clear(grb->scratch_pool);
 
-  SVN_DBG(("creating git repos in '%s'\n", sess->repos_abspath));
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-  /* Init (or reinit) a bare git repository. */
-  git_err = git_repository_init(&sess->repos, sess->repos_abspath,
-                                TRUE /* is_bare */);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-
-
-  remote_callbacks = apr_pcalloc(session->pool, sizeof(*remote_callbacks));
-  remote_callbacks->version = GIT_REMOTE_CALLBACKS_VERSION;
-  remote_callbacks->sideband_progress = remote_sideband_progress_cb;
-  remote_callbacks->transfer_progress = remote_transfer_progress_cb;
-  remote_callbacks->update_tips = remote_update_tips_cb;
-  remote_callbacks->payload = sess;
-
-  /* Split the session URL into a git remote URL and, possibly, a path within
-   * the repository (in sess->fs_path). */
-  svn_pool_clear(sess->scratch_pool);
-  SVN_ERR(split_url(&sess->remote_url, sess->fs_path, sess->repos,
-                    sess->session_url, remote_callbacks,
-                    session->pool, sess->scratch_pool));
-
-  /* Check if our remote already exists. */
-  git_err = git_remote_lookup(&sess->remote, sess->repos,
-                            RA_GIT_DEFAULT_REMOTE_NAME);
-  if (git_err)
-    {
-      if (git_err == GIT_ENOTFOUND)
-        {
-          giterr_clear();
-          sess->remote = NULL;
-        }
-      else
-        return svn_error_trace(svn_ra_git__wrap_git_error());
-    }
+  if (!grb->err)
+    grb->err = remote_transport_certificate_check(grb,
+                                                  cert, valid, host,
+                                                  grb->scratch_pool);
 
-  if (sess->remote == NULL)
+  if (grb->err && !grb->stopped)
     {
-      git_err = git_remote_create_with_fetchspec(
-                  &sess->remote, sess->repos, RA_GIT_DEFAULT_REMOTE_NAME,
-                  sess->remote_url, RA_GIT_DEFAULT_REFSPEC);
-      if (git_err)
-        return svn_error_trace(svn_ra_git__wrap_git_error());
+      grb->stopped = TRUE;
+      git_remote_stop(grb->remote);
     }
 
-  git_err = git_revwalk_new(&sess->revwalk, sess->repos);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-
-  return SVN_NO_ERROR;
+  return (grb->err ? 1 : 0);
 }
 
-static svn_error_t *
-svn_ra_git__set_svn_ra_open(svn_ra_session_t *session,
-                            svn_ra__open_func_t func)
+static int remote_transfer_progress_cb(const git_transfer_progress *stats,
+                                       void *data)
 {
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  sess->svn_ra_open = func;
-
-  /* TODO: Open ra_local session, etc. */
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-svn_ra_git__dup_session(svn_ra_session_t *new_session,
-                        svn_ra_session_t *session,
-                        const char *new_session_url,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_ra_git__session_baton_t *old_sess = session->priv;
-  svn_ra_git__session_baton_t *new_sess;
-
-  /* Allocate and stash the session_sess args we have already. */
-  new_sess = apr_pcalloc(result_pool, sizeof(*new_sess));
-  new_sess->callbacks = old_sess->callbacks;
-  new_sess->callback_baton = old_sess->callback_baton;
-
-  /* ### Make a deep copy of these? */
-  new_sess->repos = old_sess->repos;
-  new_sess->remote = old_sess->remote;
-  new_sess->revwalk = old_sess->revwalk;
-  new_sess->revmap = old_sess->revmap;
-
-  new_sess->fetch_done = old_sess->fetch_done;
-  new_sess->session_url = apr_pstrdup(result_pool, old_sess->session_url);
-  new_sess->remote_url = apr_pstrdup(result_pool, old_sess->remote_url);
-  new_sess->fs_path = svn_stringbuf_dup(old_sess->fs_path, result_pool);
-
-  /* Cache the repository UUID as well */
-  new_sess->uuid = apr_pstrdup(result_pool, old_sess->uuid);
-
-  new_sess->username = old_sess->username
-                            ? apr_pstrdup(result_pool, old_sess->username)
-                            : NULL;
-
-  new_sess->useragent = apr_pstrdup(result_pool, old_sess->useragent);
-  new_session->priv = new_sess;
+  ra_git_remote_baton_t *grb = data;
 
-  new_sess->scratch_pool = old_sess->scratch_pool;
+  svn_pool_clear(grb->scratch_pool);
 
-  return SVN_NO_ERROR;
-}
+  if (!grb->err && grb->cancel_func)
+    grb->err = grb->cancel_func(grb->cancel_baton);
 
-static svn_error_t *
-svn_ra_git__reparent(svn_ra_session_t *session,
-                     const char *url,
-                     apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  const char *relpath = svn_uri_skip_ancestor(sess->remote_url,
-                                              make_git_url(url), pool);
-
-  /* If the new URL isn't the same as our repository root URL, then
-     let's ensure that it's some child of it. */
-  if (! relpath)
-    return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
-       _("URL '%s' is not a child of the session's repository root "
-         "URL '%s'"), url, sess->session_url);
+  if (!grb->err)
+    grb->err = remote_transfer_progress(grb, stats, grb->scratch_pool);
 
-  if (strcmp(sess->session_url, url) != 0)
+  if (grb->err && !grb->stopped)
     {
-      svn_stringbuf_set(sess->fs_path, svn_relpath_canonicalize(relpath, pool));
-      sess->session_url = apr_pstrdup(pool, url);
+      grb->stopped = TRUE;
+      git_remote_stop(grb->remote);
     }
 
-  return SVN_NO_ERROR;
+  return (grb->err ? 1 : 0);
 }
 
 static svn_error_t *
-svn_ra_git__get_session_url(svn_ra_session_t *session,
-                            const char **url,
-                            apr_pool_t *pool)
+open_git_repos(git_repository **repos,
+               git_remote **remote,
+               git_remote_callbacks **callbacks,
+               svn_ra_git__session_t *sess,
+               apr_pool_t *result_pool,
+               apr_pool_t *scratch_pool)
 {
-  svn_ra_git__session_baton_t *sess = session->priv;
-  *url = apr_pstrdup(pool, sess->session_url);
-  return SVN_NO_ERROR;
-}
+  SVN_ERR(svn_atomic__init_once(&do_libgit2_init_called, do_libgit2_init,
+                                NULL, scratch_pool));
+
+  GIT2_ERR(git_repository_open(repos,
+                               svn_dirent_join(sess->local_repos_abspath,
+                                               "db/git", scratch_pool)));
+
+  if (*repos)
+    apr_pool_cleanup_register(result_pool, *repos, cleanup_git_repos,
+                              apr_pool_cleanup_null);
+
+  if (remote)
+    {
+      *remote = NULL;
+      if (callbacks)
+        {
+          git_remote_callbacks cb = GIT_REMOTE_CALLBACKS_INIT;
+          ra_git_remote_baton_t *grb = apr_pcalloc(result_pool, sizeof(*grb));
+
+          cb.sideband_progress = git_remote_sideband_progress_cb;
+          cb.completion = git_remote_completion_cb;
+          cb.credentials = git_remote_credentials_acquire_cb;
+          cb.certificate_check = remote_transport_certificate_check_cb;
+          cb.transfer_progress = remote_transfer_progress_cb;
+          cb.update_tips = remote_update_tips_cb;
+          cb.payload = grb;
+
+          *callbacks = apr_pmemdup(result_pool, &cb, sizeof(cb));
+
+          grb->remote = *remote;
+          grb->sess = sess;
+          grb->remote_pool = result_pool;
+          grb->scratch_pool = svn_pool_create(result_pool);
+          grb->cancel_func = sess->callbacks->cancel_func;
+          grb->cancel_baton = sess->callback_baton;
+        }
+
+    /* Check if our remote already exists. */
+      GIT2_ERR_NOTFOUND(remote, git_remote_lookup(remote, *repos,
+                                                  RA_GIT_DEFAULT_REMOTE_NAME));
+      if (!*remote)
+        {
+          /* No remote yet. Let's setup a remote in a similar way as
+             git --mirror would */
+          GIT2_ERR(git_remote_create_with_fetchspec(
+                    remote, *repos, RA_GIT_DEFAULT_REMOTE_NAME,
+                    sess->git_remote_url, RA_GIT_DEFAULT_REFSPEC));
+        }
+
+      if (*remote)
+        apr_pool_cleanup_register(result_pool, *remote, cleanup_git_remote,
+                                  apr_pool_cleanup_null);
+    }
 
-static svn_error_t *
-svn_ra_git__get_latest_revnum(svn_ra_session_t *session,
-                              svn_revnum_t *latest_revnum,
-                              apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, pool);
-  *latest_revnum = apr_hash_count(sess->revmap);
   return SVN_NO_ERROR;
 }
 
-static svn_error_t *
-svn_ra_git__get_file_revs(svn_ra_session_t *session,
-                          const char *path,
-                          svn_revnum_t start,
-                          svn_revnum_t end,
-                          svn_boolean_t include_merged_revisions,
-                          svn_file_rev_handler_t handler,
-                          void *handler_baton,
-                          apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-static svn_error_t *
-svn_ra_git__get_dated_revision(svn_ra_session_t *session,
-                               svn_revnum_t *revision,
-                               apr_time_t tm,
-                               apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-
-static svn_error_t *
-svn_ra_git__change_rev_prop(svn_ra_session_t *session,
-                            svn_revnum_t rev,
-                            const char *name,
-                            const svn_string_t *const *old_value_p,
-                            const svn_string_t *value,
-                            apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-static svn_error_t *
-svn_ra_git__get_uuid(svn_ra_session_t *session,
-                     const char **uuid,
-                     apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  *uuid = sess->uuid;
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-svn_ra_git__get_repos_root(svn_ra_session_t *session,
-                           const char **url,
-                           apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  *url = svn_uri_get_longest_ancestor(make_svn_url(sess->remote_url, pool),
-                                      sess->session_url, pool);
-  return SVN_NO_ERROR;
-}
-
-apr_hash_t *
-svn_ra_git__make_revprops_hash(git_commit *commit, apr_pool_t *pool)
-{
-  apr_hash_t *props = apr_hash_make(pool);
-  svn_hash_sets(props, SVN_PROP_REVISION_LOG,
-                svn_string_create(git_commit_message(commit), pool));
-  svn_hash_sets(props, SVN_PROP_REVISION_AUTHOR,
-                svn_string_create(git_commit_author(commit)->email, pool));
-  svn_hash_sets(props, SVN_PROP_REVISION_DATE,
-                svn_string_create(
-                  svn_time_to_cstring(git_commit_time(commit) * 1000000, pool),
-                  pool));
-  return props;
-}
-
-static svn_error_t *
-svn_ra_git__rev_proplist(svn_ra_session_t *session,
-                         svn_revnum_t rev,
-                         apr_hash_t **props,
-                         apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  int git_err;
-  git_oid *oid;
-  git_commit *commit;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, pool);
-
-  oid = apr_hash_get(sess->revmap, &rev, sizeof(rev));
-  if (oid == NULL)
-    return svn_error_create(SVN_ERR_FS_NO_SUCH_REVISION, NULL, NULL);
-
-  git_err = git_commit_lookup(&commit, sess->repos, oid);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-
-  *props = svn_ra_git__make_revprops_hash(commit, pool);
-  git_commit_free(commit);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-svn_ra_git__rev_prop(svn_ra_session_t *session,
-                     svn_revnum_t rev,
-                     const char *name,
-                     svn_string_t **value,
-                     apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-static svn_error_t *
-svn_ra_git__get_commit_editor(svn_ra_session_t *session,
-                              const svn_delta_editor_t **editor,
-                              void **edit_baton,
-                              apr_hash_t *revprop_table,
-                              svn_commit_callback2_t callback,
-                              void *callback_baton,
-                              apr_hash_t *lock_tokens,
-                              svn_boolean_t keep_locks,
-                              apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-
-static svn_error_t *
-svn_ra_git__get_mergeinfo(svn_ra_session_t *session,
-                          svn_mergeinfo_catalog_t *catalog,
-                          const apr_array_header_t *paths,
-                          svn_revnum_t revision,
-                          svn_mergeinfo_inheritance_t inherit,
-                          svn_boolean_t include_descendants,
-                          apr_pool_t *pool)
-{
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-
-static svn_error_t *
-svn_ra_git__do_update(svn_ra_session_t *session,
-                        const svn_ra_reporter3_t **reporter,
-                        void **report_baton,
-                        svn_revnum_t update_revision,
-                        const char *update_target,
-                        svn_depth_t depth,
-                        svn_boolean_t send_copyfrom_args,
-                        svn_boolean_t ignore_ancestry,
-                        const svn_delta_editor_t *update_editor,
-                        void *update_baton,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, scratch_pool);
-  return make_reporter(session,
-                       reporter,
-                       report_baton,
-                       update_revision,
-                       update_target,
-                       NULL,
-                       TRUE,
-                       depth,
-                       send_copyfrom_args,
-                       ignore_ancestry,
-                       update_editor,
-                       update_baton,
-                       result_pool, scratch_pool);
-}
-
-
-static svn_error_t *
-svn_ra_git__do_switch(svn_ra_session_t *session,
-                        const svn_ra_reporter3_t **reporter,
-                        void **report_baton,
-                        svn_revnum_t update_revision,
-                        const char *update_target,
-                        svn_depth_t depth,
-                        const char *switch_url,
-                        svn_boolean_t send_copyfrom_args,
-                        svn_boolean_t ignore_ancestry,
-                        const svn_delta_editor_t *update_editor,
-                        void *update_baton,
-                        apr_pool_t *result_pool,
-                        apr_pool_t *scratch_pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, scratch_pool);
-  return make_reporter(session,
-                       reporter,
-                       report_baton,
-                       update_revision,
-                       update_target,
-                       switch_url,
-                       TRUE /* text_deltas */,
-                       depth,
-                       send_copyfrom_args,
-                       ignore_ancestry,
-                       update_editor,
-                       update_baton,
-                       result_pool, scratch_pool);
-}
-
-
-static svn_error_t *
-svn_ra_git__do_status(svn_ra_session_t *session,
-                        const svn_ra_reporter3_t **reporter,
-                        void **report_baton,
-                        const char *status_target,
-                        svn_revnum_t revision,
-                        svn_depth_t depth,
-                        const svn_delta_editor_t *status_editor,
-                        void *status_baton,
-                        apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, pool);
-  return make_reporter(session,
-                       reporter,
-                       report_baton,
-                       revision,
-                       status_target,
-                       NULL,
-                       FALSE,
-                       depth,
-                       FALSE,
-                       FALSE,
-                       status_editor,
-                       status_baton,
-                       pool, pool);
-}
-
-
-static svn_error_t *
-svn_ra_git__do_diff(svn_ra_session_t *session,
-                    const svn_ra_reporter3_t **reporter,
-                    void **report_baton,
-                    svn_revnum_t update_revision,
-                    const char *update_target,
-                    svn_depth_t depth,
-                    svn_boolean_t ignore_ancestry,
-                    svn_boolean_t text_deltas,
-                    const char *switch_url,
-                    const svn_delta_editor_t *update_editor,
-                    void *update_baton,
-                    apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-
-  do_git_fetch(sess);
-  fill_revmap(sess->revwalk, sess->repos, sess->revmap, pool);
-  return make_reporter(session,
-                       reporter,
-                       report_baton,
-                       update_revision,
-                       update_target,
-                       switch_url,
-                       text_deltas,
-                       depth,
-                       FALSE,
-                       ignore_ancestry,
-                       update_editor,
-                       update_baton,
-                       pool, pool);
-}
-
-
-struct walk_added_tree_baton {
-  apr_hash_t *changed_paths;
-  const char *root_relpath;
-  apr_pool_t *pool;
-} walk_added_tree_baton;
-
-/* Implements git_treewalk_cb */
-static int
-walk_added_tree_cb(const char *root,
-                   const git_tree_entry *entry,
-                   void *payload)
-{
-  struct walk_added_tree_baton *b = payload;
-  svn_log_changed_path2_t *changed_path;
-  const char *entry_relpath;
-
-  changed_path = svn_log_changed_path2_create(b->pool);
-  changed_path->action = 'A';
-  root = svn_relpath_canonicalize(root, b->pool);
-  entry_relpath = svn_relpath_join(b->root_relpath,
-                                   svn_relpath_canonicalize(root, b->pool),
-                                   b->pool);
-  entry_relpath = svn_relpath_join(entry_relpath, git_tree_entry_name(entry),
-                                   b->pool);
-  svn_hash_sets(b->changed_paths, entry_relpath, changed_path);
-
-  return 0;
-}
-
-static svn_error_t *
-walk_added_tree(apr_hash_t *changed_paths,
-                const char *root_relpath,
-                git_tree *tree,
-                apr_pool_t *pool)
-{
-  int git_err;
-  struct walk_added_tree_baton b;
-
-  b.changed_paths = changed_paths;
-  b.root_relpath = root_relpath;
-  b.pool = pool;
-
-  /* Walk tree entries to compare children. */
-  git_err = git_tree_walk(tree, GIT_TREEWALK_PRE,
-                          walk_added_tree_cb, &b);
-  if (git_err)
-    return svn_error_trace(svn_ra_git__wrap_git_error());
-
-  return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-compare_git_tree_entries(apr_hash_t *changed_paths,
-                         git_repository *repos,
-                         git_tree *tree,
-                         git_tree *other_tree,
-                         const char *tree_relpath,
-                         apr_pool_t *pool)
-{
-  svn_log_changed_path2_t *changed_path;
-  apr_hash_t *other_entries;
-  int git_err;
-  int i;
-
-  /* Get the other tree's entries so we can compare entries of
-   * both tree objects. */
-  other_entries = apr_hash_make(pool);
-  for (i = 0; i < git_tree_entrycount(other_tree); i++)
-    {
-      const git_tree_entry *e;
-
-      /* Remember the entry's name and its oid. */
-      e = git_tree_entry_byindex(other_tree, i);
-      svn_hash_sets(other_entries, git_tree_entry_name(e),
-                    git_tree_entry_id(e));
-    }
-
-  /* Compare the trees' entries, pruning the other entries list
-   * of entries which exist in both trees or don't exist in the
-   * other tree. */
-  for (i = 0; i < git_tree_entrycount(tree); i++)
-    {
-      const git_tree_entry *e;
-      const git_oid *oid;
-      const git_oid *other_oid;
-
-      e = git_tree_entry_byindex(tree, i);
-      oid = git_tree_entry_id(e);
-      other_oid = svn_hash_gets(other_entries, git_tree_entry_name(e));
-
-      if (other_oid == NULL)
-        {
-          /* This entry was deleted in the other tree.
-           * Mark it as deleted. */
-          changed_path = svn_log_changed_path2_create(pool);
-          changed_path->action = 'D';
-          if (git_tree_entry_type(e) == GIT_OBJ_BLOB)
-            changed_path->node_kind = svn_node_file;
-          else if (git_tree_entry_type(e) == GIT_OBJ_TREE)
-            changed_path->node_kind = svn_node_dir;
-          else
-            changed_path->node_kind = svn_node_unknown;
-          svn_hash_sets(changed_paths,
-                        svn_relpath_join(svn_relpath_canonicalize(
-                                           tree_relpath, pool),
-                                         git_tree_entry_name(e), pool),
-                          changed_path);
-        }
-      else if (!git_oid_equal(oid, other_oid))
-        {
-          /* The entries differ.
-           * If it's a blob, mark it as modified if the other entry is
-           * also a blob, or mark it as replaced if the other entry is not
-           * a blob. If it's a tree object we'll deal with it later instead,
-           * while traversing it. */
-          if (git_tree_entry_type(e) == GIT_OBJ_BLOB)
-            {
-              const git_tree_entry *other_entry;
-              const char *entry_relpath;
-
-              changed_path = svn_log_changed_path2_create(pool);
-              other_entry = git_tree_entry_byid(other_tree, other_oid);
-              if (git_tree_entry_type(other_entry) == GIT_OBJ_BLOB)
-                changed_path->action = 'M';
-              else
-                changed_path->action = 'R';
-              entry_relpath = svn_relpath_join(svn_relpath_canonicalize(
-                                                 tree_relpath, pool),
-                                               git_tree_entry_name(e), pool),
-              svn_hash_sets(changed_paths, entry_relpath, changed_path);
-
-              if (changed_path->action == 'R' &&
-                  git_tree_entry_type(other_entry) == GIT_OBJ_TREE)
-                {
-                  git_tree *added_tree;
-
-                  git_err = git_tree_entry_to_object(
-                              (git_object **)&added_tree, repos,
-                              other_entry);
-                  if (git_err)
-                    return svn_error_trace(svn_ra_git__wrap_git_error());
-                  SVN_ERR(walk_added_tree(changed_paths, entry_relpath, added_tree, pool));
-                  git_tree_free(added_tree);
-                }
-            }
-        }
-
-      /* This other entry has been dealt with. */
-      svn_hash_sets(other_entries, git_tree_entry_name(e), NULL);
-    }
-
-  /* Mark any remaining other entries as newly added. */
-  if (apr_hash_count(other_entries))
-    {
-      apr_hash_index_t *hi;
-
-      for (hi = apr_hash_first(pool, other_entries); hi;
-           hi = apr_hash_next(hi))
-        {
-          const char *other_entry_name = apr_hash_this_key(hi);
-          const git_oid *other_entry_id = apr_hash_this_val(hi);
-          const git_tree_entry *other_entry;
-          const char *other_entry_relpath;
-
-          changed_path = svn_log_changed_path2_create(pool);
-          changed_path->action = 'A';
-          other_entry_relpath = svn_relpath_join(
-                                  svn_relpath_canonicalize(tree_relpath,
-                                                           pool),
-                                  other_entry_name, pool),
-          svn_hash_sets(changed_paths, other_entry_relpath, changed_path);
-
-          other_entry = git_tree_entry_byid(other_tree, other_entry_id);
-          if (git_tree_entry_type(other_entry) == GIT_OBJ_TREE)
-            {
-              git_tree *added_tree;
-
-              git_err = git_tree_entry_to_object(
-                          (git_object **)&added_tree, repos, other_entry);
-              if (git_err)
-                return svn_error_trace(svn_ra_git__wrap_git_error());
-
-              SVN_ERR(walk_added_tree(changed_paths, other_entry_relpath,
-                                      added_tree, pool));
-              git_tree_free(added_tree);
-            }
-        }
-    }
-
-  return SVN_NO_ERROR;
-}
-
-struct find_changed_paths_walk_baton {
-  apr_hash_t *changed_paths;
-  git_repository *repos;
-  git_tree *other_tree;
-  apr_pool_t *pool;
-  svn_error_t *err;
-} find_changed_paths_walk_baton;
-
-/* Implements git_treewalk_cb */
-static int
-find_changed_paths_walk_cb(const char *root,
-                           const git_tree_entry *entry,
-                           void *payload)
-{
-  struct find_changed_paths_walk_baton *b = payload;
-  int git_err;
-  git_tree_entry *other_root_entry;
-  git_tree_entry *other_entry;
-  git_otype other_type;
-  git_tree *tree;
-  git_tree *other_tree;
-  const char *entry_relpath;
-  svn_log_changed_path2_t *changed_path;
-
-  /* If this entry is not a tree object, we're not interested. */
-  if (git_tree_entry_type(entry) != GIT_OBJ_TREE)
-    return 0;
-
-  /* If this entry's root doesn't exist in the other tree,
-   * this entry was deleted along with the root. */
-  git_err = git_tree_entry_bypath(&other_root_entry, b->other_tree, root);
-  if (git_err)
-    {
-      if (git_err == GIT_ENOTFOUND)
-        {
-          giterr_clear();
-          return 0;
-        }
-
-      b->err = svn_error_trace(svn_ra_git__wrap_git_error());
-      return -1;
-    }
-  git_tree_entry_free(other_root_entry);
-
-  /* Look up the corresponding entry in the other tree. */
-  root = svn_relpath_canonicalize(root, b->pool);
-  entry_relpath = svn_relpath_join(root, git_tree_entry_name(entry), b->pool);
-  git_err = git_tree_entry_bypath(&other_entry, b->other_tree, entry_relpath);
-  if (git_err)
-    {
-      if (git_err == GIT_ENOTFOUND)
-        {
-          /* The entry has been deleted in the other tree. */
-          giterr_clear();
-          changed_path = svn_log_changed_path2_create(b->pool);
-          changed_path->action = 'D';
-          svn_hash_sets(b->changed_paths, entry_relpath, changed_path);
-          return 0;
-        }
-
-      b->err = svn_error_trace(svn_ra_git__wrap_git_error());
-      return -1;
-    }
-
-  other_type = git_tree_entry_type(other_entry);
-  if (other_type != GIT_OBJ_TREE)
-    {
-      /* The tree object has been replaced in the other tree
-       * by an object of a different type, most likely a blob. */
-      changed_path = svn_log_changed_path2_create(b->pool);
-      changed_path->action = 'R';
-      svn_hash_sets(b->changed_paths, entry_relpath, changed_path);
-      return 0;
-    }
-
-  /* Fetch the entry's tree object... */
-  git_err = git_tree_entry_to_object(((git_object **)&tree), b->repos, entry);
-  if (git_err)
-    {
-      b->err = svn_error_trace(svn_ra_git__wrap_git_error());
-      return -1;
-    }
-
-  /* .. and fetch the other entry's tree object .. */
-  git_err = git_tree_entry_to_object(((git_object **)&other_tree), b->repos,
-                                     other_entry);
-  git_tree_entry_free(other_entry);
-  if (git_err)
-    {
-      b->err = svn_error_trace(svn_ra_git__wrap_git_error());
-      return -1;
-    }
-
-  /* .. and compare the entries of both trees. */
-  b->err = svn_error_trace(compare_git_tree_entries(b->changed_paths,
-                                                    b->repos, tree, other_tree,
-                                                    entry_relpath, b->pool));
-  if (b->err)
-    return -1;
-    
-  return 0;
-}
-
-static svn_error_t *
-find_changed_paths(apr_hash_t **changed_paths,
-                   git_repository *repos,
-                   git_tree *tree,
-                   git_tree *other_tree,
-                   apr_pool_t *pool)
-{
-  int git_err;
-  struct find_changed_paths_walk_baton b;
-
-  b.changed_paths = apr_hash_make(pool);
-  b.repos = repos;
-  b.other_tree = other_tree;
-  b.pool = pool;
-  b.err = SVN_NO_ERROR;
-
-  if (tree == NULL)
-    {
-      SVN_ERR(walk_added_tree(b.changed_paths, "", other_tree, pool));
-    }
-  else
-    {
-      /* Compare the root entries. */
-      SVN_ERR(compare_git_tree_entries(b.changed_paths, repos, tree, other_tree,
-                                      "", pool));
-
-      /* Walk tree entries to compare children. */
-      git_err = git_tree_walk(tree, GIT_TREEWALK_PRE,
-                              find_changed_paths_walk_cb, &b);
-      if (git_err)
-        return svn_error_trace(svn_ra_git__wrap_git_error());
-      if (b.err)
-        return svn_error_trace(b.err);
-    }
-
-  *changed_paths = b.changed_paths;
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-svn_ra_git__get_log(svn_ra_session_t *session,
-                    const apr_array_header_t *paths,
-                    svn_revnum_t start,
-                    svn_revnum_t end,
-                    int limit,
-                    svn_boolean_t discover_changed_paths,
-                    svn_boolean_t strict_node_history,
-                    svn_boolean_t include_merged_revisions,
-                    const apr_array_header_t *revprops,
-                    svn_log_entry_receiver_t receiver,
-                    void *receiver_baton,
-                    apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  int git_err;
-  git_commit *commit;
-  git_tree *tree;
-  svn_revnum_t revision;
-  int step;
-  apr_pool_t *iterpool;
-
-  if (!SVN_IS_VALID_REVNUM(start))
-    SVN_ERR(svn_ra_git__get_latest_revnum(session, &start, pool));
-  if (!SVN_IS_VALID_REVNUM(end))
-    SVN_ERR(svn_ra_git__get_latest_revnum(session, &end, pool));
-
-  step = (start < end) ? 1 : -1;
-  revision = start;
-  if (step == 1)
-    end++;
-  if (start == 0 && revision != end)
-    revision++;
-  if (revision == end)
-    end += step;
-  iterpool = svn_pool_create(sess->scratch_pool);
-  while (revision != end)
-    {
-      apr_hash_t *changed_paths;
-      git_tree *parent_tree;
-
-      svn_pool_clear(iterpool);
-
-      SVN_ERR(fetch_revision_root(&tree, &commit, NULL, &revision, sess, "",
-                                  revision, pool));
-      if (git_commit_parentcount(commit) == 0)
-        {
-          /* First commit. All tree entries were added. */
-          parent_tree = NULL;
-        }
-      else
-        {
-          git_commit *parent_commit;
-
-          git_err = git_commit_parent(&parent_commit, commit, 0);
-          if (git_err)
-            return svn_error_trace(svn_ra_git__wrap_git_error());
-
-          git_err = git_commit_tree(&parent_tree, parent_commit);
-          if (git_err)
-            {
-              git_commit_free(parent_commit);
-              return svn_error_trace(svn_ra_git__wrap_git_error());
-            }
-
-          git_commit_free(parent_commit);
-        }
-
-      SVN_ERR((find_changed_paths(&changed_paths, sess->repos,
-                                  parent_tree, tree, iterpool)));
-      if (parent_tree)
-        git_tree_free(parent_tree);
-
-      if (apr_hash_count(changed_paths) > 0)
-        {
-          svn_boolean_t show_log = FALSE;
-
-          if (paths)
-            {
-              int i;
-
-              /* Check if a desired path is among the changed paths. */
-              for (i = 0; i < paths->nelts; i++)
-                {
-                  const char *path = APR_ARRAY_IDX(paths, i, const char *);
-
-                  if (!svn_stringbuf_isempty(sess->fs_path))
-                    path = svn_relpath_join(sess->fs_path->data, path,
-                                            iterpool);
-                  
-                  show_log = (path[0] == '\0' ||
-                              svn_hash_gets(changed_paths, path) != NULL);
-                  if (show_log)
-                    break;
-                }
-            }
-          else
-            show_log = TRUE;
-
-          if (show_log)
-            {
-              svn_log_entry_t *log_entry = svn_log_entry_create(iterpool);
-
-              if (discover_changed_paths)
-                {
-                  /* ### Some callers expect svn_fspath style keys...
-                   * ### convert all keys. */
-                  apr_hash_index_t *hi;
-
-                  log_entry->changed_paths2 = apr_hash_make(iterpool);
-
-                  for (hi = apr_hash_first(pool, changed_paths); hi;
-                       hi = apr_hash_next(hi))
-                    {
-                      const char *relpath_key = apr_hash_this_key(hi);
-                      void *val = apr_hash_this_val(hi);
-                      const char *fspath_key;
-
-                      fspath_key = apr_pstrcat(iterpool, "/", relpath_key,
-                                               SVN_VA_NULL);
-                      svn_hash_sets(log_entry->changed_paths2, fspath_key, val);
-                    }
-
-                  log_entry->changed_paths = log_entry->changed_paths2;
-                }
-
-              log_entry->revision = revision;
-
-              if (revprops)
-                {
-                  apr_hash_t *revprops_hash = NULL;
-                  int i;
-
-                  if (revprops->nelts > 0)
-                    revprops_hash = svn_ra_git__make_revprops_hash(commit,
-                                                                   iterpool);
-
-                  log_entry->revprops = apr_hash_make(pool);
-                  for (i = 0; i < revprops->nelts; i++)
-                    {
-                      const char *revprop_name = APR_ARRAY_IDX(revprops, i,
-                                                               const char *);
-                      svn_string_t *val = svn_hash_gets(revprops_hash,
-                                                        revprop_name);
-                      if (val)
-                        svn_hash_sets(log_entry->revprops, revprop_name, val);
-                    }
-                }
-              else
-                log_entry->revprops = svn_ra_git__make_revprops_hash(commit,
-                                                                     iterpool);
-
-              SVN_ERR(receiver(receiver_baton, log_entry, iterpool));
-
-              if (limit > 0)
-                if (--limit == 0)
-                  break;
-            }
-        }
-
-      revision += step;
-    }
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_ra_git__check_path(svn_node_kind_t *kind, git_tree *tree, const char *path)
-{
-  git_tree_entry *entry;
-  int git_err;
-
-  if (path[0] == '\0')
-    {
-      /* The root directory of the repository. */
-      *kind = svn_node_dir;
-      return SVN_NO_ERROR;
-    }
-
-  git_err = git_tree_entry_bypath(&entry, tree, path);
-  if (git_err)
-    {
-      git_tree_free(tree);
-
-      if (git_err == GIT_ENOTFOUND)
-        {
-          *kind = svn_node_none;
-          return SVN_NO_ERROR;
-        }
-
-      return svn_error_trace(svn_ra_git__wrap_git_error());
-    }
-
-  if (git_tree_entry_filemode(entry) == GIT_FILEMODE_COMMIT)
-    *kind = svn_node_none; /* ### submodule, map to external */
-  else
-    {
-      git_otype type = git_tree_entry_type(entry);
-
-      if (type == GIT_OBJ_TREE) 
-        *kind = svn_node_dir;
-      else if (type == GIT_OBJ_BLOB)
-        *kind = svn_node_file;
-      else
-        *kind = svn_node_unknown;
-    }
-
-  git_tree_entry_free(entry);
-
-  return SVN_NO_ERROR;
-}
-
-static svn_error_t *
-svn_ra_git__do_check_path(svn_ra_session_t *session,
-                          const char *path,
-                          svn_revnum_t revision,
-                          svn_node_kind_t *kind,
-                          apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  git_commit *commit;
-  git_tree *tree;
-
-  SVN_ERR(fetch_revision_root(&tree, &commit, &path, &revision,
-                              sess, path, revision, pool));
-
-  SVN_ERR(svn_ra_git__check_path(kind, tree, path));
-
-  git_tree_free(tree);
-  git_commit_free(commit);
-  return SVN_NO_ERROR;
-}
-
-
-static svn_error_t *
-svn_ra_git__stat(svn_ra_session_t *session,
-                 const char *path,
-                 svn_revnum_t revision,
-                 svn_dirent_t **dirent,
-                 apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  int git_err;
-  git_commit *commit;
-  git_tree *tree;
-  git_otype type;
-
-  SVN_ERR(fetch_revision_root(&tree, &commit, &path, &revision,
-                              sess, path, revision, pool));
-
-  if (path[0] == '\0')
-    {
-      /* The root directory of the repository. */
-      type = GIT_OBJ_TREE;
-      SVN_ERR(map_obj_to_dirent(dirent, sess->revmap, path, revision, SVN_DIRENT_ALL,
-                                sess->repos, commit, (git_object *)tree, pool));
-    }
-  else
-    {
-      git_tree_entry *entry;
-
-      git_err = git_tree_entry_bypath(&entry, tree, path);
-      if (git_err)
-        {
-          git_tree_free(tree);
-          git_commit_free(commit);
-
-          if (git_err == GIT_ENOTFOUND)
-            {
-              *dirent = NULL;
-              return SVN_NO_ERROR;
-            }
-
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-
-      type = git_tree_entry_type(entry);
-      if (type == GIT_OBJ_TREE || type == GIT_OBJ_BLOB)
-        {
-          git_object *object;
-
-          git_err = git_object_lookup(&object, sess->repos, git_tree_entry_id(entry), type);
-          if (git_err)
-            {
-              git_tree_entry_free(entry);
-              git_tree_free(tree);
-              git_commit_free(commit);
-
-              return svn_error_trace(svn_ra_git__wrap_git_error());
-            }
-
-          SVN_ERR(map_obj_to_dirent(dirent, sess->revmap, path, revision, SVN_DIRENT_ALL,
-                                    sess->repos, commit, object, pool));
-          git_object_free(object);
-        }
-      else
-        {
-          git_tree_entry_free(entry);
-          return svn_error_trace(svn_error_create(SVN_ERR_FS_NO_SUCH_ENTRY, NULL, NULL));
-        }
-
-      git_tree_entry_free(entry);
-    }
-
-  git_tree_free(tree);
-  git_commit_free(commit);
-
-  return SVN_NO_ERROR;
-}
-
-
-
-/* Obtain the properties for a node, including its 'entry props */
-static svn_error_t *
-get_node_props(apr_hash_t **props,
-               svn_fs_root_t *root,
-               const char *path,
-               const char *uuid,
-               apr_pool_t *result_pool,
-               apr_pool_t *scratch_pool)
-{
-  /* We have no 'wcprops' in ra_git, but might someday. */
-  return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
-}
-
-
-/* Getting just one file. */
-static svn_error_t *
-svn_ra_git__get_file(svn_ra_session_t *session,
-                     const char *path,
-                     svn_revnum_t revision,
-                     svn_stream_t *stream,
-                     svn_revnum_t *fetched_rev,
-                     apr_hash_t **props,
-                     apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  int git_err;
-  git_commit *commit;
-  git_tree *tree;
-  git_tree_entry *entry;
-  git_otype type;
-  git_blob *blob;
-
-  SVN_ERR(fetch_revision_root(&tree, &commit, &path, &revision,
-                              sess, path, revision, pool));
-
-  if (path[0] == '\0')
-    {
-      git_tree_free(tree);
-      git_commit_free(commit);
-      return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, NULL);
-    }
-
-  git_err = git_tree_entry_bypath(&entry, tree, path);
-  if (git_err)
-    {
-      git_tree_free(tree);
-      git_commit_free(commit);
-
-      if (git_err == GIT_ENOTFOUND)
-        return svn_error_create(SVN_ERR_FS_NO_SUCH_ENTRY, NULL, NULL);
-
-      return svn_error_trace(svn_ra_git__wrap_git_error());
-    }
-
-  type = git_tree_entry_type(entry);
-  if (type != GIT_OBJ_BLOB)
-    {
-      git_tree_entry_free(entry);
-      git_tree_free(tree);
-      git_commit_free(commit);
-      return svn_error_create(SVN_ERR_FS_NOT_FILE, NULL, NULL);
-    }
-
-  if (stream)
-    {
-      svn_filesize_t total_size;
-      const char *data;
-      apr_size_t bytes_copied;
-
-      git_err = git_blob_lookup(&blob, sess->repos, git_tree_entry_id(entry));
-      if (git_err)
-        {
-          git_tree_free(tree);
-          git_commit_free(commit);
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-
-      total_size = git_blob_rawsize(blob);
-      data = git_blob_rawcontent(blob);
-      bytes_copied = 0;
-
-      while (bytes_copied < total_size)
-        {
-          apr_size_t chunk_size = 1024;
-          apr_size_t len;
-
-          if (total_size - bytes_copied < chunk_size)
-            chunk_size = total_size - bytes_copied;
-
-          len = chunk_size;
-          SVN_ERR(svn_stream_write(stream, data, &len));
-          if (len != chunk_size)
-            {
-              git_tree_entry_free(entry);
-              git_tree_free(tree);
-              git_commit_free(commit);
-              return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL, NULL);
-            }
-
-          data += chunk_size;
-          bytes_copied += chunk_size;
-        }
-    }
-
-  if (fetched_rev)
-    *fetched_rev = revision;
-
-  if (props)
-    *props = apr_hash_make(pool);
-
-  git_tree_entry_free(entry);
-  git_tree_free(tree);
-  git_commit_free(commit);
-
-  return SVN_NO_ERROR;
-}
-
-/* Getting a directory's entries */
-static svn_error_t *
-svn_ra_git__get_dir(svn_ra_session_t *session,
-                    apr_hash_t **dirents,
-                    svn_revnum_t *fetched_rev,
-                    apr_hash_t **props,
-                    const char *path,
-                    svn_revnum_t revision,
-                    apr_uint32_t dirent_fields,
-                    apr_pool_t *pool)
-{
-  svn_ra_git__session_baton_t *sess = session->priv;
-  int git_err;
-  git_commit *commit;
-  git_tree *tree;
-
-  SVN_ERR(fetch_revision_root(&tree, &commit, &path, &revision,
-                              sess, path, revision, pool));
-
-  if (path[0] != '\0')
-    {
-      git_tree_entry *entry;
-      git_otype type;
-      git_object *subtree;
-
-      git_err = git_tree_entry_bypath(&entry, tree, path);
-      if (git_err)
-        {
-          git_tree_free(tree);
-          git_commit_free(commit);
-
-          if (git_err == GIT_ENOTFOUND)
-            return svn_error_create(SVN_ERR_FS_NO_SUCH_ENTRY, NULL, NULL);
-
-          return svn_error_trace(svn_ra_git__wrap_git_error());
-        }
-
-      /* ### Ignore git submodules for now.
-       * ### Eventually we'll map them to svn:externals. */ 
-      if (git_tree_entry_filemode(entry) == GIT_FILEMODE_COMMIT)
-        {
-          git_tree_entry_free(entry);
-          return svn_error_createf(SVN_ERR_FS_NO_SUCH_ENTRY, NULL,
-                                   _("'%s' is a git submodule but submodules are not "
-                                     "yet supported"), path);
-        }
-
-      type = git_tree_entry_type(entry);
-      if (type != GIT_OBJ_TREE)
-        {
-          git_tree_entry_free(entry);
-          git_tree_free(tree);
-          git_commit_free(commit);
-          return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, NULL);
-        }
-
-      git_err = git_tree_entry_to_object(&subtree, sess->repos, entry);
-      if (git_err)
-        return svn_error_trace(svn_ra_git__wrap_git_error());
-
-      git_tree_free(tree);
-      tree = (git_tree *)subtree;
-      git_tree_entry_free(entry);
-    }
-
-  if (dirents)
-    {

[... 321 lines stripped ...]



Mime
View raw message