static enum ack_type get_ack(int fd, unsigned char *result_sha1) { static char line[1000]; int len = packet_read_line(fd, line, sizeof(line)); if (!len) die("git fetch-pack: expected ACK/NAK, got EOF"); if (line[len-1] == '\n') line[--len] = 0; if (!strcmp(line, "NAK")) return NAK; if (!prefixcmp(line, "ACK ")) { if (!get_sha1_hex(line+4, result_sha1)) { if (strstr(line+45, "continue")) return ACK_continue; if (strstr(line+45, "common")) return ACK_common; if (strstr(line+45, "ready")) return ACK_ready; return ACK; } } die("git fetch_pack: expected ACK/NAK, got '%s'", line); }
static enum ack_type get_ack(int fd, unsigned char *result_sha1) { int len; char *line = packet_read_line(fd, &len); if (!len) die("git fetch-pack: expected ACK/NAK, got EOF"); if (!strcmp(line, "NAK")) return NAK; if (!prefixcmp(line, "ACK ")) { if (!get_sha1_hex(line+4, result_sha1)) { if (len < 45) return ACK; if (strstr(line+45, "continue")) return ACK_continue; if (strstr(line+45, "common")) return ACK_common; if (strstr(line+45, "ready")) return ACK_ready; return ACK; } } die("git fetch_pack: expected ACK/NAK, got '%s'", line); }
static int post_rpc(struct rpc_state *rpc) { struct active_request_slot *slot; struct curl_slist *headers = NULL; int use_gzip = rpc->gzip_request; char *gzip_body = NULL; int err, large_request = 0; /* Try to load the entire request, if we can fit it into the * allocated buffer space we can use HTTP/1.0 and avoid the * chunked encoding mess. */ while (1) { size_t left = rpc->alloc - rpc->len; char *buf = rpc->buf + rpc->len; int n; if (left < LARGE_PACKET_MAX) { large_request = 1; use_gzip = 0; break; } n = packet_read_line(rpc->out, buf, left); if (!n) break; rpc->len += n; } if (large_request) { err = probe_rpc(rpc); if (err) return err; } slot = get_active_slot(); curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0); curl_easy_setopt(slot->curl, CURLOPT_POST, 1); curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url); curl_easy_setopt(slot->curl, CURLOPT_ENCODING, ""); headers = curl_slist_append(headers, rpc->hdr_content_type); headers = curl_slist_append(headers, rpc->hdr_accept); headers = curl_slist_append(headers, "Expect:"); if (large_request) { /* The request body is large and the size cannot be predicted. * We must use chunked encoding to send it. */ headers = curl_slist_append(headers, "Transfer-Encoding: chunked"); rpc->initial_buffer = 1; curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out); curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc); #ifndef NO_CURL_IOCTL curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl); curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc); #endif if (options.verbosity > 1) { fprintf(stderr, "POST %s (chunked)\n", rpc->service_name); fflush(stderr); } } else if (use_gzip && 1024 < rpc->len) { /* The client backend isn't giving us compressed data so * we can try to deflate it ourselves, this may save on. * the transfer time. */ size_t size; git_zstream stream; int ret; memset(&stream, 0, sizeof(stream)); git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION); size = git_deflate_bound(&stream, rpc->len); gzip_body = xmalloc(size); stream.next_in = (unsigned char *)rpc->buf; stream.avail_in = rpc->len; stream.next_out = (unsigned char *)gzip_body; stream.avail_out = size; ret = git_deflate(&stream, Z_FINISH); if (ret != Z_STREAM_END) die("cannot deflate request; zlib deflate error %d", ret); ret = git_deflate_end_gently(&stream); if (ret != Z_OK) die("cannot deflate request; zlib end error %d", ret); size = stream.total_out; headers = curl_slist_append(headers, "Content-Encoding: gzip"); curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body); curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size); if (options.verbosity > 1) { fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n", rpc->service_name, (unsigned long)rpc->len, (unsigned long)size); fflush(stderr); } } else { /* We know the complete request size in advance, use the * more normal Content-Length approach. */ curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf); curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len); if (options.verbosity > 1) { fprintf(stderr, "POST %s (%lu bytes)\n", rpc->service_name, (unsigned long)rpc->len); fflush(stderr); } } curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers); curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in); curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc); err = run_slot(slot); curl_slist_free_all(headers); free(gzip_body); return err; }
static int execute(struct sockaddr *addr) { static char line[1000]; int pktlen, len, i; if (addr) { char addrbuf[256] = ""; int port = -1; if (addr->sa_family == AF_INET) { struct sockaddr_in *sin_addr = (void *) addr; inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); port = ntohs(sin_addr->sin_port); #ifndef NO_IPV6 } else if (addr && addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6_addr = (void *) addr; char *buf = addrbuf; *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */ inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1); strcat(buf, "]"); port = ntohs(sin6_addr->sin6_port); #endif } loginfo("Connection from %s:%d", addrbuf, port); setenv("REMOTE_ADDR", addrbuf, 1); } else { unsetenv("REMOTE_ADDR"); } alarm(init_timeout ? init_timeout : timeout); pktlen = packet_read_line(0, line, sizeof(line)); alarm(0); len = strlen(line); if (pktlen != len) loginfo("Extended attributes (%d bytes) exist <%.*s>", (int) pktlen - len, (int) pktlen - len, line + len + 1); if (len && line[len-1] == '\n') { line[--len] = 0; pktlen--; } free(hostname); free(canon_hostname); free(ip_address); free(tcp_port); hostname = canon_hostname = ip_address = tcp_port = NULL; if (len != pktlen) parse_host_arg(line + len + 1, pktlen - len - 1); for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); int namelen = strlen(s->name); if (!prefixcmp(line, "git-") && !strncmp(s->name, line + 4, namelen) && line[namelen + 4] == ' ') { /* * Note: The directory here is probably context sensitive, * and might depend on the actual service being performed. */ return run_service(line + namelen + 5, s); } } logerror("Protocol error: '%s'", line); return -1; }
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; if (args->stateless_rpc && multi_ack == 1) die(_("--stateless-rpc requires multi_ack_detailed")); 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) packet_buf_write(&req_buf, "filter %s", args->filter_options.filter_spec); packet_buf_flush(&req_buf); state_len = req_buf.len; if (args->deepen) { char *line; const char *arg; struct object_id oid; send_request(args, fd[1], &req_buf); while ((line = packet_read_line(fd[0], NULL))) { if (skip_prefix(line, "shallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid shallow line: %s"), line); register_shallow(the_repository, &oid); continue; } if (skip_prefix(line, "unshallow ", &arg)) { if (get_oid_hex(arg, &oid)) die(_("invalid unshallow line: %s"), line); if (!lookup_object(the_repository, oid.hash)) die(_("object not found: %s"), line); /* make sure that it is parsed as shallow */ if (!parse_object(the_repository, &oid)) die(_("error in object: %s"), line); if (unregister_shallow(&oid)) die(_("no shallow found: %s"), line); continue; } die(_("expected shallow/unshallow, got %s"), 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, fd[0]); do { ack = get_ack(fd[0], 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, fd[0]); while (flushes || multi_ack) { int ack = get_ack(fd[0], 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; }
static struct command *read_head_info(struct sha1_array *shallow) { struct command *commands = NULL; struct command **p = &commands; for (;;) { char *line; int len, linelen; line = packet_read_line(0, &len); if (!line) break; if (len == 48 && starts_with(line, "shallow ")) { unsigned char sha1[20]; if (get_sha1_hex(line + 8, sha1)) die("protocol error: expected shallow sha, got '%s'", line + 8); sha1_array_append(shallow, sha1); continue; } linelen = strlen(line); if (linelen < len) { const char *feature_list = line + linelen + 1; if (parse_feature_request(feature_list, "report-status")) report_status = 1; if (parse_feature_request(feature_list, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; if (parse_feature_request(feature_list, "quiet")) quiet = 1; if (advertise_atomic_push && parse_feature_request(feature_list, "atomic")) use_atomic = 1; if (advertise_push_options && parse_feature_request(feature_list, "push-options")) use_push_options = 1; } if (!strcmp(line, "push-cert")) { int true_flush = 0; char certbuf[1024]; for (;;) { len = packet_read(0, NULL, NULL, certbuf, sizeof(certbuf), 0); if (!len) { true_flush = 1; break; } if (!strcmp(certbuf, "push-cert-end\n")) break; /* end of cert */ strbuf_addstr(&push_cert, certbuf); } if (true_flush) break; continue; } p = queue_command(p, line, linelen); } if (push_cert.len) queue_commands_from_cert(p, &push_cert); return commands; }
static int receive_status(int in, struct ref *refs) { struct ref *hint; char line[1000]; int ret = 0; int len = packet_read_line(in, line, sizeof(line)); if (len < 10 || memcmp(line, "unpack ", 7)) return error("did not receive remote status"); if (memcmp(line, "unpack ok\n", 10)) { char *p = line + strlen(line) - 1; if (*p == '\n') *p = '\0'; error("unpack failed: %s", line + 7); ret = -1; } hint = NULL; while (1) { char *refname; char *msg; len = packet_read_line(in, line, sizeof(line)); if (!len) break; if (len < 3 || (memcmp(line, "ok ", 3) && memcmp(line, "ng ", 3))) { fprintf(stderr, "protocol error: %s\n", line); ret = -1; break; } line[strlen(line)-1] = '\0'; refname = 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 (line[0] == 'o' && line[1] == 'k') hint->status = REF_STATUS_OK; else { hint->status = REF_STATUS_REMOTE_REJECT; ret = -1; } if (msg) hint->remote_status = xstrdup(msg); /* start our next search from the next ref */ hint = hint->next; } return ret; }
static int get_common_commits(void) { unsigned char sha1[20]; char last_hex[41]; int got_common = 0; int got_other = 0; int sent_ready = 0; save_commit_buffer = 0; for (;;) { char *line = packet_read_line(0, NULL); reset_timeout(); if (!line) { if (multi_ack == 2 && got_common && !got_other && ok_to_give_up()) { sent_ready = 1; packet_write(1, "ACK %s ready\n", last_hex); } if (have_obj.nr == 0 || multi_ack) packet_write(1, "NAK\n"); if (no_done && sent_ready) { packet_write(1, "ACK %s\n", last_hex); return 0; } if (stateless_rpc) exit(0); got_common = 0; got_other = 0; continue; } if (starts_with(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ got_other = 1; if (multi_ack && ok_to_give_up()) { const char *hex = sha1_to_hex(sha1); if (multi_ack == 2) { sent_ready = 1; packet_write(1, "ACK %s ready\n", hex); } else packet_write(1, "ACK %s continue\n", hex); } break; default: got_common = 1; memcpy(last_hex, sha1_to_hex(sha1), 41); if (multi_ack == 2) packet_write(1, "ACK %s common\n", last_hex); else if (multi_ack) packet_write(1, "ACK %s continue\n", last_hex); else if (have_obj.nr == 1) packet_write(1, "ACK %s\n", last_hex); break; } continue; } if (!strcmp(line, "done")) { if (have_obj.nr > 0) { if (multi_ack) packet_write(1, "ACK %s\n", last_hex); return 0; } packet_write(1, "NAK\n"); return -1; } die("git upload-pack: expected SHA1 list, got '%s'", line); } }
int recv_sideband(const char *me, int in_stream, int out, int err) { unsigned pf = strlen(PREFIX); unsigned sf; char buf[LARGE_PACKET_MAX + 2*FIX_SIZE]; char *suffix, *term; memcpy(buf, PREFIX, pf); term = getenv("TERM"); if (term && strcmp(term, "dumb")) suffix = ANSI_SUFFIX; else suffix = DUMB_SUFFIX; sf = strlen(suffix); while (1) { int band, len; len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { len = sprintf(buf, "%s: protocol error: no band designator\n", me); safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; len--; switch (band) { case 3: buf[pf] = ' '; buf[pf+1+len] = '\n'; safe_write(err, buf, pf+1+len+1); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; len += pf+1; while (1) { int brk = pf+1; /* Break the buffer into separate lines. */ while (brk < len) { brk++; if (buf[brk-1] == '\n' || buf[brk-1] == '\r') break; } /* * Let's insert a suffix to clear the end * of the screen line, but only if current * line data actually contains something. */ if (brk > pf+1 + 1) { char save[FIX_SIZE]; memcpy(save, buf + brk, sf); buf[brk + sf - 1] = buf[brk - 1]; memcpy(buf + brk - 1, suffix, sf); safe_write(err, buf, brk + sf); memcpy(buf + brk, save, sf); } else safe_write(err, buf, brk); if (brk < len) { memmove(buf + pf+1, buf + brk, len - brk); len = len - brk + pf+1; } else break; } continue; case 1: safe_write(out, buf + pf+1, len); continue; default: len = sprintf(buf, "%s: protocol error: bad band #%d\n", me, band); safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } } return 0; }
int cmd_fetch_pack(int argc, const char **argv, const char *prefix) { int i, ret; struct ref *ref = NULL; const char *dest = NULL; struct ref **sought = NULL; int nr_sought = 0, alloc_sought = 0; int fd[2]; char *pack_lockfile = NULL; char **pack_lockfile_ptr = NULL; struct child_process *conn; struct fetch_pack_args args; struct sha1_array shallow = SHA1_ARRAY_INIT; packet_trace_identity("fetch-pack"); memset(&args, 0, sizeof(args)); args.uploadpack = "git-upload-pack"; for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; if (starts_with(arg, "--upload-pack=")) { args.uploadpack = arg + 14; continue; } if (starts_with(arg, "--exec=")) { args.uploadpack = arg + 7; continue; } if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { args.quiet = 1; continue; } if (!strcmp("--keep", arg) || !strcmp("-k", arg)) { args.lock_pack = args.keep_pack; args.keep_pack = 1; continue; } if (!strcmp("--thin", arg)) { args.use_thin_pack = 1; continue; } if (!strcmp("--include-tag", arg)) { args.include_tag = 1; continue; } if (!strcmp("--all", arg)) { args.fetch_all = 1; continue; } if (!strcmp("--stdin", arg)) { args.stdin_refs = 1; continue; } if (!strcmp("--diag-url", arg)) { args.diag_url = 1; continue; } if (!strcmp("-v", arg)) { args.verbose = 1; continue; } if (starts_with(arg, "--depth=")) { args.depth = strtol(arg + 8, NULL, 0); continue; } if (!strcmp("--no-progress", arg)) { args.no_progress = 1; continue; } if (!strcmp("--stateless-rpc", arg)) { args.stateless_rpc = 1; continue; } if (!strcmp("--lock-pack", arg)) { args.lock_pack = 1; pack_lockfile_ptr = &pack_lockfile; continue; } if (!strcmp("--check-self-contained-and-connected", arg)) { args.check_self_contained_and_connected = 1; continue; } if (!strcmp("--cloning", arg)) { args.cloning = 1; continue; } if (!strcmp("--update-shallow", arg)) { args.update_shallow = 1; continue; } usage(fetch_pack_usage); } if (i < argc) dest = argv[i++]; else usage(fetch_pack_usage); /* * Copy refs from cmdline to growable list, then append any * refs from the standard input: */ for (; i < argc; i++) add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]); if (args.stdin_refs) { if (args.stateless_rpc) { /* in stateless RPC mode we use pkt-line to read * from stdin, until we get a flush packet */ for (;;) { char *line = packet_read_line(0, NULL); if (!line) break; add_sought_entry(&sought, &nr_sought, &alloc_sought, line); } } else { /* read from stdin one ref per line, until EOF */ struct strbuf line = STRBUF_INIT; while (strbuf_getline(&line, stdin, '\n') != EOF) add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf); strbuf_release(&line); } } if (args.stateless_rpc) { conn = NULL; fd[0] = 0; fd[1] = 1; } else { int flags = args.verbose ? CONNECT_VERBOSE : 0; if (args.diag_url) flags |= CONNECT_DIAG_URL; conn = git_connect(fd, dest, args.uploadpack, flags); if (!conn) return args.diag_url ? 0 : 1; } get_remote_heads(fd[0], NULL, 0, &ref, 0, NULL, &shallow); ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought, &shallow, pack_lockfile_ptr); if (pack_lockfile) { printf("lock %s\n", pack_lockfile); fflush(stdout); } if (args.check_self_contained_and_connected && args.self_contained_and_connected) { printf("connectivity-ok\n"); fflush(stdout); } close(fd[0]); close(fd[1]); if (finish_connect(conn)) return 1; ret = !ref; /* * If the heads to pull were given, we should have consumed * all of them by matching the remote. Otherwise, 'git fetch * remote no-such-ref' would silently succeed without issuing * an error. */ for (i = 0; i < nr_sought; i++) { if (!sought[i] || sought[i]->matched) continue; error("no such remote ref %s", sought[i]->name); ret = 1; } while (ref) { printf("%s %s\n", sha1_to_hex(ref->old_sha1), ref->name); ref = ref->next; } return ret; }
static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; int depth = 0; int has_non_tip = 0; timestamp_t deepen_since = 0; int deepen_rev_list = 0; shallow_nr = 0; for (;;) { struct object *o; const char *features; struct object_id oid_buf; char *line = packet_read_line(0, NULL); const char *arg; reset_timeout(); if (!line) break; if (skip_prefix(line, "shallow ", &arg)) { struct object_id oid; struct object *object; if (get_oid_hex(arg, &oid)) die("invalid shallow line: %s", line); object = parse_object(&oid); if (!object) continue; if (object->type != OBJ_COMMIT) die("invalid shallow object %s", oid_to_hex(&oid)); if (!(object->flags & CLIENT_SHALLOW)) { object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); } continue; } if (skip_prefix(line, "deepen ", &arg)) { char *end = NULL; depth = strtol(arg, &end, 0); if (!end || *end || depth <= 0) die("Invalid deepen: %s", line); continue; } if (skip_prefix(line, "deepen-since ", &arg)) { char *end = NULL; deepen_since = parse_timestamp(arg, &end, 0); if (!end || *end || !deepen_since || /* revisions.c's max_age -1 is special */ deepen_since == -1) die("Invalid deepen-since: %s", line); deepen_rev_list = 1; continue; } if (skip_prefix(line, "deepen-not ", &arg)) { char *ref = NULL; struct object_id oid; if (expand_ref(arg, strlen(arg), &oid, &ref) != 1) die("git upload-pack: ambiguous deepen-not: %s", line); string_list_append(&deepen_not, ref); free(ref); deepen_rev_list = 1; continue; } if (skip_prefix(line, "filter ", &arg)) { if (!filter_capability_requested) die("git upload-pack: filtering capability not negotiated"); parse_list_objects_filter(&filter_options, arg); continue; } if (!skip_prefix(line, "want ", &arg) || get_oid_hex(arg, &oid_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); features = arg + 40; if (parse_feature_request(features, "deepen-relative")) deepen_relative = 1; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; if (parse_feature_request(features, "no-done")) no_done = 1; if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (parse_feature_request(features, "no-progress")) no_progress = 1; if (parse_feature_request(features, "include-tag")) use_include_tag = 1; if (allow_filter && parse_feature_request(features, "filter")) filter_capability_requested = 1; o = parse_object(&oid_buf); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", oid_to_hex(&oid_buf)); die("git upload-pack: not our ref %s", oid_to_hex(&oid_buf)); } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 || is_our_ref(o))) has_non_tip = 1; add_object_array(o, NULL, &want_obj); } } /* * We have sent all our refs already, and the other end * should have chosen out of them. When we are operating * in the stateless RPC mode, however, their choice may * have been based on the set of older refs advertised * by another process that handled the initial request. */ if (has_non_tip) check_non_tip(); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && !deepen_rev_list && shallows.nr == 0) return; if (depth > 0 && deepen_rev_list) die("git upload-pack: deepen and deepen-since (or deepen-not) cannot be used together"); if (depth > 0) deepen(depth, deepen_relative, &shallows); else if (deepen_rev_list) { struct argv_array av = ARGV_ARRAY_INIT; int i; argv_array_push(&av, "rev-list"); if (deepen_since) argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since); if (deepen_not.nr) { argv_array_push(&av, "--not"); for (i = 0; i < deepen_not.nr; i++) { struct string_list_item *s = deepen_not.items + i; argv_array_push(&av, s->string); } argv_array_push(&av, "--not"); } for (i = 0; i < want_obj.nr; i++) { struct object *o = want_obj.objects[i].item; argv_array_push(&av, oid_to_hex(&o->oid)); } deepen_by_rev_list(av.argc, av.argv, &shallows); argv_array_clear(&av); } else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) register_shallow(&shallows.objects[i].item->oid); } shallow_nr += shallows.nr; object_array_clear(&shallows); }
int recv_sideband(const char *me, int in_stream, int out, int err) { unsigned pf = strlen(PREFIX); unsigned sf; char buf[LARGE_PACKET_MAX + 2*FIX_SIZE]; char *suffix, *term; int skip_pf = 0; memcpy(buf, PREFIX, pf); term = getenv("TERM"); if (term && strcmp(term, "dumb")) suffix = ANSI_SUFFIX; else suffix = DUMB_SUFFIX; sf = strlen(suffix); while (1) { int band, len; len = packet_read_line(in_stream, buf + pf, LARGE_PACKET_MAX); if (len == 0) break; if (len < 1) { len = sprintf(buf, "%s: protocol error: no band designator\n", me); safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } band = buf[pf] & 0xff; len--; switch (band) { case 3: buf[pf] = ' '; buf[pf+1+len] = '\n'; safe_write(err, buf, pf+1+len+1); return SIDEBAND_REMOTE_ERROR; case 2: buf[pf] = ' '; do { char *b = buf; int brk = 0; /* * If the last buffer didn't end with a line * break then we should not print a prefix * this time around. */ if (skip_pf) { b += pf+1; } else { len += pf+1; brk += pf+1; } /* Look for a line break. */ for (;;) { brk++; if (brk > len) { brk = 0; break; } if (b[brk-1] == '\n' || b[brk-1] == '\r') break; } /* * Let's insert a suffix to clear the end * of the screen line if a line break was * found. Also, if we don't skip the * prefix, then a non-empty string must be * present too. */ if (brk > (skip_pf ? 0 : (pf+1 + 1))) { char save[FIX_SIZE]; memcpy(save, b + brk, sf); b[brk + sf - 1] = b[brk - 1]; memcpy(b + brk - 1, suffix, sf); safe_write(err, b, brk + sf); memcpy(b + brk, save, sf); len -= brk; } else { int l = brk ? brk : len; safe_write(err, b, l); len -= l; } skip_pf = !brk; memmove(buf + pf+1, b + brk, len); } while (len); continue; case 1: safe_write(out, buf + pf+1, len); continue; default: len = sprintf(buf, "%s: protocol error: bad band #%d\n", me, band); safe_write(err, buf, len); return SIDEBAND_PROTOCOL_ERROR; } } return 0; }
static void receive_needs(void) { struct object_array shallows = {0, 0, NULL}; static char line[1000]; int len, depth = 0; if (debug_fd) write_in_full(debug_fd, "#S\n", 3); for (;;) { struct object *o; unsigned char sha1_buf[20]; len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) break; if (debug_fd) write_in_full(debug_fd, line, len); if (!prefixcmp(line, "shallow ")) { unsigned char sha1[20]; struct object *object; use_thin_pack = 0; if (get_sha1(line + 8, sha1)) die("invalid shallow line: %s", line); object = parse_object(sha1); if (!object) die("did not find object for %s", line); object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); continue; } if (!prefixcmp(line, "deepen ")) { char *end; use_thin_pack = 0; depth = strtol(line + 7, &end, 0); if (end == line + 7 || depth <= 0) die("Invalid deepen: %s", line); continue; } if (prefixcmp(line, "want ") || get_sha1_hex(line+5, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); if (strstr(line+45, "multi_ack")) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; if (strstr(line+45, "ofs-delta")) use_ofs_delta = 1; if (strstr(line+45, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (strstr(line+45, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (strstr(line+45, "no-progress")) no_progress = 1; if (strstr(line+45, "include-tag")) use_include_tag = 1; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are * asking for nonsense. * * Hmph. We may later want to allow "want" line that * asks for something like "master~10" (symbolic)... * would it make sense? I don't know. */ o = lookup_object(sha1_buf); if (!o || !(o->flags & OUR_REF)) die("git upload-pack: not our ref %s", line+5); if (!(o->flags & WANTED)) { o->flags |= WANTED; add_object_array(o, NULL, &want_obj); } } if (debug_fd) write_in_full(debug_fd, "#E\n", 3); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && shallows.nr == 0) return; if (depth > 0) { struct commit_list *result, *backup; int i; backup = result = get_shallow_commits(&want_obj, depth, SHALLOW, NOT_SHALLOW); while (result) { struct object *object = &result->item->object; if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write(1, "shallow %s", sha1_to_hex(object->sha1)); register_shallow(object->sha1); } result = result->next; } free_commit_list(backup); for (i = 0; i < shallows.nr; i++) { struct object *object = shallows.objects[i].item; if (object->flags & NOT_SHALLOW) { struct commit_list *parents; packet_write(1, "unshallow %s", sha1_to_hex(object->sha1)); object->flags &= ~CLIENT_SHALLOW; /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; if (parse_commit((struct commit *)object)) die("invalid commit"); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, NULL, &want_obj); parents = parents->next; } } /* make sure commit traversal conforms to client */ register_shallow(object->sha1); } packet_flush(1); } else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) register_shallow(shallows.objects[i].item->sha1); } free(shallows.objects); }
static int rpc_service(struct rpc_state *rpc, struct discovery *heads) { const char *svc = rpc->service_name; struct strbuf buf = STRBUF_INIT; struct strbuf *preamble = rpc->stdin_preamble; struct child_process client; int err = 0; memset(&client, 0, sizeof(client)); client.in = -1; client.out = -1; client.git_cmd = 1; client.argv = rpc->argv; if (start_command(&client)) exit(1); if (preamble) write_or_die(client.in, preamble->buf, preamble->len); if (heads) write_or_die(client.in, heads->buf, heads->len); rpc->alloc = http_post_buffer; rpc->buf = xmalloc(rpc->alloc); rpc->in = client.in; rpc->out = client.out; strbuf_init(&rpc->result, 0); strbuf_addf(&buf, "%s%s", url, svc); rpc->service_url = strbuf_detach(&buf, NULL); strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc); rpc->hdr_content_type = strbuf_detach(&buf, NULL); strbuf_addf(&buf, "Accept: application/x-%s-result", svc); rpc->hdr_accept = strbuf_detach(&buf, NULL); while (!err) { int n = packet_read_line(rpc->out, rpc->buf, rpc->alloc); if (!n) break; rpc->pos = 0; rpc->len = n; err |= post_rpc(rpc); } close(client.in); client.in = -1; if (!err) { strbuf_read(&rpc->result, client.out, 0); } else { char buf[4096]; for (;;) if (xread(client.out, buf, sizeof(buf)) <= 0) break; } close(client.out); client.out = -1; err |= finish_command(&client); free(rpc->service_url); free(rpc->hdr_content_type); free(rpc->hdr_accept); free(rpc->buf); strbuf_release(&buf); return err; }
static int get_common_commits(void) { struct object_id oid; char last_hex[GIT_MAX_HEXSZ + 1]; int got_common = 0; int got_other = 0; int sent_ready = 0; save_commit_buffer = 0; for (;;) { char *line = packet_read_line(0, NULL); const char *arg; reset_timeout(); if (!line) { if (multi_ack == 2 && got_common && !got_other && ok_to_give_up()) { sent_ready = 1; packet_write_fmt(1, "ACK %s ready\n", last_hex); } if (have_obj.nr == 0 || multi_ack) packet_write_fmt(1, "NAK\n"); if (no_done && sent_ready) { packet_write_fmt(1, "ACK %s\n", last_hex); return 0; } if (stateless_rpc) exit(0); got_common = 0; got_other = 0; continue; } if (skip_prefix(line, "have ", &arg)) { switch (got_oid(arg, &oid)) { case -1: /* they have what we do not */ got_other = 1; if (multi_ack && ok_to_give_up()) { const char *hex = oid_to_hex(&oid); if (multi_ack == 2) { sent_ready = 1; packet_write_fmt(1, "ACK %s ready\n", hex); } else packet_write_fmt(1, "ACK %s continue\n", hex); } break; default: got_common = 1; oid_to_hex_r(last_hex, &oid); if (multi_ack == 2) packet_write_fmt(1, "ACK %s common\n", last_hex); else if (multi_ack) packet_write_fmt(1, "ACK %s continue\n", last_hex); else if (have_obj.nr == 1) packet_write_fmt(1, "ACK %s\n", last_hex); break; } continue; } if (!strcmp(line, "done")) { if (have_obj.nr > 0) { if (multi_ack) packet_write_fmt(1, "ACK %s\n", last_hex); return 0; } packet_write_fmt(1, "NAK\n"); return -1; } die("git upload-pack: expected SHA1 list, got '%s'", line); } }
int cmd_fetch_pack(int argc, const char **argv, const char *prefix) { int i, ret; struct ref *ref = NULL; const char *dest = NULL; struct ref **sought = NULL; int nr_sought = 0, alloc_sought = 0; int fd[2]; char *pack_lockfile = NULL; char **pack_lockfile_ptr = NULL; struct child_process *conn; struct fetch_pack_args args; struct oid_array shallow = OID_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; struct packet_reader reader; enum protocol_version version; fetch_if_missing = 0; packet_trace_identity("fetch-pack"); memset(&args, 0, sizeof(args)); args.uploadpack = "git-upload-pack"; for (i = 1; i < argc && *argv[i] == '-'; i++) { const char *arg = argv[i]; if (skip_prefix(arg, "--upload-pack=", &arg)) { args.uploadpack = arg; continue; } if (skip_prefix(arg, "--exec=", &arg)) { args.uploadpack = arg; continue; } if (!strcmp("--quiet", arg) || !strcmp("-q", arg)) { args.quiet = 1; continue; } if (!strcmp("--keep", arg) || !strcmp("-k", arg)) { args.lock_pack = args.keep_pack; args.keep_pack = 1; continue; } if (!strcmp("--thin", arg)) { args.use_thin_pack = 1; continue; } if (!strcmp("--include-tag", arg)) { args.include_tag = 1; continue; } if (!strcmp("--all", arg)) { args.fetch_all = 1; continue; } if (!strcmp("--stdin", arg)) { args.stdin_refs = 1; continue; } if (!strcmp("--diag-url", arg)) { args.diag_url = 1; continue; } if (!strcmp("-v", arg)) { args.verbose = 1; continue; } if (skip_prefix(arg, "--depth=", &arg)) { args.depth = strtol(arg, NULL, 0); continue; } if (skip_prefix(arg, "--shallow-since=", &arg)) { args.deepen_since = xstrdup(arg); continue; } if (skip_prefix(arg, "--shallow-exclude=", &arg)) { string_list_append(&deepen_not, arg); continue; } if (!strcmp(arg, "--deepen-relative")) { args.deepen_relative = 1; continue; } if (!strcmp("--no-progress", arg)) { args.no_progress = 1; continue; } if (!strcmp("--stateless-rpc", arg)) { args.stateless_rpc = 1; continue; } if (!strcmp("--lock-pack", arg)) { args.lock_pack = 1; pack_lockfile_ptr = &pack_lockfile; continue; } if (!strcmp("--check-self-contained-and-connected", arg)) { args.check_self_contained_and_connected = 1; continue; } if (!strcmp("--cloning", arg)) { args.cloning = 1; continue; } if (!strcmp("--update-shallow", arg)) { args.update_shallow = 1; continue; } if (!strcmp("--from-promisor", arg)) { args.from_promisor = 1; continue; } if (!strcmp("--no-dependents", arg)) { args.no_dependents = 1; continue; } if (skip_prefix(arg, ("--" CL_ARG__FILTER "="), &arg)) { parse_list_objects_filter(&args.filter_options, arg); continue; } if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) { list_objects_filter_set_no_filter(&args.filter_options); continue; } usage(fetch_pack_usage); } if (deepen_not.nr) args.deepen_not = &deepen_not; if (i < argc) dest = argv[i++]; else usage(fetch_pack_usage); /* * Copy refs from cmdline to growable list, then append any * refs from the standard input: */ for (; i < argc; i++) add_sought_entry(&sought, &nr_sought, &alloc_sought, argv[i]); if (args.stdin_refs) { if (args.stateless_rpc) { /* in stateless RPC mode we use pkt-line to read * from stdin, until we get a flush packet */ for (;;) { char *line = packet_read_line(0, NULL); if (!line) break; add_sought_entry(&sought, &nr_sought, &alloc_sought, line); } } else { /* read from stdin one ref per line, until EOF */ struct strbuf line = STRBUF_INIT; while (strbuf_getline_lf(&line, stdin) != EOF) add_sought_entry(&sought, &nr_sought, &alloc_sought, line.buf); strbuf_release(&line); } } if (args.stateless_rpc) { conn = NULL; fd[0] = 0; fd[1] = 1; } else { int flags = args.verbose ? CONNECT_VERBOSE : 0; if (args.diag_url) flags |= CONNECT_DIAG_URL; conn = git_connect(fd, dest, args.uploadpack, flags); if (!conn) return args.diag_url ? 0 : 1; } packet_reader_init(&reader, fd[0], NULL, 0, PACKET_READ_CHOMP_NEWLINE | PACKET_READ_GENTLE_ON_EOF | PACKET_READ_DIE_ON_ERR_PACKET); version = discover_version(&reader); switch (version) { case protocol_v2: get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL); break; case protocol_v1: case protocol_v0: get_remote_heads(&reader, &ref, 0, NULL, &shallow); break; case protocol_unknown_version: BUG("unknown protocol version"); } ref = fetch_pack(&args, fd, conn, ref, dest, sought, nr_sought, &shallow, pack_lockfile_ptr, version); if (pack_lockfile) { printf("lock %s\n", pack_lockfile); fflush(stdout); } if (args.check_self_contained_and_connected && args.self_contained_and_connected) { printf("connectivity-ok\n"); fflush(stdout); } close(fd[0]); close(fd[1]); if (finish_connect(conn)) return 1; ret = !ref; /* * If the heads to pull were given, we should have consumed * all of them by matching the remote. Otherwise, 'git fetch * remote no-such-ref' would silently succeed without issuing * an error. */ ret |= report_unmatched_refs(sought, nr_sought); while (ref) { printf("%s %s\n", oid_to_hex(&ref->old_oid), ref->name); ref = ref->next; } return ret; }
static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; struct string_list deepen_not = STRING_LIST_INIT_DUP; int depth = 0; int has_non_tip = 0; timestamp_t deepen_since = 0; int deepen_rev_list = 0; shallow_nr = 0; for (;;) { struct object *o; const char *features; struct object_id oid_buf; char *line = packet_read_line(0, NULL); const char *arg; reset_timeout(); if (!line) break; if (process_shallow(line, &shallows)) continue; if (process_deepen(line, &depth)) continue; if (process_deepen_since(line, &deepen_since, &deepen_rev_list)) continue; if (process_deepen_not(line, &deepen_not, &deepen_rev_list)) continue; if (skip_prefix(line, "filter ", &arg)) { if (!filter_capability_requested) die("git upload-pack: filtering capability not negotiated"); parse_list_objects_filter(&filter_options, arg); continue; } if (!skip_prefix(line, "want ", &arg) || parse_oid_hex(arg, &oid_buf, &features)) die("git upload-pack: protocol error, " "expected to get object ID, not '%s'", line); if (parse_feature_request(features, "deepen-relative")) deepen_relative = 1; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; if (parse_feature_request(features, "no-done")) no_done = 1; if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (parse_feature_request(features, "no-progress")) no_progress = 1; if (parse_feature_request(features, "include-tag")) use_include_tag = 1; if (allow_filter && parse_feature_request(features, "filter")) filter_capability_requested = 1; o = parse_object(&oid_buf); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", oid_to_hex(&oid_buf)); die("git upload-pack: not our ref %s", oid_to_hex(&oid_buf)); } if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!((allow_unadvertised_object_request & ALLOW_ANY_SHA1) == ALLOW_ANY_SHA1 || is_our_ref(o))) has_non_tip = 1; add_object_array(o, NULL, &want_obj); } } /* * We have sent all our refs already, and the other end * should have chosen out of them. When we are operating * in the stateless RPC mode, however, their choice may * have been based on the set of older refs advertised * by another process that handled the initial request. */ if (has_non_tip) check_non_tip(); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && !deepen_rev_list && shallows.nr == 0) return; if (send_shallow_list(depth, deepen_rev_list, deepen_since, &deepen_not, &shallows)) packet_flush(1); object_array_clear(&shallows); }
static void receive_needs(void) { struct object_array shallows = OBJECT_ARRAY_INIT; int depth = 0; int has_non_tip = 0; shallow_nr = 0; for (;;) { struct object *o; const char *features; unsigned char sha1_buf[20]; char *line = packet_read_line(0, NULL); reset_timeout(); if (!line) break; if (starts_with(line, "shallow ")) { unsigned char sha1[20]; struct object *object; if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); object = parse_object(sha1); if (!object) continue; if (object->type != OBJ_COMMIT) die("invalid shallow object %s", sha1_to_hex(sha1)); if (!(object->flags & CLIENT_SHALLOW)) { object->flags |= CLIENT_SHALLOW; add_object_array(object, NULL, &shallows); } continue; } if (starts_with(line, "deepen ")) { char *end; depth = strtol(line + 7, &end, 0); if (end == line + 7 || depth <= 0) die("Invalid deepen: %s", line); continue; } if (!starts_with(line, "want ") || get_sha1_hex(line+5, sha1_buf)) die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); features = line + 45; if (parse_feature_request(features, "multi_ack_detailed")) multi_ack = 2; else if (parse_feature_request(features, "multi_ack")) multi_ack = 1; if (parse_feature_request(features, "no-done")) no_done = 1; if (parse_feature_request(features, "thin-pack")) use_thin_pack = 1; if (parse_feature_request(features, "ofs-delta")) use_ofs_delta = 1; if (parse_feature_request(features, "side-band-64k")) use_sideband = LARGE_PACKET_MAX; else if (parse_feature_request(features, "side-band")) use_sideband = DEFAULT_PACKET_MAX; if (parse_feature_request(features, "no-progress")) no_progress = 1; if (parse_feature_request(features, "include-tag")) use_include_tag = 1; o = parse_object(sha1_buf); if (!o) die("git upload-pack: not our ref %s", sha1_to_hex(sha1_buf)); if (!(o->flags & WANTED)) { o->flags |= WANTED; if (!is_our_ref(o)) has_non_tip = 1; add_object_array(o, NULL, &want_obj); } } /* * We have sent all our refs already, and the other end * should have chosen out of them. When we are operating * in the stateless RPC mode, however, their choice may * have been based on the set of older refs advertised * by another process that handled the initial request. */ if (has_non_tip) check_non_tip(); if (!use_sideband && daemon_mode) no_progress = 1; if (depth == 0 && shallows.nr == 0) return; if (depth > 0) { struct commit_list *result = NULL, *backup = NULL; int i; if (depth == INFINITE_DEPTH && !is_repository_shallow()) for (i = 0; i < shallows.nr; i++) { struct object *object = shallows.objects[i].item; object->flags |= NOT_SHALLOW; } else backup = result = get_shallow_commits(&want_obj, depth, SHALLOW, NOT_SHALLOW); while (result) { struct object *object = &result->item->object; if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write(1, "shallow %s", sha1_to_hex(object->sha1)); register_shallow(object->sha1); shallow_nr++; } result = result->next; } free_commit_list(backup); for (i = 0; i < shallows.nr; i++) { struct object *object = shallows.objects[i].item; if (object->flags & NOT_SHALLOW) { struct commit_list *parents; packet_write(1, "unshallow %s", sha1_to_hex(object->sha1)); object->flags &= ~CLIENT_SHALLOW; /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; parse_commit_or_die((struct commit *)object); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, NULL, &want_obj); parents = parents->next; } add_object_array(object, NULL, &extra_edge_obj); } /* make sure commit traversal conforms to client */ register_shallow(object->sha1); } packet_flush(1); } else if (shallows.nr > 0) { int i; for (i = 0; i < shallows.nr; i++) register_shallow(shallows.objects[i].item->sha1); } shallow_nr += shallows.nr; free(shallows.objects); }
static int find_common(int fd[2], unsigned char *result_sha1, struct ref *refs) { int fetching; int count = 0, flushes = 0, retval; const unsigned char *sha1; unsigned in_vain = 0; int got_continue = 0; if (marked) for_each_ref(clear_marks, NULL); marked = 1; for_each_ref(rev_list_insert_ref, NULL); fetching = 0; for ( ; refs ; refs = refs->next) { unsigned char *remote = refs->old_sha1; 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. */ if (((o = lookup_object(remote)) != NULL) && (o->flags & COMPLETE)) { continue; } if (!fetching) packet_write(fd[1], "want %s%s%s%s%s%s%s%s\n", sha1_to_hex(remote), (multi_ack ? " multi_ack" : ""), (use_sideband == 2 ? " side-band-64k" : ""), (use_sideband == 1 ? " side-band" : ""), (args.use_thin_pack ? " thin-pack" : ""), (args.no_progress ? " no-progress" : ""), (args.include_tag ? " include-tag" : ""), " ofs-delta"); else packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } if (is_repository_shallow()) write_shallow_commits(fd[1], 1); if (args.depth > 0) packet_write(fd[1], "deepen %d", args.depth); packet_flush(fd[1]); if (!fetching) return 1; if (args.depth > 0) { char line[1024]; unsigned char sha1[20]; while (packet_read_line(fd[0], line, sizeof(line))) { if (!prefixcmp(line, "shallow ")) { if (get_sha1_hex(line + 8, sha1)) die("invalid shallow line: %s", line); register_shallow(sha1); continue; } if (!prefixcmp(line, "unshallow ")) { if (get_sha1_hex(line + 10, sha1)) die("invalid unshallow line: %s", line); if (!lookup_object(sha1)) die("object not found: %s", line); /* make sure that it is parsed as shallow */ if (!parse_object(sha1)) die("error in object: %s", line); if (unregister_shallow(sha1)) die("no shallow found: %s", line); continue; } die("expected shallow/unshallow, got %s", line); } } flushes = 0; retval = -1; while ((sha1 = get_rev())) { packet_write(fd[1], "have %s\n", sha1_to_hex(sha1)); if (args.verbose) fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); in_vain++; if (!(31 & ++count)) { int ack; packet_flush(fd[1]); flushes++; /* * We keep one window "ahead" of the other side, and * will wait for an ACK only on the next one */ if (count == 32) continue; do { ack = get_ack(fd[0], result_sha1); if (args.verbose && ack) fprintf(stderr, "got ack %d %s\n", ack, sha1_to_hex(result_sha1)); if (ack == 1) { flushes = 0; multi_ack = 0; retval = 0; goto done; } else if (ack == 2) { struct commit *commit = lookup_commit(result_sha1); mark_common(commit, 0, 1); retval = 0; in_vain = 0; got_continue = 1; } } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { if (args.verbose) fprintf(stderr, "giving up\n"); break; /* give up */ } } } done: packet_write(fd[1], "done\n"); if (args.verbose) fprintf(stderr, "done\n"); if (retval != 0) { multi_ack = 0; flushes++; } while (flushes || multi_ack) { int ack = get_ack(fd[0], result_sha1); if (ack) { if (args.verbose) fprintf(stderr, "got ack (%d) %s\n", ack, sha1_to_hex(result_sha1)); if (ack == 1) return 0; multi_ack = 1; continue; } flushes--; } /* it is no error to fetch into a completely empty repo */ return count ? retval : 0; }
static int find_common(struct fetch_pack_args *args, int fd[2], unsigned char *result_sha1, struct ref *refs) { int fetching; int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval; const unsigned char *sha1; unsigned in_vain = 0; int got_continue = 0; int got_ready = 0; struct strbuf req_buf = STRBUF_INIT; size_t state_len = 0; if (args->stateless_rpc && multi_ack == 1) die("--stateless-rpc requires multi_ack_detailed"); if (marked) for_each_ref(clear_marks, NULL); marked = 1; for_each_ref(rev_list_insert_ref, NULL); for_each_alternate_ref(insert_one_alternate_ref, NULL); fetching = 0; for ( ; refs ; refs = refs->next) { unsigned char *remote = refs->old_sha1; 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. */ if (((o = lookup_object(remote)) != NULL) && (o->flags & COMPLETE)) { continue; } remote_hex = sha1_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->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 (agent_supported) strbuf_addf(&c, " agent=%s", git_user_agent_sanitized()); 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()) write_shallow_commits(&req_buf, 1, NULL); if (args->depth > 0) packet_buf_write(&req_buf, "deepen %d", args->depth); packet_buf_flush(&req_buf); state_len = req_buf.len; if (args->depth > 0) { char *line; const char *arg; unsigned char sha1[20]; send_request(args, fd[1], &req_buf); while ((line = packet_read_line(fd[0], NULL))) { if (skip_prefix(line, "shallow ", &arg)) { if (get_sha1_hex(arg, sha1)) die("invalid shallow line: %s", line); register_shallow(sha1); continue; } if (skip_prefix(line, "unshallow ", &arg)) { if (get_sha1_hex(arg, sha1)) die("invalid unshallow line: %s", line); if (!lookup_object(sha1)) die("object not found: %s", line); /* make sure that it is parsed as shallow */ if (!parse_object(sha1)) die("error in object: %s", line); if (unregister_shallow(sha1)) die("no shallow found: %s", line); continue; } die("expected shallow/unshallow, got %s", 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; while ((sha1 = get_rev())) { packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1)); if (args->verbose) fprintf(stderr, "have %s\n", sha1_to_hex(sha1)); 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, 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, fd[0]); do { ack = get_ack(fd[0], result_sha1); if (args->verbose && ack) fprintf(stderr, "got ack %d %s\n", ack, sha1_to_hex(result_sha1)); 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(result_sha1); if (!commit) die("invalid commit %s", sha1_to_hex(result_sha1)); if (args->stateless_rpc && ack == ACK_common && !(commit->object.flags & 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 = sha1_to_hex(result_sha1); packet_buf_write(&req_buf, "have %s\n", hex); state_len = req_buf.len; } mark_common(commit, 0, 1); retval = 0; in_vain = 0; got_continue = 1; if (ack == ACK_ready) { clear_prio_queue(&rev_list); got_ready = 1; } break; } } } while (ack); flushes--; if (got_continue && MAX_IN_VAIN < in_vain) { if (args->verbose) fprintf(stderr, "giving up\n"); break; /* give up */ } } } done: if (!got_ready || !no_done) { packet_buf_write(&req_buf, "done\n"); send_request(args, fd[1], &req_buf); } if (args->verbose) fprintf(stderr, "done\n"); if (retval != 0) { multi_ack = 0; flushes++; } strbuf_release(&req_buf); if (!got_ready || !no_done) consume_shallow_list(args, fd[0]); while (flushes || multi_ack) { int ack = get_ack(fd[0], result_sha1); if (ack) { if (args->verbose) fprintf(stderr, "got ack (%d) %s\n", ack, sha1_to_hex(result_sha1)); 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; }
static int execute(struct sockaddr *addr) { static char line[1000]; int pktlen, len, i; if (addr) { char addrbuf[256] = ""; int port = -1; if (addr->sa_family == AF_INET) { struct sockaddr_in *sin_addr = (void *) addr; inet_ntop(addr->sa_family, &sin_addr->sin_addr, addrbuf, sizeof(addrbuf)); port = ntohs(sin_addr->sin_port); #ifndef NO_IPV6 } else if (addr && addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin6_addr = (void *) addr; char *buf = addrbuf; *buf++ = '['; *buf = '\0'; /* stpcpy() is cool */ inet_ntop(AF_INET6, &sin6_addr->sin6_addr, buf, sizeof(addrbuf) - 1); strcat(buf, "]"); port = ntohs(sin6_addr->sin6_port); #endif } loginfo("Connection from %s:%d", addrbuf, port); } alarm(init_timeout ? init_timeout : timeout); pktlen = packet_read_line(0, line, sizeof(line)); alarm(0); len = strlen(line); if (pktlen != len) loginfo("Extended attributes (%d bytes) exist <%.*s>", (int) pktlen - len, (int) pktlen - len, line + len + 1); if (len && line[len-1] == '\n') { line[--len] = 0; pktlen--; } /* * Initialize the path interpolation table for this connection. */ interp_clear_table(interp_table, ARRAY_SIZE(interp_table)); interp_set_entry(interp_table, INTERP_SLOT_PERCENT, "%"); if (len != pktlen) { parse_extra_args(interp_table, line + len + 1, pktlen - len - 1); fill_in_extra_table_entries(interp_table); } for (i = 0; i < ARRAY_SIZE(daemon_service); i++) { struct daemon_service *s = &(daemon_service[i]); int namelen = strlen(s->name); if (!prefixcmp(line, "git-") && !strncmp(s->name, line + 4, namelen) && line[namelen + 4] == ' ') { /* * Note: The directory here is probably context sensitive, * and might depend on the actual service being performed. */ interp_set_entry(interp_table, INTERP_SLOT_DIR, line + namelen + 5); return run_service(interp_table, s); } } logerror("Protocol error: '%s'", line); return -1; }