Exemple #1
0
int sync_with_remote(git_repository *repo, const char *remote, const char *branch, enum remote_transport rt)
{
	int error;
	git_remote *origin;
	char *proxy_string;
	git_config *conf;

	if (verbose)
		fprintf(stderr, "sync with remote %s[%s]\n", remote, branch);
	git_repository_config(&conf, repo);
	if (rt == RT_HTTPS && getProxyString(&proxy_string)) {
		git_config_set_string(conf, "http.proxy", proxy_string);
		free(proxy_string);
	} else {
		git_config_set_string(conf, "http.proxy", "");
	}

	/*
	 * NOTE! Remote errors are reported, but are nonfatal:
	 * we still successfully return the local repository.
	 */
	error = git_remote_lookup(&origin, repo, "origin");
	if (error) {
		report_error("Repository '%s' origin lookup failed (%s)",
			remote, giterr_last()->message);
		return 0;
	}

	if (rt == RT_HTTPS && !canReachCloudServer()) {
		// this is not an error, just a warning message, so return 0
		report_error("Cannot connect to cloud server, working with local copy");
		return 0;
	}
#if USE_LIBGIT23_API
	git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
	opts.callbacks.transfer_progress = &transfer_progress_cb;
	if (rt == RT_SSH)
		opts.callbacks.credentials = credential_ssh_cb;
	else if (rt == RT_HTTPS)
		opts.callbacks.credentials = credential_https_cb;
	opts.callbacks.certificate_check = certificate_check_cb;
	error = git_remote_fetch(origin, NULL, &opts, NULL);
#else
	error = git_remote_fetch(origin, NULL, NULL, NULL);
#endif
	// NOTE! A fetch error is not fatal, we just report it
	if (error) {
		report_error("Unable to fetch remote '%s'", remote);
		error = 0;
	} else {
		error = check_remote_status(repo, origin, branch, rt);
	}
	git_remote_free(origin);
	return error;
}
Exemple #2
0
void test_network_fetchlocal__prune(void)
{
	git_repository *repo;
	git_remote *origin;
	int callcount = 0;
	git_strarray refnames = {0};
	git_reference *ref;
	git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
	const char *url = cl_git_path_url(git_repository_path(remote_repo));
	git_fetch_options options = GIT_FETCH_OPTIONS_INIT;

	options.callbacks.transfer_progress = transfer_cb;
	options.callbacks.payload = &callcount;

	cl_set_cleanup(&cleanup_local_repo, "foo");
	cl_git_pass(git_repository_init(&repo, "foo", true));

	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));

	cl_git_pass(git_reference_list(&refnames, repo));
	cl_assert_equal_i(19, (int)refnames.count);
	cl_assert(callcount > 0);
	git_strarray_free(&refnames);
	git_remote_free(origin);

	cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/br2"));
	cl_git_pass(git_reference_delete(ref));
	git_reference_free(ref);

	cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
	cl_git_pass(git_remote_prune(origin, &options.callbacks));

	cl_git_pass(git_reference_list(&refnames, repo));
	cl_assert_equal_i(18, (int)refnames.count);
	git_strarray_free(&refnames);
	git_remote_free(origin);

	cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/packed"));
	cl_git_pass(git_reference_delete(ref));
	git_reference_free(ref);

	cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
	cl_git_pass(git_remote_prune(origin, &options.callbacks));

	cl_git_pass(git_reference_list(&refnames, repo));
	cl_assert_equal_i(17, (int)refnames.count);
	git_strarray_free(&refnames);
	git_remote_free(origin);

	git_repository_free(repo);
}
Exemple #3
0
/**
 * Fetch new data and update tips
 *
 * @param repo S4 class git_repository
 * @param name The name of the remote to fetch from
 * @param credentials The credentials for remote repository access.
 * @param msg The one line long message to be appended to the reflog
 * @return R_NilValue
 */
SEXP git2r_remote_fetch(
    SEXP repo,
    SEXP name,
    SEXP credentials,
    SEXP msg)
{
    int err;
    SEXP result = R_NilValue;
    const git_transfer_progress *stats;
    git_remote *remote = NULL;
    git_repository *repository = NULL;
    git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;

    if (git2r_arg_check_string(name))
        git2r_error(git2r_err_string_arg, __func__, "name");
    if (git2r_arg_check_credentials(credentials))
        git2r_error(git2r_err_credentials_arg, __func__, "credentials");
    if (git2r_arg_check_string(msg))
        git2r_error(git2r_err_string_arg, __func__, "msg");

    repository = git2r_repository_open(repo);
    if (!repository)
        git2r_error(git2r_err_invalid_repository, __func__, NULL);

    err = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0)));
    if (GIT_OK != err)
        goto cleanup;

    callbacks.credentials = &git2r_cred_acquire_cb;
    callbacks.payload = credentials;
    err = git_remote_set_callbacks(remote, &callbacks);
    if (GIT_OK != err)
        goto cleanup;

    err = git_remote_fetch(remote, NULL,  CHAR(STRING_ELT(msg, 0)));
    if (GIT_OK != err)
        goto cleanup;

    stats = git_remote_stats(remote);
    PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_transfer_progress")));
    git2r_transfer_progress_init(stats, result);

