Merge branch 'kh/svn'

* kh/svn:
  git-svnimport: Don't assume that copied files haven't changed
diff --git a/Makefile b/Makefile
index 3367b8c..6b10eaa 100644
--- a/Makefile
+++ b/Makefile
@@ -208,7 +208,7 @@
 	quote.o read-cache.o refs.o run-command.o \
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
-	fetch-clone.o revision.o pager.o tree-walk.o \
+	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
 	$(DIFF_OBJS)
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
@@ -324,10 +324,12 @@
 	curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
 	ifeq "$(curl_check)" "070908"
 		ifndef NO_EXPAT
-			EXPAT_LIBEXPAT = -lexpat
 			PROGRAMS += git-http-push$X
 		endif
 	endif
+	ifndef NO_EXPAT
+		EXPAT_LIBEXPAT = -lexpat
+	endif
 endif
 
 ifndef NO_OPENSSL
@@ -513,6 +515,11 @@
 http.o: http.c
 	$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
 
+ifdef NO_EXPAT
+http-fetch.o: http-fetch.c
+	$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
+endif
+
 git-%$X: %.o $(GITLIBS)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -535,7 +542,7 @@
 
 git-http-fetch$X: fetch.o http.o http-fetch.o $(LIB_FILE)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
-		$(LIBS) $(CURL_LIBCURL)
+		$(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
 git-http-push$X: revision.o http.o http-push.o $(LIB_FILE)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
diff --git a/blame.c b/blame.c
index 9bb34e6..6730b10 100644
--- a/blame.c
+++ b/blame.c
@@ -16,6 +16,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "revision.h"
+#include "xdiff-interface.h"
 
 #define DEBUG 0
 
@@ -57,116 +58,89 @@
 static int num_commits = 0;
 static int patch_time = 0;
 
-#define TEMPFILE_PATH_LEN 60
+struct blame_diff_state {
+	struct xdiff_emit_state xm;
+	struct patch *ret;
+};
+
+static void process_u0_diff(void *state_, char *line, unsigned long len)
+{
+	struct blame_diff_state *state = state_;
+	struct chunk *chunk;
+
+	if (len < 4 || line[0] != '@' || line[1] != '@')
+		return;
+
+	if (DEBUG)
+		printf("chunk line: %.*s", (int)len, line);
+	state->ret->num++;
+	state->ret->chunks = xrealloc(state->ret->chunks,
+				      sizeof(struct chunk) * state->ret->num);
+	chunk = &state->ret->chunks[state->ret->num - 1];
+
+	assert(!strncmp(line, "@@ -", 4));
+
+	if (parse_hunk_header(line, len,
+			      &chunk->off1, &chunk->len1,
+			      &chunk->off2, &chunk->len2)) {
+		state->ret->num--;
+		return;
+	}
+
+	if (chunk->len1 == 0)
+		chunk->off1++;
+	if (chunk->len2 == 0)
+		chunk->off2++;
+
+	if (chunk->off1 > 0)
+		chunk->off1--;
+	if (chunk->off2 > 0)
+		chunk->off2--;
+
+	assert(chunk->off1 >= 0);
+	assert(chunk->off2 >= 0);
+}
+
 static struct patch *get_patch(struct commit *commit, struct commit *other)
 {
-	struct patch *ret;
+	struct blame_diff_state state;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	mmfile_t file_c, file_o;
+	xdemitcb_t ecb;
 	struct util_info *info_c = (struct util_info *)commit->object.util;
 	struct util_info *info_o = (struct util_info *)other->object.util;
-	char tmp_path1[TEMPFILE_PATH_LEN], tmp_path2[TEMPFILE_PATH_LEN];
-	char diff_cmd[TEMPFILE_PATH_LEN*2 + 20];
 	struct timeval tv_start, tv_end;
-	int fd;
-	FILE *fin;
-	char buf[1024];
-
-	ret = xmalloc(sizeof(struct patch));
-	ret->chunks = NULL;
-	ret->num = 0;
 
 	get_blob(commit);
+	file_c.ptr = info_c->buf;
+	file_c.size = info_c->size;
+
 	get_blob(other);
+	file_o.ptr = info_o->buf;
+	file_o.size = info_o->size;
 
 	gettimeofday(&tv_start, NULL);
 
-	fd = git_mkstemp(tmp_path1, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file: %s", strerror(errno));
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 0;
+	xecfg.flags = 0;
+	ecb.outf = xdiff_outf;
+	ecb.priv = &state;
+	memset(&state, 0, sizeof(state));
+	state.xm.consume = process_u0_diff;
+	state.ret = xmalloc(sizeof(struct patch));
+	state.ret->chunks = NULL;
+	state.ret->num = 0;
 
-	if (xwrite(fd, info_c->buf, info_c->size) != info_c->size)
-		die("write failed: %s", strerror(errno));
-	close(fd);
-
-	fd = git_mkstemp(tmp_path2, TEMPFILE_PATH_LEN, "git-blame-XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file: %s", strerror(errno));
-
-	if (xwrite(fd, info_o->buf, info_o->size) != info_o->size)
-		die("write failed: %s", strerror(errno));
-	close(fd);
-
-	sprintf(diff_cmd, "diff -U 0 %s %s", tmp_path1, tmp_path2);
-	fin = popen(diff_cmd, "r");
-	if (!fin)
-		die("popen failed: %s", strerror(errno));
-
-	while (fgets(buf, sizeof(buf), fin)) {
-		struct chunk *chunk;
-		char *start, *sp;
-
-		if (buf[0] != '@' || buf[1] != '@')
-			continue;
-
-		if (DEBUG)
-			printf("chunk line: %s", buf);
-		ret->num++;
-		ret->chunks = xrealloc(ret->chunks,
-				       sizeof(struct chunk) * ret->num);
-		chunk = &ret->chunks[ret->num - 1];
-
-		assert(!strncmp(buf, "@@ -", 4));
-
-		start = buf + 4;
-		sp = index(start, ' ');
-		*sp = '\0';
-		if (index(start, ',')) {
-			int ret =
-			    sscanf(start, "%d,%d", &chunk->off1, &chunk->len1);
-			assert(ret == 2);
-		} else {
-			int ret = sscanf(start, "%d", &chunk->off1);
-			assert(ret == 1);
-			chunk->len1 = 1;
-		}
-		*sp = ' ';
-
-		start = sp + 1;
-		sp = index(start, ' ');
-		*sp = '\0';
-		if (index(start, ',')) {
-			int ret =
-			    sscanf(start, "%d,%d", &chunk->off2, &chunk->len2);
-			assert(ret == 2);
-		} else {
-			int ret = sscanf(start, "%d", &chunk->off2);
-			assert(ret == 1);
-			chunk->len2 = 1;
-		}
-		*sp = ' ';
-
-		if (chunk->len1 == 0)
-			chunk->off1++;
-		if (chunk->len2 == 0)
-			chunk->off2++;
-
-		if (chunk->off1 > 0)
-			chunk->off1--;
-		if (chunk->off2 > 0)
-			chunk->off2--;
-
-		assert(chunk->off1 >= 0);
-		assert(chunk->off2 >= 0);
-	}
-	pclose(fin);
-	unlink(tmp_path1);
-	unlink(tmp_path2);
+	xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb);
 
 	gettimeofday(&tv_end, NULL);
 	patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
 		tv_end.tv_usec - tv_start.tv_usec;
 
 	num_get_patch++;
-	return ret;
+	return state.ret;
 }
 
 static void free_patch(struct patch *p)
@@ -674,7 +648,7 @@
 	static char author_buf[1024];
 
 	tmp = strstr(commit->buffer, "\nauthor ") + 8;
-	len = index(tmp, '\n') - tmp;
+	len = strchr(tmp, '\n') - tmp;
 	ret->author = author_buf;
 	memcpy(ret->author, tmp, len);
 
@@ -729,11 +703,30 @@
 	return util->topo_data;
 }
 
