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 #define NETWORK_XFER_THRESHOLD (100*1024)
24 struct filter_payload
{
26 const git_refspec
*spec
, *tagspec
;
31 static int filter_ref__cb(git_remote_head
*head
, void *payload
)
33 struct filter_payload
*p
= payload
;
36 if (!git_reference_is_valid_name(head
->name
))
39 if (!p
->found_head
&& strcmp(head
->name
, GIT_HEAD_FILE
) == 0)
41 else if (git_refspec_src_matches(p
->spec
, head
->name
))
43 else if (p
->remote
->download_tags
== GIT_REMOTE_DOWNLOAD_TAGS_ALL
&&
44 git_refspec_src_matches(p
->tagspec
, head
->name
))
50 /* If we have the object, mark it so we don't ask for it */
51 if (git_odb_exists(p
->odb
, &head
->oid
))
54 p
->remote
->need_pack
= 1;
56 return git_vector_insert(&p
->remote
->refs
, head
);
59 static int filter_wants(git_remote
*remote
)
61 struct filter_payload p
;
65 git_vector_clear(&remote
->refs
);
66 if (git_refspec__parse(&tagspec
, GIT_REFSPEC_TAGS
, true) < 0)
70 * The fetch refspec can be NULL, and what this means is that the
71 * user didn't specify one. This is fine, as it means that we're
72 * not interested in any particular branch but just the remote's
73 * HEAD, which will be stored in FETCH_HEAD after the fetch.
75 p
.spec
= git_remote_fetchspec(remote
);
80 if (git_repository_odb__weakptr(&p
.odb
, remote
->repo
) < 0)
83 error
= git_remote_ls(remote
, filter_ref__cb
, &p
);
86 git_refspec__free(&tagspec
);
91 /* Wait until we get an ack from the */
92 static int recv_pkt(git_pkt
**out
, gitno_buffer
*buf
)
94 const char *ptr
= buf
->data
, *line_end
= ptr
;
96 int pkt_type
, error
= 0, ret
;
100 error
= git_pkt_parse_line(&pkt
, ptr
, &line_end
, buf
->offset
);
105 break; /* return the pkt */
107 if (error
< 0 && error
!= GIT_EBUFS
)
110 if ((ret
= gitno_recv(buf
)) < 0)
114 gitno_consume(buf
, line_end
);
115 pkt_type
= pkt
->type
;
124 static int store_common(git_transport
*t
)
127 gitno_buffer
*buf
= &t
->buffer
;
130 if (recv_pkt(&pkt
, buf
) < 0)
133 if (pkt
->type
== GIT_PKT_ACK
) {
134 if (git_vector_insert(&t
->common
, pkt
) < 0)
147 * In this first version, we push all our refs in and start sending
148 * them out. When we get an ACK we hide that commit and continue
149 * traversing until we're done
151 int git_fetch_negotiate(git_remote
*remote
)
153 git_transport
*t
= remote
->transport
;
154 gitno_buffer
*buf
= &t
->buffer
;
155 git_buf data
= GIT_BUF_INIT
;
156 git_revwalk
*walk
= NULL
;
157 int error
= -1, pkt_type
;
161 if (filter_wants(remote
) < 0) {
162 giterr_set(GITERR_NET
, "Failed to filter the reference list for wants");
166 /* Don't try to negotiate when we don't want anything */
167 if (remote
->refs
.length
== 0 || !remote
->need_pack
)
171 * Now we have everything set up so we can start tell the
172 * server what we want and what we have. Call the function if
173 * the transport has its own logic. This is transitional and
174 * will be removed once this function can support git and http.
177 return t
->negotiate_fetch(t
, remote
->repo
, &remote
->refs
);
179 /* No own logic, do our thing */
180 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
183 if (git_fetch_setup_walk(&walk
, remote
->repo
) < 0)
186 * We don't support any kind of ACK extensions, so the negotiation
187 * boils down to sending what we have and listening for an ACK
188 * every once in a while.
191 while ((error
= git_revwalk_next(&oid
, walk
)) == 0) {
192 git_pkt_buffer_have(&oid
, &data
);
196 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
201 git_pkt_buffer_flush(&data
);
202 if (git_buf_oom(&data
))
205 if (t
->negotiation_step(t
, data
.ptr
, data
.size
) < 0)
208 git_buf_clear(&data
);
209 if (t
->caps
.multi_ack
) {
210 if (store_common(t
) < 0)
213 pkt_type
= recv_pkt(NULL
, buf
);
215 if (pkt_type
== GIT_PKT_ACK
) {
217 } else if (pkt_type
== GIT_PKT_NAK
) {
220 giterr_set(GITERR_NET
, "Unexpected pkt type");
226 if (t
->common
.length
> 0)
229 if (i
% 20 == 0 && t
->rpc
) {
233 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
236 git_vector_foreach(&t
->common
, i
, pkt
) {
237 git_pkt_buffer_have(&pkt
->oid
, &data
);
240 if (git_buf_oom(&data
))
245 if (error
< 0 && error
!= GIT_ITEROVER
)
248 /* Tell the other end that we're done negotiating */
249 if (t
->rpc
&& t
->common
.length
> 0) {
253 if (git_pkt_buffer_wants(&remote
->refs
, &t
->caps
, &data
) < 0)
256 git_vector_foreach(&t
->common
, i
, pkt
) {
257 git_pkt_buffer_have(&pkt
->oid
, &data
);
260 if (git_buf_oom(&data
))
264 git_pkt_buffer_done(&data
);
266 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
270 if (t
->negotiation_step(t
, data
.ptr
, data
.size
) < 0)
274 git_revwalk_free(walk
);
276 /* Now let's eat up whatever the server gives us */
277 if (!t
->caps
.multi_ack
) {
278 pkt_type
= recv_pkt(NULL
, buf
);
279 if (pkt_type
!= GIT_PKT_ACK
&& pkt_type
!= GIT_PKT_NAK
) {
280 giterr_set(GITERR_NET
, "Unexpected pkt type");
286 if (recv_pkt((git_pkt
**)&pkt
, buf
) < 0)
289 if (pkt
->type
== GIT_PKT_NAK
||
290 (pkt
->type
== GIT_PKT_ACK
&& pkt
->status
!= GIT_ACK_CONTINUE
)) {
302 git_revwalk_free(walk
);
307 int git_fetch_download_pack(
309 git_transfer_progress_callback progress_cb
,
310 void *progress_payload
)
312 git_transport
*t
= remote
->transport
;
314 if(!remote
->need_pack
)
318 return t
->download_pack(t
, remote
->repo
, &remote
->stats
);
320 return git_fetch__download_pack(t
, remote
->repo
, &remote
->stats
,
321 progress_cb
, progress_payload
);
325 static int no_sideband(git_transport
*t
, git_indexer_stream
*idx
, gitno_buffer
*buf
, git_transfer_progress
*stats
)
331 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
335 if (git_indexer_stream_add(idx
, buf
->data
, buf
->offset
, stats
) < 0)
338 gitno_consume_n(buf
, buf
->offset
);
340 if ((recvd
= gitno_recv(buf
)) < 0)
344 if (git_indexer_stream_finalize(idx
, stats
))
350 struct network_packetsize_payload
352 git_transfer_progress_callback callback
;
354 git_transfer_progress
*stats
;
355 git_off_t last_fired_bytes
;
358 static void network_packetsize(int received
, void *payload
)
360 struct network_packetsize_payload
*npp
= (struct network_packetsize_payload
*)payload
;
362 /* Accumulate bytes */
363 npp
->stats
->received_bytes
+= received
;
365 /* Fire notification if the threshold is reached */
366 if ((npp
->stats
->received_bytes
- npp
->last_fired_bytes
) > NETWORK_XFER_THRESHOLD
) {
367 npp
->last_fired_bytes
= npp
->stats
->received_bytes
;
368 npp
->callback(npp
->stats
, npp
->payload
);
372 /* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
373 int git_fetch__download_pack(
375 git_repository
*repo
,
376 git_transfer_progress
*stats
,
377 git_transfer_progress_callback progress_cb
,
378 void *progress_payload
)
380 git_buf path
= GIT_BUF_INIT
;
381 gitno_buffer
*buf
= &t
->buffer
;
382 git_indexer_stream
*idx
= NULL
;
384 struct network_packetsize_payload npp
= {0};
387 npp
.callback
= progress_cb
;
388 npp
.payload
= progress_payload
;
390 buf
->packetsize_cb
= &network_packetsize
;
391 buf
->packetsize_payload
= &npp
;
394 if (git_buf_joinpath(&path
, git_repository_path(repo
), "objects/pack") < 0)
397 if (git_indexer_stream_new(&idx
, git_buf_cstr(&path
), progress_cb
, progress_payload
) < 0)
401 memset(stats
, 0, sizeof(git_transfer_progress
));
404 * If the remote doesn't support the side-band, we can feed
405 * the data directly to the indexer. Otherwise, we need to
406 * check which one belongs there.
408 if (!t
->caps
.side_band
&& !t
->caps
.side_band_64k
) {
409 if (no_sideband(t
, idx
, buf
, stats
) < 0)
412 git_indexer_stream_free(idx
);
420 giterr_set(GITERR_NET
, "The fetch was cancelled by the user");
425 if (recv_pkt(&pkt
, buf
) < 0)
428 if (pkt
->type
== GIT_PKT_PROGRESS
) {
429 if (t
->progress_cb
) {
430 git_pkt_progress
*p
= (git_pkt_progress
*) pkt
;
431 t
->progress_cb(p
->data
, p
->len
, t
->cb_data
);
434 } else if (pkt
->type
== GIT_PKT_DATA
) {
435 git_pkt_data
*p
= (git_pkt_data
*) pkt
;
436 if (git_indexer_stream_add(idx
, p
->data
, p
->len
, stats
) < 0)
440 } else if (pkt
->type
== GIT_PKT_FLUSH
) {
441 /* A flush indicates the end of the packfile */
447 if (git_indexer_stream_finalize(idx
, stats
) < 0)
450 git_indexer_stream_free(idx
);
455 git_indexer_stream_free(idx
);
459 int git_fetch_setup_walk(git_revwalk
**out
, git_repository
*repo
)
466 if (git_reference_list(&refs
, repo
, GIT_REF_LISTALL
) < 0)
469 if (git_revwalk_new(&walk
, repo
) < 0)
472 git_revwalk_sorting(walk
, GIT_SORT_TIME
);
474 for (i
= 0; i
< refs
.count
; ++i
) {
476 if (!git__prefixcmp(refs
.strings
[i
], GIT_REFS_TAGS_DIR
))
479 if (git_reference_lookup(&ref
, repo
, refs
.strings
[i
]) < 0)
482 if (git_reference_type(ref
) == GIT_REF_SYMBOLIC
)
484 if (git_revwalk_push(walk
, git_reference_oid(ref
)) < 0)
487 git_reference_free(ref
);
490 git_strarray_free(&refs
);
495 git_reference_free(ref
);
496 git_strarray_free(&refs
);