2 * Copyright (C) 2009-2012 the libgit2 contributors
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.
10 #include "git2/revwalk.h"
11 #include "git2/indexer.h"
14 #include "transport.h"
22 struct filter_payload
{
24 const git_refspec
*spec
, *tagspec
;
29 static int filter_ref__cb(git_remote_head
*head
, void *payload
)
31 struct filter_payload
*p
= payload
;
34 if (!git_reference_is_valid_name(head
->name
))
37 if (!p
->found_head
&& strcmp(head
->name
, GIT_HEAD_FILE
) == 0)
39 else if (git_refspec_src_matches(p
->spec
, head
->name
))
41 else if (p
->remote
->download_tags
== GIT_REMOTE_DOWNLOAD_TAGS_ALL
&&
42 git_refspec_src_matches(p
->tagspec
, head
->name
))
48 /* If we have the object, mark it so we don't ask for it */
49 if (git_odb_exists(p
->odb
, &head
->oid
))
52 p
->remote
->need_pack
= 1;
54 return git_vector_insert(&p
->remote
->refs
, head
);
57 static int filter_wants(git_remote
*remote
)
59 struct filter_payload p
;
63 git_vector_clear(&remote
->refs
);
64 if (git_refspec__parse(&tagspec
, GIT_REFSPEC_TAGS
, true) < 0)
68 * The fetch refspec can be NULL, and what this means is that the
69 * user didn't specify one. This is fine, as it means that we're
70 * not interested in any particular branch but just the remote's
71 * HEAD, which will be stored in FETCH_HEAD after the fetch.
73 p
.spec
= git_remote_fetchspec(remote
);
78 if (git_repository_odb__weakptr(&p
.odb
, remote
->repo
) < 0)
81 error
= git_remote_ls(remote
, filter_ref__cb
, &p
);
84 git_refspec__free(&tagspec
);
89 /* Wait until we get an ack from the */
90 static int recv_pkt(git_pkt
**out
, gitno_buffer
*buf
)
92 const char *ptr
= buf
->data
, *line_end
= ptr
;
94 int pkt_type
, error
= 0, ret
;
98 error
= git_pkt_parse_line(&pkt
, ptr
, &line_end
, buf
->offset
);
103 break; /* return the pkt */
105 if (error
< 0 && error
!= GIT_EBUFS
)
108 if ((ret
= gitno_recv(buf
)) < 0)
112 gitno_consume(buf
, line_end
);
113 pkt_type
= pkt
->type
;
122 static int store_common(git_transport
*t
)
125 gitno_buffer
*buf
= &t
->buffer
;
128 if (recv_pkt(&pkt
, buf
) < 0)
131 if (pkt
->type
== GIT_PKT_ACK
) {
132 if (git_vector_insert(&t
->common
, pkt
) < 0)
145 * In this first version, we push all our refs in and start sending
146 * them out. When we get an ACK we hide that commit and continue
147 * traversing until we're done
149 int git_fetch_negotiate(git_remote
*remote
)
151 git_transport
*t
= remote
->transport
;
152 gitno_buffer
*buf
= &t
->buffer
;
153 git_buf data
= GIT_BUF_INIT
;
154 git_revwalk
*walk
= NULL
;
155 int error
= -1, pkt_type
;
159 if (filter_wants(remote
) < 0) {
160 giterr_set(GITERR_NET
, "Failed to filter the reference list for wants");
164 /* Don't try to negotiate when we don't want anything */
165 if (remote
->refs
.length
== 0 || !remote
->need_pack
)
169 * Now we have everything set up so we can start tell the
170 * server what we want and what we have. Call the function if
171 * the transport has its own logic. This is transitional and
172 * will be removed once this function can support git and http.
175 return t
->negotiate_fetch(t
, remote
->repo
, &remote
->refs
);
177 /* No own logic, do our thing */
178 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
181 if (git_fetch_setup_walk(&walk
, remote
->repo
) < 0)
184 * We don't support any kind of ACK extensions, so the negotiation
185 * boils down to sending what we have and listening for an ACK
186 * every once in a while.
189 while ((error
= git_revwalk_next(&oid
, walk
)) == 0) {
190 git_pkt_buffer_have(&oid
, &data
);
194 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
199 git_pkt_buffer_flush(&data
);
200 if (git_buf_oom(&data
))
203 if (t
->negotiation_step(t
, data
.ptr
, data
.size
) < 0)
206 git_buf_clear(&data
);
207 if (t
->caps
.multi_ack
) {
208 if (store_common(t
) < 0)
211 pkt_type
= recv_pkt(NULL
, buf
);
213 if (pkt_type
== GIT_PKT_ACK
) {
215 } else if (pkt_type
== GIT_PKT_NAK
) {
218 giterr_set(GITERR_NET
, "Unexpected pkt type");
224 if (t
->common
.length
> 0)
227 if (i
% 20 == 0 && t
->rpc
) {
231 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
234 git_vector_foreach(&t
->common
, i
, pkt
) {
235 git_pkt_buffer_have(&pkt
->oid
, &data
);
238 if (git_buf_oom(&data
))
243 if (error
< 0 && error
!= GIT_ITEROVER
)
246 /* Tell the other end that we're done negotiating */
247 if (t
->rpc
&& t
->common
.length
> 0) {
251 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
254 git_vector_foreach(&t
->common
, i
, pkt
) {
255 git_pkt_buffer_have(&pkt
->oid
, &data
);
258 if (git_buf_oom(&data
))
262 git_pkt_buffer_done(&data
);
264 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
268 if (t
->negotiation_step(t
, data
.ptr
, data
.size
) < 0)
272 git_revwalk_free(walk
);
274 /* Now let's eat up whatever the server gives us */
275 if (!t
->caps
.multi_ack
) {
276 pkt_type
= recv_pkt(NULL
, buf
);
277 if (pkt_type
!= GIT_PKT_ACK
&& pkt_type
!= GIT_PKT_NAK
) {
278 giterr_set(GITERR_NET
, "Unexpected pkt type");
284 if (recv_pkt((git_pkt
**)&pkt
, buf
) < 0)
287 if (pkt
->type
== GIT_PKT_NAK
||
288 (pkt
->type
== GIT_PKT_ACK
&& pkt
->status
!= GIT_ACK_CONTINUE
)) {
300 git_revwalk_free(walk
);
305 int git_fetch_download_pack(git_remote
*remote
, git_off_t
*bytes
)
307 git_transport
*t
= remote
->transport
;
309 if(!remote
->need_pack
)
313 return t
->download_pack(t
, remote
->repo
, bytes
, &remote
->stats
);
315 return git_fetch__download_pack(t
, remote
->repo
, bytes
, &remote
->stats
);
319 static int no_sideband(git_transport
*t
, git_indexer_stream
*idx
, gitno_buffer
*buf
, git_off_t
*bytes
, git_indexer_stats
*stats
)
325 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
329 if (git_indexer_stream_add(idx
, buf
->data
, buf
->offset
, stats
) < 0)
332 gitno_consume_n(buf
, buf
->offset
);
334 if ((recvd
= gitno_recv(buf
)) < 0)
340 if (git_indexer_stream_finalize(idx
, stats
))
346 /* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
347 int git_fetch__download_pack(
349 git_repository
*repo
,
351 git_indexer_stats
*stats
)
353 git_buf path
= GIT_BUF_INIT
;
354 gitno_buffer
*buf
= &t
->buffer
;
355 git_indexer_stream
*idx
= NULL
;
358 if (git_buf_joinpath(&path
, git_repository_path(repo
), "objects/pack") < 0)
361 if (git_indexer_stream_new(&idx
, git_buf_cstr(&path
)) < 0)
365 memset(stats
, 0, sizeof(git_indexer_stats
));
369 * If the remote doesn't support the side-band, we can feed
370 * the data directly to the indexer. Otherwise, we need to
371 * check which one belongs there.
373 if (!t
->caps
.side_band
&& !t
->caps
.side_band_64k
) {
374 if (no_sideband(t
, idx
, buf
, bytes
, stats
) < 0)
377 git_indexer_stream_free(idx
);
385 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
390 if (recv_pkt(&pkt
, buf
) < 0)
393 if (pkt
->type
== GIT_PKT_PROGRESS
) {
394 if (t
->progress_cb
) {
395 git_pkt_progress
*p
= (git_pkt_progress
*) pkt
;
396 t
->progress_cb(p
->data
, p
->len
, t
->cb_data
);
399 } else if (pkt
->type
== GIT_PKT_DATA
) {
400 git_pkt_data
*p
= (git_pkt_data
*) pkt
;
402 if (git_indexer_stream_add(idx
, p
->data
, p
->len
, stats
) < 0)
406 } else if (pkt
->type
== GIT_PKT_FLUSH
) {
407 /* A flush indicates the end of the packfile */
413 if (git_indexer_stream_finalize(idx
, stats
) < 0)
416 git_indexer_stream_free(idx
);
421 git_indexer_stream_free(idx
);
425 int git_fetch_setup_walk(git_revwalk
**out
, git_repository
*repo
)
432 if (git_reference_list(&refs
, repo
, GIT_REF_LISTALL
) < 0)
435 if (git_revwalk_new(&walk
, repo
) < 0)
438 git_revwalk_sorting(walk
, GIT_SORT_TIME
);
440 for (i
= 0; i
< refs
.count
; ++i
) {
442 if (!git__prefixcmp(refs
.strings
[i
], GIT_REFS_TAGS_DIR
))
445 if (git_reference_lookup(&ref
, repo
, refs
.strings
[i
]) < 0)
448 if (git_reference_type(ref
) == GIT_REF_SYMBOLIC
)
450 if (git_revwalk_push(walk
, git_reference_oid(ref
)) < 0)
453 git_reference_free(ref
);
456 git_strarray_free(&refs
);
461 git_reference_free(ref
);
462 git_strarray_free(&refs
);