+static int read_ancestry(const char *graft_file,
+			 unsigned char **start_sha1)
+{
+	FILE *fp = fopen(graft_file, "r");
+	char buf[1024];
+	if (!fp)
+		return -1;
+	while (fgets(buf, sizeof(buf), fp)) {
+		/* The format is just "Commit Parent1 Parent2 ...\n" */
+		int len = strlen(buf);
+		struct commit_graft *graft = read_graft_line(buf, len);
+		register_commit_graft(graft, 0);
+		if (!*start_sha1)
+			*start_sha1 = graft->sha1;
+	}
+	fclose(fp);
+	return 0;
+}
+
 int main(int argc, const char **argv)
 {
 	int i;
 	struct commit *initial = NULL;
-	unsigned char sha1[20];
+	unsigned char sha1[20], *sha1_p = NULL;
 
 	const char *filename = NULL, *commit = NULL;
 	char filename_buf[256];
@@ -767,6 +760,14 @@
 				  !strcmp(argv[i], "--compability")) {
 				compability = 1;
 				continue;
+			} else if(!strcmp(argv[i], "-S")) {
+				if (i + 1 < argc &&
+				    !read_ancestry(argv[i + 1], &sha1_p)) {
+					compability = 1;
+					i++;
+					continue;
+				}
+				usage(blame_usage);
 			} else if(!strcmp(argv[i], "--")) {
 				options = 0;
 				continue;
@@ -788,7 +789,9 @@
 
 	if(!filename)
 		usage(blame_usage);
-	if(!commit)
+	if (commit && sha1_p)
+		usage(blame_usage);
+	else if(!commit)
 		commit = "HEAD";
 
 	if(prefix)
@@ -797,9 +800,12 @@
 		strcpy(filename_buf, filename);
 	filename = filename_buf;
 
-	if (get_sha1(commit, sha1))
-		die("get_sha1 failed, commit '%s' not found", commit);
-	start_commit = lookup_commit_reference(sha1);
+	if (!sha1_p) {
+		if (get_sha1(commit, sha1))
+			die("get_sha1 failed, commit '%s' not found", commit);
+		sha1_p = sha1;
+	}
+	start_commit = lookup_commit_reference(sha1_p);
 	get_util(start_commit)->pathname = filename;
 	if (fill_util_info(start_commit)) {
 		printf("%s not found in %s\n", filename, commit);
@@ -876,7 +882,7 @@
 			if(blame_contents[blame_len-1] != '\n')
 				putc('\n', stdout);
 		} else {
-			char* next_buf = index(buf, '\n') + 1;
+			char* next_buf = strchr(buf, '\n') + 1;
 			fwrite(buf, next_buf - buf, 1, stdout);
 			buf = next_buf;
 		}
diff --git a/combine-diff.c b/combine-diff.c
index 7693884..eb0d757 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -4,6 +4,7 @@
 #include "diff.h"
 #include "diffcore.h"
 #include "quote.h"
+#include "xdiff-interface.h"
 
 static int uninteresting(struct diff_filepair *p)
 {
@@ -110,78 +111,9 @@
 	return blob;
 }
 
-#define TMPPATHLEN 50
-#define MAXLINELEN 10240
-
-static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
-{
-	int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
-	if (fd < 0)
-		die("unable to create temp-file");
-	if (write(fd, blob, size) != size)
-		die("unable to write temp-file");
-	close(fd);
-}
-
-static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
-{
-	unsigned long size;
-	void *blob;
-	blob = grab_blob(sha1, &size);
-	write_to_temp_file(tmpfile, blob, size);
-	free(blob);
-}
-
-static int parse_num(char **cp_p, unsigned int *num_p)
-{
-	char *cp = *cp_p;
-	unsigned int num = 0;
-	int read_some;
-
-	while ('0' <= *cp && *cp <= '9')
-		num = num * 10 + *cp++ - '0';
-	if (!(read_some = cp - *cp_p))
-		return -1;
-	*cp_p = cp;
-	*num_p = num;
-	return 0;
-}
-
-static int parse_hunk_header(char *line, int len,
-			     unsigned int *ob, unsigned int *on,
-			     unsigned int *nb, unsigned int *nn)
-{
-	char *cp;
-	cp = line + 4;
-	if (parse_num(&cp, ob)) {
-	bad_line:
-		return error("malformed diff output: %s", line);
-	}
-	if (*cp == ',') {
-		cp++;
-		if (parse_num(&cp, on))
-			goto bad_line;
-	}
-	else
-		*on = 1;
-	if (*cp++ != ' ' || *cp++ != '+')
-		goto bad_line;
-	if (parse_num(&cp, nb))
-		goto bad_line;
-	if (*cp == ',') {
-		cp++;
-		if (parse_num(&cp, nn))
-			goto bad_line;
-	}
-	else
-		*nn = 1;
-	return -!!memcmp(cp, " @@", 3);
-}
-
-static void append_lost(struct sline *sline, int n, const char *line)
+static void append_lost(struct sline *sline, int n, const char *line, int len)
 {
 	struct lline *lline;
-	int len = strlen(line);
 	unsigned long this_mask = (1UL<<n);
 	if (line[len-1] == '\n')
 		len--;
@@ -216,70 +148,93 @@
 	sline->lost_tail = &lline->next;
 }
 
-static void combine_diff(const unsigned char *parent, const char *ourtmp,
+struct combine_diff_state {
+	struct xdiff_emit_state xm;
+
+	unsigned int lno;
+	int ob, on, nb, nn;
+	unsigned long nmask;
+	int num_parent;
+	int n;
+	struct sline *sline;
+	struct sline *lost_bucket;
+};
+
+static void consume_line(void *state_, char *line, unsigned long len)
+{
+	struct combine_diff_state *state = state_;
+	if (5 < len && !memcmp("@@ -", line, 4)) {
+		if (parse_hunk_header(line, len,
+				      &state->ob, &state->on,
+				      &state->nb, &state->nn))
+			return;
+		state->lno = state->nb;
+		if (!state->nb)
+			/* @@ -1,2 +0,0 @@ to remove the
+			 * first two lines...
+			 */
+			state->nb = 1;
+		if (state->nn == 0)
+			/* @@ -X,Y +N,0 @@ removed Y lines
+			 * that would have come *after* line N
+			 * in the result.  Our lost buckets hang
+			 * to the line after the removed lines,
+			 */
+			state->lost_bucket = &state->sline[state->nb];
+		else
+			state->lost_bucket = &state->sline[state->nb-1];
+		if (!state->sline[state->nb-1].p_lno)
+			state->sline[state->nb-1].p_lno =
+				xcalloc(state->num_parent,
+					sizeof(unsigned long));
+		state->sline[state->nb-1].p_lno[state->n] = state->ob;
+		return;
+	}
+	if (!state->lost_bucket)
+		return; /* not in any hunk yet */
+	switch (line[0]) {
+	case '-':
+		append_lost(state->lost_bucket, state->n, line+1, len-1);
+		break;
+	case '+':
+		state->sline[state->lno-1].flag |= state->nmask;
+		state->lno++;
+		break;
+	}
+}
+
+static void combine_diff(const unsigned char *parent, mmfile_t *result_file,
 			 struct sline *sline, int cnt, int n, int num_parent)
 {
-	FILE *in;
-	char parent_tmp[TMPPATHLEN];
-	char cmd[TMPPATHLEN * 2 + 1024];
-	char line[MAXLINELEN];
-	unsigned int lno, ob, on, nb, nn, p_lno;
+	unsigned int p_lno, lno;
 	unsigned long nmask = (1UL << n);
-	struct sline *lost_bucket = NULL;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	mmfile_t parent_file;
+	xdemitcb_t ecb;
+	struct combine_diff_state state;
+	unsigned long sz;
 
 	if (!cnt)
 		return; /* result deleted */
 
-	write_temp_blob(parent_tmp, parent);
-	sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
-		parent_tmp, ourtmp);
-	in = popen(cmd, "r");
-	if (!in)
-		die("cannot spawn %s", cmd);
+	parent_file.ptr = grab_blob(parent, &sz);
+	parent_file.size = sz;
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 0;
+	xecfg.flags = 0;
+	ecb.outf = xdiff_outf;
+	ecb.priv = &state;
+	memset(&state, 0, sizeof(state));
+	state.xm.consume = consume_line;
+	state.nmask = nmask;
+	state.sline = sline;
+	state.lno = 1;
+	state.num_parent = num_parent;
+	state.n = n;
 
-	lno = 1;
-	while (fgets(line, sizeof(line), in) != NULL) {
-		int len = strlen(line);
-		if (5 < len && !memcmp("@@ -", line, 4)) {
-			if (parse_hunk_header(line, len,
-					      &ob, &on, &nb, &nn))
-				break;
-			lno = nb;
-			if (!nb)
-				/* @@ -1,2 +0,0 @@ to remove the
-				 * first two lines...
-				 */
-				nb = 1;
-			if (nn == 0)
-				/* @@ -X,Y +N,0 @@ removed Y lines
-				 * that would have come *after* line N
-				 * in the result.  Our lost buckets hang
-				 * to the line after the removed lines,
-				 */
-				lost_bucket = &sline[nb];
-			else
-				lost_bucket = &sline[nb-1];
-			if (!sline[nb-1].p_lno)
-				sline[nb-1].p_lno =
-					xcalloc(num_parent,
-						sizeof(unsigned long));
-			sline[nb-1].p_lno[n] = ob;
-			continue;
-		}
-		if (!lost_bucket)
-			continue; /* not in any hunk yet */
-		switch (line[0]) {
-		case '-':
-			append_lost(lost_bucket, n, line+1);
-			break;
-		case '+':
-			sline[lno-1].flag |= nmask;
-			lno++;
-			break;
-		}
-	}
-	fclose(in);
-	unlink(parent_tmp);
+	xdl_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+	free(parent_file.ptr);
 
 	/* Assign line numbers for this parent.
 	 *
@@ -625,61 +580,56 @@
 			   int dense, const char *header,
 			   struct diff_options *opt)
 {
-	unsigned long size, cnt, lno;
+	unsigned long result_size, cnt, lno;
 	char *result, *cp, *ep;
 	struct sline *sline; /* survived lines */
 	int mode_differs = 0;
 	int i, show_hunks, shown_header = 0;
-	char ourtmp_buf[TMPPATHLEN];
-	char *ourtmp = ourtmp_buf;
 	int working_tree_file = !memcmp(elem->sha1, null_sha1, 20);
 	int abbrev = opt->full_index ? 40 : DEFAULT_ABBREV;
+	mmfile_t result_file;
 
 	/* Read the result of merge first */
-	if (!working_tree_file) {
-		result = grab_blob(elem->sha1, &size);
-		write_to_temp_file(ourtmp, result, size);
-	}
+	if (!working_tree_file)
+		result = grab_blob(elem->sha1, &result_size);
 	else {
 		/* Used by diff-tree to read from the working tree */
 		struct stat st;
 		int fd;
-		ourtmp = elem->path;
-		if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+		if (0 <= (fd = open(elem->path, O_RDONLY)) &&
 		    !fstat(fd, &st)) {
 			int len = st.st_size;
 			int cnt = 0;
 
 			elem->mode = canon_mode(st.st_mode);
-			size = len;
+			result_size = len;
 			result = xmalloc(len + 1);
 			while (cnt < len) {
 				int done = xread(fd, result+cnt, len-cnt);
 				if (done == 0)
 					break;
 				if (done < 0)
-					die("read error '%s'", ourtmp);
+					die("read error '%s'", elem->path);
 				cnt += done;
 			}
 			result[len] = 0;
 		}
 		else {
 			/* deleted file */
-			size = 0;
+			result_size = 0;
 			elem->mode = 0;
 			result = xmalloc(1);
 			result[0] = 0;
-			ourtmp = "/dev/null";
 		}
 		if (0 <= fd)
 			close(fd);
 	}
 
-	for (cnt = 0, cp = result; cp - result < size; cp++) {
+	for (cnt = 0, cp = result; cp - result < result_size; cp++) {
 		if (*cp == '\n')
 			cnt++;
 	}
-	if (size && result[size-1] != '\n')
+	if (result_size && result[result_size-1] != '\n')
 		cnt++; /* incomplete line */
 
 	sline = xcalloc(cnt+1, sizeof(*sline));
@@ -689,7 +639,7 @@
 		sline[lno].lost_tail = &sline[lno].lost_head;
 		sline[lno].flag = 0;
 	}
-	for (lno = 0, cp = result; cp - result < size; cp++) {
+	for (lno = 0, cp = result; cp - result < result_size; cp++) {
 		if (*cp == '\n') {
 			sline[lno].len = cp - sline[lno].bol;
 			lno++;
@@ -697,8 +647,11 @@
 				sline[lno].bol = cp + 1;
 		}
 	}
-	if (size && result[size-1] != '\n')
-		sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+	if (result_size && result[result_size-1] != '\n')
+		sline[cnt-1].len = result_size - (sline[cnt-1].bol - result);
+
+	result_file.ptr = result;
+	result_file.size = result_size;
 
 	sline[0].p_lno = xcalloc((cnt+1) * num_parent, sizeof(unsigned long));
 	for (lno = 0; lno < cnt; lno++)
@@ -714,7 +667,7 @@
 			}
 		}
 		if (i <= j)
-			combine_diff(elem->parent[i].sha1, ourtmp, sline,
+			combine_diff(elem->parent[i].sha1, &result_file, sline,
 				     cnt, i, num_parent);
 		if (elem->parent[i].mode != elem->mode)
 			mode_differs = 1;
@@ -767,8 +720,6 @@
 		}
 		dump_sline(sline, cnt, num_parent);
 	}
