Merge branch 'ew/rebase' into next

* ew/rebase:
  rebase --merge: fix for rebasing more than 7 commits.
diff --git a/.gitignore b/.gitignore
index afd0876..65aa939 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,7 +17,6 @@
 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 09f43ee..0000000
--- 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 a90521e..f973c64 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -62,7 +62,7 @@
 --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 @@
 --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 60debca..f9457d4 100644
--- a/Documentation/git-receive-pack.txt
+++ b/Documentation/git-receive-pack.txt
@@ -18,8 +18,7 @@
 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-repo-config.txt b/Documentation/git-repo-config.txt
index d5142e0..803c0d5 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -73,6 +73,18 @@
 	List all variables set in .git/config.
 
 
+ENVIRONMENT
+-----------
+
+GIT_CONFIG::
+	Take the configuration from the given file instead of .git/config.
+
+GIT_CONFIG_LOCAL::
+	Currently the same as $GIT_CONFIG; when Git will support global
+	configuration files, this will cause it to take the configuration
+	from the global configuration file in addition to the given file.
+
+
 EXAMPLE
 -------
 
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index 4795e98..b2c9307 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -12,7 +12,7 @@
 
 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 d4472b5..51f20c6 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -192,10 +192,6 @@
 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 @@
 	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]::
diff --git a/INSTALL b/INSTALL
index 63af8ec..f8337e2 100644
--- a/INSTALL
+++ b/INSTALL
@@ -96,7 +96,7 @@
 
 	$ 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
diff --git a/Makefile b/Makefile
index 0887945..de76141 100644
--- a/Makefile
+++ b/Makefile
@@ -144,34 +144,33 @@
 
 # The ones that do not have to link with lcrypto, lz nor xdiff.
 SIMPLE_PROGRAMS = \
-	git-mailsplit$X \
-	git-stripspace$X git-daemon$X
+	git-daemon$X
 
 # ... 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-mailinfo$X git-merge-base$X \
+	git-merge-base$X \
 	git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
 	git-peek-remote$X git-prune-packed$X git-receive-pack$X \
 	git-send-pack$X git-shell$X \
 	git-show-index$X git-ssh-fetch$X \
 	git-ssh-upload$X git-unpack-file$X \
-	git-unpack-objects$X git-update-index$X git-update-server-info$X \
-	git-upload-pack$X git-verify-pack$X git-write-tree$X \
-	git-update-ref$X git-symbolic-ref$X \
+	git-unpack-objects$X git-update-server-info$X \
+	git-upload-pack$X git-verify-pack$X \
+	git-symbolic-ref$X \
 	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
 	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
 
-BUILT_INS = git-log$X git-whatchanged$X git-show$X \
-	git-count-objects$X git-diff$X git-push$X \
-	git-grep$X git-add$X git-rm$X git-rev-list$X \
-	git-check-ref-format$X git-rev-parse$X \
+BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
+	git-count-objects$X git-diff$X git-push$X git-mailsplit$X \
+	git-grep$X git-add$X git-rm$X git-rev-list$X git-stripspace$X \
+	git-check-ref-format$X git-rev-parse$X git-mailinfo$X \
 	git-init-db$X git-tar-tree$X git-upload-tar$X git-format-patch$X \
 	git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
-	git-read-tree$X git-commit-tree$X \
-	git-apply$X git-show-branch$X git-diff-files$X \
+	git-read-tree$X git-commit-tree$X git-write-tree$X \
+	git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
 	git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
@@ -222,12 +221,13 @@
 	builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
 	builtin-grep.o builtin-add.o builtin-rev-list.o builtin-check-ref-format.o \
 	builtin-rm.o builtin-init-db.o builtin-rev-parse.o \
-	builtin-tar-tree.o builtin-upload-tar.o \
-	builtin-ls-files.o builtin-ls-tree.o \
-	builtin-read-tree.o builtin-commit-tree.o \
+	builtin-tar-tree.o builtin-upload-tar.o builtin-update-index.o \
+	builtin-ls-files.o builtin-ls-tree.o builtin-write-tree.o \
+	builtin-read-tree.o builtin-commit-tree.o builtin-mailinfo.o \
 	builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
 	builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
-	builtin-cat-file.o
+	builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
+	builtin-update-ref.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -380,9 +380,7 @@
 	else
 		ICONV_LINK =
 	endif
-	LIB_4_ICONV = $(ICONV_LINK) -liconv
-else
-	LIB_4_ICONV =
+	LIBS += $(ICONV_LINK) -liconv
 endif
 ifdef NEEDS_SOCKET
 	LIBS += -lsocket
@@ -565,10 +563,6 @@
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIB_FILE) $(SIMPLE_LIB)
 
-git-mailinfo$X: mailinfo.o $(LIB_FILE)
-	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-		$(LIB_FILE) $(SIMPLE_LIB) $(LIB_4_ICONV)
-
 git-local-fetch$X: fetch.o
 git-ssh-fetch$X: rsh.o fetch.o
 git-ssh-upload$X: rsh.o
diff --git a/blame.c b/blame.c
index 7c0b62c..c86e2fd 100644
--- a/blame.c
+++ b/blame.c
@@ -301,9 +301,9 @@
 				if (DEBUG)
 					printf("map: i1: %d %d %p i2: %d %d %p\n",
 					       i1, map[i1],
-					       i1 != -1 ? blame_lines[map[i1]] : NULL,
+					       (void *) (i1 != -1 ? blame_lines[map[i1]] : NULL),
 					       i2, map2[i2],
-					       i2 != -1 ? blame_lines[map2[i2]] : NULL);
+					       (void *) (i2 != -1 ? blame_lines[map2[i2]] : NULL));
 				if (map2[i2] != -1 &&
 				    blame_lines[map[i1]] &&
 				    !blame_lines[map2[i2]])
diff --git a/builtin-apply.c b/builtin-apply.c
index e113c74..6dd0472 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -148,7 +148,7 @@
 			buffer = xrealloc(buffer, alloc);
 			nr = alloc - size;
 		}
-		nr = xread(fd, buffer + size, nr);
+		nr = xread(fd, (char *) buffer + size, nr);
 		if (!nr)
 			break;
 		if (nr < 0)
@@ -164,7 +164,7 @@
 	 */
 	if (alloc < size + SLOP)
 		buffer = xrealloc(buffer, size + SLOP);
-	memset(buffer + size, 0, SLOP);
+	memset((char *) buffer + size, 0, SLOP);
 	return buffer;
 }
 
@@ -1194,7 +1194,7 @@
 			return error("unable to open %s", path);
 		got = 0;
 		for (;;) {
-			int ret = xread(fd, buf + got, size - got);
+			int ret = xread(fd, (char *) buf + got, size - got);
 			if (ret <= 0)
 				break;
 			got += ret;
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index cebda82..5afc1d7 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -41,7 +41,7 @@
 	 * rev.max_count is reasonable (0 <= n <= 3),
 	 * there is no other revision filtering parameters.
 	 */
-	if (rev.pending_objects ||
+	if (rev.pending.nr ||
 	    rev.min_age != -1 || rev.max_age != -1)
 		usage(diff_files_usage);
 	/*
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index 1958580..c42ef9a 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -32,7 +32,7 @@
 	 * Make sure there is one revision (i.e. pending object),
 	 * and there is no revision filtering parameters.
 	 */
-	if (!rev.pending_objects || rev.pending_objects->next ||
+	if (rev.pending.nr != 1 ||
 	    rev.max_count != -1 || rev.min_age != -1 || rev.max_age != -1)
 		usage(diff_cache_usage);
 	return run_diff_index(&rev, cached);
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index 58cf658..3409a39 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -65,7 +65,6 @@
 	char line[1000];
 	struct object *tree1, *tree2;
 	static struct rev_info *opt = &log_tree_opt;
-	struct object_list *list;
 	int read_stdin = 0;
 
 	git_config(git_diff_config);
@@ -86,45 +85,28 @@
 	}
 
 	/*
-	 * NOTE! "setup_revisions()" will have inserted the revisions
-	 * it parsed in reverse order. So if you do
-	 *
-	 *	git-diff-tree a b
-	 *
-	 * the commit list will be "b" -> "a" -> NULL, so we reverse
-	 * the order of the objects if the first one is not marked
-	 * UNINTERESTING.
+	 * NOTE! We expect "a ^b" to be equal to "a..b", so we
+	 * reverse the order of the objects if the second one
+	 * is marked UNINTERESTING.
 	 */
-	nr_sha1 = 0;
-	list = opt->pending_objects;
-	if (list) {
-		nr_sha1++;
-		tree1 = list->item;
-		list = list->next;
-		if (list) {
-			nr_sha1++;
-			tree2 = tree1;
-			tree1 = list->item;
-			if (list->next)
-				usage(diff_tree_usage);
-			/* Switch them around if the second one was uninteresting.. */
-			if (tree2->flags & UNINTERESTING) {
-				struct object *tmp = tree2;
-				tree2 = tree1;
-				tree1 = tmp;
-			}
-		}
-	}
-
+	nr_sha1 = opt->pending.nr;
 	switch (nr_sha1) {
 	case 0:
 		if (!read_stdin)
 			usage(diff_tree_usage);
 		break;
 	case 1:
+		tree1 = opt->pending.objects[0].item;
 		diff_tree_commit_sha1(tree1->sha1);
 		break;
 	case 2:
+		tree1 = opt->pending.objects[0].item;
+		tree2 = opt->pending.objects[1].item;
+		if (tree2->flags & UNINTERESTING) {
+			struct object *tmp = tree2;
+			tree2 = tree1;
+			tree1 = tmp;
+		}
 		diff_tree_sha1(tree1->sha1,
 			       tree2->sha1,
 			       "", &opt->diffopt);
diff --git a/builtin-diff.c b/builtin-diff.c
index 6ac3d4b..99a2f76 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -50,7 +50,7 @@
 	 * specified rev.max_count is reasonable (0 <= n <= 3), and
 	 * there is no other revision filtering parameter.
 	 */
-	if (revs->pending_objects ||
+	if (revs->pending.nr ||
 	    revs->min_age != -1 ||
 	    revs->max_age != -1 ||
 	    3 < revs->max_count)
@@ -172,7 +172,7 @@
 	 * Make sure there is one revision (i.e. pending object),
 	 * and there is no revision filtering parameters.
 	 */
-	if (!revs->pending_objects || revs->pending_objects->next ||
+	if (revs->pending.nr != 1 ||
 	    revs->max_count != -1 || revs->min_age != -1 ||
 	    revs->max_age != -1)
 		usage(builtin_diff_usage);
@@ -181,10 +181,10 @@
 
 static int builtin_diff_tree(struct rev_info *revs,
 			     int argc, const char **argv,
-			     struct object_list *ent)
+			     struct object_array_entry *ent)
 {
 	const unsigned char *(sha1[2]);
-	int swap = 1;
+	int swap = 0;
 	while (1 < argc) {
 		const char *arg = argv[1];
 		if (!strcmp(arg, "--raw"))
@@ -195,10 +195,10 @@
 	}
 
 	/* We saw two trees, ent[0] and ent[1].
-	 * unless ent[0] is unintesting, they are swapped
+	 * if ent[1] is unintesting, they are swapped
 	 */
-	if (ent[0].item->flags & UNINTERESTING)
-		swap = 0;
+	if (ent[1].item->flags & UNINTERESTING)
+		swap = 1;
 	sha1[swap] = ent[0].item->sha1;
 	sha1[1-swap] = ent[1].item->sha1;
 	diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
@@ -208,7 +208,7 @@
 
 static int builtin_diff_combined(struct rev_info *revs,
 				 int argc, const char **argv,
-				 struct object_list *ent,
+				 struct object_array_entry *ent,
 				 int ents)
 {
 	const unsigned char (*parent)[20];
@@ -242,13 +242,14 @@
 	obj = parse_object(sha1);
 	if (!obj)
 		return;
-	add_object(obj, &revs->pending_objects, NULL, "HEAD");
+	add_pending_object(revs, obj, "HEAD");
 }
 
 int cmd_diff(int argc, const char **argv, char **envp)
 {
+	int i;
 	struct rev_info rev;
-	struct object_list *list, ent[100];
+	struct object_array_entry ent[100];
 	int ents = 0, blobs = 0, paths = 0;
 	const char *path = NULL;
 	struct blobinfo blob[2];
@@ -281,7 +282,7 @@
 	/* Do we have --cached and not have a pending object, then
 	 * default to HEAD by hand.  Eek.
 	 */
-	if (!rev.pending_objects) {
+	if (!rev.pending.nr) {
 		int i;
 		for (i = 1; i < argc; i++) {
 			const char *arg = argv[i];
@@ -294,7 +295,8 @@
 		}
 	}
 
-	for (list = rev.pending_objects; list; list = list->next) {
+	for (i = 0; i < rev.pending.nr; i++) {
+		struct object_array_entry *list = rev.pending.objects+i;
 		struct object *obj = list->item;
 		const char *name = list->name;
 		int flags = (obj->flags & UNINTERESTING);
diff --git a/builtin-grep.c b/builtin-grep.c
index f7767bb..2e7986c 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -658,7 +658,7 @@
 	int cached = 0;
 	int seen_dashdash = 0;
 	struct grep_opt opt;
-	struct object_list *list, **tail, *object_list = NULL;
+	struct object_array list = { 0, 0, NULL };
 	const char *prefix = setup_git_directory();
 	const char **paths = NULL;
 	int i;
@@ -678,7 +678,6 @@
 	 * that continues up to the -- (if exists), and then paths.
 	 */
 
-	tail = &object_list;
 	while (1 < argc) {
 		const char *arg = argv[1];
 		argc--; argv++;
@@ -852,12 +851,9 @@
 		/* Is it a rev? */
 		if (!get_sha1(arg, sha1)) {
 			struct object *object = parse_object(sha1);
-			struct object_list *elem;
 			if (!object)
 				die("bad object %s", arg);
-			elem = object_list_insert(object, tail);
-			elem->name = arg;
-			tail = &elem->next;
+			add_object_array(object, arg, &list);
 			continue;
 		}
 		if (!strcmp(arg, "--")) {
@@ -882,16 +878,16 @@
 		paths[1] = NULL;
 	}
 
-	if (!object_list)
+	if (!list.nr)
 		return !grep_cache(&opt, paths, cached);
 
 	if (cached)
 		die("both --cached and trees are given.");
 
-	for (list = object_list; list; list = list->next) {
+	for (i = 0; i < list.nr; i++) {
 		struct object *real_obj;
-		real_obj = deref_tag(list->item, NULL, 0);
-		if (grep_object(&opt, paths, real_obj, list->name))
+		real_obj = deref_tag(list.objects[i].item, NULL, 0);
+		if (grep_object(&opt, paths, real_obj, list.objects[i].name))
 			hit = 1;
 	}
 	return !hit;
diff --git a/builtin-log.c b/builtin-log.c
index 9187fd3..5a8a50b 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -257,8 +257,8 @@
 			    output_directory);
 	}
 
-	if (rev.pending_objects && rev.pending_objects->next == NULL) {
-		rev.pending_objects->item->flags |= UNINTERESTING;
+	if (rev.pending.nr == 1) {
+		rev.pending.objects[0].item->flags |= UNINTERESTING;
 		add_head(&rev);
 	}
 
diff --git a/mailinfo.c b/builtin-mailinfo.c
similarity index 94%
rename from mailinfo.c
rename to builtin-mailinfo.c
index d9b74f3..821642a 100644
--- a/mailinfo.c
+++ b/builtin-mailinfo.c
@@ -12,11 +12,12 @@
 #endif
 #include "git-compat-util.h"
 #include "cache.h"
+#include "builtin.h"
 
-static FILE *cmitmsg, *patchfile;
+static FILE *cmitmsg, *patchfile, *fin, *fout;
 
 static int keep_subject = 0;
-static char *metainfo_charset = NULL;
+static const char *metainfo_charset = NULL;
 static char line[1000];
 static char date[1000];
 static char name[1000];
@@ -49,7 +50,7 @@
 
 	/* This is fallback, so do not bother if we already have an
 	 * e-mail address.
-	 */ 
+	 */
 	if (*email)
 		return 0;
 
@@ -322,13 +323,13 @@
 			if (remove <= len *2) {
 				subject = p+1;
 				continue;
-			}	
+			}
 			break;
 		}
 		eatspace(subject);
 		return subject;
 	}
-}			
+}
 
 static void cleanup_space(char *buf)
 {
@@ -648,7 +649,7 @@
 	cleanup_space(email);
 	cleanup_space(sub);
 
-	printf("Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
+	fprintf(fout, "Author: %s\nEmail: %s\nSubject: %s\nDate: %s\n\n",
 	       name, email, sub, date);
 }
 
@@ -685,7 +686,7 @@
 			continue;
 
 		fputs(line, cmitmsg);
-	} while (fgets(line, sizeof(line), stdin) != NULL);
+	} while (fgets(line, sizeof(line), fin) != NULL);
 	fclose(cmitmsg);
 	cmitmsg = NULL;
 	return 0;
@@ -706,7 +707,7 @@
 		decode_transfer_encoding(line);
 		fputs(line, patchfile);
 		patch_lines++;
-	} while (fgets(line, sizeof(line), stdin) != NULL);
+	} while (fgets(line, sizeof(line), fin) != NULL);
 }
 
 /* multipart boundary and transfer encoding are set up for us, and we
@@ -719,7 +720,7 @@
 {
 	int n = 0;
 
-	while (fgets(line, sizeof(line), stdin) != NULL) {
+	while (fgets(line, sizeof(line), fin) != NULL) {
 	again:
 		n++;
 		if (is_multipart_boundary(line))
@@ -740,7 +741,7 @@
 	int part_num = 0;
 
 	/* Skip up to the first boundary */
-	while (fgets(line, sizeof(line), stdin) != NULL)
+	while (fgets(line, sizeof(line), fin) != NULL)
 		if (is_multipart_boundary(line)) {
 			part_num = 1;
 			break;
@@ -749,7 +750,7 @@
 		return;
 	/* We are on boundary line.  Start slurping the subhead. */
 	while (1) {
-		int hdr = read_one_header_line(line, sizeof(line), stdin);
+		int hdr = read_one_header_line(line, sizeof(line), fin);
 		if (!hdr) {
 			if (handle_multipart_one_part(&seen) < 0)
 				return;
@@ -781,10 +782,45 @@
 	}
 }
 
+int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
+	     const char *msg, const char *patch)
+{
+	keep_subject = ks;
+	metainfo_charset = encoding;
+	fin = in;
+	fout = out;
+
+	cmitmsg = fopen(msg, "w");
+	if (!cmitmsg) {
+		perror(msg);
+		return -1;
+	}
+	patchfile = fopen(patch, "w");
+	if (!patchfile) {
+		perror(patch);
+		fclose(cmitmsg);
+		return -1;
+	}
+	while (1) {
+		int hdr = read_one_header_line(line, sizeof(line), fin);
+		if (!hdr) {
+			if (multipart_boundary[0])
+				handle_multipart_body();
+			else
+				handle_body();
+			handle_info();
+			break;
+		}
+		check_header_line(line);
+	}
+
+	return 0;
+}
+
 static const char mailinfo_usage[] =
 	"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
 
-int main(int argc, char **argv)
+int cmd_mailinfo(int argc, const char **argv, char **envp)
 {
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
@@ -805,27 +841,6 @@
 
 	if (argc != 3)
 		usage(mailinfo_usage);
-	cmitmsg = fopen(argv[1], "w");
-	if (!cmitmsg) {
-		perror(argv[1]);
-		exit(1);
-	}
-	patchfile = fopen(argv[2], "w");
-	if (!patchfile) {
-		perror(argv[2]);
-		exit(1);
-	}
-	while (1) {
-		int hdr = read_one_header_line(line, sizeof(line), stdin);
-		if (!hdr) {
-			if (multipart_boundary[0])
-				handle_multipart_body();
-			else
-				handle_body();
-			handle_info();
-			break;
-		}
-		check_header_line(line);
-	}
-	return 0;
+
+	return !!mailinfo(stdin, stdout, keep_subject, metainfo_charset, argv[1], argv[2]);
 }
diff --git a/mailsplit.c b/builtin-mailsplit.c
similarity index 84%
rename from mailsplit.c
rename to builtin-mailsplit.c
index 70a569c..e2a0058 100644
--- a/mailsplit.c
+++ b/builtin-mailsplit.c
@@ -12,6 +12,7 @@
 #include <string.h>
 #include <stdio.h>
 #include "cache.h"
+#include "builtin.h"
 
 static const char git_mailsplit_usage[] =
 "git-mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> <mbox>...";
@@ -102,14 +103,48 @@
 	exit(1);
 }
 
