/src/git/transport-helper.c
Line | Count | Source (jump to first uncovered line) |
1 | | #define USE_THE_REPOSITORY_VARIABLE |
2 | | |
3 | | #include "git-compat-util.h" |
4 | | #include "transport.h" |
5 | | #include "quote.h" |
6 | | #include "run-command.h" |
7 | | #include "commit.h" |
8 | | #include "environment.h" |
9 | | #include "gettext.h" |
10 | | #include "hex.h" |
11 | | #include "object-name.h" |
12 | | #include "repository.h" |
13 | | #include "remote.h" |
14 | | #include "string-list.h" |
15 | | #include "thread-utils.h" |
16 | | #include "sigchain.h" |
17 | | #include "strvec.h" |
18 | | #include "refs.h" |
19 | | #include "refspec.h" |
20 | | #include "transport-internal.h" |
21 | | #include "protocol.h" |
22 | | #include "packfile.h" |
23 | | |
24 | | static int debug; |
25 | | |
26 | | struct helper_data { |
27 | | char *name; |
28 | | struct child_process *helper; |
29 | | FILE *out; |
30 | | unsigned fetch : 1, |
31 | | import : 1, |
32 | | bidi_import : 1, |
33 | | export : 1, |
34 | | option : 1, |
35 | | push : 1, |
36 | | connect : 1, |
37 | | stateless_connect : 1, |
38 | | signed_tags : 1, |
39 | | check_connectivity : 1, |
40 | | no_disconnect_req : 1, |
41 | | no_private_update : 1, |
42 | | object_format : 1; |
43 | | |
44 | | /* |
45 | | * As an optimization, the transport code may invoke fetch before |
46 | | * get_refs_list. If this happens, and if the transport helper doesn't |
47 | | * support connect or stateless_connect, we need to invoke |
48 | | * get_refs_list ourselves if we haven't already done so. Keep track of |
49 | | * whether we have invoked get_refs_list. |
50 | | */ |
51 | | unsigned get_refs_list_called : 1; |
52 | | |
53 | | char *export_marks; |
54 | | char *import_marks; |
55 | | /* These go from remote name (as in "list") to private name */ |
56 | | struct refspec rs; |
57 | | /* Transport options for fetch-pack/send-pack (should one of |
58 | | * those be invoked). |
59 | | */ |
60 | | struct git_transport_options transport_options; |
61 | | }; |
62 | | |
63 | | static void sendline(struct helper_data *helper, struct strbuf *buffer) |
64 | 0 | { |
65 | 0 | if (debug) |
66 | 0 | fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf); |
67 | 0 | if (write_in_full(helper->helper->in, buffer->buf, buffer->len) < 0) |
68 | 0 | die_errno(_("full write to remote helper failed")); |
69 | 0 | } |
70 | | |
71 | | static int recvline_fh(FILE *helper, struct strbuf *buffer) |
72 | 0 | { |
73 | 0 | strbuf_reset(buffer); |
74 | 0 | if (debug) |
75 | 0 | fprintf(stderr, "Debug: Remote helper: Waiting...\n"); |
76 | 0 | if (strbuf_getline(buffer, helper) == EOF) { |
77 | 0 | if (debug) |
78 | 0 | fprintf(stderr, "Debug: Remote helper quit.\n"); |
79 | 0 | return 1; |
80 | 0 | } |
81 | | |
82 | 0 | if (debug) |
83 | 0 | fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf); |
84 | 0 | return 0; |
85 | 0 | } |
86 | | |
87 | | static int recvline(struct helper_data *helper, struct strbuf *buffer) |
88 | 0 | { |
89 | 0 | return recvline_fh(helper->out, buffer); |
90 | 0 | } |
91 | | |
92 | | static void write_constant(int fd, const char *str) |
93 | 0 | { |
94 | 0 | if (debug) |
95 | 0 | fprintf(stderr, "Debug: Remote helper: -> %s", str); |
96 | 0 | if (write_in_full(fd, str, strlen(str)) < 0) |
97 | 0 | die_errno(_("full write to remote helper failed")); |
98 | 0 | } |
99 | | |
100 | | static const char *remove_ext_force(const char *url) |
101 | 0 | { |
102 | 0 | if (url) { |
103 | 0 | const char *colon = strchr(url, ':'); |
104 | 0 | if (colon && colon[1] == ':') |
105 | 0 | return colon + 2; |
106 | 0 | } |
107 | 0 | return url; |
108 | 0 | } |
109 | | |
110 | | static void do_take_over(struct transport *transport) |
111 | 0 | { |
112 | 0 | struct helper_data *data; |
113 | 0 | data = (struct helper_data *)transport->data; |
114 | 0 | transport_take_over(transport, data->helper); |
115 | 0 | fclose(data->out); |
116 | 0 | free(data->name); |
117 | 0 | free(data); |
118 | 0 | } |
119 | | |
120 | | static void standard_options(struct transport *t); |
121 | | |
122 | | static struct child_process *get_helper(struct transport *transport) |
123 | 0 | { |
124 | 0 | struct helper_data *data = transport->data; |
125 | 0 | struct strbuf buf = STRBUF_INIT; |
126 | 0 | struct child_process *helper; |
127 | 0 | int duped; |
128 | 0 | int code; |
129 | |
|
130 | 0 | if (data->helper) |
131 | 0 | return data->helper; |
132 | | |
133 | 0 | helper = xmalloc(sizeof(*helper)); |
134 | 0 | child_process_init(helper); |
135 | 0 | helper->in = -1; |
136 | 0 | helper->out = -1; |
137 | 0 | helper->err = 0; |
138 | 0 | strvec_pushf(&helper->args, "remote-%s", data->name); |
139 | 0 | strvec_push(&helper->args, transport->remote->name); |
140 | 0 | strvec_push(&helper->args, remove_ext_force(transport->url)); |
141 | 0 | helper->git_cmd = 1; |
142 | 0 | helper->silent_exec_failure = 1; |
143 | |
|
144 | 0 | if (have_git_dir()) |
145 | 0 | strvec_pushf(&helper->env, "%s=%s", |
146 | 0 | GIT_DIR_ENVIRONMENT, get_git_dir()); |
147 | |
|
148 | 0 | helper->trace2_child_class = helper->args.v[0]; /* "remote-<name>" */ |
149 | |
|
150 | 0 | code = start_command(helper); |
151 | 0 | if (code < 0 && errno == ENOENT) |
152 | 0 | die(_("unable to find remote helper for '%s'"), data->name); |
153 | 0 | else if (code != 0) |
154 | 0 | exit(code); |
155 | | |
156 | 0 | data->helper = helper; |
157 | 0 | data->no_disconnect_req = 0; |
158 | 0 | refspec_init(&data->rs, REFSPEC_FETCH); |
159 | | |
160 | | /* |
161 | | * Open the output as FILE* so strbuf_getline_*() family of |
162 | | * functions can be used. |
163 | | * Do this with duped fd because fclose() will close the fd, |
164 | | * and stuff like taking over will require the fd to remain. |
165 | | */ |
166 | 0 | duped = dup(helper->out); |
167 | 0 | if (duped < 0) |
168 | 0 | die_errno(_("can't dup helper output fd")); |
169 | 0 | data->out = xfdopen(duped, "r"); |
170 | |
|
171 | 0 | write_constant(helper->in, "capabilities\n"); |
172 | |
|
173 | 0 | while (1) { |
174 | 0 | const char *capname, *arg; |
175 | 0 | int mandatory = 0; |
176 | 0 | if (recvline(data, &buf)) |
177 | 0 | exit(128); |
178 | | |
179 | 0 | if (!*buf.buf) |
180 | 0 | break; |
181 | | |
182 | 0 | if (*buf.buf == '*') { |
183 | 0 | capname = buf.buf + 1; |
184 | 0 | mandatory = 1; |
185 | 0 | } else |
186 | 0 | capname = buf.buf; |
187 | |
|
188 | 0 | if (debug) |
189 | 0 | fprintf(stderr, "Debug: Got cap %s\n", capname); |
190 | 0 | if (!strcmp(capname, "fetch")) |
191 | 0 | data->fetch = 1; |
192 | 0 | else if (!strcmp(capname, "option")) |
193 | 0 | data->option = 1; |
194 | 0 | else if (!strcmp(capname, "push")) |
195 | 0 | data->push = 1; |
196 | 0 | else if (!strcmp(capname, "import")) |
197 | 0 | data->import = 1; |
198 | 0 | else if (!strcmp(capname, "bidi-import")) |
199 | 0 | data->bidi_import = 1; |
200 | 0 | else if (!strcmp(capname, "export")) |
201 | 0 | data->export = 1; |
202 | 0 | else if (!strcmp(capname, "check-connectivity")) |
203 | 0 | data->check_connectivity = 1; |
204 | 0 | else if (skip_prefix(capname, "refspec ", &arg)) { |
205 | 0 | refspec_append(&data->rs, arg); |
206 | 0 | } else if (!strcmp(capname, "connect")) { |
207 | 0 | data->connect = 1; |
208 | 0 | } else if (!strcmp(capname, "stateless-connect")) { |
209 | 0 | data->stateless_connect = 1; |
210 | 0 | } else if (!strcmp(capname, "signed-tags")) { |
211 | 0 | data->signed_tags = 1; |
212 | 0 | } else if (skip_prefix(capname, "export-marks ", &arg)) { |
213 | 0 | data->export_marks = xstrdup(arg); |
214 | 0 | } else if (skip_prefix(capname, "import-marks ", &arg)) { |
215 | 0 | data->import_marks = xstrdup(arg); |
216 | 0 | } else if (starts_with(capname, "no-private-update")) { |
217 | 0 | data->no_private_update = 1; |
218 | 0 | } else if (starts_with(capname, "object-format")) { |
219 | 0 | data->object_format = 1; |
220 | 0 | } else if (mandatory) { |
221 | 0 | die(_("unknown mandatory capability %s; this remote " |
222 | 0 | "helper probably needs newer version of Git"), |
223 | 0 | capname); |
224 | 0 | } |
225 | 0 | } |
226 | 0 | if (!data->rs.nr && (data->import || data->bidi_import || data->export)) { |
227 | 0 | warning(_("this remote helper should implement refspec capability")); |
228 | 0 | } |
229 | 0 | strbuf_release(&buf); |
230 | 0 | if (debug) |
231 | 0 | fprintf(stderr, "Debug: Capabilities complete.\n"); |
232 | 0 | standard_options(transport); |
233 | 0 | return data->helper; |
234 | 0 | } |
235 | | |
236 | | static int disconnect_helper(struct transport *transport) |
237 | 0 | { |
238 | 0 | struct helper_data *data = transport->data; |
239 | 0 | int res = 0; |
240 | |
|
241 | 0 | if (data->helper) { |
242 | 0 | if (debug) |
243 | 0 | fprintf(stderr, "Debug: Disconnecting.\n"); |
244 | 0 | if (!data->no_disconnect_req) { |
245 | | /* |
246 | | * Ignore write errors; there's nothing we can do, |
247 | | * since we're about to close the pipe anyway. And the |
248 | | * most likely error is EPIPE due to the helper dying |
249 | | * to report an error itself. |
250 | | */ |
251 | 0 | sigchain_push(SIGPIPE, SIG_IGN); |
252 | 0 | xwrite(data->helper->in, "\n", 1); |
253 | 0 | sigchain_pop(SIGPIPE); |
254 | 0 | } |
255 | 0 | close(data->helper->in); |
256 | 0 | close(data->helper->out); |
257 | 0 | fclose(data->out); |
258 | 0 | res = finish_command(data->helper); |
259 | 0 | FREE_AND_NULL(data->name); |
260 | 0 | FREE_AND_NULL(data->helper); |
261 | 0 | } |
262 | 0 | return res; |
263 | 0 | } |
264 | | |
265 | | static const char *unsupported_options[] = { |
266 | | TRANS_OPT_UPLOADPACK, |
267 | | TRANS_OPT_RECEIVEPACK, |
268 | | TRANS_OPT_THIN, |
269 | | TRANS_OPT_KEEP |
270 | | }; |
271 | | |
272 | | static const char *boolean_options[] = { |
273 | | TRANS_OPT_THIN, |
274 | | TRANS_OPT_KEEP, |
275 | | TRANS_OPT_FOLLOWTAGS, |
276 | | TRANS_OPT_DEEPEN_RELATIVE |
277 | | }; |
278 | | |
279 | | static int strbuf_set_helper_option(struct helper_data *data, |
280 | | struct strbuf *buf) |
281 | 0 | { |
282 | 0 | int ret; |
283 | |
|
284 | 0 | sendline(data, buf); |
285 | 0 | if (recvline(data, buf)) |
286 | 0 | exit(128); |
287 | | |
288 | 0 | if (!strcmp(buf->buf, "ok")) |
289 | 0 | ret = 0; |
290 | 0 | else if (starts_with(buf->buf, "error")) |
291 | 0 | ret = -1; |
292 | 0 | else if (!strcmp(buf->buf, "unsupported")) |
293 | 0 | ret = 1; |
294 | 0 | else { |
295 | 0 | warning(_("%s unexpectedly said: '%s'"), data->name, buf->buf); |
296 | 0 | ret = 1; |
297 | 0 | } |
298 | 0 | return ret; |
299 | 0 | } |
300 | | |
301 | | static int string_list_set_helper_option(struct helper_data *data, |
302 | | const char *name, |
303 | | struct string_list *list) |
304 | 0 | { |
305 | 0 | struct strbuf buf = STRBUF_INIT; |
306 | 0 | int i, ret = 0; |
307 | |
|
308 | 0 | for (i = 0; i < list->nr; i++) { |
309 | 0 | strbuf_addf(&buf, "option %s ", name); |
310 | 0 | quote_c_style(list->items[i].string, &buf, NULL, 0); |
311 | 0 | strbuf_addch(&buf, '\n'); |
312 | |
|
313 | 0 | if ((ret = strbuf_set_helper_option(data, &buf))) |
314 | 0 | break; |
315 | 0 | strbuf_reset(&buf); |
316 | 0 | } |
317 | 0 | strbuf_release(&buf); |
318 | 0 | return ret; |
319 | 0 | } |
320 | | |
321 | | static int set_helper_option(struct transport *transport, |
322 | | const char *name, const char *value) |
323 | 0 | { |
324 | 0 | struct helper_data *data = transport->data; |
325 | 0 | struct strbuf buf = STRBUF_INIT; |
326 | 0 | int i, ret, is_bool = 0; |
327 | |
|
328 | 0 | get_helper(transport); |
329 | |
|
330 | 0 | if (!data->option) |
331 | 0 | return 1; |
332 | | |
333 | 0 | if (!strcmp(name, "deepen-not")) |
334 | 0 | return string_list_set_helper_option(data, name, |
335 | 0 | (struct string_list *)value); |
336 | | |
337 | 0 | for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) { |
338 | 0 | if (!strcmp(name, unsupported_options[i])) |
339 | 0 | return 1; |
340 | 0 | } |
341 | | |
342 | 0 | for (i = 0; i < ARRAY_SIZE(boolean_options); i++) { |
343 | 0 | if (!strcmp(name, boolean_options[i])) { |
344 | 0 | is_bool = 1; |
345 | 0 | break; |
346 | 0 | } |
347 | 0 | } |
348 | |
|
349 | 0 | strbuf_addf(&buf, "option %s ", name); |
350 | 0 | if (is_bool) |
351 | 0 | strbuf_addstr(&buf, value ? "true" : "false"); |
352 | 0 | else |
353 | 0 | quote_c_style(value, &buf, NULL, 0); |
354 | 0 | strbuf_addch(&buf, '\n'); |
355 | |
|
356 | 0 | ret = strbuf_set_helper_option(data, &buf); |
357 | 0 | strbuf_release(&buf); |
358 | 0 | return ret; |
359 | 0 | } |
360 | | |
361 | | static void standard_options(struct transport *t) |
362 | 0 | { |
363 | 0 | char buf[16]; |
364 | 0 | int v = t->verbose; |
365 | |
|
366 | 0 | set_helper_option(t, "progress", t->progress ? "true" : "false"); |
367 | |
|
368 | 0 | xsnprintf(buf, sizeof(buf), "%d", v + 1); |
369 | 0 | set_helper_option(t, "verbosity", buf); |
370 | |
|
371 | 0 | switch (t->family) { |
372 | 0 | case TRANSPORT_FAMILY_ALL: |
373 | | /* |
374 | | * this is already the default, |
375 | | * do not break old remote helpers by setting "all" here |
376 | | */ |
377 | 0 | break; |
378 | 0 | case TRANSPORT_FAMILY_IPV4: |
379 | 0 | set_helper_option(t, "family", "ipv4"); |
380 | 0 | break; |
381 | 0 | case TRANSPORT_FAMILY_IPV6: |
382 | 0 | set_helper_option(t, "family", "ipv6"); |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | } |
386 | | |
387 | | static int release_helper(struct transport *transport) |
388 | 0 | { |
389 | 0 | int res = 0; |
390 | 0 | struct helper_data *data = transport->data; |
391 | 0 | refspec_clear(&data->rs); |
392 | 0 | res = disconnect_helper(transport); |
393 | 0 | free(transport->data); |
394 | 0 | return res; |
395 | 0 | } |
396 | | |
397 | | static int fetch_with_fetch(struct transport *transport, |
398 | | int nr_heads, struct ref **to_fetch) |
399 | 0 | { |
400 | 0 | struct helper_data *data = transport->data; |
401 | 0 | int i; |
402 | 0 | struct strbuf buf = STRBUF_INIT; |
403 | |
|
404 | 0 | for (i = 0; i < nr_heads; i++) { |
405 | 0 | const struct ref *posn = to_fetch[i]; |
406 | 0 | if (posn->status & REF_STATUS_UPTODATE) |
407 | 0 | continue; |
408 | | |
409 | 0 | strbuf_addf(&buf, "fetch %s %s\n", |
410 | 0 | oid_to_hex(&posn->old_oid), |
411 | 0 | posn->symref ? posn->symref : posn->name); |
412 | 0 | } |
413 | |
|
414 | 0 | strbuf_addch(&buf, '\n'); |
415 | 0 | sendline(data, &buf); |
416 | |
|
417 | 0 | while (1) { |
418 | 0 | const char *name; |
419 | |
|
420 | 0 | if (recvline(data, &buf)) |
421 | 0 | exit(128); |
422 | | |
423 | 0 | if (skip_prefix(buf.buf, "lock ", &name)) { |
424 | 0 | if (transport->pack_lockfiles.nr) |
425 | 0 | warning(_("%s also locked %s"), data->name, name); |
426 | 0 | else |
427 | 0 | string_list_append(&transport->pack_lockfiles, |
428 | 0 | name); |
429 | 0 | } |
430 | 0 | else if (data->check_connectivity && |
431 | 0 | data->transport_options.check_self_contained_and_connected && |
432 | 0 | !strcmp(buf.buf, "connectivity-ok")) |
433 | 0 | data->transport_options.self_contained_and_connected = 1; |
434 | 0 | else if (!buf.len) |
435 | 0 | break; |
436 | 0 | else |
437 | 0 | warning(_("%s unexpectedly said: '%s'"), data->name, buf.buf); |
438 | 0 | } |
439 | 0 | strbuf_release(&buf); |
440 | |
|
441 | 0 | reprepare_packed_git(the_repository); |
442 | 0 | return 0; |
443 | 0 | } |
444 | | |
445 | | static int get_importer(struct transport *transport, struct child_process *fastimport) |
446 | 0 | { |
447 | 0 | struct child_process *helper = get_helper(transport); |
448 | 0 | struct helper_data *data = transport->data; |
449 | 0 | int cat_blob_fd, code; |
450 | 0 | child_process_init(fastimport); |
451 | 0 | fastimport->in = xdup(helper->out); |
452 | 0 | strvec_push(&fastimport->args, "fast-import"); |
453 | 0 | strvec_push(&fastimport->args, "--allow-unsafe-features"); |
454 | 0 | strvec_push(&fastimport->args, debug ? "--stats" : "--quiet"); |
455 | |
|
456 | 0 | if (data->bidi_import) { |
457 | 0 | cat_blob_fd = xdup(helper->in); |
458 | 0 | strvec_pushf(&fastimport->args, "--cat-blob-fd=%d", cat_blob_fd); |
459 | 0 | } |
460 | 0 | fastimport->git_cmd = 1; |
461 | |
|
462 | 0 | code = start_command(fastimport); |
463 | 0 | return code; |
464 | 0 | } |
465 | | |
466 | | static int get_exporter(struct transport *transport, |
467 | | struct child_process *fastexport, |
468 | | struct string_list *revlist_args) |
469 | 0 | { |
470 | 0 | struct helper_data *data = transport->data; |
471 | 0 | struct child_process *helper = get_helper(transport); |
472 | 0 | int i; |
473 | |
|
474 | 0 | child_process_init(fastexport); |
475 | | |
476 | | /* we need to duplicate helper->in because we want to use it after |
477 | | * fastexport is done with it. */ |
478 | 0 | fastexport->out = dup(helper->in); |
479 | 0 | strvec_push(&fastexport->args, "fast-export"); |
480 | 0 | strvec_push(&fastexport->args, "--use-done-feature"); |
481 | 0 | strvec_push(&fastexport->args, data->signed_tags ? |
482 | 0 | "--signed-tags=verbatim" : "--signed-tags=warn-strip"); |
483 | 0 | if (data->export_marks) |
484 | 0 | strvec_pushf(&fastexport->args, "--export-marks=%s.tmp", data->export_marks); |
485 | 0 | if (data->import_marks) |
486 | 0 | strvec_pushf(&fastexport->args, "--import-marks=%s", data->import_marks); |
487 | |
|
488 | 0 | for (i = 0; i < revlist_args->nr; i++) |
489 | 0 | strvec_push(&fastexport->args, revlist_args->items[i].string); |
490 | |
|
491 | 0 | fastexport->git_cmd = 1; |
492 | 0 | return start_command(fastexport); |
493 | 0 | } |
494 | | |
495 | | static int fetch_with_import(struct transport *transport, |
496 | | int nr_heads, struct ref **to_fetch) |
497 | 0 | { |
498 | 0 | struct child_process fastimport; |
499 | 0 | struct helper_data *data = transport->data; |
500 | 0 | int i; |
501 | 0 | struct ref *posn; |
502 | 0 | struct strbuf buf = STRBUF_INIT; |
503 | |
|
504 | 0 | get_helper(transport); |
505 | |
|
506 | 0 | if (get_importer(transport, &fastimport)) |
507 | 0 | die(_("couldn't run fast-import")); |
508 | | |
509 | 0 | for (i = 0; i < nr_heads; i++) { |
510 | 0 | posn = to_fetch[i]; |
511 | 0 | if (posn->status & REF_STATUS_UPTODATE) |
512 | 0 | continue; |
513 | | |
514 | 0 | strbuf_addf(&buf, "import %s\n", |
515 | 0 | posn->symref ? posn->symref : posn->name); |
516 | 0 | sendline(data, &buf); |
517 | 0 | strbuf_reset(&buf); |
518 | 0 | } |
519 | |
|
520 | 0 | write_constant(data->helper->in, "\n"); |
521 | | /* |
522 | | * remote-helpers that advertise the bidi-import capability are required to |
523 | | * buffer the complete batch of import commands until this newline before |
524 | | * sending data to fast-import. |
525 | | * These helpers read back data from fast-import on their stdin, which could |
526 | | * be mixed with import commands, otherwise. |
527 | | */ |
528 | |
|
529 | 0 | if (finish_command(&fastimport)) |
530 | 0 | die(_("error while running fast-import")); |
531 | | |
532 | | /* |
533 | | * The fast-import stream of a remote helper that advertises |
534 | | * the "refspec" capability writes to the refs named after the |
535 | | * right hand side of the first refspec matching each ref we |
536 | | * were fetching. |
537 | | * |
538 | | * (If no "refspec" capability was specified, for historical |
539 | | * reasons we default to the equivalent of *:*.) |
540 | | * |
541 | | * Store the result in to_fetch[i].old_sha1. Callers such |
542 | | * as "git fetch" can use the value to write feedback to the |
543 | | * terminal, populate FETCH_HEAD, and determine what new value |
544 | | * should be written to peer_ref if the update is a |
545 | | * fast-forward or this is a forced update. |
546 | | */ |
547 | 0 | for (i = 0; i < nr_heads; i++) { |
548 | 0 | char *private, *name; |
549 | 0 | posn = to_fetch[i]; |
550 | 0 | if (posn->status & REF_STATUS_UPTODATE) |
551 | 0 | continue; |
552 | 0 | name = posn->symref ? posn->symref : posn->name; |
553 | 0 | if (data->rs.nr) |
554 | 0 | private = apply_refspecs(&data->rs, name); |
555 | 0 | else |
556 | 0 | private = xstrdup(name); |
557 | 0 | if (private) { |
558 | 0 | if (refs_read_ref(get_main_ref_store(the_repository), private, &posn->old_oid) < 0) |
559 | 0 | die(_("could not read ref %s"), private); |
560 | 0 | free(private); |
561 | 0 | } |
562 | 0 | } |
563 | 0 | strbuf_release(&buf); |
564 | 0 | return 0; |
565 | 0 | } |
566 | | |
567 | | static int run_connect(struct transport *transport, struct strbuf *cmdbuf) |
568 | 0 | { |
569 | 0 | struct helper_data *data = transport->data; |
570 | 0 | int ret = 0; |
571 | 0 | int duped; |
572 | 0 | FILE *input; |
573 | 0 | struct child_process *helper; |
574 | |
|
575 | 0 | helper = get_helper(transport); |
576 | | |
577 | | /* |
578 | | * Yes, dup the pipe another time, as we need unbuffered version |
579 | | * of input pipe as FILE*. fclose() closes the underlying fd and |
580 | | * stream buffering only can be changed before first I/O operation |
581 | | * on it. |
582 | | */ |
583 | 0 | duped = dup(helper->out); |
584 | 0 | if (duped < 0) |
585 | 0 | die_errno(_("can't dup helper output fd")); |
586 | 0 | input = xfdopen(duped, "r"); |
587 | 0 | setvbuf(input, NULL, _IONBF, 0); |
588 | |
|
589 | 0 | sendline(data, cmdbuf); |
590 | 0 | if (recvline_fh(input, cmdbuf)) |
591 | 0 | exit(128); |
592 | | |
593 | 0 | if (!strcmp(cmdbuf->buf, "")) { |
594 | 0 | data->no_disconnect_req = 1; |
595 | 0 | if (debug) |
596 | 0 | fprintf(stderr, "Debug: Smart transport connection " |
597 | 0 | "ready.\n"); |
598 | 0 | ret = 1; |
599 | 0 | } else if (!strcmp(cmdbuf->buf, "fallback")) { |
600 | 0 | if (debug) |
601 | 0 | fprintf(stderr, "Debug: Falling back to dumb " |
602 | 0 | "transport.\n"); |
603 | 0 | } else { |
604 | 0 | die(_("unknown response to connect: %s"), |
605 | 0 | cmdbuf->buf); |
606 | 0 | } |
607 | | |
608 | 0 | fclose(input); |
609 | 0 | return ret; |
610 | 0 | } |
611 | | |
612 | | static int process_connect_service(struct transport *transport, |
613 | | const char *name, const char *exec) |
614 | 0 | { |
615 | 0 | struct helper_data *data = transport->data; |
616 | 0 | struct strbuf cmdbuf = STRBUF_INIT; |
617 | 0 | int ret = 0; |
618 | | |
619 | | /* |
620 | | * Handle --upload-pack and friends. This is fire and forget... |
621 | | * just warn if it fails. |
622 | | */ |
623 | 0 | if (strcmp(name, exec)) { |
624 | 0 | int r = set_helper_option(transport, "servpath", exec); |
625 | 0 | if (r > 0) |
626 | 0 | warning(_("setting remote service path not supported by protocol")); |
627 | 0 | else if (r < 0) |
628 | 0 | warning(_("invalid remote service path")); |
629 | 0 | } |
630 | |
|
631 | 0 | if (data->connect) { |
632 | 0 | strbuf_addf(&cmdbuf, "connect %s\n", name); |
633 | 0 | ret = run_connect(transport, &cmdbuf); |
634 | 0 | } else if (data->stateless_connect && |
635 | 0 | (get_protocol_version_config() == protocol_v2) && |
636 | 0 | (!strcmp("git-upload-pack", name) || |
637 | 0 | !strcmp("git-upload-archive", name))) { |
638 | 0 | strbuf_addf(&cmdbuf, "stateless-connect %s\n", name); |
639 | 0 | ret = run_connect(transport, &cmdbuf); |
640 | 0 | if (ret) |
641 | 0 | transport->stateless_rpc = 1; |
642 | 0 | } |
643 | |
|
644 | 0 | strbuf_release(&cmdbuf); |
645 | 0 | return ret; |
646 | 0 | } |
647 | | |
648 | | static int process_connect(struct transport *transport, |
649 | | int for_push) |
650 | 0 | { |
651 | 0 | struct helper_data *data = transport->data; |
652 | 0 | const char *name; |
653 | 0 | const char *exec; |
654 | 0 | int ret; |
655 | |
|
656 | 0 | name = for_push ? "git-receive-pack" : "git-upload-pack"; |
657 | 0 | if (for_push) |
658 | 0 | exec = data->transport_options.receivepack; |
659 | 0 | else |
660 | 0 | exec = data->transport_options.uploadpack; |
661 | |
|
662 | 0 | ret = process_connect_service(transport, name, exec); |
663 | 0 | if (ret) |
664 | 0 | do_take_over(transport); |
665 | 0 | return ret; |
666 | 0 | } |
667 | | |
668 | | static int connect_helper(struct transport *transport, const char *name, |
669 | | const char *exec, int fd[2]) |
670 | 0 | { |
671 | 0 | struct helper_data *data = transport->data; |
672 | | |
673 | | /* Get_helper so connect is inited. */ |
674 | 0 | get_helper(transport); |
675 | |
|
676 | 0 | if (!process_connect_service(transport, name, exec)) |
677 | 0 | die(_("can't connect to subservice %s"), name); |
678 | | |
679 | 0 | fd[0] = data->helper->out; |
680 | 0 | fd[1] = data->helper->in; |
681 | |
|
682 | 0 | do_take_over(transport); |
683 | 0 | return 0; |
684 | 0 | } |
685 | | |
686 | | static struct ref *get_refs_list_using_list(struct transport *transport, |
687 | | int for_push); |
688 | | |
689 | | static int fetch_refs(struct transport *transport, |
690 | | int nr_heads, struct ref **to_fetch) |
691 | 0 | { |
692 | 0 | struct helper_data *data = transport->data; |
693 | 0 | int i, count; |
694 | |
|
695 | 0 | get_helper(transport); |
696 | |
|
697 | 0 | if (process_connect(transport, 0)) |
698 | 0 | return transport->vtable->fetch_refs(transport, nr_heads, to_fetch); |
699 | | |
700 | | /* |
701 | | * If we reach here, then the server, the client, and/or the transport |
702 | | * helper does not support protocol v2. --negotiate-only requires |
703 | | * protocol v2. |
704 | | */ |
705 | 0 | if (data->transport_options.acked_commits) { |
706 | 0 | warning(_("--negotiate-only requires protocol v2")); |
707 | 0 | return -1; |
708 | 0 | } |
709 | | |
710 | 0 | if (!data->get_refs_list_called) |
711 | 0 | get_refs_list_using_list(transport, 0); |
712 | |
|
713 | 0 | count = 0; |
714 | 0 | for (i = 0; i < nr_heads; i++) |
715 | 0 | if (!(to_fetch[i]->status & REF_STATUS_UPTODATE)) |
716 | 0 | count++; |
717 | |
|
718 | 0 | if (!count) |
719 | 0 | return 0; |
720 | | |
721 | 0 | if (data->check_connectivity && |
722 | 0 | data->transport_options.check_self_contained_and_connected) |
723 | 0 | set_helper_option(transport, "check-connectivity", "true"); |
724 | |
|
725 | 0 | if (transport->cloning) |
726 | 0 | set_helper_option(transport, "cloning", "true"); |
727 | |
|
728 | 0 | if (data->transport_options.update_shallow) |
729 | 0 | set_helper_option(transport, "update-shallow", "true"); |
730 | |
|
731 | 0 | if (data->transport_options.refetch) |
732 | 0 | set_helper_option(transport, "refetch", "true"); |
733 | |
|
734 | 0 | if (data->transport_options.filter_options.choice) { |
735 | 0 | const char *spec = expand_list_objects_filter_spec( |
736 | 0 | &data->transport_options.filter_options); |
737 | 0 | set_helper_option(transport, "filter", spec); |
738 | 0 | } |
739 | |
|
740 | 0 | if (data->transport_options.negotiation_tips) |
741 | 0 | warning("Ignoring --negotiation-tip because the protocol does not support it."); |
742 | |
|
743 | 0 | if (data->fetch) |
744 | 0 | return fetch_with_fetch(transport, nr_heads, to_fetch); |
745 | | |
746 | 0 | if (data->import) |
747 | 0 | return fetch_with_import(transport, nr_heads, to_fetch); |
748 | | |
749 | 0 | return -1; |
750 | 0 | } |
751 | | |
752 | | struct push_update_ref_state { |
753 | | struct ref *hint; |
754 | | struct ref_push_report *report; |
755 | | int new_report; |
756 | | }; |
757 | | |
758 | | static int push_update_ref_status(struct strbuf *buf, |
759 | | struct push_update_ref_state *state, |
760 | | struct ref *remote_refs) |
761 | 0 | { |
762 | 0 | char *refname, *msg; |
763 | 0 | int status, forced = 0; |
764 | |
|
765 | 0 | if (starts_with(buf->buf, "option ")) { |
766 | 0 | struct object_id old_oid, new_oid; |
767 | 0 | const char *key, *val; |
768 | 0 | char *p; |
769 | |
|
770 | 0 | if (!state->hint || !(state->report || state->new_report)) |
771 | 0 | die(_("'option' without a matching 'ok/error' directive")); |
772 | 0 | if (state->new_report) { |
773 | 0 | if (!state->hint->report) { |
774 | 0 | CALLOC_ARRAY(state->hint->report, 1); |
775 | 0 | state->report = state->hint->report; |
776 | 0 | } else { |
777 | 0 | state->report = state->hint->report; |
778 | 0 | while (state->report->next) |
779 | 0 | state->report = state->report->next; |
780 | 0 | CALLOC_ARRAY(state->report->next, 1); |
781 | 0 | state->report = state->report->next; |
782 | 0 | } |
783 | 0 | state->new_report = 0; |
784 | 0 | } |
785 | 0 | key = buf->buf + 7; |
786 | 0 | p = strchr(key, ' '); |
787 | 0 | if (p) |
788 | 0 | *p++ = '\0'; |
789 | 0 | val = p; |
790 | 0 | if (!strcmp(key, "refname")) |
791 | 0 | state->report->ref_name = xstrdup_or_null(val); |
792 | 0 | else if (!strcmp(key, "old-oid") && val && |
793 | 0 | !parse_oid_hex(val, &old_oid, &val)) |
794 | 0 | state->report->old_oid = oiddup(&old_oid); |
795 | 0 | else if (!strcmp(key, "new-oid") && val && |
796 | 0 | !parse_oid_hex(val, &new_oid, &val)) |
797 | 0 | state->report->new_oid = oiddup(&new_oid); |
798 | 0 | else if (!strcmp(key, "forced-update")) |
799 | 0 | state->report->forced_update = 1; |
800 | | /* Not update remote namespace again. */ |
801 | 0 | return 1; |
802 | 0 | } |
803 | | |
804 | 0 | state->report = NULL; |
805 | 0 | state->new_report = 0; |
806 | |
|
807 | 0 | if (starts_with(buf->buf, "ok ")) { |
808 | 0 | status = REF_STATUS_OK; |
809 | 0 | refname = buf->buf + 3; |
810 | 0 | } else if (starts_with(buf->buf, "error ")) { |
811 | 0 | status = REF_STATUS_REMOTE_REJECT; |
812 | 0 | refname = buf->buf + 6; |
813 | 0 | } else |
814 | 0 | die(_("expected ok/error, helper said '%s'"), buf->buf); |
815 | | |
816 | 0 | msg = strchr(refname, ' '); |
817 | 0 | if (msg) { |
818 | 0 | struct strbuf msg_buf = STRBUF_INIT; |
819 | 0 | const char *end; |
820 | |
|
821 | 0 | *msg++ = '\0'; |
822 | 0 | if (!unquote_c_style(&msg_buf, msg, &end)) |
823 | 0 | msg = strbuf_detach(&msg_buf, NULL); |
824 | 0 | else |
825 | 0 | msg = xstrdup(msg); |
826 | 0 | strbuf_release(&msg_buf); |
827 | |
|
828 | 0 | if (!strcmp(msg, "no match")) { |
829 | 0 | status = REF_STATUS_NONE; |
830 | 0 | FREE_AND_NULL(msg); |
831 | 0 | } |
832 | 0 | else if (!strcmp(msg, "up to date")) { |
833 | 0 | status = REF_STATUS_UPTODATE; |
834 | 0 | FREE_AND_NULL(msg); |
835 | 0 | } |
836 | 0 | else if (!strcmp(msg, "non-fast forward")) { |
837 | 0 | status = REF_STATUS_REJECT_NONFASTFORWARD; |
838 | 0 | FREE_AND_NULL(msg); |
839 | 0 | } |
840 | 0 | else if (!strcmp(msg, "already exists")) { |
841 | 0 | status = REF_STATUS_REJECT_ALREADY_EXISTS; |
842 | 0 | FREE_AND_NULL(msg); |
843 | 0 | } |
844 | 0 | else if (!strcmp(msg, "fetch first")) { |
845 | 0 | status = REF_STATUS_REJECT_FETCH_FIRST; |
846 | 0 | FREE_AND_NULL(msg); |
847 | 0 | } |
848 | 0 | else if (!strcmp(msg, "needs force")) { |
849 | 0 | status = REF_STATUS_REJECT_NEEDS_FORCE; |
850 | 0 | FREE_AND_NULL(msg); |
851 | 0 | } |
852 | 0 | else if (!strcmp(msg, "stale info")) { |
853 | 0 | status = REF_STATUS_REJECT_STALE; |
854 | 0 | FREE_AND_NULL(msg); |
855 | 0 | } |
856 | 0 | else if (!strcmp(msg, "remote ref updated since checkout")) { |
857 | 0 | status = REF_STATUS_REJECT_REMOTE_UPDATED; |
858 | 0 | FREE_AND_NULL(msg); |
859 | 0 | } |
860 | 0 | else if (!strcmp(msg, "forced update")) { |
861 | 0 | forced = 1; |
862 | 0 | FREE_AND_NULL(msg); |
863 | 0 | } |
864 | 0 | else if (!strcmp(msg, "expecting report")) { |
865 | 0 | status = REF_STATUS_EXPECTING_REPORT; |
866 | 0 | FREE_AND_NULL(msg); |
867 | 0 | } |
868 | 0 | } |
869 | |
|
870 | 0 | if (state->hint) |
871 | 0 | state->hint = find_ref_by_name(state->hint, refname); |
872 | 0 | if (!state->hint) |
873 | 0 | state->hint = find_ref_by_name(remote_refs, refname); |
874 | 0 | if (!state->hint) { |
875 | 0 | warning(_("helper reported unexpected status of %s"), refname); |
876 | 0 | return 1; |
877 | 0 | } |
878 | | |
879 | 0 | if (state->hint->status != REF_STATUS_NONE) { |
880 | | /* |
881 | | * Earlier, the ref was marked not to be pushed, so ignore the ref |
882 | | * status reported by the remote helper if the latter is 'no match'. |
883 | | */ |
884 | 0 | if (status == REF_STATUS_NONE) |
885 | 0 | return 1; |
886 | 0 | } |
887 | | |
888 | 0 | if (status == REF_STATUS_OK) |
889 | 0 | state->new_report = 1; |
890 | 0 | state->hint->status = status; |
891 | 0 | state->hint->forced_update |= forced; |
892 | 0 | state->hint->remote_status = msg; |
893 | 0 | return !(status == REF_STATUS_OK); |
894 | 0 | } |
895 | | |
896 | | static int push_update_refs_status(struct helper_data *data, |
897 | | struct ref *remote_refs, |
898 | | int flags) |
899 | 0 | { |
900 | 0 | struct ref *ref; |
901 | 0 | struct ref_push_report *report; |
902 | 0 | struct strbuf buf = STRBUF_INIT; |
903 | 0 | struct push_update_ref_state state = { remote_refs, NULL, 0 }; |
904 | |
|
905 | 0 | for (;;) { |
906 | 0 | if (recvline(data, &buf)) { |
907 | 0 | strbuf_release(&buf); |
908 | 0 | return 1; |
909 | 0 | } |
910 | 0 | if (!buf.len) |
911 | 0 | break; |
912 | 0 | push_update_ref_status(&buf, &state, remote_refs); |
913 | 0 | } |
914 | 0 | strbuf_release(&buf); |
915 | |
|
916 | 0 | if (flags & TRANSPORT_PUSH_DRY_RUN || !data->rs.nr || data->no_private_update) |
917 | 0 | return 0; |
918 | | |
919 | | /* propagate back the update to the remote namespace */ |
920 | 0 | for (ref = remote_refs; ref; ref = ref->next) { |
921 | 0 | char *private; |
922 | |
|
923 | 0 | if (ref->status != REF_STATUS_OK) |
924 | 0 | continue; |
925 | | |
926 | 0 | if (!ref->report) { |
927 | 0 | private = apply_refspecs(&data->rs, ref->name); |
928 | 0 | if (!private) |
929 | 0 | continue; |
930 | 0 | refs_update_ref(get_main_ref_store(the_repository), |
931 | 0 | "update by helper", private, |
932 | 0 | &(ref->new_oid), |
933 | 0 | NULL, 0, 0); |
934 | 0 | free(private); |
935 | 0 | } else { |
936 | 0 | for (report = ref->report; report; report = report->next) { |
937 | 0 | private = apply_refspecs(&data->rs, |
938 | 0 | report->ref_name |
939 | 0 | ? report->ref_name |
940 | 0 | : ref->name); |
941 | 0 | if (!private) |
942 | 0 | continue; |
943 | 0 | refs_update_ref(get_main_ref_store(the_repository), |
944 | 0 | "update by helper", private, |
945 | 0 | report->new_oid |
946 | 0 | ? report->new_oid |
947 | 0 | : &(ref->new_oid), |
948 | 0 | NULL, 0, 0); |
949 | 0 | free(private); |
950 | 0 | } |
951 | 0 | } |
952 | 0 | } |
953 | 0 | return 0; |
954 | 0 | } |
955 | | |
956 | | static void set_common_push_options(struct transport *transport, |
957 | | const char *name, int flags) |
958 | 0 | { |
959 | 0 | if (flags & TRANSPORT_PUSH_DRY_RUN) { |
960 | 0 | if (set_helper_option(transport, "dry-run", "true") != 0) |
961 | 0 | die(_("helper %s does not support dry-run"), name); |
962 | 0 | } else if (flags & TRANSPORT_PUSH_CERT_ALWAYS) { |
963 | 0 | if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0) |
964 | 0 | die(_("helper %s does not support --signed"), name); |
965 | 0 | } else if (flags & TRANSPORT_PUSH_CERT_IF_ASKED) { |
966 | 0 | if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "if-asked") != 0) |
967 | 0 | die(_("helper %s does not support --signed=if-asked"), name); |
968 | 0 | } |
969 | | |
970 | 0 | if (flags & TRANSPORT_PUSH_ATOMIC) |
971 | 0 | if (set_helper_option(transport, TRANS_OPT_ATOMIC, "true") != 0) |
972 | 0 | die(_("helper %s does not support --atomic"), name); |
973 | | |
974 | 0 | if (flags & TRANSPORT_PUSH_FORCE_IF_INCLUDES) |
975 | 0 | if (set_helper_option(transport, TRANS_OPT_FORCE_IF_INCLUDES, "true") != 0) |
976 | 0 | die(_("helper %s does not support --%s"), |
977 | 0 | name, TRANS_OPT_FORCE_IF_INCLUDES); |
978 | | |
979 | 0 | if (flags & TRANSPORT_PUSH_OPTIONS) { |
980 | 0 | struct string_list_item *item; |
981 | 0 | for_each_string_list_item(item, transport->push_options) |
982 | 0 | if (set_helper_option(transport, "push-option", item->string) != 0) |
983 | 0 | die(_("helper %s does not support 'push-option'"), name); |
984 | 0 | } |
985 | 0 | } |
986 | | |
987 | | static int push_refs_with_push(struct transport *transport, |
988 | | struct ref *remote_refs, int flags) |
989 | 0 | { |
990 | 0 | int force_all = flags & TRANSPORT_PUSH_FORCE; |
991 | 0 | int mirror = flags & TRANSPORT_PUSH_MIRROR; |
992 | 0 | int atomic = flags & TRANSPORT_PUSH_ATOMIC; |
993 | 0 | struct helper_data *data = transport->data; |
994 | 0 | struct strbuf buf = STRBUF_INIT; |
995 | 0 | struct ref *ref; |
996 | 0 | struct string_list cas_options = STRING_LIST_INIT_DUP; |
997 | 0 | struct string_list_item *cas_option; |
998 | |
|
999 | 0 | get_helper(transport); |
1000 | 0 | if (!data->push) |
1001 | 0 | return 1; |
1002 | | |
1003 | 0 | for (ref = remote_refs; ref; ref = ref->next) { |
1004 | 0 | if (!ref->peer_ref && !mirror) |
1005 | 0 | continue; |
1006 | | |
1007 | | /* Check for statuses set by set_ref_status_for_push() */ |
1008 | 0 | switch (ref->status) { |
1009 | 0 | case REF_STATUS_REJECT_NONFASTFORWARD: |
1010 | 0 | case REF_STATUS_REJECT_STALE: |
1011 | 0 | case REF_STATUS_REJECT_ALREADY_EXISTS: |
1012 | 0 | case REF_STATUS_REJECT_REMOTE_UPDATED: |
1013 | 0 | if (atomic) { |
1014 | 0 | reject_atomic_push(remote_refs, mirror); |
1015 | 0 | string_list_clear(&cas_options, 0); |
1016 | 0 | return 0; |
1017 | 0 | } else |
1018 | 0 | continue; |
1019 | 0 | case REF_STATUS_UPTODATE: |
1020 | 0 | continue; |
1021 | 0 | default: |
1022 | 0 | ; /* do nothing */ |
1023 | 0 | } |
1024 | | |
1025 | 0 | if (force_all) |
1026 | 0 | ref->force = 1; |
1027 | |
|
1028 | 0 | strbuf_addstr(&buf, "push "); |
1029 | 0 | if (!ref->deletion) { |
1030 | 0 | if (ref->force) |
1031 | 0 | strbuf_addch(&buf, '+'); |
1032 | 0 | if (ref->peer_ref) |
1033 | 0 | strbuf_addstr(&buf, ref->peer_ref->name); |
1034 | 0 | else |
1035 | 0 | strbuf_addstr(&buf, oid_to_hex(&ref->new_oid)); |
1036 | 0 | } |
1037 | 0 | strbuf_addch(&buf, ':'); |
1038 | 0 | strbuf_addstr(&buf, ref->name); |
1039 | 0 | strbuf_addch(&buf, '\n'); |
1040 | | |
1041 | | /* |
1042 | | * The "--force-with-lease" options without explicit |
1043 | | * values to expect have already been expanded into |
1044 | | * the ref->old_oid_expect[] field; we can ignore |
1045 | | * transport->smart_options->cas altogether and instead |
1046 | | * can enumerate them from the refs. |
1047 | | */ |
1048 | 0 | if (ref->expect_old_sha1) { |
1049 | 0 | struct strbuf cas = STRBUF_INIT; |
1050 | 0 | strbuf_addf(&cas, "%s:%s", |
1051 | 0 | ref->name, oid_to_hex(&ref->old_oid_expect)); |
1052 | 0 | string_list_append_nodup(&cas_options, |
1053 | 0 | strbuf_detach(&cas, NULL)); |
1054 | 0 | } |
1055 | 0 | } |
1056 | 0 | if (buf.len == 0) { |
1057 | 0 | string_list_clear(&cas_options, 0); |
1058 | 0 | return 0; |
1059 | 0 | } |
1060 | | |
1061 | 0 | for_each_string_list_item(cas_option, &cas_options) |
1062 | 0 | set_helper_option(transport, "cas", cas_option->string); |
1063 | 0 | set_common_push_options(transport, data->name, flags); |
1064 | |
|
1065 | 0 | strbuf_addch(&buf, '\n'); |
1066 | 0 | sendline(data, &buf); |
1067 | 0 | strbuf_release(&buf); |
1068 | 0 | string_list_clear(&cas_options, 0); |
1069 | |
|
1070 | 0 | return push_update_refs_status(data, remote_refs, flags); |
1071 | 0 | } |
1072 | | |
1073 | | static int push_refs_with_export(struct transport *transport, |
1074 | | struct ref *remote_refs, int flags) |
1075 | 0 | { |
1076 | 0 | struct ref *ref; |
1077 | 0 | struct child_process *helper, exporter; |
1078 | 0 | struct helper_data *data = transport->data; |
1079 | 0 | struct string_list revlist_args = STRING_LIST_INIT_DUP; |
1080 | 0 | struct strbuf buf = STRBUF_INIT; |
1081 | |
|
1082 | 0 | if (!data->rs.nr) |
1083 | 0 | die(_("remote-helper doesn't support push; refspec needed")); |
1084 | | |
1085 | 0 | set_common_push_options(transport, data->name, flags); |
1086 | 0 | if (flags & TRANSPORT_PUSH_FORCE) { |
1087 | 0 | if (set_helper_option(transport, "force", "true") != 0) |
1088 | 0 | warning(_("helper %s does not support '--force'"), data->name); |
1089 | 0 | } |
1090 | |
|
1091 | 0 | helper = get_helper(transport); |
1092 | |
|
1093 | 0 | write_constant(helper->in, "export\n"); |
1094 | |
|
1095 | 0 | for (ref = remote_refs; ref; ref = ref->next) { |
1096 | 0 | char *private; |
1097 | 0 | struct object_id oid; |
1098 | |
|
1099 | 0 | private = apply_refspecs(&data->rs, ref->name); |
1100 | 0 | if (private && !repo_get_oid(the_repository, private, &oid)) { |
1101 | 0 | strbuf_addf(&buf, "^%s", private); |
1102 | 0 | string_list_append_nodup(&revlist_args, |
1103 | 0 | strbuf_detach(&buf, NULL)); |
1104 | 0 | oidcpy(&ref->old_oid, &oid); |
1105 | 0 | } |
1106 | 0 | free(private); |
1107 | |
|
1108 | 0 | if (ref->peer_ref) { |
1109 | 0 | if (strcmp(ref->name, ref->peer_ref->name)) { |
1110 | 0 | if (!ref->deletion) { |
1111 | 0 | const char *name; |
1112 | 0 | int flag; |
1113 | | |
1114 | | /* Follow symbolic refs (mainly for HEAD). */ |
1115 | 0 | name = refs_resolve_ref_unsafe(get_main_ref_store(the_repository), |
1116 | 0 | ref->peer_ref->name, |
1117 | 0 | RESOLVE_REF_READING, |
1118 | 0 | &oid, |
1119 | 0 | &flag); |
1120 | 0 | if (!name || !(flag & REF_ISSYMREF)) |
1121 | 0 | name = ref->peer_ref->name; |
1122 | |
|
1123 | 0 | strbuf_addf(&buf, "%s:%s", name, ref->name); |
1124 | 0 | } else |
1125 | 0 | strbuf_addf(&buf, ":%s", ref->name); |
1126 | |
|
1127 | 0 | string_list_append(&revlist_args, "--refspec"); |
1128 | 0 | string_list_append(&revlist_args, buf.buf); |
1129 | 0 | strbuf_release(&buf); |
1130 | 0 | } |
1131 | 0 | if (!ref->deletion) |
1132 | 0 | string_list_append(&revlist_args, ref->peer_ref->name); |
1133 | 0 | } |
1134 | 0 | } |
1135 | |
|
1136 | 0 | if (get_exporter(transport, &exporter, &revlist_args)) |
1137 | 0 | die(_("couldn't run fast-export")); |
1138 | | |
1139 | 0 | string_list_clear(&revlist_args, 1); |
1140 | |
|
1141 | 0 | if (finish_command(&exporter)) |
1142 | 0 | die(_("error while running fast-export")); |
1143 | 0 | if (push_update_refs_status(data, remote_refs, flags)) |
1144 | 0 | return 1; |
1145 | | |
1146 | 0 | if (data->export_marks) { |
1147 | 0 | strbuf_addf(&buf, "%s.tmp", data->export_marks); |
1148 | 0 | rename(buf.buf, data->export_marks); |
1149 | 0 | strbuf_release(&buf); |
1150 | 0 | } |
1151 | |
|
1152 | 0 | return 0; |
1153 | 0 | } |
1154 | | |
1155 | | static int push_refs(struct transport *transport, |
1156 | | struct ref *remote_refs, int flags) |
1157 | 0 | { |
1158 | 0 | struct helper_data *data = transport->data; |
1159 | |
|
1160 | 0 | if (process_connect(transport, 1)) |
1161 | 0 | return transport->vtable->push_refs(transport, remote_refs, flags); |
1162 | | |
1163 | 0 | if (!remote_refs) { |
1164 | 0 | fprintf(stderr, |
1165 | 0 | _("No refs in common and none specified; doing nothing.\n" |
1166 | 0 | "Perhaps you should specify a branch.\n")); |
1167 | 0 | return 0; |
1168 | 0 | } |
1169 | | |
1170 | 0 | if (data->push) |
1171 | 0 | return push_refs_with_push(transport, remote_refs, flags); |
1172 | | |
1173 | 0 | if (data->export) |
1174 | 0 | return push_refs_with_export(transport, remote_refs, flags); |
1175 | | |
1176 | 0 | return -1; |
1177 | 0 | } |
1178 | | |
1179 | | |
1180 | | static int has_attribute(const char *attrs, const char *attr) |
1181 | 0 | { |
1182 | 0 | int len; |
1183 | 0 | if (!attrs) |
1184 | 0 | return 0; |
1185 | | |
1186 | 0 | len = strlen(attr); |
1187 | 0 | for (;;) { |
1188 | 0 | const char *space = strchrnul(attrs, ' '); |
1189 | 0 | if (len == space - attrs && !strncmp(attrs, attr, len)) |
1190 | 0 | return 1; |
1191 | 0 | if (!*space) |
1192 | 0 | return 0; |
1193 | 0 | attrs = space + 1; |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | | static struct ref *get_refs_list(struct transport *transport, int for_push, |
1198 | | struct transport_ls_refs_options *transport_options) |
1199 | 0 | { |
1200 | 0 | get_helper(transport); |
1201 | |
|
1202 | 0 | if (process_connect(transport, for_push)) |
1203 | 0 | return transport->vtable->get_refs_list(transport, for_push, |
1204 | 0 | transport_options); |
1205 | | |
1206 | 0 | return get_refs_list_using_list(transport, for_push); |
1207 | 0 | } |
1208 | | |
1209 | | static struct ref *get_refs_list_using_list(struct transport *transport, |
1210 | | int for_push) |
1211 | 0 | { |
1212 | 0 | struct helper_data *data = transport->data; |
1213 | 0 | struct child_process *helper; |
1214 | 0 | struct ref *ret = NULL; |
1215 | 0 | struct ref **tail = &ret; |
1216 | 0 | struct ref *posn; |
1217 | 0 | struct strbuf buf = STRBUF_INIT; |
1218 | |
|
1219 | 0 | data->get_refs_list_called = 1; |
1220 | 0 | helper = get_helper(transport); |
1221 | |
|
1222 | 0 | if (data->object_format) |
1223 | 0 | set_helper_option(transport, "object-format", "true"); |
1224 | |
|
1225 | 0 | if (data->push && for_push) |
1226 | 0 | write_constant(helper->in, "list for-push\n"); |
1227 | 0 | else |
1228 | 0 | write_constant(helper->in, "list\n"); |
1229 | |
|
1230 | 0 | while (1) { |
1231 | 0 | char *eov, *eon; |
1232 | 0 | if (recvline(data, &buf)) |
1233 | 0 | exit(128); |
1234 | | |
1235 | 0 | if (!*buf.buf) |
1236 | 0 | break; |
1237 | 0 | else if (buf.buf[0] == ':') { |
1238 | 0 | const char *value; |
1239 | 0 | if (skip_prefix(buf.buf, ":object-format ", &value)) { |
1240 | 0 | int algo = hash_algo_by_name(value); |
1241 | 0 | if (algo == GIT_HASH_UNKNOWN) |
1242 | 0 | die(_("unsupported object format '%s'"), |
1243 | 0 | value); |
1244 | 0 | transport->hash_algo = &hash_algos[algo]; |
1245 | 0 | } |
1246 | 0 | continue; |
1247 | 0 | } |
1248 | | |
1249 | 0 | eov = strchr(buf.buf, ' '); |
1250 | 0 | if (!eov) |
1251 | 0 | die(_("malformed response in ref list: %s"), buf.buf); |
1252 | 0 | eon = strchr(eov + 1, ' '); |
1253 | 0 | *eov = '\0'; |
1254 | 0 | if (eon) |
1255 | 0 | *eon = '\0'; |
1256 | 0 | *tail = alloc_ref(eov + 1); |
1257 | 0 | if (buf.buf[0] == '@') |
1258 | 0 | (*tail)->symref = xstrdup(buf.buf + 1); |
1259 | 0 | else if (buf.buf[0] != '?') |
1260 | 0 | get_oid_hex_algop(buf.buf, &(*tail)->old_oid, transport->hash_algo); |
1261 | 0 | if (eon) { |
1262 | 0 | if (has_attribute(eon + 1, "unchanged")) { |
1263 | 0 | (*tail)->status |= REF_STATUS_UPTODATE; |
1264 | 0 | if (refs_read_ref(get_main_ref_store(the_repository), (*tail)->name, &(*tail)->old_oid) < 0) |
1265 | 0 | die(_("could not read ref %s"), |
1266 | 0 | (*tail)->name); |
1267 | 0 | } |
1268 | 0 | } |
1269 | 0 | tail = &((*tail)->next); |
1270 | 0 | } |
1271 | 0 | if (debug) |
1272 | 0 | fprintf(stderr, "Debug: Read ref listing.\n"); |
1273 | 0 | strbuf_release(&buf); |
1274 | |
|
1275 | 0 | for (posn = ret; posn; posn = posn->next) |
1276 | 0 | resolve_remote_symref(posn, ret); |
1277 | |
|
1278 | 0 | return ret; |
1279 | 0 | } |
1280 | | |
1281 | | static int get_bundle_uri(struct transport *transport) |
1282 | 0 | { |
1283 | 0 | get_helper(transport); |
1284 | |
|
1285 | 0 | if (process_connect(transport, 0)) |
1286 | 0 | return transport->vtable->get_bundle_uri(transport); |
1287 | | |
1288 | 0 | return -1; |
1289 | 0 | } |
1290 | | |
1291 | | static struct transport_vtable vtable = { |
1292 | | .set_option = set_helper_option, |
1293 | | .get_refs_list = get_refs_list, |
1294 | | .get_bundle_uri = get_bundle_uri, |
1295 | | .fetch_refs = fetch_refs, |
1296 | | .push_refs = push_refs, |
1297 | | .connect = connect_helper, |
1298 | | .disconnect = release_helper |
1299 | | }; |
1300 | | |
1301 | | int transport_helper_init(struct transport *transport, const char *name) |
1302 | 0 | { |
1303 | 0 | struct helper_data *data = xcalloc(1, sizeof(*data)); |
1304 | 0 | data->name = xstrdup(name); |
1305 | |
|
1306 | 0 | transport_check_allowed(name); |
1307 | |
|
1308 | 0 | if (getenv("GIT_TRANSPORT_HELPER_DEBUG")) |
1309 | 0 | debug = 1; |
1310 | |
|
1311 | 0 | list_objects_filter_init(&data->transport_options.filter_options); |
1312 | |
|
1313 | 0 | transport->data = data; |
1314 | 0 | transport->vtable = &vtable; |
1315 | 0 | transport->smart_options = &(data->transport_options); |
1316 | 0 | return 0; |
1317 | 0 | } |
1318 | | |
1319 | | /* |
1320 | | * Linux pipes can buffer 65536 bytes at once (and most platforms can |
1321 | | * buffer less), so attempt reads and writes with up to that size. |
1322 | | */ |
1323 | 0 | #define BUFFERSIZE 65536 |
1324 | | /* This should be enough to hold debugging message. */ |
1325 | 0 | #define PBUFFERSIZE 8192 |
1326 | | |
1327 | | /* Print bidirectional transfer loop debug message. */ |
1328 | | __attribute__((format (printf, 1, 2))) |
1329 | | static void transfer_debug(const char *fmt, ...) |
1330 | 0 | { |
1331 | | /* |
1332 | | * NEEDSWORK: This function is sometimes used from multiple threads, and |
1333 | | * we end up using debug_enabled racily. That "should not matter" since |
1334 | | * we always write the same value, but it's still wrong. This function |
1335 | | * is listed in .tsan-suppressions for the time being. |
1336 | | */ |
1337 | |
|
1338 | 0 | va_list args; |
1339 | 0 | char msgbuf[PBUFFERSIZE]; |
1340 | 0 | static int debug_enabled = -1; |
1341 | |
|
1342 | 0 | if (debug_enabled < 0) |
1343 | 0 | debug_enabled = getenv("GIT_TRANSLOOP_DEBUG") ? 1 : 0; |
1344 | 0 | if (!debug_enabled) |
1345 | 0 | return; |
1346 | | |
1347 | 0 | va_start(args, fmt); |
1348 | 0 | vsnprintf(msgbuf, PBUFFERSIZE, fmt, args); |
1349 | 0 | va_end(args); |
1350 | 0 | fprintf(stderr, "Transfer loop debugging: %s\n", msgbuf); |
1351 | 0 | } |
1352 | | |
1353 | | /* Stream state: More data may be coming in this direction. */ |
1354 | 0 | #define SSTATE_TRANSFERRING 0 |
1355 | | /* |
1356 | | * Stream state: No more data coming in this direction, flushing rest of |
1357 | | * data. |
1358 | | */ |
1359 | 0 | #define SSTATE_FLUSHING 1 |
1360 | | /* Stream state: Transfer in this direction finished. */ |
1361 | 0 | #define SSTATE_FINISHED 2 |
1362 | | |
1363 | 0 | #define STATE_NEEDS_READING(state) ((state) <= SSTATE_TRANSFERRING) |
1364 | 0 | #define STATE_NEEDS_WRITING(state) ((state) <= SSTATE_FLUSHING) |
1365 | 0 | #define STATE_NEEDS_CLOSING(state) ((state) == SSTATE_FLUSHING) |
1366 | | |
1367 | | /* Unidirectional transfer. */ |
1368 | | struct unidirectional_transfer { |
1369 | | /* Source */ |
1370 | | int src; |
1371 | | /* Destination */ |
1372 | | int dest; |
1373 | | /* Is source socket? */ |
1374 | | int src_is_sock; |
1375 | | /* Is destination socket? */ |
1376 | | int dest_is_sock; |
1377 | | /* Transfer state (TRANSFERRING/FLUSHING/FINISHED) */ |
1378 | | int state; |
1379 | | /* Buffer. */ |
1380 | | char buf[BUFFERSIZE]; |
1381 | | /* Buffer used. */ |
1382 | | size_t bufuse; |
1383 | | /* Name of source. */ |
1384 | | const char *src_name; |
1385 | | /* Name of destination. */ |
1386 | | const char *dest_name; |
1387 | | }; |
1388 | | |
1389 | | /* Closes the target (for writing) if transfer has finished. */ |
1390 | | static void udt_close_if_finished(struct unidirectional_transfer *t) |
1391 | 0 | { |
1392 | 0 | if (STATE_NEEDS_CLOSING(t->state) && !t->bufuse) { |
1393 | 0 | t->state = SSTATE_FINISHED; |
1394 | 0 | if (t->dest_is_sock) |
1395 | 0 | shutdown(t->dest, SHUT_WR); |
1396 | 0 | else |
1397 | 0 | close(t->dest); |
1398 | 0 | transfer_debug("Closed %s.", t->dest_name); |
1399 | 0 | } |
1400 | 0 | } |
1401 | | |
1402 | | /* |
1403 | | * Tries to read data from source into buffer. If buffer is full, |
1404 | | * no data is read. Returns 0 on success, -1 on error. |
1405 | | */ |
1406 | | static int udt_do_read(struct unidirectional_transfer *t) |
1407 | 0 | { |
1408 | 0 | ssize_t bytes; |
1409 | |
|
1410 | 0 | if (t->bufuse == BUFFERSIZE) |
1411 | 0 | return 0; /* No space for more. */ |
1412 | | |
1413 | 0 | transfer_debug("%s is readable", t->src_name); |
1414 | 0 | bytes = xread(t->src, t->buf + t->bufuse, BUFFERSIZE - t->bufuse); |
1415 | 0 | if (bytes < 0) { |
1416 | 0 | error_errno(_("read(%s) failed"), t->src_name); |
1417 | 0 | return -1; |
1418 | 0 | } else if (bytes == 0) { |
1419 | 0 | transfer_debug("%s EOF (with %i bytes in buffer)", |
1420 | 0 | t->src_name, (int)t->bufuse); |
1421 | 0 | t->state = SSTATE_FLUSHING; |
1422 | 0 | } else if (bytes > 0) { |
1423 | 0 | t->bufuse += bytes; |
1424 | 0 | transfer_debug("Read %i bytes from %s (buffer now at %i)", |
1425 | 0 | (int)bytes, t->src_name, (int)t->bufuse); |
1426 | 0 | } |
1427 | 0 | return 0; |
1428 | 0 | } |
1429 | | |
1430 | | /* Tries to write data from buffer into destination. If buffer is empty, |
1431 | | * no data is written. Returns 0 on success, -1 on error. |
1432 | | */ |
1433 | | static int udt_do_write(struct unidirectional_transfer *t) |
1434 | 0 | { |
1435 | 0 | ssize_t bytes; |
1436 | |
|
1437 | 0 | if (t->bufuse == 0) |
1438 | 0 | return 0; /* Nothing to write. */ |
1439 | | |
1440 | 0 | transfer_debug("%s is writable", t->dest_name); |
1441 | 0 | bytes = xwrite(t->dest, t->buf, t->bufuse); |
1442 | 0 | if (bytes < 0) { |
1443 | 0 | error_errno(_("write(%s) failed"), t->dest_name); |
1444 | 0 | return -1; |
1445 | 0 | } else if (bytes > 0) { |
1446 | 0 | t->bufuse -= bytes; |
1447 | 0 | if (t->bufuse) |
1448 | 0 | memmove(t->buf, t->buf + bytes, t->bufuse); |
1449 | 0 | transfer_debug("Wrote %i bytes to %s (buffer now at %i)", |
1450 | 0 | (int)bytes, t->dest_name, (int)t->bufuse); |
1451 | 0 | } |
1452 | 0 | return 0; |
1453 | 0 | } |
1454 | | |
1455 | | |
1456 | | /* State of bidirectional transfer loop. */ |
1457 | | struct bidirectional_transfer_state { |
1458 | | /* Direction from program to git. */ |
1459 | | struct unidirectional_transfer ptg; |
1460 | | /* Direction from git to program. */ |
1461 | | struct unidirectional_transfer gtp; |
1462 | | }; |
1463 | | |
1464 | | static void *udt_copy_task_routine(void *udt) |
1465 | 0 | { |
1466 | 0 | struct unidirectional_transfer *t = (struct unidirectional_transfer *)udt; |
1467 | 0 | while (t->state != SSTATE_FINISHED) { |
1468 | 0 | if (STATE_NEEDS_READING(t->state)) |
1469 | 0 | if (udt_do_read(t)) |
1470 | 0 | return NULL; |
1471 | 0 | if (STATE_NEEDS_WRITING(t->state)) |
1472 | 0 | if (udt_do_write(t)) |
1473 | 0 | return NULL; |
1474 | 0 | if (STATE_NEEDS_CLOSING(t->state)) |
1475 | 0 | udt_close_if_finished(t); |
1476 | 0 | } |
1477 | 0 | return udt; /* Just some non-NULL value. */ |
1478 | 0 | } |
1479 | | |
1480 | | #ifndef NO_PTHREADS |
1481 | | |
1482 | | /* |
1483 | | * Join thread, with appropriate errors on failure. Name is name for the |
1484 | | * thread (for error messages). Returns 0 on success, 1 on failure. |
1485 | | */ |
1486 | | static int tloop_join(pthread_t thread, const char *name) |
1487 | 0 | { |
1488 | 0 | int err; |
1489 | 0 | void *tret; |
1490 | 0 | err = pthread_join(thread, &tret); |
1491 | 0 | if (!tret) { |
1492 | 0 | error(_("%s thread failed"), name); |
1493 | 0 | return 1; |
1494 | 0 | } |
1495 | 0 | if (err) { |
1496 | 0 | error(_("%s thread failed to join: %s"), name, strerror(err)); |
1497 | 0 | return 1; |
1498 | 0 | } |
1499 | 0 | return 0; |
1500 | 0 | } |
1501 | | |
1502 | | /* |
1503 | | * Spawn the transfer tasks and then wait for them. Returns 0 on success, |
1504 | | * -1 on failure. |
1505 | | */ |
1506 | | static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s) |
1507 | 0 | { |
1508 | 0 | pthread_t gtp_thread; |
1509 | 0 | pthread_t ptg_thread; |
1510 | 0 | int err; |
1511 | 0 | int ret = 0; |
1512 | 0 | err = pthread_create(>p_thread, NULL, udt_copy_task_routine, |
1513 | 0 | &s->gtp); |
1514 | 0 | if (err) |
1515 | 0 | die(_("can't start thread for copying data: %s"), strerror(err)); |
1516 | 0 | err = pthread_create(&ptg_thread, NULL, udt_copy_task_routine, |
1517 | 0 | &s->ptg); |
1518 | 0 | if (err) |
1519 | 0 | die(_("can't start thread for copying data: %s"), strerror(err)); |
1520 | | |
1521 | 0 | ret |= tloop_join(gtp_thread, "Git to program copy"); |
1522 | 0 | ret |= tloop_join(ptg_thread, "Program to git copy"); |
1523 | 0 | return ret; |
1524 | 0 | } |
1525 | | #else |
1526 | | |
1527 | | /* Close the source and target (for writing) for transfer. */ |
1528 | | static void udt_kill_transfer(struct unidirectional_transfer *t) |
1529 | | { |
1530 | | t->state = SSTATE_FINISHED; |
1531 | | /* |
1532 | | * Socket read end left open isn't a disaster if nobody |
1533 | | * attempts to read from it (mingw compat headers do not |
1534 | | * have SHUT_RD)... |
1535 | | * |
1536 | | * We can't fully close the socket since otherwise gtp |
1537 | | * task would first close the socket it sends data to |
1538 | | * while closing the ptg file descriptors. |
1539 | | */ |
1540 | | if (!t->src_is_sock) |
1541 | | close(t->src); |
1542 | | if (t->dest_is_sock) |
1543 | | shutdown(t->dest, SHUT_WR); |
1544 | | else |
1545 | | close(t->dest); |
1546 | | } |
1547 | | |
1548 | | /* |
1549 | | * Join process, with appropriate errors on failure. Name is name for the |
1550 | | * process (for error messages). Returns 0 on success, 1 on failure. |
1551 | | */ |
1552 | | static int tloop_join(pid_t pid, const char *name) |
1553 | | { |
1554 | | int tret; |
1555 | | if (waitpid(pid, &tret, 0) < 0) { |
1556 | | error_errno(_("%s process failed to wait"), name); |
1557 | | return 1; |
1558 | | } |
1559 | | if (!WIFEXITED(tret) || WEXITSTATUS(tret)) { |
1560 | | error(_("%s process failed"), name); |
1561 | | return 1; |
1562 | | } |
1563 | | return 0; |
1564 | | } |
1565 | | |
1566 | | /* |
1567 | | * Spawn the transfer tasks and then wait for them. Returns 0 on success, |
1568 | | * -1 on failure. |
1569 | | */ |
1570 | | static int tloop_spawnwait_tasks(struct bidirectional_transfer_state *s) |
1571 | | { |
1572 | | pid_t pid1, pid2; |
1573 | | int ret = 0; |
1574 | | |
1575 | | /* Fork thread #1: git to program. */ |
1576 | | pid1 = fork(); |
1577 | | if (pid1 < 0) |
1578 | | die_errno(_("can't start thread for copying data")); |
1579 | | else if (pid1 == 0) { |
1580 | | udt_kill_transfer(&s->ptg); |
1581 | | exit(udt_copy_task_routine(&s->gtp) ? 0 : 1); |
1582 | | } |
1583 | | |
1584 | | /* Fork thread #2: program to git. */ |
1585 | | pid2 = fork(); |
1586 | | if (pid2 < 0) |
1587 | | die_errno(_("can't start thread for copying data")); |
1588 | | else if (pid2 == 0) { |
1589 | | udt_kill_transfer(&s->gtp); |
1590 | | exit(udt_copy_task_routine(&s->ptg) ? 0 : 1); |
1591 | | } |
1592 | | |
1593 | | /* |
1594 | | * Close both streams in parent as to not interfere with |
1595 | | * end of file detection and wait for both tasks to finish. |
1596 | | */ |
1597 | | udt_kill_transfer(&s->gtp); |
1598 | | udt_kill_transfer(&s->ptg); |
1599 | | ret |= tloop_join(pid1, "Git to program copy"); |
1600 | | ret |= tloop_join(pid2, "Program to git copy"); |
1601 | | return ret; |
1602 | | } |
1603 | | #endif |
1604 | | |
1605 | | /* |
1606 | | * Copies data from stdin to output and from input to stdout simultaneously. |
1607 | | * Additionally filtering through given filter. If filter is NULL, uses |
1608 | | * identity filter. |
1609 | | */ |
1610 | | int bidirectional_transfer_loop(int input, int output) |
1611 | 0 | { |
1612 | 0 | struct bidirectional_transfer_state state; |
1613 | | |
1614 | | /* Fill the state fields. */ |
1615 | 0 | state.ptg.src = input; |
1616 | 0 | state.ptg.dest = 1; |
1617 | 0 | state.ptg.src_is_sock = (input == output); |
1618 | 0 | state.ptg.dest_is_sock = 0; |
1619 | 0 | state.ptg.state = SSTATE_TRANSFERRING; |
1620 | 0 | state.ptg.bufuse = 0; |
1621 | 0 | state.ptg.src_name = "remote input"; |
1622 | 0 | state.ptg.dest_name = "stdout"; |
1623 | |
|
1624 | 0 | state.gtp.src = 0; |
1625 | 0 | state.gtp.dest = output; |
1626 | 0 | state.gtp.src_is_sock = 0; |
1627 | 0 | state.gtp.dest_is_sock = (input == output); |
1628 | 0 | state.gtp.state = SSTATE_TRANSFERRING; |
1629 | 0 | state.gtp.bufuse = 0; |
1630 | 0 | state.gtp.src_name = "stdin"; |
1631 | 0 | state.gtp.dest_name = "remote output"; |
1632 | |
|
1633 | 0 | return tloop_spawnwait_tasks(&state); |
1634 | 0 | } |
1635 | | |
1636 | | void reject_atomic_push(struct ref *remote_refs, int mirror_mode) |
1637 | 0 | { |
1638 | 0 | struct ref *ref; |
1639 | | |
1640 | | /* Mark other refs as failed */ |
1641 | 0 | for (ref = remote_refs; ref; ref = ref->next) { |
1642 | 0 | if (!ref->peer_ref && !mirror_mode) |
1643 | 0 | continue; |
1644 | | |
1645 | 0 | switch (ref->status) { |
1646 | 0 | case REF_STATUS_NONE: |
1647 | 0 | case REF_STATUS_OK: |
1648 | 0 | case REF_STATUS_EXPECTING_REPORT: |
1649 | 0 | ref->status = REF_STATUS_ATOMIC_PUSH_FAILED; |
1650 | 0 | continue; |
1651 | 0 | default: |
1652 | 0 | break; /* do nothing */ |
1653 | 0 | } |
1654 | 0 | } |
1655 | 0 | return; |
1656 | 0 | } |