-	if (ourtmp == ourtmp_buf)
-		unlink(ourtmp);
 	free(result);
 
 	for (i = 0; i < cnt; i++) {
diff --git a/commit.c b/commit.c
index d4976fb..d534c9b 100644
--- a/commit.c
+++ b/commit.c
@@ -101,11 +101,7 @@
 	return date;
 }
 
-static struct commit_graft {
-	unsigned char sha1[20];
-	int nr_parent;
-	unsigned char parent[0][20]; /* more */
-} **commit_graft;
+static struct commit_graft **commit_graft;
 static int commit_graft_alloc, commit_graft_nr;
 
 static int commit_graft_pos(const unsigned char *sha1)
@@ -127,70 +123,98 @@
 	return -lo - 1;
 }
 
-static void prepare_commit_graft(void)
+int register_commit_graft(struct commit_graft *graft, int ignore_dups)
 {
-	char *graft_file = get_graft_file();
+	int pos = commit_graft_pos(graft->sha1);
+	
+	if (0 <= pos) {
+		if (ignore_dups)
+			free(graft);
+		else {
+			free(commit_graft[pos]);
+			commit_graft[pos] = graft;
+		}
+		return 1;
+	}
+	pos = -pos - 1;
+	if (commit_graft_alloc <= ++commit_graft_nr) {
+		commit_graft_alloc = alloc_nr(commit_graft_alloc);
+		commit_graft = xrealloc(commit_graft,
+					sizeof(*commit_graft) *
+					commit_graft_alloc);
+	}
+	if (pos < commit_graft_nr)
+		memmove(commit_graft + pos + 1,
+			commit_graft + pos,
+			(commit_graft_nr - pos - 1) *
+			sizeof(*commit_graft));
+	commit_graft[pos] = graft;
+	return 0;
+}
+
+struct commit_graft *read_graft_line(char *buf, int len)
+{
+	/* The format is just "Commit Parent1 Parent2 ...\n" */
+	int i;
+	struct commit_graft *graft = NULL;
+
+	if (buf[len-1] == '\n')
+		buf[--len] = 0;
+	if (buf[0] == '#')
+		return 0;
+	if ((len + 1) % 41) {
+	bad_graft_data:
+		error("bad graft data: %s", buf);
+		free(graft);
+		return NULL;
+	}
+	i = (len + 1) / 41 - 1;
+	graft = xmalloc(sizeof(*graft) + 20 * i);
+	graft->nr_parent = i;
+	if (get_sha1_hex(buf, graft->sha1))
+		goto bad_graft_data;
+	for (i = 40; i < len; i += 41) {
+		if (buf[i] != ' ')
+			goto bad_graft_data;
+		if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
+			goto bad_graft_data;
+	}
+	return graft;
+}
+
+int read_graft_file(const char *graft_file)
+{
 	FILE *fp = fopen(graft_file, "r");
 	char buf[1024];
-	if (!fp) {
-		commit_graft = (struct commit_graft **) "hack";
-		return;
-	}
+	if (!fp)
+		return -1;
 	while (fgets(buf, sizeof(buf), fp)) {
 		/* The format is just "Commit Parent1 Parent2 ...\n" */
 		int len = strlen(buf);
-		int i;
-		struct commit_graft *graft = NULL;
-
-		if (buf[len-1] == '\n')
-			buf[--len] = 0;
-		if (buf[0] == '#')
-			continue;
-		if ((len + 1) % 41) {
-		bad_graft_data:
-			error("bad graft data: %s", buf);
-			free(graft);
-			continue;
-		}
-		i = (len + 1) / 41 - 1;
-		graft = xmalloc(sizeof(*graft) + 20 * i);
-		graft->nr_parent = i;
-		if (get_sha1_hex(buf, graft->sha1))
-			goto bad_graft_data;
-		for (i = 40; i < len; i += 41) {
-			if (buf[i] != ' ')
-				goto bad_graft_data;
-			if (get_sha1_hex(buf + i + 1, graft->parent[i/41]))
-				goto bad_graft_data;
-		}
-		i = commit_graft_pos(graft->sha1);
-		if (0 <= i) {
+		struct commit_graft *graft = read_graft_line(buf, len);
+		if (register_commit_graft(graft, 1))
 			error("duplicate graft data: %s", buf);
-			free(graft);
-			continue;
-		}
-		i = -i - 1;
-		if (commit_graft_alloc <= ++commit_graft_nr) {
-			commit_graft_alloc = alloc_nr(commit_graft_alloc);
-			commit_graft = xrealloc(commit_graft,
-						sizeof(*commit_graft) *
-						commit_graft_alloc);
-		}
-		if (i < commit_graft_nr)
-			memmove(commit_graft + i + 1,
-				commit_graft + i,
-				(commit_graft_nr - i - 1) *
-				sizeof(*commit_graft));
-		commit_graft[i] = graft;
 	}
 	fclose(fp);
+	return 0;
+}
+
+static void prepare_commit_graft(void)
+{
+	static int commit_graft_prepared;
+	char *graft_file;
+
+	if (commit_graft_prepared)
+		return;
+	graft_file = get_graft_file();
+	read_graft_file(graft_file);
+	commit_graft_prepared = 1;
 }
 
 static struct commit_graft *lookup_commit_graft(const unsigned char *sha1)
 {
 	int pos;
-	if (!commit_graft)
-		prepare_commit_graft();
+	prepare_commit_graft();
 	pos = commit_graft_pos(sha1);
 	if (pos < 0)
 		return NULL;
diff --git a/commit.h b/commit.h
index 98682b2..918c9ab 100644
--- a/commit.h
+++ b/commit.h
@@ -90,4 +90,15 @@
 void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
 				  topo_sort_set_fn_t setter,
 				  topo_sort_get_fn_t getter);
