]> git.proxmox.com Git - libgit2.git/blob - src/transports/smart_protocol.c
Merge pull request #3016 from pks-t/ignore-exclude-fix
[libgit2.git] / src / transports / smart_protocol.c
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 #include "git2.h"
8 #include "git2/odb_backend.h"
9
10 #include "smart.h"
11 #include "refs.h"
12 #include "repository.h"
13 #include "push.h"
14 #include "pack-objects.h"
15 #include "remote.h"
16 #include "util.h"
17
18 #define NETWORK_XFER_THRESHOLD (100*1024)
19 /* The minimal interval between progress updates (in seconds). */
20 #define MIN_PROGRESS_UPDATE_INTERVAL 0.5
21
22 int git_smart__store_refs(transport_smart *t, int flushes)
23 {
24 gitno_buffer *buf = &t->buffer;
25 git_vector *refs = &t->refs;
26 int error, flush = 0, recvd;
27 const char *line_end = NULL;
28 git_pkt *pkt = NULL;
29 size_t i;
30
31 /* Clear existing refs in case git_remote_connect() is called again
32 * after git_remote_disconnect().
33 */
34 git_vector_foreach(refs, i, pkt) {
35 git_pkt_free(pkt);
36 }
37 git_vector_clear(refs);
38 pkt = NULL;
39
40 do {
41 if (buf->offset > 0)
42 error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset);
43 else
44 error = GIT_EBUFS;
45
46 if (error < 0 && error != GIT_EBUFS)
47 return error;
48
49 if (error == GIT_EBUFS) {
50 if ((recvd = gitno_recv(buf)) < 0)
51 return recvd;
52
53 if (recvd == 0 && !flush) {
54 giterr_set(GITERR_NET, "Early EOF");
55 return -1;
56 }
57
58 continue;
59 }
60
61 gitno_consume(buf, line_end);
62 if (pkt->type == GIT_PKT_ERR) {
63 giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error);
64 git__free(pkt);
65 return -1;
66 }
67
68 if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
69 return -1;
70
71 if (pkt->type == GIT_PKT_FLUSH) {
72 flush++;
73 git_pkt_free(pkt);
74 }
75 } while (flush < flushes);
76
77 return flush;
78 }
79
80 static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
81 {
82 int error;
83 const char *end;
84 git_buf buf = GIT_BUF_INIT;
85 git_refspec *mapping = NULL;
86
87 ptr += strlen(GIT_CAP_SYMREF);
88 if (*ptr != '=')
89 goto on_invalid;
90
91 ptr++;
92 if (!(end = strchr(ptr, ' ')) &&
93 !(end = strchr(ptr, '\0')))
94 goto on_invalid;
95
96 if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0)
97 return error;
98
99 /* symref mapping has refspec format */
100 mapping = git__calloc(1, sizeof(git_refspec));
101 GITERR_CHECK_ALLOC(mapping);
102
103 error = git_refspec__parse(mapping, git_buf_cstr(&buf), true);
104 git_buf_free(&buf);
105
106 /* if the error isn't OOM, then it's a parse error; let's use a nicer message */
107 if (error < 0) {
108 if (giterr_last()->klass != GITERR_NOMEMORY)
109 goto on_invalid;
110
111 return error;
112 }
113
114 if ((error = git_vector_insert(symrefs, mapping)) < 0)
115 return error;
116
117 *out = end;
118 return 0;
119
120 on_invalid:
121 giterr_set(GITERR_NET, "remote sent invalid symref");
122 git_refspec__free(mapping);
123 return -1;
124 }
125
126 int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
127 {
128 const char *ptr;
129
130 /* No refs or capabilites, odd but not a problem */
131 if (pkt == NULL || pkt->capabilities == NULL)
132 return 0;
133
134 ptr = pkt->capabilities;
135 while (ptr != NULL && *ptr != '\0') {
136 if (*ptr == ' ')
137 ptr++;
138
139 if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
140 caps->common = caps->ofs_delta = 1;
141 ptr += strlen(GIT_CAP_OFS_DELTA);
142 continue;
143 }
144
145 /* Keep multi_ack_detailed before multi_ack */
146 if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
147 caps->common = caps->multi_ack_detailed = 1;
148 ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
149 continue;
150 }
151
152 if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
153 caps->common = caps->multi_ack = 1;
154 ptr += strlen(GIT_CAP_MULTI_ACK);
155 continue;
156 }
157
158 if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
159 caps->common = caps->include_tag = 1;
160 ptr += strlen(GIT_CAP_INCLUDE_TAG);
161 continue;
162 }
163
164 /* Keep side-band check after side-band-64k */
165 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
166 caps->common = caps->side_band_64k = 1;
167 ptr += strlen(GIT_CAP_SIDE_BAND_64K);
168 continue;
169 }
170
171 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
172 caps->common = caps->side_band = 1;
173 ptr += strlen(GIT_CAP_SIDE_BAND);
174 continue;
175 }
176
177 if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
178 caps->common = caps->delete_refs = 1;
179 ptr += strlen(GIT_CAP_DELETE_REFS);
180 continue;
181 }
182
183 if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
184 caps->common = caps->thin_pack = 1;
185 ptr += strlen(GIT_CAP_THIN_PACK);
186 continue;
187 }
188
189 if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
190 int error;
191
192 if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
193 return error;
194
195 continue;
196 }
197
198 /* We don't know this capability, so skip it */
199 ptr = strchr(ptr, ' ');
200 }
201
202 return 0;
203 }
204
205 static int recv_pkt(git_pkt **out, gitno_buffer *buf)
206 {
207 const char *ptr = buf->data, *line_end = ptr;
208 git_pkt *pkt = NULL;
209 int pkt_type, error = 0, ret;
210
211 do {
212 if (buf->offset > 0)
213 error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset);
214 else
215 error = GIT_EBUFS;
216
217 if (error == 0)
218 break; /* return the pkt */
219
220 if (error < 0 && error != GIT_EBUFS)
221 return error;
222
223 if ((ret = gitno_recv(buf)) < 0)
224 return ret;
225 } while (error);
226
227 gitno_consume(buf, line_end);
228 pkt_type = pkt->type;
229 if (out != NULL)
230 *out = pkt;
231 else
232 git__free(pkt);
233
234 return pkt_type;
235 }
236
237 static int store_common(transport_smart *t)
238 {
239 git_pkt *pkt = NULL;
240 gitno_buffer *buf = &t->buffer;
241 int error;
242
243 do {
244 if ((error = recv_pkt(&pkt, buf)) < 0)
245 return error;
246
247 if (pkt->type == GIT_PKT_ACK) {
248 if (git_vector_insert(&t->common, pkt) < 0)
249 return -1;
250 } else {
251 git__free(pkt);
252 return 0;
253 }
254
255 } while (1);
256
257 return 0;
258 }
259
260 static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
261 {
262 git_revwalk *walk = NULL;
263 git_strarray refs;
264 unsigned int i;
265 git_reference *ref;
266 int error;
267
268 if ((error = git_reference_list(&refs, repo)) < 0)
269 return error;
270
271 if ((error = git_revwalk_new(&walk, repo)) < 0)
272 return error;
273
274 git_revwalk_sorting(walk, GIT_SORT_TIME);
275
276 for (i = 0; i < refs.count; ++i) {
277 /* No tags */
278 if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
279 continue;
280
281 if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
282 goto on_error;
283
284 if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
285 continue;
286
287 if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
288 goto on_error;
289
290 git_reference_free(ref);
291 }
292
293 git_strarray_free(&refs);
294 *out = walk;
295 return 0;
296
297 on_error:
298 git_revwalk_free(walk);
299 git_reference_free(ref);
300 git_strarray_free(&refs);
301 return error;
302 }
303
304 static int wait_while_ack(gitno_buffer *buf)
305 {
306 int error;
307 git_pkt_ack *pkt = NULL;
308
309 while (1) {
310 git__free(pkt);
311
312 if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0)
313 return error;
314
315 if (pkt->type == GIT_PKT_NAK)
316 break;
317
318 if (pkt->type == GIT_PKT_ACK &&
319 (pkt->status != GIT_ACK_CONTINUE &&
320 pkt->status != GIT_ACK_COMMON)) {
321 git__free(pkt);
322 return 0;
323 }
324 }
325
326 git__free(pkt);
327 return 0;
328 }
329
330 int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
331 {
332 transport_smart *t = (transport_smart *)transport;
333 gitno_buffer *buf = &t->buffer;
334 git_buf data = GIT_BUF_INIT;
335 git_revwalk *walk = NULL;
336 int error = -1, pkt_type;
337 unsigned int i;
338 git_oid oid;
339
340 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
341 return error;
342
343 if ((error = fetch_setup_walk(&walk, repo)) < 0)
344 goto on_error;
345
346 /*
347 * Our support for ACK extensions is simply to parse them. On
348 * the first ACK we will accept that as enough common
349 * objects. We give up if we haven't found an answer in the
350 * first 256 we send.
351 */
352 i = 0;
353 while (i < 256) {
354 error = git_revwalk_next(&oid, walk);
355
356 if (error < 0) {
357 if (GIT_ITEROVER == error)
358 break;
359
360 goto on_error;
361 }
362
363 git_pkt_buffer_have(&oid, &data);
364 i++;
365 if (i % 20 == 0) {
366 if (t->cancelled.val) {
367 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
368 error = GIT_EUSER;
369 goto on_error;
370 }
371
372 git_pkt_buffer_flush(&data);
373 if (git_buf_oom(&data)) {
374 error = -1;
375 goto on_error;
376 }
377
378 if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
379 goto on_error;
380
381 git_buf_clear(&data);
382 if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
383 if ((error = store_common(t)) < 0)
384 goto on_error;
385 } else {
386 pkt_type = recv_pkt(NULL, buf);
387
388 if (pkt_type == GIT_PKT_ACK) {
389 break;
390 } else if (pkt_type == GIT_PKT_NAK) {
391 continue;
392 } else if (pkt_type < 0) {
393 /* recv_pkt returned an error */
394 error = pkt_type;
395 goto on_error;
396 } else {
397 giterr_set(GITERR_NET, "Unexpected pkt type");
398 error = -1;
399 goto on_error;
400 }
401 }
402 }
403
404 if (t->common.length > 0)
405 break;
406
407 if (i % 20 == 0 && t->rpc) {
408 git_pkt_ack *pkt;
409 unsigned int i;
410
411 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
412 goto on_error;
413
414 git_vector_foreach(&t->common, i, pkt) {
415 if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
416 goto on_error;
417 }
418
419 if (git_buf_oom(&data)) {
420 error = -1;
421 goto on_error;
422 }
423 }
424 }
425
426 /* Tell the other end that we're done negotiating */
427 if (t->rpc && t->common.length > 0) {
428 git_pkt_ack *pkt;
429 unsigned int i;
430
431 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
432 goto on_error;
433
434 git_vector_foreach(&t->common, i, pkt) {
435 if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
436 goto on_error;
437 }
438
439 if (git_buf_oom(&data)) {
440 error = -1;
441 goto on_error;
442 }
443 }
444
445 if ((error = git_pkt_buffer_done(&data)) < 0)
446 goto on_error;
447
448 if (t->cancelled.val) {
449 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
450 error = GIT_EUSER;
451 goto on_error;
452 }
453 if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
454 goto on_error;
455
456 git_buf_free(&data);
457 git_revwalk_free(walk);
458
459 /* Now let's eat up whatever the server gives us */
460 if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
461 pkt_type = recv_pkt(NULL, buf);
462
463 if (pkt_type < 0) {
464 return pkt_type;
465 } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
466 giterr_set(GITERR_NET, "Unexpected pkt type");
467 return -1;
468 }
469 } else {
470 error = wait_while_ack(buf);
471 }
472
473 return error;
474
475 on_error:
476 git_revwalk_free(walk);
477 git_buf_free(&data);
478 return error;
479 }
480
481 static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
482 {
483 int recvd;
484
485 do {
486 if (t->cancelled.val) {
487 giterr_set(GITERR_NET, "The fetch was cancelled by the user");
488 return GIT_EUSER;
489 }
490
491 if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
492 return -1;
493
494 gitno_consume_n(buf, buf->offset);
495
496 if ((recvd = gitno_recv(buf)) < 0)
497 return recvd;
498 } while(recvd > 0);
499
500 if (writepack->commit(writepack, stats) < 0)
501 return -1;
502
503 return 0;
504 }
505
506 struct network_packetsize_payload
507 {
508 git_transfer_progress_cb callback;
509 void *payload;
510 git_transfer_progress *stats;
511 size_t last_fired_bytes;
512 };
513
514 static int network_packetsize(size_t received, void *payload)
515 {
516 struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
517
518 /* Accumulate bytes */
519 npp->stats->received_bytes += received;
520
521 /* Fire notification if the threshold is reached */
522 if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
523 npp->last_fired_bytes = npp->stats->received_bytes;
524
525 if (npp->callback(npp->stats, npp->payload))
526 return GIT_EUSER;
527 }
528
529 return 0;
530 }
531
532 int git_smart__download_pack(
533 git_transport *transport,
534 git_repository *repo,
535 git_transfer_progress *stats,
536 git_transfer_progress_cb transfer_progress_cb,
537 void *progress_payload)
538 {
539 transport_smart *t = (transport_smart *)transport;
540 gitno_buffer *buf = &t->buffer;
541 git_odb *odb;
542 struct git_odb_writepack *writepack = NULL;
543 int error = 0;
544 struct network_packetsize_payload npp = {0};
545
546 memset(stats, 0, sizeof(git_transfer_progress));
547
548 if (transfer_progress_cb) {
549 npp.callback = transfer_progress_cb;
550 npp.payload = progress_payload;
551 npp.stats = stats;
552 t->packetsize_cb = &network_packetsize;
553 t->packetsize_payload = &npp;
554
555 /* We might have something in the buffer already from negotiate_fetch */
556 if (t->buffer.offset > 0 && !t->cancelled.val)
557 if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
558 git_atomic_set(&t->cancelled, 1);
559 }
560
561 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
562 ((error = git_odb_write_pack(&writepack, odb, transfer_progress_cb, progress_payload)) != 0))
563 goto done;
564
565 /*
566 * If the remote doesn't support the side-band, we can feed
567 * the data directly to the pack writer. Otherwise, we need to
568 * check which one belongs there.
569 */
570 if (!t->caps.side_band && !t->caps.side_band_64k) {
571 error = no_sideband(t, writepack, buf, stats);
572 goto done;
573 }
574
575 do {
576 git_pkt *pkt = NULL;
577
578 /* Check cancellation before network call */
579 if (t->cancelled.val) {
580 giterr_clear();
581 error = GIT_EUSER;
582 goto done;
583 }
584
585 if ((error = recv_pkt(&pkt, buf)) >= 0) {
586 /* Check cancellation after network call */
587 if (t->cancelled.val) {
588 giterr_clear();
589 error = GIT_EUSER;
590 } else if (pkt->type == GIT_PKT_PROGRESS) {
591 if (t->progress_cb) {
592 git_pkt_progress *p = (git_pkt_progress *) pkt;
593 error = t->progress_cb(p->data, p->len, t->message_cb_payload);
594 }
595 } else if (pkt->type == GIT_PKT_DATA) {
596 git_pkt_data *p = (git_pkt_data *) pkt;
597
598 if (p->len)
599 error = writepack->append(writepack, p->data, p->len, stats);
600 } else if (pkt->type == GIT_PKT_FLUSH) {
601 /* A flush indicates the end of the packfile */
602 git__free(pkt);
603 break;
604 }
605 }
606
607 git__free(pkt);
608 if (error < 0)
609 goto done;
610
611 } while (1);
612
613 /*
614 * Trailing execution of transfer_progress_cb, if necessary...
615 * Only the callback through the npp datastructure currently
616 * updates the last_fired_bytes value. It is possible that
617 * progress has already been reported with the correct
618 * "received_bytes" value, but until (if?) this is unified
619 * then we will report progress again to be sure that the
620 * correct last received_bytes value is reported.
621 */
622 if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
623 error = npp.callback(npp.stats, npp.payload);
624 if (error != 0)
625 goto done;
626 }
627
628 error = writepack->commit(writepack, stats);
629
630 done:
631 if (writepack)
632 writepack->free(writepack);
633 if (transfer_progress_cb) {
634 t->packetsize_cb = NULL;
635 t->packetsize_payload = NULL;
636 }
637
638 return error;
639 }
640
641 static int gen_pktline(git_buf *buf, git_push *push)
642 {
643 push_spec *spec;
644 size_t i, len;
645 char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
646
647 old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
648
649 git_vector_foreach(&push->specs, i, spec) {
650 len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
651
652 if (i == 0) {
653 ++len; /* '\0' */
654 if (push->report_status)
655 len += strlen(GIT_CAP_REPORT_STATUS) + 1;
656 len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
657 }
658
659 git_oid_fmt(old_id, &spec->roid);
660 git_oid_fmt(new_id, &spec->loid);
661
662 git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
663
664 if (i == 0) {
665 git_buf_putc(buf, '\0');
666 /* Core git always starts their capabilities string with a space */
667 if (push->report_status) {
668 git_buf_putc(buf, ' ');
669 git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
670 }
671 git_buf_putc(buf, ' ');
672 git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
673 }
674
675 git_buf_putc(buf, '\n');
676 }
677
678 git_buf_puts(buf, "0000");
679 return git_buf_oom(buf) ? -1 : 0;
680 }
681
682 static int add_push_report_pkt(git_push *push, git_pkt *pkt)
683 {
684 push_status *status;
685
686 switch (pkt->type) {
687 case GIT_PKT_OK:
688 status = git__calloc(1, sizeof(push_status));
689 GITERR_CHECK_ALLOC(status);
690 status->msg = NULL;
691 status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
692 if (!status->ref ||
693 git_vector_insert(&push->status, status) < 0) {
694 git_push_status_free(status);
695 return -1;
696 }
697 break;
698 case GIT_PKT_NG:
699 status = git__calloc(1, sizeof(push_status));
700 GITERR_CHECK_ALLOC(status);
701 status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
702 status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
703 if (!status->ref || !status->msg ||
704 git_vector_insert(&push->status, status) < 0) {
705 git_push_status_free(status);
706 return -1;
707 }
708 break;
709 case GIT_PKT_UNPACK:
710 push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
711 break;
712 case GIT_PKT_FLUSH:
713 return GIT_ITEROVER;
714 default:
715 giterr_set(GITERR_NET, "report-status: protocol error");
716 return -1;
717 }
718
719 return 0;
720 }
721
722 static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt)
723 {
724 git_pkt *pkt;
725 const char *line = data_pkt->data, *line_end;
726 size_t line_len = data_pkt->len;
727 int error;
728
729 while (line_len > 0) {
730 error = git_pkt_parse_line(&pkt, line, &line_end, line_len);
731
732 if (error < 0)
733 return error;
734
735 /* Advance in the buffer */
736 line_len -= (line_end - line);
737 line = line_end;
738
739 error = add_push_report_pkt(push, pkt);
740
741 git_pkt_free(pkt);
742
743 if (error < 0 && error != GIT_ITEROVER)
744 return error;
745 }
746
747 return 0;
748 }
749
750 static int parse_report(transport_smart *transport, git_push *push)
751 {
752 git_pkt *pkt = NULL;
753 const char *line_end = NULL;
754 gitno_buffer *buf = &transport->buffer;
755 int error, recvd;
756
757 for (;;) {
758 if (buf->offset > 0)
759 error = git_pkt_parse_line(&pkt, buf->data,
760 &line_end, buf->offset);
761 else
762 error = GIT_EBUFS;
763
764 if (error < 0 && error != GIT_EBUFS)
765 return -1;
766
767 if (error == GIT_EBUFS) {
768 if ((recvd = gitno_recv(buf)) < 0)
769 return recvd;
770
771 if (recvd == 0) {
772 giterr_set(GITERR_NET, "Early EOF");
773 return -1;
774 }
775 continue;
776 }
777
778 gitno_consume(buf, line_end);
779
780 error = 0;
781
782 switch (pkt->type) {
783 case GIT_PKT_DATA:
784 /* This is a sideband packet which contains other packets */
785 error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt);
786 break;
787 case GIT_PKT_ERR:
788 giterr_set(GITERR_NET, "report-status: Error reported: %s",
789 ((git_pkt_err *)pkt)->error);
790 error = -1;
791 break;
792 case GIT_PKT_PROGRESS:
793 if (transport->progress_cb) {
794 git_pkt_progress *p = (git_pkt_progress *) pkt;
795 error = transport->progress_cb(p->data, p->len, transport->message_cb_payload);
796 }
797 break;
798 default:
799 error = add_push_report_pkt(push, pkt);
800 break;
801 }
802
803 git_pkt_free(pkt);
804
805 /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
806 if (error == GIT_ITEROVER)
807 return 0;
808
809 if (error < 0)
810 return error;
811 }
812 }
813
814 static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
815 {
816 git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
817 GITERR_CHECK_ALLOC(added);
818
819 added->type = GIT_PKT_REF;
820 git_oid_cpy(&added->head.oid, &push_spec->loid);
821 added->head.name = git__strdup(push_spec->refspec.dst);
822
823 if (!added->head.name ||
824 git_vector_insert(refs, added) < 0) {
825 git_pkt_free((git_pkt *)added);
826 return -1;
827 }
828
829 return 0;
830 }
831
832 static int update_refs_from_report(
833 git_vector *refs,
834 git_vector *push_specs,
835 git_vector *push_report)
836 {
837 git_pkt_ref *ref;
838 push_spec *push_spec;
839 push_status *push_status;
840 size_t i, j, refs_len;
841 int cmp;
842
843 /* For each push spec we sent to the server, we should have
844 * gotten back a status packet in the push report */
845 if (push_specs->length != push_report->length) {
846 giterr_set(GITERR_NET, "report-status: protocol error");
847 return -1;
848 }
849
850 /* We require that push_specs be sorted with push_spec_rref_cmp,
851 * and that push_report be sorted with push_status_ref_cmp */
852 git_vector_sort(push_specs);
853 git_vector_sort(push_report);
854
855 git_vector_foreach(push_specs, i, push_spec) {
856 push_status = git_vector_get(push_report, i);
857
858 /* For each push spec we sent to the server, we should have
859 * gotten back a status packet in the push report which matches */
860 if (strcmp(push_spec->refspec.dst, push_status->ref)) {
861 giterr_set(GITERR_NET, "report-status: protocol error");
862 return -1;
863 }
864 }
865
866 /* We require that refs be sorted with ref_name_cmp */
867 git_vector_sort(refs);
868 i = j = 0;
869 refs_len = refs->length;
870
871 /* Merge join push_specs with refs */
872 while (i < push_specs->length && j < refs_len) {
873 push_spec = git_vector_get(push_specs, i);
874 push_status = git_vector_get(push_report, i);
875 ref = git_vector_get(refs, j);
876
877 cmp = strcmp(push_spec->refspec.dst, ref->head.name);
878
879 /* Iterate appropriately */
880 if (cmp <= 0) i++;
881 if (cmp >= 0) j++;
882
883 /* Add case */
884 if (cmp < 0 &&
885 !push_status->msg &&
886 add_ref_from_push_spec(refs, push_spec) < 0)
887 return -1;
888
889 /* Update case, delete case */
890 if (cmp == 0 &&
891 !push_status->msg)
892 git_oid_cpy(&ref->head.oid, &push_spec->loid);
893 }
894
895 for (; i < push_specs->length; i++) {
896 push_spec = git_vector_get(push_specs, i);
897 push_status = git_vector_get(push_report, i);
898
899 /* Add case */
900 if (!push_status->msg &&
901 add_ref_from_push_spec(refs, push_spec) < 0)
902 return -1;
903 }
904
905 /* Remove any refs which we updated to have a zero OID. */
906 git_vector_rforeach(refs, i, ref) {
907 if (git_oid_iszero(&ref->head.oid)) {
908 git_vector_remove(refs, i);
909 git_pkt_free((git_pkt *)ref);
910 }
911 }
912
913 git_vector_sort(refs);
914
915 return 0;
916 }
917
918 struct push_packbuilder_payload
919 {
920 git_smart_subtransport_stream *stream;
921 git_packbuilder *pb;
922 git_push_transfer_progress cb;
923 void *cb_payload;
924 size_t last_bytes;
925 double last_progress_report_time;
926 };
927
928 static int stream_thunk(void *buf, size_t size, void *data)
929 {
930 int error = 0;
931 struct push_packbuilder_payload *payload = data;
932
933 if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
934 return error;
935
936 if (payload->cb) {
937 double current_time = git__timer();
938 payload->last_bytes += size;
939
940 if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
941 payload->last_progress_report_time = current_time;
942 error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
943 }
944 }
945
946 return error;
947 }
948
949 int git_smart__push(git_transport *transport, git_push *push)
950 {
951 transport_smart *t = (transport_smart *)transport;
952 struct push_packbuilder_payload packbuilder_payload = {0};
953 git_buf pktline = GIT_BUF_INIT;
954 int error = 0, need_pack = 0;
955 push_spec *spec;
956 unsigned int i;
957
958 packbuilder_payload.pb = push->pb;
959
960 if (push->transfer_progress_cb) {
961 packbuilder_payload.cb = push->transfer_progress_cb;
962 packbuilder_payload.cb_payload = push->transfer_progress_cb_payload;
963 }
964
965 #ifdef PUSH_DEBUG
966 {
967 git_remote_head *head;
968 char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
969
970 git_vector_foreach(&push->remote->refs, i, head) {
971 git_oid_fmt(hex, &head->oid);
972 fprintf(stderr, "%s (%s)\n", hex, head->name);
973 }
974
975 git_vector_foreach(&push->specs, i, spec) {
976 git_oid_fmt(hex, &spec->roid);
977 fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
978 git_oid_fmt(hex, &spec->loid);
979 fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
980 spec->rref : spec->lref);
981 }
982 }
983 #endif
984
985 /*
986 * Figure out if we need to send a packfile; which is in all
987 * cases except when we only send delete commands
988 */
989 git_vector_foreach(&push->specs, i, spec) {
990 if (spec->refspec.src && spec->refspec.src[0] != '\0') {
991 need_pack = 1;
992 break;
993 }
994 }
995
996 if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
997 (error = gen_pktline(&pktline, push)) < 0 ||
998 (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
999 goto done;
1000
1001 if (need_pack &&
1002 (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
1003 goto done;
1004
1005 /* If we sent nothing or the server doesn't support report-status, then
1006 * we consider the pack to have been unpacked successfully */
1007 if (!push->specs.length || !push->report_status)
1008 push->unpack_ok = 1;
1009 else if ((error = parse_report(t, push)) < 0)
1010 goto done;
1011
1012 /* If progress is being reported write the final report */
1013 if (push->transfer_progress_cb) {
1014 error = push->transfer_progress_cb(
1015 push->pb->nr_written,
1016 push->pb->nr_objects,
1017 packbuilder_payload.last_bytes,
1018 push->transfer_progress_cb_payload);
1019
1020 if (error < 0)
1021 goto done;
1022 }
1023
1024 if (push->status.length) {
1025 error = update_refs_from_report(&t->refs, &push->specs, &push->status);
1026 if (error < 0)
1027 goto done;
1028
1029 error = git_smart__update_heads(t, NULL);
1030 }
1031
1032 done:
1033 git_buf_free(&pktline);
1034 return error;
1035 }