static void receive_wanted_refs(struct packet_reader *reader, struct ref **sought, int nr_sought) { process_section_header(reader, "wanted-refs", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { struct object_id oid; const char *end; int i; if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ') die(_("expected wanted-ref, got '%s'"), reader->line); for (i = 0; i < nr_sought; i++) { if (!strcmp(end, sought[i]->name)) { oidcpy(&sought[i]->old_oid, &oid); break; } } if (i == nr_sought) die(_("unexpected wanted-ref: '%s'"), reader->line); } if (reader->status != PACKET_READ_DELIM) die(_("error processing wanted refs: %d"), reader->status); }
static enum ack_type get_ack(struct packet_reader *reader, struct object_id *result_oid) { int len; const char *arg; if (packet_reader_read(reader) != PACKET_READ_NORMAL) die(_("git fetch-pack: expected ACK/NAK, got a flush packet")); len = reader->pktlen; if (!strcmp(reader->line, "NAK")) return NAK; if (skip_prefix(reader->line, "ACK ", &arg)) { if (!get_oid_hex(arg, result_oid)) { arg += 40; len -= arg - reader->line; if (len < 1) return ACK; if (strstr(arg, "continue")) return ACK_continue; if (strstr(arg, "common")) return ACK_common; if (strstr(arg, "ready")) return ACK_ready; return ACK; } } die(_("git fetch-pack: expected ACK/NAK, got '%s'"), reader->line); }
static void process_capabilities_v2(struct packet_reader *reader) { while (packet_reader_read(reader) == PACKET_READ_NORMAL) argv_array_push(&server_capabilities_v2, reader->line); if (reader->status != PACKET_READ_FLUSH) die(_("expected flush after capabilities")); }
static int receive_unpack_status(struct packet_reader *reader) { if (packet_reader_read(reader) != PACKET_READ_NORMAL) return error(_("unexpected flush packet while reading remote unpack status")); if (!skip_prefix(reader->line, "unpack ", &reader->line)) return error(_("unable to parse remote unpack status: %s"), reader->line); if (strcmp(reader->line, "ok")) return error(_("remote unpack failed: %s"), reader->line); return 0; }
static int process_acks(struct fetch_negotiator *negotiator, struct packet_reader *reader, struct oidset *common) { /* received */ int received_ready = 0; int received_ack = 0; process_section_header(reader, "acknowledgments", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { const char *arg; if (!strcmp(reader->line, "NAK")) continue; if (skip_prefix(reader->line, "ACK ", &arg)) { struct object_id oid; if (!get_oid_hex(arg, &oid)) { struct commit *commit; oidset_insert(common, &oid); commit = lookup_commit(the_repository, &oid); negotiator->ack(negotiator, commit); } continue; } if (!strcmp(reader->line, "ready")) { received_ready = 1; continue; } die(_("unexpected acknowledgment line: '%s'"), reader->line); } if (reader->status != PACKET_READ_FLUSH && reader->status != PACKET_READ_DELIM) die(_("error processing acks: %d"), reader->status); /* * If an "acknowledgments" section is sent, a packfile is sent if and * only if "ready" was sent in this section. The other sections * ("shallow-info" and "wanted-refs") are sent only if a packfile is * sent. Therefore, a DELIM is expected if "ready" is sent, and a FLUSH * otherwise. */ if (received_ready && reader->status != PACKET_READ_DELIM) die(_("expected packfile to be sent after 'ready'")); if (!received_ready && reader->status != PACKET_READ_FLUSH) die(_("expected no other sections to be sent after no 'ready'")); /* return 0 if no common, 1 if there are common, or 2 if ready */ return received_ready ? 2 : (received_ack ? 1 : 0); }
static int receive_status(struct packet_reader *reader, struct ref *refs) { struct ref *hint; int ret; hint = NULL; ret = receive_unpack_status(reader); while (1) { const char *refname; char *msg; if (packet_reader_read(reader) != PACKET_READ_NORMAL) break; if (!starts_with(reader->line, "ok ") && !starts_with(reader->line, "ng ")) { error("invalid ref status from remote: %s", reader->line); ret = -1; break; } refname = reader->line + 3; msg = strchr(refname, ' '); if (msg) *msg++ = '\0'; /* first try searching at our hint, falling back to all refs */ if (hint) hint = find_ref_by_name(hint, refname); if (!hint) hint = find_ref_by_name(refs, refname); if (!hint) { warning("remote reported status on unknown ref: %s", refname); continue; } if (hint->status != REF_STATUS_EXPECTING_REPORT) { warning("remote reported status on unexpected ref: %s", refname); continue; } if (reader->line[0] == 'o' && reader->line[1] == 'k') hint->status = REF_STATUS_OK; else { hint->status = REF_STATUS_REMOTE_REJECT; ret = -1; } hint->remote_status = xstrdup_or_null(msg); /* start our next search from the next ref */ hint = hint->next; } return ret; }
/* * CURLOPT_READFUNCTION callback function. * Attempts to copy over a single packet-line at a time into the * curl provided buffer. */ static size_t proxy_in(char *buffer, size_t eltsize, size_t nmemb, void *userdata) { size_t max; struct proxy_state *p = userdata; size_t avail = p->request_buffer.len - p->pos; if (eltsize != 1) BUG("curl read callback called with size = %"PRIuMAX" != 1", (uintmax_t)eltsize); max = nmemb; if (!avail) { if (p->seen_flush) { p->seen_flush = 0; return 0; } strbuf_reset(&p->request_buffer); switch (packet_reader_read(&p->reader)) { case PACKET_READ_EOF: die("unexpected EOF when reading from parent process"); case PACKET_READ_NORMAL: packet_buf_write_len(&p->request_buffer, p->reader.line, p->reader.pktlen); break; case PACKET_READ_DELIM: packet_buf_delim(&p->request_buffer); break; case PACKET_READ_FLUSH: packet_buf_flush(&p->request_buffer); p->seen_flush = 1; break; } p->pos = 0; avail = p->request_buffer.len; } if (max < avail) avail = max; memcpy(buffer, p->request_buffer.buf + p->pos, avail); p->pos += avail; return avail; }
/* * Processes a section header in a server's response and checks if it matches * `section`. If the value of `peek` is 1, the header line will be peeked (and * not consumed); if 0, the line will be consumed and the function will die if * the section header doesn't match what was expected. */ static int process_section_header(struct packet_reader *reader, const char *section, int peek) { int ret; if (packet_reader_peek(reader) != PACKET_READ_NORMAL) die(_("error reading section header '%s'"), section); ret = !strcmp(reader->line, section); if (!peek) { if (!ret) die(_("expected '%s', received '%s'"), section, reader->line); packet_reader_read(reader); } return ret; }
static void consume_shallow_list(struct fetch_pack_args *args, struct packet_reader *reader) { if (args->stateless_rpc && args->deepen) { /* If we sent a depth we will get back "duplicate" * shallow and unshallow commands every time there * is a block of have lines exchanged. */ while (packet_reader_read(reader) == PACKET_READ_NORMAL) { if (starts_with(reader->line, "shallow ")) continue; if (starts_with(reader->line, "unshallow ")) continue; die(_("git fetch-pack: expected shallow list")); } if (reader->status != PACKET_READ_FLUSH) die(_("git fetch-pack: expected a flush packet after shallow list")); } }
static void receive_shallow_info(struct fetch_pack_args *args, struct packet_reader *reader) { int line_received = 0; process_section_header(reader, "shallow-info", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { const char *arg; struct object_id oid; if (skip_prefix(reader->line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), reader->line); register_shallow(the_repository, &oid); line_received = 1; continue; } if (skip_prefix(reader->line, "unshallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid unshallow line: %s"), reader->line); if (!lookup_object(the_repository, oid.hash)) die(_("object not found: %s"), reader->line); /* make sure that it is parsed as shallow */ if (!parse_object(the_repository, &oid)) die(_("error in object: %s"), reader->line); if (unregister_shallow(&oid)) die(_("no shallow found: %s"), reader->line); line_received = 1; continue; } die(_("expected shallow/unshallow, got %s"), reader->line); } if (reader->status != PACKET_READ_FLUSH && reader->status != PACKET_READ_DELIM) die(_("error processing shallow info: %d"), reader->status); if (line_received) { setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL); args->deepen = 1; } }
static int process_acks(struct fetch_negotiator *negotiator, struct packet_reader *reader, struct oidset *common) { /* received */ int received_ready = 0; int received_ack = 0; process_section_header(reader, "acknowledgments", 0); while (packet_reader_read(reader) == PACKET_READ_NORMAL) { const char *arg; if (!strcmp(reader->line, "NAK")) continue; if (skip_prefix(reader->line, "ACK ", &arg)) { struct object_id oid; if (!get_oid_hex(arg, &oid)) { struct commit *commit; oidset_insert(common, &oid); commit = lookup_commit(the_repository, &oid); negotiator->ack(negotiator, commit); } continue; } if (!strcmp(reader->line, "ready")) { received_ready = 1; continue; } die(_("unexpected acknowledgment line: '%s'"), reader->line); } if (reader->status != PACKET_READ_FLUSH && reader->status != PACKET_READ_DELIM) die(_("error processing acks: %d"), reader->status); /* return 0 if no common, 1 if there are common, or 2 if ready */ return received_ready ? 2 : (received_ack ? 1 : 0); }
enum protocol_version discover_version(struct packet_reader *reader) { enum protocol_version version = protocol_unknown_version; /* * Peek the first line of the server's response to * determine the protocol version the server is speaking. */ switch (packet_reader_peek(reader)) { case PACKET_READ_EOF: die_initial_contact(0); case PACKET_READ_FLUSH: case PACKET_READ_DELIM: version = protocol_v0; break; case PACKET_READ_NORMAL: version = determine_protocol_version_client(reader->line); break; } switch (version) { case protocol_v2: process_capabilities_v2(reader); break; case protocol_v1: /* Read the peeked version line */ packet_reader_read(reader); break; case protocol_v0: break; case protocol_unknown_version: BUG("unknown protocol version"); } return version; }
static void process_args(struct packet_reader *request, struct upload_pack_data *data) { while (packet_reader_read(request) != PACKET_READ_FLUSH) { const char *arg = request->line; const char *p; /* process want */ if (parse_want(arg)) continue; /* process have line */ if (parse_have(arg, &data->haves)) continue; /* process args like thin-pack */ if (!strcmp(arg, "thin-pack")) { use_thin_pack = 1; continue; } if (!strcmp(arg, "ofs-delta")) { use_ofs_delta = 1; continue; } if (!strcmp(arg, "no-progress")) { no_progress = 1; continue; } if (!strcmp(arg, "include-tag")) { use_include_tag = 1; continue; } if (!strcmp(arg, "done")) { data->done = 1; continue; } /* Shallow related arguments */ if (process_shallow(arg, &data->shallows)) continue; if (process_deepen(arg, &data->depth)) continue; if (process_deepen_since(arg, &data->deepen_since, &data->deepen_rev_list)) continue; if (process_deepen_not(arg, &data->deepen_not, &data->deepen_rev_list)) continue; if (!strcmp(arg, "deepen-relative")) { data->deepen_relative = 1; continue; } if (allow_filter && skip_prefix(arg, "filter ", &p)) { parse_list_objects_filter(&filter_options, p); continue; } /* ignore unknown lines maybe? */ die("unexpected line: '%s'", arg); } }
static int find_common(struct fetch_negotiator *negotiator, struct fetch_pack_args *args, int fd[2], struct object_id *result_oid, struct ref *refs) { int fetching; int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval; const struct object_id *oid; unsigned in_vain = 0; int got_continue = 0; int got_ready = 0; struct strbuf req_buf = STRBUF_INIT; size_t state_len = 0; struct packet_reader reader; if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE | PACKET_READ_DIE_ON_ERR_PACKET); if (!args->no_dependents) { mark_tips(negotiator, args->negotiation_tips); for_each_cached_alternate(negotiator, insert_one_alternate_object); } fetching = 0; for ( ; refs ; refs = refs->next) { struct object_id *remote = &refs->old_oid; const char *remote_hex; struct object *o; /* * If that object is complete (i.e. it is an ancestor of a * local ref), we tell them we have it but do not have to * tell them about its ancestors, which they already know * about. * * We use lookup_object here because we are only * interested in the case we *know* the object is * reachable and we have already scanned it. * * Do this only if args->no_dependents is false (if it is true, * we cannot trust the object flags). */ if (!args->no_dependents && ((o = lookup_object(the_repository, remote->hash)) != NULL) && (o->flags & COMPLETE)) { continue; } remote_hex = oid_to_hex(remote); if (!fetching) { struct strbuf c = STRBUF_INIT; if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed"); if (multi_ack == 1) strbuf_addstr(&c, " multi_ack"); if (no_done) strbuf_addstr(&c, " no-done"); if (use_sideband == 2) strbuf_addstr(&c, " side-band-64k"); if (use_sideband == 1) strbuf_addstr(&c, " side-band"); if (args->deepen_relative) strbuf_addstr(&c, " deepen-relative"); if (args->use_thin_pack) strbuf_addstr(&c, " thin-pack"); if (args->no_progress) strbuf_addstr(&c, " no-progress"); if (args->include_tag) strbuf_addstr(&c, " include-tag"); if (prefer_ofs_delta) strbuf_addstr(&c, " ofs-delta"); if (deepen_since_ok) strbuf_addstr(&c, " deepen-since"); if (deepen_not_ok) strbuf_addstr(&c, " deepen-not"); if (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); if (args->filter_options.choice) strbuf_addstr(&c, " filter"); packet_buf_write(&req_buf, "want %s%s\n", remote_hex, c.buf); strbuf_release(&c); } else packet_buf_write(&req_buf, "want %s\n", remote_hex); fetching++; } if (!fetching) { strbuf_release(&req_buf); packet_flush(fd[1]); return 1; } if (is_repository_shallow(the_repository)) write_shallow_commits(&req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); if (args->deepen_since) { timestamp_t max_age = approxidate(args->deepen_since); packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age); } if (args->deepen_not) { int i; for (i = 0; i < args->deepen_not->nr; i++) { struct string_list_item *s = args->deepen_not->items + i; packet_buf_write(&req_buf, "deepen-not %s", s->string); } } if (server_supports_filtering && args->filter_options.choice) { struct strbuf expanded_filter_spec = STRBUF_INIT; expand_list_objects_filter_spec(&args->filter_options, &expanded_filter_spec); packet_buf_write(&req_buf, "filter %s", expanded_filter_spec.buf); strbuf_release(&expanded_filter_spec); } packet_buf_flush(&req_buf); state_len = req_buf.len; if (args->deepen) { const char *arg; struct object_id oid; send_request(args, fd[1], &req_buf); while (packet_reader_read(&reader) == PACKET_READ_NORMAL) { if (skip_prefix(reader.line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), reader.line); register_shallow(the_repository, &oid); continue; } if (skip_prefix(reader.line, "unshallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid unshallow line: %s"), reader.line); if (!lookup_object(the_repository, oid.hash)) die(_("object not found: %s"), reader.line); /* make sure that it is parsed as shallow */ if (!parse_object(the_repository, &oid)) die(_("error in object: %s"), reader.line); if (unregister_shallow(&oid)) die(_("no shallow found: %s"), reader.line); continue; } die(_("expected shallow/unshallow, got %s"), reader.line); } } else if (!args->stateless_rpc) send_request(args, fd[1], &req_buf); if (!args->stateless_rpc) { /* If we aren't using the stateless-rpc interface * we don't need to retain the headers. */ strbuf_setlen(&req_buf, 0); state_len = 0; } flushes = 0; retval = -1; if (args->no_dependents) goto done; while ((oid = negotiator->next(negotiator))) { packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid)); print_verbose(args, "have %s", oid_to_hex(oid)); in_vain++; if (flush_at <= ++count) { int ack; packet_buf_flush(&req_buf); send_request(args, fd[1], &req_buf); strbuf_setlen(&req_buf, state_len); flushes++; flush_at = next_flush(args->stateless_rpc, count); /* * We keep one window "ahead" of the other side, and * will wait for an ACK only on the next one */ if (!args->stateless_rpc && count == INITIAL_FLUSH) continue; consume_shallow_list(args, &reader); do { ack = get_ack(&reader, result_oid); if (ack) print_verbose(args, _("got %s %d %s"), "ack", ack, oid_to_hex(result_oid)); switch (ack) { case ACK: flushes = 0; multi_ack = 0; retval = 0; goto done; case ACK_common: case ACK_ready: case ACK_continue: { struct commit *commit = lookup_commit(the_repository, result_oid); int was_common; if (!commit) die(_("invalid commit %s"), oid_to_hex(result_oid)); was_common = negotiator->ack(negotiator, commit); if (args->stateless_rpc && ack == ACK_common && !was_common) { /* We need to replay the have for this object * on the next RPC request so the peer knows * it is in common with us. */ const char *hex = oid_to_hex(result_oid); packet_buf_write(&req_buf, "have %s\n", hex); state_len = req_buf.len; /* * Reset in_vain because an ack * for this commit has not been * seen. */ in_vain = 0; } else if (!args->stateless_rpc || ack != ACK_common) in_vain = 0; retval = 0; got_continue = 1; if (ack == ACK_ready) got_ready = 1; break; } } } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { print_verbose(args, _("giving up")); break; /* give up */ } if (got_ready) break; } } done: if (!got_ready || !no_done) { packet_buf_write(&req_buf, "done\n"); send_request(args, fd[1], &req_buf); } print_verbose(args, _("done")); if (retval != 0) { multi_ack = 0; flushes++; } strbuf_release(&req_buf); if (!got_ready || !no_done) consume_shallow_list(args, &reader); while (flushes || multi_ack) { int ack = get_ack(&reader, result_oid); if (ack) { print_verbose(args, _("got %s (%d) %s"), "ack", ack, oid_to_hex(result_oid)); if (ack == ACK) return 0; multi_ack = 1; continue; } flushes--; } /* it is no error to fetch into a completely empty repo */ return count ? retval : 0; }