Unverified Commit 1d811f0e by Edward Thomson Committed by GitHub

Merge pull request #6203 from libgit2/ethomson/fetch_by_oid

Fetch by object id
parents d299a7aa 9d88300a
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_remote_h
#define INCLUDE_sys_git_remote_h
/**
* @file git2/sys/remote.h
* @brief Low-level remote functionality for custom transports
* @defgroup git_remote Low-level remote functionality
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef enum {
/** Remote supports fetching an advertised object by ID. */
GIT_REMOTE_CAPABILITY_TIP_OID = (1 << 0),
/** Remote supports fetching an individual reachable object. */
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
} git_remote_capability_t;
/** @} */
GIT_END_DECL
#endif
...@@ -47,6 +47,16 @@ struct git_transport { ...@@ -47,6 +47,16 @@ struct git_transport {
const git_remote_connect_options *connect_opts); const git_remote_connect_options *connect_opts);
/** /**
* Gets the capabilities for this remote repository.
*
* This function may be called after a successful call to
* `connect()`.
*/
int GIT_CALLBACK(capabilities)(
unsigned int *capabilities,
git_transport *transport);
/**
* Get the list of available references in the remote repository. * Get the list of available references in the remote repository.
* *
* This function may be called after a successful call to * This function may be called after a successful call to
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "git2/refs.h" #include "git2/refs.h"
#include "git2/revwalk.h" #include "git2/revwalk.h"
#include "git2/transport.h" #include "git2/transport.h"
#include "git2/sys/remote.h"
#include "remote.h" #include "remote.h"
#include "refspec.h" #include "refspec.h"
...@@ -19,7 +20,7 @@ ...@@ -19,7 +20,7 @@
#include "repository.h" #include "repository.h"
#include "refs.h" #include "refs.h"
static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt) static int maybe_want(git_remote *remote, git_remote_head *head, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
{ {
int match = 0, valid; int match = 0, valid;
...@@ -44,23 +45,57 @@ static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, g ...@@ -44,23 +45,57 @@ static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, g
if (!match) if (!match)
return 0; return 0;
/* If we have the object, mark it so we don't ask for it */ return git_vector_insert(&remote->refs, head);
if (git_odb_exists(odb, &head->oid)) { }
head->local = 1;
static int mark_local(git_remote *remote)
{
git_remote_head *head;
git_odb *odb;
size_t i;
if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
return -1;
git_vector_foreach(&remote->refs, i, head) {
/* If we have the object, mark it so we don't ask for it */
if (git_odb_exists(odb, &head->oid))
head->local = 1;
else
remote->need_pack = 1;
} }
else
remote->need_pack = 1;
return git_vector_insert(&remote->refs, head); return 0;
}
static int maybe_want_oid(git_remote *remote, git_refspec *spec)
{
git_remote_head *oid_head;
oid_head = git__calloc(1, sizeof(git_remote_head));
GIT_ERROR_CHECK_ALLOC(oid_head);
git_oid_fromstr(&oid_head->oid, spec->src);
oid_head->name = git__strdup(spec->dst);
GIT_ERROR_CHECK_ALLOC(oid_head->name);
if (git_vector_insert(&remote->local_heads, oid_head) < 0 ||
git_vector_insert(&remote->refs, oid_head) < 0)
return -1;
return 0;
} }
static int filter_wants(git_remote *remote, const git_fetch_options *opts) static int filter_wants(git_remote *remote, const git_fetch_options *opts)
{ {
git_remote_head **heads; git_remote_head **heads;
git_refspec tagspec, head; git_refspec tagspec, head, *spec;
int error = 0; int error = 0;
git_odb *odb; git_odb *odb;
size_t i, heads_len; size_t i, heads_len;
unsigned int remote_caps;
unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
GIT_REMOTE_CAPABILITY_REACHABLE_OID;
git_remote_autotag_option_t tagopt = remote->download_tags; git_remote_autotag_option_t tagopt = remote->download_tags;
if (opts && opts->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED) if (opts && opts->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
...@@ -90,14 +125,33 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts) ...@@ -90,14 +125,33 @@ static int filter_wants(git_remote *remote, const git_fetch_options *opts)
if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0) if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
goto cleanup; goto cleanup;
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0) if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
(error = git_remote_capabilities(&remote_caps, remote)) < 0)
goto cleanup; goto cleanup;
/* Handle remote heads */
for (i = 0; i < heads_len; i++) { for (i = 0; i < heads_len; i++) {
if ((error = maybe_want(remote, heads[i], odb, &tagspec, tagopt)) < 0) if ((error = maybe_want(remote, heads[i], &tagspec, tagopt)) < 0)
break; goto cleanup;
}
/* Handle explicitly specified OID specs */
git_vector_foreach(&remote->active_refspecs, i, spec) {
if (!git_oid__is_hexstr(spec->src))
continue;
if (!(remote_caps & oid_mask)) {
git_error_set(GIT_ERROR_INVALID, "cannot fetch a specific object from the remote repository");
error = -1;
goto cleanup;
}
if ((error = maybe_want_oid(remote, spec)) < 0)
goto cleanup;
} }
error = mark_local(remote);
cleanup: cleanup:
git_refspec__dispose(&tagspec); git_refspec__dispose(&tagspec);
...@@ -115,10 +169,8 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts) ...@@ -115,10 +169,8 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
remote->need_pack = 0; remote->need_pack = 0;
if (filter_wants(remote, opts) < 0) { if (filter_wants(remote, opts) < 0)
git_error_set(GIT_ERROR_NET, "failed to filter the reference list for wants");
return -1; return -1;
}
/* Don't try to negotiate when we don't want anything */ /* Don't try to negotiate when we don't want anything */
if (!remote->need_pack) if (!remote->need_pack)
......
...@@ -48,4 +48,16 @@ GIT_INLINE(void) git_oid__cpy_prefix( ...@@ -48,4 +48,16 @@ GIT_INLINE(void) git_oid__cpy_prefix(
out->id[len / 2] &= 0xF0; out->id[len / 2] &= 0xF0;
} }
GIT_INLINE(bool) git_oid__is_hexstr(const char *str)
{
size_t i;
for (i = 0; str[i] != '\0'; i++) {
if (git__fromhex(str[i]) < 0)
return false;
}
return (i == GIT_OID_HEXSZ);
}
#endif #endif
...@@ -1012,6 +1012,20 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote ...@@ -1012,6 +1012,20 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote
return remote->transport->ls(out, size, remote->transport); return remote->transport->ls(out, size, remote->transport);
} }
int git_remote_capabilities(unsigned int *out, git_remote *remote)
{
GIT_ASSERT_ARG(remote);
*out = 0;
if (!remote->transport) {
git_error_set(GIT_ERROR_NET, "this remote has never connected");
return -1;
}
return remote->transport->capabilities(out, remote->transport);
}
static int lookup_config(char **out, git_config *cfg, const char *name) static int lookup_config(char **out, git_config *cfg, const char *name)
{ {
git_config_entry *ce = NULL; git_config_entry *ce = NULL;
...@@ -1702,141 +1716,207 @@ cleanup: ...@@ -1702,141 +1716,207 @@ cleanup:
return error; return error;
} }
static int update_tips_for_spec( static int update_ref(
git_remote *remote, const git_remote *remote,
const git_remote_callbacks *callbacks, const char *ref_name,
int update_fetchhead, git_oid *id,
git_remote_autotag_option_t tagopt, const char *msg,
git_refspec *spec, const git_remote_callbacks *callbacks)
git_vector *refs,
const char *log_message)
{ {
int error = 0, autotag, valid;
unsigned int i = 0;
git_str refname = GIT_STR_INIT;
git_oid old;
git_odb *odb;
git_remote_head *head;
git_reference *ref; git_reference *ref;
git_refspec tagspec; git_oid old_id;
git_vector update_heads; int error;
GIT_ASSERT_ARG(remote); error = git_reference_name_to_id(&old_id, remote->repo, ref_name);
if (git_repository_odb__weakptr(&odb, remote->repo) < 0) if (error < 0 && error != GIT_ENOTFOUND)
return -1; return error;
else if (error == 0 && git_oid_equal(&old_id, id))
return 0;
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) /* If we did find a current reference, make sure we haven't lost a race */
return -1; if (error)
error = git_reference_create(&ref, remote->repo, ref_name, id, true, msg);
else
error = git_reference_create_matching(&ref, remote->repo, ref_name, id, true, &old_id, msg);
/* Make a copy of the transport's refs */ git_reference_free(ref);
if (git_vector_init(&update_heads, 16, NULL) < 0)
return -1;
for (; i < refs->length; ++i) { if (error < 0)
head = git_vector_get(refs, i); return error;
autotag = 0;
git_str_clear(&refname);
/* Ignore malformed ref names (which also saves us from tag^{} */ if (callbacks && callbacks->update_tips &&
if (git_reference_name_is_valid(&valid, head->name) < 0) (error = callbacks->update_tips(ref_name, &old_id, id, callbacks->payload)) < 0)
goto on_error; return error;
if (!valid) return 0;
continue; }
/* If we have a tag, see if the auto-follow rules say to update it */ static int update_one_tip(
if (git_refspec_src_matches(&tagspec, head->name)) { git_vector *update_heads,
if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { git_remote *remote,
git_refspec *spec,
git_remote_head *head,
git_refspec *tagspec,
git_remote_autotag_option_t tagopt,
const char *log_message,
const git_remote_callbacks *callbacks)
{
git_odb *odb;
git_str refname = GIT_STR_INIT;
git_reference *ref = NULL;
bool autotag = false;
git_oid old;
int valid;
int error;
if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO) if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
autotag = 1; goto done;
git_str_clear(&refname); /* Ignore malformed ref names (which also saves us from tag^{} */
if (git_str_puts(&refname, head->name) < 0) if ((error = git_reference_name_is_valid(&valid, head->name)) < 0)
goto on_error; goto done;
}
}
/* If we didn't want to auto-follow the tag, check if the refspec matches */ if (!valid)
if (!autotag && git_refspec_src_matches(spec, head->name)) { goto done;
if (spec->dst) {
if (git_refspec__transform(&refname, spec, head->name) < 0)
goto on_error;
} else {
/*
* no rhs mans store it in FETCH_HEAD, even if we don't
update anything else.
*/
if ((error = git_vector_insert(&update_heads, head)) < 0)
goto on_error;
continue; /* If we have a tag, see if the auto-follow rules say to update it */
} if (git_refspec_src_matches(tagspec, head->name)) {
if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
autotag = true;
if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
if (git_str_puts(&refname, head->name) < 0)
goto done;
} }
}
/* If we still don't have a refname, we don't want it */ /* If we didn't want to auto-follow the tag, check if the refspec matches */
if (git_str_len(&refname) == 0) { if (!autotag && git_refspec_src_matches(spec, head->name)) {
continue; if (spec->dst) {
if ((error = git_refspec__transform(&refname, spec, head->name)) < 0)
goto done;
} else {
/*
* no rhs means store it in FETCH_HEAD, even if we don't
* update anything else.
*/
error = git_vector_insert(update_heads, head);
goto done;
} }
}
/* In autotag mode, only create tags for objects already in db */ /* If we still don't have a refname, we don't want it */
if (autotag && !git_odb_exists(odb, &head->oid)) if (git_str_len(&refname) == 0)
continue; goto done;
if (!autotag && git_vector_insert(&update_heads, head) < 0) /* In autotag mode, only create tags for objects already in db */
goto on_error; if (autotag && !git_odb_exists(odb, &head->oid))
goto done;
error = git_reference_name_to_id(&old, remote->repo, refname.ptr); if (!autotag && (error = git_vector_insert(update_heads, head)) < 0)
if (error < 0 && error != GIT_ENOTFOUND) goto done;
goto on_error;
if (!(error || error == GIT_ENOTFOUND) error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
&& !spec->force
&& !git_graph_descendant_of(remote->repo, &head->oid, &old))
continue;
if (error == GIT_ENOTFOUND) { if (error < 0 && error != GIT_ENOTFOUND)
memset(&old, 0, GIT_OID_RAWSZ); goto done;
if (autotag && git_vector_insert(&update_heads, head) < 0) if (!(error || error == GIT_ENOTFOUND) &&
goto on_error; !spec->force &&
} !git_graph_descendant_of(remote->repo, &head->oid, &old)) {
error = 0;
goto done;
}
if (!git_oid__cmp(&old, &head->oid)) if (error == GIT_ENOTFOUND) {
continue; memset(&old, 0, GIT_OID_RAWSZ);
error = 0;
/* In autotag mode, don't overwrite any locally-existing tags */ if (autotag && (error = git_vector_insert(update_heads, head)) < 0)
error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, goto done;
log_message); }
if (!git_oid__cmp(&old, &head->oid))
goto done;
/* In autotag mode, don't overwrite any locally-existing tags */
error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
log_message);
if (error < 0) {
if (error == GIT_EEXISTS) if (error == GIT_EEXISTS)
continue; error = 0;
if (error < 0) goto done;
}
if (callbacks && callbacks->update_tips != NULL &&
callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
git_error_set_after_callback_function(error, "git_remote_fetch");
done:
git_reference_free(ref);
git_str_dispose(&refname);
return error;
}
static int update_tips_for_spec(
git_remote *remote,
const git_remote_callbacks *callbacks,
int update_fetchhead,
git_remote_autotag_option_t tagopt,
git_refspec *spec,
git_vector *refs,
const char *log_message)
{
git_refspec tagspec;
git_remote_head *head, oid_head;
git_vector update_heads;
int error = 0;
size_t i;
GIT_ASSERT_ARG(remote);
if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
return -1;
/* Make a copy of the transport's refs */
if (git_vector_init(&update_heads, 16, NULL) < 0)
return -1;
/* Update tips based on the remote heads */
git_vector_foreach(refs, i, head) {
if (update_one_tip(&update_heads, remote, spec, head, &tagspec, tagopt, log_message, callbacks) < 0)
goto on_error; goto on_error;
}
git_reference_free(ref); /* Handle specified oid sources */
if (git_oid__is_hexstr(spec->src)) {
git_oid id;
if (callbacks && callbacks->update_tips != NULL) { if ((error = git_oid_fromstr(&id, spec->src)) < 0 ||
if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0) (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
goto on_error; goto on_error;
}
git_oid_cpy(&oid_head.oid, &id);
oid_head.name = spec->src;
if ((error = git_vector_insert(&update_heads, &oid_head)) < 0)
goto on_error;
} }
if (update_fetchhead && if (update_fetchhead &&
(error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0) (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
goto on_error; goto on_error;
git_vector_free(&update_heads);
git_refspec__dispose(&tagspec); git_refspec__dispose(&tagspec);
git_str_dispose(&refname); git_vector_free(&update_heads);
return 0; return 0;
on_error: on_error:
git_vector_free(&update_heads);
git_refspec__dispose(&tagspec); git_refspec__dispose(&tagspec);
git_str_dispose(&refname); git_vector_free(&update_heads);
return -1; return -1;
} }
...@@ -1902,20 +1982,22 @@ static int next_head(const git_remote *remote, git_vector *refs, ...@@ -1902,20 +1982,22 @@ static int next_head(const git_remote *remote, git_vector *refs,
return GIT_ITEROVER; return GIT_ITEROVER;
} }
static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks, static int opportunistic_updates(
git_vector *refs, const char *msg) const git_remote *remote,
const git_remote_callbacks *callbacks,
git_vector *refs,
const char *msg)
{ {
size_t i, j, k; size_t i, j, k;
git_refspec *spec; git_refspec *spec;
git_remote_head *head; git_remote_head *head;
git_reference *ref;
git_str refname = GIT_STR_INIT; git_str refname = GIT_STR_INIT;
int error = 0; int error = 0;
i = j = k = 0; i = j = k = 0;
/* Handle refspecs matching remote heads */
while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
git_oid old = {{ 0 }};
/* /*
* If we got here, there is a refspec which was used * If we got here, there is a refspec which was used
* for fetching which matches the source of one of the * for fetching which matches the source of one of the
...@@ -1925,33 +2007,15 @@ static int opportunistic_updates(const git_remote *remote, const git_remote_call ...@@ -1925,33 +2007,15 @@ static int opportunistic_updates(const git_remote *remote, const git_remote_call
*/ */
git_str_clear(&refname); git_str_clear(&refname);
if ((error = git_refspec__transform(&refname, spec, head->name)) < 0) if ((error = git_refspec__transform(&refname, spec, head->name)) < 0 ||
goto cleanup; (error = update_ref(remote, refname.ptr, &head->oid, msg, callbacks)) < 0)
error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
if (error < 0 && error != GIT_ENOTFOUND)
goto cleanup;
if (!git_oid_cmp(&old, &head->oid))
continue;
/* If we did find a current reference, make sure we haven't lost a race */
if (error)
error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg);
else
error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg);
git_reference_free(ref);
if (error < 0)
goto cleanup; goto cleanup;
if (callbacks && callbacks->update_tips != NULL) {
if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
goto cleanup;
}
} }
if (error == GIT_ITEROVER) if (error != GIT_ITEROVER)
error = 0; goto cleanup;
error = 0;
cleanup: cleanup:
git_str_dispose(&refname); git_str_dispose(&refname);
...@@ -2018,7 +2082,7 @@ int git_remote_update_tips( ...@@ -2018,7 +2082,7 @@ int git_remote_update_tips(
goto out; goto out;
} }
/* Only try to do opportunistic updates if the refpec lists differ. */ /* Only try to do opportunistic updates if the refspec lists differ. */
if (remote->passed_refspecs) if (remote->passed_refspecs)
error = opportunistic_updates(remote, callbacks, &refs, reflog_message); error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
...@@ -2059,6 +2123,17 @@ int git_remote_disconnect(git_remote *remote) ...@@ -2059,6 +2123,17 @@ int git_remote_disconnect(git_remote *remote)
return 0; return 0;
} }
static void free_heads(git_vector *heads)
{
git_remote_head *head;
size_t i;
git_vector_foreach(heads, i, head) {
git__free(head->name);
git__free(head);
}
}
void git_remote_free(git_remote *remote) void git_remote_free(git_remote *remote)
{ {
if (remote == NULL) if (remote == NULL)
...@@ -2082,6 +2157,9 @@ void git_remote_free(git_remote *remote) ...@@ -2082,6 +2157,9 @@ void git_remote_free(git_remote *remote)
free_refspecs(&remote->passive_refspecs); free_refspecs(&remote->passive_refspecs);
git_vector_free(&remote->passive_refspecs); git_vector_free(&remote->passive_refspecs);
free_heads(&remote->local_heads);
git_vector_free(&remote->local_heads);
git_push_free(remote->push); git_push_free(remote->push);
git__free(remote->url); git__free(remote->url);
git__free(remote->pushurl); git__free(remote->pushurl);
......
...@@ -27,6 +27,7 @@ struct git_remote { ...@@ -27,6 +27,7 @@ struct git_remote {
git_vector refspecs; git_vector refspecs;
git_vector active_refspecs; git_vector active_refspecs;
git_vector passive_refspecs; git_vector passive_refspecs;
git_vector local_heads;
git_transport *transport; git_transport *transport;
git_repository *repo; git_repository *repo;
git_push *push; git_push *push;
...@@ -54,4 +55,6 @@ int git_remote_connect_options_normalize( ...@@ -54,4 +55,6 @@ int git_remote_connect_options_normalize(
const git_remote_connect_options *src); const git_remote_connect_options *src);
void git_remote_connect_options_dispose(git_remote_connect_options *opts); void git_remote_connect_options_dispose(git_remote_connect_options *opts);
int git_remote_capabilities(unsigned int *out, git_remote *remote);
#endif #endif
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "git2/pack.h" #include "git2/pack.h"
#include "git2/commit.h" #include "git2/commit.h"
#include "git2/revparse.h" #include "git2/revparse.h"
#include "git2/sys/remote.h"
typedef struct { typedef struct {
git_transport parent; git_transport parent;
...@@ -256,6 +257,15 @@ static int local_set_connect_opts( ...@@ -256,6 +257,15 @@ static int local_set_connect_opts(
return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts); return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
} }
static int local_capabilities(unsigned int *capabilities, git_transport *transport)
{
GIT_UNUSED(transport);
*capabilities = GIT_REMOTE_CAPABILITY_TIP_OID |
GIT_REMOTE_CAPABILITY_REACHABLE_OID;
return 0;
}
static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport) static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{ {
transport_local *t = (transport_local *)transport; transport_local *t = (transport_local *)transport;
...@@ -721,6 +731,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param) ...@@ -721,6 +731,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param)
t->parent.version = GIT_TRANSPORT_VERSION; t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.connect = local_connect; t->parent.connect = local_connect;
t->parent.set_connect_opts = local_set_connect_opts; t->parent.set_connect_opts = local_set_connect_opts;
t->parent.capabilities = local_capabilities;
t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.negotiate_fetch = local_negotiate_fetch;
t->parent.download_pack = local_download_pack; t->parent.download_pack = local_download_pack;
t->parent.push = local_push; t->parent.push = local_push;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "smart.h" #include "smart.h"
#include "git2.h" #include "git2.h"
#include "git2/sys/remote.h"
#include "refs.h" #include "refs.h"
#include "refspec.h" #include "refspec.h"
#include "proxy.h" #include "proxy.h"
...@@ -226,6 +227,21 @@ static int git_smart__set_connect_opts( ...@@ -226,6 +227,21 @@ static int git_smart__set_connect_opts(
return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, opts); return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, opts);
} }
static int git_smart__capabilities(unsigned int *capabilities, git_transport *transport)
{
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
*capabilities = 0;
if (t->caps.want_tip_sha1)
*capabilities |= GIT_REMOTE_CAPABILITY_TIP_OID;
if (t->caps.want_reachable_sha1)
*capabilities |= GIT_REMOTE_CAPABILITY_REACHABLE_OID;
return 0;
}
static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport) static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
{ {
transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent); transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
...@@ -423,6 +439,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) ...@@ -423,6 +439,7 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param)
t->parent.version = GIT_TRANSPORT_VERSION; t->parent.version = GIT_TRANSPORT_VERSION;
t->parent.connect = git_smart__connect; t->parent.connect = git_smart__connect;
t->parent.set_connect_opts = git_smart__set_connect_opts; t->parent.set_connect_opts = git_smart__set_connect_opts;
t->parent.capabilities = git_smart__capabilities;
t->parent.close = git_smart__close; t->parent.close = git_smart__close;
t->parent.free = git_smart__free; t->parent.free = git_smart__free;
t->parent.negotiate_fetch = git_smart__negotiate_fetch; t->parent.negotiate_fetch = git_smart__negotiate_fetch;
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#define GIT_CAP_REPORT_STATUS "report-status" #define GIT_CAP_REPORT_STATUS "report-status"
#define GIT_CAP_THIN_PACK "thin-pack" #define GIT_CAP_THIN_PACK "thin-pack"
#define GIT_CAP_SYMREF "symref" #define GIT_CAP_SYMREF "symref"
#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
extern bool git_smart__ofs_delta_enabled; extern bool git_smart__ofs_delta_enabled;
...@@ -128,7 +130,9 @@ typedef struct transport_smart_caps { ...@@ -128,7 +130,9 @@ typedef struct transport_smart_caps {
include_tag:1, include_tag:1,
delete_refs:1, delete_refs:1,
report_status:1, report_status:1,
thin_pack:1; thin_pack:1,
want_tip_sha1:1,
want_reachable_sha1:1;
} transport_smart_caps; } transport_smart_caps;
typedef int (*packetsize_cb)(size_t received, void *payload); typedef int (*packetsize_cb)(size_t received, void *payload);
......
...@@ -205,6 +205,18 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec ...@@ -205,6 +205,18 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec
continue; continue;
} }
if (!git__prefixcmp(ptr, GIT_CAP_WANT_TIP_SHA1)) {
caps->common = caps->want_tip_sha1 = 1;
ptr += strlen(GIT_CAP_DELETE_REFS);
continue;
}
if (!git__prefixcmp(ptr, GIT_CAP_WANT_REACHABLE_SHA1)) {
caps->common = caps->want_reachable_sha1 = 1;
ptr += strlen(GIT_CAP_DELETE_REFS);
continue;
}
/* We don't know this capability, so skip it */ /* We don't know this capability, so skip it */
ptr = strchr(ptr, ' '); ptr = strchr(ptr, ' ');
} }
......
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "oid.h"
static git_oid id; static git_oid id;
static git_oid idp; static git_oid idp;
...@@ -68,3 +69,11 @@ void test_core_oid__ncmp(void) ...@@ -68,3 +69,11 @@ void test_core_oid__ncmp(void)
cl_assert(!git_oid_ncmp(&id, &id, 40)); cl_assert(!git_oid_ncmp(&id, &id, 40));
cl_assert(!git_oid_ncmp(&id, &id, 41)); cl_assert(!git_oid_ncmp(&id, &id, 41));
} }
void test_core_oid__is_hexstr(void)
{
cl_assert(git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
cl_assert(!git_oid__is_hexstr("deadbeefdeadbeef"));
cl_assert(!git_oid__is_hexstr("zeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
cl_assert(!git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef1"));
}
#include "clar_libgit2.h"
#include "futils.h"
static git_repository *repo;
void test_fetch_local__initialize(void)
{
cl_git_pass(git_repository_init(&repo, "./fetch", 0));
}
void test_fetch_local__cleanup(void)
{
git_repository_free(repo);
repo = NULL;
cl_fixture_cleanup("./fetch");
}
void test_fetch_local__defaults(void)
{
git_remote *remote;
git_object *obj;
git_oid expected_id;
cl_git_pass(git_remote_create(&remote, repo, "test",
cl_fixture("testrepo.git")));
cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
git_oid_fromstr(&expected_id, "258f0e2a959a364e40ed6603d5d44fbb24765b10");
cl_git_pass(git_revparse_single(&obj, repo, "refs/remotes/test/haacked"));
cl_assert_equal_oid(&expected_id, git_object_id(obj));
git_object_free(obj);
git_remote_free(remote);
}
void test_fetch_local__reachable_commit(void)
{
git_remote *remote;
git_strarray refspecs;
git_object *obj;
git_oid expected_id;
git_str fetchhead = GIT_STR_INIT;
char *refspec = "+5b5b025afb0b4c913b4c338a42934a3863bf3644:refs/success";
refspecs.strings = &refspec;
refspecs.count = 1;
git_oid_fromstr(&expected_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
cl_git_pass(git_remote_create(&remote, repo, "test",
cl_fixture("testrepo.git")));
cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
cl_git_pass(git_revparse_single(&obj, repo, "refs/success"));
cl_assert_equal_oid(&expected_id, git_object_id(obj));
cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
cl_assert_equal_strn(fetchhead.ptr,
"5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of ",
strlen("5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of "));
git_str_dispose(&fetchhead);
git_object_free(obj);
git_remote_free(remote);
}
#include "clar_libgit2.h" #include "clar_libgit2.h"
#include "futils.h"
static git_repository *_repo; static git_repository *_repo;
static int counter; static int counter;
...@@ -290,3 +291,33 @@ void test_online_fetch__redirect_config(void) ...@@ -290,3 +291,33 @@ void test_online_fetch__redirect_config(void)
cl_git_fail(do_redirected_fetch(_remote_redirect_initial, "initial", "false")); cl_git_fail(do_redirected_fetch(_remote_redirect_initial, "initial", "false"));
cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "false")); cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "false"));
} }
void test_online_fetch__reachable_commit(void)
{
git_remote *remote;
git_strarray refspecs;
git_object *obj;
git_oid expected_id;
git_str fetchhead = GIT_STR_INIT;
char *refspec = "+2c349335b7f797072cf729c4f3bb0914ecb6dec9:refs/success";
refspecs.strings = &refspec;
refspecs.count = 1;
git_oid_fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9");
cl_git_pass(git_remote_create(&remote, _repo, "test",
"https://github.com/libgit2/TestGitRepository"));
cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
cl_git_pass(git_revparse_single(&obj, _repo, "refs/success"));
cl_assert_equal_oid(&expected_id, git_object_id(obj));
cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
cl_assert_equal_s(fetchhead.ptr,
"2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n");
git_str_dispose(&fetchhead);
git_object_free(obj);
git_remote_free(remote);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment