]> git.proxmox.com Git - libgit2.git/blame - src/transports/smart_protocol.c
push: remove own copy of callbacks
[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 29 size_t i;
41fb1ca0 30
613d5eb9
PK
31 /* Clear existing refs in case git_remote_connect() is called again
32 * after git_remote_disconnect().
33 */
4c4408c3
CMN
34 git_vector_foreach(refs, i, pkt) {
35 git_pkt_free(pkt);
d8041638 36 }
613d5eb9 37 git_vector_clear(refs);
4c4408c3 38 pkt = NULL;
613d5eb9 39
41fb1ca0
PK
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)
25e0b157 47 return error;
41fb1ca0
PK
48
49 if (error == GIT_EBUFS) {
50 if ((recvd = gitno_recv(buf)) < 0)
80fc7d6b 51 return recvd;
41fb1ca0
PK
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
8156835d
CMN
80static 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;
6f73e026 85 git_refspec *mapping = NULL;
8156835d
CMN
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 */
6f73e026 100 mapping = git__calloc(1, sizeof(git_refspec));
8156835d
CMN
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
120on_invalid:
121 giterr_set(GITERR_NET, "remote sent invalid symref");
6f73e026 122 git_refspec__free(mapping);
8156835d
CMN
123 return -1;
124}
125
126int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs)
41fb1ca0
PK
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
613d5eb9 139 if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
41fb1ca0
PK
140 caps->common = caps->ofs_delta = 1;
141 ptr += strlen(GIT_CAP_OFS_DELTA);
142 continue;
143 }
144
2f8c481c
CMN
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
613d5eb9 152 if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
41fb1ca0
PK
153 caps->common = caps->multi_ack = 1;
154 ptr += strlen(GIT_CAP_MULTI_ACK);
155 continue;
156 }
157
613d5eb9 158 if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
41fb1ca0
PK
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 */
613d5eb9 165 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
41fb1ca0
PK
166 caps->common = caps->side_band_64k = 1;
167 ptr += strlen(GIT_CAP_SIDE_BAND_64K);
168 continue;
169 }
170
613d5eb9 171 if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
41fb1ca0
PK
172 caps->common = caps->side_band = 1;
173 ptr += strlen(GIT_CAP_SIDE_BAND);
174 continue;
175 }
176
613d5eb9
PK
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
b4342b11
CMN
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
8156835d
CMN
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
41fb1ca0
PK
198 /* We don't know this capability, so skip it */
199 ptr = strchr(ptr, ' ');
200 }
201
202 return 0;
203}
204
205static int recv_pkt(git_pkt **out, gitno_buffer *buf)
206{
207 const char *ptr = buf->data, *line_end = ptr;
e583334c 208 git_pkt *pkt = NULL;
41fb1ca0
PK
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)
80fc7d6b 221 return error;
41fb1ca0
PK
222
223 if ((ret = gitno_recv(buf)) < 0)
80fc7d6b 224 return ret;
41fb1ca0
PK
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
237static int store_common(transport_smart *t)
238{
239 git_pkt *pkt = NULL;
240 gitno_buffer *buf = &t->buffer;
80fc7d6b 241 int error;
41fb1ca0
PK
242
243 do {
80fc7d6b
ET
244 if ((error = recv_pkt(&pkt, buf)) < 0)
245 return error;
41fb1ca0
PK
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
260static int fetch_setup_walk(git_revwalk **out, git_repository *repo)
261{
6f73e026 262 git_revwalk *walk = NULL;
41fb1ca0
PK
263 git_strarray refs;
264 unsigned int i;
265 git_reference *ref;
25e0b157 266 int error;
41fb1ca0 267
25e0b157
RB
268 if ((error = git_reference_list(&refs, repo)) < 0)
269 return error;
41fb1ca0 270
25e0b157
RB
271 if ((error = git_revwalk_new(&walk, repo)) < 0)
272 return error;
41fb1ca0
PK
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
25e0b157 281 if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0)
41fb1ca0
PK
282 goto on_error;
283
284 if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
285 continue;
80fc7d6b 286
25e0b157 287 if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0)
41fb1ca0
PK
288 goto on_error;
289
290 git_reference_free(ref);
291 }
292
293 git_strarray_free(&refs);
294 *out = walk;
295 return 0;
296
297on_error:
6f73e026 298 git_revwalk_free(walk);
41fb1ca0
PK
299 git_reference_free(ref);
300 git_strarray_free(&refs);
25e0b157 301 return error;
41fb1ca0
PK
302}
303
2f8c481c
CMN
304static 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 &&
5cd81bb3 319 (pkt->status != GIT_ACK_CONTINUE &&
2f8c481c
CMN
320 pkt->status != GIT_ACK_COMMON)) {
321 git__free(pkt);
7616b8d3 322 return 0;
2f8c481c
CMN
323 }
324 }
325
326 git__free(pkt);
327 return 0;
328}
329
af613ecd 330int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count)
41fb1ca0
PK
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
af613ecd 340 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
77844988 341 return error;
41fb1ca0 342
77844988 343 if ((error = fetch_setup_walk(&walk, repo)) < 0)
41fb1ca0 344 goto on_error;
a7382aa2 345
41fb1ca0 346 /*
a7382aa2
CMN
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.
41fb1ca0
PK
351 */
352 i = 0;
a7382aa2 353 while (i < 256) {
77844988
PK
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
41fb1ca0
PK
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);
77844988
PK
373 if (git_buf_oom(&data)) {
374 error = -1;
41fb1ca0 375 goto on_error;
77844988 376 }
41fb1ca0 377
77844988 378 if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
41fb1ca0
PK
379 goto on_error;
380
381 git_buf_clear(&data);
2f8c481c 382 if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
77844988 383 if ((error = store_common(t)) < 0)
41fb1ca0
PK
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;
77844988
PK
392 } else if (pkt_type < 0) {
393 /* recv_pkt returned an error */
394 error = pkt_type;
395 goto on_error;
41fb1ca0
PK
396 } else {
397 giterr_set(GITERR_NET, "Unexpected pkt type");
77844988 398 error = -1;
41fb1ca0
PK
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
af613ecd 411 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
41fb1ca0
PK
412 goto on_error;
413
414 git_vector_foreach(&t->common, i, pkt) {
77844988
PK
415 if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
416 goto on_error;
41fb1ca0
PK
417 }
418
77844988
PK
419 if (git_buf_oom(&data)) {
420 error = -1;
41fb1ca0 421 goto on_error;
77844988 422 }
41fb1ca0
PK
423 }
424 }
425
41fb1ca0
PK
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
af613ecd 431 if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0)
41fb1ca0
PK
432 goto on_error;
433
434 git_vector_foreach(&t->common, i, pkt) {
77844988
PK
435 if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
436 goto on_error;
41fb1ca0
PK
437 }
438
77844988
PK
439 if (git_buf_oom(&data)) {
440 error = -1;
41fb1ca0 441 goto on_error;
77844988 442 }
41fb1ca0
PK
443 }
444
77844988
PK
445 if ((error = git_pkt_buffer_done(&data)) < 0)
446 goto on_error;
447
41fb1ca0
PK
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 }
77844988 453 if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
41fb1ca0
PK
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 */
2f8c481c 460 if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
41fb1ca0 461 pkt_type = recv_pkt(NULL, buf);
77844988
PK
462
463 if (pkt_type < 0) {
464 return pkt_type;
465 } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
41fb1ca0
PK
466 giterr_set(GITERR_NET, "Unexpected pkt type");
467 return -1;
468 }
469 } else {
2f8c481c 470 error = wait_while_ack(buf);
41fb1ca0
PK
471 }
472
2f8c481c 473 return error;
41fb1ca0
PK
474
475on_error:
476 git_revwalk_free(walk);
477 git_buf_free(&data);
478 return error;
479}
480
09cc0b92 481static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats)
41fb1ca0
PK
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
a6154f21 491 if (writepack->append(writepack, buf->data, buf->offset, stats) < 0)
41fb1ca0
PK
492 return -1;
493
494 gitno_consume_n(buf, buf->offset);
495
496 if ((recvd = gitno_recv(buf)) < 0)
80fc7d6b 497 return recvd;
41fb1ca0
PK
498 } while(recvd > 0);
499
80fc7d6b 500 if (writepack->commit(writepack, stats) < 0)
41fb1ca0
PK
501 return -1;
502
503 return 0;
504}
505
a8122b5d 506struct network_packetsize_payload
41fb1ca0 507{
48e60ae7 508 git_transfer_progress_cb callback;
41fb1ca0
PK
509 void *payload;
510 git_transfer_progress *stats;
438906e1 511 size_t last_fired_bytes;
41fb1ca0
PK
512};
513
5b188225 514static int network_packetsize(size_t received, void *payload)
41fb1ca0
PK
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) {
a8122b5d 523 npp->last_fired_bytes = npp->stats->received_bytes;
5b188225 524
7baa7631 525 if (npp->callback(npp->stats, npp->payload))
5b188225 526 return GIT_EUSER;
41fb1ca0 527 }
5b188225
JM
528
529 return 0;
41fb1ca0
PK
530}
531
532int git_smart__download_pack(
533 git_transport *transport,
534 git_repository *repo,
535 git_transfer_progress *stats,
48e60ae7 536 git_transfer_progress_cb transfer_progress_cb,
41fb1ca0
PK
537 void *progress_payload)
538{
539 transport_smart *t = (transport_smart *)transport;
41fb1ca0 540 gitno_buffer *buf = &t->buffer;
09cc0b92
ET
541 git_odb *odb;
542 struct git_odb_writepack *writepack = NULL;
5b188225 543 int error = 0;
41fb1ca0
PK
544 struct network_packetsize_payload npp = {0};
545
438906e1
PK
546 memset(stats, 0, sizeof(git_transfer_progress));
547
9effa2fb
JG
548 if (transfer_progress_cb) {
549 npp.callback = transfer_progress_cb;
41fb1ca0
PK
550 npp.payload = progress_payload;
551 npp.stats = stats;
552 t->packetsize_cb = &network_packetsize;
553 t->packetsize_payload = &npp;
438906e1
PK
554
555 /* We might have something in the buffer already from negotiate_fetch */
5b188225 556 if (t->buffer.offset > 0 && !t->cancelled.val)
7baa7631 557 if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
5b188225 558 git_atomic_set(&t->cancelled, 1);
41fb1ca0
PK
559 }
560
09cc0b92 561 if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
9effa2fb 562 ((error = git_odb_write_pack(&writepack, odb, transfer_progress_cb, progress_payload)) != 0))
5b188225 563 goto done;
41fb1ca0 564
41fb1ca0
PK
565 /*
566 * If the remote doesn't support the side-band, we can feed
09cc0b92 567 * the data directly to the pack writer. Otherwise, we need to
41fb1ca0
PK
568 * check which one belongs there.
569 */
570 if (!t->caps.side_band && !t->caps.side_band_64k) {
5b188225
JM
571 error = no_sideband(t, writepack, buf, stats);
572 goto done;
41fb1ca0
PK
573 }
574
575 do {
9effa2fb 576 git_pkt *pkt = NULL;
41fb1ca0 577
5b188225 578 /* Check cancellation before network call */
41fb1ca0 579 if (t->cancelled.val) {
db4cbfe5 580 giterr_clear();
41fb1ca0 581 error = GIT_EUSER;
5b188225 582 goto done;
41fb1ca0
PK
583 }
584
9effa2fb
JG
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;
bc8a0886
PK
597
598 if (p->len)
599 error = writepack->append(writepack, p->data, p->len, stats);
9effa2fb
JG
600 } else if (pkt->type == GIT_PKT_FLUSH) {
601 /* A flush indicates the end of the packfile */
602 git__free(pkt);
603 break;
604 }
5b188225 605 }
41fb1ca0 606
9effa2fb
JG
607 git__free(pkt);
608 if (error < 0)
609 goto done;
41fb1ca0 610
41fb1ca0
PK
611 } while (1);
612
db4cbfe5 613 /*
9effa2fb 614 * Trailing execution of transfer_progress_cb, if necessary...
db4cbfe5
JM
615 * Only the callback through the npp datastructure currently
616 * updates the last_fired_bytes value. It is possible that
25e0b157 617 * progress has already been reported with the correct
db4cbfe5
JM
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) {
25e0b157
RB
623 error = npp.callback(npp.stats, npp.payload);
624 if (error != 0)
db4cbfe5 625 goto done;
db4cbfe5
JM
626 }
627
7baa7631 628 error = writepack->commit(writepack, stats);
41fb1ca0 629
5b188225 630done:
613d5eb9
PK
631 if (writepack)
632 writepack->free(writepack);
9effa2fb 633 if (transfer_progress_cb) {
300f4412
M
634 t->packetsize_cb = NULL;
635 t->packetsize_payload = NULL;
636 }
438906e1 637
41fb1ca0
PK
638 return error;
639}
613d5eb9
PK
640
641static int gen_pktline(git_buf *buf, git_push *push)
642{
613d5eb9 643 push_spec *spec;
4128f5aa 644 size_t i, len;
3b2cb2c9 645 char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1];
4128f5aa 646
3b2cb2c9 647 old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0';
613d5eb9
PK
648
649 git_vector_foreach(&push->specs, i, spec) {
aad638f3 650 len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst);
613d5eb9
PK
651
652 if (i == 0) {
4128f5aa 653 ++len; /* '\0' */
613d5eb9 654 if (push->report_status)
b8c32580
PK
655 len += strlen(GIT_CAP_REPORT_STATUS) + 1;
656 len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
613d5eb9
PK
657 }
658
4128f5aa
CW
659 git_oid_fmt(old_id, &spec->roid);
660 git_oid_fmt(new_id, &spec->loid);
613d5eb9 661
aad638f3 662 git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
613d5eb9
PK
663
664 if (i == 0) {
665 git_buf_putc(buf, '\0');
b8c32580
PK
666 /* Core git always starts their capabilities string with a space */
667 if (push->report_status) {
668 git_buf_putc(buf, ' ');
613d5eb9 669 git_buf_printf(buf, GIT_CAP_REPORT_STATUS);
b8c32580
PK
670 }
671 git_buf_putc(buf, ' ');
672 git_buf_printf(buf, GIT_CAP_SIDE_BAND_64K);
613d5eb9
PK
673 }
674
675 git_buf_putc(buf, '\n');
676 }
4128f5aa 677
613d5eb9
PK
678 git_buf_puts(buf, "0000");
679 return git_buf_oom(buf) ? -1 : 0;
680}
681
b8c32580
PK
682static 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:
392702ee 688 status = git__calloc(1, sizeof(push_status));
b8c32580
PK
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:
392702ee 699 status = git__calloc(1, sizeof(push_status));
b8c32580
PK
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
722static 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
f5898324 743 if (error < 0 && error != GIT_ITEROVER)
b8c32580
PK
744 return error;
745 }
746
747 return 0;
748}
749
9effa2fb 750static int parse_report(transport_smart *transport, git_push *push)
613d5eb9 751{
e583334c
L
752 git_pkt *pkt = NULL;
753 const char *line_end = NULL;
9effa2fb 754 gitno_buffer *buf = &transport->buffer;
613d5eb9
PK
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)
80fc7d6b 769 return recvd;
613d5eb9
PK
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
b8c32580 780 error = 0;
613d5eb9 781
b8c32580
PK
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:
9effa2fb
JG
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 }
b8c32580
PK
797 break;
798 default:
799 error = add_push_report_pkt(push, pkt);
800 break;
613d5eb9
PK
801 }
802
b8c32580 803 git_pkt_free(pkt);
613d5eb9 804
b8c32580 805 /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
f5898324 806 if (error == GIT_ITEROVER)
613d5eb9 807 return 0;
613d5eb9 808
b8c32580
PK
809 if (error < 0)
810 return error;
613d5eb9
PK
811 }
812}
813
df93a681
PK
814static 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);
aad638f3 821 added->head.name = git__strdup(push_spec->refspec.dst);
df93a681
PK
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
832static 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;
a150cc87 839 push_status *push_status;
df93a681
PK
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 */
aad638f3 860 if (strcmp(push_spec->refspec.dst, push_status->ref)) {
df93a681
PK
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);
a150cc87 874 push_status = git_vector_get(push_report, i);
df93a681
PK
875 ref = git_vector_get(refs, j);
876
aad638f3 877 cmp = strcmp(push_spec->refspec.dst, ref->head.name);
df93a681
PK
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);
a150cc87 897 push_status = git_vector_get(push_report, i);
df93a681
PK
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
b176eded
JM
918struct 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
613d5eb9
PK
928static int stream_thunk(void *buf, size_t size, void *data)
929{
b176eded
JM
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;
613d5eb9 939
b176eded
JM
940 if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
941 payload->last_progress_report_time = current_time;
4f62163e 942 error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
b176eded
JM
943 }
944 }
945
946 return error;
613d5eb9
PK
947}
948
949int git_smart__push(git_transport *transport, git_push *push)
950{
951 transport_smart *t = (transport_smart *)transport;
b176eded 952 struct push_packbuilder_payload packbuilder_payload = {0};
05259114 953 git_remote_callbacks *cbs = &push->remote->callbacks;
613d5eb9 954 git_buf pktline = GIT_BUF_INIT;
5b188225 955 int error = 0, need_pack = 0;
51e4da6d
CMN
956 push_spec *spec;
957 unsigned int i;
613d5eb9 958
b176eded
JM
959 packbuilder_payload.pb = push->pb;
960
05259114
CMN
961 if (cbs->transfer_progress) {
962 packbuilder_payload.cb = cbs->push_transfer_progress;
963 packbuilder_payload.cb_payload = cbs->payload;
b176eded
JM
964 }
965
613d5eb9
PK
966#ifdef PUSH_DEBUG
967{
968 git_remote_head *head;
3b2cb2c9 969 char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0';
613d5eb9
PK
970
971 git_vector_foreach(&push->remote->refs, i, head) {
972 git_oid_fmt(hex, &head->oid);
973 fprintf(stderr, "%s (%s)\n", hex, head->name);
974 }
975
976 git_vector_foreach(&push->specs, i, spec) {
977 git_oid_fmt(hex, &spec->roid);
978 fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
979 git_oid_fmt(hex, &spec->loid);
980 fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
981 spec->rref : spec->lref);
982 }
983}
984#endif
985
51e4da6d
CMN
986 /*
987 * Figure out if we need to send a packfile; which is in all
988 * cases except when we only send delete commands
989 */
990 git_vector_foreach(&push->specs, i, spec) {
aad638f3 991 if (spec->refspec.src && spec->refspec.src[0] != '\0') {
51e4da6d
CMN
992 need_pack = 1;
993 break;
994 }
995 }
996
5b188225
JM
997 if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
998 (error = gen_pktline(&pktline, push)) < 0 ||
999 (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
1000 goto done;
51e4da6d 1001
5b188225
JM
1002 if (need_pack &&
1003 (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
1004 goto done;
613d5eb9
PK
1005
1006 /* If we sent nothing or the server doesn't support report-status, then
1007 * we consider the pack to have been unpacked successfully */
1008 if (!push->specs.length || !push->report_status)
1009 push->unpack_ok = 1;
9effa2fb 1010 else if ((error = parse_report(t, push)) < 0)
5b188225 1011 goto done;
613d5eb9 1012
b176eded 1013 /* If progress is being reported write the final report */
05259114
CMN
1014 if (cbs->push_transfer_progress) {
1015 error = cbs->push_transfer_progress(
4f62163e
JG
1016 push->pb->nr_written,
1017 push->pb->nr_objects,
1018 packbuilder_payload.last_bytes,
05259114 1019 cbs->payload);
4f62163e
JG
1020
1021 if (error < 0)
1022 goto done;
b176eded
JM
1023 }
1024
a6192d7c 1025 if (push->status.length) {
7baa7631 1026 error = update_refs_from_report(&t->refs, &push->specs, &push->status);
a6192d7c
CMN
1027 if (error < 0)
1028 goto done;
1029
8156835d 1030 error = git_smart__update_heads(t, NULL);
a6192d7c 1031 }
613d5eb9 1032
5b188225 1033done:
613d5eb9 1034 git_buf_free(&pktline);
613d5eb9
PK
1035 return error;
1036}