Merge branch 'jc/upload-corrupt'

* jc/upload-corrupt:
  daemon: send stderr to /dev/null instead of closing.
  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
This commit is contained in:
Junio C Hamano 2006-06-22 10:25:51 -07:00
commit 16bf4e1f1e
17 changed files with 338 additions and 293 deletions

1
.gitignore vendored
View File

@ -17,7 +17,6 @@ git-cherry
git-cherry-pick git-cherry-pick
git-clean git-clean
git-clone git-clone
git-clone-pack
git-commit git-commit
git-commit-tree git-commit-tree
git-convert-objects git-convert-objects

View File

@ -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

View File

@ -62,7 +62,7 @@ OPTIONS
--quiet:: --quiet::
-q:: -q::
Operate quietly. This flag is passed to "rsync" and Operate quietly. This flag is passed to "rsync" and
"git-clone-pack" commands when given. "git-fetch-pack" commands when given.
-n:: -n::
No checkout of HEAD is performed after the clone is complete. No checkout of HEAD is performed after the clone is complete.
@ -85,7 +85,7 @@ OPTIONS
--upload-pack <upload-pack>:: --upload-pack <upload-pack>::
-u <upload-pack>:: -u <upload-pack>::
When given, and the repository to clone from is handled 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 the command to specify non-default path for the command
run on the other end. run on the other end.

View File

@ -18,8 +18,7 @@ information fed from the remote end.
This command is usually not invoked directly by the end user. 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 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 program pair is meant to be used to push updates to remote
repository. For pull operations, see 'git-fetch-pack' and repository. For pull operations, see 'git-fetch-pack'.
'git-clone-pack'.
The command allows for creation and fast forwarding of sha1 refs The command allows for creation and fast forwarding of sha1 refs
(heads/tags) on the remote end (strictly speaking, it is the (heads/tags) on the remote end (strictly speaking, it is the

View File

@ -12,7 +12,7 @@ SYNOPSIS
DESCRIPTION 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. objects the other side is missing, and sends them after packing.
This command is usually not invoked directly by the end user. This command is usually not invoked directly by the end user.

View File

@ -192,10 +192,6 @@ the working tree.
Synching repositories 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]:: gitlink:git-fetch-pack[1]::
Updates from a remote repository (engine for ssh and Updates from a remote repository (engine for ssh and
local transport). local transport).
@ -237,7 +233,7 @@ gitlink:git-update-server-info[1]::
clients discover references and packs on it. clients discover references and packs on it.
gitlink:git-upload-pack[1]:: 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. what are asked for.
gitlink:git-upload-tar[1]:: gitlink:git-upload-tar[1]::

View File

@ -96,7 +96,7 @@ Issues of note:
$ mkdir manual && cd manual $ mkdir manual && cd manual
$ git init-db $ 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 while read a b
do do
echo $a >.git/$b echo $a >.git/$b

View File

@ -149,7 +149,7 @@ SIMPLE_PROGRAMS = \
# ... and all the rest that could be moved out of bindir to gitexecdir # ... and all the rest that could be moved out of bindir to gitexecdir
PROGRAMS = \ 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-convert-objects$X git-fetch-pack$X git-fsck-objects$X \
git-hash-object$X git-index-pack$X git-local-fetch$X \ git-hash-object$X git-index-pack$X git-local-fetch$X \
git-mailinfo$X git-merge-base$X \ git-mailinfo$X git-merge-base$X \

View File

@ -374,8 +374,8 @@ extern char git_commit_encoding[MAX_ENCODING_LENGTH];
extern int copy_fd(int ifd, int ofd); extern int copy_fd(int ifd, int ofd);
/* Finish off pack transfer receiving end */ /* Finish off pack transfer receiving end */
extern int receive_unpack_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); extern int receive_keep_pack(int fd[2], const char *me, int quiet, int);
/* pager.c */ /* pager.c */
extern void setup_pager(void); extern void setup_pager(void);

View File