cleanup:
    if (remote) {
        if (git_remote_connected(remote))
            git_remote_disconnect(remote);
        git_remote_free(remote);
    }

    if (repository)
        git_repository_free(repository);

    if (R_NilValue != result)
        UNPROTECT(1);

    if (GIT_OK != err)
        git2r_error(git2r_err_from_libgit2, __func__, giterr_last()->message);

    return result;
}
Exemple #4
0
void test_network_remote_local__fetch(void)
{
    char *refspec_strings[] = {
        "master:remotes/sloppy/master",
    };
    git_strarray array = {
        refspec_strings,
        1,
    };

    git_reflog *log;
    const git_reflog_entry *entry;
    git_reference *ref;

    connect_to_local_repository(cl_fixture("testrepo.git"));

    cl_git_pass(git_remote_fetch(remote, &array, NULL, "UPDAAAAAATE!!"));

    cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
    git_reference_free(ref);

    cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
    cl_assert_equal_i(1, git_reflog_entrycount(log));
    entry = git_reflog_entry_byindex(log, 0);
    cl_assert_equal_s("*****@*****.**", git_reflog_entry_committer(entry)->email);
    cl_assert_equal_s("UPDAAAAAATE!!", git_reflog_entry_message(entry));

    git_reflog_free(log);
}
Exemple #5
0
void test_network_remote_remotes__nonmatch_upstream_refspec(void)
{
	git_config *config;
	git_remote *remote;
	char *specstr[] = {
		"refs/tags/*:refs/tags/*",
	};
	git_strarray specs = {
		specstr,
		1,
	};

	cl_git_pass(git_remote_create(&remote, _repo, "taggy", git_repository_path(_repo)));

	/*
	 * Set the current branch's upstream remote to a dummy ref so we call into the code
	 * which tries to check for the current branch's upstream in the refspecs
	 */
	cl_git_pass(git_repository_config(&config, _repo));
	cl_git_pass(git_config_set_string(config, "branch.master.remote", "taggy"));
	cl_git_pass(git_config_set_string(config, "branch.master.merge", "refs/heads/foo"));

	cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL));

	git_remote_free(remote);
}
Exemple #6
0
void test_network_fetchlocal__complete(void)
{
	git_repository *repo;
	git_remote *origin;
	int callcount = 0;
	git_strarray refnames = {0};

	const char *url = cl_git_fixture_url("testrepo.git");
	git_fetch_options options = GIT_FETCH_OPTIONS_INIT;

	options.callbacks.transfer_progress = transfer_cb;
	options.callbacks.payload = &callcount;

	cl_set_cleanup(&cleanup_local_repo, "foo");
	cl_git_pass(git_repository_init(&repo, "foo", true));

	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
	cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));

	cl_git_pass(git_reference_list(&refnames, repo));
	cl_assert_equal_i(19, (int)refnames.count);
	cl_assert(callcount > 0);

	git_strarray_free(&refnames);
	git_remote_free(origin);
	git_repository_free(repo);
}
Exemple #7
0
emacs_value egit_remote_fetch(
    emacs_env *env, emacs_value _remote, emacs_value _refspecs, emacs_value opts, emacs_value _msg)
{
    EGIT_ASSERT_REMOTE(_remote);
    EM_ASSERT_STRING_OR_NIL(_msg);

    git_strarray refspecs;
    if (!egit_strarray_from_list(&refspecs, env, _refspecs))
        return em_nil;

    git_fetch_options options;
    egit_fetch_options_parse(env, opts, &options);
    if (env->non_local_exit_check(env)) {
        egit_strarray_dispose(&refspecs);
        return em_nil;
    }

    git_remote *remote = EGIT_EXTRACT(_remote);
    char *msg = EM_EXTRACT_STRING_OR_NULL(_msg);
    int retval = git_remote_fetch(remote, &refspecs, &options, msg);

    free(msg);
    egit_strarray_dispose(&refspecs);
    egit_fetch_options_release(&options);

    EGIT_CHECK_ERROR(retval);
    return em_nil;
}
Exemple #8
0
static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
{
	git_remote *remote;
	git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
	git_buf fetchhead_buf = GIT_BUF_INIT;
	int equals = 0;
	git_strarray array, *active_refs = NULL;

	cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
	fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;

	if(fetchspec != NULL) {
		array.count = 1;
		array.strings = (char **) &fetchspec;
		active_refs = &array;
	}

	cl_git_pass(git_remote_fetch(remote, active_refs, &fetch_opts, NULL));
	git_remote_free(remote);

	cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));

	equals = (strcmp(fetchhead_buf.ptr, expected_fetchhead) == 0);

	git_buf_dispose(&fetchhead_buf);

	cl_assert(equals);
}
Exemple #9
0
void test_network_remote_local__push_to_bare_remote(void)
{
    char *refspec_strings[] = {
        "master:master",
    };
    git_strarray array = {
        refspec_strings,
        1,
    };

    /* Should be able to push to a bare remote */
    git_remote *localremote;

    /* Get some commits */
    connect_to_local_repository(cl_fixture("testrepo.git"));
    cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));

    /* Set up an empty bare repo to push into */
    {
        git_repository *localbarerepo;
        cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1));
        git_repository_free(localbarerepo);
    }

    /* Connect to the bare repo */
    cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git"));
    cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL));

    /* Try to push */
    cl_git_pass(git_remote_upload(localremote, &push_array, NULL));

    /* Clean up */
    git_remote_free(localremote);
    cl_fixture_cleanup("localbare.git");
}
Exemple #10
0
void test_network_remote_local__push_to_non_bare_remote(void)
{
    char *refspec_strings[] = {
        "master:master",
    };
    git_strarray array = {
        refspec_strings,
        1,
    };
    /* Shouldn't be able to push to a non-bare remote */
    git_remote *localremote;
    git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;

    /* Get some commits */
    connect_to_local_repository(cl_fixture("testrepo.git"));
    cl_git_pass(git_remote_fetch(remote, &array, &fetch_opts, NULL));

    /* Set up an empty non-bare repo to push into */
    {
        git_repository *remoterepo = NULL;
        cl_git_pass(git_repository_init(&remoterepo, "localnonbare", 0));
        git_repository_free(remoterepo);
    }

    /* Connect to the bare repo */
    cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare"));
    cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL));

    /* Try to push */
    cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL));

    /* Clean up */
    git_remote_free(localremote);
    cl_fixture_cleanup("localbare.git");
}
Exemple #11
0
static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
{
	int error;
	git_buf reflog_message = GIT_BUF_INIT;
	git_fetch_options fetch_opts;
	git_remote *remote;

	assert(repo && _remote);

	if (!git_repository_is_empty(repo)) {
		git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
		return -1;
	}

	if ((error = git_remote_dup(&remote, _remote)) < 0)
		return error;

	memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
	fetch_opts.update_fetchhead = 0;
	fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));

