Merge branch 'sp/reflog'

* sp/reflog:
  Record rebase changes as 'rebase' in the reflog.
  Log ref changes made by resolve.
  Log ref changes made by quiltimport.
  Log ref changes made by git-merge and git-pull.
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 2b0efe7..6dbe45b 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -46,6 +46,7 @@
 
 html: $(DOC_HTML)
 
+$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN7): asciidoc.conf
 
 man: man1 man7
 man1: $(DOC_MAN1)
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 7ce7151..8196d78 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -9,6 +9,8 @@
 
 [attributes]
 caret=^
+startsb=[
+endsb=]
 
 ifdef::backend-docbook[]
 [gitlink-inlinemacro]
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 4d8a2ad..8520b97 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -207,7 +207,7 @@
 An example:
 
 --------------------------------------------------------------
-    $ cat .git/ignore
+    $ cat .git/info/exclude
     # ignore objects and archives, anywhere in the tree.
     *.[oa]
     $ cat Documentation/.gitignore
@@ -217,7 +217,7 @@
     !foo.html
     $ git-ls-files --ignored \
         --exclude='Documentation/*.[0-9]' \
-        --exclude-from=.git/ignore \
+        --exclude-from=.git/info/exclude \
         --exclude-per-directory=.gitignore
 --------------------------------------------------------------
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index d00cc3e..ce30581 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -615,6 +615,13 @@
 	gitlink:git-diff-files[1];
 	gitlink:git-diff-tree[1]
 
+other
+~~~~~
+'GIT_TRACE'::
+	If this variable is set git will print `trace:` messages on
+	stderr telling about alias expansion, built-in command
+	execution and external command execution.
+
 Discussion[[Discussion]]
 ------------------------
 include::README[]
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index b52dfdc..275d18b 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -120,9 +120,11 @@
 
 info/exclude::
 	This file, by convention among Porcelains, stores the
-	exclude pattern list.  `git status` looks at it, but
-	otherwise it is not looked at by any of the core git
-	commands.
+	exclude pattern list. `.gitignore` is the per-directory
+	ignore file.  `git status`, `git add`, `git rm` and `git
+	clean` look at it but the core git commands do not look
+	at it.  See also: gitlink:git-ls-files[1] `--exclude-from`
+	and `--exclude-per-directory`.
 
 remotes::
 	Stores shorthands to be used to give URL and default
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 9abec80..26ecba5 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -10,20 +10,21 @@
 - https://host.xz/path/to/repo.git/
 - git://host.xz/path/to/repo.git/
 - git://host.xz/~user/path/to/repo.git/
-- ssh://[user@]host.xz/path/to/repo.git/
-- ssh://[user@]host.xz/~user/path/to/repo.git/
-- ssh://[user@]host.xz/~/path/to/repo.git
+- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
 ===============================================================
 
-SSH Is the default transport protocol and also supports an
-scp-like syntax.  Both syntaxes support username expansion,
+SSH is the default transport protocol.  You can optionally specify
+which user to log-in as, and an alternate, scp-like syntax is also
+supported.  Both syntaxes support username expansion,
 as does the native git protocol. The following three are
 identical to the last three above, respectively:
 
 ===============================================================
-- host.xz:/path/to/repo.git/
-- host.xz:~user/path/to/repo.git/
-- host.xz:path/to/repo.git
+- {startsb}user@{endsb}host.xz:/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:~user/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:path/to/repo.git
 ===============================================================
 
 To sync with a local directory, use:
diff --git a/Makefile b/Makefile
index e75fb13..01fb9cf 100644
--- a/Makefile
+++ b/Makefile
@@ -124,7 +124,7 @@
 	git-fetch.sh \
 	git-ls-remote.sh \
 	git-merge-one-file.sh git-parse-remote.sh \
-	git-prune.sh git-pull.sh git-rebase.sh \
+	git-pull.sh git-rebase.sh \
 	git-repack.sh git-request-pull.sh git-reset.sh \
 	git-resolve.sh git-revert.sh git-sh-setup.sh \
 	git-tag.sh git-verify-tag.sh \
@@ -178,7 +178,7 @@
 	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 \
-	git-fmt-merge-msg$X
+	git-fmt-merge-msg$X git-prune$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -222,7 +222,7 @@
 	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 xdiff-interface.o \
-	alloc.o $(DIFF_OBJS)
+	alloc.o merge-file.o $(DIFF_OBJS)
 
 BUILTIN_OBJS = \
 	builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
@@ -234,7 +234,7 @@
 	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-mailsplit.o builtin-stripspace.o \