-int main(int argc, const char **argv)
+int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip)
 {
-	int nr = 0, nr_prec = 4;
+	char *name = xmalloc(strlen(dir) + 2 + 3 * sizeof(skip));
+	int ret = -1;
+
+	while (*mbox) {
+		const char *file = *mbox++;
+		FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
+		int file_done = 0;
+
+		if ( !f ) {
+			error("cannot open mbox %s", file);
+			goto out;
+		}
+
+		if (fgets(buf, sizeof(buf), f) == NULL) {
+			if (f == stdin)
+				break; /* empty stdin is OK */
+			error("cannot read mbox %s", file);
+			goto out;
+		}
+
+		while (!file_done) {
+			sprintf(name, "%s/%0*d", dir, nr_prec, ++skip);
+			file_done = split_one(f, name, allow_bare);
+		}
+
+		if (f != stdin)
+			fclose(f);
+	}
+	ret = skip;
+out:
+	free(name);
+	return ret;
+}
+int cmd_mailsplit(int argc, const char **argv, char **envp)
+{
+	int nr = 0, nr_prec = 4, ret;
 	int allow_bare = 0;
 	const char *dir = NULL;
 	const char **argp;
 	static const char *stdin_only[] = { "-", NULL };
-	char *name;
 
 	for (argp = argv+1; *argp; argp++) {
 		const char *arg = *argp;
@@ -158,31 +193,9 @@
 			argp = stdin_only;
 	}
 
-	name = xmalloc(strlen(dir) + 2 + 3 * sizeof(nr));
+	ret = split_mbox(argp, dir, allow_bare, nr_prec, nr);
+	if (ret != -1)
+		printf("%d\n", ret);
 
-	while (*argp) {
-		const char *file = *argp++;
-		FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
-		int file_done = 0;
-
-		if ( !f )
-			die ("cannot open mbox %s", file);
-
-		if (fgets(buf, sizeof(buf), f) == NULL) {
-			if (f == stdin)
-				break; /* empty stdin is OK */
-			die("cannot read mbox %s", file);
-		}
-
-		while (!file_done) {
-			sprintf(name, "%s/%0*d", dir, nr_prec, ++nr);
-			file_done = split_one(f, name, allow_bare);
-		}
-
-		if (f != stdin)
-			fclose(f);
-	}
-
-	printf("%d\n", nr);
-	return 0;
+	return ret == -1;
 }
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 04506da..9a2099d 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -31,8 +31,7 @@
 
 static struct object_list *trees = NULL;
 
-static struct cache_entry df_conflict_entry = {
-};
+static struct cache_entry df_conflict_entry;
 
 struct tree_entry_list {
 	struct tree_entry_list *next;
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 71353eb..63bad0e 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -99,26 +99,26 @@
 	}
 }
 
-static struct object_list **process_blob(struct blob *blob,
-					 struct object_list **p,
-					 struct name_path *path,
-					 const char *name)
+static void process_blob(struct blob *blob,
+			 struct object_array *p,
+			 struct name_path *path,
+			 const char *name)
 {
 	struct object *obj = &blob->object;
 
 	if (!revs.blob_objects)
-		return p;
+		return;
 	if (obj->flags & (UNINTERESTING | SEEN))
-		return p;
+		return;
 	obj->flags |= SEEN;
 	name = strdup(name);
-	return add_object(obj, p, path, name);
+	add_object(obj, p, path, name);
 }
 
-static struct object_list **process_tree(struct tree *tree,
-					 struct object_list **p,
-					 struct name_path *path,
-					 const char *name)
+static void process_tree(struct tree *tree,
+			 struct object_array *p,
+			 struct name_path *path,
+			 const char *name)
 {
 	struct object *obj = &tree->object;
 	struct tree_desc desc;
@@ -126,14 +126,14 @@
 	struct name_path me;
 
 	if (!revs.tree_objects)
-		return p;
+		return;
 	if (obj->flags & (UNINTERESTING | SEEN))
-		return p;
+		return;
 	if (parse_tree(tree) < 0)
 		die("bad tree object %s", sha1_to_hex(obj->sha1));
 	obj->flags |= SEEN;
 	name = strdup(name);
-	p = add_object(obj, p, path, name);
+	add_object(obj, p, path, name);
 	me.up = path;
 	me.elem = name;
 	me.elem_len = strlen(name);
@@ -143,57 +143,59 @@
 
 	while (tree_entry(&desc, &entry)) {
 		if (S_ISDIR(entry.mode))
-			p = process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+			process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
 		else
-			p = process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+			process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
 	}
 	free(tree->buffer);
 	tree->buffer = NULL;
-	return p;
 }
 
 static void show_commit_list(struct rev_info *revs)
 {
+	int i;
 	struct commit *commit;
-	struct object_list *objects = NULL, **p = &objects, *pending;
+	struct object_array objects = { 0, 0, NULL };
 
 	while ((commit = get_revision(revs)) != NULL) {
-		p = process_tree(commit->tree, p, NULL, "");
+		process_tree(commit->tree, &objects, NULL, "");
 		show_commit(commit);
 	}
-	for (pending = revs->pending_objects; pending; pending = pending->next) {
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object_array_entry *pending = revs->pending.objects + i;
 		struct object *obj = pending->item;
 		const char *name = pending->name;
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
 		if (obj->type == TYPE_TAG) {
 			obj->flags |= SEEN;
-			p = add_object(obj, p, NULL, name);
+			add_object_array(obj, name, &objects);
 			continue;
 		}
 		if (obj->type == TYPE_TREE) {
-			p = process_tree((struct tree *)obj, p, NULL, name);
+			process_tree((struct tree *)obj, &objects, NULL, name);
 			continue;
 		}
 		if (obj->type == TYPE_BLOB) {
-			p = process_blob((struct blob *)obj, p, NULL, name);
+			process_blob((struct blob *)obj, &objects, NULL, name);
 			continue;
 		}
 		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
 	}
-	while (objects) {
+	for (i = 0; i < objects.nr; i++) {
+		struct object_array_entry *p = objects.objects + i;
+
 		/* An object with name "foo\n0000000..." can be used to
 		 * confuse downstream git-pack-objects very badly.
 		 */
-		const char *ep = strchr(objects->name, '\n');
+		const char *ep = strchr(p->name, '\n');
 		if (ep) {
-			printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
-			       (int) (ep - objects->name),
-			       objects->name);
+			printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
+			       (int) (ep - p->name),
+			       p->name);
 		}
 		else
-			printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
-		objects = objects->next;
+			printf("%s %s\n", sha1_to_hex(p->item->sha1), p->name);
 	}
 }
 
@@ -348,7 +350,7 @@
 
 	if ((!list &&
 	     (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
-	      !revs.pending_objects)) ||
+	      !revs.pending.nr)) ||
 	    revs.diff)
 		usage(rev_list_usage);
 
diff --git a/stripspace.c b/builtin-stripspace.c
similarity index 75%
rename from stripspace.c
rename to builtin-stripspace.c
index 65a6346..2ce1264 100644
--- a/stripspace.c
+++ b/builtin-stripspace.c
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
+#include "builtin.h"
 
 /*
  * Remove empty lines from the beginning and end.
@@ -28,21 +29,21 @@
 	return 1;
 }
 
-int main(int argc, char **argv)
+void stripspace(FILE *in, FILE *out)
 {
 	int empties = -1;
 	int incomplete = 0;
 	char line[1024];
 
-	while (fgets(line, sizeof(line), stdin)) {
+	while (fgets(line, sizeof(line), in)) {
 		incomplete = cleanup(line);
 
 		/* Not just an empty line? */
 		if (line[0] != '\n') {
 			if (empties > 0)
-				putchar('\n');
+				fputc('\n', out);
 			empties = 0;
-			fputs(line, stdout);
+			fputs(line, out);
 			continue;
 		}
 		if (empties < 0)
@@ -50,6 +51,11 @@
 		empties++;
 	}
 	if (incomplete)
-		putchar('\n');
+		fputc('\n', out);
+}
+
+int cmd_stripspace(int argc, const char **argv, char **envp)
+{
+	stripspace(stdin, stdout);
 	return 0;
 }