cleanup:
	git_remote_free(remote);
	git_buf_dispose(&reflog_message);

	return error;
}
Exemple #12
0
void test_network_remote_local__fetch_default_reflog_message(void)
{
    char *refspec_strings[] = {
        "master:remotes/sloppy/master",
    };
    git_strarray array = {
        refspec_strings,
        1,
    };

    git_reflog *log;
    const git_reflog_entry *entry;
    char expected_reflog_msg[1024];

    connect_to_local_repository(cl_fixture("testrepo.git"));

    cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));

    cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
    cl_assert_equal_i(1, git_reflog_entrycount(log));
    entry = git_reflog_entry_byindex(log, 0);
    cl_assert_equal_s("*****@*****.**", git_reflog_entry_committer(entry)->email);

    sprintf(expected_reflog_msg, "fetch %s", git_remote_url(remote));
    cl_assert_equal_s(expected_reflog_msg, git_reflog_entry_message(entry));

    git_reflog_free(log);
}
Exemple #13
0
static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature)
{
	int error, flags;
	git_repository *src;
	git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
	git_buf reflog_message = GIT_BUF_INIT;

	assert(repo && remote);

	if (!git_repository_is_empty(repo)) {
		giterr_set(GITERR_INVALID, "the repository is not empty");
		return -1;
	}

	/*
	 * Let's figure out what path we should use for the source
	 * repo, if it's not rooted, the path should be relative to
	 * the repository's worktree/gitdir.
	 */
	if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
		return error;

	/* Copy .git/objects/ from the source to the target */
	if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
		git_buf_free(&src_path);
		return error;
	}

	git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR);
	git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR);
	if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) {
		error = -1;
		goto cleanup;
	}

	flags = 0;
	if (can_link(git_repository_path(src), git_repository_path(repo), link))
		flags |= GIT_CPDIR_LINK_FILES;

	if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
				     flags, GIT_OBJECT_DIR_MODE)) < 0)
		goto cleanup;

	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));

