Skip to content

Commit 1d811f0

Browse files
authored
Merge pull request libgit2#6203 from libgit2/ethomson/fetch_by_oid
Fetch by object id
2 parents d299a7a + 9d88300 commit 1d811f0

File tree

13 files changed

+474
-137
lines changed

13 files changed

+474
-137
lines changed

include/git2/sys/remote.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (C) the libgit2 contributors. All rights reserved.
3+
*
4+
* This file is part of libgit2, distributed under the GNU GPL v2 with
5+
* a Linking Exception. For full terms see the included COPYING file.
6+
*/
7+
8+
#ifndef INCLUDE_sys_git_remote_h
9+
#define INCLUDE_sys_git_remote_h
10+
11+
/**
12+
* @file git2/sys/remote.h
13+
* @brief Low-level remote functionality for custom transports
14+
* @defgroup git_remote Low-level remote functionality
15+
* @ingroup Git
16+
* @{
17+
*/
18+
19+
GIT_BEGIN_DECL
20+
21+
typedef enum {
22+
/** Remote supports fetching an advertised object by ID. */
23+
GIT_REMOTE_CAPABILITY_TIP_OID = (1 << 0),
24+
25+
/** Remote supports fetching an individual reachable object. */
26+
GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
27+
} git_remote_capability_t;
28+
29+
/** @} */
30+
GIT_END_DECL
31+
#endif

include/git2/sys/transport.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ struct git_transport {
4646
git_transport *transport,
4747
const git_remote_connect_options *connect_opts);
4848

49+
/**
50+
* Gets the capabilities for this remote repository.
51+
*
52+
* This function may be called after a successful call to
53+
* `connect()`.
54+
*/
55+
int GIT_CALLBACK(capabilities)(
56+
unsigned int *capabilities,
57+
git_transport *transport);
58+
4959
/**
5060
* Get the list of available references in the remote repository.
5161
*

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/oid.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,16 @@ GIT_INLINE(void) git_oid__cpy_prefix(
4848
out->id[len / 2] &= 0xF0;
4949
}
5050

51+
GIT_INLINE(bool) git_oid__is_hexstr(const char *str)
52+
{
53+
size_t i;
54+
55+
for (i = 0; str[i] != '\0'; i++) {
56+
if (git__fromhex(str[i]) < 0)
57+
return false;
58+
}
59+
60+
return (i == GIT_OID_HEXSZ);
61+
}
62+
5163
#endif

0 commit comments

Comments
 (0)