diff --git a/update-index.c b/builtin-update-index.c
similarity index 97%
rename from update-index.c
rename to builtin-update-index.c
index fbccc4a..ef50243 100644
--- a/update-index.c
+++ b/builtin-update-index.c
@@ -8,6 +8,7 @@
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
+#include "builtin.h"
 
 /*
  * Default to not allowing changes to the list of files. The
@@ -186,8 +187,6 @@
 	die("git-update-index: cannot chmod %cx '%s'", flip, path);
 }
 
-static struct lock_file lock_file;
-
 static void update_one(const char *path, const char *prefix, int prefix_length)
 {
 	const char *p = prefix_path(prefix, prefix_length, path);
@@ -238,7 +237,7 @@
 		 * (2) mode SP type SP sha1          TAB path
 		 * The second format is to stuff git-ls-tree output
 		 * into the index file.
-		 * 
+		 *
 		 * (3) mode         SP sha1 SP stage TAB path
 		 * This format is to put higher order stages into the
 		 * index file and matches git-ls-files --stage output.
@@ -477,7 +476,7 @@
 	return 0;
 }
 
-int main(int argc, const char **argv)
+int cmd_update_index(int argc, const char **argv, char **envp)
 {
 	int i, newfd, entries, has_errors = 0, line_termination = '\n';
 	int allow_options = 1;
@@ -486,12 +485,16 @@
 	int prefix_length = prefix ? strlen(prefix) : 0;
 	char set_executable_bit = 0;
 	unsigned int refresh_flags = 0;
+	struct lock_file *lock_file;
 
 	git_config(git_default_config);
 
-	newfd = hold_lock_file_for_update(&lock_file, get_index_file());
+	/* We can't free this memory, it becomes part of a linked list parsed atexit() */
+	lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	newfd = hold_lock_file_for_update(lock_file, get_index_file());
 	if (newfd < 0)
-		die("unable to create new index file");
+		die("unable to create new cachefile");
 
 	entries = read_cache();
 	if (entries < 0)
@@ -645,9 +648,11 @@
  finish:
 	if (active_cache_changed) {
 		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_lock_file(&lock_file))
+		    commit_lock_file(lock_file))
 			die("Unable to write new index file");
 	}
 
+	rollback_lock_file(lock_file);
+
 	return has_errors ? 1 : 0;
 }
diff --git a/update-ref.c b/builtin-update-ref.c
similarity index 88%
rename from update-ref.c
rename to builtin-update-ref.c
index a1e6bb9..00333c7 100644
--- a/update-ref.c
+++ b/builtin-update-ref.c
@@ -1,10 +1,11 @@
 #include "cache.h"
 #include "refs.h"
+#include "builtin.h"
 
 static const char git_update_ref_usage[] =
 "git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
 
-int main(int argc, char **argv)
+int cmd_update_ref(int argc, const char **argv, char **envp)
 {
 	const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
 	struct ref_lock *lock;
@@ -52,5 +53,7 @@
 		return 1;
 	if (write_ref_sha1(lock, sha1, msg) < 0)
 		return 1;
+
+	/* write_ref_sha1 always unlocks the ref, no need to do it explicitly */
 	return 0;
 }
diff --git a/write-tree.c b/builtin-write-tree.c
similarity index 66%
rename from write-tree.c
rename to builtin-write-tree.c
index bd07da6..70e9b6f 100644
--- a/write-tree.c
+++ b/builtin-write-tree.c
@@ -3,29 +3,72 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
+#include "builtin.h"
 #include "cache.h"
 #include "tree.h"
 #include "cache-tree.h"
 
-static int missing_ok = 0;
-static char *prefix = NULL;
-
 static const char write_tree_usage[] =
 "git-write-tree [--missing-ok] [--prefix=<prefix>/]";
 