cleanup:
	git_buf_free(&reflog_message);
	git_buf_free(&src_path);
	git_buf_free(&src_odb);
	git_buf_free(&dst_odb);
	git_repository_free(src);
	return error;
}
Exemple #14
0
void
do_update_repo(char *url, char *path)
{
	git_repository *repo;
	git_remote *remote = NULL;
	git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;

	const git_transfer_progress *stats;

	char textfield_final_string[MAXLINE];
	memset(textfield_final_string, 0, sizeof(textfield_final_string));

	int error = git_repository_open(&repo, path);
	if (error != 0)
		git_error_handling();

	if (git_remote_lookup(&remote, repo, url) != 0) {
		error = git_remote_create_anonymous(&remote, repo, url);
		if (error != 0)
			git_error_handling();
	}

	fetch_opts.callbacks.update_tips = &update_tips;
	fetch_opts.callbacks.sideband_progress = sideband_progress;
	fetch_opts.callbacks.transfer_progress = fetch_progress;

	error = git_remote_fetch(remote, NULL, &fetch_opts, "fetch");
	if (error != 0)
		git_error_handling();

	stats = git_remote_stats(remote);
	int receive_kbyte = stats->received_bytes / 1024;
	if (stats->local_objects > 0) {
		snprintf(textfield_final_string,
			 sizeof(textfield_final_string),
			 _("Fetched: (%d/%d) %d kB (used %d local objects)"),
			 stats->indexed_objects, stats->total_objects,
			 receive_kbyte,
			 stats->local_objects);

		write_info_msg(textfield_final_string);
	} else {
		snprintf(textfield_final_string,
			 sizeof(textfield_final_string),
			 _("Fetched: (%d/%d) %d kB"),
			 stats->indexed_objects, stats->total_objects,
			 receive_kbyte);

		write_info_msg(textfield_final_string);
	}

out:
	if (remote)
		git_remote_free(remote);
	if (repo)
		git_repository_free(repo);

}
Exemple #15
0
/*
 *  call-seq:
 *    remote.fetch(refspecs = nil, options = {}) -> hash
 *
 *  Downloads new data from the remote for the given +refspecs+ and updates tips.
 *
 *  You can optionally pass in a single or multiple alternative +refspecs+ to use instead of the fetch
 *  refspecs already configured for +remote+.
 *
 *  Returns a hash containing statistics for the fetch operation.
 *
 *  The following options can be passed in the +options+ Hash:
 *
 *  :credentials ::
 *    The credentials to use for the fetch operation. Can be either an instance of one
 *    of the Rugged::Credentials types, or a proc returning one of the former.
 *    The proc will be called with the +url+, the +username+ from the url (if applicable) and
 *    a list of applicable credential types.
 *
 *  :headers ::
 *    Extra HTTP headers to include with the request (only applies to http:// or https:// remotes)
 *
 *  :progress ::
 *    A callback that will be executed with the textual progress received from the remote.
 *    This is the text send over the progress side-band (ie. the "counting objects" output).
 *
 *  :transfer_progress ::
 *    A callback that will be executed to report clone progress information. It will be passed
 *    the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+,
 *    +total_deltas+, +indexed_deltas+ and +received_bytes+.
 *
 *  :update_tips ::
 *    A callback that will be executed each time a reference is updated locally. It will be
 *    passed the +refname+, +old_oid+ and +new_oid+.
 *
 *  :certificate_check ::
 *    A callback that will be executed each time we validate a certificate using https. It
 *    will be passed the +valid+, +host_name+ and the callback should return a true/false to
 *    indicate if the certificate has been validated.
 *
 *  :message ::
 *    The message to insert into the reflogs. Defaults to "fetch".
 *
 *  :prune ::
 *    Specifies the prune mode for the fetch. +true+ remove any remote-tracking references that
 *    no longer exist, +false+ do not prune, +nil+ use configured settings Defaults to "nil".
 *
 *  Example:
 *
 *    remote = Rugged::Remote.lookup(@repo, 'origin')
 *    remote.fetch({
 *      transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes|
 *        # ...
 *      }
 *    })
 */
static VALUE rb_git_remote_fetch(int argc, VALUE *argv, VALUE self)
{
	git_remote *remote;
	git_strarray refspecs;
	git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
	const git_transfer_progress *stats;
	struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };

	char *log_message = NULL;
	int error;

	VALUE rb_options, rb_refspecs, rb_result = Qnil;

	rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options);

	rugged_rb_ary_to_strarray(rb_refspecs, &refspecs);

	Data_Get_Struct(self, git_remote, remote);

	rugged_remote_init_callbacks_and_payload_from_options(rb_options, &opts.callbacks, &payload);
	init_custom_headers(rb_options, &opts.custom_headers);

	if (!NIL_P(rb_options)) {
		VALUE rb_prune_type;
		VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message"));

		if (!NIL_P(rb_val))
			log_message = StringValueCStr(rb_val);

		rb_prune_type = rb_hash_aref(rb_options, CSTR2SYM("prune"));
		opts.prune = parse_prune_type(rb_prune_type);
	}

	error = git_remote_fetch(remote, &refspecs, &opts, log_message);

	xfree(refspecs.strings);
	git_strarray_free(&opts.custom_headers);

	if (payload.exception)
		rb_jump_tag(payload.exception);

	rugged_exception_check(error);

	stats = git_remote_stats(remote);

	rb_result = rb_hash_new();
	rb_hash_aset(rb_result, CSTR2SYM("total_objects"),    UINT2NUM(stats->total_objects));
	rb_hash_aset(rb_result, CSTR2SYM("indexed_objects"),  UINT2NUM(stats->indexed_objects));
	rb_hash_aset(rb_result, CSTR2SYM("received_objects"), UINT2NUM(stats->received_objects));
	rb_hash_aset(rb_result, CSTR2SYM("local_objects"),    UINT2NUM(stats->local_objects));
	rb_hash_aset(rb_result, CSTR2SYM("total_deltas"),     UINT2NUM(stats->total_deltas));
	rb_hash_aset(rb_result, CSTR2SYM("indexed_deltas"),   UINT2NUM(stats->indexed_deltas));
	rb_hash_aset(rb_result, CSTR2SYM("received_bytes"),   INT2FIX(stats->received_bytes));

	return rb_result;
}
Exemple #16
0
/*
 *  call-seq:
 *    remote.fetch(refspecs = nil, options = {}) -> hash
 *
 *  Downloads new data from the remote for the given +refspecs+ and updates tips.
 *
 *  You can optionally pass in a single or multiple alternative +refspecs+ to use instead of the fetch
 *  refspecs already configured for +remote+.
 *
 *  Returns a hash containing statistics for the fetch operation.
 *
 *  The following options can be passed in the +options+ Hash:
 *
 *  :credentials ::
 *    The credentials to use for the fetch operation. Can be either an instance of one
 *    of the Rugged::Credentials types, or a proc returning one of the former.
 *    The proc will be called with the +url+, the +username+ from the url (if applicable) and
 *    a list of applicable credential types.
 *
 *  :progress ::
 *    A callback that will be executed with the textual progress received from the remote.
 *    This is the text send over the progress side-band (ie. the "counting objects" output).
 *
 *  :transfer_progress ::
 *    A callback that will be executed to report clone progress information. It will be passed
 *    the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+,
 *    +total_deltas+, +indexed_deltas+ and +received_bytes+.
 *
 *  :update_tips ::
 *    A callback that will be executed each time a reference is updated locally. It will be
 *    passed the +refname+, +old_oid+ and +new_oid+.
 *
 *  :message ::
 *    The message to insert into the reflogs. Defaults to "fetch".
 *
 *  Example:
 *
 *    remote = Rugged::Remote.lookup(@repo, 'origin')
 *    remote.fetch({
 *      transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes|
 *        # ...
 *      }
 *    })
 */
