#include "cache.h" #include "transport.h" #include "run-command.h" #include "commit.h" #include "diff.h" #include "revision.h" #include "remote.h" struct helper_data { const char *name; struct child_process *helper; unsigned fetch : 1; unsigned import : 1; /* These go from remote name (as in "list") to private name */ struct refspec *refspecs; int refspec_nr; }; static struct child_process *get_helper(struct transport *transport) { struct helper_data *data = transport->data; struct strbuf buf = STRBUF_INIT; struct child_process *helper; FILE *file; const char **refspecs = NULL; int refspec_nr = 0; int refspec_alloc = 0; if (data->helper) return data->helper; helper = xcalloc(1, sizeof(*helper)); helper->in = -1; helper->out = -1; helper->err = 0; helper->argv = xcalloc(4, sizeof(*helper->argv)); strbuf_addf(&buf, "remote-%s", data->name); helper->argv[0] = strbuf_detach(&buf, NULL); helper->argv[1] = transport->remote->name; helper->argv[2] = transport->url; helper->git_cmd = 1; if (start_command(helper)) die("Unable to run helper: git %s", helper->argv[0]); data->helper = helper; write_str_in_full(helper->in, "capabilities\n"); file = xfdopen(helper->out, "r"); while (1) { if (strbuf_getline(&buf, file, '\n') == EOF) exit(128); /* child died, message supplied already */ if (!*buf.buf) break; if (!strcmp(buf.buf, "fetch")) data->fetch = 1; if (!strcmp(buf.buf, "import")) data->import = 1; if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) { ALLOC_GROW(refspecs, refspec_nr + 1, refspec_alloc); refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec ")); } } if (refspecs) { int i; data->refspec_nr = refspec_nr; data->refspecs = parse_fetch_refspec(refspec_nr, refspecs); for (i = 0; i < refspec_nr; i++) { free((char *)refspecs[i]); } free(refspecs); } strbuf_release(&buf); return data->helper; } static int disconnect_helper(struct transport *transport) { struct helper_data *data = transport->data; if (data->helper) { write_str_in_full(data->helper->in, "\n"); close(data->helper->in); finish_command(data->helper); free((char *)data->helper->argv[0]); free(data->helper->argv); free(data->helper); data->helper = NULL; } return 0; } static int release_helper(struct transport *transport) { struct helper_data *data = transport->data; free_refspec(data->refspec_nr, data->refspecs); data->refspecs = NULL; disconnect_helper(transport); free(transport->data); return 0; } static int fetch_with_fetch(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct child_process *helper = get_helper(transport); FILE *file = xfdopen(helper->out, "r"); int i; struct strbuf buf = STRBUF_INIT; for (i = 0; i < nr_heads; i++) { const struct ref *posn = to_fetch[i]; if (posn->status & REF_STATUS_UPTODATE) continue; strbuf_addf(&buf, "fetch %s %s\n", sha1_to_hex(posn->old_sha1), posn->name); write_in_full(helper->in, buf.buf, buf.len); strbuf_reset(&buf); if (strbuf_getline(&buf, file, '\n') == EOF) exit(128); /* child died, message supplied already */ } return 0; } static int get_importer(struct transport *transport, struct child_process *fastimport) { struct child_process *helper = get_helper(transport); memset(fastimport, 0, sizeof(*fastimport)); fastimport->in = helper->out; fastimport->argv = xcalloc(5, sizeof(*fastimport->argv)); fastimport->argv[0] = "fast-import"; fastimport->argv[1] = "--quiet"; fastimport->git_cmd = 1; return start_command(fastimport); } static int fetch_with_import(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct child_process fastimport; struct child_process *helper = get_helper(transport); struct helper_data *data = transport->data; int i; struct ref *posn; struct strbuf buf = STRBUF_INIT; if (get_importer(transport, &fastimport)) die("Couldn't run fast-import"); for (i = 0; i < nr_heads; i++) { posn = to_fetch[i]; if (posn->status & REF_STATUS_UPTODATE) continue; strbuf_addf(&buf, "import %s\n", posn->name); write_in_full(helper->in, buf.buf, buf.len); strbuf_reset(&buf); } disconnect_helper(transport); finish_command(&fastimport); free(fastimport.argv); fastimport.argv = NULL; for (i = 0; i < nr_heads; i++) { char *private; posn = to_fetch[i]; if (posn->status & REF_STATUS_UPTODATE) continue; if (data->refspecs) private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name); else private = strdup(posn->name); read_ref(private, posn->old_sha1); free(private); } strbuf_release(&buf); return 0; } static int fetch(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct helper_data *data = transport->data; int i, count; count = 0; for (i = 0; i < nr_heads; i++) if (!(to_fetch[i]->status & REF_STATUS_UPTODATE)) count++; if (!count) return 0; if (data->fetch) return fetch_with_fetch(transport, nr_heads, to_fetch); if (data->import) return fetch_with_import(transport, nr_heads, to_fetch); return -1; } static struct ref *get_refs_list(struct transport *transport, int for_push) { struct child_process *helper; struct ref *ret = NULL; struct ref **tail = &ret; struct ref *posn; struct strbuf buf = STRBUF_INIT; FILE *file; helper = get_helper(transport); write_str_in_full(helper->in, "list\n"); file = xfdopen(helper->out, "r"); while (1) { char *eov, *eon; if (strbuf_getline(&buf, file, '\n') == EOF) exit(128); /* child died, message supplied already */ if (!*buf.buf) break; eov = strchr(buf.buf, ' '); if (!eov) die("Malformed response in ref list: %s", buf.buf); eon = strchr(eov + 1, ' '); *eov = '\0'; if (eon) *eon = '\0'; *tail = alloc_ref(eov + 1); if (buf.buf[0] == '@') (*tail)->symref = xstrdup(buf.buf + 1); else if (buf.buf[0] != '?') get_sha1_hex(buf.buf, (*tail)->old_sha1); tail = &((*tail)->next); } strbuf_release(&buf); for (posn = ret; posn; posn = posn->next) resolve_remote_symref(posn, ret); return ret; } int transport_helper_init(struct transport *transport, const char *name) { struct helper_data *data = xcalloc(sizeof(*data), 1); data->name = name; transport->data = data; transport->get_refs_list = get_refs_list; transport->fetch = fetch; transport->disconnect = release_helper; return 0; }