Skip to content

Commit 9d88300

Browse files
committed
fetch: support oids in fetch specs
1 parent 07264ea commit 9d88300

File tree

5 files changed

+160
-15
lines changed

5 files changed

+160
-15
lines changed

src/fetch.c

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "git2/refs.h"
1212
#include "git2/revwalk.h"
1313
#include "git2/transport.h"
14+
#include "git2/sys/remote.h"
1415

1516
#include "remote.h"
1617
#include "refspec.h"
@@ -19,7 +20,7 @@
1920
#include "repository.h"
2021
#include "refs.h"
2122

22-
static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
23+
static int maybe_want(git_remote *remote, git_remote_head *head, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
2324
{
2425
int match = 0, valid;
2526

@@ -44,23 +45,57 @@ static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, g
4445
if (!match)
4546
return 0;
4647

47-
/* If we have the object, mark it so we don't ask for it */
48-
if (git_odb_exists(odb, &head->oid)) {
49-
head->local = 1;
48+
return git_vector_insert(&remote->refs, head);
49+
}
50+
51+
static int mark_local(git_remote *remote)
52+
{
53+
git_remote_head *head;
54+
git_odb *odb;
55+
size_t i;
56+
57+
if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
58+
return -1;
59+
60+
git_vector_foreach(&remote->refs, i, head) {
61+
/* If we have the object, mark it so we don't ask for it */
62+
if (git_odb_exists(odb, &head->oid))
63+
head->local = 1;
64+
else
65+
remote->need_pack = 1;
5066
}
51-
else
52-
remote->need_pack = 1;
5367

54-
return git_vector_insert(&remote->refs, head);
68+
return 0;
69+
}
70+
71+
static int maybe_want_oid(git_remote *remote, git_refspec *spec)
72+
{
73+
git_remote_head *oid_head;
74+
75+
oid_head = git__calloc(1, sizeof(git_remote_head));
76+
GIT_ERROR_CHECK_ALLOC(oid_head);
77+
78+
git_oid_fromstr(&oid_head->oid, spec->src);
79+
oid_head->name = git__strdup(spec->dst);
80+
GIT_ERROR_CHECK_ALLOC(oid_head->name);
81+
82+
if (git_vector_insert(&remote->local_heads, oid_head) < 0 ||
83+
git_vector_insert(&remote->refs, oid_head) < 0)
84+
return -1;
85+
86+
return 0;
5587
}
5688

5789
static int filter_wants(git_remote *remote, const git_fetch_options *opts)
5890
{
5991
git_remote_head **heads;
60-
git_refspec tagspec, head;
92+
git_refspec tagspec, head, *spec;
6193
int error = 0;
6294
git_odb *odb;
6395
size_t i, heads_len;
96+
unsigned int remote_caps;
97+
unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
98+
GIT_REMOTE_CAPABILITY_REACHABLE_OID;
6499
git_remote_autotag_option_t tagopt = remote->download_tags;
65100

66101
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)
90125
if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
91126
goto cleanup;
92127

93-
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0)
128+
if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
129+
(error = git_remote_capabilities(&remote_caps, remote)) < 0)
94130
goto cleanup;
95131

132+
/* Handle remote heads */
96133
for (i = 0; i < heads_len; i++) {
97-
if ((error = maybe_want(remote, heads[i], odb, &tagspec, tagopt)) < 0)
98-
break;
134+
if ((error = maybe_want(remote, heads[i], &tagspec, tagopt)) < 0)
135+
goto cleanup;
136+
}
137+
138+
/* Handle explicitly specified OID specs */
139+
git_vector_foreach(&remote->active_refspecs, i, spec) {
140+
if (!git_oid__is_hexstr(spec->src))
141+
continue;
142+
143+
if (!(remote_caps & oid_mask)) {
144+
git_error_set(GIT_ERROR_INVALID, "cannot fetch a specific object from the remote repository");
145+
error = -1;
146+
goto cleanup;
147+
}
148+
149+
if ((error = maybe_want_oid(remote, spec)) < 0)
150+
goto cleanup;
99151
}
100152

153+
error = mark_local(remote);
154+
101155
cleanup:
102156
git_refspec__dispose(&tagspec);
103157

@@ -115,10 +169,8 @@ int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
115169

116170
remote->need_pack = 0;
117171

118-
if (filter_wants(remote, opts) < 0) {
119-
git_error_set(GIT_ERROR_NET, "failed to filter the reference list for wants");
172+
if (filter_wants(remote, opts) < 0)
120173
return -1;
121-
}
122174

123175
/* Don't try to negotiate when we don't want anything */
124176
if (!remote->need_pack)