static VALUE rb_git_remote_fetch(int argc, VALUE *argv, VALUE self)
{
	git_remote *remote;
	git_repository *repo;
	git_strarray refspecs;
	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
	struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, Qnil, 0 };

	char *log_message = NULL;
	int error;

	VALUE rb_options, rb_refspecs, rb_result = Qnil, rb_repo = rugged_owner(self);

	rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options);

	rugged_rb_ary_to_strarray(rb_refspecs, &refspecs);

	Data_Get_Struct(self, git_remote, remote);
	rugged_check_repo(rb_repo);
	Data_Get_Struct(rb_repo, git_repository, repo);

	rugged_remote_init_callbacks_and_payload_from_options(rb_options, &callbacks, &payload);

	if (!NIL_P(rb_options)) {
		VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("message"));
		if (!NIL_P(rb_val))
			log_message = StringValueCStr(rb_val);
	}

	if ((error = git_remote_set_callbacks(remote, &callbacks)))
		goto cleanup;

	if ((error = git_remote_fetch(remote, &refspecs, log_message)) == GIT_OK) {
		const git_transfer_progress *stats = git_remote_stats(remote);

		rb_result = rb_hash_new();
		rb_hash_aset(rb_result, CSTR2SYM("total_objects"),    UINT2NUM(stats->total_objects));
		rb_hash_aset(rb_result, CSTR2SYM("indexed_objects"),  UINT2NUM(stats->indexed_objects));
		rb_hash_aset(rb_result, CSTR2SYM("received_objects"), UINT2NUM(stats->received_objects));
		rb_hash_aset(rb_result, CSTR2SYM("local_objects"),    UINT2NUM(stats->local_objects));
		rb_hash_aset(rb_result, CSTR2SYM("total_deltas"),     UINT2NUM(stats->total_deltas));
		rb_hash_aset(rb_result, CSTR2SYM("indexed_deltas"),   UINT2NUM(stats->indexed_deltas));
		rb_hash_aset(rb_result, CSTR2SYM("received_bytes"),   INT2FIX(stats->received_bytes));
	}

	cleanup:

	xfree(refspecs.strings);

	if (payload.exception)
		rb_jump_tag(payload.exception);

	rugged_exception_check(error);

	return rb_result;
}
Exemple #17
0
void test_network_remote_local__tagopt(void)
{
    git_reference *ref;
    git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;

    cl_git_pass(git_remote_create(&remote, repo, "tagopt", cl_git_path_url(cl_fixture("testrepo.git"))));
    fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
    cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));

    cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master"));
    git_reference_free(ref);
    cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
    git_reference_free(ref);

    fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
    cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
    cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master"));
    git_reference_free(ref);
}
void GitRepository::fetch()
{
    git_repository* repo = repository();

    const char* remote_name = "origin";
    git_auto<git_remote> remote;
    git_eval(git_remote_lookup(&remote, repo, remote_name));
    git_eval(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL));
    git_eval(git_remote_fetch(remote, NULL, NULL, NULL));
}
Exemple #19
0
/** Entry point for this command */
int fetch(git_repository *repo, int argc, char **argv)
{
	git_remote *remote = NULL;
	const git_transfer_progress *stats;
	struct dl_data data;
	git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;

	if (argc < 2) {
		fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
		return EXIT_FAILURE;
	}

	// Figure out whether it's a named remote or a URL
	printf("Fetching %s for repo %p\n", argv[1], repo);
	if (git_remote_lookup(&remote, repo, argv[1]) < 0) {
		if (git_remote_create_anonymous(&remote, repo, argv[1]) < 0)
			return -1;
	}

	// Set up the callbacks (only update_tips for now)
	fetch_opts.callbacks.update_tips = &update_cb;
	fetch_opts.callbacks.sideband_progress = &progress_cb;
	fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
	fetch_opts.callbacks.credentials = cred_acquire_cb;

	/**
	 * Perform the fetch with the configured refspecs from the
	 * config. Update the reflog for the updated references with
	 * "fetch".
	 */
	if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
		return -1;

	/**
	 * If there are local objects (we got a thin pack), then tell
	 * the user how many objects we saved from having to cross the
	 * network.
	 */
	stats = git_remote_stats(remote);
	if (stats->local_objects > 0) {
		printf("\rReceived %d/%d objects in %" PRIuZ " bytes (used %d local objects)\n",
		       stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
	} else{
		printf("\rReceived %d/%d objects in %" PRIuZ "bytes\n",
			stats->indexed_objects, stats->total_objects, stats->received_bytes);
	}

	git_remote_free(remote);

	return 0;

 on_error:
	git_remote_free(remote);
	return -1;
}
Exemple #20
0
Future<void> GitRepo::fetch() const
{
	return async([this](Notifier notifier)
	{
		auto remote = GitResource<git_remote>::create(&git_remote_lookup, &git_remote_free, m_repo, "origin");

		GitPayload payload{notifier};

		git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
		opts.callbacks.transfer_progress = &gitFetchNotifier;
		opts.callbacks.payload = &payload;
		opts.callbacks.credentials = &credentialsCallback;

		GitException::checkAndThrow(git_remote_fetch(remote, nullptr, &opts, nullptr));
	});
}
Exemple #21
0
/**
 * Fetches the contents of the server into the "incoming" branch.
 */
int sync_fetch(git_repository *repo,
               const char *server)
{
    int e = 0;
    git_remote *remote = NULL;
    git_fetch_options options= GIT_FETCH_OPTIONS_INIT;
    char *refspec[] = {SYNC_FETCH_REFSPEC};
    git_strarray refspecs = {refspec, 1};

    git_check(git_remote_create_anonymous(&remote, repo, server));
    git_check(git_remote_fetch(remote, &refspecs, &options, "fetch"));

exit:
    if (remote)     git_remote_free(remote);
    return e;
}
Exemple #22
0
void QGit::fetch()
{
    git_repository *repo = nullptr;
    git_remote *remote = nullptr;
    int res = 0;

    QGitError error;

    try {

        res = git_repository_open(&repo, m_path.absolutePath().toUtf8().constData());
        if (res)
        {
            throw QGitError("git_repository_open", res);
        }

        res = git_remote_lookup(&remote, repo, "origin");
        if (res)
        {
            throw QGitError("git_remote_lookup", res);
        }

        res = git_remote_fetch(remote, nullptr, nullptr, "fetch");
        if (res)
        {
            throw QGitError("git_remote_fetch", res);
        }

    } catch(const QGitError &ex) {
        error = ex;
    }

    emit fetchReply(error);

    if (remote)
    {
        git_remote_free(remote);
        remote = nullptr;
    }

    if (repo)
    {
        git_repository_free(repo);
        repo = nullptr;
    }
}
Exemple #23
0
void test_checkout_index__honor_coresymlinks_default(void)
{
	git_repository *repo;
	git_remote *origin;
	git_object *target;
	char cwd[GIT_PATH_MAX];

	const char *url = git_repository_path(g_repo);

	cl_assert(getcwd(cwd, sizeof(cwd)) != NULL);
	cl_assert_equal_i(0, p_mkdir("readonly", 0555)); // Read-only directory
	cl_assert_equal_i(0, chdir("readonly"));
	cl_git_pass(git_repository_init(&repo, "../symlink.git", true));
	cl_assert_equal_i(0, chdir(cwd));
	cl_assert_equal_i(0, p_mkdir("symlink", 0777));
	cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));

	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
	cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
	git_remote_free(origin);

	cl_git_pass(git_revparse_single(&target, repo, "remotes/origin/master"));
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
	git_object_free(target);
	git_repository_free(repo);

