#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>";
return safe_write(fd, data, sz);
}
-FILE *pack_pipe = NULL;
+static FILE *pack_pipe = NULL;
static void show_commit(struct commit *commit)
{
if (commit->object.flags & BOUNDARY)
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);
+ }
+ prepare_revision_walk(&revs);
+ mark_edges_uninteresting(revs.commits, &revs, show_edge);
+ traverse_commit_list(&revs, show_commit, show_object);
+ 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)
+ 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");
- 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);
+ argv[arg++] = "pack-objects";
+ argv[arg++] = "--stdout";
+ if (!no_progress)
+ argv[arg++] = "--progress";
+ if (use_ofs_delta)
+ argv[arg++] = "--delta-base-offset";
+ 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");
- }
- 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.
+ /* 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();
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++;
+ }
+ sz = xread(pack_objects.out, cp,
+ sizeof(data) - outsz);
+ if (0 < sz)
+ ;
+ else if (sz == 0) {
+ close(pack_objects.out);
+ pack_objects.out = -1;
}
- if (!WIFEXITED(status) || WEXITSTATUS(status) > 0) {
- error("git-upload-pack: %s died with error.",
- who);
+ 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);
}
break;
}
}
-
+
if (i != argc-1)
usage(upload_pack_usage);
dir = argv[i];