diff options
author | Junio C Hamano <junkio@cox.net> | 2006-06-21 02:50:59 -0700 |
---|---|---|
committer | Junio C Hamano <junkio@cox.net> | 2006-06-21 02:50:59 -0700 |
commit | 3bec0da08d6df03a103ddc4237cf2c79992c7dd4 (patch) | |
tree | c6b26993418a86b7c1eb72dcdff17489b5e410b1 | |
parent | Merge branch 'jn/web' into next (diff) | |
parent | upload-pack/fetch-pack: support side-band communication (diff) | |
download | tgif-3bec0da08d6df03a103ddc4237cf2c79992c7dd4.tar.xz |
Merge branch 'jc/upload-corrupt' into next
* jc/upload-corrupt:
upload-pack/fetch-pack: support side-band communication
Retire git-clone-pack
upload-pack: prepare for sideband message support.
upload-pack: avoid sending an incomplete pack upon failure
Fix possible out-of-bounds array access
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Documentation/git-clone-pack.txt | 64 | ||||
-rw-r--r-- | Documentation/git-clone.txt | 4 | ||||
-rw-r--r-- | Documentation/git-receive-pack.txt | 3 | ||||
-rw-r--r-- | Documentation/git-upload-pack.txt | 2 | ||||
-rw-r--r-- | Documentation/git.txt | 6 | ||||
-rw-r--r-- | INSTALL | 2 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | builtin-grep.c | 9 | ||||
-rw-r--r-- | cache.h | 4 | ||||
-rw-r--r-- | clone-pack.c | 186 | ||||
-rw-r--r-- | fetch-clone.c | 71 | ||||
-rw-r--r-- | fetch-pack.c | 22 | ||||
-rw-r--r-- | pack-objects.c | 4 | ||||
-rw-r--r-- | pkt-line.c | 4 | ||||
-rw-r--r-- | pkt-line.h | 1 | ||||
-rw-r--r-- | upload-pack.c | 253 |
17 files changed, 342 insertions, 296 deletions
diff --git a/.gitignore b/.gitignore index afd0876218..65aa939f6b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,6 @@ git-cherry git-cherry-pick git-clean git-clone -git-clone-pack git-commit git-commit-tree git-convert-objects diff --git a/Documentation/git-clone-pack.txt b/Documentation/git-clone-pack.txt deleted file mode 100644 index 09f43eefe4..0000000000 --- a/Documentation/git-clone-pack.txt +++ /dev/null @@ -1,64 +0,0 @@ -git-clone-pack(1) -================= - -NAME ----- -git-clone-pack - Clones a repository by receiving packed objects - - -SYNOPSIS --------- -'git-clone-pack' [--exec=<git-upload-pack>] [<host>:]<directory> [<head>...] - -DESCRIPTION ------------ -Clones a repository into the current repository by invoking -'git-upload-pack', possibly on the remote host via ssh, in -the named repository, and stores the sent pack in the local -repository. - -OPTIONS -------- ---exec=<git-upload-pack>:: - Use this to specify the path to 'git-upload-pack' on the - remote side, if it is not found on your $PATH. - Installations of sshd ignore the user's environment - setup scripts for login shells (e.g. .bash_profile) and - your privately installed git may not be found on the system - default $PATH. Another workaround suggested is to set - up your $PATH in ".bashrc", but this flag is for people - who do not want to pay the overhead for non-interactive - shells by having a lean .bashrc file (they set most of - the things up in .bash_profile). - -<host>:: - A remote host that houses the repository. When this - part is specified, 'git-upload-pack' is invoked via - ssh. - -<directory>:: - The repository to sync from. - -<head>...:: - The heads to update. This is relative to $GIT_DIR - (e.g. "HEAD", "refs/heads/master"). When unspecified, - all heads are updated to match the remote repository. -+ -Usually all the refs from existing repository are stored -under the same name in the new repository. Giving explicit -<head> arguments instead writes the object names and refs to -the standard output, just like get-fetch-pack does. - -Author ------- -Written by Linus Torvalds <torvalds@osdl.org> - -Documentation --------------- -Documentation by Junio C Hamano. - - -GIT ---- -Part of the gitlink:git[7] suite - diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt index a90521e513..f973c64313 100644 --- a/Documentation/git-clone.txt +++ b/Documentation/git-clone.txt @@ -62,7 +62,7 @@ OPTIONS --quiet:: -q:: Operate quietly. This flag is passed to "rsync" and - "git-clone-pack" commands when given. + "git-fetch-pack" commands when given. -n:: No checkout of HEAD is performed after the clone is complete. @@ -85,7 +85,7 @@ OPTIONS --upload-pack <upload-pack>:: -u <upload-pack>:: When given, and the repository to clone from is handled - by 'git-clone-pack', '--exec=<upload-pack>' is passed to + by 'git-fetch-pack', '--exec=<upload-pack>' is passed to the command to specify non-default path for the command run on the other end. diff --git a/Documentation/git-receive-pack.txt b/Documentation/git-receive-pack.txt index 60debca487..f9457d45ed 100644 --- a/Documentation/git-receive-pack.txt +++ b/Documentation/git-receive-pack.txt @@ -18,8 +18,7 @@ information fed from the remote end. This command is usually not invoked directly by the end user. The UI for the protocol is on the 'git-send-pack' side, and the program pair is meant to be used to push updates to remote -repository. For pull operations, see 'git-fetch-pack' and -'git-clone-pack'. +repository. For pull operations, see 'git-fetch-pack'. The command allows for creation and fast forwarding of sha1 refs (heads/tags) on the remote end (strictly speaking, it is the diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt index 4795e98754..b2c9307661 100644 --- a/Documentation/git-upload-pack.txt +++ b/Documentation/git-upload-pack.txt @@ -12,7 +12,7 @@ SYNOPSIS DESCRIPTION ----------- -Invoked by 'git-clone-pack' and/or 'git-fetch-pack', learns what +Invoked by 'git-fetch-pack', learns what objects the other side is missing, and sends them after packing. This command is usually not invoked directly by the end user. diff --git a/Documentation/git.txt b/Documentation/git.txt index d4472b56d1..51f20c6e67 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -192,10 +192,6 @@ the working tree. Synching repositories ~~~~~~~~~~~~~~~~~~~~~ -gitlink:git-clone-pack[1]:: - Clones a repository into the current repository (engine - for ssh and local transport). - gitlink:git-fetch-pack[1]:: Updates from a remote repository (engine for ssh and local transport). @@ -237,7 +233,7 @@ gitlink:git-update-server-info[1]:: clients discover references and packs on it. gitlink:git-upload-pack[1]:: - Invoked by 'git-clone-pack' and 'git-fetch-pack' to push + Invoked by 'git-fetch-pack' to push what are asked for. gitlink:git-upload-tar[1]:: @@ -96,7 +96,7 @@ Issues of note: $ mkdir manual && cd manual $ git init-db - $ git clone-pack git://git.kernel.org/pub/scm/git/git.git man html | + $ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html | while read a b do echo $a >.git/$b @@ -148,7 +148,7 @@ SIMPLE_PROGRAMS = \ # ... and all the rest that could be moved out of bindir to gitexecdir PROGRAMS = \ - git-checkout-index$X git-clone-pack$X \ + git-checkout-index$X \ git-convert-objects$X git-fetch-pack$X git-fsck-objects$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \ git-merge-base$X \ diff --git a/builtin-grep.c b/builtin-grep.c index 6a240fb6e9..2e7986cece 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -29,10 +29,11 @@ static int pathspec_matches(const char **paths, const char *name) int matchlen = strlen(match); const char *cp, *meta; - if ((matchlen <= namelen) && - !strncmp(name, match, matchlen) && - (match[matchlen-1] == '/' || - name[matchlen] == '\0' || name[matchlen] == '/')) + if (!matchlen || + ((matchlen <= namelen) && + !strncmp(name, match, matchlen) && + (match[matchlen-1] == '/' || + name[matchlen] == '\0' || name[matchlen] == '/'))) return 1; if (!fnmatch(match, name, 0)) return 1; @@ -374,8 +374,8 @@ extern char git_commit_encoding[MAX_ENCODING_LENGTH]; extern int copy_fd(int ifd, int ofd); /* Finish off pack transfer receiving end */ -extern int receive_unpack_pack(int fd[2], const char *me, int quiet); -extern int receive_keep_pack(int fd[2], const char *me, int quiet); +extern int receive_unpack_pack(int fd[2], const char *me, int quiet, int); +extern int receive_keep_pack(int fd[2], const char *me, int quiet, int); /* pager.c */ extern void setup_pager(void); diff --git a/clone-pack.c b/clone-pack.c deleted file mode 100644 index a4370f595f..0000000000 --- a/clone-pack.c +++ /dev/null @@ -1,186 +0,0 @@ -#include "cache.h" -#include "refs.h" -#include "pkt-line.h" - -static const char clone_pack_usage[] = -"git-clone-pack [--exec=<git-upload-pack>] [<host>:]<directory> [<heads>]*"; -static const char *exec = "git-upload-pack"; - -static int quiet = 0; - -static void clone_handshake(int fd[2], struct ref *ref) -{ - unsigned char sha1[20]; - - while (ref) { - packet_write(fd[1], "want %s\n", sha1_to_hex(ref->old_sha1)); - ref = ref->next; - } - packet_flush(fd[1]); - - /* We don't have nuttin' */ - packet_write(fd[1], "done\n"); - if (get_ack(fd[0], sha1)) - error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1)); -} - -static int is_master(struct ref *ref) -{ - return !strcmp(ref->name, "refs/heads/master"); -} - -static void write_one_ref(struct ref *ref) -{ - char *path = git_path("%s", ref->name); - int fd; - char *hex; - - if (!strncmp(ref->name, "refs/", 5) && - check_ref_format(ref->name + 5)) { - error("refusing to create funny ref '%s' locally", ref->name); - return; - } - - if (safe_create_leading_directories(path)) - die("unable to create leading directory for %s", ref->name); - fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666); - if (fd < 0) - die("unable to create ref %s", ref->name); - hex = sha1_to_hex(ref->old_sha1); - hex[40] = '\n'; - if (write(fd, hex, 41) != 41) - die("unable to write ref %s", ref->name); - close(fd); -} - -static void write_refs(struct ref *ref) -{ - struct ref *head = NULL, *head_ptr, *master_ref; - char *head_path; - - /* Upload-pack must report HEAD first */ - if (!strcmp(ref->name, "HEAD")) { - head = ref; - ref = ref->next; - } - head_ptr = NULL; - master_ref = NULL; - while (ref) { - if (is_master(ref)) - master_ref = ref; - if (head && - !memcmp(ref->old_sha1, head->old_sha1, 20) && - !strncmp(ref->name, "refs/heads/",11) && - (!head_ptr || ref == master_ref)) - head_ptr = ref; - - write_one_ref(ref); - ref = ref->next; - } - if (!head) { - fprintf(stderr, "No HEAD in remote.\n"); - return; - } - - head_path = strdup(git_path("HEAD")); - if (!head_ptr) { - /* - * If we had a master ref, and it wasn't HEAD, we need to undo the - * symlink, and write a standalone HEAD. Give a warning, because that's - * really really wrong. - */ - if (master_ref) { - error("HEAD doesn't point to any refs! Making standalone HEAD"); - unlink(head_path); - } - write_one_ref(head); - free(head_path); - return; - } - - /* We reset to the master branch if it's available */ - if (master_ref) - return; - - fprintf(stderr, "Setting HEAD to %s\n", head_ptr->name); - - /* - * Uhhuh. Other end didn't have master. We start HEAD off with - * the first branch with the same value. - */ - if (create_symref(head_path, head_ptr->name) < 0) - die("unable to link HEAD to %s", head_ptr->name); - free(head_path); -} - -static int clone_pack(int fd[2], int nr_match, char **match) -{ - struct ref *refs; - int status; - - get_remote_heads(fd[0], &refs, nr_match, match, 1); - if (!refs) { - packet_flush(fd[1]); - die("no matching remote head"); - } - clone_handshake(fd, refs); - - status = receive_keep_pack(fd, "git-clone-pack", quiet); - if (!quiet) - fprintf(stderr, "\n"); - - if (!status) { - if (nr_match == 0) - write_refs(refs); - else - while (refs) { - printf("%s %s\n", - sha1_to_hex(refs->old_sha1), - refs->name); - refs = refs->next; - } - } - return status; -} - -int main(int argc, char **argv) -{ - int i, ret, nr_heads; - char *dest = NULL, **heads; - int fd[2]; - pid_t pid; - - setup_git_directory(); - - nr_heads = 0; - heads = NULL; - for (i = 1; i < argc; i++) { - char *arg = argv[i]; - - if (*arg == '-') { - if (!strcmp("-q", arg)) { - quiet = 1; - continue; - } - if (!strncmp("--exec=", arg, 7)) { - exec = arg + 7; - continue; - } - usage(clone_pack_usage); - } - dest = arg; - heads = argv + i + 1; - nr_heads = argc - i - 1; - break; - } - if (!dest) - usage(clone_pack_usage); - pid = git_connect(fd, dest, exec); - if (pid < 0) - return 1; - ret = clone_pack(fd, nr_heads, heads); - close(fd[0]); - close(fd[1]); - finish_connect(pid); - return ret; -} diff --git a/fetch-clone.c b/fetch-clone.c index da1b3ffbaa..c16b0c481b 100644 --- a/fetch-clone.c +++ b/fetch-clone.c @@ -1,5 +1,6 @@ #include "cache.h" #include "exec_cmd.h" +#include "pkt-line.h" #include <sys/wait.h> #include <sys/time.h> @@ -23,7 +24,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me) pid = fork(); if (pid < 0) - die("git-clone-pack: unable to fork off git-index-pack"); + die("%s: unable to fork off git-index-pack", me); if (!pid) { close(0); dup2(pipe_fd[1], 1); @@ -94,11 +95,69 @@ static int finish_pack(const char *pack_tmp_name, const char *me) exit(1); } -int receive_unpack_pack(int fd[2], const char *me, int quiet) +static pid_t setup_sideband(int sideband, const char *me, int fd[2], int xd[2]) +{ + pid_t side_pid; + + if (!sideband) { + fd[0] = xd[0]; + fd[1] = xd[1]; + return 0; + } + /* xd[] is talking with upload-pack; subprocess reads from + * xd[0], spits out band#2 to stderr, and feeds us band#1 + * through our fd[0]. + */ + if (pipe(fd) < 0) + die("%s: unable to set up pipe", me); + side_pid = fork(); + if (side_pid < 0) + die("%s: unable to fork off sideband demultiplexer", me); + if (!side_pid) { + /* subprocess */ + close(fd[0]); + if (xd[0] != xd[1]) + close(xd[1]); + while (1) { + char buf[1024]; + int len = packet_read_line(xd[0], buf, sizeof(buf)); + if (len == 0) + break; + if (len < 1) + die("%s: protocol error: no band designator", + me); + len--; + switch (buf[0] & 0xFF) { + case 3: + safe_write(2, buf+1, len); + fprintf(stderr, "\n"); + exit(1); + case 2: + safe_write(2, buf+1, len); + continue; + case 1: + safe_write(fd[1], buf+1, len); + continue; + default: + die("%s: protocol error: bad band #%d", + me, (buf[0] & 0xFF)); + } + } + exit(0); + } + close(xd[0]); + close(fd[1]); + fd[1] = xd[1]; + return side_pid; +} + +int receive_unpack_pack(int xd[2], const char *me, int quiet, int sideband) { int status; - pid_t pid; + pid_t pid, side_pid; + int fd[2]; + side_pid = setup_sideband(sideband, me, fd, xd); pid = fork(); if (pid < 0) die("%s: unable to fork off git-unpack-objects", me); @@ -147,10 +206,10 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet) */ #define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10)) -int receive_keep_pack(int fd[2], const char *me, int quiet) +int receive_keep_pack(int xd[2], const char *me, int quiet, int sideband) { char tmpfile[PATH_MAX]; - int ofd, ifd; + int ofd, ifd, fd[2]; unsigned long total; static struct timeval prev_tv; struct average { @@ -160,6 +219,8 @@ int receive_keep_pack(int fd[2], const char *me, int quiet) unsigned long avg_bytes, avg_time; int idx = 0; + setup_sideband(sideband, me, fd, xd); + ifd = fd[0]; snprintf(tmpfile, sizeof(tmpfile), "%s/pack/tmp-XXXXXX", get_object_directory()); diff --git a/fetch-pack.c b/fetch-pack.c index 7d23a8071a..f2c51ebe4b 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -25,7 +25,7 @@ static const char *exec = "git-upload-pack"; #define MAX_IN_VAIN 256 static struct commit_list *rev_list = NULL; -static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0; +static int non_common_revs = 0, multi_ack = 0, use_thin_pack = 0, use_sideband; static void rev_list_push(struct commit *commit, int mark) { @@ -165,9 +165,14 @@ static int find_common(int fd[2], unsigned char *result_sha1, continue; } - packet_write(fd[1], "want %s%s%s\n", sha1_to_hex(remote), - (multi_ack ? " multi_ack" : ""), - (use_thin_pack ? " thin-pack" : "")); + if (!fetching) + packet_write(fd[1], "want %s%s%s%s\n", + sha1_to_hex(remote), + (multi_ack ? " multi_ack" : ""), + (use_sideband ? " side-band" : ""), + (use_thin_pack ? " thin-pack" : "")); + else + packet_write(fd[1], "want %s\n", sha1_to_hex(remote)); fetching++; } packet_flush(fd[1]); @@ -421,6 +426,11 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "Server supports multi_ack\n"); multi_ack = 1; } + if (server_supports("side-band")) { + if (verbose) + fprintf(stderr, "Server supports side-band\n"); + use_sideband = 1; + } if (!ref) { packet_flush(fd[1]); die("no matching remote head"); @@ -437,9 +447,9 @@ static int fetch_pack(int fd[2], int nr_match, char **match) fprintf(stderr, "warning: no common commits\n"); if (keep_pack) - status = receive_keep_pack(fd, "git-fetch-pack", quiet); + status = receive_keep_pack(fd, "git-fetch-pack", quiet, use_sideband); else - status = receive_unpack_pack(fd, "git-fetch-pack", quiet); + status = receive_unpack_pack(fd, "git-fetch-pack", quiet, use_sideband); if (status) die("git-fetch-pack: fetch failed."); diff --git a/pack-objects.c b/pack-objects.c index 179560f2bd..7a8c16c317 100644 --- a/pack-objects.c +++ b/pack-objects.c @@ -1221,6 +1221,10 @@ int main(int argc, char **argv) local = 1; continue; } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } if (!strcmp("--incremental", arg)) { incremental = 1; continue; diff --git a/pkt-line.c b/pkt-line.c index bb3bab05cd..3d724acf23 100644 --- a/pkt-line.c +++ b/pkt-line.c @@ -16,8 +16,9 @@ * The writing side could use stdio, but since the reading * side can't, we stay with pure read/write interfaces. */ -static void safe_write(int fd, const void *buf, unsigned n) +ssize_t safe_write(int fd, const void *buf, ssize_t n) { + ssize_t nn = n; while (n) { int ret = xwrite(fd, buf, n); if (ret > 0) { @@ -29,6 +30,7 @@ static void safe_write(int fd, const void *buf, unsigned n) die("write error (disk full?)"); die("write error (%s)", strerror(errno)); } + return nn; } /* diff --git a/pkt-line.h b/pkt-line.h index 51d0cbe219..9abef24de3 100644 --- a/pkt-line.h +++ b/pkt-line.h @@ -8,5 +8,6 @@ void packet_flush(int fd); void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); int packet_read_line(int fd, char *buffer, unsigned size); +ssize_t safe_write(int, const void *, ssize_t); #endif diff --git a/upload-pack.c b/upload-pack.c index 979e58306e..7b86f6965b 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -5,6 +5,9 @@ #include "object.h" #include "commit.h" #include "exec_cmd.h" +#include <signal.h> +#include <sys/poll.h> +#include <sys/wait.h> static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>"; @@ -18,6 +21,7 @@ static int use_thin_pack = 0; static unsigned char has_sha1[MAX_HAS][20]; static unsigned char needs_sha1[MAX_NEEDS][20]; static unsigned int timeout = 0; +static int use_sideband = 0; static void reset_timeout(void) { @@ -31,19 +35,63 @@ static int strip(char *line, int len) return len; } +#define PACKET_MAX 1000 +static ssize_t send_client_data(int fd, const char *data, ssize_t sz) +{ + ssize_t ssz; + const char *p; + + if (!data) { + if (!use_sideband) + return 0; + packet_flush(1); + } + + if (!use_sideband) { + if (fd == 3) + /* emergency quit */ + fd = 2; + return safe_write(fd, data, sz); + } + p = data; + ssz = sz; + while (sz) { + unsigned n; + char hdr[5]; + + n = sz; + if (PACKET_MAX - 5 < n) + n = PACKET_MAX - 5; + sprintf(hdr, "%04x", n + 5); + hdr[4] = fd; + safe_write(1, hdr, 5); + safe_write(1, p, n); + p += n; + sz -= n; + } + return ssz; +} + static void create_pack_file(void) { - int fd[2]; - pid_t pid; + /* 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; int create_full_pack = (nr_our_refs == nr_needs && !nr_has); + char data[8193], progress[128]; + char abort_msg[] = "aborting due to possible repository " + "corruption on the remote side."; + int buffered = -1; - if (pipe(fd) < 0) + if (pipe(lp_pipe) < 0) die("git-upload-pack: unable to create pipe"); - pid = fork(); - if (pid < 0) + pid_rev_list = fork(); + if (pid_rev_list < 0) die("git-upload-pack: unable to fork git-rev-list"); - if (!pid) { + if (!pid_rev_list) { int i; int args; const char **argv; @@ -60,10 +108,10 @@ static void create_pack_file(void) argv = (const char **) p; buf = xmalloc(args * 45); - dup2(fd[1], 1); + dup2(lp_pipe[1], 1); close(0); - close(fd[0]); - close(fd[1]); + close(lp_pipe[0]); + close(lp_pipe[1]); *p++ = "rev-list"; *p++ = use_thin_pack ? "--objects-edge" : "--objects"; if (create_full_pack || MAX_NEEDS <= nr_needs) @@ -86,11 +134,184 @@ static void create_pack_file(void) execv_git_cmd(argv); die("git-upload-pack: unable to exec git-rev-list"); } - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - execl_git_cmd("pack-objects", "--stdout", NULL); - die("git-upload-pack: unable to exec git-pack-objects"); + + 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) { + 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]); + execl_git_cmd("pack-objects", "--stdout", "--progress", NULL); + 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. + */ + 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; + + pollsize = 0; + pe = pu = -1; + + if (0 <= pu_pipe[0]) { + pfd[pollsize].fd = pu_pipe[0]; + pfd[pollsize].events = POLLIN; + pu = pollsize; + pollsize++; + } + if (0 <= pe_pipe[0]) { + pfd[pollsize].fd = pe_pipe[0]; + 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 = read(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 = read(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; + } + } + + /* 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 (!WIFEXITED(status) || WEXITSTATUS(status) > 0) { + error("git-upload-pack: %s died with error.", + who); + goto fail; + } + 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); + if (sz < 0) + goto fail; + fprintf(stderr, "flushed.\n"); + } + send_client_data(1, NULL, 0); + 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); } static int got_sha1(char *hex, unsigned char *sha1) @@ -197,6 +418,8 @@ static int receive_needs(void) multi_ack = 1; if (strstr(line+45, "thin-pack")) use_thin_pack = 1; + if (strstr(line+45, "side-band")) + use_sideband = 1; /* We have sent all our refs already, and the other end * should have chosen out of them; otherwise they are @@ -218,7 +441,7 @@ static int receive_needs(void) static int send_ref(const char *refname, const unsigned char *sha1) { - static char *capabilities = "multi_ack thin-pack"; + static char *capabilities = "multi_ack thin-pack side-band"; struct object *o = parse_object(sha1); if (!o) |