#ifdef GIT_WIN32
	check_file_contents("./symlink/link_to_new.txt", "new.txt");
#else
	{
		char link_data[1024];
		size_t link_size = 1024;

		link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
		link_data[link_size] = '\0';
		cl_assert_equal_i(link_size, strlen("new.txt"));
		cl_assert_equal_s(link_data, "new.txt");
		check_file_contents("./symlink/link_to_new.txt", "my new file\n");
	}
#endif

	cl_fixture_cleanup("symlink");
}
Exemple #24
0
void test_network_remote_local__shorthand_fetch_refspec1(void)
{
    char *refspec_strings[] = {
        "master",
        "hard_tag",
    };
    git_strarray array = {
        refspec_strings,
        2,
    };

    git_reference *ref;

    connect_to_local_repository(cl_fixture("testrepo.git"));

    cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));

    cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/origin/master"));
    cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
}
Exemple #25
0
void test_network_remote_local__opportunistic_update(void)
{
    git_reference *ref;
    char *refspec_strings[] = {
        "master",
    };
    git_strarray array = {
        refspec_strings,
        1,
    };

    /* this remote has a passive refspec of "refs/heads/<star>:refs/remotes/origin/<star>" */
    cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git")));
    /* and we pass the active refspec "master" */
    cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));

    /* and we expect that to update our copy of origin's master */
    cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master"));
    git_reference_free(ref);
}
Exemple #26
0
static int clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature)
{
	int error;
	git_buf reflog_message = GIT_BUF_INIT;
	git_remote *remote;
	const git_remote_callbacks *callbacks;

	assert(repo && _remote);

	if (!git_repository_is_empty(repo)) {
		giterr_set(GITERR_INVALID, "the repository is not empty");
		return -1;
	}

	if ((error = git_remote_dup(&remote, _remote)) < 0)
		return error;

	callbacks = git_remote_get_callbacks(_remote);
	if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") &&
	    (error = git_remote_set_callbacks(remote, callbacks)) < 0)
		goto cleanup;

	if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
		goto cleanup;

	git_remote_set_update_fetchhead(remote, 0);
	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

	if ((error = git_remote_fetch(remote, signature, git_buf_cstr(&reflog_message))) != 0)
		goto cleanup;

	error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message));

