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"
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
5789static 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+
101155cleanup :
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 )
0 commit comments