+
+struct commit_graft {
+	unsigned char sha1[20];
+	int nr_parent;
+	unsigned char parent[FLEX_ARRAY][20]; /* more */
+};
+
+struct commit_graft *read_graft_line(char *buf, int len);
+int register_commit_graft(struct commit_graft *, int);
+int read_graft_file(const char *graft_file);
+
 #endif /* COMMIT_H */
diff --git a/date.c b/date.c
index 376d25d..034d722 100644
--- a/date.c
+++ b/date.c
@@ -197,26 +197,43 @@
 	return skip_alpha(date);
 }
 
-static int is_date(int year, int month, int day, struct tm *tm)
+static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
 {
 	if (month > 0 && month < 13 && day > 0 && day < 32) {
-		if (year == -1) {
-			tm->tm_mon = month-1;
-			tm->tm_mday = day;
-			return 1;
-		}
-		if (year >= 1970 && year < 2100) {
-			year -= 1900;
-		} else if (year > 70 && year < 100) {
-			/* ok */
-		} else if (year < 38) {
-			year += 100;
-		} else
-			return 0;
+		struct tm check = *tm;
+		struct tm *r = (now_tm ? &check : tm);
+		time_t specified;
 
-		tm->tm_mon = month-1;
-		tm->tm_mday = day;
-		tm->tm_year = year;
+		r->tm_mon = month - 1;
+		r->tm_mday = day;
+		if (year == -1) {
+			if (!now_tm)
+				return 1;
+			r->tm_year = now_tm->tm_year;
+		}
+		else if (year >= 1970 && year < 2100)
+			r->tm_year = year - 1900;
+		else if (year > 70 && year < 100)
+			r->tm_year = year;
+		else if (year < 38)
+			r->tm_year = year + 100;
+		else
+			return 0;
+		if (!now_tm)
+			return 1;
+
+		specified = my_mktime(r);
+
+		/* Be it commit time or author time, it does not make
+		 * sense to specify timestamp way into the future.  Make
+		 * sure it is not later than ten days from now...
+		 */
+		if (now + 10*24*3600 < specified)
+			return 0;
+		tm->tm_mon = r->tm_mon;
+		tm->tm_mday = r->tm_mday;
+		if (year != -1)
+			tm->tm_year = r->tm_year;
 		return 1;
 	}
 	return 0;
@@ -224,6 +241,9 @@
 
 static int match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
 {
+	time_t now;
+	struct tm now_tm;
+	struct tm *refuse_future;
 	long num2, num3;
 
 	num2 = strtol(end+1, &end, 10);
@@ -246,19 +266,33 @@
 
 	case '-':
 	case '/':
+	case '.':
+		now = time(NULL);
+		refuse_future = NULL;
+		if (gmtime_r(&now, &now_tm))
+			refuse_future = &now_tm;
+
 		if (num > 70) {
 			/* yyyy-mm-dd? */
-			if (is_date(num, num2, num3, tm))
+			if (is_date(num, num2, num3, refuse_future, now, tm))
 				break;
 			/* yyyy-dd-mm? */
-			if (is_date(num, num3, num2, tm))
+			if (is_date(num, num3, num2, refuse_future, now, tm))
 				break;
 		}
-		/* mm/dd/yy ? */
-		if (is_date(num3, num, num2, tm))
+		/* Our eastern European friends say dd.mm.yy[yy]
+		 * is the norm there, so giving precedence to
+		 * mm/dd/yy[yy] form only when separator is not '.'
+		 */
+		if (c != '.' &&
+		    is_date(num3, num, num2, refuse_future, now, tm))
 			break;
-		/* dd/mm/yy ? */
-		if (is_date(num3, num2, num, tm))
+		/* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
+		if (is_date(num3, num2, num, refuse_future, now, tm))
+			break;
+		/* Funny European mm.dd.yy */
+		if (c == '.' &&
+		    is_date(num3, num, num2, refuse_future, now, tm))
 			break;
 		return 0;
 	}
@@ -288,10 +322,11 @@
 	}
 
 	/*
-	 * Check for special formats: num[:-/]num[same]num
+	 * Check for special formats: num[-.:/]num[same]num
 	 */
 	switch (*end) {
 	case ':':
+	case '.':
 	case '/':
 	case '-':
 		if (isdigit(end[1])) {
diff --git a/delta.h b/delta.h
index a15350d..9464f3e 100644
--- a/delta.h
+++ b/delta.h
@@ -16,7 +16,8 @@
  * This must be called twice on the delta data buffer, first to get the
  * expected reference buffer size, and again to get the result buffer size.
  */
-static inline unsigned long get_delta_hdr_size(const unsigned char **datap)
+static inline unsigned long get_delta_hdr_size(const unsigned char **datap,
+					       const unsigned char *top)
 {
 	const unsigned char *data = *datap;
 	unsigned char cmd;
@@ -26,7 +27,7 @@
 		cmd = *data++;
 		size |= (cmd & ~0x80) << i;
 		i += 7;
-	} while (cmd & 0x80);
+	} while (cmd & 0x80 && data < top);
 	*datap = data;
 	return size;
 }