cleanup:
	git_remote_free(remote);
	git_buf_free(&reflog_message);

	return error;
}
Exemple #27
0
int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch)
{
	int error = 0, old_fetchhead;
	size_t nspecs;

	assert(repo && remote);

	if (!git_repository_is_empty(repo)) {
		giterr_set(GITERR_INVALID, "the repository is not empty");
		return -1;
	}

	if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0)
		return error;

	old_fetchhead = git_remote_update_fetchhead(remote);
	git_remote_set_update_fetchhead(remote, 0);

	if ((error = git_remote_fetch(remote)) < 0)
		goto cleanup;

	if (branch)
		error = update_head_to_branch(repo, git_remote_name(remote), branch);
	/* Point HEAD to the same ref as the remote's head */
	else
		error = update_head_to_remote(repo, remote);

	if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
		error = git_checkout_head(repo, co_opts);

cleanup:
	git_remote_set_update_fetchhead(remote, old_fetchhead);
	/* Remove the tags refspec */
	nspecs = git_remote_refspec_count(remote);
	git_remote_remove_refspec(remote, nspecs);

	return error;
}
Exemple #28
0
void test_network_remote_local__shorthand_fetch_refspec0(void)
{
    char *refspec_strings[] = {
        "master:remotes/sloppy/master",
        "master:boh/sloppy/master",
    };
    git_strarray array = {
        refspec_strings,
        2,
    };

    git_reference *ref;

    connect_to_local_repository(cl_fixture("testrepo.git"));

    cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));

    cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
    git_reference_free(ref);

    cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
    git_reference_free(ref);
}
Exemple #29
0
/**
 * Fetch new data and update tips
 *
 * @param repo S4 class git_repository
 * @param name The name of the remote to fetch from
 * @param credentials The credentials for remote repository access.
 * @param msg The one line long message to be appended to the reflog
 * @param verbose Print information each time a reference is updated locally.
 * @param refspecs The refspecs to use for this fetch. Pass R_NilValue
 *        to use the base refspecs.
 * @return R_NilValue
 */
SEXP git2r_remote_fetch(
    SEXP repo,
    SEXP name,
    SEXP credentials,
    SEXP msg,
    SEXP verbose,
    SEXP refspecs)
{
    int err;
    SEXP result = R_NilValue;
    const git_transfer_progress *stats;
    git_remote *remote = NULL;
    git_repository *repository = NULL;
    git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
    git2r_transfer_data payload = GIT2R_TRANSFER_DATA_INIT;
    git_strarray refs = {0};

    if (git2r_arg_check_string(name))
        git2r_error(__func__, NULL, "'name'", git2r_err_string_arg);
    if (git2r_arg_check_credentials(credentials))
        git2r_error(__func__, NULL, "'credentials'", git2r_err_credentials_arg);
    if (git2r_arg_check_string(msg))
        git2r_error(__func__, NULL, "'msg'", git2r_err_string_arg);
    if (git2r_arg_check_logical(verbose))
        git2r_error(__func__, NULL, "'verbose'", git2r_err_logical_arg);
    if (refspecs != R_NilValue && git2r_arg_check_string_vec(refspecs))
        git2r_error(__func__, NULL, "'refspecs'", git2r_err_string_vec_arg);

    repository = git2r_repository_open(repo);
    if (!repository)
        git2r_error(__func__, NULL, git2r_err_invalid_repository, NULL);

    err = git_remote_lookup(&remote, repository, CHAR(STRING_ELT(name, 0)));
    if (err)
        goto cleanup;

    if (refspecs != R_NilValue) {
        size_t i, len;

        /* Count number of non NA values */
        len = length(refspecs);
        for (i = 0; i < len; i++)
            if (NA_STRING != STRING_ELT(refspecs, i))
                refs.count++;

        if (refs.count) {
            /* Allocate the strings in refs */
            refs.strings = malloc(refs.count * sizeof(char*));
            if (!refs.strings) {
                giterr_set_str(GITERR_NONE, git2r_err_alloc_memory_buffer);
                err = GIT_ERROR;
                goto cleanup;
            }

            /* Populate the strings in refs */
            for (i = 0; i < refs.count; i++)
                if (NA_STRING != STRING_ELT(refspecs, i))
                    refs.strings[i] = (char *)CHAR(STRING_ELT(refspecs, i));
        }
    }

    if (LOGICAL(verbose)[0])
        payload.verbose = 1;
    payload.credentials = credentials;
    fetch_opts.callbacks.payload = &payload;
    fetch_opts.callbacks.credentials = &git2r_cred_acquire_cb;
    fetch_opts.callbacks.update_tips = &git2r_update_tips_cb;
    err = git_remote_fetch(remote, &refs, &fetch_opts, CHAR(STRING_ELT(msg, 0)));
    if (err)
        goto cleanup;

    stats = git_remote_stats(remote);
    PROTECT(result = NEW_OBJECT(MAKE_CLASS("git_transfer_progress")));
    git2r_transfer_progress_init(stats, result);

cleanup:
    if (refs.strings)
        free(refs.strings);

    if (remote) {
        if (git_remote_connected(remote))
            git_remote_disconnect(remote);
        git_remote_free(remote);
    }

    if (repository)
        git_repository_free(repository);

    if (R_NilValue != result)
        UNPROTECT(1);

    if (err)
        git2r_error(
            __func__,
            giterr_last(),
            git2r_err_unable_to_authenticate,
            NULL);

    return result;
}
Exemple #30
0
/*
 *  call-seq:
 *    remote.fetch(refspecs = nil, options = {}) -> hash
 *
 *  Downloads new data from the remote for the given +refspecs+ and updates tips.
 *
 *  You can optionally pass in an alternative list of +refspecs+ to use instead of the fetch
 *  refspecs already configured for +remote+.
 *
 *  Returns a hash containing statistics for the fetch operation.
 *
 *  The following options can be passed in the +options+ Hash:
 *
 *  :credentials ::
 *    The credentials to use for the fetch operation. Can be either an instance of one
 *    of the Rugged::Credentials types, or a proc returning one of the former.
 *    The proc will be called with the +url+, the +username+ from the url (if applicable) and
 *    a list of applicable credential types.
 *
 *  :progress ::
 *    A callback that will be executed with the textual progress received from the remote.
 *    This is the text send over the progress side-band (ie. the "counting objects" output).
 *
 *  :transfer_progress ::
 *    A callback that will be executed to report clone progress information. It will be passed
 *    the amount of +total_objects+, +indexed_objects+, +received_objects+, +local_objects+,
 *    +total_deltas+, +indexed_deltas+ and +received_bytes+.
 *
 *  :update_tips ::
 *    A callback that will be executed each time a reference is updated locally. It will be
 *    passed the +refname+, +old_oid+ and +new_oid+.
 *
 *  :message ::
 *    The message to insert into the reflogs. Defaults to "fetch".
 *
 *  :signature ::
 *    The signature to be used for updating the reflogs.
 *
 *  Example:
 *
 *    remote = Rugged::Remote.lookup(@repo, 'origin')
 *    remote.fetch({
 *      transfer_progress: lambda { |total_objects, indexed_objects, received_objects, local_objects, total_deltas, indexed_deltas, received_bytes|
 *        # ...
 *      }
 *    })
 */