-static struct lock_file lock_file;
-
-int main(int argc, char **argv)
+int write_tree(unsigned char *sha1, int missing_ok, const char *prefix)
 {
 	int entries, was_valid, newfd;
 
+	/* We can't free this memory, it becomes part of a linked list parsed atexit() */
+	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
+
+	newfd = hold_lock_file_for_update(lock_file, get_index_file());
+
+	entries = read_cache();
+	if (entries < 0)
+		die("git-write-tree: error reading cache");
+
+	if (!active_cache_tree)
+		active_cache_tree = cache_tree();
+
+	was_valid = cache_tree_fully_valid(active_cache_tree);
+
+	if (!was_valid) {
+		if (cache_tree_update(active_cache_tree,
+				      active_cache, active_nr,
+				      missing_ok, 0) < 0)
+			die("git-write-tree: error building trees");
+		if (0 <= newfd) {
+			if (!write_cache(newfd, active_cache, active_nr))
+				commit_lock_file(lock_file);
+		}
+		/* Not being able to write is fine -- we are only interested
+		 * in updating the cache-tree part, and if the next caller
+		 * ends up using the old index with unupdated cache-tree part
+		 * it misses the work we did here, but that is just a
+		 * performance penalty and not a big deal.
+		 */
+	}
+
+	if (prefix) {
+		struct cache_tree *subtree =
+			cache_tree_find(active_cache_tree, prefix);
+		memcpy(sha1, subtree->sha1, 20);
+	}
+	else
+		memcpy(sha1, active_cache_tree->sha1, 20);
+
+	rollback_lock_file(lock_file);
+
+	return 0;
+}
+
+int cmd_write_tree(int argc, const char **argv, char **envp)
+{
+	int missing_ok = 0, ret;
+	const char *prefix = NULL;
+	unsigned char sha1[20];
+
 	setup_git_directory();
 
-	newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-	entries = read_cache();
-
 	while (1 < argc) {
-		char *arg = argv[1];
+		const char *arg = argv[1];
 		if (!strcmp(arg, "--missing-ok"))
 			missing_ok = 1;
 		else if (!strncmp(arg, "--prefix=", 9))
@@ -38,35 +81,8 @@
 	if (argc > 2)
 		die("too many options");
 
-	if (entries < 0)
-		die("git-write-tree: error reading cache");
+	ret = write_tree(sha1, missing_ok, prefix);
+	printf("%s\n", sha1_to_hex(sha1));
 
-	if (!active_cache_tree)
-		active_cache_tree = cache_tree();
-
-	was_valid = cache_tree_fully_valid(active_cache_tree);
-	if (!was_valid) {
-		if (cache_tree_update(active_cache_tree,
-				      active_cache, active_nr,
-				      missing_ok, 0) < 0)
-			die("git-write-tree: error building trees");
-		if (0 <= newfd) {
-			if (!write_cache(newfd, active_cache, active_nr))
-				commit_lock_file(&lock_file);
-		}
-		/* Not being able to write is fine -- we are only interested
-		 * in updating the cache-tree part, and if the next caller
-		 * ends up using the old index with unupdated cache-tree part
-		 * it misses the work we did here, but that is just a
-		 * performance penalty and not a big deal.
-		 */
-	}
-	if (prefix) {
-		struct cache_tree *subtree =
-			cache_tree_find(active_cache_tree, prefix);
-		printf("%s\n", sha1_to_hex(subtree->sha1));
-	}
-	else
-		printf("%s\n", sha1_to_hex(active_cache_tree->sha1));
-	return 0;
+	return ret;
 }
diff --git a/builtin.h b/builtin.h
index b9f36be..f12d5e6 100644
--- a/builtin.h
+++ b/builtin.h
@@ -1,6 +1,8 @@
 #ifndef BUILTIN_H
 #define BUILTIN_H
 
+#include <stdio.h>
+
 #ifndef PATH_MAX
 # define PATH_MAX 4096
 #endif
@@ -45,5 +47,18 @@
 extern int cmd_diff_tree(int argc, const char **argv, char **envp);
 extern int cmd_cat_file(int argc, const char **argv, char **envp);
 extern int cmd_rev_parse(int argc, const char **argv, char **envp);
+extern int cmd_update_index(int argc, const char **argv, char **envp);
+extern int cmd_update_ref(int argc, const char **argv, char **envp);
 
+extern int cmd_write_tree(int argc, const char **argv, char **envp);
+extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
+
+extern int cmd_mailsplit(int argc, const char **argv, char **envp);
+extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
+
+extern int cmd_mailinfo(int argc, const char **argv, char **envp);
+extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
+
+extern int cmd_stripspace(int argc, const char **argv, char **envp);
+extern void stripspace(FILE *in, FILE *out);
 #endif
diff --git a/cache.h b/cache.h
index eaa5c0c..efeafea 100644
--- a/cache.h
+++ b/cache.h
@@ -374,8 +374,8 @@
 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 a4370f5..0000000
--- 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/config.c b/config.c
index 984c75f..3e077d4 100644
--- a/config.c
+++ b/config.c
@@ -317,7 +317,33 @@
 
 int git_config(config_fn_t fn)
 {
-	return git_config_from_file(fn, git_path("config"));
+	int ret = 0;
+	char *repo_config = NULL;
+	const char *home = NULL, *filename;
+
+	/* $GIT_CONFIG makes git read _only_ the given config file,
+	 * $GIT_CONFIG_LOCAL will make it process it in addition to the
+	 * global config file, the same way it would the per-repository
+	 * config file otherwise. */
+	filename = getenv("GIT_CONFIG");
+	if (!filename) {
+		home = getenv("HOME");
+		filename = getenv("GIT_CONFIG_LOCAL");
+		if (!filename)
+			filename = repo_config = strdup(git_path("config"));
+	}
+
+	if (home) {
+		char *user_config = strdup(mkpath("%s/.gitconfig", home));
+		if (!access(user_config, R_OK))
+			ret = git_config_from_file(fn, user_config);
+		free(user_config);
+	}
+
+	ret += git_config_from_file(fn, filename);
+	if (repo_config)
+		free(repo_config);
+	return ret;
 }
 
 /*
@@ -490,10 +516,19 @@
 	int i, dot;
 	int fd = -1, in_fd;
 	int ret;
-	char* config_filename = strdup(git_path("config"));
-	char* lock_file = strdup(git_path("config.lock"));
+	char* config_filename;
+	char* lock_file;
 	const char* last_dot = strrchr(key, '.');
 
+	config_filename = getenv("GIT_CONFIG");
+	if (!config_filename) {
+		config_filename = getenv("GIT_CONFIG_LOCAL");
+		if (!config_filename)
+			config_filename  = git_path("config");
+	}
+	config_filename = strdup(config_filename);
+	lock_file = strdup(mkpath("%s.lock", config_filename));
+
 	/*
 	 * Since "key" actually contains the section name and the real
 	 * key name separated by a dot, we have to know where the dot is.
@@ -600,7 +635,7 @@
 		 * As a side effect, we make sure to transform only a valid
 		 * existing config file.
 		 */
-		if (git_config(store_aux)) {
+		if (git_config_from_file(store_aux, config_filename)) {
 			fprintf(stderr, "invalid config file\n");
 			free(store.key);
 			if (store.value_regex != NULL) {
diff --git a/connect.c b/connect.c
index 52d709e..db7342e 100644
--- a/connect.c
+++ b/connect.c
@@ -581,6 +581,11 @@
 	enum protocol protocol = PROTO_LOCAL;
 	int free_path = 0;
 
+	/* Without this we cannot rely on waitpid() to tell
+	 * what happened to our children.
+	 */
+	signal(SIGCHLD, SIG_DFL);
+
 	host = strstr(url, "://");
 	if(host) {
 		*host = '\0';
diff --git a/convert-objects.c b/convert-objects.c
index a67d6b4..0fabd89 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -103,12 +103,12 @@
 		if (!slash) {
 			newlen += sprintf(new + newlen, "%o %s", mode, path);
 			new[newlen++] = '\0';
-			memcpy(new + newlen, buffer + len - 20, 20);
+			memcpy(new + newlen, (char *) buffer + len - 20, 20);
 			newlen += 20;
 
 			used += len;
 			size -= len;
-			buffer += len;
+			buffer = (char *) buffer + len;
 			continue;
 		}
 
@@ -121,7 +121,7 @@
 
 		used += len;
 		size -= len;
-		buffer += len;
+		buffer = (char *) buffer + len;
 	}
 
 	write_sha1_file(new, newlen, tree_type, result_sha1);
@@ -137,13 +137,13 @@
 	while (size) {
 		int len = 1+strlen(buffer);
 
-		convert_binary_sha1(buffer + len);
+		convert_binary_sha1((char *) buffer + len);
 
 		len += 20;
 		if (len > size)
 			die("corrupt tree object");
 		size -= len;
-		buffer += len;
+		buffer = (char *) buffer + len;
 	}
 
 	write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1);
@@ -244,14 +244,14 @@
 	// "tree <sha1>\n"
 	memcpy(new + newlen, buffer, 46);
 	newlen += 46;
-	buffer += 46;
+	buffer = (char *) buffer + 46;
 	size -= 46;
 
 	// "parent <sha1>\n"
 	while (!memcmp(buffer, "parent ", 7)) {
 		memcpy(new + newlen, buffer, 48);
 		newlen += 48;
-		buffer += 48;
+		buffer = (char *) buffer + 48;
 		size -= 48;
 	}
 
@@ -275,11 +275,11 @@
 
 	if (memcmp(buffer, "tree ", 5))
 		die("Bad commit '%s'", (char*) buffer);
-	convert_ascii_sha1(buffer+5);
-	buffer += 46;    /* "tree " + "hex sha1" + "\n" */
+	convert_ascii_sha1((char *) buffer + 5);
+	buffer = (char *) buffer + 46;    /* "tree " + "hex sha1" + "\n" */
 	while (!memcmp(buffer, "parent ", 7)) {
-		convert_ascii_sha1(buffer+7);
-		buffer += 48;
+		convert_ascii_sha1((char *) buffer + 7);
+		buffer = (char *) buffer + 48;
 	}
 	convert_date(orig_buffer, orig_size, result_sha1);
 }
diff --git a/csum-file.c b/csum-file.c
index 5f9249a..ebaad03 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -17,7 +17,7 @@
 	for (;;) {
 		int ret = xwrite(f->fd, buf, count);
 		if (ret > 0) {
-			buf += ret;
+			buf = (char *) buf + ret;
 			count -= ret;
 			if (count)
 				continue;
@@ -57,7 +57,7 @@
 		memcpy(f->buffer + offset, buf, nr);
 		count -= nr;
 		offset += nr;
-		buf += nr;
+		buf = (char *) buf + nr;
 		left -= nr;
 		if (!left) {
 			SHA1_Update(&f->ctx, f->buffer, offset);
diff --git a/daemon.c b/daemon.c
index bdfe80d..1ba4d66 100644
--- a/daemon.c
+++ b/daemon.c
@@ -673,6 +673,11 @@
 	int inetd_mode = 0;
 	int i;
 
+	/* Without this we cannot rely on waitpid() to tell
+	 * what happened to our children.
+	 */
+	signal(SIGCHLD, SIG_DFL);
+
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
@@ -757,7 +762,7 @@
 		struct sockaddr *peer = (struct sockaddr *)&ss;
 		socklen_t slen = sizeof(ss);
 
-		fclose(stderr); //FIXME: workaround
+		freopen("/dev/null", "w", stderr);
 
 		if (getpeername(0, peer, &slen))
 			peer = NULL;
diff --git a/diff-delta.c b/diff-delta.c
index 25a798d..8b9172a 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -22,6 +22,7 @@
 #include <string.h>
 #include "delta.h"
 
+#include "git-compat-util.h"
 
 /* maximum hash entry list for the same hash bucket */
 #define HASH_LIMIT 64
@@ -131,7 +132,7 @@
 	const void *src_buf;
 	unsigned long src_size;
 	unsigned int hash_mask;
-	struct index_entry *hash[0];
+	struct index_entry *hash[FLEX_ARRAY];
 };
 
 struct delta_index * create_delta_index(const void *buf, unsigned long bufsize)
@@ -283,7 +284,7 @@
 	ref_data = index->src_buf;
 	ref_top = ref_data + index->src_size;
 	data = trg_buf;
-	top = trg_buf + trg_size;
+	top = (const unsigned char *) trg_buf + trg_size;
 
 	outpos++;
 	val = 0;
diff --git a/diff-lib.c b/diff-lib.c
index 2183b41..116b5a9 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -34,21 +34,23 @@
 			continue;
 
 		if (ce_stage(ce)) {
-			struct {
-				struct combine_diff_path p;
-				struct combine_diff_parent filler[5];
-			} combine;
+			struct combine_diff_path *dpath;
 			int num_compare_stages = 0;
+			size_t path_len;
 
-			combine.p.next = NULL;
-			combine.p.len = ce_namelen(ce);
-			combine.p.path = xmalloc(combine.p.len + 1);
-			memcpy(combine.p.path, ce->name, combine.p.len);
-			combine.p.path[combine.p.len] = 0;
-			combine.p.mode = 0;
-			memset(combine.p.sha1, 0, 20);
-			memset(&combine.p.parent[0], 0,
-			       sizeof(combine.filler));
+			path_len = ce_namelen(ce);
+
+			dpath = xmalloc (combine_diff_path_size (5, path_len));
+			dpath->path = (char *) &(dpath->parent[5]);
+
+			dpath->next = NULL;
+			dpath->len = path_len;
+			memcpy(dpath->path, ce->name, path_len);
+			dpath->path[path_len] = '\0';
+			dpath->mode = 0;
+			memset(dpath->sha1, 0, 20);
+			memset(&(dpath->parent[0]), 0,
+					sizeof(struct combine_diff_parent)*5);
 
 			while (i < entries) {
 				struct cache_entry *nce = active_cache[i];
@@ -64,11 +66,11 @@
 				if (2 <= stage) {
 					int mode = ntohl(nce->ce_mode);
 					num_compare_stages++;
-					memcpy(combine.p.parent[stage-2].sha1,
+					memcpy(dpath->parent[stage-2].sha1,
 					       nce->sha1, 20);
-					combine.p.parent[stage-2].mode =
+					dpath->parent[stage-2].mode =
 						canon_mode(mode);
-					combine.p.parent[stage-2].status =
+					dpath->parent[stage-2].status =
 						DIFF_STATUS_MODIFIED;
 				}
 
@@ -83,13 +85,14 @@
 			i--;
 
 			if (revs->combine_merges && num_compare_stages == 2) {
-				show_combined_diff(&combine.p, 2,
+				show_combined_diff(dpath, 2,
 						   revs->dense_combined_merges,
 						   revs);
-				free(combine.p.path);
+				free(dpath);
 				continue;
 			}
-			free(combine.p.path);
+			free(dpath);
+			dpath = NULL;
 
 			/*
 			 * Show the diff for the 'ce' if we found the one
@@ -329,8 +332,8 @@
 	}
 	mark_merge_entries();
 
-	ent = revs->pending_objects->item;
-	tree_name = revs->pending_objects->name;
+	ent = revs->pending.objects[0].item;
+	tree_name = revs->pending.objects[0].name;
 	tree = parse_tree_indirect(ent->sha1);
 	if (!tree)
 		return error("bad tree object %s", tree_name);
diff --git a/diff.c b/diff.c
index 9e9cfc8..22b643c 100644
--- a/diff.c
+++ b/diff.c
@@ -25,6 +25,20 @@
 	return git_default_config(var, value);
 }
 
+enum color_diff {
+	DIFF_PLAIN = 0,
+	DIFF_METAINFO = 1,
+	DIFF_FILE_OLD = 2,
+	DIFF_FILE_NEW = 3,
+};
+
+static const char *diff_colors[] = {
+	"\033[0;0m",
+	"\033[1;35m",
+	"\033[1;31m",
+	"\033[1;34m",
+};
+
 static char *quote_one(const char *str)
 {
 	int needlen;
@@ -177,23 +191,54 @@
 }
 
 struct emit_callback {
+	struct xdiff_emit_state xm;
+	int nparents, color_diff;
 	const char **label_path;
 };
 
-static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+static inline void color_diff(int diff_use_color, enum color_diff ix)
+{
+	if (diff_use_color)
+		fputs(diff_colors[ix], stdout);
+}
+
+static void fn_out_consume(void *priv, char *line, unsigned long len)
 {
 	int i;
 	struct emit_callback *ecbdata = priv;
 
 	if (ecbdata->label_path[0]) {
+		color_diff(ecbdata->color_diff, DIFF_METAINFO);
 		printf("--- %s\n", ecbdata->label_path[0]);
+		color_diff(ecbdata->color_diff, DIFF_METAINFO);
 		printf("+++ %s\n", ecbdata->label_path[1]);
 		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
 	}
-	for (i = 0; i < nbuf; i++)
-		if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
-			return -1;
-	return 0;
+
+	/* This is not really necessary for now because
+	 * this codepath only deals with two-way diffs.
+	 */
+	for (i = 0; i < len && line[i] == '@'; i++)
+		;
+	if (2 <= i && i < len && line[i] == ' ') {
+		ecbdata->nparents = i - 1;
+		color_diff(ecbdata->color_diff, DIFF_METAINFO);
+	}
+	else if (len < ecbdata->nparents)
+		color_diff(ecbdata->color_diff, DIFF_PLAIN);
+	else {
+		int nparents = ecbdata->nparents;
+		int color = DIFF_PLAIN;
+		for (i = 0; i < nparents && len; i++) {
+			if (line[i] == '-')
+				color = DIFF_FILE_OLD;
+			else if (line[i] == '+')
+				color = DIFF_FILE_NEW;
+		}
+		color_diff(ecbdata->color_diff, color);
+	}
+	fwrite(line, len, 1, stdout);
+	color_diff(ecbdata->color_diff, DIFF_PLAIN);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -515,7 +560,7 @@
 		else
 			line[0] = bytes - 26 + 'a' - 1;
 		encode_85(line + 1, cp, bytes);
-		cp += bytes;
+		cp = (char *) cp + bytes;
 		puts(line);
 	}
 	printf("\n");
@@ -549,25 +594,35 @@
 	b_two = quote_two("b/", name_b);
 	lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
 	lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
+	color_diff(o->color_diff, DIFF_METAINFO);
 	printf("diff --git %s %s\n", a_one, b_two);
 	if (lbl[0][0] == '/') {
 		/* /dev/null */
+		color_diff(o->color_diff, DIFF_METAINFO);
 		printf("new file mode %06o\n", two->mode);
-		if (xfrm_msg && xfrm_msg[0])
+		if (xfrm_msg && xfrm_msg[0]) {
+			color_diff(o->color_diff, DIFF_METAINFO);
 			puts(xfrm_msg);
+		}
 	}
 	else if (lbl[1][0] == '/') {
 		printf("deleted file mode %06o\n", one->mode);
-		if (xfrm_msg && xfrm_msg[0])
+		if (xfrm_msg && xfrm_msg[0]) {
+			color_diff(o->color_diff, DIFF_METAINFO);
 			puts(xfrm_msg);
+		}
 	}
 	else {
 		if (one->mode != two->mode) {
+			color_diff(o->color_diff, DIFF_METAINFO);
 			printf("old mode %06o\n", one->mode);
+			color_diff(o->color_diff, DIFF_METAINFO);
 			printf("new mode %06o\n", two->mode);
 		}
-		if (xfrm_msg && xfrm_msg[0])
+		if (xfrm_msg && xfrm_msg[0]) {
+			color_diff(o->color_diff, DIFF_METAINFO);
 			puts(xfrm_msg);
+		}
 		/*
 		 * we do not run diff between different kind
 		 * of objects.
@@ -575,6 +630,7 @@
 		if ((one->mode ^ two->mode) & S_IFMT)
 			goto free_ab_and_return;
 		if (complete_rewrite) {
+			color_diff(o->color_diff, DIFF_PLAIN);
 			emit_rewrite_diff(name_a, name_b, one, two);
 			goto free_ab_and_return;
 		}
@@ -602,7 +658,9 @@
 		xdemitcb_t ecb;
 		struct emit_callback ecbdata;
 
+		memset(&ecbdata, 0, sizeof(ecbdata));
 		ecbdata.label_path = lbl;
+		ecbdata.color_diff = o->color_diff;
 		xpp.flags = XDF_NEED_MINIMAL;
 		xecfg.ctxlen = o->context;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
@@ -612,8 +670,9 @@
 			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 		else if (!strncmp(diffopts, "-u", 2))
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-		ecb.outf = fn_out;
+		ecb.outf = xdiff_outf;
 		ecb.priv = &ecbdata;
+		ecbdata.xm.consume = fn_out_consume;
 		xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
 	}
 
@@ -1456,6 +1515,8 @@
 		else if (40 < options->abbrev)
 			options->abbrev = 40;
 	}
+	else if (!strcmp(arg, "--color"))
+		options->color_diff = 1;
 	else
 		return 0;
 	return 1;
diff --git a/diff.h b/diff.h
index 4fc597c..de9de57 100644
--- a/diff.h
+++ b/diff.h
@@ -32,7 +32,8 @@
 		 full_index:1,
 		 silent_on_remove:1,
 		 find_copies_harder:1,
-		 summary:1;
+		 summary:1,
+		 color_diff:1;
 	int context;
 	int break_opt;
 	int detect_rename;
diff --git a/diffcore-order.c b/diffcore-order.c
index 0bc2b22..aef6da6 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -30,7 +30,7 @@
 	close(fd);
 	if (map == MAP_FAILED)
 		return;
-	endp = map + st.st_size;
+	endp = (char *) map + st.st_size;
 	for (pass = 0; pass < 2; pass++) {
 		cnt = 0;
 		cp = map;
diff --git a/fetch-clone.c b/fetch-clone.c
index da1b3ff..c16b0c4 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 @@
 
 	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 @@
 	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 @@
  */
 #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 @@
 	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 7d23a80..f2c51eb 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -25,7 +25,7 @@
 #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 @@
 			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 @@
 			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 @@
 			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/git.c b/git.c
index 329ebec..94e9a4a 100644
--- a/git.c
+++ b/git.c
@@ -178,7 +178,13 @@
 		{ "diff-stages", cmd_diff_stages },
 		{ "diff-tree", cmd_diff_tree },
 		{ "cat-file", cmd_cat_file },
-		{ "rev-parse", cmd_rev_parse }
+		{ "rev-parse", cmd_rev_parse },
+		{ "write-tree", cmd_write_tree },
+		{ "mailsplit", cmd_mailsplit },
+		{ "mailinfo", cmd_mailinfo },
+		{ "stripspace", cmd_stripspace },
+		{ "update-index", cmd_update_index },
+		{ "update-ref", cmd_update_ref }
 	};
 	int i;
 
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 5eabe06..035e76d 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -17,30 +17,50 @@
 binmode STDOUT, ':utf8';
 
 my $cgi = new CGI;
-my $version =		"267";
-my $my_url =		$cgi->url();
-my $my_uri =		$cgi->url(-absolute => 1);
-my $rss_link =		"";
-
-# absolute fs-path which will be prepended to the project path
-#my $projectroot =	"/pub/scm";
-my $projectroot =	"/home/kay/public_html/pub/scm";
+my $version = "267";
+my $my_url = $cgi->url();
+my $my_uri = $cgi->url(-absolute => 1);
+my $rss_link = "";
 
 # location of the git-core binaries
-my $gitbin =		"/usr/bin";
+my $gitbin = "/usr/bin";
+
+# absolute fs-path which will be prepended to the project path
+#my $projectroot = "/pub/scm";
+my $projectroot = "/home/kay/public_html/pub/scm";
+
+# version of the git-core binaries
+my $git_version = qx($gitbin/git --version);
+if ($git_version =~ m/git version (.*)$/) {
+	$git_version = $1;
+} else {
+	$git_version = "unknown";
+}
 
 # location for temporary files needed for diffs
-my $git_temp =		"/tmp/gitweb";
+my $git_temp = "/tmp/gitweb";
 
 # target of the home link on top of all pages
-my $home_link =		$my_uri;
+my $home_link = $my_uri;
 
 # html text to include at home page
-my $home_text =		"indextext.html";
+my $home_text = "indextext.html";
+
+# URI of default stylesheet
+my $stylesheet = "gitweb.css";
 
 # source of projects list
-#my $projects_list =	$projectroot;
-my $projects_list =	"index/index.aux";
+#my $projects_list = $projectroot;
+my $projects_list = "index/index.aux";
+
+# default blob_plain mimetype and default charset for text/plain blob
+my $default_blob_plain_mimetype = 'text/plain';
+my $default_text_plain_charset  = undef;
+
+# file to use for guessing MIME types before trying /etc/mime.types
+# (relative to the current git repository)
+my $mimetypes_file = undef;
+
 
 # input validation and dispatch
 my $action = $cgi->param('a');
@@ -66,8 +86,9 @@
 	}
 }
 
-my $project = $cgi->param('p');
+my $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
 if (defined $project) {
+	$project =~ s|^/||; $project =~ s|/$||;
 	$project = validate_input($project);
 	if (!defined($project)) {
 		die_error(undef, "Invalid project parameter.");
@@ -240,6 +261,19 @@
 	return $str;
 }
 
+# CSS class for given age value (in seconds)
+sub age_class {
+	my $age = shift;
+
+	if ($age < 60*60*2) {
+		return "age0";
+	} elsif ($age < 60*60*24*2) {
+		return "age1";
+	} else {
+		return "age2";
+	}
+}
+
 sub git_header_html {
 	my $status = shift || "200 OK";
 	my $expires = shift;
@@ -249,6 +283,12 @@
 		$title .= " - $project";
 		if (defined $action) {
 			$title .= "/$action";
+			if (defined $file_name) {
+				$title .= " - $file_name";
+				if ($action eq "tree" && $file_name !~ m|/$|) {
+					$title .= "/";
+				}
+			}
 		}
 	}
 	print $cgi->header(-type=>'text/html',  -charset => 'utf-8', -status=> $status, -expires => $expires);
@@ -257,71 +297,13 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
 <!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
+<!-- git core binaries version $git_version -->
 <head>
 <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
+<link rel="stylesheet" type="text/css" href="$stylesheet"/>
 $rss_link
-<style type="text/css">
-body {
-	font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px;
-	margin:10px; background-color:#ffffff; color:#000000;
-}
-a { color:#0000cc; }
-a:hover, a:visited, a:active { color:#880000; }
-div.page_header { height:25px; padding:8px; font-size:18px; font-weight:bold; background-color:#d9d8d1; }
-div.page_header a:visited, a.header { color:#0000cc; }
-div.page_header a:hover { color:#880000; }
-div.page_nav { padding:8px; }
-div.page_nav a:visited { color:#0000cc; }
-div.page_path { padding:8px; border:solid #d9d8d1; border-width:0px 0px 1px}
-div.page_footer { height:17px; padding:4px 8px; background-color: #d9d8d1; }
-div.page_footer_text { float:left; color:#555555; font-style:italic; }
-div.page_body { padding:8px; }
-div.title, a.title {
-	display:block; padding:6px 8px;
-	font-weight:bold; background-color:#edece6; text-decoration:none; color:#000000;
-}
-a.title:hover { background-color: #d9d8d1; }
-div.title_text { padding:6px 0px; border: solid #d9d8d1; border-width:0px 0px 1px; }
-div.log_body { padding:8px 8px 8px 150px; }
-span.age { position:relative; float:left; width:142px; font-style:italic; }
-div.log_link {
-	padding:0px 8px;
-	font-size:10px; font-family:sans-serif; font-style:normal;
-	position:relative; float:left; width:136px;
-}
-div.list_head { padding:6px 8px 4px; border:solid #d9d8d1; border-width:1px 0px 0px; font-style:italic; }
-a.list { text-decoration:none; color:#000000; }
-a.list:hover { text-decoration:underline; color:#880000; }
-a.text { text-decoration:none; color:#0000cc; }
-a.text:visited { text-decoration:none; color:#880000; }
-a.text:hover { text-decoration:underline; color:#880000; }
-table { padding:8px 4px; }
-th { padding:2px 5px; font-size:12px; text-align:left; }
-tr.light:hover { background-color:#edece6; }
-tr.dark { background-color:#f6f6f0; }
-tr.dark:hover { background-color:#edece6; }
-td { padding:2px 5px; font-size:12px; vertical-align:top; }
-td.link { padding:2px 5px; font-family:sans-serif; font-size:10px; }
-div.pre { font-family:monospace; font-size:12px; white-space:pre; }
-div.diff_info { font-family:monospace; color:#000099; background-color:#edece6; font-style:italic; }
-div.index_include { border:solid #d9d8d1; border-width:0px 0px 1px; padding:12px 8px; }
-div.search { margin:4px 8px; position:absolute; top:56px; right:12px }
-a.linenr { color:#999999; text-decoration:none }
-a.rss_logo {
-	float:right; padding:3px 0px; width:35px; line-height:10px;
-	border:1px solid; border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
-	color:#ffffff; background-color:#ff6600;
-	font-weight:bold; font-family:sans-serif; font-size:10px;
-	text-align:center; text-decoration:none;
-}
-a.rss_logo:hover { background-color:#ee5500; }
-span.tag {
-	padding:0px 4px; font-size:10px; font-weight:normal;
-	background-color:#ffffaa; border:1px solid; border-color:#ffffcc #ffee00 #ffee00 #ffffcc;
-}
-</style>
 </head>
 <body>
 EOF
@@ -340,10 +322,12 @@
 			$searchtext = "";
 		}
 		my $search_hash;
-		if (defined $hash) {
+		if (defined $hash_base) {
+			$search_hash = $hash_base;
+		} elsif (defined $hash) {
 			$search_hash = $hash;
 		} else {
-			$search_hash  = "HEAD";
+			$search_hash = "HEAD";
 		}
 		$cgi->param("a", "search");
 		$cgi->param("h", $search_hash);
@@ -377,7 +361,7 @@
 
 sub die_error {
 	my $status = shift || "403 Forbidden";
-	my $error = shift || "Malformed query, file missing or permission denied"; 
+	my $error = shift || "Malformed query, file missing or permission denied";
 
 	git_header_html($status);
 	print "<div class=\"page_body\">\n" .
@@ -641,13 +625,13 @@
 		while (my $line = <$fd>) {
 			chomp($line);
 			my $char = substr($line, 0, 1);
-			my $color = "";
+			my $diff_class = "";
 			if ($char eq '+') {
-				$color = " style=\"color:#008800;\"";
+				$diff_class = " add";
 			} elsif ($char eq "-") {
-				$color = " style=\"color:#cc0000;\"";
+				$diff_class = " rem";
 			} elsif ($char eq "@") {
-				$color = " style=\"color:#990099;\"";
+				$diff_class = " chunk_header";
 			} elsif ($char eq "\\") {
 				# skip errors
 				next;
@@ -658,7 +642,7 @@
 					$line =~ s/\t/$spaces/;
 				}
 			}
-			print "<div class=\"pre\"$color>" . esc_html($line) . "</div>\n";
+			print "<div class=\"diff$diff_class\">" . esc_html($line) . "</div>\n";
 		}
 	}
 	close $fd;
@@ -844,7 +828,7 @@
 	$key =~ s/^gitweb\.//;
 	return if ($key =~ m/\W/);
 
-	my $val = qx(git-repo-config --get gitweb.$key);
+	my $val = qx($gitbin/git-repo-config --get gitweb.$key);
 	return ($val);
 }
 
@@ -890,7 +874,7 @@
 		close $fd;
 		print "</div>\n";
 	}
-	print "<table cellspacing=\"0\">\n" .
+	print "<table class=\"project_list\">\n" .
 	      "<tr>\n";
 	if (!defined($order) || (defined($order) && ($order eq "project"))) {
 		@projects = sort {$a->{'path'} cmp $b->{'path'}} @projects;
@@ -929,15 +913,7 @@
 		print "<td>" . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary"), -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
 		      "<td>$pr->{'descr'}</td>\n" .
 		      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
-		my $colored_age;
-		if ($pr->{'commit'}{'age'} < 60*60*2) {
-			$colored_age = "<span style =\"color: #009900;\"><b><i>$pr->{'commit'}{'age_string'}</i></b></span>";
-		} elsif ($pr->{'commit'}{'age'} < 60*60*24*2) {
-			$colored_age = "<span style =\"color: #009900;\"><i>$pr->{'commit'}{'age_string'}</i></span>";
-		} else {
-			$colored_age = "<i>$pr->{'commit'}{'age_string'}</i>";
-		}
-		print "<td>$colored_age</td>\n" .
+		print "<td class=\"". age_class($pr->{'commit'}{'age'}) . "\">" . $pr->{'commit'}{'age_string'} . "</td>\n" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=summary")}, "summary") .
 		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$pr->{'path'};a=shortlog")}, "shortlog") .
@@ -1102,7 +1078,7 @@
 			      "<td>";
 			if (length($co{'title_short'}) < length($co{'title'})) {
 				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list", -title => "$co{'title'}"},
-			              "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
+				      "<b>" . esc_html($co{'title_short'}) . "$ref</b>");
 			} else {
 				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit"), -class => "list"},
 				      "<b>" . esc_html($co{'title'}) . "$ref</b>");
@@ -1160,7 +1136,7 @@
 				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
 				if ($tag{'reftype'} eq "commit") {
 				      print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-				            " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+					    " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
 				}
 				print "</td>\n" .
 				      "</tr>";
@@ -1281,7 +1257,7 @@
 	print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
 	print "<div class=\"page_body\">\n";
 	print <<HTML;
-<table style="border-collapse: collapse;">
+<table class="blame">
   <tr>
     <th>Commit</th>
     <th>Age</th>
@@ -1302,7 +1278,7 @@
 		my $data;
 		my $age;
 		my $age_str;
-		my $age_style;
+		my $age_class;
 
 		chomp $line;
 		$line_class_num = ($line_class_num + 1) % $line_class_len;
@@ -1314,16 +1290,14 @@
 			$lineno   = $4;
 			$data     = $5;
 		} else {
-			print qq(  <tr><td colspan="5" style="color: red; background-color: yellow;">Unable to parse: $line</td></tr>\n);
+			print qq(  <tr><td colspan="5" class="error">Unable to parse: $line</td></tr>\n);
 			next;
 		}
 		$short_rev  = substr ($long_rev, 0, 8);
 		$age        = time () - $time;
 		$age_str    = age_string ($age);
 		$age_str    =~ s/ /&nbsp;/g;
-		$age_style  = 'font-style: italic;';
-		$age_style .= ' color: #009900; background: transparent;' if ($age < 60*60*24*2);
-		$age_style .= ' font-weight: bold;' if ($age < 60*60*2);
+		$age_class  = age_class($age);
 		$author     = esc_html ($author);
 		$author     =~ s/ /&nbsp;/g;
 		# escape tabs
@@ -1334,15 +1308,14 @@
 			}
 		}
 		$data = esc_html ($data);
-		$data =~ s/ /&nbsp;/g;
 
 		print <<HTML;
   <tr class="$line_class[$line_class_num]">
-    <td style="font-family: monospace;"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
-    <td style="$age_style">$age_str</td>
+    <td class="sha1"><a href="$my_uri?${\esc_param ("p=$project;a=commit;h=$long_rev")}" class="text">$short_rev..</a></td>
+    <td class="$age_class">$age_str</td>
     <td>$author</td>
-    <td style="text-align: right;"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
-    <td style="font-family: monospace;">$data</td>
+    <td class="linenr"><a id="$lineno" href="#$lineno" class="linenr">$lineno</a></td>
+    <td class="pre">$data</td>
   </tr>
 HTML
 	} # while (my $line = <$fd>)
@@ -1401,7 +1374,7 @@
 			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=$tag{'reftype'};h=$tag{'refid'}")}, $tag{'reftype'});
 			if ($tag{'reftype'} eq "commit") {
 			      print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog;h=$tag{'name'}")}, "shortlog") .
-			            " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
+				    " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log;h=$tag{'refid'}")}, "log");
 			}
 			print "</td>\n" .
 			      "</tr>";
@@ -1538,15 +1511,85 @@
 	git_footer_html();
 }
 
+sub mimetype_guess_file {
+	my $filename = shift;
+	my $mimemap = shift;
+	-r $mimemap or return undef;
+
+	my %mimemap;
+	open(MIME, $mimemap) or return undef;
+	while (<MIME>) {
+		my ($mime, $exts) = split(/\t+/);
+		my @exts = split(/\s+/, $exts);
+		foreach my $ext (@exts) {
+			$mimemap{$ext} = $mime;
+		}
+	}
+	close(MIME);
+
+	$filename =~ /\.(.*?)$/;
+	return $mimemap{$1};
+}
+
+sub mimetype_guess {
+	my $filename = shift;
+	my $mime;
+	$filename =~ /\./ or return undef;
+
+	if ($mimetypes_file) {
+		my $file = $mimetypes_file;
+		#$file =~ m#^/# or $file = "$projectroot/$path/$file";
+		$mime = mimetype_guess_file($filename, $file);
+	}
+	$mime ||= mimetype_guess_file($filename, '/etc/mime.types');
+	return $mime;
+}
+
+sub git_blob_plain_mimetype {
+	my $fd = shift;
+	my $filename = shift;
+
+	# just in case
+	return $default_blob_plain_mimetype unless $fd;
+
+	if ($filename) {
+		my $mime = mimetype_guess($filename);
+		$mime and return $mime;
+	}
+
+	if (-T $fd) {
+		return 'text/plain' .
+		       ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
+	} elsif (! $filename) {
+		return 'application/octet-stream';
+	} elsif ($filename =~ m/\.png$/i) {
+		return 'image/png';
+	} elsif ($filename =~ m/\.gif$/i) {
+		return 'image/gif';
+	} elsif ($filename =~ m/\.jpe?g$/i) {
+		return 'image/jpeg';
+	} else {
+		return 'application/octet-stream';
+	}
+}
+
 sub git_blob_plain {
-	my $save_as = "$hash.txt";
+	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+	my $type = git_blob_plain_mimetype($fd, $file_name);
+
+	# save as filename, even when no $file_name is given
+	my $save_as = "$hash";
 	if (defined $file_name) {
 		$save_as = $file_name;
+	} elsif ($type =~ m/^text\//) {
+		$save_as .= '.txt';
 	}
-	print $cgi->header(-type => "text/plain", -charset => 'utf-8', '-content-disposition' => "inline; filename=\"$save_as\"");
-	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
+
+	print $cgi->header(-type => "$type", '-content-disposition' => "inline; filename=\"$save_as\"");
 	undef $/;
+	binmode STDOUT, ':raw';
 	print <$fd>;
+	binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
 	$/ = "\n";
 	close $fd;
 }
@@ -1617,7 +1660,7 @@
 			print "<tr class=\"light\">\n";
 		}
 		$alternate ^= 1;
-		print "<td style=\"font-family:monospace\">" . mode_str($t_mode) . "</td>\n";
+		print "<td class=\"mode\">" . mode_str($t_mode) . "</td>\n";
 		if ($t_type eq "blob") {
 			print "<td class=\"list\">" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name"), -class => "list"}, esc_html($t_name)) .
@@ -1724,8 +1767,8 @@
 		}
 
 		my $path = esc_html(chop_str($proj{'path'}, 25, 5));
-		my $rss =  "$my_url?p=$proj{'path'};a=rss";
-		my $html =  "$my_url?p=$proj{'path'};a=summary";
+		my $rss  = "$my_url?p=$proj{'path'};a=rss";
+		my $html = "$my_url?p=$proj{'path'};a=summary";
 		print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
 	}
 	print "</outline>\n".
@@ -1883,7 +1926,7 @@
 	      "<tr>" .
 	      "<td></td><td> $ad{'rfc2822'}";
 	if ($ad{'hour_local'} < 6) {
-		printf(" (<span style=\"color: #cc0000;\">%02d:%02d</span> %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
+		printf(" (<span class=\"atnight\">%02d:%02d</span> %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
 	} else {
 		printf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
 	}
@@ -1891,27 +1934,27 @@
 	      "</tr>\n";
 	print "<tr><td>committer</td><td>" . esc_html($co{'committer'}) . "</td></tr>\n";
 	print "<tr><td></td><td> $cd{'rfc2822'}" . sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) . "</td></tr>\n";
-	print "<tr><td>commit</td><td style=\"font-family:monospace\">$co{'id'}</td></tr>\n";
+	print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
 	print "<tr>" .
 	      "<td>tree</td>" .
-	      "<td style=\"font-family:monospace\">" .
+	      "<td class=\"sha1\">" .
 	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash"), class => "list"}, $co{'tree'}) .
 	      "</td>" .
 	      "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
 	      "</td>" .
 	      "</tr>\n";
-	my $parents  = $co{'parents'};
+	my $parents = $co{'parents'};
 	foreach my $par (@$parents) {
 		print "<tr>" .
 		      "<td>parent</td>" .
-		      "<td style=\"font-family:monospace\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
+		      "<td class=\"sha1\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par"), class => "list"}, $par) . "</td>" .
 		      "<td class=\"link\">" .
 		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$par")}, "commit") .
 		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash;hp=$par")}, "commitdiff") .
 		      "</td>" .
 		      "</tr>\n";
 	}
-	print "</table>". 
+	print "</table>".
 	      "</div>\n";
 	print "<div class=\"page_body\">\n";
 	my $comment = $co{'comment'};
@@ -1929,7 +1972,7 @@
 		}
 		if ($line =~ m/^ *(signed[ \-]off[ \-]by[ :]|acked[ \-]by[ :]|cc[ :])/i) {
 			$signed = 1;
-			print "<span style=\"color: #888888\">" . esc_html($line) . "</span><br/>\n";
+			print "<span class=\"signoff\">" . esc_html($line) . "</span><br/>\n";
 		} else {
 			$signed = 0;
 			print format_log_line_html($line) . "<br/>\n";
@@ -1941,7 +1984,7 @@
 		print(($#difftree + 1) . " files changed:\n");
 	}
 	print "</div>\n";
-	print "<table cellspacing=\"0\">\n";
+	print "<table class=\"diff_tree\">\n";
 	my $alternate = 0;
 	foreach my $line (@difftree) {
 		# ':100644 100644 03b218260e99b78c6df0ed378e59ed9205ccc96d 3b93d5e7cc7f7dd4ebed13a5cc1a4ad976fc94d8 M      ls-files.c'
@@ -1969,12 +2012,12 @@
 			}
 			print "<td>" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
-			      "<td><span style=\"color: #008000;\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
+			      "<td><span class=\"file_status new\">[new " . file_type($to_mode) . "$mode_chng]</span></td>\n" .
 			      "<td class=\"link\">" . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$file")}, "blob") . "</td>\n";
 		} elsif ($status eq "D") {
 			print "<td>" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file"), -class => "list"}, esc_html($file)) . "</td>\n" .
-			      "<td><span style=\"color: #c00000;\">[deleted " . file_type($from_mode). "]</span></td>\n" .
+			      "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
 			      "<td class=\"link\">" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
 			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
@@ -1982,7 +2025,7 @@
 		} elsif ($status eq "M" || $status eq "T") {
 			my $mode_chnge = "";
 			if ($from_mode != $to_mode) {
-				$mode_chnge = " <span style=\"color: #777777;\">[changed";
+				$mode_chnge = " <span class=\"file_status mode_chnge\">[changed";
 				if (((oct $from_mode) & S_IFMT) != ((oct $to_mode) & S_IFMT)) {
 					$mode_chnge .= " from " . file_type($from_mode) . " to " . file_type($to_mode);
 				}
@@ -2018,7 +2061,7 @@
 			}
 			print "<td>" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$to_id;hb=$hash;f=$to_file"), -class => "list"}, esc_html($to_file)) . "</td>\n" .
-			      "<td><span style=\"color: #777777;\">[moved from " .
+			      "<td><span class=\"file_status moved\">[moved from " .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$from_file"), -class => "list"}, esc_html($from_file)) .
 			      " with " . (int $similarity) . "% similarity$mode_chng]</span></td>\n" .
 			      "<td class=\"link\">" .
@@ -2378,7 +2421,7 @@
 					my $match = esc_html($2) || "";
 					my $trail = esc_html($3) || "";
 					$trail = chop_str($trail, 30, 10);
-					my $text = "$lead<span style=\"color:#e00000\">$match</span>$trail";
+					my $text = "$lead<span class=\"match\">$match</span>$trail";
 					print chop_str($text, 80, 5) . "<br/>\n";
 				}
 			}
@@ -2427,7 +2470,7 @@
 					while (my $setref = shift @files) {
 						my %set = %$setref;
 						print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$set{'id'};hb=$co{'id'};f=$set{'file'}"), class => "list"},
-						      "<span style=\"color:#e00000\">" . esc_html($set{'file'}) . "</span>") .
+						      "<span class=\"match\">" . esc_html($set{'file'}) . "</span>") .
 						      "<br/>\n";
 					}
 					print "</td>\n" .
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
new file mode 100644
index 0000000..98410f5
--- /dev/null
+++ b/gitweb/gitweb.css
@@ -0,0 +1,320 @@
+body {
+	font-family: sans-serif;
+	font-size: 12px;
+	border: solid #d9d8d1;
+	border-width: 1px;
+	margin: 10px;
+	background-color: #ffffff;
+	color: #000000;
+}
+
+a {
+	color: #0000cc;
+}
+
+a:hover, a:visited, a:active {
+	color: #880000;
+}
+
+div.page_header {
+	height: 25px;
+	padding: 8px;
+	font-size: 18px;
+	font-weight: bold;
+	background-color: #d9d8d1;
+}
+
+div.page_header a:visited, a.header {
+	color: #0000cc;
+}
+
+div.page_header a:hover {
+	color: #880000;
+}
+
+div.page_nav {
+	padding: 8px;
+}
+
+div.page_nav a:visited {
+	color: #0000cc;
+}
+
+div.page_path {
+	padding: 8px;
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+}
+
+div.page_footer {
+	height: 17px;
+	padding: 4px 8px;
+	background-color: #d9d8d1;
+}
+
+div.page_footer_text {
+	float: left;
+	color: #555555;
+	font-style: italic;
+}
+
+div.page_body {
+	padding: 8px;
+}
+
+div.title, a.title {
+	display: block;
+	padding: 6px 8px;
+	font-weight: bold;
+	background-color: #edece6;
+	text-decoration: none;
+	color: #000000;
+}
+
+a.title:hover {
+	background-color: #d9d8d1;
+}
+
+div.title_text {
+	padding: 6px 0px;
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+}
+
+div.log_body {
+	padding: 8px 8px 8px 150px;
+}
+
+span.age {
+	position: relative;
+	float: left;
+	width: 142px;
+	font-style: italic;
+}
+
+div.page_body span.signoff {
+	color: #888888;
+}
+
+div.log_link {
+	padding: 0px 8px;
+	font-size: 10px;
+	font-family: sans-serif;
+	font-style: normal;
+	position: relative;
+	float: left;
+	width: 136px;
+}
+
+div.list_head {
+	padding: 6px 8px 4px;
+	border: solid #d9d8d1;
+	border-width: 1px 0px 0px;
+	font-style: italic;
+}
+
+a.list {
+	text-decoration: none;
+	color: #000000;
+}
+
+a.list:hover {
+	text-decoration: underline;
+	color: #880000;
+}
+
+a.text {
+	text-decoration: none;
+	color: #0000cc;
+}
+
+a.text:visited {
+	text-decoration: none;
+	color: #880000;
+}
+
+a.text:hover {
+	text-decoration: underline;
+	color: #880000;
+}
+
+table {
+	padding: 8px 4px;
+}
+
+table.project_list, table.diff_tree {
+	border-spacing: 0;
+}
+
+table.blame {
+	border-collapse: collapse;
+}
+
+th {
+	padding: 2px 5px;
+	font-size: 12px;
+	text-align: left;
+}
+
+tr.light:hover {
+	background-color: #edece6;
+}
+
+tr.dark {
+	background-color: #f6f6f0;
+}
+
+tr.dark:hover {
+	background-color: #edece6;
+}
+
+td {
+	padding: 2px 5px;
+	font-size: 12px;
+	vertical-align: top;
+}
+
+td.link {
+	padding: 2px 5px;
+	font-family: sans-serif;
+	font-size: 10px;
+}
+
+td.sha1 {
+	font-family: monospace;
+}
+
+td.error {
+	color: red;
+	background-color: yellow;
+}
+
+table.diff_tree span.file_status.new {
+	color: #008000;
+}
+
+table.diff_tree span.file_status.deleted {
+	color: #c00000;
+}
+
+table.diff_tree span.file_status.moved,
+table.diff_tree span.file_status.mode_chnge {
+	color: #777777;
+}
+
+/* age2: 60*60*24*2 <= age */
+table.project_list td.age2, table.blame td.age2 {
+	font-style: italic;
+}
+
+/* age1: 60*60*2 <= age < 60*60*24*2 */
+table.project_list td.age1 {
+	color: #009900;
+	font-style: italic;
+}
+
+table.blame td.age1 {
+	color: #009900;
+	background: transparent;
+}
+
+/* age0: age < 60*60*2 */
+table.project_list td.age0 {
+	color: #009900;
+	font-style: italic;
+	font-weight: bold;
+}
+
+table.blame td.age0 {
+	color: #009900;
+	background: transparent;
+	font-weight: bold;
+}
+
+td.pre, div.pre, div.diff {
+	font-family: monospace;
+	font-size: 12px;
+	white-space: pre;
+}
+
+td.mode {
+	font-family: monospace;
+}
+
+div.diff.add {
+	color: #008800;
+}
+
+div.diff.rem {
+	color: #cc0000;
+}
+
+div.diff.chunk_header {
+	color: #990099;
+}
+
+div.diff_info {
+	font-family: monospace;
+	color: #000099;
+	background-color: #edece6;
+	font-style: italic;
+}
+
+div.index_include {
+	border: solid #d9d8d1;
+	border-width: 0px 0px 1px;
+	padding: 12px 8px;
+}
+
+div.search {
+	margin: 4px 8px;
+	position: absolute;
+	top: 56px;
+	right: 12px
+}
+
+td.linenr {
+	text-align: right;
+}
+
+a.linenr {
+	color: #999999;
+	text-decoration: none
+}
+
+a.rss_logo {
+	float: right;
+	padding: 3px 0px;
+	width: 35px;
+	line-height: 10px;
+	border: 1px solid;
+	border-color: #fcc7a5 #7d3302 #3e1a01 #ff954e;
+	color: #ffffff;
+	background-color: #ff6600;
+	font-weight: bold;
+	font-family: sans-serif;
+	font-size: 10px;
+	text-align: center;
+	text-decoration: none;
+}
+
+a.rss_logo:hover {
+	background-color: #ee5500;
+}
+
+span.tag {
+	padding: 0px 4px;
+	font-size: 10px;
+	font-weight: normal;
+	background-color: #ffffaa;
+	border: 1px solid;
+	border-color: #ffffcc #ffee00 #ffee00 #ffffcc;
+}
+
+span.atnight {
+	color: #cc0000;
+}
+
+span.match {
+	color: #e00000;
+}
diff --git a/http-fetch.c b/http-fetch.c
index da1a7f5..2b63d89 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -123,7 +123,7 @@
 	struct object_request *obj_req = (struct object_request *)data;
 	do {
 		ssize_t retval = write(obj_req->local,
-				       ptr + posn, size - posn);
+				       (char *) ptr + posn, size - posn);
 		if (retval < 0)
 			return posn;
 		posn += retval;
@@ -1136,13 +1136,14 @@
 
 static inline int needs_quote(int ch)
 {
-	switch (ch) {
-	case '/': case '-': case '.':
-	case 'A'...'Z':	case 'a'...'z':	case '0'...'9':
+	if (((ch >= 'A') && (ch <= 'Z'))
+			|| ((ch >= 'a') && (ch <= 'z'))
+			|| ((ch >= '0') && (ch <= '9'))
+			|| (ch == '/')
+			|| (ch == '-')
+			|| (ch == '.'))
 		return 0;
-	default:
-		return 1;
-	}
+	return 1;
 }
 
 static inline int hex(int v)
diff --git a/http-push.c b/http-push.c
index ba64f8f..8d472f0 100644
--- a/http-push.c
+++ b/http-push.c
@@ -196,7 +196,7 @@
 	struct transfer_request *request = (struct transfer_request *)data;
 	do {
 		ssize_t retval = write(request->local_fileno,
-				       ptr + posn, size - posn);
+				       (char *) ptr + posn, size - posn);
 		if (retval < 0)
 			return posn;
 		posn += retval;
@@ -1077,13 +1077,14 @@
 
 static inline int needs_quote(int ch)
 {
-	switch (ch) {
-	case '/': case '-': case '.':
-	case 'A'...'Z':	case 'a'...'z':	case '0'...'9':
+	if (((ch >= 'A') && (ch <= 'Z'))
+			|| ((ch >= 'a') && (ch <= 'z'))
+			|| ((ch >= '0') && (ch <= '9'))
+			|| (ch == '/')
+			|| (ch == '-')
+			|| (ch == '.'))
 		return 0;
-	default:
-		return 1;
-	}
+	return 1;
 }
 
 static inline int hex(int v)
@@ -1171,7 +1172,7 @@
 
 	obj->flags |= REMOTE;
 	if (!object_list_contains(objects, obj))
-		add_object(obj, &objects, NULL, "");
+		object_list_insert(obj, &objects);
 }
 
 static void handle_lockprop_ctx(struct xml_ctx *ctx, int tag_closed)
@@ -1699,6 +1700,15 @@
 	return lock_flags;
 }
 
+struct object_list **add_one_object(struct object *obj, struct object_list **p)
+{
+	struct object_list *entry = xmalloc(sizeof(struct object_list));
+	entry->item = obj;
+	entry->next = *p;
+	*p = entry;
+	return &entry->next;
+}
+
 static struct object_list **process_blob(struct blob *blob,
 					 struct object_list **p,
 					 struct name_path *path,
@@ -1712,8 +1722,7 @@
 		return p;
 
 	obj->flags |= SEEN;
-	name = strdup(name);
-	return add_object(obj, p, path, name);
+	return add_one_object(obj, p);
 }
 
 static struct object_list **process_tree(struct tree *tree,
@@ -1735,7 +1744,7 @@
 
 	obj->flags |= SEEN;
 	name = strdup(name);
-	p = add_object(obj, p, NULL, name);
+	p = add_one_object(obj, p);
 	me.up = path;
 	me.elem = name;
 	me.elem_len = strlen(name);
@@ -1756,8 +1765,9 @@
 
 static int get_delta(struct rev_info *revs, struct remote_lock *lock)
 {
+	int i;
 	struct commit *commit;
-	struct object_list **p = &objects, *pending;
+	struct object_list **p = &objects;
 	int count = 0;
 
 	while ((commit = get_revision(revs)) != NULL) {
@@ -1767,15 +1777,16 @@
 			count += add_send_request(&commit->object, lock);
 	}
 
-	for (pending = revs->pending_objects; pending; pending = pending->next) {
-		struct object *obj = pending->item;
-		const char *name = pending->name;
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object_array_entry *entry = revs->pending.objects + i;
+		struct object *obj = entry->item;
+		const char *name = entry->name;
 
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
 		if (obj->type == TYPE_TAG) {
 			obj->flags |= SEEN;
-			p = add_object(obj, p, NULL, name);
+			p = add_one_object(obj, p);
 			continue;
 		}
 		if (obj->type == TYPE_TREE) {
diff --git a/http.c b/http.c
index 08769cc..6c1937b 100644
--- a/http.c
+++ b/http.c
@@ -34,7 +34,7 @@
 	size_t size = eltsize * nmemb;
 	if (size > buffer->size - buffer->posn)
 		size = buffer->size - buffer->posn;
-	memcpy(ptr, buffer->buffer + buffer->posn, size);
+	memcpy(ptr, (char *) buffer->buffer + buffer->posn, size);
 	buffer->posn += size;
 	return size;
 }
@@ -49,7 +49,7 @@
 			buffer->size = buffer->posn + size;
 		buffer->buffer = xrealloc(buffer->buffer, buffer->size);
 	}
-	memcpy(buffer->buffer + buffer->posn, ptr, size);
+	memcpy((char *) buffer->buffer + buffer->posn, ptr, size);
 	buffer->posn += size;
 	data_received++;
 	return size;
diff --git a/imap-send.c b/imap-send.c
index 285ad29..94e39cd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -93,7 +93,7 @@
 	char *data;
 	int len;
 	unsigned char flags;
-	unsigned char crlf:1;
+	unsigned int crlf:1;
 } msg_data_t;
 
 #define DRV_OK          0
diff --git a/merge-index.c b/merge-index.c
index 024196e..190e12f 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -99,6 +99,11 @@
 {
 	int i, force_file = 0;
 
+	/* Without this we cannot rely on waitpid() to tell
+	 * what happened to our children.
+	 */
+	signal(SIGCHLD, SIG_DFL);
+
 	if (argc < 3)
 		usage("git-merge-index [-o] [-q] <merge-program> (-a | <filename>*)");
 
diff --git a/name-rev.c b/name-rev.c
index c29b93e..3a5ac35 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -125,11 +125,10 @@
 
 	return buffer;
 }
-	
+
 int main(int argc, char **argv)
 {
-	struct object_list *revs = NULL;
-	struct object_list **walker = &revs;
+	struct object_array revs = { 0, 0, NULL };
 	int as_is = 0, all = 0, transform_stdin = 0;
 
 	setup_git_directory();
@@ -184,9 +183,7 @@
 		if (cutoff > commit->date)
 			cutoff = commit->date;
 
-		object_list_append((struct object *)commit, walker);
-		(*walker)->name = *argv;
-		walker = &((*walker)->next);
+		add_object_array((struct object *)commit, *argv, &revs);
 	}
 
 	for_each_ref(name_ref);
@@ -243,9 +240,13 @@
 			if (objs[i])
 				printf("%s %s\n", sha1_to_hex(objs[i]->sha1),
 						get_rev_name(objs[i]));
-	} else
-		for ( ; revs; revs = revs->next)
-			printf("%s %s\n", revs->name, get_rev_name(revs->item));
+	} else {
+		int i;
+		for (i = 0; i < revs.nr; i++)
+			printf("%s %s\n",
+				revs.objects[i].name,
+				get_rev_name(revs.objects[i].item));
+	}
 
 	return 0;
 }
diff --git a/object-refs.c b/object-refs.c
index a7d49c6..b1b8065 100644
--- a/object-refs.c
+++ b/object-refs.c
@@ -12,6 +12,18 @@
 	return hash % n;
 }
 
+static void insert_ref_hash(struct object_refs *ref, struct object_refs **hash, unsigned int size)
+{
+	int j = hash_obj(ref->base, size);
+
+	while (hash[j]) {
+		j++;
+		if (j >= size)
+			j = 0;
+	}
+	hash[j] = ref;
+}
+
 static void grow_refs_hash(void)
 {
 	int i;
@@ -20,30 +32,16 @@
 
 	new_hash = calloc(new_hash_size, sizeof(struct object_refs *));
 	for (i = 0; i < refs_hash_size; i++) {
-		int j;
 		struct object_refs *ref = refs_hash[i];
 		if (!ref)
 			continue;
-		j = hash_obj(ref->base, new_hash_size);
-		new_hash[j] = ref;
+		insert_ref_hash(ref, new_hash, new_hash_size);
 	}
 	free(refs_hash);
 	refs_hash = new_hash;
 	refs_hash_size = new_hash_size;
 }
 
-static void insert_ref_hash(struct object_refs *ref)
-{
-	int j = hash_obj(ref->base, refs_hash_size);
-
-	while (refs_hash[j]) {
-		j++;
-		if (j >= refs_hash_size)
-			j = 0;
-	}
-	refs_hash[j] = ref;
-}
-
 static void add_object_refs(struct object *obj, struct object_refs *ref)
 {
 	int nr = nr_object_refs + 1;
@@ -51,7 +49,7 @@
 	if (nr > refs_hash_size * 2 / 3)
 		grow_refs_hash();
 	ref->base = obj;
-	insert_ref_hash(ref);
+	insert_ref_hash(ref, refs_hash, refs_hash_size);
 	nr_object_refs = nr;
 }
 
diff --git a/object.c b/object.c
index e26e319..37784ce 100644
--- a/object.c
+++ b/object.c
@@ -200,3 +200,20 @@
 	}
 	return 0;
 }
+
+void add_object_array(struct object *obj, const char *name, struct object_array *array)
+{
+	unsigned nr = array->nr;
+	unsigned alloc = array->alloc;
+	struct object_array_entry *objects = array->objects;
+
+	if (nr >= alloc) {
+		alloc = (alloc + 32) * 2;
+		objects = xrealloc(objects, alloc * sizeof(*objects));
+		array->alloc = alloc;
+		array->objects = objects;
+	}
+	objects[nr].item = obj;
+	objects[nr].name = name;
+	array->nr = ++nr;
+}
diff --git a/object.h b/object.h
index c537b4b..6f23a9a 100644
--- a/object.h
+++ b/object.h
@@ -4,7 +4,6 @@
 struct object_list {
 	struct object *item;
 	struct object_list *next;
-	const char *name;
 };
 
 struct object_refs {
@@ -13,6 +12,15 @@
 	struct object *ref[FLEX_ARRAY]; /* more */
 };
 
+struct object_array {
+	unsigned int nr;
+	unsigned int alloc;
+	struct object_array_entry {
+		struct object *item;
+		const char *name;
+	} *objects;
+};
+
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
@@ -72,4 +80,7 @@
 
 int object_list_contains(struct object_list *list, struct object *obj);
 
+/* Object array handling .. */
+void add_object_array(struct object *obj, const char *name, struct object_array *array);
+
 #endif /* OBJECT_H */
diff --git a/pack-check.c b/pack-check.c
index e575879..3a62e1b 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -29,10 +29,10 @@
 	pack_base = p->pack_base;
 	SHA1_Update(&ctx, pack_base, pack_size - 20);
 	SHA1_Final(sha1, &ctx);
-	if (memcmp(sha1, pack_base + pack_size - 20, 20))
+	if (memcmp(sha1, (char *) pack_base + pack_size - 20, 20))
 		return error("Packfile %s SHA1 mismatch with itself",
 			     p->pack_name);
-	if (memcmp(sha1, index_base + index_size - 40, 20))
+	if (memcmp(sha1, (char *) index_base + index_size - 40, 20))
 		return error("Packfile %s SHA1 mismatch with idx",
 			     p->pack_name);
 
@@ -135,7 +135,7 @@
 	SHA1_Init(&ctx);
 	SHA1_Update(&ctx, index_base, index_size - 20);
 	SHA1_Final(sha1, &ctx);
-	if (memcmp(sha1, index_base + index_size - 20, 20))
+	if (memcmp(sha1, (char *) index_base + index_size - 20, 20))
 		ret = error("Packfile index for %s SHA1 mismatch",
 			    p->pack_name);
 
diff --git a/pack-objects.c b/pack-objects.c
index 179560f..bed2497 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -156,7 +156,7 @@
 
 	rix->revindex = xmalloc(sizeof(unsigned long) * (num_ent + 1));
 	for (i = 0; i < num_ent; i++) {
-		unsigned int hl = *((unsigned int *)(index + 24 * i));
+		unsigned int hl = *((unsigned int *)((char *) index + 24*i));
 		rix->revindex[i] = ntohl(hl);
 	}
 	/* This knows the pack format -- the 20-byte trailer
@@ -300,7 +300,7 @@
 		use_packed_git(p);
 
 		datalen = find_packed_object_size(p, entry->in_pack_offset);
-		buf = p->pack_base + entry->in_pack_offset;
+		buf = (char *) p->pack_base + entry->in_pack_offset;
 		sha1write(f, buf, datalen);
 		unuse_packed_git(p);
 		hdrlen = 0; /* not really */
@@ -1221,6 +1221,10 @@
 				local = 1;
 				continue;
 			}
+			if (!strcmp("--progress", arg)) {
+				progress = 1;
+				continue;
+			}
 			if (!strcmp("--incremental", arg)) {
 				incremental = 1;
 				continue;
diff --git a/pack-redundant.c b/pack-redundant.c
index cd81f5a..41fb960 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -246,12 +246,12 @@
 static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)
 {
 	int p1_off, p2_off;
-	void *p1_base, *p2_base;
+	unsigned char *p1_base, *p2_base;
 	struct llist_item *p1_hint = NULL, *p2_hint = NULL;
-	
+
 	p1_off = p2_off = 256 * 4 + 4;
-	p1_base = (void *)p1->pack->index_base;
-	p2_base = (void *)p2->pack->index_base;
+	p1_base = (unsigned char *) p1->pack->index_base;
+	p2_base = (unsigned char *) p2->pack->index_base;
 
 	while (p1_off <= p1->pack->index_size - 3 * 20 &&
 	       p2_off <= p2->pack->index_size - 3 * 20)
@@ -351,11 +351,11 @@
 {
 	size_t ret = 0;
 	int p1_off, p2_off;
-	void *p1_base, *p2_base;
+	char *p1_base, *p2_base;
 
 	p1_off = p2_off = 256 * 4 + 4;
-	p1_base = (void *)p1->index_base;
-	p2_base = (void *)p2->index_base;
+	p1_base = (char *)p1->index_base;
+	p2_base = (char *)p2->index_base;
 
 	while (p1_off <= p1->index_size - 3 * 20 &&
 	       p2_off <= p2->index_size - 3 * 20)
@@ -534,7 +534,7 @@
 {
 	struct pack_list l;
 	size_t off;
-	void *base;
+	unsigned char *base;
 
 	if (!p->pack_local && !(alt_odb || verbose))
 		return NULL;
@@ -543,7 +543,7 @@
 	llist_init(&l.all_objects);
 
 	off = 256 * 4 + 4;
-	base = (void *)p->index_base;
+	base = (unsigned char *)p->index_base;
 	while (off <= p->index_size - 3 * 20) {
 		llist_insert_back(l.all_objects, base + off);
 		off += 24;
diff --git a/pager.c b/pager.c
index 9a30939..2d186e8 100644
--- a/pager.c
+++ b/pager.c
@@ -46,7 +46,7 @@
 	close(fd[0]);
 	close(fd[1]);
 
-	setenv("LESS", "-S", 0);
+	setenv("LESS", "-RS", 0);
 	run_pager(pager);
 	die("unable to execute pager '%s'", pager);
 	exit(255);
diff --git a/patch-delta.c b/patch-delta.c
index 8f318ed..e3a1d42 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -25,7 +25,7 @@
 		return NULL;
 
 	data = delta_buf;
-	top = delta_buf + delta_size;
+	top = (const unsigned char *) delta_buf + delta_size;
 
 	/* make sure the orig file size matches what we expect */
 	size = get_delta_hdr_size(&data, top);
@@ -56,7 +56,7 @@
 			    cp_off + cp_size > src_size ||
 			    cp_size > size)
 				goto bad;
-			memcpy(out, src_buf + cp_off, cp_size);
+			memcpy(out, (char *) src_buf + cp_off, cp_size);
 			out += cp_size;
 			size -= cp_size;
 		} else if (cmd) {
diff --git a/pkt-line.c b/pkt-line.c
index bb3bab0..c1e81f9 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -16,12 +16,13 @@
  * 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) {
-			buf += ret;
+			buf = (char *) buf + ret;
 			n -= ret;
 			continue;
 		}
@@ -29,6 +30,7 @@
 			die("write error (disk full?)");
 		die("write error (%s)", strerror(errno));
 	}
+	return nn;
 }
 
 /*
@@ -66,7 +68,7 @@
 	int n = 0;
 
 	while (n < size) {
-		int ret = xread(fd, buffer + n, size - n);
+		int ret = xread(fd, (char *) buffer + n, size - n);
 		if (ret < 0)
 			die("read error (%s)", strerror(errno));
 		if (!ret)
diff --git a/pkt-line.h b/pkt-line.h
index 51d0cbe..9abef24 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -8,5 +8,6 @@
 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/quote.c b/quote.c
index 06792d4..dcc2326 100644
--- a/quote.c
+++ b/quote.c
@@ -206,7 +206,14 @@
 				case '\\': case '"':
 					break; /* verbatim */
 
-				case '0'...'7':
+				case '0':
+				case '1':
+				case '2':
+				case '3':
+				case '4':
+				case '5':
+				case '6':
+				case '7':
 					/* octal */
 					ac = ((ch - '0') << 6);
 					if ((ch = *sp++) < '0' || '7' < ch)
diff --git a/read-cache.c b/read-cache.c
index c499c51..3c32aae 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -706,7 +706,7 @@
 	SHA1_Init(&c);
 	SHA1_Update(&c, hdr, size - 20);
 	SHA1_Final(sha1, &c);
-	if (memcmp(sha1, (void *)hdr + size - 20, 20))
+	if (memcmp(sha1, (char *) hdr + size - 20, 20))
 		return error("bad index file sha1 signature");
 	return 0;
 }
@@ -770,7 +770,7 @@
 
 	offset = sizeof(*hdr);
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = map + offset;
+		struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);
 		offset = offset + ce_size(ce);
 		active_cache[i] = ce;
 	}
@@ -783,10 +783,11 @@
 		 * in 4-byte network byte order.
 		 */
 		unsigned long extsize;
-		memcpy(&extsize, map + offset + 4, 4);
+		memcpy(&extsize, (char *) map + offset + 4, 4);
 		extsize = ntohl(extsize);
-		if (read_index_extension(map + offset,
-					 map + offset + 8, extsize) < 0)
+		if (read_index_extension(((const char *) map) + offset,
+					 (char *) map + offset + 8,
+					 extsize) < 0)
 			goto unmap;
 		offset += 8;
 		offset += extsize;
@@ -820,7 +821,7 @@
 		}
 		write_buffer_len = buffered;
 		len -= partial;
