]>
Commit | Line | Data |
---|---|---|
41fb1ca0 PK |
1 | /* |
2 | * Copyright (C) 2009-2012 the libgit2 contributors | |
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 PK |
7 | #include "git2.h" |
8 | ||
41fb1ca0 PK |
9 | #include "smart.h" |
10 | #include "refs.h" | |
09cc0b92 | 11 | #include "repository.h" |
613d5eb9 PK |
12 | #include "push.h" |
13 | #include "pack-objects.h" | |
14 | #include "remote.h" | |
41fb1ca0 PK |
15 | |
16 | #define NETWORK_XFER_THRESHOLD (100*1024) | |
17 | ||
18 | int git_smart__store_refs(transport_smart *t, int flushes) | |
19 | { | |
20 | gitno_buffer *buf = &t->buffer; | |
21 | git_vector *refs = &t->refs; | |
22 | int error, flush = 0, recvd; | |
23 | const char *line_end; | |
24 | git_pkt *pkt; | |
25 | ||
613d5eb9 PK |
26 | /* Clear existing refs in case git_remote_connect() is called again |
27 | * after git_remote_disconnect(). | |
28 | */ | |
29 | git_vector_clear(refs); | |
30 | ||
41fb1ca0 PK |
31 | do { |
32 | if (buf->offset > 0) | |
33 | error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); | |
34 | else | |
35 | error = GIT_EBUFS; | |
36 | ||
37 | if (error < 0 && error != GIT_EBUFS) | |
38 | return -1; | |
39 | ||
40 | if (error == GIT_EBUFS) { | |
41 | if ((recvd = gitno_recv(buf)) < 0) | |
42 | return -1; | |
43 | ||
44 | if (recvd == 0 && !flush) { | |
45 | giterr_set(GITERR_NET, "Early EOF"); | |
46 | return -1; | |
47 | } | |
48 | ||
49 | continue; | |
50 | } | |
51 | ||
52 | gitno_consume(buf, line_end); | |
53 | if (pkt->type == GIT_PKT_ERR) { | |
54 | giterr_set(GITERR_NET, "Remote error: %s", ((git_pkt_err *)pkt)->error); | |
55 | git__free(pkt); | |
56 | return -1; | |
57 | } | |
58 | ||
59 | if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0) | |
60 | return -1; | |
61 | ||
62 | if (pkt->type == GIT_PKT_FLUSH) { | |
63 | flush++; | |
64 | git_pkt_free(pkt); | |
65 | } | |
66 | } while (flush < flushes); | |
67 | ||
68 | return flush; | |
69 | } | |
70 | ||
71 | int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps) | |
72 | { | |
73 | const char *ptr; | |
74 | ||
75 | /* No refs or capabilites, odd but not a problem */ | |
76 | if (pkt == NULL || pkt->capabilities == NULL) | |
77 | return 0; | |
78 | ||
79 | ptr = pkt->capabilities; | |
80 | while (ptr != NULL && *ptr != '\0') { | |
81 | if (*ptr == ' ') | |
82 | ptr++; | |
83 | ||
613d5eb9 | 84 | if (!git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) { |
41fb1ca0 PK |
85 | caps->common = caps->ofs_delta = 1; |
86 | ptr += strlen(GIT_CAP_OFS_DELTA); | |
87 | continue; | |
88 | } | |
89 | ||
613d5eb9 | 90 | if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) { |
41fb1ca0 PK |
91 | caps->common = caps->multi_ack = 1; |
92 | ptr += strlen(GIT_CAP_MULTI_ACK); | |
93 | continue; | |
94 | } | |
95 | ||
613d5eb9 | 96 | if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) { |
41fb1ca0 PK |
97 | caps->common = caps->include_tag = 1; |
98 | ptr += strlen(GIT_CAP_INCLUDE_TAG); | |
99 | continue; | |
100 | } | |
101 | ||
102 | /* Keep side-band check after side-band-64k */ | |
613d5eb9 | 103 | if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) { |
41fb1ca0 PK |
104 | caps->common = caps->side_band_64k = 1; |
105 | ptr += strlen(GIT_CAP_SIDE_BAND_64K); | |
106 | continue; | |
107 | } | |
108 | ||
613d5eb9 | 109 | if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) { |
41fb1ca0 PK |
110 | caps->common = caps->side_band = 1; |
111 | ptr += strlen(GIT_CAP_SIDE_BAND); | |
112 | continue; | |
113 | } | |
114 | ||
613d5eb9 PK |
115 | if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) { |
116 | caps->common = caps->delete_refs = 1; | |
117 | ptr += strlen(GIT_CAP_DELETE_REFS); | |
118 | continue; | |
119 | } | |
120 | ||
41fb1ca0 PK |
121 | /* We don't know this capability, so skip it */ |
122 | ptr = strchr(ptr, ' '); | |
123 | } | |
124 | ||
125 | return 0; | |
126 | } | |
127 | ||
128 | static int recv_pkt(git_pkt **out, gitno_buffer *buf) | |
129 | { | |
130 | const char *ptr = buf->data, *line_end = ptr; | |
131 | git_pkt *pkt; | |
132 | int pkt_type, error = 0, ret; | |
133 | ||
134 | do { | |
135 | if (buf->offset > 0) | |
136 | error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); | |
137 | else | |
138 | error = GIT_EBUFS; | |
139 | ||
140 | if (error == 0) | |
141 | break; /* return the pkt */ | |
142 | ||
143 | if (error < 0 && error != GIT_EBUFS) | |
144 | return -1; | |
145 | ||
146 | if ((ret = gitno_recv(buf)) < 0) | |
147 | return -1; | |
148 | } while (error); | |
149 | ||
150 | gitno_consume(buf, line_end); | |
151 | pkt_type = pkt->type; | |
152 | if (out != NULL) | |
153 | *out = pkt; | |
154 | else | |
155 | git__free(pkt); | |
156 | ||
157 | return pkt_type; | |
158 | } | |
159 | ||
160 | static int store_common(transport_smart *t) | |
161 | { | |
162 | git_pkt *pkt = NULL; | |
163 | gitno_buffer *buf = &t->buffer; | |
164 | ||
165 | do { | |
166 | if (recv_pkt(&pkt, buf) < 0) | |
167 | return -1; | |
168 | ||
169 | if (pkt->type == GIT_PKT_ACK) { | |
170 | if (git_vector_insert(&t->common, pkt) < 0) | |
171 | return -1; | |
172 | } else { | |
173 | git__free(pkt); | |
174 | return 0; | |
175 | } | |
176 | ||
177 | } while (1); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static int fetch_setup_walk(git_revwalk **out, git_repository *repo) | |
183 | { | |
184 | git_revwalk *walk; | |
185 | git_strarray refs; | |
186 | unsigned int i; | |
187 | git_reference *ref; | |
188 | ||
189 | if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0) | |
190 | return -1; | |
191 | ||
192 | if (git_revwalk_new(&walk, repo) < 0) | |
193 | return -1; | |
194 | ||
195 | git_revwalk_sorting(walk, GIT_SORT_TIME); | |
196 | ||
197 | for (i = 0; i < refs.count; ++i) { | |
198 | /* No tags */ | |
199 | if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) | |
200 | continue; | |
201 | ||
202 | if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0) | |
203 | goto on_error; | |
204 | ||
205 | if (git_reference_type(ref) == GIT_REF_SYMBOLIC) | |
206 | continue; | |
2508cc66 | 207 | if (git_revwalk_push(walk, git_reference_target(ref)) < 0) |
41fb1ca0 PK |
208 | goto on_error; |
209 | ||
210 | git_reference_free(ref); | |
211 | } | |
212 | ||
213 | git_strarray_free(&refs); | |
214 | *out = walk; | |
215 | return 0; | |
216 | ||
217 | on_error: | |
218 | git_reference_free(ref); | |
219 | git_strarray_free(&refs); | |
220 | return -1; | |
221 | } | |
222 | ||
223 | int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count) | |
224 | { | |
225 | transport_smart *t = (transport_smart *)transport; | |
226 | gitno_buffer *buf = &t->buffer; | |
227 | git_buf data = GIT_BUF_INIT; | |
228 | git_revwalk *walk = NULL; | |
229 | int error = -1, pkt_type; | |
230 | unsigned int i; | |
231 | git_oid oid; | |
232 | ||
233 | /* No own logic, do our thing */ | |
234 | if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0) | |
235 | return -1; | |
236 | ||
237 | if (fetch_setup_walk(&walk, repo) < 0) | |
238 | goto on_error; | |
239 | /* | |
240 | * We don't support any kind of ACK extensions, so the negotiation | |
241 | * boils down to sending what we have and listening for an ACK | |
242 | * every once in a while. | |
243 | */ | |
244 | i = 0; | |
245 | while ((error = git_revwalk_next(&oid, walk)) == 0) { | |
246 | git_pkt_buffer_have(&oid, &data); | |
247 | i++; | |
248 | if (i % 20 == 0) { | |
249 | if (t->cancelled.val) { | |
250 | giterr_set(GITERR_NET, "The fetch was cancelled by the user"); | |
251 | error = GIT_EUSER; | |
252 | goto on_error; | |
253 | } | |
254 | ||
255 | git_pkt_buffer_flush(&data); | |
256 | if (git_buf_oom(&data)) | |
257 | goto on_error; | |
258 | ||
259 | if (git_smart__negotiation_step(&t->parent, data.ptr, data.size) < 0) | |
260 | goto on_error; | |
261 | ||
262 | git_buf_clear(&data); | |
263 | if (t->caps.multi_ack) { | |
264 | if (store_common(t) < 0) | |
265 | goto on_error; | |
266 | } else { | |
267 | pkt_type = recv_pkt(NULL, buf); | |
268 | ||
269 | if (pkt_type == GIT_PKT_ACK) { | |
270 | break; | |
271 | } else if (pkt_type == GIT_PKT_NAK) { | |
272 | continue; | |
273 | } else { | |
274 | giterr_set(GITERR_NET, "Unexpected pkt type"); | |
275 | goto on_error; | |
276 | } | |
277 | } | |
278 | } | |
279 | ||
280 | if (t->common.length > 0) | |
281 | break; | |
282 | ||
283 | if (i % 20 == 0 && t->rpc) { | |
284 | git_pkt_ack *pkt; | |
285 | unsigned int i; | |
286 | ||
287 | if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0) | |
288 | goto on_error; | |
289 | ||
290 | git_vector_foreach(&t->common, i, pkt) { | |
291 | git_pkt_buffer_have(&pkt->oid, &data); | |
292 | } | |
293 | ||
294 | if (git_buf_oom(&data)) | |
295 | goto on_error; | |
296 | } | |
297 | } | |
298 | ||
299 | if (error < 0 && error != GIT_ITEROVER) | |
300 | goto on_error; | |
301 | ||
302 | /* Tell the other end that we're done negotiating */ | |
303 | if (t->rpc && t->common.length > 0) { | |
304 | git_pkt_ack *pkt; | |
305 | unsigned int i; | |
306 | ||
307 | if (git_pkt_buffer_wants(refs, count, &t->caps, &data) < 0) | |
308 | goto on_error; | |
309 | ||
310 | git_vector_foreach(&t->common, i, pkt) { | |
311 | git_pkt_buffer_have(&pkt->oid, &data); | |
312 | } | |
313 | ||
314 | if (git_buf_oom(&data)) | |
315 | goto on_error; | |
316 | } | |
317 | ||
318 | git_pkt_buffer_done(&data); | |
319 | if (t->cancelled.val) { | |
320 | giterr_set(GITERR_NET, "The fetch was cancelled by the user"); | |
321 | error = GIT_EUSER; | |
322 | goto on_error; | |
323 | } | |
324 | if (git_smart__negotiation_step(&t->parent, data.ptr, data.size) < 0) | |
325 | goto on_error; | |
326 | ||
327 | git_buf_free(&data); | |
328 | git_revwalk_free(walk); | |
329 | ||
330 | /* Now let's eat up whatever the server gives us */ | |
331 | if (!t->caps.multi_ack) { | |
332 | pkt_type = recv_pkt(NULL, buf); | |
333 | if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { | |
334 | giterr_set(GITERR_NET, "Unexpected pkt type"); | |
335 | return -1; | |
336 | } | |
337 | } else { | |
338 | git_pkt_ack *pkt; | |
339 | do { | |
340 | if (recv_pkt((git_pkt **)&pkt, buf) < 0) | |
341 | return -1; | |
342 | ||
343 | if (pkt->type == GIT_PKT_NAK || | |
344 | (pkt->type == GIT_PKT_ACK && pkt->status != GIT_ACK_CONTINUE)) { | |
345 | git__free(pkt); | |
346 | break; | |
347 | } | |
348 | ||
349 | git__free(pkt); | |
350 | } while (1); | |
351 | } | |
352 | ||
353 | return 0; | |
354 | ||
355 | on_error: | |
356 | git_revwalk_free(walk); | |
357 | git_buf_free(&data); | |
358 | return error; | |
359 | } | |
360 | ||
09cc0b92 | 361 | static int no_sideband(transport_smart *t, struct git_odb_writepack *writepack, gitno_buffer *buf, git_transfer_progress *stats) |
41fb1ca0 PK |
362 | { |
363 | int recvd; | |
364 | ||
365 | do { | |
366 | if (t->cancelled.val) { | |
367 | giterr_set(GITERR_NET, "The fetch was cancelled by the user"); | |
368 | return GIT_EUSER; | |
369 | } | |
370 | ||
09cc0b92 | 371 | if (writepack->add(writepack, buf->data, buf->offset, stats) < 0) |
41fb1ca0 PK |
372 | return -1; |
373 | ||
374 | gitno_consume_n(buf, buf->offset); | |
375 | ||
376 | if ((recvd = gitno_recv(buf)) < 0) | |
377 | return -1; | |
378 | } while(recvd > 0); | |
379 | ||
09cc0b92 | 380 | if (writepack->commit(writepack, stats)) |
41fb1ca0 PK |
381 | return -1; |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
a8122b5d | 386 | struct network_packetsize_payload |
41fb1ca0 PK |
387 | { |
388 | git_transfer_progress_callback callback; | |
389 | void *payload; | |
390 | git_transfer_progress *stats; | |
438906e1 | 391 | size_t last_fired_bytes; |
41fb1ca0 PK |
392 | }; |
393 | ||
a8122b5d | 394 | static void network_packetsize(size_t received, void *payload) |
41fb1ca0 PK |
395 | { |
396 | struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload; | |
397 | ||
398 | /* Accumulate bytes */ | |
399 | npp->stats->received_bytes += received; | |
400 | ||
401 | /* Fire notification if the threshold is reached */ | |
402 | if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) { | |
a8122b5d RB |
403 | npp->last_fired_bytes = npp->stats->received_bytes; |
404 | npp->callback(npp->stats, npp->payload); | |
41fb1ca0 PK |
405 | } |
406 | } | |
407 | ||
408 | int git_smart__download_pack( | |
409 | git_transport *transport, | |
410 | git_repository *repo, | |
411 | git_transfer_progress *stats, | |
412 | git_transfer_progress_callback progress_cb, | |
413 | void *progress_payload) | |
414 | { | |
415 | transport_smart *t = (transport_smart *)transport; | |
41fb1ca0 | 416 | gitno_buffer *buf = &t->buffer; |
09cc0b92 ET |
417 | git_odb *odb; |
418 | struct git_odb_writepack *writepack = NULL; | |
41fb1ca0 PK |
419 | int error = -1; |
420 | struct network_packetsize_payload npp = {0}; | |
421 | ||
438906e1 PK |
422 | memset(stats, 0, sizeof(git_transfer_progress)); |
423 | ||
41fb1ca0 PK |
424 | if (progress_cb) { |
425 | npp.callback = progress_cb; | |
426 | npp.payload = progress_payload; | |
427 | npp.stats = stats; | |
428 | t->packetsize_cb = &network_packetsize; | |
429 | t->packetsize_payload = &npp; | |
438906e1 PK |
430 | |
431 | /* We might have something in the buffer already from negotiate_fetch */ | |
432 | if (t->buffer.offset > 0) | |
a8122b5d | 433 | t->packetsize_cb((int)t->buffer.offset, t->packetsize_payload); |
41fb1ca0 PK |
434 | } |
435 | ||
09cc0b92 ET |
436 | if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || |
437 | ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0)) | |
41fb1ca0 PK |
438 | goto on_error; |
439 | ||
41fb1ca0 PK |
440 | /* |
441 | * If the remote doesn't support the side-band, we can feed | |
09cc0b92 | 442 | * the data directly to the pack writer. Otherwise, we need to |
41fb1ca0 PK |
443 | * check which one belongs there. |
444 | */ | |
445 | if (!t->caps.side_band && !t->caps.side_band_64k) { | |
09cc0b92 | 446 | if (no_sideband(t, writepack, buf, stats) < 0) |
41fb1ca0 PK |
447 | goto on_error; |
448 | ||
438906e1 | 449 | goto on_success; |
41fb1ca0 PK |
450 | } |
451 | ||
452 | do { | |
453 | git_pkt *pkt; | |
454 | ||
455 | if (t->cancelled.val) { | |
456 | giterr_set(GITERR_NET, "The fetch was cancelled by the user"); | |
457 | error = GIT_EUSER; | |
458 | goto on_error; | |
459 | } | |
460 | ||
461 | if (recv_pkt(&pkt, buf) < 0) | |
462 | goto on_error; | |
463 | ||
464 | if (pkt->type == GIT_PKT_PROGRESS) { | |
465 | if (t->progress_cb) { | |
466 | git_pkt_progress *p = (git_pkt_progress *) pkt; | |
467 | t->progress_cb(p->data, p->len, t->message_cb_payload); | |
468 | } | |
469 | git__free(pkt); | |
470 | } else if (pkt->type == GIT_PKT_DATA) { | |
471 | git_pkt_data *p = (git_pkt_data *) pkt; | |
09cc0b92 | 472 | if (writepack->add(writepack, p->data, p->len, stats) < 0) |
41fb1ca0 PK |
473 | goto on_error; |
474 | ||
475 | git__free(pkt); | |
476 | } else if (pkt->type == GIT_PKT_FLUSH) { | |
477 | /* A flush indicates the end of the packfile */ | |
478 | git__free(pkt); | |
479 | break; | |
480 | } | |
481 | } while (1); | |
482 | ||
09cc0b92 | 483 | if (writepack->commit(writepack, stats) < 0) |
41fb1ca0 PK |
484 | goto on_error; |
485 | ||
438906e1 PK |
486 | on_success: |
487 | error = 0; | |
41fb1ca0 PK |
488 | |
489 | on_error: | |
613d5eb9 PK |
490 | if (writepack) |
491 | writepack->free(writepack); | |
438906e1 PK |
492 | |
493 | /* Trailing execution of progress_cb, if necessary */ | |
494 | if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) | |
495 | npp.callback(npp.stats, npp.payload); | |
496 | ||
41fb1ca0 PK |
497 | return error; |
498 | } | |
613d5eb9 PK |
499 | |
500 | static int gen_pktline(git_buf *buf, git_push *push) | |
501 | { | |
502 | git_remote_head *head; | |
503 | push_spec *spec; | |
504 | unsigned int i, j, len; | |
505 | char hex[41]; hex[40] = '\0'; | |
506 | ||
507 | git_vector_foreach(&push->specs, i, spec) { | |
508 | len = 2*GIT_OID_HEXSZ + 7; | |
509 | ||
510 | if (i == 0) { | |
511 | len +=1; /* '\0' */ | |
512 | if (push->report_status) | |
513 | len += strlen(GIT_CAP_REPORT_STATUS); | |
514 | } | |
515 | ||
516 | if (spec->lref) { | |
517 | len += spec->rref ? strlen(spec->rref) : strlen(spec->lref); | |
518 | ||
519 | if (git_oid_iszero(&spec->roid)) { | |
520 | ||
521 | /* | |
522 | * Create remote reference | |
523 | */ | |
524 | git_oid_fmt(hex, &spec->loid); | |
525 | git_buf_printf(buf, "%04x%s %s %s", len, | |
526 | GIT_OID_HEX_ZERO, hex, | |
527 | spec->rref ? spec->rref : spec->lref); | |
528 | ||
529 | } else { | |
530 | ||
531 | /* | |
532 | * Update remote reference | |
533 | */ | |
534 | git_oid_fmt(hex, &spec->roid); | |
535 | git_buf_printf(buf, "%04x%s ", len, hex); | |
536 | ||
537 | git_oid_fmt(hex, &spec->loid); | |
538 | git_buf_printf(buf, "%s %s", hex, | |
539 | spec->rref ? spec->rref : spec->lref); | |
540 | } | |
541 | } else { | |
542 | /* | |
543 | * Delete remote reference | |
544 | */ | |
545 | git_vector_foreach(&push->remote->refs, j, head) { | |
546 | if (!strcmp(spec->rref, head->name)) { | |
547 | len += strlen(spec->rref); | |
548 | ||
549 | git_oid_fmt(hex, &head->oid); | |
550 | git_buf_printf(buf, "%04x%s %s %s", len, | |
551 | hex, GIT_OID_HEX_ZERO, head->name); | |
552 | ||
553 | break; | |
554 | } | |
555 | } | |
556 | } | |
557 | ||
558 | if (i == 0) { | |
559 | git_buf_putc(buf, '\0'); | |
560 | if (push->report_status) | |
561 | git_buf_printf(buf, GIT_CAP_REPORT_STATUS); | |
562 | } | |
563 | ||
564 | git_buf_putc(buf, '\n'); | |
565 | } | |
566 | git_buf_puts(buf, "0000"); | |
567 | return git_buf_oom(buf) ? -1 : 0; | |
568 | } | |
569 | ||
570 | static int parse_report(gitno_buffer *buf, git_push *push) | |
571 | { | |
572 | git_pkt *pkt; | |
573 | const char *line_end; | |
574 | int error, recvd; | |
575 | ||
576 | for (;;) { | |
577 | if (buf->offset > 0) | |
578 | error = git_pkt_parse_line(&pkt, buf->data, | |
579 | &line_end, buf->offset); | |
580 | else | |
581 | error = GIT_EBUFS; | |
582 | ||
583 | if (error < 0 && error != GIT_EBUFS) | |
584 | return -1; | |
585 | ||
586 | if (error == GIT_EBUFS) { | |
587 | if ((recvd = gitno_recv(buf)) < 0) | |
588 | return -1; | |
589 | ||
590 | if (recvd == 0) { | |
591 | giterr_set(GITERR_NET, "Early EOF"); | |
592 | return -1; | |
593 | } | |
594 | continue; | |
595 | } | |
596 | ||
597 | gitno_consume(buf, line_end); | |
598 | ||
599 | if (pkt->type == GIT_PKT_OK) { | |
6762fe08 | 600 | push_status *status = git__malloc(sizeof(push_status)); |
613d5eb9 PK |
601 | GITERR_CHECK_ALLOC(status); |
602 | status->ref = git__strdup(((git_pkt_ok *)pkt)->ref); | |
603 | status->msg = NULL; | |
604 | git_pkt_free(pkt); | |
605 | if (git_vector_insert(&push->status, status) < 0) { | |
606 | git__free(status); | |
607 | return -1; | |
608 | } | |
609 | continue; | |
610 | } | |
611 | ||
612 | if (pkt->type == GIT_PKT_NG) { | |
6762fe08 | 613 | push_status *status = git__malloc(sizeof(push_status)); |
613d5eb9 PK |
614 | GITERR_CHECK_ALLOC(status); |
615 | status->ref = git__strdup(((git_pkt_ng *)pkt)->ref); | |
616 | status->msg = git__strdup(((git_pkt_ng *)pkt)->msg); | |
617 | git_pkt_free(pkt); | |
618 | if (git_vector_insert(&push->status, status) < 0) { | |
619 | git__free(status); | |
620 | return -1; | |
621 | } | |
622 | continue; | |
623 | } | |
624 | ||
625 | if (pkt->type == GIT_PKT_UNPACK) { | |
626 | push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok; | |
627 | git_pkt_free(pkt); | |
628 | continue; | |
629 | } | |
630 | ||
631 | if (pkt->type == GIT_PKT_FLUSH) { | |
632 | git_pkt_free(pkt); | |
633 | return 0; | |
634 | } | |
635 | ||
636 | git_pkt_free(pkt); | |
637 | giterr_set(GITERR_NET, "report-status: protocol error"); | |
638 | return -1; | |
639 | } | |
640 | } | |
641 | ||
642 | static int stream_thunk(void *buf, size_t size, void *data) | |
643 | { | |
644 | git_smart_subtransport_stream *s = (git_smart_subtransport_stream *)data; | |
645 | ||
646 | return s->write(s, (const char *)buf, size); | |
647 | } | |
648 | ||
649 | int git_smart__push(git_transport *transport, git_push *push) | |
650 | { | |
651 | transport_smart *t = (transport_smart *)transport; | |
652 | git_smart_subtransport_stream *s; | |
653 | git_buf pktline = GIT_BUF_INIT; | |
654 | char *url = NULL; | |
655 | int error = -1; | |
656 | ||
657 | #ifdef PUSH_DEBUG | |
658 | { | |
659 | git_remote_head *head; | |
660 | push_spec *spec; | |
661 | unsigned int i; | |
662 | char hex[41]; hex[40] = '\0'; | |
663 | ||
664 | git_vector_foreach(&push->remote->refs, i, head) { | |
665 | git_oid_fmt(hex, &head->oid); | |
666 | fprintf(stderr, "%s (%s)\n", hex, head->name); | |
667 | } | |
668 | ||
669 | git_vector_foreach(&push->specs, i, spec) { | |
670 | git_oid_fmt(hex, &spec->roid); | |
671 | fprintf(stderr, "%s (%s) -> ", hex, spec->lref); | |
672 | git_oid_fmt(hex, &spec->loid); | |
673 | fprintf(stderr, "%s (%s)\n", hex, spec->rref ? | |
674 | spec->rref : spec->lref); | |
675 | } | |
676 | } | |
677 | #endif | |
678 | ||
679 | if (git_smart__get_push_stream(t, &s) < 0 || | |
680 | gen_pktline(&pktline, push) < 0 || | |
681 | s->write(s, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0 || | |
682 | git_packbuilder_foreach(push->pb, &stream_thunk, s) < 0) | |
683 | goto on_error; | |
684 | ||
685 | /* If we sent nothing or the server doesn't support report-status, then | |
686 | * we consider the pack to have been unpacked successfully */ | |
687 | if (!push->specs.length || !push->report_status) | |
688 | push->unpack_ok = 1; | |
689 | else if (parse_report(&t->buffer, push) < 0) | |
690 | goto on_error; | |
691 | ||
692 | /* If we updated at least one ref, then we need to re-acquire the list of | |
693 | * refs so the caller can call git_remote_update_tips afterward. TODO: Use | |
694 | * the data from the push report to do this without another network call */ | |
695 | if (push->specs.length) { | |
696 | git_cred_acquire_cb cred_cb = t->cred_acquire_cb; | |
59bccf33 | 697 | void *cred_payload = t->cred_acquire_payload; |
613d5eb9 PK |
698 | int flags = t->flags; |
699 | ||
700 | url = git__strdup(t->url); | |
701 | ||
702 | if (!url || t->parent.close(&t->parent) < 0 || | |
59bccf33 | 703 | t->parent.connect(&t->parent, url, cred_cb, cred_payload, GIT_DIRECTION_PUSH, flags)) |
613d5eb9 PK |
704 | goto on_error; |
705 | } | |
706 | ||
707 | error = 0; | |
708 | ||
709 | on_error: | |
710 | git__free(url); | |
711 | git_buf_free(&pktline); | |
712 | ||
713 | return error; | |
714 | } |