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