You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2015/12/03 14:04:57 UTC
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
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 ...]