@ -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;
}

View File

@ -762,7 +762,7 @@ int main(int argc, char **argv)
struct sockaddr *peer = (struct sockaddr *)&ss; struct sockaddr *peer = (struct sockaddr *)&ss;
socklen_t slen = sizeof(ss); socklen_t slen = sizeof(ss);
fclose(stderr); //FIXME: workaround freopen("/dev/null", "w", stderr);
if (getpeername(0, peer, &slen)) if (getpeername(0, peer, &slen))
peer = NULL; peer = NULL;

View File

@ -1,5 +1,6 @@
#include "cache.h" #include "cache.h"
#include "exec_cmd.h" #include "exec_cmd.h"
#include "pkt-line.h"
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/time.h> #include <sys/time.h>
@ -23,7 +24,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me)
pid = fork(); pid = fork();
if (pid < 0) 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) { if (!pid) {
close(0); close(0);
dup2(pipe_fd[1], 1); dup2(pipe_fd[1], 1);
@ -94,11 +95,69 @@ static int finish_pack(const char *pack_tmp_name, const char *me)
exit(1); 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; int status;
pid_t pid; pid_t pid, side_pid;
int fd[2];
side_pid = setup_sideband(sideband, me, fd, xd);
pid = fork(); pid = fork();
if (pid < 0) if (pid < 0)
die("%s: unable to fork off git-unpack-objects", me); 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)) #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]; char tmpfile[PATH_MAX];
int ofd, ifd; int ofd, ifd, fd[2];
unsigned long total; unsigned long total;
static struct timeval prev_tv; static struct timeval prev_tv;
struct average { struct average {
@ -160,6 +219,8 @@ int receive_keep_pack(int fd[2], const char *me, int quiet)
unsigned long avg_bytes, avg_time; unsigned long avg_bytes, avg_time;
int idx = 0; int idx = 0;
setup_sideband(sideband, me, fd, xd);
ifd = fd[0]; ifd = fd[0];
snprintf(tmpfile, sizeof(tmpfile), snprintf(tmpfile, sizeof(tmpfile),
"%s/pack/tmp-XXXXXX", get_object_directory()); "%s/pack/tmp-XXXXXX", get_object_directory());

View File

@ -25,7 +25,7 @@ static const char *exec = "git-upload-pack";
#define MAX_IN_VAIN 256 #define MAX_IN_VAIN 256
static struct commit_list *rev_list = NULL; 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) 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; continue;
} }
packet_write(fd[1], "want %s%s%s\n", sha1_to_hex(remote), if (!fetching)
(multi_ack ? " multi_ack" : ""), packet_write(fd[1], "want %s%s%s%s\n",
(use_thin_pack ? " thin-pack" : "")); 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++; fetching++;
} }
packet_flush(fd[1]); 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"); fprintf(stderr, "Server supports multi_ack\n");
multi_ack = 1; multi_ack = 1;
} }
if (server_supports("side-band")) {
if (verbose)
fprintf(stderr, "Server supports side-band\n");
use_sideband = 1;
}
if (!ref) { if (!ref) {
packet_flush(fd[1]); packet_flush(fd[1]);
die("no matching remote head"); 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"); fprintf(stderr, "warning: no common commits\n");
if (keep_pack) if (keep_pack)
status = receive_keep_pack(fd, "git-fetch-pack", quiet); status = receive_keep_pack(fd, "git-fetch-pack", quiet, use_sideband);
else else
status = receive_unpack_pack(fd, "git-fetch-pack", quiet); status = receive_unpack_pack(fd, "git-fetch-pack", quiet, use_sideband);
if (status) if (status)
die("git-fetch-pack: fetch failed."); die("git-fetch-pack: fetch failed.");

View File

@ -1221,6 +1221,10 @@ int main(int argc, char **argv)
local = 1; local = 1;
continue; continue;
} }
if (!strcmp("--progress", arg)) {
progress = 1;
continue;
}
if (!strcmp("--incremental", arg)) { if (!strcmp("--incremental", arg)) {
incremental = 1; incremental = 1;
continue; continue;

View File

@ -16,8 +16,9 @@
* The writing side could use stdio, but since the reading * The writing side could use stdio, but since the reading
* side can't, we stay with pure read/write interfaces. * 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) { while (n) {
int ret = xwrite(fd, buf, n); int ret = xwrite(fd, buf, n);
if (ret > 0) { 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 (disk full?)");
die("write error (%s)", strerror(errno)); die("write error (%s)", strerror(errno));
} }
return nn;
} }
/* /*

View File

@ -8,5 +8,6 @@ void packet_flush(int fd);
void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3))); void packet_write(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
int packet_read_line(int fd, char *buffer, unsigned size); int packet_read_line(int fd, char *buffer, unsigned size);
ssize_t safe_write(int, const void *, ssize_t);
#endif #endif

View File

@ -5,6 +5,9 @@
#include "object.h" #include "object.h"
#include "commit.h" #include "commit.h"
#include "exec_cmd.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>"; 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 has_sha1[MAX_HAS][20];
static unsigned char needs_sha1[MAX_NEEDS][20]; static unsigned char needs_sha1[MAX_NEEDS][20];
static unsigned int timeout = 0; static unsigned int timeout = 0;
static int use_sideband = 0;
static void reset_timeout(void) static void reset_timeout(void)
{ {
@ -31,19 +35,63 @@ static int strip(char *line, int len)
return 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) static void create_pack_file(void)
{ {
int fd[2]; /* Pipes between rev-list to pack-objects, pack-objects to us
pid_t pid; * 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); 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"); die("git-upload-pack: unable to create pipe");
pid = fork(); pid_rev_list = fork();
if (pid < 0) if (pid_rev_list < 0)
die("git-upload-pack: unable to fork git-rev-list"); die("git-upload-pack: unable to fork git-rev-list");
if (!pid) { if (!pid_rev_list) {
int i; int i;
int args; int args;
const char **argv; const char **argv;
@ -60,10 +108,10 @@ static void create_pack_file(void)
argv = (const char **) p; argv = (const char **) p;
buf = xmalloc(args * 45); buf = xmalloc(args * 45);
dup2(fd[1], 1); dup2(lp_pipe[1], 1);
close(0); close(0);
close(fd[0]); close(lp_pipe[0]);
close(fd[1]); close(lp_pipe[1]);
*p++ = "rev-list"; *p++ = "rev-list";
*p++ = use_thin_pack ? "--objects-edge" : "--objects"; *p++ = use_thin_pack ? "--objects-edge" : "--objects";
if (create_full_pack || MAX_NEEDS <= nr_needs) if (create_full_pack || MAX_NEEDS <= nr_needs)
@ -86,11 +134,184 @@ static void create_pack_file(void)
execv_git_cmd(argv); execv_git_cmd(argv);
die("git-upload-pack: unable to exec git-rev-list"); die("git-upload-pack: unable to exec git-rev-list");
} }
dup2(fd[0], 0);
close(fd[0]); if (pipe(pu_pipe) < 0)
close(fd[1]); die("git-upload-pack: unable to create pipe");
execl_git_cmd("pack-objects", "--stdout", NULL); if (pipe(pe_pipe) < 0)
die("git-upload-pack: unable to exec git-pack-objects"); 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) static int got_sha1(char *hex, unsigned char *sha1)
@ -197,6 +418,8 @@ static int receive_needs(void)
multi_ack = 1; multi_ack = 1;
if (strstr(line+45, "thin-pack")) if (strstr(line+45, "thin-pack"))
use_thin_pack = 1; use_thin_pack = 1;
if (strstr(line+45, "side-band"))
use_sideband = 1;
/* We have sent all our refs already, and the other end /* We have sent all our refs already, and the other end
* should have chosen out of them; otherwise they are * 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 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); struct object *o = parse_object(sha1);
if (!o) if (!o)