Staging
v0.5.1
Revision fc4c4cd21c783b6dc387002c6e018d26f7405e9f authored by Junio C Hamano on 04 April 2006, 21:52:53 UTC, committed by Junio C Hamano on 04 April 2006, 21:52:53 UTC
Bunch of cleanups with a few notable enhancements since
1.3.0-rc1:

 - revision traversal infrastructure is updated so that
   existence of paths limiters and/or --max-age does not cause
   it to call limit_list().  This helps the latency working with
   the command quite a bit.

 - comes with updated gitk.

One notable fix is to make sure that the IO is restarted upon
signal even on platforms whose default signal semantics is not
to do so.  This is the fix for the notorious "clone is broken
since 1.2.2 on Solaris" problem.

Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 3d9c54d
Raw File
fetch-clone.c
#include "cache.h"
#include "exec_cmd.h"
#include <sys/wait.h>
#include <sys/time.h>

static int finish_pack(const char *pack_tmp_name, const char *me)
{
	int pipe_fd[2];
	pid_t pid;
	char idx[PATH_MAX];
	char final[PATH_MAX];
	char hash[41];
	unsigned char sha1[20];
	char *cp;
	int err = 0;

	if (pipe(pipe_fd) < 0)
		die("%s: unable to set up pipe", me);

	strcpy(idx, pack_tmp_name); /* ".git/objects/pack-XXXXXX" */
	cp = strrchr(idx, '/');
	memcpy(cp, "/pidx", 5);

	pid = fork();
	if (pid < 0)
		die("git-clone-pack: unable to fork off git-index-pack");
	if (!pid) {
		close(0);
		dup2(pipe_fd[1], 1);
		close(pipe_fd[0]);
		close(pipe_fd[1]);
		execl_git_cmd("index-pack", "-o", idx, pack_tmp_name, NULL);
		error("cannot exec git-index-pack <%s> <%s>",
		      idx, pack_tmp_name);
		exit(1);
	}
	close(pipe_fd[1]);
	if (read(pipe_fd[0], hash, 40) != 40) {
		error("%s: unable to read from git-index-pack", me);
		err = 1;
	}
	close(pipe_fd[0]);

	for (;;) {
		int status, code;
		int retval = waitpid(pid, &status, 0);

		if (retval < 0) {
			if (errno == EINTR)
				continue;
			error("waitpid failed (%s)", strerror(errno));
			goto error_die;
		}
		if (WIFSIGNALED(status)) {
			int sig = WTERMSIG(status);
			error("git-index-pack died of signal %d", sig);
			goto error_die;
		}
		if (!WIFEXITED(status)) {
			error("git-index-pack died of unnatural causes %d",
			      status);
			goto error_die;
		}
		code = WEXITSTATUS(status);
		if (code) {
			error("git-index-pack died with error code %d", code);
			goto error_die;
		}
		if (err)
			goto error_die;
		break;
	}
	hash[40] = 0;
	if (get_sha1_hex(hash, sha1)) {
		error("git-index-pack reported nonsense '%s'", hash);
		goto error_die;
	}
	/* Now we have pack in pack_tmp_name[], and
	 * idx in idx[]; rename them to their final names.
	 */
	snprintf(final, sizeof(final),
		 "%s/pack/pack-%s.pack", get_object_directory(), hash);
	move_temp_to_file(pack_tmp_name, final);
	chmod(final, 0444);
	snprintf(final, sizeof(final),
		 "%s/pack/pack-%s.idx", get_object_directory(), hash);
	move_temp_to_file(idx, final);
	chmod(final, 0444);
	return 0;

 error_die:
	unlink(idx);
	unlink(pack_tmp_name);
	exit(1);
}