-		data += partial;
+		data = (char *) data + partial;
  	}
  	return 0;
 }
diff --git a/repo-config.c b/repo-config.c
index 08fc4cc..ab8f1af 100644
--- a/repo-config.c
+++ b/repo-config.c
@@ -64,7 +64,20 @@
 
 static int get_value(const char* key_, const char* regex_)
 {
+	int ret = -1;
 	char *tl;
+	char *global = NULL, *repo_config = NULL;
+	const char *local;
+
+	local = getenv("GIT_CONFIG");
+	if (!local) {
+		const char *home = getenv("HOME");
+		local = getenv("GIT_CONFIG_LOCAL");
+		if (!local)
+			local = repo_config = strdup(git_path("config"));
+		if (home)
+			global = strdup(mkpath("%s/.gitconfig", home));
+	}
 
 	key = strdup(key_);
 	for (tl=key+strlen(key)-1; tl >= key && *tl != '.'; --tl)
@@ -76,7 +89,7 @@
 		key_regexp = (regex_t*)malloc(sizeof(regex_t));
 		if (regcomp(key_regexp, key, REG_EXTENDED)) {
 			fprintf(stderr, "Invalid key pattern: %s\n", key_);
-			return -1;
+			goto free_strings;
 		}
 	}
 
@@ -89,11 +102,16 @@
 		regexp = (regex_t*)malloc(sizeof(regex_t));
 		if (regcomp(regexp, regex_, REG_EXTENDED)) {
 			fprintf(stderr, "Invalid pattern: %s\n", regex_);
-			return -1;
+			goto free_strings;
 		}
 	}
 
