diff options
author | Junio C Hamano <gitster@pobox.com> | 2010-01-30 16:03:10 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2010-01-30 16:03:10 -0800 |
commit | 00d3278c8534a8244ae3447189401111e017fd5d (patch) | |
tree | f1c19903bc10ffe4816642040080fb6cfd5da376 /upload-pack.c | |
parent | b9b727ddb3c9e005bc4e9af0b990b6ef06d7f621 (diff) | |
parent | b319ef70a94731a5c6f18d07a49d5dda3f06f5d3 (diff) | |
download | git-00d3278c8534a8244ae3447189401111e017fd5d.tar.gz git-00d3278c8534a8244ae3447189401111e017fd5d.tar.xz |
Merge commit 'b319ef7' into jc/maint-fix-test-perm
* commit 'b319ef7': (8132 commits)
Add a small patch-mode testing library
git-apply--interactive: Refactor patch mode code
t8005: Nobody writes Russian in shift_jis
Fix severe breakage in "git-apply --whitespace=fix"
Update release notes for 1.6.4
After renaming a section, print any trailing variable definitions
Make section_name_match start on '[', and return the length on success
send-email: detect cycles in alias expansion
Show the presence of untracked files in the bash prompt.
SunOS grep does not understand -C<n> nor -e
Fix export_marks() error handling.
git repack: keep commits hidden by a graft
Add a test showing that 'git repack' throws away grafted-away parents
git branch: clean up detached branch handling
git branch: avoid unnecessary object lookups
git branch: fix performance problem
git svn: fix shallow clone when upstream revision is too new
do_one_ref(): null_sha1 check is not about broken ref
configure.ac: properly unset NEEDS_SSL_WITH_CRYPTO when sha1 func is missing
janitor: useless checks before free
...
Diffstat (limited to 'upload-pack.c')
-rw-r--r-- | upload-pack.c | 441 |
1 files changed, 205 insertions, 236 deletions
diff --git a/upload-pack.c b/upload-pack.c index d3a09e78d..841ebb534 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -9,8 +9,9 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "run-command.h" -static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>"; +static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=nn] <dir>"; /* bits #0..7 in revision.h, #8..10 in commit.c */ #define THEY_HAVE (1u << 11) @@ -26,7 +27,8 @@ static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=n static unsigned long oldest_have; static int multi_ack, nr_our_refs; -static int use_thin_pack, use_ofs_delta, no_progress; +static int use_thin_pack, use_ofs_delta, use_include_tag; +static int no_progress, daemon_mode; static struct object_array have_obj; static struct object_array want_obj; static unsigned int timeout; @@ -34,6 +36,7 @@ static unsigned int timeout; * otherwise maximum packet size (up to 65520 bytes). */ static int use_sideband; +static int debug_fd; static void reset_timeout(void) { @@ -62,8 +65,8 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz) return safe_write(fd, data, sz); } -FILE *pack_pipe = NULL; -static void show_commit(struct commit *commit) +static FILE *pack_pipe = NULL; +static void show_commit(struct commit *commit, void *data) { if (commit->object.flags & BOUNDARY) fputc('-', pack_pipe); @@ -75,20 +78,22 @@ static void show_commit(struct commit *commit) commit->buffer = NULL; } -static void show_object(struct object_array_entry *p) +static void show_object(struct object *obj, const struct name_path *path, const char *component) { /* An object with name "foo\n0000000..." can be used to * confuse downstream git-pack-objects very badly. */ - const char *ep = strchr(p->name, '\n'); + const char *name = path_name(path, component); + const char *ep = strchr(name, '\n'); if (ep) { - fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(p->item->sha1), - (int) (ep - p->name), - p->name); + fprintf(pack_pipe, "%s %.*s\n", sha1_to_hex(obj->sha1), + (int) (ep - name), + name); } else fprintf(pack_pipe, "%s %s\n", - sha1_to_hex(p->item->sha1), p->name); + sha1_to_hex(obj->sha1), name); + free((char *)name); } static void show_edge(struct commit *commit) @@ -96,117 +101,92 @@ static void show_edge(struct commit *commit) fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1)); } +static int do_rev_list(int fd, void *create_full_pack) +{ + int i; + struct rev_info revs; + + pack_pipe = fdopen(fd, "w"); + if (create_full_pack) + use_thin_pack = 0; /* no point doing it */ + init_revisions(&revs, NULL); + revs.tag_objects = 1; + revs.tree_objects = 1; + revs.blob_objects = 1; + if (use_thin_pack) + revs.edge_hint = 1; + + if (create_full_pack) { + const char *args[] = {"rev-list", "--all", NULL}; + setup_revisions(2, args, &revs, NULL); + } else { + for (i = 0; i < want_obj.nr; i++) { + struct object *o = want_obj.objects[i].item; + /* why??? */ + o->flags &= ~UNINTERESTING; + add_pending_object(&revs, o, NULL); + } + for (i = 0; i < have_obj.nr; i++) { + struct object *o = have_obj.objects[i].item; + o->flags |= UNINTERESTING; + add_pending_object(&revs, o, NULL); + } + setup_revisions(0, NULL, &revs, NULL); + } + if (prepare_revision_walk(&revs)) + die("revision walk setup failed"); + mark_edges_uninteresting(revs.commits, &revs, show_edge); + traverse_commit_list(&revs, show_commit, show_object, NULL); + fflush(pack_pipe); + fclose(pack_pipe); + return 0; +} + static void create_pack_file(void) { - /* Pipes between rev-list to pack-objects, pack-objects to us - * and pack-objects error stream for progress bar. - */ - int lp_pipe[2], pu_pipe[2], pe_pipe[2]; - pid_t pid_rev_list, pid_pack_objects; + struct async rev_list; + struct child_process pack_objects; int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr); char data[8193], progress[128]; char abort_msg[] = "aborting due to possible repository " "corruption on the remote side."; int buffered = -1; - - if (pipe(lp_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - pid_rev_list = fork(); - if (pid_rev_list < 0) - die("git-upload-pack: unable to fork git-rev-list"); - - if (!pid_rev_list) { - int i; - struct rev_info revs; - - close(lp_pipe[0]); - pack_pipe = fdopen(lp_pipe[1], "w"); - - if (create_full_pack) - use_thin_pack = 0; /* no point doing it */ - init_revisions(&revs, NULL); - revs.tag_objects = 1; - revs.tree_objects = 1; - revs.blob_objects = 1; - if (use_thin_pack) - revs.edge_hint = 1; - - if (create_full_pack) { - const char *args[] = {"rev-list", "--all", NULL}; - setup_revisions(2, args, &revs, NULL); - } else { - for (i = 0; i < want_obj.nr; i++) { - struct object *o = want_obj.objects[i].item; - /* why??? */ - o->flags &= ~UNINTERESTING; - add_pending_object(&revs, o, NULL); - } - for (i = 0; i < have_obj.nr; i++) { - struct object *o = have_obj.objects[i].item; - o->flags |= UNINTERESTING; - add_pending_object(&revs, o, NULL); - } - setup_revisions(0, NULL, &revs, NULL); - } - prepare_revision_walk(&revs); - mark_edges_uninteresting(revs.commits, &revs, show_edge); - traverse_commit_list(&revs, show_commit, show_object); - exit(0); - } - - if (pipe(pu_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - if (pipe(pe_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - pid_pack_objects = fork(); - if (pid_pack_objects < 0) { - /* daemon sets things up to ignore TERM */ - kill(pid_rev_list, SIGKILL); - die("git-upload-pack: unable to fork git-pack-objects"); - } - if (!pid_pack_objects) { - const char *argv[10]; - int i = 0; - - dup2(lp_pipe[0], 0); - dup2(pu_pipe[1], 1); - dup2(pe_pipe[1], 2); - - close(lp_pipe[0]); - close(lp_pipe[1]); - close(pu_pipe[0]); - close(pu_pipe[1]); - close(pe_pipe[0]); - close(pe_pipe[1]); - - argv[i++] = "pack-objects"; - argv[i++] = "--stdout"; - if (!no_progress) - argv[i++] = "--progress"; - if (use_ofs_delta) - argv[i++] = "--delta-base-offset"; - argv[i++] = NULL; - - execv_git_cmd(argv); - kill(pid_rev_list, SIGKILL); - die("git-upload-pack: unable to exec git-pack-objects"); - } - - close(lp_pipe[0]); - close(lp_pipe[1]); - - /* We read from pe_pipe[0] to capture stderr output for - * progress bar, and pu_pipe[0] to capture the pack data. + ssize_t sz; + const char *argv[10]; + int arg = 0; + + rev_list.proc = do_rev_list; + /* .data is just a boolean: any non-NULL value will do */ + rev_list.data = create_full_pack ? &rev_list : NULL; + if (start_async(&rev_list)) + die("git upload-pack: unable to fork git-rev-list"); + + argv[arg++] = "pack-objects"; + argv[arg++] = "--stdout"; + if (!no_progress) + argv[arg++] = "--progress"; + if (use_ofs_delta) + argv[arg++] = "--delta-base-offset"; + if (use_include_tag) + argv[arg++] = "--include-tag"; + argv[arg++] = NULL; + + memset(&pack_objects, 0, sizeof(pack_objects)); + pack_objects.in = rev_list.out; /* start_command closes it */ + pack_objects.out = -1; + pack_objects.err = -1; + pack_objects.git_cmd = 1; + pack_objects.argv = argv; + + if (start_command(&pack_objects)) + die("git upload-pack: unable to fork git-pack-objects"); + + /* We read from pack_objects.err to capture stderr output for + * progress bar, and pack_objects.out to capture the pack data. */ - close(pe_pipe[1]); - close(pu_pipe[1]); while (1) { - const char *who; struct pollfd pfd[2]; - pid_t pid; - int status; - ssize_t sz; int pe, pu, pollsize; reset_timeout(); @@ -214,138 +194,106 @@ static void create_pack_file(void) pollsize = 0; pe = pu = -1; - if (0 <= pu_pipe[0]) { - pfd[pollsize].fd = pu_pipe[0]; + if (0 <= pack_objects.out) { + pfd[pollsize].fd = pack_objects.out; pfd[pollsize].events = POLLIN; pu = pollsize; pollsize++; } - if (0 <= pe_pipe[0]) { - pfd[pollsize].fd = pe_pipe[0]; + if (0 <= pack_objects.err) { + pfd[pollsize].fd = pack_objects.err; pfd[pollsize].events = POLLIN; pe = pollsize; pollsize++; } - if (pollsize) { - if (poll(pfd, pollsize, -1) < 0) { - if (errno != EINTR) { - error("poll failed, resuming: %s", - strerror(errno)); - sleep(1); - } - continue; - } - if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { - /* Data ready; we keep the last byte - * to ourselves in case we detect - * broken rev-list, so that we can - * leave the stream corrupted. This - * is unfortunate -- unpack-objects - * would happily accept a valid pack - * data with trailing garbage, so - * appending garbage after we pass all - * the pack data is not good enough to - * signal breakage to downstream. - */ - char *cp = data; - ssize_t outsz = 0; - if (0 <= buffered) { - *cp++ = buffered; - outsz++; - } - sz = xread(pu_pipe[0], cp, - sizeof(data) - outsz); - if (0 < sz) - ; - else if (sz == 0) { - close(pu_pipe[0]); - pu_pipe[0] = -1; - } - else - goto fail; - sz += outsz; - if (1 < sz) { - buffered = data[sz-1] & 0xFF; - sz--; - } - else - buffered = -1; - sz = send_client_data(1, data, sz); - if (sz < 0) - goto fail; - } - if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { - /* Status ready; we ship that in the side-band - * or dump to the standard error. - */ - sz = xread(pe_pipe[0], progress, - sizeof(progress)); - if (0 < sz) - send_client_data(2, progress, sz); - else if (sz == 0) { - close(pe_pipe[0]); - pe_pipe[0] = -1; - } - else - goto fail; + if (!pollsize) + break; + + if (poll(pfd, pollsize, -1) < 0) { + if (errno != EINTR) { + error("poll failed, resuming: %s", + strerror(errno)); + sleep(1); } + continue; } - - /* See if the children are still there */ - if (pid_rev_list || pid_pack_objects) { - pid = waitpid(-1, &status, WNOHANG); - if (!pid) - continue; - who = ((pid == pid_rev_list) ? "git-rev-list" : - (pid == pid_pack_objects) ? "git-pack-objects" : - NULL); - if (!who) { - if (pid < 0) { - error("git-upload-pack: %s", - strerror(errno)); - goto fail; - } - error("git-upload-pack: we weren't " - "waiting for %d", pid); - continue; + if (0 <= pu && (pfd[pu].revents & (POLLIN|POLLHUP))) { + /* Data ready; we keep the last byte to ourselves + * in case we detect broken rev-list, so that we + * can leave the stream corrupted. This is + * unfortunate -- unpack-objects would happily + * accept a valid packdata with trailing garbage, + * so appending garbage after we pass all the + * pack data is not good enough to signal + * breakage to downstream. + */ + char *cp = data; + ssize_t outsz = 0; + if (0 <= buffered) { + *cp++ = buffered; + outsz++; } - if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { - error("git-upload-pack: %s died with error.", - who); + sz = xread(pack_objects.out, cp, + sizeof(data) - outsz); + if (0 < sz) + ; + else if (sz == 0) { + close(pack_objects.out); + pack_objects.out = -1; + } + else goto fail; + sz += outsz; + if (1 < sz) { + buffered = data[sz-1] & 0xFF; + sz--; } - if (pid == pid_rev_list) - pid_rev_list = 0; - if (pid == pid_pack_objects) - pid_pack_objects = 0; - if (pid_rev_list || pid_pack_objects) - continue; - } - - /* both died happily */ - if (pollsize) - continue; - - /* flush the data */ - if (0 <= buffered) { - data[0] = buffered; - sz = send_client_data(1, data, 1); + else + buffered = -1; + sz = send_client_data(1, data, sz); if (sz < 0) goto fail; - fprintf(stderr, "flushed.\n"); } - if (use_sideband) - packet_flush(1); - return; + if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) { + /* Status ready; we ship that in the side-band + * or dump to the standard error. + */ + sz = xread(pack_objects.err, progress, + sizeof(progress)); + if (0 < sz) + send_client_data(2, progress, sz); + else if (sz == 0) { + close(pack_objects.err); + pack_objects.err = -1; + } + else + goto fail; + } + } + + if (finish_command(&pack_objects)) { + error("git upload-pack: git-pack-objects died with error."); + goto fail; + } + if (finish_async(&rev_list)) + goto fail; /* error was already reported */ + + /* flush the data */ + if (0 <= buffered) { + data[0] = buffered; + sz = send_client_data(1, data, 1); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); } + if (use_sideband) + packet_flush(1); + return; + fail: - if (pid_pack_objects) - kill(pid_pack_objects, SIGKILL); - if (pid_rev_list) - kill(pid_rev_list, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); - die("git-upload-pack: %s", abort_msg); + die("git upload-pack: %s", abort_msg); } static int got_sha1(char *hex, unsigned char *sha1) @@ -354,7 +302,7 @@ static int got_sha1(char *hex, unsigned char *sha1) int we_knew_they_have = 0; if (get_sha1_hex(hex, sha1)) - die("git-upload-pack: expected SHA1 object, got '%s'", hex); + die("git upload-pack: expected SHA1 object, got '%s'", hex); if (!has_sha1_file(sha1)) return -1; @@ -451,13 +399,11 @@ static int get_common_commits(void) static char line[1000]; unsigned char sha1[20]; char hex[41], last_hex[41]; - int len; - track_object_refs = 0; save_commit_buffer = 0; for(;;) { - len = packet_read_line(0, line, sizeof(line)); + int len = packet_read_line(0, line, sizeof(line)); reset_timeout(); if (!len) { @@ -465,7 +411,7 @@ static int get_common_commits(void) packet_write(1, "NAK\n"); continue; } - len = strip(line, len); + strip(line, len); if (!prefixcmp(line, "have ")) { switch (got_sha1(line+5, sha1)) { case -1: /* they have what we do not */ @@ -495,7 +441,7 @@ static int get_common_commits(void) packet_write(1, "NAK\n"); return -1; } - die("git-upload-pack: expected SHA1 list, got '%s'", line); + die("git upload-pack: expected SHA1 list, got '%s'", line); } } @@ -505,6 +451,8 @@ static void receive_needs(void) 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]; @@ -512,6 +460,8 @@ static void receive_needs(void) reset_timeout(); if (!len) break; + if (debug_fd) + write_in_full(debug_fd, line, len); if (!prefixcmp(line, "shallow ")) { unsigned char sha1[20]; @@ -536,7 +486,7 @@ static void receive_needs(void) } if (prefixcmp(line, "want ") || get_sha1_hex(line+5, sha1_buf)) - die("git-upload-pack: protocol error, " + die("git upload-pack: protocol error, " "expected to get sha, not '%s'", line); if (strstr(line+45, "multi_ack")) multi_ack = 1; @@ -550,6 +500,8 @@ static void receive_needs(void) 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 @@ -561,12 +513,18 @@ static void receive_needs(void) */ o = lookup_object(sha1_buf); if (!o || !(o->flags & OUR_REF)) - die("git-upload-pack: not our ref %s", line+5); + 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) { @@ -594,7 +552,8 @@ static void receive_needs(void) /* make sure the real parents are parsed */ unregister_shallow(object->sha1); object->parsed = 0; - parse_commit((struct commit *)object); + if (parse_commit((struct commit *)object)) + die("invalid commit"); parents = ((struct commit *)object)->parents; while (parents) { add_object_array(&parents->item->object, @@ -618,11 +577,12 @@ static void receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band" - " side-band-64k ofs-delta shallow no-progress"; + " side-band-64k ofs-delta shallow no-progress" + " include-tag"; struct object *o = parse_object(sha1); if (!o) - die("git-upload-pack: cannot find object %s:", sha1_to_hex(sha1)); + die("git upload-pack: cannot find object %s:", sha1_to_hex(sha1)); if (capabilities) packet_write(1, "%s %s%c%s\n", sha1_to_hex(sha1), refname, @@ -636,7 +596,8 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo } if (o->type == OBJ_TAG) { o = deref_tag(o, refname, 0); - packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); + if (o) + packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname); } return 0; } @@ -660,6 +621,8 @@ int main(int argc, char **argv) int i; int strict = 0; + git_extract_argv0_path(argv[0]); + for (i = 1; i < argc; i++) { char *arg = argv[i]; @@ -671,6 +634,7 @@ int main(int argc, char **argv) } if (!prefixcmp(arg, "--timeout=")) { timeout = atoi(arg+10); + daemon_mode = 1; continue; } if (!strcmp(arg, "--")) { @@ -678,15 +642,20 @@ int main(int argc, char **argv) break; } } - + if (i != argc-1) usage(upload_pack_usage); + + setup_path(); + dir = argv[i]; if (!enter_repo(dir, strict)) - die("'%s': unable to chdir or not a git archive", dir); + die("'%s' does not appear to be a git repository", dir); if (is_repository_shallow()) die("attempt to fetch/clone from a shallow repository"); + if (getenv("GIT_DEBUG_SEND_PACK")) + debug_fd = atoi(getenv("GIT_DEBUG_SEND_PACK")); upload_pack(); return 0; } |