src/remote.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1871,7 +1871,7 @@ static int update_tips_for_spec(
18711871
const char *log_message)
18721872
{
18731873
git_refspec tagspec;
1874-
git_remote_head *head;
1874+
git_remote_head *head, oid_head;
18751875
git_vector update_heads;
18761876
int error = 0;
18771877
size_t i;
@@ -1885,11 +1885,27 @@ static int update_tips_for_spec(
18851885
if (git_vector_init(&update_heads, 16, NULL) < 0)
18861886
return -1;
18871887

1888+
/* Update tips based on the remote heads */
18881889
git_vector_foreach(refs, i, head) {
18891890
if (update_one_tip(&update_heads, remote, spec, head, &tagspec, tagopt, log_message, callbacks) < 0)
18901891
goto on_error;
18911892
}
18921893

1894+
/* Handle specified oid sources */
1895+
if (git_oid__is_hexstr(spec->src)) {
1896+
git_oid id;
1897+
1898+
if ((error = git_oid_fromstr(&id, spec->src)) < 0 ||
1899+
(error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
1900+
goto on_error;
1901+
1902+
git_oid_cpy(&oid_head.oid, &id);
1903+
oid_head.name = spec->src;
1904+
1905+
if ((error = git_vector_insert(&update_heads, &oid_head)) < 0)
1906+
goto on_error;
1907+
}
1908+
18931909
if (update_fetchhead &&
18941910
(error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
18951911
goto on_error;
@@ -2107,6 +2123,17 @@ int git_remote_disconnect(git_remote *remote)
21072123
return 0;
21082124
}
21092125

2126+
static void free_heads(git_vector *heads)
2127+
{
2128+
git_remote_head *head;
2129+
size_t i;
2130+
2131+
git_vector_foreach(heads, i, head) {
2132+
git__free(head->name);
2133+
git__free(head);
2134+
}
2135+
}
2136+
21102137
void git_remote_free(git_remote *remote)
21112138
{
21122139
if (remote == NULL)
@@ -2130,6 +2157,9 @@ void git_remote_free(git_remote *remote)
21302157
free_refspecs(&remote->passive_refspecs);
21312158
git_vector_free(&remote->passive_refspecs);
21322159

2160+
free_heads(&remote->local_heads);
2161+
git_vector_free(&remote->local_heads);
2162+
21332163
git_push_free(remote->push);
21342164
git__free(remote->url);
21352165
git__free(remote->pushurl);

src/remote.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct git_remote {
2727
git_vector refspecs;
2828
git_vector active_refspecs;
2929
git_vector passive_refspecs;
30+
git_vector local_heads;
3031
git_transport *transport;
3132
git_repository *repo;
3233
git_push *push;

tests/fetch/local.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,34 @@ void test_fetch_local__defaults(void)
3434
git_object_free(obj);
3535
git_remote_free(remote);
3636
}
37+
38+
void test_fetch_local__reachable_commit(void)
39+
{
40+
git_remote *remote;
41+
git_strarray refspecs;
42+
git_object *obj;
43+
git_oid expected_id;
44+
git_str fetchhead = GIT_STR_INIT;
45+
char *refspec = "+5b5b025afb0b4c913b4c338a42934a3863bf3644:refs/success";
46+
47+
refspecs.strings = &refspec;
48+
refspecs.count = 1;
49+
50+
git_oid_fromstr(&expected_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
51+
52+
cl_git_pass(git_remote_create(&remote, repo, "test",
53+
cl_fixture("testrepo.git")));
54+
cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
55+
56+
cl_git_pass(git_revparse_single(&obj, repo, "refs/success"));
57+
cl_assert_equal_oid(&expected_id, git_object_id(obj));
58+
59+
cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
60+
cl_assert_equal_strn(fetchhead.ptr,
61+
"5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of ",
62+
strlen("5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of "));
63+
64+
git_str_dispose(&fetchhead);
65+
git_object_free(obj);
66+
git_remote_free(remote);
67+
}

tests/online/fetch.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "clar_libgit2.h"
2+
#include "futils.h"
23

34
static git_repository *_repo;
45
static int counter;
@@ -290,3 +291,33 @@ void test_online_fetch__redirect_config(void)
290291
cl_git_fail(do_redirected_fetch(_remote_redirect_initial, "initial", "false"));
291292
cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "false"));
292293
}
294+
295+
void test_online_fetch__reachable_commit(void)
296+
{
297+
git_remote *remote;
298+
git_strarray refspecs;
299+
git_object *obj;
300+
git_oid expected_id;
301+
git_str fetchhead = GIT_STR_INIT;
302+
char *refspec = "+2c349335b7f797072cf729c4f3bb0914ecb6dec9:refs/success";
303+
304+
refspecs.strings = &refspec;
305+
refspecs.count = 1;
306+
307+
git_oid_fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9");
308+
309+
cl_git_pass(git_remote_create(&remote, _repo, "test",
310+
"https://github.com/libgit2/TestGitRepository"));
311+
cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
312+
313+
cl_git_pass(git_revparse_single(&obj, _repo, "refs/success"));
314+
cl_assert_equal_oid(&expected_id, git_object_id(obj));
315+
316+
cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
317+
cl_assert_equal_s(fetchhead.ptr,
318+
"2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n");
319+
320+
git_str_dispose(&fetchhead);
321+
git_object_free(obj);
322+
git_remote_free(remote);
323+
}

0 commit comments

Comments
 (0)