static VALUE rb_git_remote_fetch(int argc, VALUE *argv, VALUE self)
{
	git_remote *remote, *tmp_remote = NULL;
	git_repository *repo;
	git_signature *signature = NULL;
	git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
	struct rugged_remote_cb_payload payload = { Qnil, Qnil, Qnil, Qnil, Qnil, 0 };

	char *log_message = NULL;
	int error, i;

	VALUE rb_options, rb_refspecs, rb_result = Qnil, rb_repo = rugged_owner(self);

	rb_scan_args(argc, argv, "01:", &rb_refspecs, &rb_options);

	if (!NIL_P(rb_refspecs)) {
		Check_Type(rb_refspecs, T_ARRAY);
		for (i = 0; i < RARRAY_LEN(rb_refspecs); ++i) {
			VALUE rb_refspec = rb_ary_entry(rb_refspecs, i);
			Check_Type(rb_refspec, T_STRING);
		}
	}

	Data_Get_Struct(self, git_remote, remote);
	rugged_check_repo(rb_repo);
	Data_Get_Struct(rb_repo, git_repository, repo);

	if (!NIL_P(rb_options)) {
		VALUE rb_val = rb_hash_aref(rb_options, CSTR2SYM("signature"));
		if (!NIL_P(rb_val))
			signature = rugged_signature_get(rb_val, repo);

		rb_val = rb_hash_aref(rb_options, CSTR2SYM("message"));
		if (!NIL_P(rb_val))
			log_message = StringValueCStr(rb_val);

		rugged_remote_init_callbacks_and_payload_from_options(rb_options, &callbacks, &payload);
	}

	if ((error = git_remote_dup(&tmp_remote, remote)) ||
		(error = git_remote_set_callbacks(tmp_remote, &callbacks)))
		goto cleanup;

	if (!NIL_P(rb_refspecs)) {
		git_remote_clear_refspecs(tmp_remote);
		for (i = 0; !error && i < RARRAY_LEN(rb_refspecs); ++i) {
			VALUE rb_refspec = rb_ary_entry(rb_refspecs, i);

			if ((error = git_remote_add_fetch(tmp_remote, StringValueCStr(rb_refspec))))
				goto cleanup;
		}
	}

	if ((error = git_remote_fetch(tmp_remote, signature, log_message)) == GIT_OK) {
		const git_transfer_progress *stats = git_remote_stats(tmp_remote);

		rb_result = rb_hash_new();
		rb_hash_aset(rb_result, CSTR2SYM("total_objects"),    UINT2NUM(stats->total_objects));
		rb_hash_aset(rb_result, CSTR2SYM("indexed_objects"),  UINT2NUM(stats->indexed_objects));
		rb_hash_aset(rb_result, CSTR2SYM("received_objects"), UINT2NUM(stats->received_objects));
		rb_hash_aset(rb_result, CSTR2SYM("local_objects"),    UINT2NUM(stats->local_objects));
		rb_hash_aset(rb_result, CSTR2SYM("total_deltas"),     UINT2NUM(stats->total_deltas));
		rb_hash_aset(rb_result, CSTR2SYM("indexed_deltas"),   UINT2NUM(stats->indexed_deltas));
		rb_hash_aset(rb_result, CSTR2SYM("received_bytes"),   INT2FIX(stats->received_bytes));
	}

	cleanup:

	git_signature_free(signature);
	git_remote_free(tmp_remote);

	if (payload.exception)
		rb_jump_tag(payload.exception);

	rugged_exception_check(error);

	return rb_result;
}