diff --git a/gitk b/gitk
index 26fa79a..f88c06e 100755
--- a/gitk
+++ b/gitk
@@ -2230,7 +2230,7 @@
     }
 }
 
-proc findcont {id} {
+proc findcont {} {
     global findid treediffs parentlist
     global ffileline findstartline finddidsel
     global displayorder numcommits matchinglines findinprogress
@@ -2700,7 +2700,7 @@
 	incr nextupdate 100
 	fileevent $mdf readable {}
 	update
-	fileevent $mdf readable [list getmergediffline $mdf $id]
+	fileevent $mdf readable [list getmergediffline $mdf $id $np]
     }
 }
 
diff --git a/http-fetch.c b/http-fetch.c
index dc67218..71a7daf 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -4,6 +4,35 @@
 #include "fetch.h"
 #include "http.h"
 
+#ifndef NO_EXPAT
+#include <expat.h>
+
+/* Definitions for DAV requests */
+#define DAV_PROPFIND "PROPFIND"
+#define DAV_PROPFIND_RESP ".multistatus.response"
+#define DAV_PROPFIND_NAME ".multistatus.response.href"
+#define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
+#define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
+
+/* Definitions for processing XML DAV responses */
+#ifndef XML_STATUS_OK
+enum XML_Status {
+  XML_STATUS_OK = 1,
+  XML_STATUS_ERROR = 0
+};
+#define XML_STATUS_OK    1
+#define XML_STATUS_ERROR 0
+#endif
+
+/* Flags that control remote_ls processing */
+#define PROCESS_FILES (1u << 0)
+#define PROCESS_DIRS  (1u << 1)
+#define RECURSIVE     (1u << 2)
+
+/* Flags that remote_ls passes to callback functions */
+#define IS_DIR (1u << 0)
+#endif
+
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
@@ -15,6 +44,7 @@
 struct alt_base
 {
 	char *base;
+	int path_len;
 	int got_indices;
 	struct packed_git *packs;
 	struct alt_base *next;
@@ -58,6 +88,30 @@
 	int http_specific;
 };
 
+#ifndef NO_EXPAT
+struct xml_ctx
+{
+	char *name;
+	int len;
+	char *cdata;
+	void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
+	void *userData;
+};
+
+struct remote_ls_ctx
+{
+	struct alt_base *repo;
+	char *path;
+	void (*userFunc)(struct remote_ls_ctx *ls);
+	void *userData;
+	int flags;
+	char *dentry_name;
+	int dentry_flags;
+	int rc;
+	struct remote_ls_ctx *parent;
+};
+#endif
+
 static struct object_request *object_queue_head = NULL;
 
 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
@@ -500,6 +554,7 @@
 			int serverlen = 0;
 			struct alt_base *newalt;
 			char *target = NULL;
+			char *path;
 			if (data[i] == '/') {
 				serverlen = strchr(base + 8, '/') - base;
 				okay = 1;
@@ -540,6 +595,13 @@
 				newalt->base = target;
 				newalt->got_indices = 0;
 				newalt->packs = NULL;
+				path = strstr(target, "//");
+				if (path) {
+					path = index(path+2, '/');
+					if (path)
+						newalt->path_len = strlen(path);
+				}
+
 				while (tail->next != NULL)
 					tail = tail->next;
 				tail->next = newalt;
@@ -611,6 +673,209 @@
 	free(url);
 }
 
