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