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