+#ifndef NO_EXPAT
+static void
+xml_start_tag(void *userData, const char *name, const char **atts)
+{
+	struct xml_ctx *ctx = (struct xml_ctx *)userData;
+	const char *c = index(name, ':');
+	int new_len;
+
+	if (c == NULL)
+		c = name;
+	else
+		c++;
+
+	new_len = strlen(ctx->name) + strlen(c) + 2;
+
+	if (new_len > ctx->len) {
+		ctx->name = xrealloc(ctx->name, new_len);
+		ctx->len = new_len;
+	}
+	strcat(ctx->name, ".");
+	strcat(ctx->name, c);
+
+	if (ctx->cdata) {
+		free(ctx->cdata);
+		ctx->cdata = NULL;
+	}
+
+	ctx->userFunc(ctx, 0);
+}
+
+static void
+xml_end_tag(void *userData, const char *name)
+{
+	struct xml_ctx *ctx = (struct xml_ctx *)userData;
+	const char *c = index(name, ':');
+	char *ep;
+
+	ctx->userFunc(ctx, 1);
+
+	if (c == NULL)
+		c = name;
+	else
+		c++;
+
+	ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
+	*ep = 0;
+}
+
+static void
+xml_cdata(void *userData, const XML_Char *s, int len)
+{
+	struct xml_ctx *ctx = (struct xml_ctx *)userData;
+	if (ctx->cdata)
+		free(ctx->cdata);
+	ctx->cdata = xcalloc(len+1, 1);
+	strncpy(ctx->cdata, s, len);
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+		     void (*userFunc)(struct remote_ls_ctx *ls),
+		     void *userData);
+
+static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
+{
+	struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
+
+	if (tag_closed) {
+		if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
+			if (ls->dentry_flags & IS_DIR) {
+				if (ls->flags & PROCESS_DIRS) {
+					ls->userFunc(ls);
+				}
+				if (strcmp(ls->dentry_name, ls->path) &&
+				    ls->flags & RECURSIVE) {
+					ls->rc = remote_ls(ls->repo,
+							   ls->dentry_name,
+							   ls->flags,
+							   ls->userFunc,
+							   ls->userData);
+				}
+			} else if (ls->flags & PROCESS_FILES) {
+				ls->userFunc(ls);
+			}
+		} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
+			ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+						  ls->repo->path_len + 1);
+			strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
+		} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
+			ls->dentry_flags |= IS_DIR;
+		}
+	} else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
+		if (ls->dentry_name) {
+			free(ls->dentry_name);
+		}
+		ls->dentry_name = NULL;
+		ls->dentry_flags = 0;
+	}
+}
+
+static int remote_ls(struct alt_base *repo, const char *path, int flags,
+		     void (*userFunc)(struct remote_ls_ctx *ls),
+		     void *userData)
+{
+	char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
+	struct active_request_slot *slot;
+	struct slot_results results;
+	struct buffer in_buffer;
+	struct buffer out_buffer;
+	char *in_data;
+	char *out_data;
+	XML_Parser parser = XML_ParserCreate(NULL);
+	enum XML_Status result;
+	struct curl_slist *dav_headers = NULL;
+	struct xml_ctx ctx;
+	struct remote_ls_ctx ls;
+
+	ls.flags = flags;
+	ls.repo = repo;
+	ls.path = strdup(path);
+	ls.dentry_name = NULL;
+	ls.dentry_flags = 0;
+	ls.userData = userData;
+	ls.userFunc = userFunc;
+	ls.rc = 0;
+
+	sprintf(url, "%s%s", repo->base, path);
+
+	out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
+	out_data = xmalloc(out_buffer.size + 1);
+	snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
+	out_buffer.posn = 0;
+	out_buffer.buffer = out_data;
+
+	in_buffer.size = 4096;
+	in_data = xmalloc(in_buffer.size);
+	in_buffer.posn = 0;
+	in_buffer.buffer = in_data;
+
+	dav_headers = curl_slist_append(dav_headers, "Depth: 1");
+	dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
+
+	slot = get_active_slot();
+	slot->results = &results;
+	curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
+	curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
+	curl_easy_setopt(slot->curl, CURLOPT_URL, url);
+	curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
+	curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
+	curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
+
+	if (start_active_slot(slot)) {
+		run_active_slot(slot);
+		if (results.curl_result == CURLE_OK) {
+			ctx.name = xcalloc(10, 1);
+			ctx.len = 0;
+			ctx.cdata = NULL;
+			ctx.userFunc = handle_remote_ls_ctx;
+			ctx.userData = &ls;
+			XML_SetUserData(parser, &ctx);
+			XML_SetElementHandler(parser, xml_start_tag,
+					      xml_end_tag);
+			XML_SetCharacterDataHandler(parser, xml_cdata);
+			result = XML_Parse(parser, in_buffer.buffer,
+					   in_buffer.posn, 1);
+			free(ctx.name);
+
+			if (result != XML_STATUS_OK) {
+				ls.rc = error("XML error: %s",
+					      XML_ErrorString(
+						      XML_GetErrorCode(parser)));
+			}
+		} else {
+			ls.rc = -1;
+		}
+	} else {
+		ls.rc = error("Unable to start PROPFIND request");
+	}
+
+	free(ls.path);
+	free(url);
+	free(out_data);
+	free(in_buffer.buffer);
+	curl_slist_free_all(dav_headers);
+
+	return ls.rc;
+}
+
+static void process_ls_pack(struct remote_ls_ctx *ls)
+{
+	unsigned char sha1[20];
+
+	if (strlen(ls->dentry_name) == 63 &&
+	    !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
+	    !strncmp(ls->dentry_name+58, ".pack", 5)) {
+		get_sha1_hex(ls->dentry_name + 18, sha1);
+		setup_index(ls->repo, sha1);
+	}
+}
+#endif
+
 static int fetch_indices(struct alt_base *repo)
 {
 	unsigned char sha1[20];
@@ -633,6 +898,12 @@
 	if (get_verbosely)
 		fprintf(stderr, "Getting pack list for %s\n", repo->base);
 
+#ifndef NO_EXPAT
+	if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
+		      process_ls_pack, NULL) == 0)
+		return 0;
+#endif
+
 	url = xmalloc(strlen(repo->base) + 21);
 	sprintf(url, "%s/objects/info/packs", repo->base);
 
@@ -947,6 +1218,7 @@
 {
 	char *commit_id;
 	char *url;
+	char *path;
 	int arg = 1;
 	int rc = 0;
 
@@ -987,6 +1259,12 @@
 	alt->got_indices = 0;
 	alt->packs = NULL;
 	alt->next = NULL;
+	path = strstr(url, "//");
+	if (path) {
+		path = index(path+2, '/');
+		if (path)
+			alt->path_len = strlen(path);
+	}
 
 	if (pull(commit_id))
 		rc = 1;
diff --git a/pack-objects.c b/pack-objects.c
index 9346392..09f4f2c 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -453,7 +453,7 @@
 	if (object_ix_hashsz < 1024)
 		object_ix_hashsz = 1024;
 	object_ix = xrealloc(object_ix, sizeof(int) * object_ix_hashsz);
-	object_ix = memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
+	memset(object_ix, 0, sizeof(int) * object_ix_hashsz);
 	for (i = 0, oe = objects; i < nr_objects; i++, oe++) {
 		int ix = locate_object_entry_hash(oe->sha1);
 		if (0 <= ix)
@@ -505,21 +505,6 @@
 	 * but close enough.
 	 */
 	hash = (name_hash<<DIRBITS) | (hash & ((1U<<DIRBITS )-1));
-
-	if (0) { /* debug */
-		n = name + strlen(name);
-		if (n != name && n[-1] == '\n')
-			n--;
-		while (name <= --n)
-			fputc(*n, stderr);
-		for (p = path; p; p = p->up) {
-			fputc('/', stderr);
-			n = p->elem + p->len;
-			while (p->elem <= --n)
-				fputc(*n, stderr);
-		}
-		fprintf(stderr, "\t%08x\n", hash);
-	}
 	return hash;
 }
 
@@ -587,56 +572,254 @@
 	return status;
 }
 
