diff options
Diffstat (limited to 'transport.c')
-rw-r--r-- | transport.c | 472 |
1 files changed, 332 insertions, 140 deletions
diff --git a/transport.c b/transport.c index 095e61f0ad..00d48b5b56 100644 --- a/transport.c +++ b/transport.c @@ -1,4 +1,5 @@ #include "cache.h" +#include "config.h" #include "transport.h" #include "run-command.h" #include "pkt-line.h" @@ -16,6 +17,7 @@ #include "string-list.h" #include "sha1-array.h" #include "sigchain.h" +#include "transport-internal.h" static void set_upstreams(struct transport *transport, struct ref *refs, int pretend) @@ -25,7 +27,6 @@ static void set_upstreams(struct transport *transport, struct ref *refs, const char *localname; const char *tmp; const char *remotename; - unsigned char sha[20]; int flag = 0; /* * Check suitability for tracking. Must be successful / @@ -43,7 +44,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs, localname = ref->peer_ref->name; remotename = ref->name; tmp = resolve_ref_unsafe(localname, RESOLVE_REF_READING, - sha, &flag); + NULL, &flag); if (tmp && flag & REF_ISSYMREF && starts_with(tmp, "refs/heads/")) localname = tmp; @@ -59,7 +60,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs, localname + 11, transport->remote->name, remotename); else - printf("Would set upstream of '%s' to '%s' of '%s'\n", + printf(_("Would set upstream of '%s' to '%s' of '%s'\n"), localname + 11, remotename + 11, transport->remote->name); } @@ -87,7 +88,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, int for_pus for (i = 0; i < data->header.references.nr; i++) { struct ref_list_entry *e = data->header.references.list + i; struct ref *ref = alloc_ref(e->name); - hashcpy(ref->old_oid.hash, e->sha1); + oidcpy(&ref->old_oid, &e->oid); ref->next = result; result = ref; } @@ -116,8 +117,8 @@ struct git_transport_data { struct child_process *conn; int fd[2]; unsigned got_remote_heads : 1; - struct sha1_array extra_have; - struct sha1_array shallow; + struct oid_array extra_have; + struct oid_array shallow; }; static int set_git_option(struct git_transport_options *opts, @@ -148,9 +149,27 @@ static int set_git_option(struct git_transport_options *opts, char *end; opts->depth = strtol(value, &end, 0); if (*end) - die("transport: invalid depth option '%s'", value); + die(_("transport: invalid depth option '%s'"), value); } return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_SINCE)) { + opts->deepen_since = value; + return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_NOT)) { + opts->deepen_not = (const struct string_list *)value; + return 0; + } else if (!strcmp(name, TRANS_OPT_DEEPEN_RELATIVE)) { + opts->deepen_relative = !!value; + return 0; + } else if (!strcmp(name, TRANS_OPT_FROM_PROMISOR)) { + opts->from_promisor = !!value; + return 0; + } else if (!strcmp(name, TRANS_OPT_NO_DEPENDENTS)) { + opts->no_dependents = !!value; + return 0; + } else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) { + parse_list_objects_filter(&opts->filter_options, value); + return 0; } return 1; } @@ -195,6 +214,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus static int fetch_refs_via_pack(struct transport *transport, int nr_heads, struct ref **to_fetch) { + int ret = 0; struct git_transport_data *data = transport->data; struct ref *refs; char *dest = xstrdup(transport->url); @@ -211,10 +231,16 @@ static int fetch_refs_via_pack(struct transport *transport, args.quiet = (transport->verbose < 0); args.no_progress = !transport->progress; args.depth = data->options.depth; + args.deepen_since = data->options.deepen_since; + args.deepen_not = data->options.deepen_not; + args.deepen_relative = data->options.deepen_relative; args.check_self_contained_and_connected = data->options.check_self_contained_and_connected; args.cloning = transport->cloning; args.update_shallow = data->options.update_shallow; + args.from_promisor = data->options.from_promisor; + args.no_dependents = data->options.no_dependents; + args.filter_options = data->options.filter_options; if (!data->got_remote_heads) { connect_setup(transport, 0); @@ -229,19 +255,22 @@ static int fetch_refs_via_pack(struct transport *transport, &transport->pack_lockfile); close(data->fd[0]); close(data->fd[1]); - if (finish_connect(data->conn)) { - free_refs(refs); - refs = NULL; - } + if (finish_connect(data->conn)) + ret = -1; data->conn = NULL; data->got_remote_heads = 0; data->options.self_contained_and_connected = args.self_contained_and_connected; + if (refs == NULL) + ret = -1; + if (report_unmatched_refs(to_fetch, nr_heads)) + ret = -1; + free_refs(refs_tmp); free_refs(refs); free(dest); - return (refs ? 0 : -1); + return ret; } static int push_had_errors(struct ref *ref) @@ -287,15 +316,17 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v if (verbose) fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst); if (ref->deletion) { - delete_ref(rs.dst, NULL, 0); + delete_ref(NULL, rs.dst, NULL, 0); } else - update_ref("update by push", rs.dst, - ref->new_oid.hash, NULL, 0, 0); + update_ref("update by push", rs.dst, &ref->new_oid, + NULL, 0, 0); free(rs.dst); } } -static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain) +static void print_ref_status(char flag, const char *summary, + struct ref *to, struct ref *from, const char *msg, + int porcelain, int summary_width) { if (porcelain) { if (from) @@ -307,7 +338,7 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str else fprintf(stdout, "%s\n", summary); } else { - fprintf(stderr, " %c %-*s ", flag, TRANSPORT_SUMMARY_WIDTH, summary); + fprintf(stderr, " %c %-*s ", flag, summary_width, summary); if (from) fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name)); else @@ -321,26 +352,23 @@ static void print_ref_status(char flag, const char *summary, struct ref *to, str } } -static const char *status_abbrev(unsigned char sha1[20]) -{ - return find_unique_abbrev(sha1, DEFAULT_ABBREV); -} - -static void print_ok_ref_status(struct ref *ref, int porcelain) +static void print_ok_ref_status(struct ref *ref, int porcelain, int summary_width) { if (ref->deletion) - print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain); + print_ref_status('-', "[deleted]", ref, NULL, NULL, + porcelain, summary_width); else if (is_null_oid(&ref->old_oid)) print_ref_status('*', (starts_with(ref->name, "refs/tags/") ? "[new tag]" : "[new branch]"), - ref, ref->peer_ref, NULL, porcelain); + ref, ref->peer_ref, NULL, porcelain, summary_width); else { struct strbuf quickref = STRBUF_INIT; char type; const char *msg; - strbuf_addstr(&quickref, status_abbrev(ref->old_oid.hash)); + strbuf_add_unique_abbrev(&quickref, ref->old_oid.hash, + DEFAULT_ABBREV); if (ref->forced_update) { strbuf_addstr(&quickref, "..."); type = '+'; @@ -350,102 +378,136 @@ static void print_ok_ref_status(struct ref *ref, int porcelain) type = ' '; msg = NULL; } - strbuf_addstr(&quickref, status_abbrev(ref->new_oid.hash)); + strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, + DEFAULT_ABBREV); - print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain); + print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, + porcelain, summary_width); strbuf_release(&quickref); } } -static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain) +static int print_one_push_status(struct ref *ref, const char *dest, int count, + int porcelain, int summary_width) { - if (!count) - fprintf(porcelain ? stdout : stderr, "To %s\n", dest); + if (!count) { + char *url = transport_anonymize_url(dest); + fprintf(porcelain ? stdout : stderr, "To %s\n", url); + free(url); + } switch(ref->status) { case REF_STATUS_NONE: - print_ref_status('X', "[no match]", ref, NULL, NULL, porcelain); + print_ref_status('X', "[no match]", ref, NULL, NULL, + porcelain, summary_width); break; case REF_STATUS_REJECT_NODELETE: print_ref_status('!', "[rejected]", ref, NULL, - "remote does not support deleting refs", porcelain); + "remote does not support deleting refs", + porcelain, summary_width); break; case REF_STATUS_UPTODATE: print_ref_status('=', "[up to date]", ref, - ref->peer_ref, NULL, porcelain); + ref->peer_ref, NULL, porcelain, summary_width); break; case REF_STATUS_REJECT_NONFASTFORWARD: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "non-fast-forward", porcelain); + "non-fast-forward", porcelain, summary_width); break; case REF_STATUS_REJECT_ALREADY_EXISTS: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "already exists", porcelain); + "already exists", porcelain, summary_width); break; case REF_STATUS_REJECT_FETCH_FIRST: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "fetch first", porcelain); + "fetch first", porcelain, summary_width); break; case REF_STATUS_REJECT_NEEDS_FORCE: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "needs force", porcelain); + "needs force", porcelain, summary_width); break; case REF_STATUS_REJECT_STALE: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "stale info", porcelain); + "stale info", porcelain, summary_width); break; case REF_STATUS_REJECT_SHALLOW: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "new shallow roots not allowed", porcelain); + "new shallow roots not allowed", + porcelain, summary_width); break; case REF_STATUS_REMOTE_REJECT: print_ref_status('!', "[remote rejected]", ref, - ref->deletion ? NULL : ref->peer_ref, - ref->remote_status, porcelain); + ref->deletion ? NULL : ref->peer_ref, + ref->remote_status, porcelain, summary_width); break; case REF_STATUS_EXPECTING_REPORT: print_ref_status('!', "[remote failure]", ref, - ref->deletion ? NULL : ref->peer_ref, - "remote failed to report status", porcelain); + ref->deletion ? NULL : ref->peer_ref, + "remote failed to report status", + porcelain, summary_width); break; case REF_STATUS_ATOMIC_PUSH_FAILED: print_ref_status('!', "[rejected]", ref, ref->peer_ref, - "atomic push failed", porcelain); + "atomic push failed", porcelain, summary_width); break; case REF_STATUS_OK: - print_ok_ref_status(ref, porcelain); + print_ok_ref_status(ref, porcelain, summary_width); break; } return 1; } +static int measure_abbrev(const struct object_id *oid, int sofar) +{ + char hex[GIT_MAX_HEXSZ + 1]; + int w = find_unique_abbrev_r(hex, oid->hash, DEFAULT_ABBREV); + + return (w < sofar) ? sofar : w; +} + +int transport_summary_width(const struct ref *refs) +{ + int maxw = -1; + + for (; refs; refs = refs->next) { + maxw = measure_abbrev(&refs->old_oid, maxw); + maxw = measure_abbrev(&refs->new_oid, maxw); + } + if (maxw < 0) + maxw = FALLBACK_DEFAULT_ABBREV; + return (2 * maxw + 3); +} + void transport_print_push_status(const char *dest, struct ref *refs, int verbose, int porcelain, unsigned int *reject_reasons) { struct ref *ref; int n = 0; - unsigned char head_sha1[20]; char *head; + int summary_width = transport_summary_width(refs); - head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL); + head = resolve_refdup("HEAD", RESOLVE_REF_READING, NULL, NULL); if (verbose) { for (ref = refs; ref; ref = ref->next) if (ref->status == REF_STATUS_UPTODATE) - n += print_one_push_status(ref, dest, n, porcelain); + n += print_one_push_status(ref, dest, n, + porcelain, summary_width); } for (ref = refs; ref; ref = ref->next) if (ref->status == REF_STATUS_OK) - n += print_one_push_status(ref, dest, n, porcelain); + n += print_one_push_status(ref, dest, n, + porcelain, summary_width); *reject_reasons = 0; for (ref = refs; ref; ref = ref->next) { if (ref->status != REF_STATUS_NONE && ref->status != REF_STATUS_UPTODATE && ref->status != REF_STATUS_OK) - n += print_one_push_status(ref, dest, n, porcelain); + n += print_one_push_status(ref, dest, n, + porcelain, summary_width); if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD) { if (head != NULL && !strcmp(head, ref->name)) *reject_reasons |= REJECT_NON_FF_HEAD; @@ -510,6 +572,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN); args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN); args.atomic = !!(flags & TRANSPORT_PUSH_ATOMIC); + args.push_options = transport->push_options; args.url = transport->url; if (flags & TRANSPORT_PUSH_CERT_ALWAYS) @@ -557,13 +620,22 @@ static int disconnect_git(struct transport *transport) return 0; } +static struct transport_vtable taken_over_vtable = { + NULL, + get_refs_via_connect, + fetch_refs_via_pack, + git_transport_push, + NULL, + disconnect_git +}; + void transport_take_over(struct transport *transport, struct child_process *child) { struct git_transport_data *data; if (!transport->smart_options) - die("Bug detected: Taking over transport requires non-NULL " + die("BUG: taking over transport requires non-NULL " "smart_options field."); data = xcalloc(1, sizeof(*data)); @@ -574,12 +646,7 @@ void transport_take_over(struct transport *transport, data->got_remote_heads = 0; transport->data = data; - transport->set_option = NULL; - transport->get_refs_list = get_refs_via_connect; - transport->fetch = fetch_refs_via_pack; - transport->push = NULL; - transport->push_refs = git_transport_push; - transport->disconnect = disconnect_git; + transport->vtable = &taken_over_vtable; transport->smart_options = &(data->options); transport->cannot_reuse = 1; @@ -617,23 +684,109 @@ static const struct string_list *protocol_whitelist(void) return enabled ? &allowed : NULL; } -int is_transport_allowed(const char *type) +enum protocol_allow_config { + PROTOCOL_ALLOW_NEVER = 0, + PROTOCOL_ALLOW_USER_ONLY, + PROTOCOL_ALLOW_ALWAYS +}; + +static enum protocol_allow_config parse_protocol_config(const char *key, + const char *value) { - const struct string_list *allowed = protocol_whitelist(); - return !allowed || string_list_has_string(allowed, type); + if (!strcasecmp(value, "always")) + return PROTOCOL_ALLOW_ALWAYS; + else if (!strcasecmp(value, "never")) + return PROTOCOL_ALLOW_NEVER; + else if (!strcasecmp(value, "user")) + return PROTOCOL_ALLOW_USER_ONLY; + + die("unknown value for config '%s': %s", key, value); } -void transport_check_allowed(const char *type) +static enum protocol_allow_config get_protocol_config(const char *type) { - if (!is_transport_allowed(type)) - die("transport '%s' not allowed", type); + char *key = xstrfmt("protocol.%s.allow", type); + char *value; + + /* first check the per-protocol config */ + if (!git_config_get_string(key, &value)) { + enum protocol_allow_config ret = + parse_protocol_config(key, value); + free(key); + free(value); + return ret; + } + free(key); + + /* if defined, fallback to user-defined default for unknown protocols */ + if (!git_config_get_string("protocol.allow", &value)) { + enum protocol_allow_config ret = + parse_protocol_config("protocol.allow", value); + free(value); + return ret; + } + + /* fallback to built-in defaults */ + /* known safe */ + if (!strcmp(type, "http") || + !strcmp(type, "https") || + !strcmp(type, "git") || + !strcmp(type, "ssh") || + !strcmp(type, "file")) + return PROTOCOL_ALLOW_ALWAYS; + + /* known scary; err on the side of caution */ + if (!strcmp(type, "ext")) + return PROTOCOL_ALLOW_NEVER; + + /* unknown; by default let them be used only directly by the user */ + return PROTOCOL_ALLOW_USER_ONLY; } -int transport_restrict_protocols(void) +int is_transport_allowed(const char *type, int from_user) { - return !!protocol_whitelist(); + const struct string_list *whitelist = protocol_whitelist(); + if (whitelist) + return string_list_has_string(whitelist, type); + + switch (get_protocol_config(type)) { + case PROTOCOL_ALLOW_ALWAYS: + return 1; + case PROTOCOL_ALLOW_NEVER: + return 0; + case PROTOCOL_ALLOW_USER_ONLY: + if (from_user < 0) + from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1); + return from_user; + } + + die("BUG: invalid protocol_allow_config type"); } +void transport_check_allowed(const char *type) +{ + if (!is_transport_allowed(type, -1)) + die("transport '%s' not allowed", type); +} + +static struct transport_vtable bundle_vtable = { + NULL, + get_refs_from_bundle, + fetch_refs_from_bundle, + NULL, + NULL, + close_bundle +}; + +static struct transport_vtable builtin_smart_vtable = { + NULL, + get_refs_via_connect, + fetch_refs_via_pack, + git_transport_push, + connect_git, + disconnect_git +}; + struct transport *transport_get(struct remote *remote, const char *url) { const char *helper; @@ -670,9 +823,7 @@ struct transport *transport_get(struct remote *remote, const char *url) struct bundle_transport_data *data = xcalloc(1, sizeof(*data)); transport_check_allowed("file"); ret->data = data; - ret->get_refs_list = get_refs_from_bundle; - ret->fetch = fetch_refs_from_bundle; - ret->disconnect = close_bundle; + ret->vtable = &bundle_vtable; ret->smart_options = NULL; } else if (!is_url(url) || starts_with(url, "file://") @@ -687,12 +838,7 @@ struct transport *transport_get(struct remote *remote, const char *url) */ struct git_transport_data *data = xcalloc(1, sizeof(*data)); ret->data = data; - ret->set_option = NULL; - ret->get_refs_list = get_refs_via_connect; - ret->fetch = fetch_refs_via_pack; - ret->push_refs = git_transport_push; - ret->connect = connect_git; - ret->disconnect = disconnect_git; + ret->vtable = &builtin_smart_vtable; ret->smart_options = &(data->options); data->conn = NULL; @@ -726,9 +872,9 @@ int transport_set_option(struct transport *transport, git_reports = set_git_option(transport->smart_options, name, value); - if (transport->set_option) - protocol_reports = transport->set_option(transport, name, - value); + if (transport->vtable->set_option) + protocol_reports = transport->vtable->set_option(transport, + name, value); /* If either report is 0, report 0 (success). */ if (!git_reports || !protocol_reports) @@ -767,19 +913,19 @@ static void die_with_unpushed_submodules(struct string_list *needs_pushing) { int i; - fprintf(stderr, "The following submodule paths contain changes that can\n" - "not be found on any remote:\n"); + fprintf(stderr, _("The following submodule paths contain changes that can\n" + "not be found on any remote:\n")); for (i = 0; i < needs_pushing->nr; i++) - printf(" %s\n", needs_pushing->items[i].string); - fprintf(stderr, "\nPlease try\n\n" - " git push --recurse-submodules=on-demand\n\n" - "or cd to the path and use\n\n" - " git push\n\n" - "to push them to a remote.\n\n"); + fprintf(stderr, " %s\n", needs_pushing->items[i].string); + fprintf(stderr, _("\nPlease try\n\n" + " git push --recurse-submodules=on-demand\n\n" + "or cd to the path and use\n\n" + " git push\n\n" + "to push them to a remote.\n\n")); string_list_clear(needs_pushing, 0); - die("Aborting."); + die(_("Aborting.")); } static int run_pre_push_hook(struct transport *transport, @@ -851,13 +997,7 @@ int transport_push(struct transport *transport, *reject_reasons = 0; transport_verify_remote_names(refspec_nr, refspec); - if (transport->push) { - /* Maybe FIXME. But no important transport uses this case. */ - if (flags & TRANSPORT_PUSH_SET_UPSTREAM) - die("This transport does not support using --set-upstream"); - - return transport->push(transport, refspec_nr, refspec, flags); - } else if (transport->push_refs) { + if (transport->vtable->push_refs) { struct ref *remote_refs; struct ref *local_refs = get_local_heads(); int match_flags = MATCH_REFS_NONE; @@ -870,7 +1010,7 @@ int transport_push(struct transport *transport, if (check_push_refs(local_refs, refspec_nr, refspec) < 0) return -1; - remote_refs = transport->get_refs_list(transport, 1); + remote_refs = transport->vtable->get_refs_list(transport, 1); if (flags & TRANSPORT_PUSH_ALL) match_flags |= MATCH_REFS_ALL; @@ -900,28 +1040,54 @@ int transport_push(struct transport *transport, if (run_pre_push_hook(transport, remote_refs)) return -1; - if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) { + if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND | + TRANSPORT_RECURSE_SUBMODULES_ONLY)) && + !is_bare_repository()) { struct ref *ref = remote_refs; + struct oid_array commits = OID_ARRAY_INIT; + for (; ref; ref = ref->next) - if (!is_null_oid(&ref->new_oid) && - !push_unpushed_submodules(ref->new_oid.hash, - transport->remote->name)) - die ("Failed to push all needed submodules!"); + if (!is_null_oid(&ref->new_oid)) + oid_array_append(&commits, + &ref->new_oid); + + if (!push_unpushed_submodules(&commits, + transport->remote, + refspec, refspec_nr, + transport->push_options, + pretend)) { + oid_array_clear(&commits); + die("Failed to push all needed submodules!"); + } + oid_array_clear(&commits); } - if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND | - TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) { + if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) || + ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND | + TRANSPORT_RECURSE_SUBMODULES_ONLY)) && + !pretend)) && !is_bare_repository()) { struct ref *ref = remote_refs; struct string_list needs_pushing = STRING_LIST_INIT_DUP; + struct oid_array commits = OID_ARRAY_INIT; for (; ref; ref = ref->next) - if (!is_null_oid(&ref->new_oid) && - find_unpushed_submodules(ref->new_oid.hash, - transport->remote->name, &needs_pushing)) - die_with_unpushed_submodules(&needs_pushing); + if (!is_null_oid(&ref->new_oid)) + oid_array_append(&commits, + &ref->new_oid); + + if (find_unpushed_submodules(&commits, transport->remote->name, + &needs_pushing)) { + oid_array_clear(&commits); + die_with_unpushed_submodules(&needs_pushing); + } + string_list_clear(&needs_pushing, 0); + oid_array_clear(&commits); } - push_ret = transport->push_refs(transport, remote_refs, flags); + if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY)) + push_ret = transport->vtable->push_refs(transport, remote_refs, flags); + else + push_ret = 0; err = push_had_errors(remote_refs); ret = push_ret | err; @@ -933,7 +1099,8 @@ int transport_push(struct transport *transport, if (flags & TRANSPORT_PUSH_SET_UPSTREAM) set_upstreams(transport, remote_refs, pretend); - if (!(flags & TRANSPORT_PUSH_DRY_RUN)) { + if (!(flags & (TRANSPORT_PUSH_DRY_RUN | + TRANSPORT_RECURSE_SUBMODULES_ONLY))) { struct ref *ref; for (ref = remote_refs; ref; ref = ref->next) transport_update_tracking_ref(transport->remote, ref, verbose); @@ -952,7 +1119,7 @@ int transport_push(struct transport *transport, const struct ref *transport_get_remote_refs(struct transport *transport) { if (!transport->got_remote_refs) { - transport->remote_refs = transport->get_refs_list(transport, 0); + transport->remote_refs = transport->vtable->get_refs_list(transport, 0); transport->got_remote_refs = 1; } @@ -989,7 +1156,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs) heads[nr_heads++] = rm; } - rc = transport->fetch(transport, nr_heads, heads); + rc = transport->vtable->fetch(transport, nr_heads, heads); free(heads); return rc; @@ -999,16 +1166,15 @@ void transport_unlock_pack(struct transport *transport) { if (transport->pack_lockfile) { unlink_or_warn(transport->pack_lockfile); - free(transport->pack_lockfile); - transport->pack_lockfile = NULL; + FREE_AND_NULL(transport->pack_lockfile); } } int transport_connect(struct transport *transport, const char *name, const char *exec, int fd[2]) { - if (transport->connect) - return transport->connect(transport, name, exec, fd); + if (transport->vtable->connect) + return transport->vtable->connect(transport, name, exec, fd); else die("Operation not supported by protocol"); } @@ -1016,8 +1182,8 @@ int transport_connect(struct transport *transport, const char *name, int transport_disconnect(struct transport *transport) { int ret = 0; - if (transport->disconnect) - ret = transport->disconnect(transport); + if (transport->vtable->disconnect) + ret = transport->vtable->disconnect(transport); free(transport); return ret; } @@ -1068,6 +1234,42 @@ literal_copy: return xstrdup(url); } +static void read_alternate_refs(const char *path, + alternate_ref_fn *cb, + void *data) +{ + struct child_process cmd = CHILD_PROCESS_INIT; + struct strbuf line = STRBUF_INIT; + FILE *fh; + + cmd.git_cmd = 1; + argv_array_pushf(&cmd.args, "--git-dir=%s", path); + argv_array_push(&cmd.args, "for-each-ref"); + argv_array_push(&cmd.args, "--format=%(objectname) %(refname)"); + cmd.env = local_repo_env; + cmd.out = -1; + + if (start_command(&cmd)) + return; + + fh = xfdopen(cmd.out, "r"); + while (strbuf_getline_lf(&line, fh) != EOF) { + struct object_id oid; + + if (get_oid_hex(line.buf, &oid) || + line.buf[GIT_SHA1_HEXSZ] != ' ') { + warning("invalid line while parsing alternate refs: %s", + line.buf); + break; + } + + cb(line.buf + GIT_SHA1_HEXSZ + 1, &oid, data); + } + + fclose(fh); + finish_command(&cmd); +} + struct alternate_refs_data { alternate_ref_fn *fn; void *data; @@ -1076,36 +1278,26 @@ struct alternate_refs_data { static int refs_from_alternate_cb(struct alternate_object_database *e, void *data) { - char *other; - size_t len; - struct remote *remote; - struct transport *transport; - const struct ref *extra; + struct strbuf path = STRBUF_INIT; + size_t base_len; struct alternate_refs_data *cb = data; - e->name[-1] = '\0'; - other = xstrdup(real_path(e->base)); - e->name[-1] = '/'; - len = strlen(other); - - while (other[len-1] == '/') - other[--len] = '\0'; - if (len < 8 || memcmp(other + len - 8, "/objects", 8)) + if (!strbuf_realpath(&path, e->path, 0)) goto out; + if (!strbuf_strip_suffix(&path, "/objects")) + goto out; + base_len = path.len; + /* Is this a git repository with refs? */ - memcpy(other + len - 8, "/refs", 6); - if (!is_directory(other)) + strbuf_addstr(&path, "/refs"); + if (!is_directory(path.buf)) goto out; - other[len - 8] = '\0'; - remote = remote_get(other); - transport = transport_get(remote, other); - for (extra = transport_get_remote_refs(transport); - extra; - extra = extra->next) - cb->fn(extra, cb->data); - transport_disconnect(transport); + strbuf_setlen(&path, base_len); + + read_alternate_refs(path.buf, cb->fn, cb->data); + out: - free(other); + strbuf_release(&path); return 0; } |