diff options
Diffstat (limited to 'transport.c')
-rw-r--r-- | transport.c | 192 |
1 files changed, 93 insertions, 99 deletions
diff --git a/transport.c b/transport.c index 06ffea2774..15f5ba4e8f 100644 --- a/transport.c +++ b/transport.c @@ -16,7 +16,7 @@ #include "url.h" #include "submodule.h" #include "string-list.h" -#include "sha1-array.h" +#include "oid-array.h" #include "sigchain.h" #include "transport-internal.h" #include "protocol.h" @@ -122,6 +122,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs, struct bundle_transport_data { int fd; struct bundle_header header; + unsigned get_refs_from_bundle_called : 1; }; static struct ref *get_refs_from_bundle(struct transport *transport, @@ -135,6 +136,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport, if (for_push) return NULL; + data->get_refs_from_bundle_called = 1; + if (data->fd > 0) close(data->fd); data->fd = read_bundle_header(transport->url, &data->header); @@ -154,7 +157,10 @@ static int fetch_refs_from_bundle(struct transport *transport, int nr_heads, struct ref **to_fetch) { struct bundle_transport_data *data = transport->data; - return unbundle(&data->header, data->fd, + + if (!data->get_refs_from_bundle_called) + get_refs_from_bundle(transport, 0, NULL); + return unbundle(the_repository, &data->header, data->fd, transport->progress ? BUNDLE_VERBOSE : 0); } @@ -224,6 +230,7 @@ static int set_git_option(struct git_transport_options *opts, opts->no_dependents = !!value; return 0; } else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) { + list_objects_filter_die_if_populated(&opts->filter_options); parse_list_objects_filter(&opts->filter_options, value); return 0; } @@ -252,8 +259,26 @@ static int connect_setup(struct transport *transport, int for_push) return 0; } -static struct ref *get_refs_via_connect(struct transport *transport, int for_push, - const struct argv_array *ref_prefixes) +static void die_if_server_options(struct transport *transport) +{ + if (!transport->server_options || !transport->server_options->nr) + return; + advise(_("see protocol.version in 'git help config' for more details")); + die(_("server options require protocol version 2 or later")); +} + +/* + * Obtains the protocol version from the transport and writes it to + * transport->data->version, first connecting if not already connected. + * + * If the protocol version is one that allows skipping the listing of remote + * refs, and must_list_refs is 0, the listing of remote refs is skipped and + * this function returns NULL. Otherwise, this function returns the list of + * remote refs. + */ +static struct ref *handshake(struct transport *transport, int for_push, + const struct argv_array *ref_prefixes, + int must_list_refs) { struct git_transport_data *data = transport->data; struct ref *refs = NULL; @@ -263,16 +288,20 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus packet_reader_init(&reader, data->fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE | - PACKET_READ_GENTLE_ON_EOF); + PACKET_READ_GENTLE_ON_EOF | + PACKET_READ_DIE_ON_ERR_PACKET); data->version = discover_version(&reader); switch (data->version) { case protocol_v2: - get_remote_refs(data->fd[1], &reader, &refs, for_push, - ref_prefixes, transport->server_options); + if (must_list_refs) + get_remote_refs(data->fd[1], &reader, &refs, for_push, + ref_prefixes, + transport->server_options); break; case protocol_v1: case protocol_v0: + die_if_server_options(transport); get_remote_heads(&reader, &refs, for_push ? REF_NORMAL : 0, &data->extra_have, @@ -283,16 +312,24 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus } data->got_remote_heads = 1; + if (reader.line_peeked) + BUG("buffer must be empty at the end of handshake()"); + return refs; } +static struct ref *get_refs_via_connect(struct transport *transport, int for_push, + const struct argv_array *ref_prefixes) +{ + return handshake(transport, for_push, ref_prefixes, 1); +} + 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 = NULL; - char *dest = xstrdup(transport->url); struct fetch_pack_args args; struct ref *refs_tmp = NULL; @@ -320,21 +357,31 @@ static int fetch_refs_via_pack(struct transport *transport, args.server_options = transport->server_options; args.negotiation_tips = data->options.negotiation_tips; - if (!data->got_remote_heads) - refs_tmp = get_refs_via_connect(transport, 0, NULL); + if (!data->got_remote_heads) { + int i; + int must_list_refs = 0; + for (i = 0; i < nr_heads; i++) { + if (!to_fetch[i]->exact_oid) { + must_list_refs = 1; + break; + } + } + refs_tmp = handshake(transport, 0, NULL, must_list_refs); + } switch (data->version) { case protocol_v2: - refs = fetch_pack(&args, data->fd, data->conn, + refs = fetch_pack(&args, data->fd, refs_tmp ? refs_tmp : transport->remote_refs, - dest, to_fetch, nr_heads, &data->shallow, + to_fetch, nr_heads, &data->shallow, &transport->pack_lockfile, data->version); break; case protocol_v1: case protocol_v0: - refs = fetch_pack(&args, data->fd, data->conn, + die_if_server_options(transport); + refs = fetch_pack(&args, data->fd, refs_tmp ? refs_tmp : transport->remote_refs, - dest, to_fetch, nr_heads, &data->shallow, + to_fetch, nr_heads, &data->shallow, &transport->pack_lockfile, data->version); break; case protocol_unknown_version: @@ -358,7 +405,6 @@ static int fetch_refs_via_pack(struct transport *transport, free_refs(refs_tmp); free_refs(refs); - free(dest); return ret; } @@ -669,7 +715,15 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re close(data->fd[1]); close(data->fd[0]); - ret |= finish_connect(data->conn); + /* + * Atomic push may abort the connection early and close the pipe, + * which may cause an error for `finish_connect()`. Ignore this error + * for atomic git-push. + */ + if (ret || args.atomic) + finish_connect(data->conn); + else + ret = finish_connect(data->conn); data->conn = NULL; data->got_remote_heads = 0; @@ -691,7 +745,7 @@ static int disconnect_git(struct transport *transport) { struct git_transport_data *data = transport->data; if (data->conn) { - if (data->got_remote_heads) + if (data->got_remote_heads && !transport->stateless_rpc) packet_flush(data->fd[1]); close(data->fd[0]); close(data->fd[1]); @@ -1028,6 +1082,7 @@ static int run_pre_push_hook(struct transport *transport, proc.argv = argv; proc.in = -1; + proc.trace2_hook_name = "pre-push"; if (start_command(&proc)) { finish_command(&proc); @@ -1072,7 +1127,8 @@ static int run_pre_push_hook(struct transport *transport, return ret; } -int transport_push(struct transport *transport, +int transport_push(struct repository *r, + struct transport *transport, struct refspec *rs, int flags, unsigned int *reject_reasons) { @@ -1097,8 +1153,10 @@ int transport_push(struct transport *transport, refspec_ref_prefixes(rs, &ref_prefixes); + trace2_region_enter("transport_push", "get_refs_list", r); remote_refs = transport->vtable->get_refs_list(transport, 1, &ref_prefixes); + trace2_region_leave("transport_push", "get_refs_list", r); argv_array_clear(&ref_prefixes); @@ -1134,20 +1192,24 @@ int transport_push(struct transport *transport, struct ref *ref = remote_refs; struct oid_array commits = OID_ARRAY_INIT; + trace2_region_enter("transport_push", "push_submodules", r); for (; ref; ref = ref->next) if (!is_null_oid(&ref->new_oid)) oid_array_append(&commits, &ref->new_oid); - if (!push_unpushed_submodules(&commits, + if (!push_unpushed_submodules(r, + &commits, transport->remote, rs, transport->push_options, pretend)) { oid_array_clear(&commits); + trace2_region_leave("transport_push", "push_submodules", r); die(_("failed to push all needed submodules")); } oid_array_clear(&commits); + trace2_region_leave("transport_push", "push_submodules", r); } if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) || @@ -1158,23 +1220,30 @@ int transport_push(struct transport *transport, struct string_list needs_pushing = STRING_LIST_INIT_DUP; struct oid_array commits = OID_ARRAY_INIT; + trace2_region_enter("transport_push", "check_submodules", r); for (; ref; ref = ref->next) if (!is_null_oid(&ref->new_oid)) oid_array_append(&commits, &ref->new_oid); - if (find_unpushed_submodules(&commits, transport->remote->name, - &needs_pushing)) { + if (find_unpushed_submodules(r, + &commits, + transport->remote->name, + &needs_pushing)) { oid_array_clear(&commits); + trace2_region_leave("transport_push", "check_submodules", r); die_with_unpushed_submodules(&needs_pushing); } string_list_clear(&needs_pushing, 0); oid_array_clear(&commits); + trace2_region_leave("transport_push", "check_submodules", r); } - if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY)) + if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY)) { + trace2_region_enter("transport_push", "push_refs", r); push_ret = transport->vtable->push_refs(transport, remote_refs, flags); - else + trace2_region_leave("transport_push", "push_refs", r); + } else push_ret = 0; err = push_had_errors(remote_refs); ret = push_ret | err; @@ -1228,7 +1297,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs) nr_refs++; if (rm->peer_ref && !is_null_oid(&rm->old_oid) && - !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid)) + oideq(&rm->peer_ref->old_oid, &rm->old_oid)) continue; ALLOC_GROW(heads, nr_heads + 1, nr_alloc); heads[nr_heads++] = rm; @@ -1324,78 +1393,3 @@ char *transport_anonymize_url(const char *url) 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; -}; - -static int refs_from_alternate_cb(struct alternate_object_database *e, - void *data) -{ - struct strbuf path = STRBUF_INIT; - size_t base_len; - struct alternate_refs_data *cb = data; - - 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? */ - strbuf_addstr(&path, "/refs"); - if (!is_directory(path.buf)) - goto out; - strbuf_setlen(&path, base_len); - - read_alternate_refs(path.buf, cb->fn, cb->data); - -out: - strbuf_release(&path); - return 0; -} - -void for_each_alternate_ref(alternate_ref_fn fn, void *data) -{ - struct alternate_refs_data cb; - cb.fn = fn; - cb.data = data; - foreach_alt_odb(refs_from_alternate_cb, &cb); -} |