-static void add_pbase_tree(struct tree_desc *tree, struct name_path *up)
+struct pbase_tree_cache {
+	unsigned char sha1[20];
+	int ref;
+	int temporary;
+	void *tree_data;
+	unsigned long tree_size;
+};
+
+static struct pbase_tree_cache *(pbase_tree_cache[256]);
+static int pbase_tree_cache_ix(const unsigned char *sha1)
+{
+	return sha1[0] % ARRAY_SIZE(pbase_tree_cache);
+}
+static int pbase_tree_cache_ix_incr(int ix)
+{
+	return (ix+1) % ARRAY_SIZE(pbase_tree_cache);
+}
+
+static struct pbase_tree {
+	struct pbase_tree *next;
+	/* This is a phony "cache" entry; we are not
+	 * going to evict it nor find it through _get()
+	 * mechanism -- this is for the toplevel node that
+	 * would almost always change with any commit.
+	 */
+	struct pbase_tree_cache pcache;
+} *pbase_tree;
+
+static struct pbase_tree_cache *pbase_tree_get(const unsigned char *sha1)
+{
+	struct pbase_tree_cache *ent, *nent;
+	void *data;
+	unsigned long size;
+	char type[20];
+	int neigh;
+	int my_ix = pbase_tree_cache_ix(sha1);
+	int available_ix = -1;
+
+	/* pbase-tree-cache acts as a limited hashtable.
+	 * your object will be found at your index or within a few
+	 * slots after that slot if it is cached.
+	 */
+	for (neigh = 0; neigh < 8; neigh++) {
+		ent = pbase_tree_cache[my_ix];
+		if (ent && !memcmp(ent->sha1, sha1, 20)) {
+			ent->ref++;
+			return ent;
+		}
+		else if (((available_ix < 0) && (!ent || !ent->ref)) ||
+			 ((0 <= available_ix) &&
+			  (!ent && pbase_tree_cache[available_ix])))
+			available_ix = my_ix;
+		if (!ent)
+			break;
+		my_ix = pbase_tree_cache_ix_incr(my_ix);
+	}
+
+	/* Did not find one.  Either we got a bogus request or
+	 * we need to read and perhaps cache.
+	 */
+	data = read_sha1_file(sha1, type, &size);
+	if (!data)
+		return NULL;
+	if (strcmp(type, tree_type)) {
+		free(data);
+		return NULL;
+	}
+
+	/* We need to either cache or return a throwaway copy */
+
+	if (available_ix < 0)
+		ent = NULL;
+	else {
+		ent = pbase_tree_cache[available_ix];
+		my_ix = available_ix;
+	}
+
+	if (!ent) {
+		nent = xmalloc(sizeof(*nent));
+		nent->temporary = (available_ix < 0);
+	}
+	else {
+		/* evict and reuse */
+		free(ent->tree_data);
+		nent = ent;
+	}
+	memcpy(nent->sha1, sha1, 20);
+	nent->tree_data = data;
+	nent->tree_size = size;
+	nent->ref = 1;
+	if (!nent->temporary)
+		pbase_tree_cache[my_ix] = nent;
+	return nent;
+}
+
+static void pbase_tree_put(struct pbase_tree_cache *cache)
+{
+	if (!cache->temporary) {
+		cache->ref--;
+		return;
+	}
+	free(cache->tree_data);
+	free(cache);
+}
+
+static int name_cmp_len(const char *name)
+{
+	int i;
+	for (i = 0; name[i] && name[i] != '\n' && name[i] != '/'; i++)
+		;
+	return i;
+}
+
+static void add_pbase_object(struct tree_desc *tree,
+			     struct name_path *up,
+			     const char *name,
+			     int cmplen)
 {
 	while (tree->size) {
 		const unsigned char *sha1;
-		const char *name;
-		unsigned mode, hash;
+		const char *entry_name;
+		int entry_len;
+		unsigned mode;
 		unsigned long size;
 		char type[20];
 
-		sha1 = tree_entry_extract(tree, &name, &mode);
+		sha1 = tree_entry_extract(tree, &entry_name, &mode);
 		update_tree_entry(tree);
-		if (!has_sha1_file(sha1))
+		entry_len = strlen(entry_name);
+		if (entry_len != cmplen ||
+		    memcmp(entry_name, name, cmplen) ||
+		    !has_sha1_file(sha1) ||
+		    sha1_object_info(sha1, type, &size))
 			continue;
-		if (sha1_object_info(sha1, type, &size))
-			continue;
-
-		hash = name_hash(up, name);
-		if (!add_object_entry(sha1, hash, 1))
-			continue;
-
+		if (name[cmplen] != '/') {
+			unsigned hash = name_hash(up, name);
+			add_object_entry(sha1, hash, 1);
+			return;
+		}
 		if (!strcmp(type, tree_type)) {
 			struct tree_desc sub;
-			void *elem;
 			struct name_path me;
+			struct pbase_tree_cache *tree;
+			const char *down = name+cmplen+1;
+			int downlen = name_cmp_len(down);
 
-			elem = read_sha1_file(sha1, type, &sub.size);
-			sub.buf = elem;
-			if (sub.buf) {
-				me.up = up;
-				me.elem = name;
-				me.len = strlen(name);
-				add_pbase_tree(&sub, &me);
-				free(elem);
-			}
+			tree = pbase_tree_get(sha1);
+			if (!tree)
+				return;
+			sub.buf = tree->tree_data;
+			sub.size = tree->tree_size;
+
+			me.up = up;
+			me.elem = entry_name;
+			me.len = entry_len;
+			add_pbase_object(&sub, &me, down, downlen);
+			pbase_tree_put(tree);
+		}
+	}
+}
+
+static unsigned *done_pbase_paths;
+static int done_pbase_paths_num;
+static int done_pbase_paths_alloc;
+static int done_pbase_path_pos(unsigned hash)
+{
+	int lo = 0;
+	int hi = done_pbase_paths_num;
+	while (lo < hi) {
+		int mi = (hi + lo) / 2;
+		if (done_pbase_paths[mi] == hash)
+			return mi;
+		if (done_pbase_paths[mi] < hash)
+			hi = mi;
+		else
+			lo = mi + 1;
+	}
+	return -lo-1;
+}
+
+static int check_pbase_path(unsigned hash)
+{
+	int pos = (!done_pbase_paths) ? -1 : done_pbase_path_pos(hash);
+	if (0 <= pos)
+		return 1;
+	pos = -pos - 1;
+	if (done_pbase_paths_alloc <= done_pbase_paths_num) {
+		done_pbase_paths_alloc = alloc_nr(done_pbase_paths_alloc);
+		done_pbase_paths = xrealloc(done_pbase_paths,
+					    done_pbase_paths_alloc *
+					    sizeof(unsigned));
+	}
+	done_pbase_paths_num++;
+	if (pos < done_pbase_paths_num)
+		memmove(done_pbase_paths + pos + 1,
+			done_pbase_paths + pos,
+			(done_pbase_paths_num - pos - 1) * sizeof(unsigned));
+	done_pbase_paths[pos] = hash;
+	return 0;
+}
+
+static void add_preferred_base_object(char *name, unsigned hash)
+{
+	struct pbase_tree *it;
+	int cmplen = name_cmp_len(name);
+
+	if (check_pbase_path(hash))
+		return;
+
+	for (it = pbase_tree; it; it = it->next) {
+		if (cmplen == 0) {
+			hash = name_hash(NULL, "");
+			add_object_entry(it->pcache.sha1, hash, 1);
+		}
+		else {
+			struct tree_desc tree;
+			tree.buf = it->pcache.tree_data;
+			tree.size = it->pcache.tree_size;
+			add_pbase_object(&tree, NULL, name, cmplen);
 		}
 	}
 }
 
 static void add_preferred_base(unsigned char *sha1)
 {
-	struct tree_desc tree;
-	void *elem;
+	struct pbase_tree *it;
+	void *data;
+	unsigned long size;
+	unsigned char tree_sha1[20];
 
-	elem = read_object_with_reference(sha1, tree_type, &tree.size, NULL);
-	tree.buf = elem;
-	if (!tree.buf)
+	data = read_object_with_reference(sha1, tree_type, &size, tree_sha1);
+	if (!data)
 		return;
-	if (add_object_entry(sha1, name_hash(NULL, ""), 1))
-		add_pbase_tree(&tree, NULL);
-	free(elem);
+
+	for (it = pbase_tree; it; it = it->next) {
+		if (!memcmp(it->pcache.sha1, tree_sha1, 20)) {
+			free(data);
+			return;
+		}
+	}
+
+	it = xcalloc(1, sizeof(*it));
+	it->next = pbase_tree;
+	pbase_tree = it;
+
+	memcpy(it->pcache.sha1, tree_sha1, 20);
+	it->pcache.tree_data = data;
+	it->pcache.tree_size = size;
 }
 
 static void check_object(struct object_entry *entry)
@@ -1051,6 +1234,7 @@
 	char line[PATH_MAX + 20];
 	int window = 10, depth = 10, pack_to_stdout = 0;
 	struct object_entry **list;
+	int num_preferred_base = 0;
 	int i;
 
 	setup_git_directory();
@@ -1116,6 +1300,7 @@
 
 	for (;;) {
 		unsigned char sha1[20];
+		unsigned hash;
 
 		if (!fgets(line, sizeof(line), stdin)) {
 			if (feof(stdin))
@@ -1132,12 +1317,15 @@
 			if (get_sha1_hex(line+1, sha1))
 				die("expected edge sha1, got garbage:\n %s",
 				    line+1);
-			add_preferred_base(sha1);
+			if (num_preferred_base++ < window)
+				add_preferred_base(sha1);
 			continue;
 		}
 		if (get_sha1_hex(line, sha1))
 			die("expected sha1, got garbage:\n %s", line);
-		add_object_entry(sha1, name_hash(NULL, line+41), 0);
+		hash = name_hash(NULL, line+41);
+		add_preferred_base_object(line+41, hash);
+		add_object_entry(sha1, hash, 0);
 	}
 	if (progress)
 		fprintf(stderr, "Done counting %d objects.\n", nr_objects);
diff --git a/patch-delta.c b/patch-delta.c
index c0e1311..d95f0d9 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -28,12 +28,12 @@
 	top = delta_buf + delta_size;
 
 	/* make sure the orig file size matches what we expect */
-	size = get_delta_hdr_size(&data);
+	size = get_delta_hdr_size(&data, top);
 	if (size != src_size)
 		return NULL;
 
 	/* now the result size */