int receive_unpack_pack(int fd[2], const char *me, int quiet)
{
	int status;
	pid_t pid;

	pid = fork();
	if (pid < 0)
		die("%s: unable to fork off git-unpack-objects", me);
	if (!pid) {
		dup2(fd[0], 0);
		close(fd[0]);
		close(fd[1]);
		execl_git_cmd("unpack-objects", quiet ? "-q" : NULL, NULL);
		die("git-unpack-objects exec failed");
	}
	close(fd[0]);
	close(fd[1]);
	while (waitpid(pid, &status, 0) < 0) {
		if (errno != EINTR)
			die("waiting for git-unpack-objects: %s",
			    strerror(errno));
	}
	if (WIFEXITED(status)) {
		int code = WEXITSTATUS(status);
		if (code)
			die("git-unpack-objects died with error code %d",
			    code);
		return 0;
	}
	if (WIFSIGNALED(status)) {
		int sig = WTERMSIG(status);
		die("git-unpack-objects died of signal %d", sig);
	}
	die("git-unpack-objects died of unnatural causes %d", status);
}

/*
 * We average out the download speed over this many "events", where
 * an event is a minimum of about half a second. That way, we get
 * a reasonably stable number.
 */
#define NR_AVERAGE (4)

/*
 * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second.
 * Keeing the time in that format means that "bytes / msecs" means
 * is the same as kB/s (modulo rounding).
 *
 * 1000512 is a magic number (usecs in a second, rounded up by half
 * of 1024, to make "rounding" come out right ;)
 */
#define usec_to_binarymsec(x) ((int)(x) / (1000512 >> 10))

int receive_keep_pack(int fd[2], const char *me, int quiet)
{
	char tmpfile[PATH_MAX];
	int ofd, ifd;
	unsigned long total;
	static struct timeval prev_tv;
	struct average {
		unsigned long bytes;
		unsigned long time;
	} download[NR_AVERAGE] = { {0, 0}, };
	unsigned long avg_bytes, avg_time;
	int idx = 0;

	ifd = fd[0];
	snprintf(tmpfile, sizeof(tmpfile),
		 "%s/pack/tmp-XXXXXX", get_object_directory());
	ofd = mkstemp(tmpfile);
	if (ofd < 0)
		return error("unable to create temporary file %s", tmpfile);

	gettimeofday(&prev_tv, NULL);
	total = 0;
	avg_bytes = 0;
	avg_time = 0;
	while (1) {
		char buf[8192];
		ssize_t sz, wsz, pos;
		sz = read(ifd, buf, sizeof(buf));
		if (sz == 0)
			break;
		if (sz < 0) {
			if (errno != EINTR && errno != EAGAIN) {
				error("error reading pack (%s)", strerror(errno));
				close(ofd);
				unlink(tmpfile);
				return -1;
			}
			sz = 0;
		}
		pos = 0;
		while (pos < sz) {
			wsz = write(ofd, buf + pos, sz - pos);
			if (wsz < 0) {
				error("error writing pack (%s)",
				      strerror(errno));
				close(ofd);
				unlink(tmpfile);
				return -1;
			}
			pos += wsz;
		}
		total += sz;
		if (!quiet) {
			static unsigned long last;
			struct timeval tv;
			unsigned long diff = total - last;
			/* not really "msecs", but a power-of-two millisec (1/1024th of a sec) */
			unsigned long msecs;

			gettimeofday(&tv, NULL);
			msecs = tv.tv_sec - prev_tv.tv_sec;
			msecs <<= 10;
			msecs += usec_to_binarymsec(tv.tv_usec - prev_tv.tv_usec);

			if (msecs > 500) {
				prev_tv = tv;
				last = total;

				/* Update averages ..*/
				avg_bytes += diff;
				avg_time += msecs;
				avg_bytes -= download[idx].bytes;
				avg_time -= download[idx].time;
				download[idx].bytes = diff;
				download[idx].time = msecs;
				idx++;
				if (idx >= NR_AVERAGE)
					idx = 0;

				fprintf(stderr, "%4lu.%03luMB  (%lu kB/s)      \r",
					total >> 20,
					1000*((total >> 10) & 1023)>>10,
					avg_bytes / avg_time );
			}
		}
	}
	close(ofd);
	return finish_pack(tmpfile, me);
}
back to top