int cmd_upload_archive(int argc, const char **argv, const char *prefix) { const char *sent_argv[MAX_ARGS]; struct child_process cld = { sent_argv }; cld.out = cld.err = -1; cld.git_cmd = 1; if (argc != 2) usage(upload_archive_usage); if (!enter_repo(argv[1], 0)) die("'%s' does not appear to be a git repository", argv[1]); prepare_argv(sent_argv, argv); if (start_command(&cld)) { int err = errno; packet_write(1, "NACK fork failed on the remote side\n"); die("upload-archive: %s", strerror(err)); } /* parent - read from child, multiplex and send out to fd#1 */ packet_write(1, "ACK\n"); packet_flush(1); while (1) { struct pollfd pfd[2]; int status; pfd[0].fd = cld.out; pfd[0].events = POLLIN; pfd[1].fd = cld.err; pfd[1].events = POLLIN; if (poll(pfd, 2, -1) < 0) { if (errno != EINTR) { error("poll failed resuming: %s", strerror(errno)); 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 (waitpid(cld.pid, &status, 0) < 0) error_clnt("%s", lostchild); else if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) error_clnt("%s", deadchild); packet_flush(1); break; } return 0; }
static struct ref *do_fetch_pack(int fd[2], const struct ref *orig_ref, int nr_match, char **match, char **pack_lockfile) { struct ref *ref = copy_ref_list(orig_ref); unsigned char sha1[20]; if (is_repository_shallow() && !server_supports("shallow")) die("Server does not support shallow clients"); if (server_supports("multi_ack_detailed")) { if (args.verbose) fprintf(stderr, "Server supports multi_ack_detailed\n"); multi_ack = 2; } else if (server_supports("multi_ack")) { if (args.verbose) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } if (server_supports("side-band-64k")) { if (args.verbose) fprintf(stderr, "Server supports side-band-64k\n"); use_sideband = 2; } else if (server_supports("side-band")) { if (args.verbose) fprintf(stderr, "Server supports side-band\n"); use_sideband = 1; } if (server_supports("ofs-delta")) { if (args.verbose) fprintf(stderr, "Server supports ofs-delta\n"); } else prefer_ofs_delta = 0; if (everything_local(&ref, nr_match, match)) { packet_flush(fd[1]); goto all_done; } if (find_common(fd, sha1, ref) < 0) if (!args.keep_pack) /* When cloning, it is not unusual to have * no common commit. */ warning("no common commits"); if (args.stateless_rpc) packet_flush(fd[1]); if (get_pack(fd, pack_lockfile)) die("git fetch-pack: fetch failed."); all_done: return ref; }
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(1, "NACK unable to spawn subprocess\n"); die("upload-archive: %s", strerror(err)); } packet_write(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("poll failed resuming: %s", strerror(errno)); 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; }
struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, struct ref **sought, int nr_sought, struct sha1_array *shallow, char **pack_lockfile) { struct ref *ref_cpy; struct shallow_info si; fetch_pack_setup(); if (nr_sought) nr_sought = remove_duplicates_in_refs(sought, nr_sought); if (!ref) { packet_flush(fd[1]); die("no matching remote head"); } prepare_shallow_info(&si, shallow); ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, &si, pack_lockfile); reprepare_packed_git(); update_shallow(args, sought, nr_sought, &si); clear_shallow_info(&si); return ref_cpy; }
static void upload_pack(void) { struct string_list symref = STRING_LIST_INIT_DUP; head_ref_namespaced(find_symref, &symref); if (advertise_refs || !stateless_rpc) { reset_timeout(); head_ref_namespaced(send_ref, &symref); for_each_namespaced_ref(send_ref, &symref); advertise_shallow_grafts(1); packet_flush(1); } else { head_ref_namespaced(mark_our_ref, NULL); for_each_namespaced_ref(mark_our_ref, NULL); } string_list_clear(&symref, 1); if (advertise_refs) return; receive_needs(); if (want_obj.nr) { get_common_commits(); create_pack_file(); } }
void upload_pack(struct upload_pack_options *options) { struct string_list symref = STRING_LIST_INIT_DUP; stateless_rpc = options->stateless_rpc; timeout = options->timeout; daemon_mode = options->daemon_mode; git_config(upload_pack_config, NULL); head_ref_namespaced(find_symref, &symref); if (options->advertise_refs || !stateless_rpc) { reset_timeout(); head_ref_namespaced(send_ref, &symref); for_each_namespaced_ref(send_ref, &symref); advertise_shallow_grafts(1); packet_flush(1); } else { head_ref_namespaced(check_ref, NULL); for_each_namespaced_ref(check_ref, NULL); } string_list_clear(&symref, 1); if (options->advertise_refs) return; receive_needs(); if (want_obj.nr) { get_common_commits(); create_pack_file(); } }
static void deepen(int depth, int deepen_relative, struct object_array *shallows) { if (depth == INFINITE_DEPTH && !is_repository_shallow()) { int i; for (i = 0; i < shallows->nr; i++) { struct object *object = shallows->objects[i].item; object->flags |= NOT_SHALLOW; } } else if (deepen_relative) { struct object_array reachable_shallows = OBJECT_ARRAY_INIT; struct commit_list *result; get_reachable_list(shallows, &reachable_shallows); result = get_shallow_commits(&reachable_shallows, depth + 1, SHALLOW, NOT_SHALLOW); send_shallow(result); free_commit_list(result); object_array_clear(&reachable_shallows); } else { struct commit_list *result; result = get_shallow_commits(&want_obj, depth, SHALLOW, NOT_SHALLOW); send_shallow(result); free_commit_list(result); } send_unshallow(shallows); packet_flush(1); }
static void get_info_refs(char *arg) { const char *service_name = get_parameter("service"); struct strbuf buf = STRBUF_INIT; hdr_nocache(); if (service_name) { const char *argv[] = {NULL /* service name */, "--stateless-rpc", "--advertise-refs", ".", NULL}; struct rpc_service *svc = select_service(service_name); strbuf_addf(&buf, "application/x-git-%s-advertisement", svc->name); hdr_str(content_type, buf.buf); end_headers(); packet_write(1, "# service=git-%s\n", svc->name); packet_flush(1); argv[0] = svc->name; run_service(argv); } else { select_getanyfile(); for_each_namespaced_ref(show_text_ref, &buf); send_strbuf("text/plain", &buf); } strbuf_release(&buf); }
struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, struct ref **sought, int nr_sought, char **pack_lockfile) { struct ref *ref_cpy; fetch_pack_setup(); if (nr_sought) nr_sought = remove_duplicates_in_refs(sought, nr_sought); if (!ref) { packet_flush(fd[1]); die("no matching remote head"); } ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, pack_lockfile); if (args->depth > 0 && alternate_shallow_file) { if (*alternate_shallow_file == '\0') { /* --unshallow */ unlink_or_warn(git_path("shallow")); rollback_lock_file(&shallow_lock); } else commit_lock_file(&shallow_lock); } reprepare_packed_git(); return ref_cpy; }
struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, int nr_heads, char **heads, char **pack_lockfile) { struct stat st; struct ref *ref_cpy; fetch_pack_setup(); if (&args != my_args) memcpy(&args, my_args, sizeof(args)); if (args.depth > 0) { if (stat(git_path("shallow"), &st)) st.st_mtime = 0; } if (heads && nr_heads) nr_heads = remove_duplicates(nr_heads, heads); if (!ref) { packet_flush(fd[1]); die("no matching remote head"); } ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile); if (args.depth > 0) { struct cache_time mtime; struct strbuf sb = STRBUF_INIT; char *shallow = git_path("shallow"); int fd; mtime.sec = st.st_mtime; mtime.nsec = ST_MTIME_NSEC(st); if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC || ST_MTIME_NSEC(st) != mtime.nsec #endif ) die("shallow file was changed during fetch"); fd = hold_lock_file_for_update(&lock, shallow, LOCK_DIE_ON_ERROR); if (!write_shallow_commits(&sb, 0) || write_in_full(fd, sb.buf, sb.len) != sb.len) { unlink_or_warn(shallow); rollback_lock_file(&lock); } else { commit_lock_file(&lock); } strbuf_release(&sb); } reprepare_packed_git(); return ref_cpy; }
static void send_request(int fd, struct strbuf *buf) { if (args.stateless_rpc) { send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX); packet_flush(fd); } else safe_write(fd, buf->buf, buf->len); }
int cmd_receive_pack(int argc, const char **argv, const char *prefix) { int i; char *dir = NULL; argv++; for (i = 1; i < argc; i++) { const char *arg = *argv++; if (*arg == '-') { /* Do flag handling here */ usage(receive_pack_usage); } if (dir) usage(receive_pack_usage); dir = xstrdup(arg); } if (!dir) usage(receive_pack_usage); setup_path(); if (!enter_repo(dir, 0)) die("'%s': unable to chdir or not a git archive", dir); if (is_repository_shallow()) die("attempt to push into a shallow repository"); git_config(receive_pack_config, NULL); if (0 <= transfer_unpack_limit) unpack_limit = transfer_unpack_limit; else if (0 <= receive_unpack_limit) unpack_limit = receive_unpack_limit; add_alternate_refs(); write_head_info(); clear_extra_refs(); /* EOF */ packet_flush(1); read_head_info(); if (commands) { const char *unpack_status = NULL; if (!delete_only(commands)) unpack_status = unpack(); execute_commands(unpack_status); if (pack_lockfile) unlink(pack_lockfile); if (report_status) report(unpack_status); run_receive_hook(post_receive_hook); run_update_post_hook(commands); } return 0; }
struct ref *fetch_pack(struct fetch_pack_args *my_args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, int nr_heads, char **heads, char **pack_lockfile) { struct stat st; struct ref *ref_cpy; fetch_pack_setup(); memcpy(&args, my_args, sizeof(args)); if (args.depth > 0) { if (stat(git_path("shallow"), &st)) st.st_mtime = 0; } if (heads && nr_heads) nr_heads = remove_duplicates(nr_heads, heads); if (!ref) { packet_flush(fd[1]); die("no matching remote head"); } ref_cpy = do_fetch_pack(fd, ref, nr_heads, heads, pack_lockfile); if (args.depth > 0) { struct cache_time mtime; char *shallow = git_path("shallow"); int fd; mtime.sec = st.st_mtime; #ifdef USE_NSEC mtime.usec = st.st_mtim.usec; #endif if (stat(shallow, &st)) { if (mtime.sec) die("shallow file was removed during fetch"); } else if (st.st_mtime != mtime.sec #ifdef USE_NSEC || st.st_mtim.usec != mtime.usec #endif ) die("shallow file was changed during fetch"); fd = hold_lock_file_for_update(&lock, shallow, 1); if (!write_shallow_commits(fd, 0)) { unlink(shallow); rollback_lock_file(&lock); } else { commit_lock_file(&lock); } } reprepare_packed_git(); return ref_cpy; }
static void send_request(struct fetch_pack_args *args, int fd, struct strbuf *buf) { if (args->stateless_rpc) { send_sideband(fd, -1, buf->buf, buf->len, LARGE_PACKET_MAX); packet_flush(fd); } else write_or_die(fd, buf->buf, buf->len); }
static void deepen_by_rev_list(int ac, const char **av, struct object_array *shallows) { struct commit_list *result; result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW); send_shallow(result); free_commit_list(result); send_unshallow(shallows); packet_flush(1); }
static void upload_pack(void) { reset_timeout(); head_ref(send_ref, NULL); for_each_ref(send_ref, NULL); packet_flush(1); receive_needs(); if (want_obj.nr) { get_common_commits(); create_pack_file(); } }
/* * 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 write_head_info(void) { struct sha1_array sa = SHA1_ARRAY_INIT; for_each_alternate_ref(collect_one_alternate_ref, &sa); sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL); sha1_array_clear(&sa); for_each_ref(show_ref_cb, NULL); if (!sent_capabilities) show_ref("capabilities^{}", null_sha1); /* EOF */ packet_flush(1); }
static int disconnect_git(struct transport *transport) { struct git_transport_data *data = transport->data; if (data->conn) { packet_flush(data->fd[1]); close(data->fd[0]); close(data->fd[1]); finish_connect(data->conn); } free(data); return 0; }
/* * 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); }
static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec, const char *name_hint) { char buf[LARGE_PACKET_MAX]; int fd[2], i, len, 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(fd[1], "argument --format=%s\n", format); } for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) die(_("git archive: expected ACK/NAK, got EOF")); if (buf[len-1] == '\n') buf[--len] = 0; if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); die(_("git archive: protocol error")); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) 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; }
struct ref *fetch_pack(struct fetch_pack_args *args, int fd[], struct child_process *conn, const struct ref *ref, const char *dest, struct ref **sought, int nr_sought, struct oid_array *shallow, char **pack_lockfile, enum protocol_version version) { struct ref *ref_cpy; struct shallow_info si; fetch_pack_setup(); if (nr_sought) nr_sought = remove_duplicates_in_refs(sought, nr_sought); if (!ref) { packet_flush(fd[1]); die(_("no matching remote head")); } prepare_shallow_info(&si, shallow); if (version == protocol_v2) ref_cpy = do_fetch_pack_v2(args, fd, ref, sought, nr_sought, pack_lockfile); else ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought, &si, pack_lockfile); reprepare_packed_git(the_repository); if (!args->cloning && args->deepen) { struct check_connected_options opt = CHECK_CONNECTED_INIT; struct ref *iterator = ref_cpy; opt.shallow_file = alternate_shallow_file; if (args->deepen) opt.is_deepening_fetch = 1; if (check_connected(iterate_ref_map, &iterator, &opt)) { error(_("remote did not send all necessary objects")); free_refs(ref_cpy); ref_cpy = NULL; rollback_lock_file(&shallow_lock); goto cleanup; } args->connectivity_checked = 1; } update_shallow(args, sought, nr_sought, &si); cleanup: clear_shallow_info(&si); return ref_cpy; }
/* * This function places the outgoing packet buffer into an outgoing * socket buffer */ static int packet_write(SSH_SESSION *session) { int rc = SSH_ERROR; enter_function(); ssh_socket_write(session->socket, buffer_get(session->out_buffer), buffer_get_len(session->out_buffer)); rc = packet_flush(session, 0); leave_function(); return rc; }
static void report(const char *unpack_status) { struct command *cmd; packet_write(1, "unpack %s\n", unpack_status ? unpack_status : "ok"); for (cmd = commands; cmd; cmd = cmd->next) { if (!cmd->error_string) packet_write(1, "ok %s\n", cmd->ref_name); else packet_write(1, "ng %s %s\n", cmd->ref_name, cmd->error_string); } packet_flush(1); }
static void upload_pack(void) { if (advertise_refs || !stateless_rpc) { reset_timeout(); head_ref(send_ref, NULL); for_each_ref(send_ref, NULL); packet_flush(1); } else { head_ref(mark_our_ref, NULL); for_each_ref(mark_our_ref, NULL); } if (advertise_refs) return; receive_needs(); if (want_obj.nr) { get_common_commits(); create_pack_file(); } }
static int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec) { char buf[LARGE_PACKET_MAX]; int fd[2], i, len, 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); for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) die(_("git archive: expected ACK/NAK, got EOF")); if (buf[len-1] == '\n') buf[--len] = 0; if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) die(_("git archive: NACK %s"), buf + 5); die(_("git archive: protocol error")); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) 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 int run_remote_archiver(int argc, const char **argv, const char *remote, const char *exec) { char *url, buf[LARGE_PACKET_MAX]; int fd[2], i, len, rv; struct child_process *conn; url = xstrdup(remote); conn = git_connect(fd, url, exec, 0); for (i = 1; i < argc; i++) packet_write(fd[1], "argument %s\n", argv[i]); packet_flush(fd[1]); len = packet_read_line(fd[0], buf, sizeof(buf)); if (!len) die("git archive: expected ACK/NAK, got EOF"); if (buf[len-1] == '\n') buf[--len] = 0; if (strcmp(buf, "ACK")) { if (len > 5 && !prefixcmp(buf, "NACK ")) die("git archive: NACK %s", buf + 5); die("git archive: protocol error"); } len = packet_read_line(fd[0], buf, sizeof(buf)); if (len) 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); close(fd[0]); close(fd[1]); rv |= finish_connect(conn); return !!rv; }
static struct ref *do_fetch_pack(struct fetch_pack_args *args, int fd[2], const struct ref *orig_ref, struct ref **sought, int nr_sought, struct shallow_info *si, char **pack_lockfile) { struct ref *ref = copy_ref_list(orig_ref); unsigned char sha1[20]; const char *agent_feature; int agent_len; sort_ref_list(&ref, ref_compare_name); qsort(sought, nr_sought, sizeof(*sought), cmp_ref_by_name); if (is_repository_shallow() && !server_supports("shallow")) die("Server does not support shallow clients"); if (server_supports("multi_ack_detailed")) { if (args->verbose) fprintf(stderr, "Server supports multi_ack_detailed\n"); multi_ack = 2; if (server_supports("no-done")) { if (args->verbose) fprintf(stderr, "Server supports no-done\n"); if (args->stateless_rpc) no_done = 1; } } else if (server_supports("multi_ack")) { if (args->verbose) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } if (server_supports("side-band-64k")) { if (args->verbose) fprintf(stderr, "Server supports side-band-64k\n"); use_sideband = 2; } else if (server_supports("side-band")) { if (args->verbose) fprintf(stderr, "Server supports side-band\n"); use_sideband = 1; } if (server_supports("allow-tip-sha1-in-want")) { if (args->verbose) fprintf(stderr, "Server supports allow-tip-sha1-in-want\n"); allow_tip_sha1_in_want = 1; } if (!server_supports("thin-pack")) args->use_thin_pack = 0; if (!server_supports("no-progress")) args->no_progress = 0; if (!server_supports("include-tag")) args->include_tag = 0; if (server_supports("ofs-delta")) { if (args->verbose) fprintf(stderr, "Server supports ofs-delta\n"); } else prefer_ofs_delta = 0; if ((agent_feature = server_feature_value("agent", &agent_len))) { agent_supported = 1; if (args->verbose && agent_len) fprintf(stderr, "Server version is %.*s\n", agent_len, agent_feature); } if (everything_local(args, &ref, sought, nr_sought)) { packet_flush(fd[1]); goto all_done; } if (find_common(args, fd, sha1, ref) < 0) if (!args->keep_pack) /* When cloning, it is not unusual to have * no common commit. */ warning("no common commits"); if (args->stateless_rpc) packet_flush(fd[1]); if (args->depth > 0) setup_alternate_shallow(&shallow_lock, &alternate_shallow_file, NULL); else if (si->nr_ours || si->nr_theirs) alternate_shallow_file = setup_temporary_shallow(si->shallow); else alternate_shallow_file = NULL; if (get_pack(args, fd, pack_lockfile)) die("git fetch-pack: fetch failed."); all_done: return ref; }
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; }
int send_pack(struct send_pack_args *args, int fd[], struct child_process *conn, struct ref *remote_refs, struct extra_have_objects *extra_have) { int in = fd[0]; int out = fd[1]; struct ref *ref; int new_refs; int ask_for_status_report = 0; int allow_deleting_refs = 0; int expect_status_report = 0; int ret; /* Does the other end support the reporting? */ if (server_supports("report-status")) ask_for_status_report = 1; if (server_supports("delete-refs")) allow_deleting_refs = 1; if (server_supports("ofs-delta")) args->use_ofs_delta = 1; if (!remote_refs) { fprintf(stderr, "No refs in common and none specified; doing nothing.\n" "Perhaps you should specify a branch such as 'master'.\n"); return 0; } /* * Finally, tell the other end! */ new_refs = 0; for (ref = remote_refs; ref; ref = ref->next) { if (ref->peer_ref) hashcpy(ref->new_sha1, ref->peer_ref->new_sha1); else if (!args->send_mirror) continue; ref->deletion = is_null_sha1(ref->new_sha1); if (ref->deletion && !allow_deleting_refs) { ref->status = REF_STATUS_REJECT_NODELETE; continue; } if (!ref->deletion && !hashcmp(ref->old_sha1, ref->new_sha1)) { ref->status = REF_STATUS_UPTODATE; continue; } /* This part determines what can overwrite what. * The rules are: * * (0) you can always use --force or +A:B notation to * selectively force individual ref pairs. * * (1) if the old thing does not exist, it is OK. * * (2) if you do not have the old thing, you are not allowed * to overwrite it; you would not know what you are losing * otherwise. * * (3) if both new and old are commit-ish, and new is a * descendant of old, it is OK. * * (4) regardless of all of the above, removing :B is * always allowed. */ ref->nonfastforward = !ref->deletion && !is_null_sha1(ref->old_sha1) && (!has_sha1_file(ref->old_sha1) || !ref_newer(ref->new_sha1, ref->old_sha1)); if (ref->nonfastforward && !ref->force && !args->force_update) { ref->status = REF_STATUS_REJECT_NONFASTFORWARD; continue; } if (!ref->deletion) new_refs++; if (!args->dry_run) { char *old_hex = sha1_to_hex(ref->old_sha1); char *new_hex = sha1_to_hex(ref->new_sha1); if (ask_for_status_report) { packet_write(out, "%s %s %s%c%s", old_hex, new_hex, ref->name, 0, "report-status"); ask_for_status_report = 0; expect_status_report = 1; } else packet_write(out, "%s %s %s", old_hex, new_hex, ref->name); } ref->status = expect_status_report ? REF_STATUS_EXPECTING_REPORT : REF_STATUS_OK; } packet_flush(out); if (new_refs && !args->dry_run) { if (pack_objects(out, remote_refs, extra_have, args) < 0) { for (ref = remote_refs; ref; ref = ref->next) ref->status = REF_STATUS_NONE; return -1; } } if (expect_status_report) ret = receive_status(in, remote_refs); else ret = 0; if (ret < 0) return ret; for (ref = remote_refs; ref; ref = ref->next) { switch (ref->status) { case REF_STATUS_NONE: case REF_STATUS_UPTODATE: case REF_STATUS_OK: break; default: return -1; } } return 0; }