-	size = get_delta_hdr_size(&data);
+	size = get_delta_hdr_size(&data, top);
 	dst_buf = malloc(size + 1);
 	if (!dst_buf)
 		return NULL;
@@ -52,21 +52,37 @@
 			if (cmd & 0x20) cp_size |= (*data++ << 8);
 			if (cmd & 0x40) cp_size |= (*data++ << 16);
 			if (cp_size == 0) cp_size = 0x10000;
+			if (cp_off + cp_size < cp_size ||
+			    cp_off + cp_size > src_size ||
+			    cp_size > size)
+				goto bad;
 			memcpy(out, src_buf + cp_off, cp_size);
 			out += cp_size;
-		} else {
+			size -= cp_size;
+		} else if (cmd) {
+			if (cmd > size)
+				goto bad;
 			memcpy(out, data, cmd);
 			out += cmd;
 			data += cmd;
+			size -= cmd;
+		} else {
+			/*
+			 * cmd == 0 is reserved for future encoding
+			 * extensions. In the mean time we must fail when
+			 * encountering them (might be data corruption).
+			 */
+			goto bad;
 		}
 	}
 
 	/* sanity check */
-	if (data != top || out - dst_buf != size) {
+	if (data != top || size != 0) {
+		bad:
 		free(dst_buf);
 		return NULL;
 	}
 
-	*dst_size = size;
+	*dst_size = out - dst_buf;
 	return dst_buf;
 }
diff --git a/rev-list.c b/rev-list.c
index 22141e2..1301502 100644
--- a/rev-list.c
+++ b/rev-list.c
@@ -30,6 +30,7 @@
 "    --unpacked\n"
 "    --header | --pretty\n"
 "    --abbrev=nr | --no-abbrev\n"
+"    --abbrev-commit\n"
 "  special purpose:\n"
 "    --bisect"
 ;
@@ -39,6 +40,7 @@
 static int bisect_list = 0;
 static int verbose_header = 0;
 static int abbrev = DEFAULT_ABBREV;
+static int abbrev_commit = 0;
 static int show_timestamp = 0;
 static int hdr_termination = 0;
 static const char *commit_prefix = "";
@@ -52,7 +54,10 @@
 		fputs(commit_prefix, stdout);
 	if (commit->object.flags & BOUNDARY)
 		putchar('-');
-	fputs(sha1_to_hex(commit->object.sha1), stdout);
+	if (abbrev_commit && abbrev)
+		fputs(find_unique_abbrev(commit->object.sha1, abbrev), stdout);
+	else
+		fputs(sha1_to_hex(commit->object.sha1), stdout);
 	if (revs.parents) {
 		struct commit_list *parents = commit->parents;
 		while (parents) {
@@ -319,6 +324,14 @@
 			abbrev = 0;
 			continue;
 		}
+		if (!strcmp(arg, "--abbrev")) {
+			abbrev = DEFAULT_ABBREV;
+			continue;
+		}
+		if (!strcmp(arg, "--abbrev-commit")) {
+			abbrev_commit = 1;
+			continue;
+		}
 		if (!strncmp(arg, "--abbrev=", 9)) {
 			abbrev = strtoul(arg + 9, NULL, 10);
 			if (abbrev && abbrev < MINIMUM_ABBREV)
diff --git a/sha1_file.c b/sha1_file.c
index ba8c4f7..e3d0113 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -808,10 +808,12 @@
 		 * the result size.
 		 */
 		data = delta_head;
-		get_delta_hdr_size(&data); /* ignore base size */
+
+		/* ignore base size */
+		get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 
 		/* Read the result size */
-		result_size = get_delta_hdr_size(&data);
+		result_size = get_delta_hdr_size(&data, delta_head+sizeof(delta_head));
 		*sizep = result_size;
 	}
 	return 0;
diff --git a/xdiff-interface.c b/xdiff-interface.c
new file mode 100644
index 0000000..6a82da7
--- /dev/null
+++ b/xdiff-interface.c
@@ -0,0 +1,104 @@
+#include "cache.h"
+#include "xdiff-interface.h"
+
+static int parse_num(char **cp_p, int *num_p)
+{
+	char *cp = *cp_p;
+	int num = 0;
+	int read_some;
+
+	while ('0' <= *cp && *cp <= '9')
+		num = num * 10 + *cp++ - '0';
+	if (!(read_some = cp - *cp_p))
+		return -1;
+	*cp_p = cp;
+	*num_p = num;
+	return 0;
+}
+
+int parse_hunk_header(char *line, int len,
+		      int *ob, int *on,
+		      int *nb, int *nn)
+{
+	char *cp;
+	cp = line + 4;
+	if (parse_num(&cp, ob)) {
+	bad_line:
+		return error("malformed diff output: %s", line);
+	}
+	if (*cp == ',') {
+		cp++;
+		if (parse_num(&cp, on))
+			goto bad_line;
+	}
+	else
+		*on = 1;
+	if (*cp++ != ' ' || *cp++ != '+')
+		goto bad_line;
+	if (parse_num(&cp, nb))
+		goto bad_line;
+	if (*cp == ',') {
+		cp++;
+		if (parse_num(&cp, nn))
+			goto bad_line;
+	}
+	else
+		*nn = 1;
+	return -!!memcmp(cp, " @@", 3);
+}
+
+static void consume_one(void *priv_, char *s, unsigned long size)
+{
+	struct xdiff_emit_state *priv = priv_;
+	char *ep;
+	while (size) {
+		unsigned long this_size;
+		ep = memchr(s, '\n', size);
+		this_size = (ep == NULL) ? size : (ep - s + 1);
+		priv->consume(priv, s, this_size);
+		size -= this_size;
+		s += this_size;
+	}
+}
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	struct xdiff_emit_state *priv = priv_;
+	int i;
+
+	for (i = 0; i < nbuf; i++) {
+		if (mb[i].ptr[mb[i].size-1] != '\n') {
+			/* Incomplete line */
+			priv->remainder = realloc(priv->remainder,
+						  priv->remainder_size +
+						  mb[i].size);
+			memcpy(priv->remainder + priv->remainder_size,
+			       mb[i].ptr, mb[i].size);
+			priv->remainder_size += mb[i].size;
+			continue;
+		}
+
+		/* we have a complete line */
+		if (!priv->remainder) {
+			consume_one(priv, mb[i].ptr, mb[i].size);
+			continue;
+		}
+		priv->remainder = realloc(priv->remainder,
+					  priv->remainder_size +
+					  mb[i].size);
+		memcpy(priv->remainder + priv->remainder_size,
+		       mb[i].ptr, mb[i].size);
+		consume_one(priv, priv->remainder,
+			    priv->remainder_size + mb[i].size);
+		free(priv->remainder);
+		priv->remainder = NULL;
+		priv->remainder_size = 0;
+	}
+	if (priv->remainder) {
+		consume_one(priv, priv->remainder, priv->remainder_size);
+		free(priv->remainder);
+		priv->remainder = NULL;
+		priv->remainder_size = 0;
+	}
+	return 0;
+}
diff --git a/xdiff-interface.h b/xdiff-interface.h
new file mode 100644
index 0000000..1346908
--- /dev/null
+++ b/xdiff-interface.h
@@ -0,0 +1,21 @@
+#ifndef XDIFF_INTERFACE_H
+#define XDIFF_INTERFACE_H
+
+#include "xdiff/xdiff.h"
+
+struct xdiff_emit_state;
+
+typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
+
+struct xdiff_emit_state {
+	xdiff_emit_consume_fn consume;
+	char *remainder;
+	unsigned long remainder_size;
+};
+
+int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int parse_hunk_header(char *line, int len,
+		      int *ob, int *on,
+		      int *nb, int *nn);
+
+#endif