-	git_config(show_config);
+	if (do_all && global)
+		git_config_from_file(show_config, global);
+	git_config_from_file(show_config, local);
+	if (!do_all && !seen && global)
+		git_config_from_file(show_config, global);
+
 	free(key);
 	if (regexp) {
 		regfree(regexp);
@@ -101,9 +119,16 @@
 	}
 
 	if (do_all)
-		return !seen;
+		ret = !seen;
+	else
+		ret =  (seen == 1) ? 0 : 1;
 
-	return (seen == 1) ? 0 : 1;
+free_strings:
+	if (repo_config)
+		free(repo_config);
+	if (global)
+		free(global);
+	return ret;
 }
 
 int main(int argc, const char **argv)
diff --git a/revision.c b/revision.c
index 7bff2a1..b963f2a 100644
--- a/revision.c
+++ b/revision.c
@@ -31,17 +31,12 @@
 	return n;
 }
 
-struct object_list **add_object(struct object *obj,
-				       struct object_list **p,
-				       struct name_path *path,
-				       const char *name)
+void add_object(struct object *obj,
+		struct object_array *p,
+		struct name_path *path,
+		const char *name)
 {
-	struct object_list *entry = xmalloc(sizeof(*entry));
-	entry->item = obj;
-	entry->next = *p;
-	entry->name = path_name(path, name);
-	*p = entry;
-	return &entry->next;
+	add_object_array(obj, path_name(path, name), p);
 }
 
 static void mark_blob_uninteresting(struct blob *blob)
