int cmd_upload_archive(int argc, const char **argv, const char *prefix) { struct child_process writer = { argv }; /* * Set up sideband subprocess. * * We (parent) monitor and read from child, sending its fd#1 and fd#2 * multiplexed out to our fd#1. If the child dies, we tell the other * end over channel #3. */ argv[0] = "upload-archive--writer"; writer.out = writer.err = -1; writer.git_cmd = 1; if (start_command(&writer)) { int err = errno; packet_write_fmt(1, "NACK unable to spawn subprocess\n"); die("upload-archive: %s", strerror(err)); } packet_write_fmt(1, "ACK\n"); packet_flush(1); while (1) { struct pollfd pfd[2]; pfd[0].fd = writer.out; pfd[0].events = POLLIN; pfd[1].fd = writer.err; pfd[1].events = POLLIN; if (poll(pfd, 2, -1) < 0) { if (errno != EINTR) { error_errno("poll failed resuming"); sleep(1); } continue; } if (pfd[1].revents & POLLIN) /* Status stream ready */ if (process_input(pfd[1].fd, 2)) continue; if (pfd[0].revents & POLLIN) /* Data stream ready */ if (process_input(pfd[0].fd, 1)) continue; if (finish_command(&writer)) error_clnt("%s", deadchild); packet_flush(1); break; } return 0; }
static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec, const char *name_hint) { char *buf; int fd[2], i, rv; struct transport *transport; struct remote *_remote; _remote = remote_get(remote); if (!_remote->url[0]) die(_("git archive: Remote with no URL")); transport = transport_get(_remote, _remote->url[0]); transport_connect(transport, "git-upload-archive", exec, fd); /* * Inject a fake --format field at the beginning of the * arguments, with the format inferred from our output * filename. This way explicit --format options can override * it. */ if (name_hint) { const char *format = archive_format_from_filename(name_hint); if (format) packet_write_fmt(fd[1], "argument --format=%s\n", format); } for (i = 1; i < argc; i++) packet_write_fmt(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); buf = packet_read_line(fd[0], NULL); if (!buf) die(_("git archive: expected ACK/NAK, got a flush packet")); if (strcmp(buf, "ACK")) { if (starts_with(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); if (starts_with(buf, "ERR ")) die(_("remote error: %s"), buf + 4); die(_("git archive: protocol error")); } if (packet_read_line(fd[0], NULL)) die(_("git archive: expected a flush")); /* Now, start reading from fd[0] and spit it out to stdout */ rv = recv_sideband("archive", fd[0], 1); rv |= transport_disconnect(transport); return !!rv; }
static void get_info_refs(struct strbuf *hdr, char *arg) { const char *service_name = get_parameter("service"); struct strbuf buf = STRBUF_INIT; hdr_nocache(hdr); if (service_name) { const char *argv[] = {NULL /* service name */, "--stateless-rpc", "--advertise-refs", ".", NULL}; struct rpc_service *svc = select_service(hdr, service_name); strbuf_addf(&buf, "application/x-git-%s-advertisement", svc->name); hdr_str(hdr, content_type, buf.buf); end_headers(hdr); packet_write_fmt(1, "# service=git-%s\n", svc->name); packet_flush(1); argv[0] = svc->name; run_service(argv, 0); } else { select_getanyfile(hdr); for_each_namespaced_ref(show_text_ref, &buf); send_strbuf(hdr, "text/plain", &buf); } strbuf_release(&buf); }
static int daemon_error(const char *dir, const char *msg) { if (!informative_errors) msg = "access denied or repository not exported"; packet_write_fmt(1, "ERR %s: %s", msg, dir); return -1; }
static void send_unshallow(const struct object_array *shallows) { int i; 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_fmt(1, "unshallow %s", oid_to_hex(&object->oid)); object->flags &= ~CLIENT_SHALLOW; /* * We want to _register_ "object" as shallow, but we * also need to traverse object's parents to deepen a * shallow clone. Unregister it for now so we can * parse and add the parents to the want list, then * re-register it. */ unregister_shallow(&object->oid); 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(the_repository, &object->oid); } }
static int parse_want(const char *line) { const char *arg; if (skip_prefix(line, "want ", &arg)) { struct object_id oid; struct object *o; if (get_oid_hex(arg, &oid)) die("git upload-pack: protocol error, " "expected to get oid, not '%s'", line); o = parse_object(&oid); if (!o) { packet_write_fmt(1, "ERR upload-pack: not our ref %s", oid_to_hex(&oid)); die("git upload-pack: not our ref %s", oid_to_hex(&oid)); } if (!(o->flags & WANTED)) { o->flags |= WANTED; add_object_array(o, NULL, &want_obj); } return 1; } return 0; }
int upload_pack_v2(struct repository *r, struct argv_array *keys, struct packet_reader *request) { enum fetch_state state = FETCH_PROCESS_ARGS; struct upload_pack_data data; git_config(upload_pack_config, NULL); upload_pack_data_init(&data); use_sideband = LARGE_PACKET_MAX; while (state != FETCH_DONE) { switch (state) { case FETCH_PROCESS_ARGS: process_args(request, &data); if (!want_obj.nr) { /* * Request didn't contain any 'want' lines, * guess they didn't want anything. */ state = FETCH_DONE; } else if (data.haves.nr) { /* * Request had 'have' lines, so lets ACK them. */ state = FETCH_SEND_ACKS; } else { /* * Request had 'want's but no 'have's so we can * immedietly go to construct and send a pack. */ state = FETCH_SEND_PACK; } break; case FETCH_SEND_ACKS: if (process_haves_and_send_acks(&data)) state = FETCH_SEND_PACK; else state = FETCH_DONE; break; case FETCH_SEND_PACK: send_shallow_info(&data); packet_write_fmt(1, "packfile\n"); create_pack_file(); state = FETCH_DONE; break; case FETCH_DONE: continue; } } upload_pack_data_clear(&data); return 0; }
int cmd_main(int argc, const char **argv) { const char *dir; int strict = 0; struct option options[] = { OPT_BOOL(0, "stateless-rpc", &stateless_rpc, N_("quit after a single request/response exchange")), OPT_BOOL(0, "advertise-refs", &advertise_refs, N_("exit immediately after initial ref advertisement")), OPT_BOOL(0, "strict", &strict, N_("do not try <directory>/.git/ if <directory> is no Git directory")), OPT_INTEGER(0, "timeout", &timeout, N_("interrupt transfer after <n> seconds of inactivity")), OPT_END() }; packet_trace_identity("upload-pack"); check_replace_refs = 0; argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0); if (argc != 1) usage_with_options(upload_pack_usage, options); if (timeout) daemon_mode = 1; setup_path(); dir = argv[0]; if (!enter_repo(dir, strict)) die("'%s' does not appear to be a git repository", dir); git_config(upload_pack_config, NULL); switch (determine_protocol_version_server()) { case protocol_v1: /* * v1 is just the original protocol with a version string, * so just fall through after writing the version string. */ if (advertise_refs || !stateless_rpc) packet_write_fmt(1, "version 1\n"); /* fallthrough */ case protocol_v0: upload_pack(); break; case protocol_unknown_version: BUG("unknown protocol version"); } return 0; }
/* * Serialize the list of changes to the given file. The goal of this * is to just serialize the key fields in wt_status so that a * later command can rebuilt it and do the printing. * * We DO NOT include the contents of wt_status_state NOR * current branch info. This info easily gets stale and * is relatively quick for the status consumer to compute * as necessary. */ void wt_status_serialize_v1(int fd, struct wt_status *s) { struct string_list_item *iter; int k; /* * version header must be first line. */ packet_write_fmt(fd, "version 1\n"); wt_serialize_v1_header(s, fd); if (s->change.nr > 0) { packet_write_fmt(fd, "changed %d\n", s->change.nr); for (k = 0; k < s->change.nr; k++) { iter = &(s->change.items[k]); wt_serialize_v1_changed(s, fd, iter); } packet_flush(fd); } if (s->untracked.nr > 0) { packet_write_fmt(fd, "untracked %d\n", s->untracked.nr); for (k = 0; k < s->untracked.nr; k++) { iter = &(s->untracked.items[k]); wt_serialize_v1_untracked(s, fd, iter); } packet_flush(fd); } if (s->ignored.nr > 0) { packet_write_fmt(fd, "ignored %d\n", s->ignored.nr); for (k = 0; k < s->ignored.nr; k++) { iter = &(s->ignored.items[k]); wt_serialize_v1_ignored(s, fd, iter); } packet_flush(fd); } }
static void send_shallow(struct commit_list *result) { while (result) { struct object *object = &result->item->object; if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) { packet_write_fmt(1, "shallow %s", oid_to_hex(&object->oid)); register_shallow(the_repository, &object->oid); shallow_nr++; } result = result->next; } }
static int send_ref(const char *refname, const struct object_id *oid, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" " side-band-64k ofs-delta shallow deepen-since deepen-not" " deepen-relative no-progress include-tag multi_ack_detailed"; const char *refname_nons = strip_namespace(refname); struct object_id peeled; if (mark_our_ref(refname_nons, refname, oid)) return 0; if (capabilities) { struct strbuf symref_info = STRBUF_INIT; format_symref_info(&symref_info, cb_data); packet_write_fmt(1, "%s %s%c%s%s%s%s%s%s agent=%s\n", oid_to_hex(oid), refname_nons, 0, capabilities, (allow_unadvertised_object_request & ALLOW_TIP_SHA1) ? " allow-tip-sha1-in-want" : "", (allow_unadvertised_object_request & ALLOW_REACHABLE_SHA1) ? " allow-reachable-sha1-in-want" : "", stateless_rpc ? " no-done" : "", symref_info.buf, allow_filter ? " filter" : "", git_user_agent_sanitized()); strbuf_release(&symref_info); } else { packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons); } capabilities = NULL; if (!peel_ref(refname, &peeled)) packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons); return 0; }
static void send_shallow_info(struct upload_pack_data *data) { /* No shallow info needs to be sent */ if (!data->depth && !data->deepen_rev_list && !data->shallows.nr && !is_repository_shallow(the_repository)) return; packet_write_fmt(1, "shallow-info\n"); if (!send_shallow_list(data->depth, data->deepen_rev_list, data->deepen_since, &data->deepen_not, &data->shallows) && is_repository_shallow(the_repository)) deepen(INFINITE_DEPTH, data->deepen_relative, &data->shallows); packet_delim(1); }
/* * Write V1 header fields. */ static void wt_serialize_v1_header(struct wt_status *s, int fd) { /* * Write select fields from the current index to help * the deserializer recognize a stale data set. */ packet_write_fmt(fd, "index_mtime %d %d\n", the_index.timestamp.sec, the_index.timestamp.nsec); /* * Write data from wt_status to qualify this status report. * That is, if this run specified "-uno", the consumer of * our serialization should know that. */ packet_write_fmt(fd, "is_initial %d\n", s->is_initial); if (s->branch) packet_write_fmt(fd, "branch %s\n", s->branch); if (s->reference) packet_write_fmt(fd, "reference %s\n", s->reference); /* pathspec */ /* verbose */ /* amend */ packet_write_fmt(fd, "whence %d\n", s->whence); /* nowarn */ /* use_color */ /* no_gettext */ /* display_comment_prefix */ /* relative_paths */ /* submodule_summary */ packet_write_fmt(fd, "show_ignored_mode %d\n", s->show_ignored_mode); packet_write_fmt(fd, "show_untracked_files %d\n", s->show_untracked_files); if (s->ignore_submodule_arg) packet_write_fmt(fd, "ignore_submodule_arg %s\n", s->ignore_submodule_arg); /* color_palette */ /* colopts */ /* null_termination */ /* commit_template */ /* show_branch */ /* show_stash */ packet_write_fmt(fd, "hints %d\n", s->hints); packet_write_fmt(fd, "detect_rename %d\n", s->detect_rename); packet_write_fmt(fd, "rename_score %d\n", s->rename_score); packet_write_fmt(fd, "rename_limit %d\n", s->rename_limit); /* status_format */ packet_write_fmt(fd, "sha1_commit %s\n", sha1_to_hex(s->sha1_commit)); packet_write_fmt(fd, "commitable %d\n", s->commitable); packet_write_fmt(fd, "workdir_dirty %d\n", s->workdir_dirty); /* prefix */ packet_flush(fd); }
/* * Write raw (not c-quoted) pathname for an ignored item. * We ALWAYS write a final LF to the packet-line (for debugging) * even though Linux pathnames allows LFs. */ static inline void wt_serialize_v1_ignored(struct wt_status *s, int fd, struct string_list_item *item) { packet_write_fmt(fd, "%s\n", item->string); }
/* * This returns a dummy child_process if the transport protocol does not * need fork(2), or a struct child_process object if it does. Once done, * finish the connection with finish_connect() with the value returned from * this function (it is safe to call finish_connect() with NULL to support * the former case). * * If it returns, the connect is successful; it just dies on errors (this * will hopefully be changed in a libification effort, to return NULL when * the connection failed). */ struct child_process *git_connect(int fd[2], const char *url, const char *prog, int flags) { char *hostandport, *path; struct child_process *conn = &no_fork; enum protocol protocol; struct strbuf cmd = STRBUF_INIT; /* Without this we cannot rely on waitpid() to tell * what happened to our children. */ signal(SIGCHLD, SIG_DFL); protocol = parse_connect_url(url, &hostandport, &path); if ((flags & CONNECT_DIAG_URL) && (protocol != PROTO_SSH)) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: hostandport=%s\n", hostandport ? hostandport : "NULL"); printf("Diag: path=%s\n", path ? path : "NULL"); conn = NULL; } else if (protocol == PROTO_GIT) { /* * Set up virtual host information based on where we will * connect, unless the user has overridden us in * the environment. */ char *target_host = getenv("GIT_OVERRIDE_VIRTUAL_HOST"); if (target_host) target_host = xstrdup(target_host); else target_host = xstrdup(hostandport); transport_check_allowed("git"); /* These underlying connection commands die() if they * cannot connect. */ if (git_use_proxy(hostandport)) conn = git_proxy_connect(fd, hostandport); else git_tcp_connect(fd, hostandport, flags); /* * Separate original protocol components prog and path * from extended host header with a NUL byte. * * Note: Do not add any other headers here! Doing so * will cause older git-daemon servers to crash. */ packet_write_fmt(fd[1], "%s %s%chost=%s%c", prog, path, 0, target_host, 0); free(target_host); } else { conn = xmalloc(sizeof(*conn)); child_process_init(conn); strbuf_addstr(&cmd, prog); strbuf_addch(&cmd, ' '); sq_quote_buf(&cmd, path); /* remove repo-local variables from the environment */ conn->env = local_repo_env; conn->use_shell = 1; conn->in = conn->out = -1; if (protocol == PROTO_SSH) { const char *ssh; int putty = 0, tortoiseplink = 0; char *ssh_host = hostandport; const char *port = NULL; transport_check_allowed("ssh"); get_host_and_port(&ssh_host, &port); if (!port) port = get_port(ssh_host); if (flags & CONNECT_DIAG_URL) { printf("Diag: url=%s\n", url ? url : "NULL"); printf("Diag: protocol=%s\n", prot_name(protocol)); printf("Diag: userandhost=%s\n", ssh_host ? ssh_host : "NULL"); printf("Diag: port=%s\n", port ? port : "NONE"); printf("Diag: path=%s\n", path ? path : "NULL"); free(hostandport); free(path); free(conn); return NULL; } ssh = get_ssh_command(); if (!ssh) { const char *base; char *ssh_dup; /* * GIT_SSH is the no-shell version of * GIT_SSH_COMMAND (and must remain so for * historical compatibility). */ conn->use_shell = 0; ssh = getenv("GIT_SSH"); if (!ssh) ssh = "ssh"; ssh_dup = xstrdup(ssh); base = basename(ssh_dup); tortoiseplink = !strcasecmp(base, "tortoiseplink") || !strcasecmp(base, "tortoiseplink.exe"); putty = tortoiseplink || !strcasecmp(base, "plink") || !strcasecmp(base, "plink.exe"); free(ssh_dup); } argv_array_push(&conn->args, ssh); if (flags & CONNECT_IPV4) argv_array_push(&conn->args, "-4"); else if (flags & CONNECT_IPV6) argv_array_push(&conn->args, "-6"); if (tortoiseplink) argv_array_push(&conn->args, "-batch"); if (port) { /* P is for PuTTY, p is for OpenSSH */ argv_array_push(&conn->args, putty ? "-P" : "-p"); argv_array_push(&conn->args, port); } argv_array_push(&conn->args, ssh_host); } else { transport_check_allowed("file"); } argv_array_push(&conn->args, cmd.buf); if (start_command(conn)) die("unable to fork"); fd[0] = conn->out; /* read from child's stdout */ fd[1] = conn->in; /* write to child's stdin */ strbuf_release(&cmd); } free(hostandport); free(path); return conn; }
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; 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); }
static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out, const struct fetch_pack_args *args, const struct ref *wants, struct oidset *common, int *haves_to_send, int *in_vain) { int ret = 0; struct strbuf req_buf = STRBUF_INIT; if (server_supports_v2("fetch", 1)) packet_buf_write(&req_buf, "command=fetch"); if (server_supports_v2("agent", 0)) packet_buf_write(&req_buf, "agent=%s", git_user_agent_sanitized()); if (args->server_options && args->server_options->nr && server_supports_v2("server-option", 1)) { int i; for (i = 0; i < args->server_options->nr; i++) packet_write_fmt(fd_out, "server-option=%s", args->server_options->items[i].string); } packet_buf_delim(&req_buf); if (args->use_thin_pack) packet_buf_write(&req_buf, "thin-pack"); if (args->no_progress) packet_buf_write(&req_buf, "no-progress"); if (args->include_tag) packet_buf_write(&req_buf, "include-tag"); if (prefer_ofs_delta) packet_buf_write(&req_buf, "ofs-delta"); /* Add shallow-info and deepen request */ if (server_supports_feature("fetch", "shallow", 0)) add_shallow_requests(&req_buf, args); else if (is_repository_shallow(the_repository) || args->deepen) die(_("Server does not support shallow requests")); /* Add filter */ if (server_supports_feature("fetch", "filter", 0) && args->filter_options.choice) { print_verbose(args, _("Server supports filter")); packet_buf_write(&req_buf, "filter %s", args->filter_options.filter_spec); } else if (args->filter_options.choice) { warning("filtering not recognized by server, ignoring"); } /* add wants */ add_wants(args->no_dependents, wants, &req_buf); if (args->no_dependents) { packet_buf_write(&req_buf, "done"); ret = 1; } else { /* Add all of the common commits we've found in previous rounds */ add_common(&req_buf, common); /* Add initial haves */ ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain); } /* Send request */ packet_buf_flush(&req_buf); write_or_die(fd_out, req_buf.buf, req_buf.len); strbuf_release(&req_buf); return ret; }
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); } }