Commit 9d88300a by Edward Thomson

fetch: support oids in fetch specs

parent 07264ea7
......@@ -11,6 +11,7 @@
#include "git2/refs.h"
#include "git2/revwalk.h"
#include "git2/transport.h"
#include "git2/sys/remote.h"
#include "remote.h"
#include "refspec.h"
......@@ -19,7 +20,7 @@
#include "repository.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;
......@@ -44,23 +45,57 @@ static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, g
if (!match)
return 0;
return git_vector_insert(&remote->refs, head);
}
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)) {
if (git_odb_exists(odb, &head->oid))
head->local = 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)
{
git_remote_head **heads;
git_refspec tagspec, head;
git_refspec tagspec, head, *spec;
int error = 0;
git_odb *odb;
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;
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)
if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
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;
/* Handle remote heads */
for (i = 0; i < heads_len; i++) {
if ((error = maybe_want(remote, heads[i], odb, &tagspec, tagopt)) < 0)
break;
if ((error = maybe_want(remote, heads[i], &tagspec, tagopt)) < 0)
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:
git_refspec__dispose(&tagspec);
......@@ -115,10 +169,8 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
remote->need_pack = 0;
if (filter_wants(remote, opts) < 0) {
git_error_set(GIT_ERROR_NET, "failed to filter the reference list for wants");
if (filter_wants(remote, opts) < 0)
return -1;
}
/* Don't try to negotiate when we don't want anything */
if (!remote->need_pack)
......
......@@ -1871,7 +1871,7 @@ static int update_tips_for_spec(
const char *log_message)
{
git_refspec tagspec;
git_remote_head *head;
git_remote_head *head, oid_head;
git_vector update_heads;
int error = 0;
size_t i;
......@@ -1885,11 +1885,27 @@ static int update_tips_for_spec(
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;
}
/* Handle specified oid sources */
if (git_oid__is_hexstr(spec->src)) {
git_oid id;
if ((error = git_oid_fromstr(&id, spec->src)) < 0 ||
(error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
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 &&
(error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
goto on_error;
......@@ -2107,6 +2123,17 @@ int git_remote_disconnect(git_remote *remote)
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)
{
if (remote == NULL)
......@@ -2130,6 +2157,9 @@ void git_remote_free(git_remote *remote)
free_refspecs(&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__free(remote->url);
git__free(remote->pushurl);
......
......@@ -27,6 +27,7 @@ struct git_remote {
git_vector refspecs;
git_vector active_refspecs;
git_vector passive_refspecs;
git_vector local_heads;
git_transport *transport;
git_repository *repo;
git_push *push;
......
......@@ -34,3 +34,34 @@ void test_fetch_local__defaults(void)
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 "futils.h"
static git_repository *_repo;
static int counter;
......@@ -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_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