@@ -117,9 +112,9 @@
 	}
 }
 
-static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
 {
-	add_object(obj, &revs->pending_objects, NULL, name);
+	add_object_array(obj, name, &revs->pending);
 }
 
 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
@@ -836,7 +831,7 @@
 		object = get_reference(revs, arg, sha1, flags ^ local_flags);
 		add_pending_object(revs, object, arg);
 	}
-	if (def && !revs->pending_objects) {
+	if (def && !revs->pending.nr) {
 		unsigned char sha1[20];
 		struct object *object;
 		if (get_sha1(def, sha1))
@@ -868,11 +863,13 @@
 
 void prepare_revision_walk(struct rev_info *revs)
 {
-	struct object_list *list;
+	int nr = revs->pending.nr;
+	struct object_array_entry *list = revs->pending.objects;
 
-	list = revs->pending_objects;
-	revs->pending_objects = NULL;
-	while (list) {
+	revs->pending.nr = 0;
+	revs->pending.alloc = 0;
+	revs->pending.objects = NULL;
+	while (--nr >= 0) {
 		struct commit *commit = handle_commit(revs, list->item, list->name);
 		if (commit) {
 			if (!(commit->object.flags & SEEN)) {
@@ -880,7 +877,7 @@
 				insert_by_date(commit, &revs->commits);
 			}
 		}
-		list = list->next;
+		list++;
 	}
 
 	if (revs->no_walk)
diff --git a/revision.h b/revision.h
index 4020e25..c010a08 100644
--- a/revision.h
+++ b/revision.h
@@ -18,7 +18,7 @@
 struct rev_info {
 	/* Starting list */
 	struct commit_list *commits;
-	struct object_list *pending_objects;
+	struct object_array pending;
 
 	/* Basic information */
 	const char *prefix;
@@ -99,9 +99,11 @@
 	const char *elem;
 };
 
-extern struct object_list **add_object(struct object *obj,
-				       struct object_list **p,
-				       struct name_path *path,
-				       const char *name);
+extern void add_object(struct object *obj,
+		       struct object_array *p,
+		       struct name_path *path,
+		       const char *name);
+
+extern void add_pending_object(struct rev_info *revs, struct object *obj, const char *name);
 
 #endif
diff --git a/sha1_file.c b/sha1_file.c
index b4ff233..c80528b 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -486,8 +486,9 @@
 		 * this is cheap.
 		 */
 		if (memcmp((char*)(p->index_base) + p->index_size - 40,
-			   p->pack_base + p->pack_size - 20, 20)) {
-			      
+			   (char *) p->pack_base + p->pack_size - 20,
+			   20)) {
+
 			die("packfile %s does not match index.", p->pack_name);
 		}
 	}
@@ -701,7 +702,7 @@
 	int bytes = strlen(buffer) + 1;
 	unsigned char *buf = xmalloc(1+size);
 
-	memcpy(buf, buffer + bytes, stream->total_out - bytes);
+	memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
 	bytes = stream->total_out - bytes;
 	if (bytes < size) {
 		stream->next_out = buf + bytes;
@@ -853,7 +854,7 @@
 	if (offset >= p->pack_size)
 		die("object offset outside of pack file");
 
-	pack =  p->pack_base + offset;
+	pack =  (unsigned char *) p->pack_base + offset;
 	c = *pack++;
 	offset++;
 	*type = (c >> 4) & 7;
@@ -883,7 +884,7 @@
 	ptr = unpack_object_header(p, ptr, kindp, sizep);
 	if (*kindp != OBJ_DELTA)
 		goto done;
-	memcpy(base, p->pack_base + ptr, 20);
+	memcpy(base, (char *) p->pack_base + ptr, 20);
 	status = 0;
  done:
 	unuse_packed_git(p);
@@ -903,7 +904,7 @@
 	enum object_type kind;
 
 	offset = unpack_object_header(p, e->offset, &kind, size);
-	pack = p->pack_base + offset;
+	pack = (unsigned char *) p->pack_base + offset;
 	if (kind != OBJ_DELTA)
 		*delta_chain_length = 0;
 	else {
@@ -919,7 +920,7 @@
 			find_pack_entry_one(pack, &base_ent, p);
 			offset = unpack_object_header(p, base_ent.offset,
 						      &kind, &junk);
-			pack = p->pack_base + offset;
+			pack = (unsigned char *) p->pack_base + offset;
 			chain_length++;
 		} while (kind == OBJ_DELTA);
 		*delta_chain_length = chain_length;
@@ -957,7 +958,7 @@
 		die("cannot map packed file");
 
 	offset = unpack_object_header(p, entry->offset, &kind, &size);
-	pack = p->pack_base + offset;
+	pack = (unsigned char *) p->pack_base + offset;
 	left = p->pack_size - offset;
 
 	switch (kind) {
@@ -1096,7 +1097,7 @@
 	void *retval;
 
 	offset = unpack_object_header(p, entry->offset, &kind, &size);
-	pack = p->pack_base + offset;
+	pack = (unsigned char *) p->pack_base + offset;
 	left = p->pack_size - offset;
 	switch (kind) {
 	case OBJ_DELTA:
@@ -1134,7 +1135,7 @@
 	void *index = p->index_base + 256;
 	if (n < 0 || num_packed_objects(p) <= n)
 		return -1;
-	memcpy(sha1, (index + 24 * n + 4), 20);
+	memcpy(sha1, (char *) index + (24 * n) + 4, 20);
 	return 0;
 }
 
@@ -1148,9 +1149,9 @@
 
 	do {
 		int mi = (lo + hi) / 2;
-		int cmp = memcmp(index + 24 * mi + 4, sha1, 20);
+		int cmp = memcmp((char *) index + (24 * mi) + 4, sha1, 20);
 		if (!cmp) {
-			e->offset = ntohl(*((unsigned int *)(index + 24 * mi)));
+			e->offset = ntohl(*((unsigned int *) ((char *) index + (24 * mi))));
 			memcpy(e->sha1, sha1, 20);
 			e->p = p;
 			return 1;
@@ -1290,7 +1291,7 @@
 		ref_length = strlen(ref_type);
 
 		if (memcmp(buffer, ref_type, ref_length) ||
-		    get_sha1_hex(buffer + ref_length, actual_sha1)) {
+		    get_sha1_hex((char *) buffer + ref_length, actual_sha1)) {
 			free(buffer);
 			return NULL;
 		}
@@ -1408,7 +1409,7 @@
 			return error("file write error (%s)", strerror(errno));
 		}
 		len -= size;
-		buf += size;
+		buf = (char *) buf + size;
 	}
 	return 0;
 }
diff --git a/ssh-fetch.c b/ssh-fetch.c
index e3067b8..1e59cd2 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -24,7 +24,7 @@
 {
 	ssize_t ret = 0;
 	while (ret < length) {
-		ssize_t size = write(fd, buffer + ret, length - ret);
+		ssize_t size = write(fd, (char *) buffer + ret, length - ret);
 		if (size < 0) {
 			return size;
 		}
diff --git a/t/Makefile b/t/Makefile
index 5495985..632c55f 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -19,7 +19,7 @@
 all: $(T) clean
 
 $(T):
-	@echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 clean:
 	rm -fr trash
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 8260d57..0de2497 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -309,5 +309,29 @@
 
 test_expect_success 'new variable inserts into proper section' 'cmp .git/config expect'
 
+cat > other-config << EOF
+[ein]
+	bahn = strasse
+EOF
+
+cat > expect << EOF
+ein.bahn=strasse
+EOF
+
+GIT_CONFIG=other-config git-repo-config -l > output
+
+test_expect_success 'alternative GIT_CONFIG' 'cmp output expect'
+
+GIT_CONFIG=other-config git-repo-config anwohner.park ausweis
+
+cat > expect << EOF
+[ein]
+	bahn = strasse
+[anwohner]
+	park = ausweis
+EOF
+
+test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
+
 test_done
 
diff --git a/tag.c b/tag.c
index 5f70a5b..74d0dab 100644
--- a/tag.c
+++ b/tag.c
@@ -47,10 +47,10 @@
 
 	if (size < 64)
 		return -1;
-	if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
+	if (memcmp("object ", data, 7) || get_sha1_hex((char *) data + 7, object))
 		return -1;
 
-	type_line = data + 48;
+	type_line = (char *) data + 48;
 	if (memcmp("\ntype ", type_line-1, 6))
 		return -1;
 
diff --git a/tree-walk.c b/tree-walk.c
index 297c697..3f83e98 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -43,7 +43,7 @@
 
 	if (size < len)
 		die("corrupt tree file");
-	desc->buf = buf + len;
+	desc->buf = (char *) buf + len;
 	desc->size = size - len;
 }
 
@@ -66,7 +66,7 @@
 	const void *tree = desc->buf;
 	unsigned long size = desc->size;
 	int len = strlen(tree)+1;
-	const unsigned char *sha1 = tree + len;
+	const unsigned char *sha1 = (unsigned char *) tree + len;
 	const char *path;
 	unsigned int mode;
 
@@ -80,7 +80,8 @@
 
 int tree_entry(struct tree_desc *desc, struct name_entry *entry)
 {
-	const void *tree = desc->buf, *path;
+	const void *tree = desc->buf;
+	const char *path;
 	unsigned long len, size = desc->size;
 
 	if (!size)
@@ -95,10 +96,10 @@
 	entry->pathlen = len;
 
 	path += len + 1;
-	entry->sha1 = path;
+	entry->sha1 = (const unsigned char *) path;
 
 	path += 20;
-	len = path - tree;
+	len = path - (char *) tree;
 	if (len > size)
 		die("corrupt tree file");
 
diff --git a/upload-pack.c b/upload-pack.c
index 979e583..7b86f69 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 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 @@
 	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 @@
 		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 @@
 		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 @@
 			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 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)