-	builtin-update-ref.o builtin-fmt-merge-msg.o
+	builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
diff --git a/blob.c b/blob.c
index 496f270..d1af2e6 100644
--- a/blob.c
+++ b/blob.c
@@ -10,12 +10,12 @@
 	if (!obj) {
 		struct blob *ret = alloc_blob_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_BLOB;
+		ret->object.type = OBJ_BLOB;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_BLOB;
-	if (obj->type != TYPE_BLOB) {
+		obj->type = OBJ_BLOB;
+	if (obj->type != OBJ_BLOB) {
 		error("Object %s is a %s, not a blob",
 		      sha1_to_hex(sha1), typename(obj->type));
 		return NULL;
diff --git a/builtin-diff.c b/builtin-diff.c
index ae901dd..cb38f44 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -285,9 +285,9 @@
 		obj = deref_tag(obj, NULL, 0);
 		if (!obj)
 			die("invalid object '%s' given.", name);
-		if (obj->type == TYPE_COMMIT)
+		if (obj->type == OBJ_COMMIT)
 			obj = &((struct commit *)obj)->tree->object;
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			if (ARRAY_SIZE(ent) <= ents)
 				die("more than %d trees given: '%s'",
 				    (int) ARRAY_SIZE(ent), name);
@@ -297,7 +297,7 @@
 			ents++;
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			if (2 <= blobs)
 				die("more than two blobs given: '%s'", name);
 			memcpy(blob[blobs].sha1, obj->sha1, 20);
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
index 6527482..f20b27b 100644
--- a/builtin-fmt-merge-msg.c
+++ b/builtin-fmt-merge-msg.c
@@ -76,6 +76,7 @@
 	unsigned char *sha1;
 	char *src, *origin;
 	struct src_data *src_data;
+	int pulling_head = 0;
 
 	if (len < 43 || line[40] != '\t')
 		return 1;
@@ -101,8 +102,11 @@
 	if (src) {
 		*src = 0;
 		src += 4;
-	} else
-		src = "HEAD";
+		pulling_head = 0;
+	} else {
+		src = line;
+		pulling_head = 1;
+	}
 
 	i = find_in_list(&srcs, src);
 	if (i < 0) {
@@ -112,7 +116,10 @@
 	}
 	src_data = srcs.payload[i];
 
-	if (!strncmp(line, "branch ", 7)) {
+	if (pulling_head) {
+		origin = strdup(src);
+		src_data->head_status |= 1;
+	} else if (!strncmp(line, "branch ", 7)) {
 		origin = strdup(line + 7);
 		append_to_list(&src_data->branch, origin, NULL);
 		src_data->head_status |= 2;
@@ -124,9 +131,6 @@
 		origin = strdup(line + 14);
 		append_to_list(&src_data->r_branch, origin, NULL);
 		src_data->head_status |= 2;
-	} else if (!strcmp(line, "HEAD")) {
-		origin = strdup(src);
-		src_data->head_status |= 1;
 	} else {
 		origin = strdup(src);
 		append_to_list(&src_data->generic, strdup(line), NULL);
@@ -177,7 +181,7 @@
 	int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED;
 
 	branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
-	if (!branch || branch->type != TYPE_COMMIT)
+	if (!branch || branch->type != OBJ_COMMIT)
 		return;
 
 	setup_revisions(0, NULL, rev, NULL);
diff --git a/builtin-grep.c b/builtin-grep.c
index 4c2f7df..a79bac3 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -891,9 +891,9 @@
 static int grep_object(struct grep_opt *opt, const char **paths,
 		       struct object *obj, const char *name)
 {
-	if (obj->type == TYPE_BLOB)
+	if (obj->type == OBJ_BLOB)
 		return grep_sha1(opt, obj->sha1, name);
-	if (obj->type == TYPE_COMMIT || obj->type == TYPE_TREE) {
+	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
 		struct tree_desc tree;
 		void *data;
 		int hit;
diff --git a/builtin-prune.c b/builtin-prune.c
new file mode 100644
index 0000000..d196c41
--- /dev/null
+++ b/builtin-prune.c
@@ -0,0 +1,259 @@
+#include "cache.h"
+#include "refs.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "builtin.h"
+#include "cache-tree.h"
+
+static const char prune_usage[] = "git prune [-n]";
+static int show_only = 0;
+static struct rev_info revs;
+
+static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+{
+	if (show_only) {
+		printf("would prune %s/%s\n", path, filename);
+		return 0;
+	}
+	unlink(mkpath("%s/%s", path, filename));
+	rmdir(path);
+	return 0;
+}
+
+static int prune_dir(int i, char *path)
+{
+	DIR *dir = opendir(path);
+	struct dirent *de;
+
+	if (!dir)
+		return 0;
+
+	while ((de = readdir(dir)) != NULL) {
+		char name[100];
+		unsigned char sha1[20];
+		int len = strlen(de->d_name);
+
+		switch (len) {
+		case 2:
+			if (de->d_name[1] != '.')
+				break;
+		case 1:
+			if (de->d_name[0] != '.')
+				break;
+			continue;
+		case 38:
+			sprintf(name, "%02x", i);
+			memcpy(name+2, de->d_name, len+1);
+			if (get_sha1_hex(name, sha1) < 0)
+				break;
+
+			/*
+			 * Do we know about this object?
+			 * It must have been reachable
+			 */
+			if (lookup_object(sha1))
+				continue;
+
+			prune_object(path, de->d_name, sha1);
+			continue;
+		}
+		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+	}
+	closedir(dir);
+	return 0;
+}
+
+static void prune_object_dir(const char *path)
+{
+	int i;
+	for (i = 0; i < 256; i++) {
+		static char dir[4096];
+		sprintf(dir, "%s/%02x", path, i);
+		prune_dir(i, dir);
+	}
+}
+
+static void process_blob(struct blob *blob,
+			 struct object_array *p,
+			 struct name_path *path,
+			 const char *name)
+{
+	struct object *obj = &blob->object;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+	/* Nothing to do, really .. The blob lookup was the important part */
+}
+
+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;
+	struct name_entry entry;
+	struct name_path me;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+	if (parse_tree(tree) < 0)
+		die("bad tree object %s", sha1_to_hex(obj->sha1));
+	name = strdup(name);
+	add_object(obj, p, path, name);
+	me.up = path;
+	me.elem = name;
+	me.elem_len = strlen(name);
+
+	desc.buf = tree->buffer;
+	desc.size = tree->size;
+
+	while (tree_entry(&desc, &entry)) {
+		if (S_ISDIR(entry.mode))
+			process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+		else
+			process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+	}
+	free(tree->buffer);
+	tree->buffer = NULL;
+}
+
+static void process_tag(struct tag *tag, struct object_array *p, const char *name)
+{
+	struct object *obj = &tag->object;
+	struct name_path me;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+
+	me.up = NULL;
+	me.elem = "tag:/";
+	me.elem_len = 5;
+
+	if (parse_tag(tag) < 0)
+		die("bad tag object %s", sha1_to_hex(obj->sha1));
+	add_object(tag->tagged, p, NULL, name);
+}
+
+static void walk_commit_list(struct rev_info *revs)
+{
+	int i;
+	struct commit *commit;
+	struct object_array objects = { 0, 0, NULL };
+
+	/* Walk all commits, process their trees */
+	while ((commit = get_revision(revs)) != NULL)
+		process_tree(commit->tree, &objects, NULL, "");
+
+	/* Then walk all the pending objects, recursively processing them too */
+	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->type == OBJ_TAG) {
+			process_tag((struct tag *) obj, &objects, name);
+			continue;
+		}
+		if (obj->type == OBJ_TREE) {
+			process_tree((struct tree *)obj, &objects, NULL, name);
+			continue;
+		}
+		if (obj->type == OBJ_BLOB) {
+			process_blob((struct blob *)obj, &objects, NULL, name);
+			continue;
+		}
+		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+	}
+}
+
+static int add_one_ref(const char *path, const unsigned char *sha1)
+{
+	struct object *object = parse_object(sha1);
+	if (!object)
+		die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
+	add_pending_object(&revs, object, "");
+	return 0;
+}
+
+static void add_one_tree(const unsigned char *sha1)
+{
+	struct tree *tree = lookup_tree(sha1);
+	add_pending_object(&revs, &tree->object, "");
+}
+
+static void add_cache_tree(struct cache_tree *it)
+{
+	int i;
+
+	if (it->entry_count >= 0)
+		add_one_tree(it->sha1);
+	for (i = 0; i < it->subtree_nr; i++)
+		add_cache_tree(it->down[i]->cache_tree);
+}
+
+static void add_cache_refs(void)
+{
+	int i;
+
+	read_cache();
+	for (i = 0; i < active_nr; i++) {
+		lookup_blob(active_cache[i]->sha1);
+		/*
+		 * We could add the blobs to the pending list, but quite
+		 * frankly, we don't care. Once we've looked them up, and
+		 * added them as objects, we've really done everything
+		 * there is to do for a blob
+		 */
+	}
+	if (active_cache_tree)
+		add_cache_tree(active_cache_tree);
+}
+
+int cmd_prune(int argc, const char **argv, char **envp)
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "-n")) {
+			show_only = 1;
+			continue;
+		}
+		usage(prune_usage);
+	}
+
+	/*
+	 * Set up revision parsing, and mark us as being interested
+	 * in all object types, not just commits.
+	 */
+	init_revisions(&revs);
+	revs.tag_objects = 1;
+	revs.blob_objects = 1;
+	revs.tree_objects = 1;
+
+	/* Add all external refs */
+	for_each_ref(add_one_ref);
+
+	/* Add all refs from the index file */
+	add_cache_refs();
+
+	/*
+	 * Set up the revision walk - this will move all commits
+	 * from the pending list to the commit walking list.
+	 */
+	prepare_revision_walk(&revs);
+
+	walk_commit_list(&revs);
+
+	prune_object_dir(get_object_directory());
+
+	return 0;
+}
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 63bad0e..8f32871 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -167,16 +167,16 @@
 		const char *name = pending->name;
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
-		if (obj->type == TYPE_TAG) {
+		if (obj->type == OBJ_TAG) {
 			obj->flags |= SEEN;
 			add_object_array(obj, name, &objects);
 			continue;
 		}
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			process_tree((struct tree *)obj, &objects, NULL, name);
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			process_blob((struct blob *)obj, &objects, NULL, name);
 			continue;
 		}
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 260cb22..3d240ca 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -172,7 +172,7 @@
 static int mark_seen(struct commit *commit, struct commit_list **seen_p)
 {
 	if (!commit->object.flags) {
-		insert_by_date(commit, seen_p);
+		commit_list_insert(commit, seen_p);
 		return 1;
 	}
 	return 0;
@@ -218,9 +218,8 @@
 	 * Postprocess to complete well-poisoning.
 	 *
 	 * At this point we have all the commits we have seen in
-	 * seen_p list (which happens to be sorted chronologically but
-	 * it does not really matter).  Mark anything that can be
-	 * reached from uninteresting commits not interesting.
+	 * seen_p list.  Mark anything that can be reached from
+	 * uninteresting commits not interesting.
 	 */
 	for (;;) {
 		int changed = 0;
@@ -701,6 +700,8 @@
 	if (0 <= extra)
 		join_revs(&list, &seen, num_rev, extra);
 
+	sort_by_date(&seen);
+
 	if (merge_base)
 		return show_merge_base(seen, num_rev);
 
diff --git a/builtin.h b/builtin.h
index d9e5483..5339d86 100644
--- a/builtin.h
+++ b/builtin.h
@@ -25,6 +25,8 @@
 extern int cmd_format_patch(int argc, const char **argv, char **envp);
 extern int cmd_count_objects(int argc, const char **argv, char **envp);
 
+extern int cmd_prune(int argc, const char **argv, char **envp);
+
 extern int cmd_push(int argc, const char **argv, char **envp);
 extern int cmd_grep(int argc, const char **argv, char **envp);
 extern int cmd_rm(int argc, const char **argv, char **envp);
diff --git a/commit.c b/commit.c
index c6bf10d..77f0ca1 100644
--- a/commit.c
+++ b/commit.c
@@ -56,7 +56,7 @@
 				   const unsigned char *sha1,
 				   int quiet)
 {
-	if (obj->type != TYPE_COMMIT) {
+	if (obj->type != OBJ_COMMIT) {
 		if (!quiet)
 			error("Object %s is a %s, not a commit",
 			      sha1_to_hex(sha1), typename(obj->type));
@@ -86,11 +86,11 @@
 	if (!obj) {
 		struct commit *ret = alloc_commit_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_COMMIT;
+		ret->object.type = OBJ_COMMIT;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_COMMIT;
+		obj->type = OBJ_COMMIT;
 	return check_commit(obj, sha1, 0);
 }
 
@@ -655,6 +655,9 @@
 			continue;
 		}
 
+		if (!subject)
+			body = 1;
+
 		if (is_empty_line(line, &linelen)) {
 			if (!body)
 				continue;
@@ -662,8 +665,6 @@
 				continue;
 			if (fmt == CMIT_FMT_SHORT)
 				break;
-		} else {
-			body = 1;
 		}
 
 		if (subject) {
@@ -702,6 +703,12 @@
 	/* Make sure there is an EOLN for the non-oneline case */
 	if (fmt != CMIT_FMT_ONELINE)
 		buf[offset++] = '\n';
+	/*
+	 * make sure there is another EOLN to separate the headers from whatever
+	 * body the caller appends if we haven't already written a body
+	 */
+	if (fmt == CMIT_FMT_EMAIL && !body)
+		buf[offset++] = '\n';
 	buf[offset] = '\0';
 	return offset;
 }
@@ -854,6 +861,7 @@
 #define PARENT1		(1u<< 8)
 #define PARENT2		(1u<< 9)
 #define STALE		(1u<<10)
+#define RESULT		(1u<<11)
 
 static struct commit *interesting(struct commit_list *list)
 {
@@ -867,183 +875,42 @@
 	return NULL;
 }
 
-/*
- * A pathological example of how this thing works.
- *
- * Suppose we had this commit graph, where chronologically
- * the timestamp on the commit are A <= B <= C <= D <= E <= F
- * and we are trying to figure out the merge base for E and F
- * commits.
- *
- *                  F
- *                 / \
- *            E   A   D
- *             \ /   /
- *              B   /
- *               \ /
- *                C
- *
- * First we push E and F to list to be processed.  E gets bit 1
- * and F gets bit 2.  The list becomes:
- *
- *     list=F(2) E(1), result=empty
- *
- * Then we pop F, the newest commit, from the list.  Its flag is 2.
- * We scan its parents, mark them reachable from the side that F is
- * reachable from, and push them to the list:
- *
- *     list=E(1) D(2) A(2), result=empty
- *
- * Next pop E and do the same.
- *
- *     list=D(2) B(1) A(2), result=empty
- *
- * Next pop D and do the same.
- *
- *     list=C(2) B(1) A(2), result=empty
- *
- * Next pop C and do the same.
- *
- *     list=B(1) A(2), result=empty
- *
- * Now it is B's turn.  We mark its parent, C, reachable from B's side,
- * and push it to the list:
- *
- *     list=C(3) A(2), result=empty
- *
- * Now pop C and notice it has flags==3.  It is placed on the result list,
- * and the list now contains:
- *
- *     list=A(2), result=C(3)
- *
- * We pop A and do the same.
- *
- *     list=B(3), result=C(3)
- *
- * Next, we pop B and something very interesting happens.  It has flags==3
- * so it is also placed on the result list, and its parents are marked
- * stale, retroactively, and placed back on the list:
- *
- *    list=C(7), result=C(7) B(3)
- *
- * Now, list does not have any interesting commit.  So we find the newest
- * commit from the result list that is not marked stale.  Which is
- * commit B.
- *
- *
- * Another pathological example how this thing used to fail to mark an
- * ancestor of a merge base as STALE before we introduced the
- * postprocessing phase (mark_reachable_commits).
- *
- *		  2
- *		  H
- *	    1    / \
- *	    G   A   \
- *	    |\ /     \
- *	    | B       \
- *	    |  \       \
- *	     \  C       F
- *	      \  \     /
- *	       \  D   /
- *		\ |  /
- *		 \| /
- *		  E
- *
- *	 list			A B C D E F G H
- *	 G1 H2			- - - - - - 1 2
- *	 H2 E1 B1		- 1 - - 1 - 1 2
- *	 F2 E1 B1 A2		2 1 - - 1 2 1 2
- *	 E3 B1 A2		2 1 - - 3 2 1 2
- *	 B1 A2			2 1 - - 3 2 1 2
- *	 C1 A2			2 1 1 - 3 2 1 2
- *	 D1 A2			2 1 1 1 3 2 1 2
- *	 A2			2 1 1 1 3 2 1 2
- *	 B3			2 3 1 1 3 2 1 2
- *	 C7			2 3 7 1 3 2 1 2
- *
- * At this point, unfortunately, everybody in the list is
- * stale, so we fail to complete the following two
- * steps to fully marking stale commits.
- *
- *	 D7			2 3 7 7 3 2 1 2
- *	 E7			2 3 7 7 7 2 1 2
- *
- * and we ended up showing E as an interesting merge base.
- * The postprocessing phase re-injects C and continues traversal
- * to contaminate D and E.
- */
-
-static void mark_reachable_commits(struct commit_list *result,
-				   struct commit_list *list)
-{
-	struct commit_list *tmp;
-
-	/*
-	 * Postprocess to fully contaminate the well.
-	 */
-	for (tmp = result; tmp; tmp = tmp->next) {
-		struct commit *c = tmp->item;
-		/* Reinject stale ones to list,
-		 * so we can scan their parents.
-		 */
-		if (c->object.flags & STALE)
-			commit_list_insert(c, &list);
-	}
-	while (list) {
-		struct commit *c = list->item;
-		struct commit_list *parents;
-
-		tmp = list;
-		list = list->next;
-		free(tmp);
-
-		/* Anything taken out of the list is stale, so
-		 * mark all its parents stale.  We do not
-		 * parse new ones (we already parsed all the relevant
-		 * ones).
-		 */
-		parents = c->parents;
-		while (parents) {
-			struct commit *p = parents->item;
-			parents = parents->next;
-			if (!(p->object.flags & STALE)) {
-				p->object.flags |= STALE;
-				commit_list_insert(p, &list);
-			}
-		}
-	}
-}
-
-struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2,
-                                    int cleanup)
+static struct commit_list *merge_bases(struct commit *one, struct commit *two)
 {
 	struct commit_list *list = NULL;
 	struct commit_list *result = NULL;
-	struct commit_list *tmp = NULL;
 
-	if (rev1 == rev2)
-		return commit_list_insert(rev1, &result);
+	if (one == two)
+		/* We do not mark this even with RESULT so we do not
+		 * have to clean it up.
+		 */
+		return commit_list_insert(one, &result);
 
-	parse_commit(rev1);
-	parse_commit(rev2);
+	parse_commit(one);
+	parse_commit(two);
 
-	rev1->object.flags |= PARENT1;
-	rev2->object.flags |= PARENT2;
-	insert_by_date(rev1, &list);
-	insert_by_date(rev2, &list);
+	one->object.flags |= PARENT1;
+	two->object.flags |= PARENT2;
+	insert_by_date(one, &list);
+	insert_by_date(two, &list);
 
 	while (interesting(list)) {
-		struct commit *commit = list->item;
+		struct commit *commit;
 		struct commit_list *parents;
-		int flags = commit->object.flags
-			& (PARENT1 | PARENT2 | STALE);
+		struct commit_list *n;
+		int flags;
 
-		tmp = list;
-		list = list->next;
-		free(tmp);
+		commit = list->item;
+		n = list->next;
+		free(list);
+		list = n;
+
+		flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
 		if (flags == (PARENT1 | PARENT2)) {
-			insert_by_date(commit, &result);
-
+			if (!(commit->object.flags & RESULT)) {
+				commit->object.flags |= RESULT;
+				insert_by_date(commit, &result);
+			}
 			/* Mark parents of a found merge stale */
 			flags |= STALE;
 		}
@@ -1059,35 +926,75 @@
 		}
 	}
 
-	if (!result)
-		goto finish;
+	/* Clean up the result to remove stale ones */
+	list = result; result = NULL;
+	while (list) {
+		struct commit_list *n = list->next;
+		if (!(list->item->object.flags & STALE))
+			insert_by_date(list->item, &result);
+		free(list);
+		list = n;
+	}
+	return result;
+}
 
-	if (result->next && list)
-		mark_reachable_commits(result, list);
+struct commit_list *get_merge_bases(struct commit *one,
+				    struct commit *two,
+                                    int cleanup)
+{
+	const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+	struct commit_list *list;
+	struct commit **rslt;
+	struct commit_list *result;
+	int cnt, i, j;
 
-	/* cull duplicates */
-	for (tmp = result, list = NULL; tmp; ) {
-		struct commit *commit = tmp->item;
-		struct commit_list *next = tmp->next;
-		if (commit->object.flags & STALE) {
-			if (list != NULL)
-				list->next = next;
-			free(tmp);
-		} else {
-			if (list == NULL)
-				result = tmp;
-			list = tmp;
-			commit->object.flags |= STALE;
+	result = merge_bases(one, two);
+	if (one == two)
+		return result;
+	if (!result || !result->next) {
+		if (cleanup) {
+			clear_commit_marks(one, all_flags);
+			clear_commit_marks(two, all_flags);
 		}
-
-		tmp = next;
+		return result;
 	}
 
- finish:
-	if (cleanup) {
-		clear_commit_marks(rev1, PARENT1 | PARENT2 | STALE);
-		clear_commit_marks(rev2, PARENT1 | PARENT2 | STALE);
+	/* There are more than one */
+	cnt = 0;
+	list = result;
+	while (list) {
+		list = list->next;
+		cnt++;
+	}
+	rslt = xcalloc(cnt, sizeof(*rslt));
+	for (list = result, i = 0; list; list = list->next)
+		rslt[i++] = list->item;
+	free_commit_list(result);
+
+	clear_commit_marks(one, all_flags);
+	clear_commit_marks(two, all_flags);
+	for (i = 0; i < cnt - 1; i++) {
+		for (j = i+1; j < cnt; j++) {
+			if (!rslt[i] || !rslt[j])
+				continue;
+			result = merge_bases(rslt[i], rslt[j]);
+			clear_commit_marks(rslt[i], all_flags);
+			clear_commit_marks(rslt[j], all_flags);
+			for (list = result; list; list = list->next) {
+				if (rslt[i] == list->item)
+					rslt[i] = NULL;
+				if (rslt[j] == list->item)
+					rslt[j] = NULL;
+			}
+		}
 	}
 
+	/* Surviving ones in rslt[] are the independent results */
+	result = NULL;
+	for (i = 0; i < cnt; i++) {
+		if (rslt[i])
+			insert_by_date(rslt[i], &result);
+	}
+	free(rslt);
 	return result;
 }
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index ebd00ef..34c9950 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -59,14 +59,16 @@
 
 (defcustom git-committer-name nil
   "User name to use for commits.
-The default is to fall back to the repository config, then to `add-log-full-name' and then to `user-full-name'."
+The default is to fall back to the repository config,
+then to `add-log-full-name' and then to `user-full-name'."
   :group 'git
   :type '(choice (const :tag "Default" nil)
                  (string :tag "Name")))
 
 (defcustom git-committer-email nil
   "Email address to use for commits.
-The default is to fall back to the git repository config, then to `add-log-mailing-address' and then to `user-mail-address'."
+The default is to fall back to the git repository config,
+then to `add-log-mailing-address' and then to `user-mail-address'."
   :group 'git
   :type '(choice (const :tag "Default" nil)
                  (string :tag "Email")))
@@ -86,6 +88,7 @@
   :group 'git
   :type 'string)
 
+
 (defface git-status-face
   '((((class color) (background light)) (:foreground "purple")))
   "Git mode face used to highlight added and modified files."
@@ -149,7 +152,8 @@
     (apply #'call-process "git" nil buffer nil args)))
 
 (defun git-call-process-env-string (env &rest args)
-  "Wrapper for call-process that sets environment strings, and returns the process output as a string."
+  "Wrapper for call-process that sets environment strings,
+and returns the process output as a string."
   (with-temp-buffer
     (and (eq 0 (apply #' git-call-process-env t env args))
          (buffer-string))))
@@ -939,6 +943,8 @@
   (let ((map (make-keymap))
         (diff-map (make-sparse-keymap)))
     (suppress-keymap map)
+    (define-key map "?"   'git-help)
+    (define-key map "h"   'git-help)
     (define-key map " "   'git-next-file)
     (define-key map "a"   'git-add-file)
     (define-key map "c"   'git-commit-file)
@@ -1008,5 +1014,10 @@
         (goto-char (point-min)))
     (message "%s is not a git working tree." dir)))
 
+(defun git-help ()
+  "Display help for Git mode."
+  (interactive)
+  (describe-function 'git-status-mode))
+
 (provide 'git)
 ;;; git.el ends here
diff --git a/daemon.c b/daemon.c
index e096bd7..e4ec676 100644
--- a/daemon.c
+++ b/daemon.c
@@ -95,6 +95,12 @@
 	va_end(params);
 }
 
+static void NORETURN daemon_die(const char *err, va_list params)
+{
+	logreport(LOG_ERR, err, params);
+	exit(1);
+}
+
 static int avoid_alias(char *p)
 {
 	int sl, ndot;
@@ -656,6 +662,45 @@
 	}
 }
 
+/* if any standard file descriptor is missing open it to /dev/null */
+static void sanitize_stdfds(void)
+{
+	int fd = open("/dev/null", O_RDWR, 0);
+	while (fd != -1 && fd < 2)
+		fd = dup(fd);
+	if (fd == -1)
+		die("open /dev/null or dup failed: %s", strerror(errno));
+	if (fd > 2)
+		close(fd);
+}
+
+static void daemonize(void)
+{
+	switch (fork()) {
+		case 0:
+			break;
+		case -1:
+			die("fork failed: %s", strerror(errno));
+		default:
+			exit(0);
+	}
+	if (setsid() == -1)
+		die("setsid failed: %s", strerror(errno));
+	close(0);
+	close(1);
+	close(2);
+	sanitize_stdfds();
+}
+
+static void store_pid(const char *path)
+{
+	FILE *f = fopen(path, "w");
+	if (!f)
+		die("cannot open pid file %s: %s", path, strerror(errno));
+	fprintf(f, "%d\n", getpid());
+	fclose(f);
+}
+
 static int serve(int port)
 {
 	int socknum, *socklist;
@@ -671,6 +716,8 @@
 {
 	int port = DEFAULT_GIT_PORT;
 	int inetd_mode = 0;
+	const char *pid_file = NULL;
+	int detach = 0;
 	int i;
 
 	/* Without this we cannot rely on waitpid() to tell
@@ -735,6 +782,15 @@
 			user_path = arg + 12;
 			continue;
 		}
+		if (!strncmp(arg, "--pid-file=", 11)) {
+			pid_file = arg + 11;
+			continue;
+		}
+		if (!strcmp(arg, "--detach")) {
+			detach = 1;
+			log_syslog = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--")) {
 			ok_paths = &argv[i+1];
 			break;
@@ -746,17 +802,14 @@
 		usage(daemon_usage);
 	}
 
-	if (log_syslog)
+	if (log_syslog) {
 		openlog("git-daemon", 0, LOG_DAEMON);
-
-	if (strict_paths && (!ok_paths || !*ok_paths)) {
-		if (!inetd_mode)
-			die("git-daemon: option --strict-paths requires a whitelist");
-
-		logerror("option --strict-paths requires a whitelist");
-		exit (1);
+		set_die_routine(daemon_die);
 	}
 
+	if (strict_paths && (!ok_paths || !*ok_paths))
+		die("option --strict-paths requires a whitelist");
+
 	if (inetd_mode) {
 		struct sockaddr_storage ss;
 		struct sockaddr *peer = (struct sockaddr *)&ss;
@@ -770,5 +823,13 @@
 		return execute(peer);
 	}
 
+	if (detach)
+		daemonize();
+	else
+		sanitize_stdfds();
+
+	if (pid_file)
+		store_pid(pid_file);
+
 	return serve(port);
 }
diff --git a/describe.c b/describe.c
index 8e68d5d..324ca89 100644
--- a/describe.c
+++ b/describe.c
@@ -67,7 +67,7 @@
 	 * Otherwise only annotated tags are used.
 	 */
 	if (!strncmp(path, "refs/tags/", 10)) {
-		if (object->type == TYPE_TAG)
+		if (object->type == OBJ_TAG)
 			prio = 2;
 		else
 			prio = 1;
diff --git a/diff.c b/diff.c
index 287a927..8b44756 100644
--- a/diff.c
+++ b/diff.c
@@ -26,30 +26,14 @@
 	DIFF_FILE_NEW = 5,
 };
 
-#define COLOR_NORMAL  ""
-#define COLOR_BOLD    "\033[1m"
-#define COLOR_DIM     "\033[2m"
-#define COLOR_UL      "\033[4m"
-#define COLOR_BLINK   "\033[5m"
-#define COLOR_REVERSE "\033[7m"
-#define COLOR_RESET   "\033[m"
-
-#define COLOR_BLACK   "\033[30m"
-#define COLOR_RED     "\033[31m"
-#define COLOR_GREEN   "\033[32m"
-#define COLOR_YELLOW  "\033[33m"
-#define COLOR_BLUE    "\033[34m"
-#define COLOR_MAGENTA "\033[35m"
-#define COLOR_CYAN    "\033[36m"
-#define COLOR_WHITE   "\033[37m"
-
-static const char *diff_colors[] = {
-	COLOR_RESET,
-	COLOR_NORMAL,
-	COLOR_BOLD,
-	COLOR_CYAN,
-	COLOR_RED,
-	COLOR_GREEN
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+static char diff_colors[][24] = {
+	"\033[m",	/* reset */
+	"",		/* normal */
+	"\033[1m",	/* bold */
+	"\033[36m",	/* cyan */
+	"\033[31m",	/* red */
+	"\033[32m"	/* green */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -67,38 +51,116 @@
 	die("bad config variable '%s'", var);
 }
 
-static const char *parse_diff_color_value(const char *value, const char *var)
+static int parse_color(const char *name, int len)
 {
-	if (!strcasecmp(value, "normal"))
-		return COLOR_NORMAL;
-	if (!strcasecmp(value, "bold"))
-		return COLOR_BOLD;
-	if (!strcasecmp(value, "dim"))
-		return COLOR_DIM;
-	if (!strcasecmp(value, "ul"))
-		return COLOR_UL;
-	if (!strcasecmp(value, "blink"))
-		return COLOR_BLINK;
-	if (!strcasecmp(value, "reverse"))
-		return COLOR_REVERSE;
-	if (!strcasecmp(value, "reset"))
-		return COLOR_RESET;
-	if (!strcasecmp(value, "black"))
-		return COLOR_BLACK;
-	if (!strcasecmp(value, "red"))
-		return COLOR_RED;
-	if (!strcasecmp(value, "green"))
-		return COLOR_GREEN;
-	if (!strcasecmp(value, "yellow"))
-		return COLOR_YELLOW;
-	if (!strcasecmp(value, "blue"))
-		return COLOR_BLUE;
-	if (!strcasecmp(value, "magenta"))
-		return COLOR_MAGENTA;
-	if (!strcasecmp(value, "cyan"))
-		return COLOR_CYAN;
-	if (!strcasecmp(value, "white"))
-		return COLOR_WHITE;
+	static const char * const color_names[] = {
+		"normal", "black", "red", "green", "yellow",
+		"blue", "magenta", "cyan", "white"
+	};
+	char *end;
+	int i;
+	for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+		const char *str = color_names[i];
+		if (!strncasecmp(name, str, len) && !str[len])
+			return i - 1;
+	}
+	i = strtol(name, &end, 10);
+	if (*name && !*end && i >= -1 && i <= 255)
+		return i;
+	return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+	static const int attr_values[] = { 1, 2, 4, 5, 7 };
+	static const char * const attr_names[] = {
+		"bold", "dim", "ul", "blink", "reverse"
+	};
+	int i;
+	for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+		const char *str = attr_names[i];
+		if (!strncasecmp(name, str, len) && !str[len])
+			return attr_values[i];
+	}
+	return -1;
+}
+
+static void parse_diff_color_value(const char *value, const char *var, char *dst)
+{
+	const char *ptr = value;
+	int attr = -1;
+	int fg = -2;
+	int bg = -2;
+
+	if (!strcasecmp(value, "reset")) {
+		strcpy(dst, "\033[m");
+		return;
+	}
+
+	/* [fg [bg]] [attr] */
+	while (*ptr) {
+		const char *word = ptr;
+		int val, len = 0;
+
+		while (word[len] && !isspace(word[len]))
+			len++;
+
+		ptr = word + len;
+		while (*ptr && isspace(*ptr))
+			ptr++;
+
+		val = parse_color(word, len);
+		if (val >= -1) {
+			if (fg == -2) {
+				fg = val;
+				continue;
+			}
+			if (bg == -2) {
+				bg = val;
+				continue;
+			}
+			goto bad;
+		}
+		val = parse_attr(word, len);
+		if (val < 0 || attr != -1)
+			goto bad;
+		attr = val;
+	}
+
+	if (attr >= 0 || fg >= 0 || bg >= 0) {
+		int sep = 0;
+
+		*dst++ = '\033';
+		*dst++ = '[';
+		if (attr >= 0) {
+			*dst++ = '0' + attr;
+			sep++;
+		}
+		if (fg >= 0) {
+			if (sep++)
+				*dst++ = ';';
+			if (fg < 8) {
+				*dst++ = '3';
+				*dst++ = '0' + fg;
+			} else {
+				dst += sprintf(dst, "38;5;%d", fg);
+			}
+		}
+		if (bg >= 0) {
+			if (sep++)
+				*dst++ = ';';
+			if (bg < 8) {
+				*dst++ = '4';
+				*dst++ = '0' + bg;
+			} else {
+				dst += sprintf(dst, "48;5;%d", bg);
+			}
+		}
+		*dst++ = 'm';
+	}
+	*dst = 0;
+	return;
+bad:
 	die("bad config value '%s' for variable '%s'", value, var);
 }
 
@@ -145,7 +207,7 @@
 	}
 	if (!strncmp(var, "diff.color.", 11)) {
 		int slot = parse_diff_color_slot(var, 11);
-		diff_colors[slot] = parse_diff_color_value(value, var);
+		parse_diff_color_value(value, var, diff_colors[slot]);
 		return 0;
 	}
 	return git_default_config(var, value);
diff --git a/exec_cmd.c b/exec_cmd.c
index c1539d1..62f51fc 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "exec_cmd.h"
+#include "quote.h"
 #define MAX_ARGS	32
 
 extern char **environ;
@@ -96,9 +97,27 @@
 		tmp = argv[0];
 		argv[0] = git_command;
 
+		if (getenv("GIT_TRACE")) {
+			const char **p = argv;
+			fputs("trace: exec:", stderr);
+			while (*p) {
+				fputc(' ', stderr);
+				sq_quote_print(stderr, *p);
+				++p;
+			}
+			putc('\n', stderr);
+			fflush(stderr);
+		}
+
 		/* execve() can only ever return if it fails */
 		execve(git_command, (char **)argv, environ);
 
+		if (getenv("GIT_TRACE")) {
+			fprintf(stderr, "trace: exec failed: %s\n",
+				strerror(errno));
+			fflush(stderr);
+		}
+
 		argv[0] = tmp;
 	}
 	return -1;
diff --git a/fetch-pack.c b/fetch-pack.c
index f2c51eb..b7824db 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -46,7 +46,7 @@
 {
 	struct object *o = deref_tag(parse_object(sha1), path, 0);
 
-	if (o && o->type == TYPE_COMMIT)
+	if (o && o->type == OBJ_COMMIT)
 		rev_list_push((struct commit *)o, SEEN);
 
 	return 0;
@@ -256,14 +256,14 @@
 {
 	struct object *o = parse_object(sha1);
 
-	while (o && o->type == TYPE_TAG) {
+	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
 		if (!t->tagged)
 			break; /* broken repository */
 		o->flags |= COMPLETE;
 		o = parse_object(t->tagged->sha1);
 	}
-	if (o && o->type == TYPE_COMMIT) {
+	if (o && o->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)o;
 		commit->object.flags |= COMPLETE;
 		insert_by_date(commit, &complete);
@@ -357,7 +357,7 @@
 		 * in sync with the other side at some time after
 		 * that (it is OK if we guess wrong here).
 		 */
-		if (o->type == TYPE_COMMIT) {
+		if (o->type == OBJ_COMMIT) {
 			struct commit *commit = (struct commit *)o;
 			if (!cutoff || cutoff < commit->date)
 				cutoff = commit->date;
@@ -376,7 +376,7 @@
 		struct object *o = deref_tag(lookup_object(ref->old_sha1),
 					     NULL, 0);
 
-		if (!o || o->type != TYPE_COMMIT || !(o->flags & COMPLETE))
+		if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
 			continue;
 
 		if (!(o->flags & SEEN)) {
diff --git a/fetch.c b/fetch.c
index 238032b..989d7a4 100644
--- a/fetch.c
+++ b/fetch.c
@@ -118,20 +118,20 @@
 
 static int process_object(struct object *obj)
 {
-	if (obj->type == TYPE_COMMIT) {
+	if (obj->type == OBJ_COMMIT) {
 		if (process_commit((struct commit *)obj))
 			return -1;
 		return 0;
 	}
-	if (obj->type == TYPE_TREE) {
+	if (obj->type == OBJ_TREE) {
 		if (process_tree((struct tree *)obj))
 			return -1;
 		return 0;
 	}
-	if (obj->type == TYPE_BLOB) {
+	if (obj->type == OBJ_BLOB) {
 		return 0;
 	}
-	if (obj->type == TYPE_TAG) {
+	if (obj->type == OBJ_TAG) {
 		if (process_tag((struct tag *)obj))
 			return -1;
 		return 0;
diff --git a/fsck-objects.c b/fsck-objects.c
index ef54a8a..e167f41 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -297,13 +297,13 @@
 	if (obj->flags & SEEN)
 		return 0;
 	obj->flags |= SEEN;
-	if (obj->type == TYPE_BLOB)
+	if (obj->type == OBJ_BLOB)
 		return 0;
-	if (obj->type == TYPE_TREE)
+	if (obj->type == OBJ_TREE)
 		return fsck_tree((struct tree *) obj);
-	if (obj->type == TYPE_COMMIT)
+	if (obj->type == OBJ_COMMIT)
 		return fsck_commit((struct commit *) obj);
-	if (obj->type == TYPE_TAG)
+	if (obj->type == OBJ_TAG)
 		return fsck_tag((struct tag *) obj);
 	/* By now, parse_object() would've returned NULL instead. */
 	return objerror(obj, "unknown type '%d' (internal fsck error)", obj->type);
@@ -472,7 +472,7 @@
 		}
 		mark_reachable(obj, REACHABLE);
 		obj->used = 1;
-		if (obj->type != TYPE_TREE)
+		if (obj->type != OBJ_TREE)
 			err |= objerror(obj, "non-tree in cache-tree");
 	}
 	for (i = 0; i < it->subtree_nr; i++)
diff --git a/git-commit.sh b/git-commit.sh
index 802dd72..4cf3fab 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -138,32 +138,26 @@
         if test -z "$untracked_files"; then
             option="--directory --no-empty-directory"
         fi
+	hdr_shown=
 	if test -f "$GIT_DIR/info/exclude"
 	then
-	    git-ls-files -z --others $option \
+	    git-ls-files --others $option \
 		--exclude-from="$GIT_DIR/info/exclude" \
 		--exclude-per-directory=.gitignore
 	else
-	    git-ls-files -z --others $option \
+	    git-ls-files --others $option \
 		--exclude-per-directory=.gitignore
 	fi |
-	@@PERL@@ -e '$/ = "\0";
-	    my $shown = 0;
-	    while (<>) {
-		chomp;
-		s|\\|\\\\|g;
-		s|\t|\\t|g;
-		s|\n|\\n|g;
-		s/^/#	/;
-		if (!$shown) {
-		    print "#\n# Untracked files:\n";
-		    print "#   (use \"git add\" to add to commit)\n";
-		    print "#\n";
-		    $shown = 1;
-		}
-		print "$_\n";
-	    }
-	'
+	while read line; do
+	    if [ -z "$hdr_shown" ]; then
+		echo '#'
+		echo '# Untracked files:'
+		echo '#   (use "git add" to add to commit)'
+		echo '#'
+		hdr_shown=1
+	    fi
+	    echo "#	$line"
+	done
 
 	if test -n "$verbose" -a -z "$IS_INITIAL"
 	then
diff --git a/git-fetch.sh b/git-fetch.sh
index ff17699..ee99280 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -153,7 +153,7 @@
 	then
 		if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
 		then
-			[ "$verbose" ] && echo >&2 "* $1: same as $3"
+			[ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
 		else
 			echo >&2 "* $1: updating with $3"
 			git-update-ref -m "$rloga: updating tag" "$1" "$2"
diff --git a/git-prune.sh b/git-prune.sh
deleted file mode 100755
index c5a5d29..0000000
--- a/git-prune.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-USAGE='[-n] [--] [<head>...]'
-. git-sh-setup
-
-dryrun=
-echo=
-while case "$#" in 0) break ;; esac
-do
-    case "$1" in
-    -n) dryrun=-n echo=echo ;;
-    --) break ;;
-    -*) usage ;;
-    *)  break ;;
-    esac
-    shift;
-done
-
-sync
-case "$#" in
-0) git-fsck-objects --full --cache --unreachable ;;
-*) git-fsck-objects --full --cache --unreachable $(git-rev-parse --all) "$@" ;;
-esac |
-
-sed -ne '/unreachable /{
-    s/unreachable [^ ][^ ]* //
-    s|\(..\)|\1/|p
-}' | {
-	cd "$GIT_OBJECT_DIRECTORY" || exit
-	xargs $echo rm -f
-	rmdir 2>/dev/null [0-9a-f][0-9a-f]
-}
-
-git-prune-packed $dryrun
-
-if redundant=$(git-pack-redundant --all 2>/dev/null) && test "" != "$redundant"
-then
-	if test "" = "$dryrun"
-	then
-		echo "$redundant" | xargs rm -f
-	else
-		echo rm -f "$redundant"
-	fi
-fi
diff --git a/git-repack.sh b/git-repack.sh
index 640ad8d..9da92fb 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -43,7 +43,9 @@
 	;;
 esac
 pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
-name=$(git-rev-list --objects --all $rev_list 2>&1 |
+name=$( { git-rev-list --objects --all $rev_list ||
+	  echo "git-rev-list died with exit code $?"
+	} |
 	git-pack-objects --non-empty $pack_objects .tmp-pack) ||
 	exit 1
 if [ -z "$name" ]; then
diff --git a/git-revert.sh b/git-revert.sh
index de8b5f0..2bf35d1 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -84,7 +84,7 @@
 		s/^[^ ]* /Revert "/
 		s/$/"/'
 	echo
-	echo "This reverts $commit commit."
+	echo "This reverts commit $commit."
 	test "$rev" = "$commit" ||
 	echo "(original 'git revert' arguments: $@)"
 	base=$commit next=$prev
diff --git a/git-svn.perl b/git-svn.perl
index 4530ffe..89ad840 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -147,7 +147,7 @@
 load_authors() if $_authors;
 load_all_refs() if $_branch_all_refs;
 svn_compat_check() unless $_use_lib;
-migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/;
+migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init|commit-diff)$/;
 $cmd{$cmd}->[0]->(@ARGV);
 exit 0;
 
diff --git a/git.c b/git.c
index 49062ca..ee5a0e8 100644
--- a/git.c
+++ b/git.c
@@ -11,6 +11,7 @@
 #include "git-compat-util.h"
 #include "exec_cmd.h"
 #include "cache.h"
+#include "quote.h"
 
 #include "builtin.h"
 
@@ -120,14 +121,25 @@
 			if (!strcmp(alias_command, new_argv[0]))
 				die("recursive alias: %s", alias_command);
 
-			/* insert after command name */
-			if (*argcp > 1) {
-				new_argv = realloc(new_argv, sizeof(char*) *
-						   (count + *argcp));
-				memcpy(new_argv + count, *argv + 1,
-				       sizeof(char*) * *argcp);
+			if (getenv("GIT_TRACE")) {
+				int i;
+				fprintf(stderr, "trace: alias expansion: %s =>",
+					alias_command);
+				for (i = 0; i < count; ++i) {
+					fputc(' ', stderr);
+					sq_quote_print(stderr, new_argv[i]);
+				}
+				fputc('\n', stderr);
+				fflush(stderr);
 			}
 
+			new_argv = realloc(new_argv, sizeof(char*) *
+					   (count + *argcp + 1));
+			/* insert after command name */
+			memcpy(new_argv + count, *argv + 1,
+			       sizeof(char*) * *argcp);
+			new_argv[count+*argcp] = NULL;
+
 			*argv = new_argv;
 			*argcp += count - 1;
 
@@ -188,7 +200,8 @@
 		{ "stripspace", cmd_stripspace },
 		{ "update-index", cmd_update_index },
 		{ "update-ref", cmd_update_ref },
-		{ "fmt-merge-msg", cmd_fmt_merge_msg }
+		{ "fmt-merge-msg", cmd_fmt_merge_msg },
+		{ "prune", cmd_prune },
 	};
 	int i;
 
@@ -202,6 +215,18 @@
 		struct cmd_struct *p = commands+i;
 		if (strcmp(p->cmd, cmd))
 			continue;
+
+		if (getenv("GIT_TRACE")) {
+			int i;
+			fprintf(stderr, "trace: built-in: git");
+			for (i = 0; i < argc; ++i) {
+				fputc(' ', stderr);
+				sq_quote_print(stderr, argv[i]);
+			}
+			putc('\n', stderr);
+			fflush(stderr);
+		}
+
 		exit(p->fn(argc, argv, envp));
 	}
 }
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 2e87de4..2fd1e5f 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -22,20 +22,16 @@
 our $my_uri = $cgi->url(-absolute => 1);
 our $rss_link = "";
 
-# location of the git-core binaries
-our $gitbin = "/usr/bin";
+# core git executable to use
+# this can just be "git" if your webserver has a sensible PATH
+our $GIT = "/usr/bin/git";
 
 # absolute fs-path which will be prepended to the project path
 #our $projectroot = "/pub/scm";
 our $projectroot = "/home/kay/public_html/pub/scm";
 
-# version of the git-core binaries
-our $git_version = qx($gitbin/git --version);
-if ($git_version =~ m/git version (.*)$/) {
-	$git_version = $1;
-} else {
-	$git_version = "unknown";
-}
+# version of the core git binary
+our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
 # location for temporary files needed for diffs
 our $git_temp = "/tmp/gitweb";
@@ -46,6 +42,10 @@
 # target of the home link on top of all pages
 our $home_link = $my_uri;
 
+# name of your site or organization to appear in page titles
+# replace this with something more descriptive for clearer bookmarks
+our $site_name = $ENV{'SERVER_NAME'} || "Untitled";
+
 # html text to include at home page
 our $home_text = "indextext.html";
 
@@ -280,7 +280,7 @@
 	my $status = shift || "200 OK";
 	my $expires = shift;
 
-	my $title = "git";
+	my $title = "$site_name git";
 	if (defined $project) {
 		$title .= " - $project";
 		if (defined $action) {
@@ -293,7 +293,17 @@
 			}
 		}
 	}
-	print $cgi->header(-type=>'text/html',  -charset => 'utf-8', -status=> $status, -expires => $expires);
+	my $content_type;
+	# require explicit support from the UA if we are to send the page as
+	# 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+	# we have to do this because MSIE sometimes globs '*/*', pretending to
+	# support xhtml+xml but choking when it gets what it asked for.
+	if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) {
+		$content_type = 'application/xhtml+xml';
+	} else {
+		$content_type = 'text/html';
+	}
+	print $cgi->header(-type=>$content_type,  -charset => 'utf-8', -status=> $status, -expires => $expires);
 	print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -301,7 +311,7 @@
 <!-- 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 http-equiv="content-type" content="$content_type; charset=utf-8"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 <link rel="stylesheet" type="text/css" href="$stylesheet"/>
@@ -378,7 +388,7 @@
 sub git_get_type {
 	my $hash = shift;
 
-	open my $fd, "-|", "$gitbin/git-cat-file -t $hash" or return;
+	open my $fd, "-|", "$GIT cat-file -t $hash" or return;
 	my $type = <$fd>;
 	close $fd or return;
 	chomp $type;
@@ -390,7 +400,7 @@
 	my $oENV = $ENV{'GIT_DIR'};
 	my $retval = undef;
 	$ENV{'GIT_DIR'} = "$projectroot/$project";
-	if (open my $fd, "-|", "$gitbin/git-rev-parse", "--verify", "HEAD") {
+	if (open my $fd, "-|", $GIT, "rev-parse", "--verify", "HEAD") {
 		my $head = <$fd>;
 		close $fd;
 		if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
@@ -430,7 +440,7 @@
 	my %tag;
 	my @comment;
 
-	open my $fd, "-|", "$gitbin/git-cat-file tag $tag_id" or return;
+	open my $fd, "-|", "$GIT cat-file tag $tag_id" or return;
 	$tag{'id'} = $tag_id;
 	while (my $line = <$fd>) {
 		chomp $line;
@@ -502,7 +512,7 @@
 		@commit_lines = @$commit_text;
 	} else {
 		$/ = "\0";
-		open my $fd, "-|", "$gitbin/git-rev-list --header --parents --max-count=1 $commit_id" or return;
+		open my $fd, "-|", "$GIT rev-list --header --parents --max-count=1 $commit_id" or return;
 		@commit_lines = split '\n', <$fd>;
 		close $fd or return;
 		$/ = "\n";
@@ -600,7 +610,7 @@
 	if (defined $from) {
 		$from_tmp = "$git_temp/gitweb_" . $$ . "_from";
 		open my $fd2, "> $from_tmp";
-		open my $fd, "-|", "$gitbin/git-cat-file blob $from";
+		open my $fd, "-|", "$GIT cat-file blob $from";
 		my @file = <$fd>;
 		print $fd2 @file;
 		close $fd2;
@@ -611,7 +621,7 @@
 	if (defined $to) {
 		$to_tmp = "$git_temp/gitweb_" . $$ . "_to";
 		open my $fd2, "> $to_tmp";
-		open my $fd, "-|", "$gitbin/git-cat-file blob $to";
+		open my $fd, "-|", "$GIT cat-file blob $to";
 		my @file = <$fd>;
 		print $fd2 @file;
 		close $fd2;
@@ -830,7 +840,7 @@
 	$key =~ s/^gitweb\.//;
 	return if ($key =~ m/\W/);
 
-	my $val = qx($gitbin/git-repo-config --get gitweb.$key);
+	my $val = qx($GIT repo-config --get gitweb.$key);
 	return ($val);
 }
 
@@ -1052,7 +1062,7 @@
 	      "<tr><td>owner</td><td>$owner</td></tr>\n" .
 	      "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
 	      "</table>\n";
-	open my $fd, "-|", "$gitbin/git-rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 	print "<div>\n" .
@@ -1240,7 +1250,7 @@
 		$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
 			or die_error(undef, "Error lookup file.");
 	}
-	open ($fd, "-|", "$gitbin/git-annotate", '-l', '-t', '-r', $file_name, $hash_base)
+	open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base)
 		or die_error(undef, "Open failed.");
 	git_header_html();
 	print "<div class=\"page_nav\">\n" .
@@ -1435,7 +1445,7 @@
 	my $tree = $base;
 	my @parts = split '/', $path;
 	while (my $part = shift @parts) {
-		open my $fd, "-|", "$gitbin/git-ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
+		open my $fd, "-|", "$GIT ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
 		my (@entries) = map { chomp; $_ } <$fd>;
 		close $fd or return undef;
 		foreach my $line (@entries) {
@@ -1522,7 +1532,7 @@
 
 sub git_blob_plain {
 	my $type = shift;
-	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
+	open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
 
 	$type ||= git_blob_plain_mimetype($fd, $file_name);
 
@@ -1549,7 +1559,7 @@
 		$hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
 	}
 	my $have_blame = git_get_project_config_bool ('blame');
-	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed.");
 	my $mimetype = git_blob_plain_mimetype($fd, $file_name);
 	if ($mimetype !~ m/^text\//) {
 		close $fd;
@@ -1615,7 +1625,7 @@
 		}
 	}
 	$/ = "\0";
-	open my $fd, "-|", "$gitbin/git-ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
+	open my $fd, "-|", "$GIT ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
 	chomp (my (@entries) = <$fd>);
 	close $fd or die_error(undef, "Reading tree failed.");
 	$/ = "\n";
@@ -1698,7 +1708,7 @@
 
 sub git_rss {
 	# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
-	open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading rev-list failed.");
 	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
@@ -1718,7 +1728,7 @@
 			last;
 		}
 		my %cd = date_str($co{'committer_epoch'});
-		open $fd, "-|", "$gitbin/git-diff-tree -r $co{'parent'} $co{'id'}" or next;
+		open $fd, "-|", "$GIT diff-tree -r $co{'parent'} $co{'id'}" or next;
 		my @difftree = map { chomp; $_ } <$fd>;
 		close $fd or next;
 		print "<item>\n" .
@@ -1760,7 +1770,7 @@
 	print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
 	      "<opml version=\"1.0\">\n".
 	      "<head>".
-	      "  <title>Git OPML Export</title>\n".
+	      "  <title>$site_name Git OPML Export</title>\n".
 	      "</head>\n".
 	      "<body>\n".
 	      "<outline text=\"git RSS feeds\">\n";
@@ -1806,7 +1816,7 @@
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 
@@ -1897,7 +1907,7 @@
 		$root = " --root";
 		$parent = "";
 	}
-	open my $fd, "-|", "$gitbin/git-diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
 	@difftree = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
@@ -2139,7 +2149,7 @@
 	if (!defined $hash_parent) {
 		$hash_parent = $co{'parent'};
 	}
-	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
 	my (@difftree) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
@@ -2229,14 +2239,14 @@
 
 sub git_commitdiff_plain {
 	mkdir($git_temp, 0700);
-	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
 	my (@difftree) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
 	# try to figure out the next tag after this commit
 	my $tagname;
 	my $refs = read_info_ref("tags");
-	open $fd, "-|", "$gitbin/git-rev-list HEAD";
+	open $fd, "-|", "$GIT rev-list HEAD";
 	chomp (my (@commits) = <$fd>);
 	close $fd;
 	foreach my $commit (@commits) {
@@ -2307,7 +2317,7 @@
 	print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
 
 	open my $fd, "-|",
-		"$gitbin/git-rev-list --full-history $hash -- \'$file_name\'";
+		"$GIT rev-list --full-history $hash -- \'$file_name\'";
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 0;
 	while (my $line = <$fd>) {
@@ -2394,7 +2404,7 @@
 	my $alternate = 0;
 	if ($commit_search) {
 		$/ = "\0";
-		open my $fd, "-|", "$gitbin/git-rev-list --header --parents $hash" or next;
+		open my $fd, "-|", "$GIT rev-list --header --parents $hash" or next;
 		while (my $commit_text = <$fd>) {
 			if (!grep m/$searchtext/i, $commit_text) {
 				next;
@@ -2444,7 +2454,7 @@
 
 	if ($pickaxe_search) {
 		$/ = "\n";
-		open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -S\'$searchtext\'";
+		open my $fd, "-|", "$GIT rev-list $hash | $GIT diff-tree -r --stdin -S\'$searchtext\'";
 		undef %co;
 		my @files;
 		while (my $line = <$fd>) {
@@ -2515,7 +2525,7 @@
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 98410f5..fffdb13 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -60,6 +60,7 @@
 
 div.page_body {
 	padding: 8px;
+	font-family: monospace;
 }
 
 div.title, a.title {
@@ -79,6 +80,7 @@
 	padding: 6px 0px;
 	border: solid #d9d8d1;
 	border-width: 0px 0px 1px;
+	font-family: monospace;
 }
 
 div.log_body {
@@ -142,10 +144,15 @@
 	padding: 8px 4px;
 }
 
-table.project_list, table.diff_tree {
+table.project_list {
 	border-spacing: 0;
 }
 
+table.diff_tree {
+	border-spacing: 0;
+	font-family: monospace;
+}
+
 table.blame {
 	border-collapse: collapse;
 }
diff --git a/http-push.c b/http-push.c
index f761584..4768619 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1784,16 +1784,16 @@
 
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
-		if (obj->type == TYPE_TAG) {
+		if (obj->type == OBJ_TAG) {
 			obj->flags |= SEEN;
 			p = add_one_object(obj, p);
 			continue;
 		}
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			p = process_tree((struct tree *)obj, p, NULL, name);
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			p = process_blob((struct blob *)obj, p, NULL, name);
 			continue;
 		}
@@ -1960,12 +1960,12 @@
 	 * old.  Otherwise we require --force.
 	 */
 	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	old = (struct commit *) o;
 
 	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	new = (struct commit *) o;
 
@@ -2044,7 +2044,7 @@
 	fwrite_buffer(ref_info, 1, len, buf);
 	free(ref_info);
 
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, ls->dentry_name, 0);
 		if (o) {
 			len = strlen(ls->dentry_name) + 45;
diff --git a/merge-file.c b/merge-file.c
new file mode 100644
index 0000000..f32c653
--- /dev/null
+++ b/merge-file.c
@@ -0,0 +1,166 @@
+#include "cache.h"
+#include "run-command.h"
+#include "xdiff-interface.h"
+#include "blob.h"
+
+static void rm_temp_file(const char *filename)
+{
+	unlink(filename);
+	free((void *)filename);
+}
+
+static const char *write_temp_file(mmfile_t *f)
+{
+	int fd;
+	const char *tmp = getenv("TMPDIR");
+	char *filename;
+
+	if (!tmp)
+		tmp = "/tmp";
+	filename = mkpath("%s/%s", tmp, "git-tmp-XXXXXX");
+	fd = mkstemp(filename);
+	if (fd < 0)
+		return NULL;
+	filename = strdup(filename);
+	if (f->size != xwrite(fd, f->ptr, f->size)) {
+		rm_temp_file(filename);
+		return NULL;
+	}
+	close(fd);
+	return filename;
+}
+
+static void *read_temp_file(const char *filename, unsigned long *size)
+{
+	struct stat st;
+	char *buf = NULL;
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+	if (!fstat(fd, &st)) {
+		*size = st.st_size;
+		buf = xmalloc(st.st_size);
+		if (st.st_size != xread(fd, buf, st.st_size)) {
+			free(buf);
+			buf = NULL;
+		}
+	}
+	close(fd);
+	return buf;
+}
+
+static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
+{
+	void *buf;
+	unsigned long size;
+	char type[20];
+
+	buf = read_sha1_file(obj->object.sha1, type, &size);
+	if (!buf)
+		return -1;
+	if (strcmp(type, blob_type))
+		return -1;
+	f->ptr = buf;
+	f->size = size;
+	return 0;
+}
+
+static void free_mmfile(mmfile_t *f)
+{
+	free(f->ptr);
+}
+
+static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
+{
+	void *res;
+	const char *t1, *t2, *t3;
+
+	t1 = write_temp_file(base);
+	t2 = write_temp_file(our);
+	t3 = write_temp_file(their);
+	res = NULL;
+	if (t1 && t2 && t3) {
+		int code = run_command("merge", t2, t1, t3, NULL);
+		if (!code || code == -1)
+			res = read_temp_file(t2, size);
+	}
+	rm_temp_file(t1);
+	rm_temp_file(t2);
+	rm_temp_file(t3);
+	return res;
+}
+
+static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	mmfile_t *dst = priv_;
+
+	for (i = 0; i < nbuf; i++) {
+		memcpy(dst->ptr + dst->size, mb[i].ptr, mb[i].size);
+		dst->size += mb[i].size;
+	}
+	return 0;
+}
+
+static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
+{
+	unsigned long size = f1->size < f2->size ? f1->size : f2->size;
+	void *ptr = xmalloc(size);
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 3;
+	xecfg.flags = XDL_EMIT_COMMON;
+	ecb.outf = common_outf;
+
+	res->ptr = ptr;
+	res->size = 0;
+
+	ecb.priv = res;
+	return xdl_diff(f1, f2, &xpp, &xecfg, &ecb);
+}
+
+void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+{
+	void *res = NULL;
+	mmfile_t f1, f2, common;
+
+	/*
+	 * Removed in either branch?
+	 *
+	 * NOTE! This depends on the caller having done the
+	 * proper warning about removing a file that got
+	 * modified in the other branch!
+	 */
+	if (!our || !their) {
+		char type[20];
+		if (base)
+			return NULL;
+		if (!our)
+			our = their;
+		return read_sha1_file(our->object.sha1, type, size);
+	}
+
+	if (fill_mmfile_blob(&f1, our) < 0)
+		goto out_no_mmfile;
+	if (fill_mmfile_blob(&f2, their) < 0)
+		goto out_free_f1;
+
+	if (base) {
+		if (fill_mmfile_blob(&common, base) < 0)
+			goto out_free_f2_f1;
+	} else {
+		if (generate_common_file(&common, &f1, &f2) < 0)
+			goto out_free_f2_f1;
+	}
+	res = three_way_filemerge(&common, &f1, &f2, size);
+	free_mmfile(&common);
+out_free_f2_f1:
+	free_mmfile(&f2);
+out_free_f1:
+	free_mmfile(&f1);
+out_no_mmfile:
+	return res;
+}
diff --git a/merge-tree.c b/merge-tree.c
index 9dcaab7..7cf00be 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -1,11 +1,152 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "xdiff-interface.h"
+#include "blob.h"
 
 static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
 static int resolve_directories = 1;
 
+struct merge_list {
+	struct merge_list *next;
+	struct merge_list *link;	/* other stages for this object */
+
+	unsigned int stage : 2,
+		     flags : 30;
+	unsigned int mode;
+	const char *path;
+	struct blob *blob;
+};
+
+static struct merge_list *merge_result, **merge_result_end = &merge_result;
+
+static void add_merge_entry(struct merge_list *entry)
+{
+	*merge_result_end = entry;
+	merge_result_end = &entry->next;
+}
+
 static void merge_trees(struct tree_desc t[3], const char *base);
 
+static const char *explanation(struct merge_list *entry)
+{
+	switch (entry->stage) {
+	case 0:
+		return "merged";
+	case 3:
+		return "added in remote";
+	case 2:
+		if (entry->link)
+			return "added in both";
+		return "added in local";
+	}
+
+	/* Existed in base */
+	entry = entry->link;
+	if (!entry)
+		return "removed in both";
+
+	if (entry->link)
+		return "changed in both";
+
+	if (entry->stage == 3)
+		return "removed in local";
+	return "removed in remote";
+}
+
+extern void *merge_file(struct blob *, struct blob *, struct blob *, unsigned long *);
+
+static void *result(struct merge_list *entry, unsigned long *size)
+{
+	char type[20];
+	struct blob *base, *our, *their;
+
+	if (!entry->stage)
+		return read_sha1_file(entry->blob->object.sha1, type, size);
+	base = NULL;
+	if (entry->stage == 1) {
+		base = entry->blob;
+		entry = entry->link;
+	}
+	our = NULL;
+	if (entry && entry->stage == 2) {
+		our = entry->blob;
+		entry = entry->link;
+	}
+	their = NULL;
+	if (entry)
+		their = entry->blob;
+	return merge_file(base, our, their, size);
+}
+
+static void *origin(struct merge_list *entry, unsigned long *size)
+{
+	char type[20];
+	while (entry) {
+		if (entry->stage == 2)
+			return read_sha1_file(entry->blob->object.sha1, type, size);
+		entry = entry->link;
+	}
+	return NULL;
+}
+
+static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		printf("%.*s", (int) mb[i].size, mb[i].ptr);
+	return 0;
+}
+
+static void show_diff(struct merge_list *entry)
+{
+	unsigned long size;
+	mmfile_t src, dst;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 3;
+	xecfg.flags = 0;
+	ecb.outf = show_outf;
+	ecb.priv = NULL;
+
+	src.ptr = origin(entry, &size);
+	if (!src.ptr)
+		size = 0;
+	src.size = size;
+	dst.ptr = result(entry, &size);
+	if (!dst.ptr)
+		size = 0;
+	dst.size = size;
+	xdl_diff(&src, &dst, &xpp, &xecfg, &ecb);
+	free(src.ptr);
+	free(dst.ptr);
+}
+
+static void show_result_list(struct merge_list *entry)
+{
+	printf("%s\n", explanation(entry));
+	do {
+		struct merge_list *link = entry->link;
+		static const char *desc[4] = { "result", "base", "our", "their" };
+		printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, sha1_to_hex(entry->blob->object.sha1), entry->path);
+		entry = link;
+	} while (entry);
+}
+
+static void show_result(void)
+{
+	struct merge_list *walk;
+
+	walk = merge_result;
+	while (walk) {
+		show_result_list(walk);
+		show_diff(walk);
+		walk = walk->next;
+	}
+}
+
 /* An empty entry never compares same, not even to another empty entry */
 static int same_entry(struct name_entry *a, struct name_entry *b)
 {
@@ -15,24 +156,34 @@
 		a->mode == b->mode;
 }
 
-static const char *sha1_to_hex_zero(const unsigned char *sha1)
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
-	if (sha1)
-		return sha1_to_hex(sha1);
-	return "0000000000000000000000000000000000000000";
+	struct merge_list *res = xmalloc(sizeof(*res));
+
+	memset(res, 0, sizeof(*res));
+	res->stage = stage;
+	res->path = path;
+	res->mode = mode;
+	res->blob = lookup_blob(sha1);
+	return res;
 }
 
 static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
 {
+	struct merge_list *orig, *final;
+	const char *path;
+
 	/* If it's already branch1, don't bother showing it */
 	if (!branch1)
 		return;
 
-	printf("0 %06o->%06o %s->%s %s%s\n",
-		branch1->mode, result->mode,
-		sha1_to_hex_zero(branch1->sha1),
-		sha1_to_hex_zero(result->sha1),
-		base, result->path);
+	path = strdup(mkpath("%s%s", base, result->path));
+	orig = create_entry(2, branch1->mode, branch1->sha1, path);
+	final = create_entry(0, result->mode, result->sha1, path);
+
+	final->link = orig;
+
+	add_merge_entry(final);
 }
 
 static int unresolved_directory(const char *base, struct name_entry n[3])
@@ -71,16 +222,40 @@
 	return 1;
 }
 
+
+static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+{
+	const char *path;
+	struct merge_list *link;
+
+	if (!n->mode)
+		return entry;
+	if (entry)
+		path = entry->path;
+	else
+		path = strdup(mkpath("%s%s", base, n->path));
+	link = create_entry(stage, n->mode, n->sha1, path);
+	link->link = entry;
+	return link;
+}
+
 static void unresolved(const char *base, struct name_entry n[3])
 {
+	struct merge_list *entry = NULL;
+
 	if (unresolved_directory(base, n))
 		return;
-	if (n[0].sha1)
-		printf("1 %06o %s %s%s\n", n[0].mode, sha1_to_hex(n[0].sha1), base, n[0].path);
-	if (n[1].sha1)
-		printf("2 %06o %s %s%s\n", n[1].mode, sha1_to_hex(n[1].sha1), base, n[1].path);
-	if (n[2].sha1)
-		printf("3 %06o %s %s%s\n", n[2].mode, sha1_to_hex(n[2].sha1), base, n[2].path);
+
+	/*
+	 * Do them in reverse order so that the resulting link
+	 * list has the stages in order - link_entry adds new
+	 * links at the front.
+	 */
+	entry = link_entry(3, base, n + 2, entry);
+	entry = link_entry(2, base, n + 1, entry);
+	entry = link_entry(1, base, n + 0, entry);
+
+	add_merge_entry(entry);
 }
 
 /*
@@ -172,5 +347,7 @@
 	free(buf1);
 	free(buf2);
 	free(buf3);
+
+	show_result();
 	return 0;
 }
diff --git a/name-rev.c b/name-rev.c
index 083d067..f92f14e 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -84,14 +84,14 @@
 	if (tags_only && strncmp(path, "refs/tags/", 10))
 		return 0;
 
-	while (o && o->type == TYPE_TAG) {
+	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
 		if (!t->tagged)
 			break; /* broken repository */
 		o = parse_object(t->tagged->sha1);
 		deref = 1;
 	}
-	if (o && o->type == TYPE_COMMIT) {
+	if (o && o->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)o;
 
 		if (!strncmp(path, "refs/heads/", 11))
@@ -111,7 +111,7 @@
 	struct rev_name *n;
 	struct commit *c;
 
-	if (o->type != TYPE_COMMIT)
+	if (o->type != OBJ_COMMIT)
 		return "undefined";
 	c = (struct commit *) o;
 	n = c->util;
@@ -172,7 +172,7 @@
 		}
 
 		o = deref_tag(parse_object(sha1), *argv, 0);
-		if (!o || o->type != TYPE_COMMIT) {
+		if (!o || o->type != OBJ_COMMIT) {
 			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
 					*argv);
 			continue;
diff --git a/object.c b/object.c
index 37277f9..b5d8ed4 100644
--- a/object.c
+++ b/object.c
@@ -19,7 +19,8 @@
 }
 
 const char *type_names[] = {
-	"none", "blob", "tree", "commit", "bad"
+	"none", "commit", "tree", "blob", "tag",
+	"bad type 5", "bad type 6", "delta", "bad",
 };
 
 static unsigned int hash_obj(struct object *obj, unsigned int n)
@@ -88,7 +89,7 @@
 {
 	obj->parsed = 0;
 	obj->used = 0;
-	obj->type = TYPE_NONE;
+	obj->type = OBJ_NONE;
 	obj->flags = 0;
 	memcpy(obj->sha1, sha1, 20);
 
@@ -131,7 +132,7 @@
 	if (!obj) {
 		union any_object *ret = xcalloc(1, sizeof(*ret));
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_NONE;
+		ret->object.type = OBJ_NONE;
 		return &ret->object;
 	}
 	return obj;
diff --git a/object.h b/object.h
index e0125e1..733faac 100644
--- a/object.h
+++ b/object.h
@@ -24,12 +24,19 @@
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
-#define TYPE_NONE   0
-#define TYPE_BLOB   1
-#define TYPE_TREE   2
-#define TYPE_COMMIT 3
-#define TYPE_TAG    4
-#define TYPE_BAD    5
+/*
+ * The object type is stored in 3 bits.
+ */
+enum object_type {
+	OBJ_NONE = 0,
+	OBJ_COMMIT = 1,
+	OBJ_TREE = 2,
+	OBJ_BLOB = 3,
+	OBJ_TAG = 4,
+	/* 5/6 for future expansion */
+	OBJ_DELTA = 7,
+	OBJ_BAD,
+};
 
 struct object {
 	unsigned parsed : 1;
@@ -40,14 +47,14 @@
 };
 
 extern int track_object_refs;
-extern const char *type_names[];
+extern const char *type_names[9];
 
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
 
 static inline const char *typename(unsigned int type)
 {
-	return type_names[type > TYPE_TAG ? TYPE_BAD : type];
+	return type_names[type > OBJ_BAD ? OBJ_BAD : type];
 }
 
 extern struct object_refs *lookup_object_refs(struct object *);
diff --git a/pack.h b/pack.h
index 694e0c5..eb07b03 100644
--- a/pack.h
+++ b/pack.h
@@ -1,20 +1,7 @@
 #ifndef PACK_H
 #define PACK_H
 
-/*
- * The packed object type is stored in 3 bits.
- * The type value 0 is a reserved prefix if ever there is more than 7
- * object types, or any future format extensions.
- */
-enum object_type {
-	OBJ_EXT = 0,
-	OBJ_COMMIT = 1,
-	OBJ_TREE = 2,
-	OBJ_BLOB = 3,
-	OBJ_TAG = 4,
-	/* 5/6 for future expansion */
-	OBJ_DELTA = 7,
-};
+#include "object.h"
 
 /*
  * Packed object header
diff --git a/quote.c b/quote.c
index 1910d00..e220dcc 100644
--- a/quote.c
+++ b/quote.c
@@ -45,6 +45,23 @@
 	return len;
 }
 
+void sq_quote_print(FILE *stream, const char *src)
+{
+	char c;
+
+	fputc('\'', stream);
+	while ((c = *src++)) {
+		if (need_bs_quote(c)) {
+			fputs("'\\", stream);
+			fputc(c, stream);
+			fputc('\'', stream);
+		} else {
+			fputc(c, stream);
+		}
+	}
+	fputc('\'', stream);
+}
+
 char *sq_quote(const char *src)
 {
 	char *buf;
diff --git a/quote.h b/quote.h
index c1ab378..fc5481e 100644
--- a/quote.h
+++ b/quote.h
@@ -29,6 +29,7 @@
  */
 
 extern char *sq_quote(const char *src);
+extern void sq_quote_print(FILE *stream, const char *src);
 extern size_t sq_quote_buf(char *dst, size_t n, const char *src);
 
 /* This unwraps what sq_quote() produces in place, but returns
diff --git a/revision.c b/revision.c
index 7df9089..874e349 100644
--- a/revision.c
+++ b/revision.c
@@ -135,7 +135,7 @@
 	/*
 	 * Tag object? Look what it points to..
 	 */
-	while (object->type == TYPE_TAG) {
+	while (object->type == OBJ_TAG) {
 		struct tag *tag = (struct tag *) object;
 		if (revs->tag_objects && !(flags & UNINTERESTING))
 			add_pending_object(revs, object, tag->tag);
@@ -148,7 +148,7 @@
 	 * Commit object? Just return it, we'll do all the complex
 	 * reachability crud.
 	 */
-	if (object->type == TYPE_COMMIT) {
+	if (object->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)object;
 		if (parse_commit(commit) < 0)
 			die("unable to parse commit %s", name);
@@ -164,7 +164,7 @@
 	 * Tree object? Either mark it uniniteresting, or add it
 	 * to the list of objects to look at later..
 	 */
-	if (object->type == TYPE_TREE) {
+	if (object->type == OBJ_TREE) {
 		struct tree *tree = (struct tree *)object;
 		if (!revs->tree_objects)
 			return NULL;
@@ -179,7 +179,7 @@
 	/*
 	 * Blob object? You know the drill by now..
 	 */
-	if (object->type == TYPE_BLOB) {
+	if (object->type == OBJ_BLOB) {
 		struct blob *blob = (struct blob *)object;
 		if (!revs->blob_objects)
 			return NULL;
@@ -494,11 +494,11 @@
 		return 0;
 	while (1) {
 		it = get_reference(revs, arg, sha1, 0);
-		if (it->type != TYPE_TAG)
+		if (it->type != OBJ_TAG)
 			break;
 		memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
 	}
-	if (it->type != TYPE_COMMIT)
+	if (it->type != OBJ_COMMIT)
 		return 0;
 	commit = (struct commit *)it;
 	for (parents = commit->parents; parents; parents = parents->next) {
diff --git a/send-pack.c b/send-pack.c
index 4019a4b..10bc8bc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -151,12 +151,12 @@
 	 * old.  Otherwise we require --force.
 	 */
 	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	old = (struct commit *) o;
 
 	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	new = (struct commit *) o;
 
diff --git a/server-info.c b/server-info.c
index fdfe05a..7df628f 100644
--- a/server-info.c
+++ b/server-info.c
@@ -12,7 +12,7 @@
 	struct object *o = parse_object(sha1);
 
 	fprintf(info_ref_fp, "%s	%s\n", sha1_to_hex(sha1), path);
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, path, 0);
 		if (o)
 			fprintf(info_ref_fp, "%s	%s^{}\n",
diff --git a/sha1_file.c b/sha1_file.c
index 8734d50..e666aec 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1331,31 +1331,29 @@
 static int link_temp_to_file(const char *tmpfile, char *filename)
 {
 	int ret;
+	char *dir;
 
 	if (!link(tmpfile, filename))
 		return 0;
 
 	/*
-	 * Try to mkdir the last path component if that failed
-	 * with an ENOENT.
+	 * Try to mkdir the last path component if that failed.
 	 *
 	 * Re-try the "link()" regardless of whether the mkdir
 	 * succeeds, since a race might mean that somebody
 	 * else succeeded.
 	 */
 	ret = errno;
-	if (ret == ENOENT) {
-		char *dir = strrchr(filename, '/');
-		if (dir) {
-			*dir = 0;
-			mkdir(filename, 0777);
-			if (adjust_shared_perm(filename))
-				return -2;
-			*dir = '/';
-			if (!link(tmpfile, filename))
-				return 0;
-			ret = errno;
-		}
+	dir = strrchr(filename, '/');
+	if (dir) {
+		*dir = 0;
+		mkdir(filename, 0777);
+		if (adjust_shared_perm(filename))
+			return -2;
+		*dir = '/';
+		if (!link(tmpfile, filename))
+			return 0;
+		ret = errno;
 	}
 	return ret;
 }
diff --git a/sha1_name.c b/sha1_name.c
index f2cbafa..5fe8e5d 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -381,13 +381,13 @@
 
 	sp++; /* beginning of type name, or closing brace for empty */
 	if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
-		expected_type = TYPE_COMMIT;
+		expected_type = OBJ_COMMIT;
 	else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
-		expected_type = TYPE_TREE;
+		expected_type = OBJ_TREE;
 	else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
-		expected_type = TYPE_BLOB;
+		expected_type = OBJ_BLOB;
 	else if (sp[0] == '}')
-		expected_type = TYPE_NONE;
+		expected_type = OBJ_NONE;
 	else
 		return -1;
 
@@ -416,9 +416,9 @@
 				memcpy(sha1, o->sha1, 20);
 				return 0;
 			}
-			if (o->type == TYPE_TAG)
+			if (o->type == OBJ_TAG)
 				o = ((struct tag*) o)->tagged;
-			else if (o->type == TYPE_COMMIT)
+			else if (o->type == OBJ_COMMIT)
 				o = &(((struct commit *) o)->tree->object);
 			else
 				return error("%.*s: expected %s type, but the object dereferences to %s type",
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 36658fb..b24c829 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -7,6 +7,9 @@
 
 . ./test-lib.sh
 
+LF='
+'
+
 test_expect_success setup '
 
 	GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
@@ -31,7 +34,7 @@
 	for i in C D; do echo $i; done >>dir/sub &&
 	rm -f file2 &&
 	git update-index --remove file0 file2 dir/sub &&
-	git commit -m Second &&
+	git commit -m "Second${LF}${LF}This is the second commit." &&
 
 	GIT_AUTHOR_DATE="2006-06-26 00:02:00 +0000" &&
 	GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
index 0ac9800..3a9f78a 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
@@ -1,5 +1,5 @@
 $ git diff-tree --cc --patch-with-stat --summary master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
index f6ecf76..49f23b9 100644
--- a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
@@ -1,5 +1,5 @@
 $ git diff-tree --cc --patch-with-stat master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
index 712ffd2..cc6eb3b 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
@@ -1,5 +1,5 @@
 $ git diff-tree --cc --stat --summary master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master
index 8d5bdc9..fae7f33 100644
--- a/t/t4013/diff.diff-tree_--cc_--stat_master
+++ b/t/t4013/diff.diff-tree_--cc_--stat_master
@@ -1,5 +1,5 @@
 $ git diff-tree --cc --stat master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_--cc_master b/t/t4013/diff.diff-tree_--cc_master
index e57d943..5ecb4e1 100644
--- a/t/t4013/diff.diff-tree_--cc_master
+++ b/t/t4013/diff.diff-tree_--cc_master
@@ -1,5 +1,5 @@
 $ git diff-tree --cc master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
 diff --cc dir/sub
 index cead32e,7289e35..992913c
 --- a/dir/sub
diff --git a/t/t4013/diff.diff-tree_-c_--abbrev_master b/t/t4013/diff.diff-tree_-c_--abbrev_master
index 39d511a..b8e4aa2 100644
--- a/t/t4013/diff.diff-tree_-c_--abbrev_master
+++ b/t/t4013/diff.diff-tree_-c_--abbrev_master
@@ -1,5 +1,5 @@
 $ git diff-tree -c --abbrev master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
 ::100644 100644 100644 cead32e... 7289e35... 992913c... MM	dir/sub
 ::100644 100644 100644 b414108... f4615da... 10a8a9f... MM	file0
 $
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
index 2d239fe..ac9f641 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_--summary_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
@@ -1,5 +1,5 @@
 $ git diff-tree -c --stat --summary master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master
index 226300b..c2fe6a9 100644
--- a/t/t4013/diff.diff-tree_-c_--stat_master
+++ b/t/t4013/diff.diff-tree_-c_--stat_master
@@ -1,5 +1,5 @@
 $ git diff-tree -c --stat master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
  dir/sub |    2 ++
  file0   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.diff-tree_-c_master b/t/t4013/diff.diff-tree_-c_master
index c258efe..e2d2bb2 100644
--- a/t/t4013/diff.diff-tree_-c_master
+++ b/t/t4013/diff.diff-tree_-c_master
@@ -1,5 +1,5 @@
 $ git diff-tree -c master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
 ::100644 100644 100644 cead32e925b1420c84c14cbf7cf755e7e45af8ad 7289e35bff32727c08dda207511bec138fdb9ea5 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 MM	dir/sub
 ::100644 100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f MM	file0
 $
diff --git a/t/t4013/diff.diff-tree_-p_-m_master b/t/t4013/diff.diff-tree_-p_-m_master
index 1be7215..b60bea0 100644
--- a/t/t4013/diff.diff-tree_-p_-m_master
+++ b/t/t4013/diff.diff-tree_-p_-m_master
@@ -1,5 +1,5 @@
 $ git diff-tree -p -m master
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
 diff --git a/dir/sub b/dir/sub
 index cead32e..992913c 100644
 --- a/dir/sub
@@ -21,7 +21,7 @@
 +A
 +B
 +C
-176b998f5d647cbd77a9d8acf4531e930754d16d
+59d314ad6f356dd08601a4cd5e530381da3e3c64
 diff --git a/dir/sub b/dir/sub
 index 7289e35..992913c 100644
 --- a/dir/sub
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
index a89bbbb..b4745e1 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -1,5 +1,5 @@
 $ git format-patch --attach --stdout initial..master
-From 7952a93e09bf565b5592766a438b40cd81f4846f Mon Sep 17 00:00:00 2001
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
 Subject: [PATCH] Second
@@ -11,6 +11,9 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
@@ -18,10 +21,10 @@
  3 files changed, 5 insertions(+), 3 deletions(-)
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/x-patch;
- name="7952a93e09bf565b5592766a438b40cd81f4846f.diff"
+ name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
 Content-Transfer-Encoding: 8bit
 Content-Disposition: inline;
- filename="7952a93e09bf565b5592766a438b40cd81f4846f.diff"
+ filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -57,7 +60,7 @@
 
 
 
-From 889b315013ef9f2e2f90aa0b054b267c8a557847 Mon Sep 17 00:00:00 2001
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH] Third
@@ -69,16 +72,17 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
 ---
  dir/sub |    2 ++
  file1   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/x-patch;
- name="889b315013ef9f2e2f90aa0b054b267c8a557847.diff"
+ name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
 Content-Transfer-Encoding: 8bit
 Content-Disposition: inline;
- filename="889b315013ef9f2e2f90aa0b054b267c8a557847.diff"
+ filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
@@ -116,6 +120,7 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
index 4de9091..a9d1cd3 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -1,5 +1,5 @@
 $ git format-patch --attach --stdout initial..master^
-From 7952a93e09bf565b5592766a438b40cd81f4846f Mon Sep 17 00:00:00 2001
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
 Subject: [PATCH] Second
@@ -11,6 +11,9 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
@@ -18,10 +21,10 @@
  3 files changed, 5 insertions(+), 3 deletions(-)
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/x-patch;
- name="7952a93e09bf565b5592766a438b40cd81f4846f.diff"
+ name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
 Content-Transfer-Encoding: 8bit
 Content-Disposition: inline;
- filename="7952a93e09bf565b5592766a438b40cd81f4846f.diff"
+ filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
@@ -57,7 +60,7 @@
 
 
 
-From 889b315013ef9f2e2f90aa0b054b267c8a557847 Mon Sep 17 00:00:00 2001
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH] Third
@@ -69,16 +72,17 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
 ---
  dir/sub |    2 ++
  file1   |    3 +++
  2 files changed, 5 insertions(+), 0 deletions(-)
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/x-patch;
- name="889b315013ef9f2e2f90aa0b054b267c8a557847.diff"
+ name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
 Content-Transfer-Encoding: 8bit
 Content-Disposition: inline;
- filename="889b315013ef9f2e2f90aa0b054b267c8a557847.diff"
+ filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
 
 diff --git a/dir/sub b/dir/sub
 index 8422d40..cead32e 100644
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
index 3769fa6..57b9d0b 100644
--- a/t/t4013/diff.format-patch_--attach_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -11,6 +11,7 @@
 --------------g-i-t--v-e-r-s-i-o-n
 Content-Type: text/plain; charset=UTF-8; format=fixed
 Content-Transfer-Encoding: 8bit
+
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
index b7b4e7c..c33302e 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -1,8 +1,10 @@
 $ git format-patch --stdout initial..master
-From 7952a93e09bf565b5592766a438b40cd81f4846f Mon Sep 17 00:00:00 2001
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
 Subject: [PATCH] Second
+
+This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
@@ -42,10 +44,11 @@
 g-i-t--v-e-r-s-i-o-n
 
 
-From 889b315013ef9f2e2f90aa0b054b267c8a557847 Mon Sep 17 00:00:00 2001
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH] Third
+
 ---
  dir/sub |    2 ++
  file1   |    3 +++
@@ -78,6 +81,7 @@
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH] Side
+
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
index e56dd98..03d0f96 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..master^
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -1,8 +1,10 @@
 $ git format-patch --stdout initial..master^
-From 7952a93e09bf565b5592766a438b40cd81f4846f Mon Sep 17 00:00:00 2001
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:01:00 +0000
 Subject: [PATCH] Second
+
+This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
@@ -42,10 +44,11 @@
 g-i-t--v-e-r-s-i-o-n
 
 
-From 889b315013ef9f2e2f90aa0b054b267c8a557847 Mon Sep 17 00:00:00 2001
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:02:00 +0000
 Subject: [PATCH] Third
+
 ---
  dir/sub |    2 ++
  file1   |    3 +++
diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side
index e7ddbf4..d10a465 100644
--- a/t/t4013/diff.format-patch_--stdout_initial..side
+++ b/t/t4013/diff.format-patch_--stdout_initial..side
@@ -3,6 +3,7 @@
 From: A U Thor <author@example.com>
 Date: Mon, 26 Jun 2006 00:03:00 +0000
 Subject: [PATCH] Side
+
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
index cc55376..3ceb8e7 100644
--- a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat --summary master -- dir/
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -25,7 +25,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -45,11 +45,13 @@
 +E
 +F
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
index b97969d..43d7776 100644
--- a/t/t4013/diff.log_--patch-with-stat_master
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -48,7 +48,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -78,11 +78,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
index 71a6d0f..5187a26 100644
--- a/t/t4013/diff.log_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -1,6 +1,6 @@
 $ git log --patch-with-stat master -- dir/
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -25,7 +25,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -45,11 +45,13 @@
 +E
 +F
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
index b652c6a..c964097 100644
--- a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root --cc --patch-with-stat --summary master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -81,7 +81,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -112,11 +112,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
index b24a504..ad050af 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat --summary master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -49,7 +49,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -80,11 +80,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
index 1e9bdc4..628c6c0 100644
--- a/t/t4013/diff.log_--root_--patch-with-stat_master
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -1,6 +1,6 @@
 $ git log --root --patch-with-stat master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -48,7 +48,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -78,11 +78,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
index 3a155d2..5d4e0f1 100644
--- a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git log --root -c --patch-with-stat --summary master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -81,7 +81,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -112,11 +112,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master
index 2296986..217a2eb 100644
--- a/t/t4013/diff.log_--root_-p_master
+++ b/t/t4013/diff.log_--root_-p_master
@@ -1,6 +1,6 @@
 $ git log --root -p master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -43,7 +43,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -69,11 +69,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master
index 7554a46..e17ccfc 100644
--- a/t/t4013/diff.log_--root_master
+++ b/t/t4013/diff.log_--root_master
@@ -1,6 +1,6 @@
 $ git log --root master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -12,17 +12,19 @@
 
     Side
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 commit 444ac553ac7612cc88969031b02b3767fb8a353a
 Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.log_-SF_-p_master b/t/t4013/diff.log_-SF_-p_master
index db2264c..5e32438 100644
--- a/t/t4013/diff.log_-SF_-p_master
+++ b/t/t4013/diff.log_-SF_-p_master
@@ -1,5 +1,5 @@
 $ git log -SF -p master
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-SF_master
index f307b4d..6162ed2 100644
--- a/t/t4013/diff.log_-SF_master
+++ b/t/t4013/diff.log_-SF_master
@@ -1,5 +1,5 @@
 $ git log -SF master
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master
index e82a72f..f8fefef 100644
--- a/t/t4013/diff.log_-p_master
+++ b/t/t4013/diff.log_-p_master
@@ -1,6 +1,6 @@
 $ git log -p master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -43,7 +43,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -69,11 +69,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master
index 7b86ed1..e9d9e7b 100644
--- a/t/t4013/diff.log_master
+++ b/t/t4013/diff.log_master
@@ -1,6 +1,6 @@
 $ git log master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -12,17 +12,19 @@
 
     Side
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
     Third
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 commit 444ac553ac7612cc88969031b02b3767fb8a353a
 Author: A U Thor <author@example.com>
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master
index 3772a87..9e6e1f2 100644
--- a/t/t4013/diff.show_master
+++ b/t/t4013/diff.show_master
@@ -1,6 +1,6 @@
 $ git show master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
index 054513f..6a467cc 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
@@ -18,7 +18,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -38,11 +38,13 @@
 +E
 +F
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master
index a89b573..1e1bbe1 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master
@@ -41,7 +41,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -71,11 +71,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
index b6d9752..13789f1 100644
--- a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
@@ -18,7 +18,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -38,11 +38,13 @@
 +E
 +F
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
index e9e17cd..5facf25 100644
--- a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git whatchanged --root --cc --patch-with-stat --summary master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -81,7 +81,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -112,11 +112,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
index f707bfa..0291153 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
@@ -42,7 +42,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -73,11 +73,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
index 61aca41..9b0349c 100644
--- a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
@@ -41,7 +41,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -71,11 +71,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
index 596765e..10f6767 100644
--- a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -1,6 +1,6 @@
 $ git whatchanged --root -c --patch-with-stat --summary master
-commit 176b998f5d647cbd77a9d8acf4531e930754d16d
-Merge: 889b315... c7a2ab9...
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:04:00 2006 +0000
 
@@ -81,7 +81,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -112,11 +112,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 ---
  dir/sub |    2 ++
  file0   |    3 +++
diff --git a/t/t4013/diff.whatchanged_--root_-p_master b/t/t4013/diff.whatchanged_--root_-p_master
index b4cd05e..ebf1f06 100644
--- a/t/t4013/diff.whatchanged_--root_-p_master
+++ b/t/t4013/diff.whatchanged_--root_-p_master
@@ -36,7 +36,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -62,11 +62,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_--root_master b/t/t4013/diff.whatchanged_--root_master
index 011a221..a405cb6 100644
--- a/t/t4013/diff.whatchanged_--root_master
+++ b/t/t4013/diff.whatchanged_--root_master
@@ -9,7 +9,7 @@
 :100644 100644 01e79c3... f4615da... M	file0
 :000000 100644 0000000... 7289e35... A	file3
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -18,11 +18,13 @@
 :100644 100644 8422d40... cead32e... M	dir/sub
 :000000 100644 0000000... b1e6722... A	file1
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 :100644 100644 35d242b... 8422d40... M	dir/sub
 :100644 100644 01e79c3... b414108... M	file0
diff --git a/t/t4013/diff.whatchanged_-SF_-p_master b/t/t4013/diff.whatchanged_-SF_-p_master
index 6a76f4e..f39da84 100644
--- a/t/t4013/diff.whatchanged_-SF_-p_master
+++ b/t/t4013/diff.whatchanged_-SF_-p_master
@@ -1,5 +1,5 @@
 $ git whatchanged -SF -p master
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
diff --git a/t/t4013/diff.whatchanged_-SF_master b/t/t4013/diff.whatchanged_-SF_master
index a4fe6f8..0499321 100644
--- a/t/t4013/diff.whatchanged_-SF_master
+++ b/t/t4013/diff.whatchanged_-SF_master
@@ -1,5 +1,5 @@
 $ git whatchanged -SF master
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
diff --git a/t/t4013/diff.whatchanged_-p_master b/t/t4013/diff.whatchanged_-p_master
index f9a4456..f18d432 100644
--- a/t/t4013/diff.whatchanged_-p_master
+++ b/t/t4013/diff.whatchanged_-p_master
@@ -36,7 +36,7 @@
 +1
 +2
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -62,11 +62,13 @@
 +B
 +C
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 diff --git a/dir/sub b/dir/sub
 index 35d242b..8422d40 100644
diff --git a/t/t4013/diff.whatchanged_master b/t/t4013/diff.whatchanged_master
index c22416c..cd3bcc2 100644
--- a/t/t4013/diff.whatchanged_master
+++ b/t/t4013/diff.whatchanged_master
@@ -9,7 +9,7 @@
 :100644 100644 01e79c3... f4615da... M	file0
 :000000 100644 0000000... 7289e35... A	file3
 
-commit 889b315013ef9f2e2f90aa0b054b267c8a557847
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:02:00 2006 +0000
 
@@ -18,11 +18,13 @@
 :100644 100644 8422d40... cead32e... M	dir/sub
 :000000 100644 0000000... b1e6722... A	file1
 
-commit 7952a93e09bf565b5592766a438b40cd81f4846f
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
 Author: A U Thor <author@example.com>
 Date:   Mon Jun 26 00:01:00 2006 +0000
 
     Second
+    
+    This is the second commit.
 
 :100644 100644 35d242b... 8422d40... M	dir/sub
 :100644 100644 01e79c3... b414108... M	file0
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 1dce123..b15920b 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -44,6 +44,43 @@
 G=$(doit 7 G $B $E)
 H=$(doit 8 H $A $F)
 
+# Setup for second test to demonstrate that relying on timestamps in a
+# distributed SCM to provide a _consistent_ partial ordering of commits
+# leads to insanity.
+#
+#               Relative
+# Structure     timestamps
+#
+#   PL  PR        +4  +4
+#  /  \/  \      /  \/  \
+# L2  C2  R2    +3  -1  +3
+# |   |   |     |   |   |
+# L1  C1  R1    +2  -2  +2
+# |   |   |     |   |   |
+# L0  C0  R0    +1  -3  +1
+#   \ |  /        \ |  /
+#     S             0
+#
+# The left and right chains of commits can be of any length and complexity as
+# long as all of the timestamps are greater than that of S.
+
+S=$(doit  0 S)
+
+C0=$(doit -3 C0 $S)
+C1=$(doit -2 C1 $C0)
+C2=$(doit -1 C2 $C1)
+
+L0=$(doit  1 L0 $S)
+L1=$(doit  2 L1 $L0)
+L2=$(doit  3 L2 $L1)
+
+R0=$(doit  1 R0 $S)
+R1=$(doit  2 R1 $R0)
+R2=$(doit  3 R2 $R1)
+
+PL=$(doit  4 PL $L2 $C2)
+PR=$(doit  4 PR $C2 $R2)
+
 test_expect_success 'compute merge-base (single)' \
     'MB=$(git-merge-base G H) &&
      expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
@@ -56,4 +93,12 @@
     'MB=$(git-show-branch --merge-base G H) &&
      expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
 
+test_expect_success 'compute merge-base (single)' \
+    'MB=$(git-merge-base PL PR) &&
+     expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
+test_expect_success 'compute merge-base (all)' \
+    'MB=$(git-merge-base --all PL PR) &&
+     expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
 test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index b0d7990..470a909 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -28,6 +28,7 @@
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
+unset GIT_TRACE
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
diff --git a/tag.c b/tag.c
index 74d0dab..864ac1b 100644
--- a/tag.c
+++ b/tag.c
@@ -5,7 +5,7 @@
 
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 {
-	while (o && o->type == TYPE_TAG)
+	while (o && o->type == OBJ_TAG)
 		o = parse_object(((struct tag *)o)->tagged->sha1);
 	if (!o && warn) {
 		if (!warnlen)
@@ -21,12 +21,12 @@
         if (!obj) {
                 struct tag *ret = alloc_tag_node();
                 created_object(sha1, &ret->object);
-                ret->object.type = TYPE_TAG;
+                ret->object.type = OBJ_TAG;
                 return ret;
         }
 	if (!obj->type)
-		obj->type = TYPE_TAG;
-        if (obj->type != TYPE_TAG) {
+		obj->type = OBJ_TAG;
+        if (obj->type != OBJ_TAG) {
                 error("Object %s is a %s, not a tree",
                       sha1_to_hex(sha1), typename(obj->type));
                 return NULL;
diff --git a/tree.c b/tree.c
index 1023655..a6032e3 100644
--- a/tree.c
+++ b/tree.c
@@ -131,12 +131,12 @@
 	if (!obj) {
 		struct tree *ret = alloc_tree_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_TREE;
+		ret->object.type = OBJ_TREE;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_TREE;
-	if (obj->type != TYPE_TREE) {
+		obj->type = OBJ_TREE;
+	if (obj->type != OBJ_TREE) {
 		error("Object %s is a %s, not a tree",
 		      sha1_to_hex(sha1), typename(obj->type));
 		return NULL;
@@ -216,11 +216,11 @@
 	do {
 		if (!obj)
 			return NULL;
-		if (obj->type == TYPE_TREE)
+		if (obj->type == OBJ_TREE)
 			return (struct tree *) obj;
-		else if (obj->type == TYPE_COMMIT)
+		else if (obj->type == OBJ_COMMIT)
 			obj = &(((struct commit *) obj)->tree->object);
-		else if (obj->type == TYPE_TAG)
+		else if (obj->type == OBJ_TAG)
 			obj = ((struct tag *) obj)->tagged;
 		else
 			return NULL;
diff --git a/upload-pack.c b/upload-pack.c
index b18eb9b..f6f5a7e 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -51,6 +51,10 @@
 		if (fd == 3)
 			/* emergency quit */
 			fd = 2;
+		if (fd == 2) {
+			xwrite(fd, data, sz);
+			return sz;
+		}
 		return safe_write(fd, data, sz);
 	}
 	p = data;
@@ -326,7 +330,7 @@
 			o = parse_object(sha1);
 		if (!o)
 			die("oops (%s)", sha1_to_hex(sha1));
-		if (o->type == TYPE_COMMIT) {
+		if (o->type == OBJ_COMMIT) {
 			struct commit_list *parents;
 			if (o->flags & THEY_HAVE)
 				return 0;
@@ -457,7 +461,7 @@
 		o->flags |= OUR_REF;
 		nr_our_refs++;
 	}
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, refname, 0);
 		packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 	}
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 2ce10b4..c9f8178 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -39,6 +39,7 @@
 #define XDL_PATCH_IGNOREBSPACE (1 << 8)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_COMMON (1 << 1)
 
 #define XDL_MMB_READONLY (1 << 0)
 
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index ad5bfb1..714c563 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -100,6 +100,21 @@
 }
 
 
+int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+		    xdemitconf_t const *xecfg) {
+	xdfile_t *xdf = &xe->xdf1;
+	const char *rchg = xdf->rchg;
+	long ix;
+
+	for (ix = 0; ix < xdf->nrec; ix++) {
+		if (rchg[ix])
+			continue;
+		if (xdl_emit_record(xdf, ix, "", ecb))
+			return -1;
+	}
+	return 0;
+}
+
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg) {
 	long s1, s2, e1, e2, lctx;
@@ -107,6 +122,9 @@
 	char funcbuf[40];
 	long funclen = 0;
 
+	if (xecfg->flags & XDL_EMIT_COMMON)
+		return xdl_emit_common(xe, xscr, ecb, xecfg);
+
 	for (xch = xche = xscr; xch; xch = xche->next) {
 		xche = xdl_get_hunk(xch, xecfg);