From cf0adba7885342e1bbcf0689fece9d13e39784b4 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 19 Nov 2006 13:22:44 -0800
Subject: [PATCH 001/330] Store peeled refs in packed-refs file.

This would speed up "show-ref -d" in a repository with mostly
packed tags.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-refs.c | 24 ++++++++++++---
 builtin-show-ref.c  | 36 ++++++++++++++++------
 refs.c              | 73 ++++++++++++++++++++++++++++++++++++++++-----
 refs.h              |  4 +++
 4 files changed, 117 insertions(+), 20 deletions(-)

diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
index 042d2718f9..ee5a5565af 100644
--- a/builtin-pack-refs.c
+++ b/builtin-pack-refs.c
@@ -1,5 +1,7 @@
 #include "cache.h"
 #include "refs.h"
+#include "object.h"
+#include "tag.h"
 
 static const char builtin_pack_refs_usage[] =
 "git-pack-refs [--all] [--prune]";
@@ -29,12 +31,26 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
 			  int flags, void *cb_data)
 {
 	struct pack_refs_cb_data *cb = cb_data;
+	int is_tag_ref;
 
-	if (!cb->all && strncmp(path, "refs/tags/", 10))
-		return 0;
 	/* Do not pack the symbolic refs */
-	if (!(flags & REF_ISSYMREF))
-		fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+	if ((flags & REF_ISSYMREF))
+		return 0;
+	is_tag_ref = !strncmp(path, "refs/tags/", 10);
+	if (!cb->all && !is_tag_ref)
+		return 0;
+
+	fprintf(cb->refs_file, "%s %s\n", sha1_to_hex(sha1), path);
+	if (is_tag_ref) {
+		struct object *o = parse_object(sha1);
+		if (o->type == OBJ_TAG) {
+			o = deref_tag(o, path, 0);
+			if (o)
+				fprintf(cb->refs_file, "%s  %s^{}\n",
+					sha1_to_hex(o->sha1), path);
+		}
+	}
+
 	if (cb->prune && !do_not_prune(flags)) {
 		int namelen = strlen(path) + 1;
 		struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 06ec400d7f..9ae3d08546 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -13,6 +13,7 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
 {
 	struct object *obj;
 	const char *hex;
+	unsigned char peeled[20];
 
 	if (tags_only || heads_only) {
 		int match;
@@ -44,12 +45,15 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
 
 match:
 	found_match++;
-	obj = parse_object(sha1);
-	if (!obj) {
-		if (quiet)
-			return 0;
-		die("git-show-ref: bad ref %s (%s)", refname, sha1_to_hex(sha1));
-	}
+
+	/* This changes the semantics slightly that even under quiet we
+	 * detect and return error if the repository is corrupt and
+	 * ref points at a nonexistent object.
+	 */
+	if (!has_sha1_file(sha1))
+		die("git-show-ref: bad ref %s (%s)", refname,
+		    sha1_to_hex(sha1));
+
 	if (quiet)
 		return 0;
 
@@ -58,11 +62,25 @@ match:
 		printf("%s\n", hex);
 	else
 		printf("%s %s\n", hex, refname);
-	if (deref_tags && obj->type == OBJ_TAG) {
-		obj = deref_tag(obj, refname, 0);
-		hex = find_unique_abbrev(obj->sha1, abbrev);
+
+	if (!deref_tags)
+		return 0;
+
+	if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
+		hex = find_unique_abbrev(peeled, abbrev);
 		printf("%s %s^{}\n", hex, refname);
 	}
+	else {
+		obj = parse_object(sha1);
+		if (!obj)
+			die("git-show-ref: bad ref %s (%s)", refname,
+			    sha1_to_hex(sha1));
+		if (obj->type == OBJ_TAG) {
+			obj = deref_tag(obj, refname, 0);
+			hex = find_unique_abbrev(obj->sha1, abbrev);
+			printf("%s %s^{}\n", hex, refname);
+		}
+	}
 	return 0;
 }
 
diff --git a/refs.c b/refs.c
index 0e156c5dee..75cbc0e7ef 100644
--- a/refs.c
+++ b/refs.c
@@ -1,16 +1,18 @@
 #include "refs.h"
 #include "cache.h"
+#include "object.h"
+#include "tag.h"
 
 #include <errno.h>
 
 struct ref_list {
 	struct ref_list *next;
-	unsigned char flag; /* ISSYMREF? ISPACKED? */
+	unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
 	unsigned char sha1[20];
 	char name[FLEX_ARRAY];
 };
 
-static const char *parse_ref_line(char *line, unsigned char *sha1)
+static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
 {
 	/*
 	 * 42: the answer to everything.
@@ -21,6 +23,7 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
 	 *  +1 (newline at the end of the line)
 	 */
 	int len = strlen(line) - 42;
+	int peeled = 0;
 
 	if (len <= 0)
 		return NULL;
@@ -29,11 +32,24 @@ static const char *parse_ref_line(char *line, unsigned char *sha1)
 	if (!isspace(line[40]))
 		return NULL;
 	line += 41;
-	if (isspace(*line))
-		return NULL;
+
+	if (isspace(*line)) {
+		/* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
+		line++;
+		len--;
+		peeled = 1;
+	}
 	if (line[len] != '\n')
 		return NULL;
 	line[len] = 0;
+
+	if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
+		return NULL;
+
+	if (!peeled)
+		*flag &= ~REF_ISPEELED;
+	else
+		*flag |= REF_ISPEELED;
 	return line;
 }
 
@@ -108,10 +124,12 @@ static struct ref_list *get_packed_refs(void)
 			char refline[PATH_MAX];
 			while (fgets(refline, sizeof(refline), f)) {
 				unsigned char sha1[20];
-				const char *name = parse_ref_line(refline, sha1);
+				int flag = REF_ISPACKED;
+				const char *name =
+					parse_ref_line(refline, sha1, &flag);
 				if (!name)
 					continue;
-				list = add_ref(name, sha1, REF_ISPACKED, list);
+				list = add_ref(name, sha1, flag, list);
 			}
 			fclose(f);
 			refs = list;
@@ -207,7 +225,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 		if (lstat(path, &st) < 0) {
 			struct ref_list *list = get_packed_refs();
 			while (list) {
-				if (!strcmp(ref, list->name)) {
+				if (!(list->flag & REF_ISPEELED) &&
+				    !strcmp(ref, list->name)) {
 					hashcpy(sha1, list->sha1);
 					if (flag)
 						*flag |= REF_ISPACKED;
@@ -329,6 +348,8 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 		return 0;
 	if (is_null_sha1(entry->sha1))
 		return 0;
+	if (entry->flag & REF_ISPEELED)
+		return 0;
 	if (!has_sha1_file(entry->sha1)) {
 		error("%s does not point to a valid object!", entry->name);
 		return 0;
@@ -336,6 +357,44 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 	return fn(entry->name + trim, entry->sha1, entry->flag, cb_data);
 }
 
+int peel_ref(const char *ref, unsigned char *sha1)
+{
+	int flag;
+	unsigned char base[20];
+	struct object *o;
+
+	if (!resolve_ref(ref, base, 1, &flag))
+		return -1;
+
+	if ((flag & REF_ISPACKED)) {
+		struct ref_list *list = get_packed_refs();
+		int len = strlen(ref);
+
+		while (list) {
+			if ((list->flag & REF_ISPEELED) &&
+			    !strncmp(list->name, ref, len) &&
+			    strlen(list->name) == len + 3 &&
+			    !strcmp(list->name + len, "^{}")) {
+				hashcpy(sha1, list->sha1);
+				return 0;
+			}
+			list = list->next;
+		}
+		/* older pack-refs did not leave peeled ones in */
+	}
+
+	/* otherwise ... */
+	o = parse_object(base);
+	if (o->type == OBJ_TAG) {
+		o = deref_tag(o, ref, 0);
+		if (o) {
+			hashcpy(sha1, o->sha1);
+			return 0;
+		}
+	}
+	return -1;
+}
+
 static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,
 			   void *cb_data)
 {
diff --git a/refs.h b/refs.h
index a57d43726a..40048a6919 100644
--- a/refs.h
+++ b/refs.h
@@ -16,6 +16,8 @@ struct ref_lock {
  */
 #define REF_ISSYMREF 01
 #define REF_ISPACKED 02
+#define REF_ISPEELED 04 /* internal use */
+
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);
@@ -23,6 +25,8 @@ extern int for_each_tag_ref(each_ref_fn, void *);
 extern int for_each_branch_ref(each_ref_fn, void *);
 extern int for_each_remote_ref(each_ref_fn, void *);
 
+extern int peel_ref(const char *, unsigned char *);
+
 /** Reads the refs file specified into sha1 **/
 extern int get_ref_sha1(const char *ref, unsigned char *sha1);
 

From b8ec59234ba2c1833e29eece9ed87f7a471cbae2 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 22 Oct 2006 13:23:31 +0200
Subject: [PATCH 002/330] Build in shortlog

[jc: with minimum squelching of compiler warning under "-pedantic"
 compilation options.]

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-shortlog.txt |   1 +
 Makefile                       |   3 +-
 builtin-shortlog.c             | 302 +++++++++++++++++++++++++++++++++
 builtin.h                      |   1 +
 git-shortlog.perl              | 234 -------------------------
 git.c                          |   1 +
 6 files changed, 307 insertions(+), 235 deletions(-)
 create mode 100644 builtin-shortlog.c
 delete mode 100755 git-shortlog.perl

diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index d54fc3e5c6..95fa9010c1 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -8,6 +8,7 @@ git-shortlog - Summarize 'git log' output
 SYNOPSIS
 --------
 git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s]
+git-shortlog [-n|--number] [-s|--summary] [<committish>...]
 
 DESCRIPTION
 -----------
diff --git a/Makefile b/Makefile
index 36ce8cd606..6fd28b0cd3 100644
--- a/Makefile
+++ b/Makefile
@@ -174,7 +174,7 @@ SCRIPT_SH = \
 
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-shortlog.perl git-rerere.perl \
+	git-rerere.perl \
 	git-cvsserver.perl \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
@@ -300,6 +300,7 @@ BUILTIN_OBJS = \
 	builtin-rev-parse.o \
 	builtin-rm.o \
 	builtin-runstatus.o \
+	builtin-shortlog.o \
 	builtin-show-branch.o \
 	builtin-stripspace.o \
 	builtin-symbolic-ref.o \
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
new file mode 100644
index 0000000000..48a2a0b0d3
--- /dev/null
+++ b/builtin-shortlog.c
@@ -0,0 +1,302 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "path-list.h"
+#include "revision.h"
+#include <string.h>
+
+static const char shortlog_usage[] =
+"git-shortlog [-n] [-s] [<commit-id>... ]\n";
+
+static int compare_by_number(const void *a1, const void *a2)
+{
+	const struct path_list_item *i1 = a1, *i2 = a2;
+	const struct path_list *l1 = i1->util, *l2 = i2->util;
+
+	if (l1->nr < l2->nr)
+		return -1;
+	else if (l1->nr == l2->nr)
+		return 0;
+	else
+		return +1;
+}
+
+static struct path_list_item mailmap_list[] = {
+	{ "R.Marek@sh.cvut.cz", (void*)"Rudolf Marek" },
+	{ "Ralf.Wildenhues@gmx.de", (void*)"Ralf Wildenhues" },
+	{ "aherrman@de.ibm.com", (void*)"Andreas Herrmann" },
+	{ "akpm@osdl.org", (void*)"Andrew Morton" },
+	{ "andrew.vasquez@qlogic.com", (void*)"Andrew Vasquez" },
+	{ "aquynh@gmail.com", (void*)"Nguyen Anh Quynh" },
+	{ "axboe@suse.de", (void*)"Jens Axboe" },
+	{ "blaisorblade@yahoo.it", (void*)"Paolo 'Blaisorblade' Giarrusso" },
+	{ "bunk@stusta.de", (void*)"Adrian Bunk" },
+	{ "domen@coderock.org", (void*)"Domen Puncer" },
+	{ "dougg@torque.net", (void*)"Douglas Gilbert" },
+	{ "dwmw2@shinybook.infradead.org", (void*)"David Woodhouse" },
+	{ "ecashin@coraid.com", (void*)"Ed L Cashin" },
+	{ "felix@derklecks.de", (void*)"Felix Moeller" },
+	{ "fzago@systemfabricworks.com", (void*)"Frank Zago" },
+	{ "gregkh@suse.de", (void*)"Greg Kroah-Hartman" },
+	{ "hch@lst.de", (void*)"Christoph Hellwig" },
+	{ "htejun@gmail.com", (void*)"Tejun Heo" },
+	{ "jejb@mulgrave.(none)", (void*)"James Bottomley" },
+	{ "jejb@titanic.il.steeleye.com", (void*)"James Bottomley" },
+	{ "jgarzik@pretzel.yyz.us", (void*)"Jeff Garzik" },
+	{ "johnpol@2ka.mipt.ru", (void*)"Evgeniy Polyakov" },
+	{ "kay.sievers@vrfy.org", (void*)"Kay Sievers" },
+	{ "minyard@acm.org", (void*)"Corey Minyard" },
+	{ "mshah@teja.com", (void*)"Mitesh shah" },
+	{ "pj@ludd.ltu.se", (void*)"Peter A Jonsson" },
+	{ "rmps@joel.ist.utl.pt", (void*)"Rui Saraiva" },
+	{ "santtu.hyrkko@gmail.com", (void*)"Santtu Hyrkk,Av(B" },
+	{ "simon@thekelleys.org.uk", (void*)"Simon Kelley" },
+	{ "ssant@in.ibm.com", (void*)"Sachin P Sant" },
+	{ "terra@gnome.org", (void*)"Morten Welinder" },
+	{ "tony.luck@intel.com", (void*)"Tony Luck" },
+	{ "welinder@anemone.rentec.com", (void*)"Morten Welinder" },
+	{ "welinder@darter.rentec.com", (void*)"Morten Welinder" },
+	{ "welinder@troll.com", (void*)"Morten Welinder" }
+};
+
+static struct path_list mailmap = {
+	mailmap_list,
+	sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
+};
+
+static int map_email(char *email, char *name, int maxlen)
+{
+	char *p;
+	struct path_list_item *item;
+
+	/* autocomplete common developers */
+	p = strchr(email, '>');
+	if (!p)
+		return 0;
+
+	*p = '\0';
+	item = path_list_lookup(email, &mailmap);
+	if (item != NULL) {
+		const char *realname = (const char *)item->util;
+		strncpy(name, realname, maxlen);
+		return 1;
+	}
+	return 0;
+}
+
+static void insert_author_oneline(struct path_list *list,
+		const char *author, int authorlen,
+		const char *oneline, int onelinelen)
+{
+	const char *dot3 = "/pub/scm/linux/kernel/git/";
+	char *buffer, *p;
+	struct path_list_item *item;
+	struct path_list *onelines;
+
+	while (authorlen > 0 && isspace(author[authorlen - 1]))
+		authorlen--;
+
+	buffer = xmalloc(authorlen + 1);
+	memcpy(buffer, author, authorlen);
+	buffer[authorlen] = '\0';
+
+	item = path_list_insert(buffer, list);
+	if (item->util == NULL)
+		item->util = xcalloc(1, sizeof(struct path_list));
+	else
+		free(buffer);
+
+	if (!strncmp(oneline, "[PATCH", 6)) {
+		char *eob = strchr(buffer, ']');
+
+		while (isspace(eob[1]) && eob[1] != '\n')
+			eob++;
+		if (eob - oneline < onelinelen) {
+			onelinelen -= eob - oneline;
+			oneline = eob;
+		}
+	}
+
+	while (onelinelen > 0 && isspace(oneline[0])) {
+		oneline++;
+		onelinelen--;
+	}
+
+	while (onelinelen > 0 && isspace(oneline[onelinelen - 1]))
+		onelinelen--;
+
+	buffer = xmalloc(onelinelen + 1);
+	memcpy(buffer, oneline, onelinelen);
+	buffer[onelinelen] = '\0';
+
+	while ((p = strstr(buffer, dot3)) != NULL) {
+		memcpy(p, "...", 3);
+		strcpy(p + 2, p + sizeof(dot3) - 1);
+	}
+
+
+	onelines = item->util;
+	if (onelines->nr >= onelines->alloc) {
+		onelines->alloc = alloc_nr(onelines->nr);
+		onelines->items = xrealloc(onelines->items,
+				onelines->alloc
+				* sizeof(struct path_list_item));
+	}
+
+	onelines->items[onelines->nr].util = NULL;
+	onelines->items[onelines->nr++].path = buffer;
+}
+
+static void read_from_stdin(struct path_list *list)
+{
+	char buffer[1024];
+
+	while (fgets(buffer, sizeof(buffer), stdin) != NULL) {
+		char *bob;
+		if ((buffer[0] == 'A' || buffer[0] == 'a') &&
+				!strncmp(buffer + 1, "uthor: ", 7) &&
+				(bob = strchr(buffer + 7, '<')) != NULL) {
+			char buffer2[1024], offset = 0;
+
+			if (map_email(bob + 1, buffer, sizeof(buffer)))
+				bob = buffer + strlen(buffer);
+			else {
+				offset = 8;
+				while (isspace(bob[-1]))
+					bob--;
+			}
+
+			while (fgets(buffer2, sizeof(buffer2), stdin) &&
+					buffer2[0] != '\n')
+				; /* chomp input */
+			if (fgets(buffer2, sizeof(buffer2), stdin))
+				insert_author_oneline(list,
+						buffer + offset,
+						bob - buffer - offset,
+						buffer2, strlen(buffer2));
+		}
+	}
+}
+
+static void get_from_rev(struct rev_info *rev, struct path_list *list)
+{
+	char scratch[1024];
+	struct commit *commit;
+
+	prepare_revision_walk(rev);
+	while ((commit = get_revision(rev)) != NULL) {
+		char *author = NULL, *oneline, *buffer;
+		int authorlen = authorlen, onelinelen;
+
+		/* get author and oneline */
+		for (buffer = commit->buffer; buffer && *buffer != '\0' &&
+				*buffer != '\n'; ) {
+			char *eol = strchr(buffer, '\n');
+
+			if (eol == NULL)
+				eol = buffer + strlen(buffer);
+			else
+				eol++;
+
+			if (!strncmp(buffer, "author ", 7)) {
+				char *bracket = strchr(buffer, '<');
+
+				if (bracket == NULL || bracket > eol)
+					die("Invalid commit buffer: %s",
+					    sha1_to_hex(commit->object.sha1));
+
+				if (map_email(bracket + 1, scratch,
+							sizeof(scratch))) {
+					author = scratch;
+					authorlen = strlen(scratch);
+				} else {
+					while (bracket[-1] == ' ')
+						bracket--;
+
+					author = buffer + 7;
+					authorlen = bracket - buffer - 7;
+				}
+			}
+			buffer = eol;
+		}
+
+		if (author == NULL)
+			die ("Missing author: %s",
+					sha1_to_hex(commit->object.sha1));
+
+		if (buffer == NULL || *buffer == '\0') {
+			oneline = "<none>";
+			onelinelen = sizeof(oneline) + 1;
+		} else {
+			char *eol;
+
+			oneline = buffer + 1;
+			eol = strchr(oneline, '\n');
+			if (eol == NULL)
+				onelinelen = strlen(oneline);
+			else
+				onelinelen = eol - oneline;
+		}
+
+		insert_author_oneline(list,
+				author, authorlen, oneline, onelinelen);
+	}
+
+}
+
+int cmd_shortlog(int argc, const char **argv, const char *prefix)
+{
+	struct rev_info rev;
+	struct path_list list = { NULL, 0, 0, 1 };
+	int i, j, sort_by_number = 0, summary = 0;
+
+	init_revisions(&rev, prefix);
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	while (argc > 1) {
+		if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
+			sort_by_number = 1;
+		else if (!strcmp(argv[1], "-s") ||
+				!strcmp(argv[1], "--summary"))
+			summary = 1;
+		else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+			usage(shortlog_usage);
+		else
+			die ("unrecognized argument: %s", argv[1]);
+		argv++;
+		argc--;
+	}
+
+	if (rev.pending.nr == 1)
+		die ("Need a range!");
+	else if (rev.pending.nr == 0)
+		read_from_stdin(&list);
+	else
+		get_from_rev(&rev, &list);
+
+	if (sort_by_number)
+		qsort(list.items, sizeof(struct path_list_item), list.nr,
+			compare_by_number);
+
+	for (i = 0; i < list.nr; i++) {
+		struct path_list *onelines = list.items[i].util;
+
+		printf("%s (%d):\n", list.items[i].path, onelines->nr);
+		if (!summary) {
+			for (j = onelines->nr - 1; j >= 0; j--)
+				printf("      %s\n", onelines->items[j].path);
+			printf("\n");
+		}
+
+		onelines->strdup_paths = 1;
+		path_list_clear(onelines, 1);
+		free(onelines);
+		list.items[i].util = NULL;
+	}
+
+	list.strdup_paths = 1;
+	path_list_clear(&list, 1);
+
+	return 0;
+}
+
diff --git a/builtin.h b/builtin.h
index 43fed329ba..b5116f30e4 100644
--- a/builtin.h
+++ b/builtin.h
@@ -55,6 +55,7 @@ extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
 extern int cmd_runstatus(int argc, const char **argv, const char *prefix);
+extern int cmd_shortlog(int argc, const char **argv, const char *prefix);
 extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
diff --git a/git-shortlog.perl b/git-shortlog.perl
deleted file mode 100755
index 334fec7477..0000000000
--- a/git-shortlog.perl
+++ /dev/null
@@ -1,234 +0,0 @@
-#!/usr/bin/perl -w
-
-use strict;
-use Getopt::Std;
-use File::Basename qw(basename dirname);
-
-our ($opt_h, $opt_n, $opt_s);
-getopts('hns');
-
-$opt_h && usage();
-
-sub usage {
-	print STDERR "Usage: ${\basename $0} [-h] [-n] [-s] < <log_data>\n";
-        exit(1);
-}
-
-my (%mailmap);
-my (%email);
-my (%map);
-my $pstate = 1;
-my $n_records = 0;
-my $n_output = 0;
-
-sub shortlog_entry($$) {
-	my ($name, $desc) = @_;
-	my $key = $name;
-
-	$desc =~ s#/pub/scm/linux/kernel/git/#/.../#g;
-	$desc =~ s#\[PATCH\] ##g;
-
-	# store description in array, in email->{desc list} map
-	if (exists $map{$key}) {
-		# grab ref
-		my $obj = $map{$key};
-
-		# add desc to array
-		push(@$obj, $desc);
-	} else {
-		# create new array, containing 1 item
-		my @arr = ($desc);
-
-		# store ref to array
-		$map{$key} = \@arr;
-	}
-}
-
-# sort comparison function
-sub by_name($$) {
-	my ($a, $b) = @_;
-
-	uc($a) cmp uc($b);
-}
-sub by_nbentries($$) {
-	my ($a, $b) = @_;
-	my $a_entries = $map{$a};
-	my $b_entries = $map{$b};
-
-	@$b_entries - @$a_entries || by_name $a, $b;
-}
-
-my $sort_method = $opt_n ? \&by_nbentries : \&by_name;
-
-sub summary_output {
-	my ($obj, $num, $key);
-
-	foreach $key (sort $sort_method keys %map) {
-		$obj = $map{$key};
-		$num = @$obj;
-		printf "%s: %u\n", $key, $num;
-		$n_output += $num;
-	}
-}
-
-sub shortlog_output {
-	my ($obj, $num, $key, $desc);
-
-	foreach $key (sort $sort_method keys %map) {
-		$obj = $map{$key};
-		$num = @$obj;
-
-		# output author
-		printf "%s (%u):\n", $key, $num;
-
-		# output author's 1-line summaries
-		foreach $desc (reverse @$obj) {
-			print "  $desc\n";
-			$n_output++;
-		}
-
-		# blank line separating author from next author
-		print "\n";
-	}
-}
-
-sub changelog_input {
-	my ($author, $desc);
-
-	while (<>) {
-		# get author and email
-		if ($pstate == 1) {
-			my ($email);
-
-			next unless /^[Aa]uthor:?\s*(.*?)\s*<(.*)>/;
-
-			$n_records++;
-
-			$author = $1;
-			$email = $2;
-			$desc = undef;
-
-			# cset author fixups
-			if (exists $mailmap{$email}) {
-				$author = $mailmap{$email};
-			} elsif (exists $mailmap{$author}) {
-				$author = $mailmap{$author};
-			} elsif (!$author) {
-				$author = $email;
-			}
-			$email{$author}{$email}++;
-			$pstate++;
-		}
-
-		# skip to blank line
-		elsif ($pstate == 2) {
-			next unless /^\s*$/;
-			$pstate++;
-		}
-
-		# skip to non-blank line
-		elsif ($pstate == 3) {
-			next unless /^\s*?(.*)/;
-
-			# skip lines that are obviously not
-			# a 1-line cset description
-			next if /^\s*From: /;
-
-			chomp;
-			$desc = $1;
-
-			&shortlog_entry($author, $desc);
-
-			$pstate = 1;
-		}
-	
-		else {
-			die "invalid parse state $pstate";
-		}
-	}
-}
-
-sub read_mailmap {
-	my ($fh, $mailmap) = @_;
-	while (<$fh>) {
-		chomp;
-		if (/^([^#].*?)\s*<(.*)>/) {
-			$mailmap->{$2} = $1;
-		}
-	}
-}
-
-sub setup_mailmap {
-	read_mailmap(\*DATA, \%mailmap);
-	if (-f '.mailmap') {
-		my $fh = undef;
-		open $fh, '<', '.mailmap';
-		read_mailmap($fh, \%mailmap);
-		close $fh;
-	}
-}
-
-sub finalize {
-	#print "\n$n_records records parsed.\n";
-
-	if ($n_records != $n_output) {
-		die "parse error: input records != output records\n";
-	}
-	if (0) {
-		for my $author (sort keys %email) {
-			my $e = $email{$author};
-			for my $email (sort keys %$e) {
-				print STDERR "$author <$email>\n";
-			}
-		}
-	}
-}
-
-&setup_mailmap;
-&changelog_input;
-$opt_s ? &summary_output : &shortlog_output;
-&finalize;
-exit(0);
-
-
-__DATA__
-#
-# Even with git, we don't always have name translations.
-# So have an email->real name table to translate the
-# (hopefully few) missing names
-#
-Adrian Bunk <bunk@stusta.de>
-Andreas Herrmann <aherrman@de.ibm.com>
-Andrew Morton <akpm@osdl.org>
-Andrew Vasquez <andrew.vasquez@qlogic.com>
-Christoph Hellwig <hch@lst.de>
-Corey Minyard <minyard@acm.org>
-David Woodhouse <dwmw2@shinybook.infradead.org>
-Domen Puncer <domen@coderock.org>
-Douglas Gilbert <dougg@torque.net>
-Ed L Cashin <ecashin@coraid.com>
-Evgeniy Polyakov <johnpol@2ka.mipt.ru>
-Felix Moeller <felix@derklecks.de>
-Frank Zago <fzago@systemfabricworks.com>
-Greg Kroah-Hartman <gregkh@suse.de>
-James Bottomley <jejb@mulgrave.(none)>
-James Bottomley <jejb@titanic.il.steeleye.com>
-Jeff Garzik <jgarzik@pretzel.yyz.us>
-Jens Axboe <axboe@suse.de>
-Kay Sievers <kay.sievers@vrfy.org>
-Mitesh shah <mshah@teja.com>
-Morten Welinder <terra@gnome.org>
-Morten Welinder <welinder@anemone.rentec.com>
-Morten Welinder <welinder@darter.rentec.com>
-Morten Welinder <welinder@troll.com>
-Nguyen Anh Quynh <aquynh@gmail.com>
-Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
-Peter A Jonsson <pj@ludd.ltu.se>
-Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
-Rudolf Marek <R.Marek@sh.cvut.cz>
-Rui Saraiva <rmps@joel.ist.utl.pt>
-Sachin P Sant <ssant@in.ibm.com>
-Santtu Hyrkk,Av(B <santtu.hyrkko@gmail.com>
-Simon Kelley <simon@thekelleys.org.uk>
-Tejun Heo <htejun@gmail.com>
-Tony Luck <tony.luck@intel.com>
diff --git a/git.c b/git.c
index 1aa07a5164..f97de602d2 100644
--- a/git.c
+++ b/git.c
@@ -260,6 +260,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "runstatus", cmd_runstatus, RUN_SETUP },
+		{ "shortlog", cmd_shortlog, RUN_SETUP },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
 		{ "stripspace", cmd_stripspace },

From 72019cdefeb6b8fd7e8bff37b9c087302a45e29e Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 19 Nov 2006 17:28:25 +0100
Subject: [PATCH 003/330] shortlog: do not crash on parsing "[PATCH"

Annoyingly, it looked for the closing bracket in the author name
instead of in the message, and then accessed the NULL pointer.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 48a2a0b0d3..26212b009c 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -108,13 +108,15 @@ static void insert_author_oneline(struct path_list *list,
 		free(buffer);
 
 	if (!strncmp(oneline, "[PATCH", 6)) {
-		char *eob = strchr(buffer, ']');
+		char *eob = strchr(oneline, ']');
 
-		while (isspace(eob[1]) && eob[1] != '\n')
-			eob++;
-		if (eob - oneline < onelinelen) {
-			onelinelen -= eob - oneline;
-			oneline = eob;
+		if (eob) {
+			while (isspace(eob[1]) && eob[1] != '\n')
+				eob++;
+			if (eob - oneline < onelinelen) {
+				onelinelen -= eob - oneline;
+				oneline = eob;
+			}
 		}
 	}
 

From d8e812502f1c72f5ef542de7eb05874e27f2b086 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 19 Nov 2006 17:28:51 +0100
Subject: [PATCH 004/330] shortlog: read mailmap from ./.mailmap again

While at it, remove the linux specific mailmap into
contrib/mailmap.linux.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c    | 79 +++++++++++++++++++++----------------------
 contrib/mailmap.linux | 40 ++++++++++++++++++++++
 2 files changed, 78 insertions(+), 41 deletions(-)
 create mode 100644 contrib/mailmap.linux

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 26212b009c..afc945663d 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -22,48 +22,40 @@ static int compare_by_number(const void *a1, const void *a2)
 		return +1;
 }
 
-static struct path_list_item mailmap_list[] = {
-	{ "R.Marek@sh.cvut.cz", (void*)"Rudolf Marek" },
-	{ "Ralf.Wildenhues@gmx.de", (void*)"Ralf Wildenhues" },
-	{ "aherrman@de.ibm.com", (void*)"Andreas Herrmann" },
-	{ "akpm@osdl.org", (void*)"Andrew Morton" },
-	{ "andrew.vasquez@qlogic.com", (void*)"Andrew Vasquez" },
-	{ "aquynh@gmail.com", (void*)"Nguyen Anh Quynh" },
-	{ "axboe@suse.de", (void*)"Jens Axboe" },
-	{ "blaisorblade@yahoo.it", (void*)"Paolo 'Blaisorblade' Giarrusso" },
-	{ "bunk@stusta.de", (void*)"Adrian Bunk" },
-	{ "domen@coderock.org", (void*)"Domen Puncer" },
-	{ "dougg@torque.net", (void*)"Douglas Gilbert" },
-	{ "dwmw2@shinybook.infradead.org", (void*)"David Woodhouse" },
-	{ "ecashin@coraid.com", (void*)"Ed L Cashin" },
-	{ "felix@derklecks.de", (void*)"Felix Moeller" },
-	{ "fzago@systemfabricworks.com", (void*)"Frank Zago" },
-	{ "gregkh@suse.de", (void*)"Greg Kroah-Hartman" },
-	{ "hch@lst.de", (void*)"Christoph Hellwig" },
-	{ "htejun@gmail.com", (void*)"Tejun Heo" },
-	{ "jejb@mulgrave.(none)", (void*)"James Bottomley" },
-	{ "jejb@titanic.il.steeleye.com", (void*)"James Bottomley" },
-	{ "jgarzik@pretzel.yyz.us", (void*)"Jeff Garzik" },
-	{ "johnpol@2ka.mipt.ru", (void*)"Evgeniy Polyakov" },
-	{ "kay.sievers@vrfy.org", (void*)"Kay Sievers" },
-	{ "minyard@acm.org", (void*)"Corey Minyard" },
-	{ "mshah@teja.com", (void*)"Mitesh shah" },
-	{ "pj@ludd.ltu.se", (void*)"Peter A Jonsson" },
-	{ "rmps@joel.ist.utl.pt", (void*)"Rui Saraiva" },
-	{ "santtu.hyrkko@gmail.com", (void*)"Santtu Hyrkk,Av(B" },
-	{ "simon@thekelleys.org.uk", (void*)"Simon Kelley" },
-	{ "ssant@in.ibm.com", (void*)"Sachin P Sant" },
-	{ "terra@gnome.org", (void*)"Morten Welinder" },
-	{ "tony.luck@intel.com", (void*)"Tony Luck" },
-	{ "welinder@anemone.rentec.com", (void*)"Morten Welinder" },
-	{ "welinder@darter.rentec.com", (void*)"Morten Welinder" },
-	{ "welinder@troll.com", (void*)"Morten Welinder" }
-};
+static struct path_list mailmap = {NULL, 0, 0, 0};
 
-static struct path_list mailmap = {
-	mailmap_list,
-	sizeof(mailmap_list) / sizeof(struct path_list_item), 0, 0
-};
+static int read_mailmap(const char *filename)
+{
+	char buffer[1024];
+	FILE *f = fopen(filename, "r");
+
+	if (f == NULL)
+		return 1;
+	while (fgets(buffer, sizeof(buffer), f) != NULL) {
+		char *end_of_name, *left_bracket, *right_bracket;
+		char *name, *email;
+		if (buffer[0] == '#')
+			continue;
+		if ((left_bracket = strchr(buffer, '<')) == NULL)
+			continue;
+		if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
+			continue;
+		if (right_bracket == left_bracket + 1)
+			continue;
+		for (end_of_name = left_bracket; end_of_name != buffer
+				&& isspace(end_of_name[-1]); end_of_name--)
+			/* keep on looking */
+		if (end_of_name == buffer)
+			continue;
+		name = xmalloc(end_of_name - buffer + 1);
+		strlcpy(name, buffer, end_of_name - buffer + 1);
+		email = xmalloc(right_bracket - left_bracket);
+		strlcpy(email, left_bracket + 1, right_bracket - left_bracket);
+		path_list_insert(email, &mailmap)->util = name;
+	}
+	fclose(f);
+	return 0;
+}
 
 static int map_email(char *email, char *name, int maxlen)
 {
@@ -269,6 +261,9 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 		argc--;
 	}
 
+	if (!access(".mailmap", R_OK))
+		read_mailmap(".mailmap");
+
 	if (rev.pending.nr == 1)
 		die ("Need a range!");
 	else if (rev.pending.nr == 0)
@@ -298,6 +293,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 
 	list.strdup_paths = 1;
 	path_list_clear(&list, 1);
+	mailmap.strdup_paths = 1;
+	path_list_clear(&mailmap, 1);
 
 	return 0;
 }
diff --git a/contrib/mailmap.linux b/contrib/mailmap.linux
new file mode 100644
index 0000000000..83927c94ee
--- /dev/null
+++ b/contrib/mailmap.linux
@@ -0,0 +1,40 @@
+#
+# Even with git, we don't always have name translations.
+# So have an email->real name table to translate the
+# (hopefully few) missing names
+#
+Adrian Bunk <bunk@stusta.de>
+Andreas Herrmann <aherrman@de.ibm.com>
+Andrew Morton <akpm@osdl.org>
+Andrew Vasquez <andrew.vasquez@qlogic.com>
+Christoph Hellwig <hch@lst.de>
+Corey Minyard <minyard@acm.org>
+David Woodhouse <dwmw2@shinybook.infradead.org>
+Domen Puncer <domen@coderock.org>
+Douglas Gilbert <dougg@torque.net>
+Ed L Cashin <ecashin@coraid.com>
+Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+Felix Moeller <felix@derklecks.de>
+Frank Zago <fzago@systemfabricworks.com>
+Greg Kroah-Hartman <gregkh@suse.de>
+James Bottomley <jejb@mulgrave.(none)>
+James Bottomley <jejb@titanic.il.steeleye.com>
+Jeff Garzik <jgarzik@pretzel.yyz.us>
+Jens Axboe <axboe@suse.de>
+Kay Sievers <kay.sievers@vrfy.org>
+Mitesh shah <mshah@teja.com>
+Morten Welinder <terra@gnome.org>
+Morten Welinder <welinder@anemone.rentec.com>
+Morten Welinder <welinder@darter.rentec.com>
+Morten Welinder <welinder@troll.com>
+Nguyen Anh Quynh <aquynh@gmail.com>
+Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
+Peter A Jonsson <pj@ludd.ltu.se>
+Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
+Rudolf Marek <R.Marek@sh.cvut.cz>
+Rui Saraiva <rmps@joel.ist.utl.pt>
+Sachin P Sant <ssant@in.ibm.com>
+Santtu Hyrkk,Av(B <santtu.hyrkko@gmail.com>
+Simon Kelley <simon@thekelleys.org.uk>
+Tejun Heo <htejun@gmail.com>
+Tony Luck <tony.luck@intel.com>

From 549652361b7fea5a5e9046571c9f0bc4a7d5d6ef Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 19 Nov 2006 17:29:14 +0100
Subject: [PATCH 005/330] shortlog: handle email addresses case-insensitively

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index afc945663d..4775c110ff 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -34,6 +34,7 @@ static int read_mailmap(const char *filename)
 	while (fgets(buffer, sizeof(buffer), f) != NULL) {
 		char *end_of_name, *left_bracket, *right_bracket;
 		char *name, *email;
+		int i;
 		if (buffer[0] == '#')
 			continue;
 		if ((left_bracket = strchr(buffer, '<')) == NULL)
@@ -50,7 +51,9 @@ static int read_mailmap(const char *filename)
 		name = xmalloc(end_of_name - buffer + 1);
 		strlcpy(name, buffer, end_of_name - buffer + 1);
 		email = xmalloc(right_bracket - left_bracket);
-		strlcpy(email, left_bracket + 1, right_bracket - left_bracket);
+		for (i = 0; i < right_bracket - left_bracket - 1; i++)
+			email[i] = tolower(left_bracket[i + 1]);
+		email[right_bracket - left_bracket - 1] = '\0';
 		path_list_insert(email, &mailmap)->util = name;
 	}
 	fclose(f);
@@ -68,6 +71,9 @@ static int map_email(char *email, char *name, int maxlen)
 		return 0;
 
 	*p = '\0';
+	/* downcase the email address */
+	for (p = email; *p; p++)
+		*p = tolower(*p);
 	item = path_list_lookup(email, &mailmap);
 	if (item != NULL) {
 		const char *realname = (const char *)item->util;

From 6d6ab6104a5055b9f66cc9a80d55d2ef59d0763c Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 21 Nov 2006 21:12:06 +0100
Subject: [PATCH 006/330] shortlog: fix "-n"

Since it is now a builtin optionally taking a range, we have to parse
the options before the rev machinery, to be able to shadow the short
hand "-n" for "--max-count".

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 4775c110ff..1456e1a191 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -15,11 +15,11 @@ static int compare_by_number(const void *a1, const void *a2)
 	const struct path_list *l1 = i1->util, *l2 = i2->util;
 
 	if (l1->nr < l2->nr)
-		return -1;
+		return 1;
 	else if (l1->nr == l2->nr)
 		return 0;
 	else
-		return +1;
+		return -1;
 }
 
 static struct path_list mailmap = {NULL, 0, 0, 0};
@@ -251,8 +251,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	struct path_list list = { NULL, 0, 0, 1 };
 	int i, j, sort_by_number = 0, summary = 0;
 
-	init_revisions(&rev, prefix);
-	argc = setup_revisions(argc, argv, &rev, NULL);
+	/* since -n is a shadowed rev argument, parse our args first */
 	while (argc > 1) {
 		if (!strcmp(argv[1], "-n") || !strcmp(argv[1], "--numbered"))
 			sort_by_number = 1;
@@ -262,10 +261,14 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 		else if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
 			usage(shortlog_usage);
 		else
-			die ("unrecognized argument: %s", argv[1]);
+			break;
 		argv++;
 		argc--;
 	}
+	init_revisions(&rev, prefix);
+	argc = setup_revisions(argc, argv, &rev, NULL);
+	if (argc > 1)
+		die ("unrecognized argument: %s", argv[1]);
 
 	if (!access(".mailmap", R_OK))
 		read_mailmap(".mailmap");
@@ -278,7 +281,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 		get_from_rev(&rev, &list);
 
 	if (sort_by_number)
-		qsort(list.items, sizeof(struct path_list_item), list.nr,
+		qsort(list.items, list.nr, sizeof(struct path_list_item),
 			compare_by_number);
 
 	for (i = 0; i < list.nr; i++) {

From ac60c94d74ff3341a5175ca865fd52a0a0189146 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Tue, 21 Nov 2006 15:49:45 -0500
Subject: [PATCH 007/330] builtin git-shortlog is broken

Another small patch to fix the output result to be conform with the
perl version.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Acked-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 1456e1a191..b760b477ea 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -7,7 +7,7 @@
 #include <string.h>
 
 static const char shortlog_usage[] =
-"git-shortlog [-n] [-s] [<commit-id>... ]\n";
+"git-shortlog [-n] [-s] [<commit-id>... ]";
 
 static int compare_by_number(const void *a1, const void *a2)
 {
@@ -287,8 +287,10 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	for (i = 0; i < list.nr; i++) {
 		struct path_list *onelines = list.items[i].util;
 
-		printf("%s (%d):\n", list.items[i].path, onelines->nr);
-		if (!summary) {
+		if (summary) {
+			printf("%s: %d\n", list.items[i].path, onelines->nr);
+		} else {
+			printf("%s (%d):\n", list.items[i].path, onelines->nr);
 			for (j = onelines->nr - 1; j >= 0; j--)
 				printf("      %s\n", onelines->items[j].path);
 			printf("\n");

From 7cdbff14d4823c3a3d64c2011ab0b23f794efef8 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 20 Nov 2006 00:49:31 -0800
Subject: [PATCH 008/330] remove merge-recursive-old

This frees the Porcelain-ish that comes with the core Python-free.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore                 |    1 -
 INSTALL                    |    3 -
 Makefile                   |   45 +-
 compat/subprocess.py       | 1149 ------------------------------------
 config.mak.in              |    2 -
 configure.ac               |   39 --
 git-merge-recursive-old.py |  944 -----------------------------
 git-merge.sh               |    5 +-
 git-rebase.sh              |    9 -
 git.spec.in                |    2 +-
 gitMergeCommon.py          |  275 ---------
 t/Makefile                 |    4 -
 t/t0000-basic.sh           |   14 +-
 t/test-lib.sh              |   15 +-
 14 files changed, 10 insertions(+), 2497 deletions(-)
 delete mode 100644 compat/subprocess.py
 delete mode 100755 git-merge-recursive-old.py
 delete mode 100644 gitMergeCommon.py

diff --git a/.gitignore b/.gitignore
index 4c8c8e4115..7f2cd55088 100644
--- a/.gitignore
+++ b/.gitignore
@@ -66,7 +66,6 @@ git-merge-one-file
 git-merge-ours
 git-merge-recur
 git-merge-recursive
-git-merge-recursive-old
 git-merge-resolve
 git-merge-stupid
 git-mktag
diff --git a/INSTALL b/INSTALL
index fce6bc39d5..8f69039fb9 100644
--- a/INSTALL
+++ b/INSTALL
@@ -99,9 +99,6 @@ Issues of note:
 	- "perl" and POSIX-compliant shells are needed to use most of
 	  the barebone Porcelainish scripts.
 
-	- "python" 2.3 or more recent; if you have 2.3, you may need
-          to build with "make WITH_OWN_SUBPROCESS_PY=YesPlease".
-
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
    have all the libraries/tools needed, or you may have
diff --git a/Makefile b/Makefile
index 36ce8cd606..fc30dcbbb9 100644
--- a/Makefile
+++ b/Makefile
@@ -69,8 +69,6 @@ all:
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
-# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
-#
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
 # Define NO_SOCKADDR_STORAGE if your platform does not have struct
@@ -116,7 +114,6 @@ prefix = $(HOME)
 bindir = $(prefix)/bin
 gitexecdir = $(bindir)
 template_dir = $(prefix)/share/git-core/templates/
-GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
 
 # default configuration for gitweb
@@ -135,7 +132,7 @@ GITWEB_FAVICON = git-favicon.png
 GITWEB_SITE_HEADER =
 GITWEB_SITE_FOOTER =
 
-export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
+export prefix bindir gitexecdir template_dir
 
 CC = gcc
 AR = ar
@@ -179,12 +176,8 @@ SCRIPT_PERL = \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
 
-SCRIPT_PYTHON = \
-	git-merge-recursive-old.py
-
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 	  $(patsubst %.perl,%,$(SCRIPT_PERL)) \
-	  $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
 	  git-cherry-pick git-status git-instaweb
 
 # ... and all the rest that could be moved out of bindir to gitexecdir
@@ -227,12 +220,6 @@ endif
 ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
-ifndef PYTHON_PATH
-	PYTHON_PATH = /usr/bin/python
-endif
-
-PYMODULES = \
-	gitMergeCommon.py
 
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
@@ -423,16 +410,6 @@ endif
 -include config.mak.autogen
 -include config.mak
 
-ifdef WITH_OWN_SUBPROCESS_PY
-	PYMODULES += compat/subprocess.py
-else
-	ifeq ($(NO_PYTHON),)
-		ifneq ($(shell $(PYTHON_PATH) -c 'import subprocess;print"OK"' 2>/dev/null),OK)
-			PYMODULES += compat/subprocess.py
-		endif
-	endif
-endif
-
 ifndef NO_CURL
 	ifdef CURLDIR
 		# This is still problematic -- gcc does not always want -R.
@@ -574,8 +551,6 @@ prefix_SQ = $(subst ','\'',$(prefix))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
-PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
-GIT_PYTHON_DIR_SQ = $(subst ','\'',$(GIT_PYTHON_DIR))
 
 LIBS = $(GITLIBS) $(EXTLIBS)
 
@@ -622,7 +597,6 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-	    -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
 	    $@.sh >$@+
 	chmod +x $@+
 	mv $@+ $@
@@ -644,15 +618,6 @@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
 	chmod +x $@+
 	mv $@+ $@
 
-$(patsubst %.py,%,$(SCRIPT_PYTHON)) : % : %.py GIT-CFLAGS
-	rm -f $@ $@+
-	sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
-	    -e 's|@@GIT_PYTHON_PATH@@|$(GIT_PYTHON_DIR_SQ)|g' \
-	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
-	    $@.py >$@+
-	chmod +x $@+
-	mv $@+ $@
-
 git-cherry-pick: git-revert
 	cp $< $@+
 	mv $@+ $@
@@ -689,7 +654,6 @@ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
-	    -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
 	    -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
 	    -e '/@@GITWEB_CGI@@/d' \
 	    -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
@@ -709,7 +673,6 @@ configure: configure.ac
 git$X git.spec \
 	$(patsubst %.sh,%,$(SCRIPT_SH)) \
 	$(patsubst %.perl,%,$(SCRIPT_PERL)) \
-	$(patsubst %.py,%,$(SCRIPT_PYTHON)) \
 	: GIT-VERSION-FILE
 
 %.o: %.c GIT-CFLAGS
@@ -783,7 +746,7 @@ tags:
 	find . -name '*.[hcS]' -print | xargs ctags -a
 
 ### Detect prefix changes
-TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):$(GIT_PYTHON_DIR_SQ):\
+TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
 
 GIT-CFLAGS: .FORCE-GIT-CFLAGS
@@ -799,7 +762,6 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
 # However, the environment gets quite big, and some programs have problems
 # with that.
 
-export NO_PYTHON
 export NO_SVN_TESTS
 
 test: all
@@ -834,8 +796,6 @@ install: all
 	$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 	$(MAKE) -C perl install
-	$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
-	$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
 	if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
 	then \
 		ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
@@ -922,7 +882,6 @@ check-docs::
 		case "$$v" in \
 		git-merge-octopus | git-merge-ours | git-merge-recursive | \
 		git-merge-resolve | git-merge-stupid | git-merge-recur | \
-		git-merge-recursive-old | \
 		git-ssh-pull | git-ssh-push ) continue ;; \
 		esac ; \
 		test -f "Documentation/$$v.txt" || \
diff --git a/compat/subprocess.py b/compat/subprocess.py
deleted file mode 100644
index 6474eab119..0000000000
--- a/compat/subprocess.py
+++ /dev/null
@@ -1,1149 +0,0 @@
-# subprocess - Subprocesses with accessible I/O streams
-#
-# For more information about this module, see PEP 324.
-#
-# This module should remain compatible with Python 2.2, see PEP 291.
-#
-# Copyright (c) 2003-2005 by Peter Astrand <astrand@lysator.liu.se>
-#
-# Licensed to PSF under a Contributor Agreement.
-# See http://www.python.org/2.4/license for licensing details.
-
-r"""subprocess - Subprocesses with accessible I/O streams
-
-This module allows you to spawn processes, connect to their
-input/output/error pipes, and obtain their return codes.  This module
-intends to replace several other, older modules and functions, like:
-
-os.system
-os.spawn*
-os.popen*
-popen2.*
-commands.*
-
-Information about how the subprocess module can be used to replace these
-modules and functions can be found below.
-
-
-
-Using the subprocess module
-===========================
-This module defines one class called Popen:
-
-class Popen(args, bufsize=0, executable=None,
-            stdin=None, stdout=None, stderr=None,
-            preexec_fn=None, close_fds=False, shell=False,
-            cwd=None, env=None, universal_newlines=False,
-            startupinfo=None, creationflags=0):
-
-
-Arguments are:
-
-args should be a string, or a sequence of program arguments.  The
-program to execute is normally the first item in the args sequence or
-string, but can be explicitly set by using the executable argument.
-
-On UNIX, with shell=False (default): In this case, the Popen class
-uses os.execvp() to execute the child program.  args should normally
-be a sequence.  A string will be treated as a sequence with the string
-as the only item (the program to execute).
-
-On UNIX, with shell=True: If args is a string, it specifies the
-command string to execute through the shell.  If args is a sequence,
-the first item specifies the command string, and any additional items
-will be treated as additional shell arguments.
-
-On Windows: the Popen class uses CreateProcess() to execute the child
-program, which operates on strings.  If args is a sequence, it will be
-converted to a string using the list2cmdline method.  Please note that
-not all MS Windows applications interpret the command line the same
-way: The list2cmdline is designed for applications using the same
-rules as the MS C runtime.
-
-bufsize, if given, has the same meaning as the corresponding argument
-to the built-in open() function: 0 means unbuffered, 1 means line
-buffered, any other positive value means use a buffer of
-(approximately) that size.  A negative bufsize means to use the system
-default, which usually means fully buffered.  The default value for
-bufsize is 0 (unbuffered).
-
-stdin, stdout and stderr specify the executed programs' standard
-input, standard output and standard error file handles, respectively.
-Valid values are PIPE, an existing file descriptor (a positive
-integer), an existing file object, and None.  PIPE indicates that a
-new pipe to the child should be created.  With None, no redirection
-will occur; the child's file handles will be inherited from the
-parent.  Additionally, stderr can be STDOUT, which indicates that the
-stderr data from the applications should be captured into the same
-file handle as for stdout.
-
-If preexec_fn is set to a callable object, this object will be called
-in the child process just before the child is executed.
-
-If close_fds is true, all file descriptors except 0, 1 and 2 will be
-closed before the child process is executed.
-
-if shell is true, the specified command will be executed through the
-shell.
-
-If cwd is not None, the current directory will be changed to cwd
-before the child is executed.
-
-If env is not None, it defines the environment variables for the new
-process.
-
-If universal_newlines is true, the file objects stdout and stderr are
-opened as a text files, but lines may be terminated by any of '\n',
-the Unix end-of-line convention, '\r', the Macintosh convention or
-'\r\n', the Windows convention.  All of these external representations
-are seen as '\n' by the Python program.  Note: This feature is only
-available if Python is built with universal newline support (the
-default).  Also, the newlines attribute of the file objects stdout,
-stdin and stderr are not updated by the communicate() method.
-
-The startupinfo and creationflags, if given, will be passed to the
-underlying CreateProcess() function.  They can specify things such as
-appearance of the main window and priority for the new process.
-(Windows only)
-
-
-This module also defines two shortcut functions:
-
-call(*args, **kwargs):
-    Run command with arguments.  Wait for command to complete, then
-    return the returncode attribute.
-
-    The arguments are the same as for the Popen constructor.  Example:
-
-    retcode = call(["ls", "-l"])
-
-
-Exceptions
-----------
-Exceptions raised in the child process, before the new program has
-started to execute, will be re-raised in the parent.  Additionally,
-the exception object will have one extra attribute called
-'child_traceback', which is a string containing traceback information
-from the childs point of view.
-
-The most common exception raised is OSError.  This occurs, for
-example, when trying to execute a non-existent file.  Applications
-should prepare for OSErrors.
-
-A ValueError will be raised if Popen is called with invalid arguments.
-
-
-Security
---------
-Unlike some other popen functions, this implementation will never call
-/bin/sh implicitly.  This means that all characters, including shell
-metacharacters, can safely be passed to child processes.
-
-
-Popen objects
-=============
-Instances of the Popen class have the following methods:
-
-poll()
-    Check if child process has terminated.  Returns returncode
-    attribute.
-
-wait()
-    Wait for child process to terminate.  Returns returncode attribute.
-
-communicate(input=None)
-    Interact with process: Send data to stdin.  Read data from stdout
-    and stderr, until end-of-file is reached.  Wait for process to
-    terminate.  The optional stdin argument should be a string to be
-    sent to the child process, or None, if no data should be sent to
-    the child.
-
-    communicate() returns a tuple (stdout, stderr).
-
-    Note: The data read is buffered in memory, so do not use this
-    method if the data size is large or unlimited.
-
-The following attributes are also available:
-
-stdin
-    If the stdin argument is PIPE, this attribute is a file object
-    that provides input to the child process.  Otherwise, it is None.
-
-stdout
-    If the stdout argument is PIPE, this attribute is a file object
-    that provides output from the child process.  Otherwise, it is
-    None.
-
-stderr
-    If the stderr argument is PIPE, this attribute is file object that
-    provides error output from the child process.  Otherwise, it is
-    None.
-
-pid
-    The process ID of the child process.
-
-returncode
-    The child return code.  A None value indicates that the process
-    hasn't terminated yet.  A negative value -N indicates that the
-    child was terminated by signal N (UNIX only).
-
-
-Replacing older functions with the subprocess module
-====================================================
-In this section, "a ==> b" means that b can be used as a replacement
-for a.
-
-Note: All functions in this section fail (more or less) silently if
-the executed program cannot be found; this module raises an OSError
-exception.
-
-In the following examples, we assume that the subprocess module is
-imported with "from subprocess import *".
-
-
-Replacing /bin/sh shell backquote
----------------------------------
-output=`mycmd myarg`
-==>
-output = Popen(["mycmd", "myarg"], stdout=PIPE).communicate()[0]
-
-
-Replacing shell pipe line
--------------------------
-output=`dmesg | grep hda`
-==>
-p1 = Popen(["dmesg"], stdout=PIPE)
-p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
-output = p2.communicate()[0]
-
-
-Replacing os.system()
----------------------
-sts = os.system("mycmd" + " myarg")
-==>
-p = Popen("mycmd" + " myarg", shell=True)
-sts = os.waitpid(p.pid, 0)
-
-Note:
-
-* Calling the program through the shell is usually not required.
-
-* It's easier to look at the returncode attribute than the
-  exitstatus.
-
-A more real-world example would look like this:
-
-try:
-    retcode = call("mycmd" + " myarg", shell=True)
-    if retcode < 0:
-        print >>sys.stderr, "Child was terminated by signal", -retcode
-    else:
-        print >>sys.stderr, "Child returned", retcode
-except OSError, e:
-    print >>sys.stderr, "Execution failed:", e
-
-
-Replacing os.spawn*
--------------------
-P_NOWAIT example:
-
-pid = os.spawnlp(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg")
-==>
-pid = Popen(["/bin/mycmd", "myarg"]).pid
-
-
-P_WAIT example:
-
-retcode = os.spawnlp(os.P_WAIT, "/bin/mycmd", "mycmd", "myarg")
-==>
-retcode = call(["/bin/mycmd", "myarg"])
-
-
-Vector example:
-
-os.spawnvp(os.P_NOWAIT, path, args)
-==>
-Popen([path] + args[1:])
-
-
-Environment example:
-
-os.spawnlpe(os.P_NOWAIT, "/bin/mycmd", "mycmd", "myarg", env)
-==>
-Popen(["/bin/mycmd", "myarg"], env={"PATH": "/usr/bin"})
-
-
-Replacing os.popen*
--------------------
-pipe = os.popen(cmd, mode='r', bufsize)
-==>
-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdout=PIPE).stdout
-
-pipe = os.popen(cmd, mode='w', bufsize)
-==>
-pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin
-
-
-(child_stdin, child_stdout) = os.popen2(cmd, mode, bufsize)
-==>
-p = Popen(cmd, shell=True, bufsize=bufsize,
-          stdin=PIPE, stdout=PIPE, close_fds=True)
-(child_stdin, child_stdout) = (p.stdin, p.stdout)
-
-
-(child_stdin,
- child_stdout,
- child_stderr) = os.popen3(cmd, mode, bufsize)
-==>
-p = Popen(cmd, shell=True, bufsize=bufsize,
-          stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
-(child_stdin,
- child_stdout,
- child_stderr) = (p.stdin, p.stdout, p.stderr)
-
-
-(child_stdin, child_stdout_and_stderr) = os.popen4(cmd, mode, bufsize)
-==>
-p = Popen(cmd, shell=True, bufsize=bufsize,
-          stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
-(child_stdin, child_stdout_and_stderr) = (p.stdin, p.stdout)
-
-
-Replacing popen2.*
-------------------
-Note: If the cmd argument to popen2 functions is a string, the command
-is executed through /bin/sh.  If it is a list, the command is directly
-executed.
-
-(child_stdout, child_stdin) = popen2.popen2("somestring", bufsize, mode)
-==>
-p = Popen(["somestring"], shell=True, bufsize=bufsize
-          stdin=PIPE, stdout=PIPE, close_fds=True)
-(child_stdout, child_stdin) = (p.stdout, p.stdin)
-
-
-(child_stdout, child_stdin) = popen2.popen2(["mycmd", "myarg"], bufsize, mode)
-==>
-p = Popen(["mycmd", "myarg"], bufsize=bufsize,
-          stdin=PIPE, stdout=PIPE, close_fds=True)
-(child_stdout, child_stdin) = (p.stdout, p.stdin)
-
-The popen2.Popen3 and popen3.Popen4 basically works as subprocess.Popen,
-except that:
-
-* subprocess.Popen raises an exception if the execution fails
-* the capturestderr argument is replaced with the stderr argument.
-* stdin=PIPE and stdout=PIPE must be specified.
-* popen2 closes all filedescriptors by default, but you have to specify
-  close_fds=True with subprocess.Popen.
-
-
-"""
-
-import sys
-mswindows = (sys.platform == "win32")
-
-import os
-import types
-import traceback
-
-if mswindows:
-    import threading
-    import msvcrt
-    if 0: # <-- change this to use pywin32 instead of the _subprocess driver
-        import pywintypes
-        from win32api import GetStdHandle, STD_INPUT_HANDLE, \
-                             STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
-        from win32api import GetCurrentProcess, DuplicateHandle, \
-                             GetModuleFileName, GetVersion
-        from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE
-        from win32pipe import CreatePipe
-        from win32process import CreateProcess, STARTUPINFO, \
-                                 GetExitCodeProcess, STARTF_USESTDHANDLES, \
-                                 STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE
-        from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0
-    else:
-        from _subprocess import *
-        class STARTUPINFO:
-            dwFlags = 0
-            hStdInput = None
-            hStdOutput = None
-            hStdError = None
-        class pywintypes:
-            error = IOError
-else:
-    import select
-    import errno
-    import fcntl
-    import pickle
-
-__all__ = ["Popen", "PIPE", "STDOUT", "call"]
-
-try:
-    MAXFD = os.sysconf("SC_OPEN_MAX")
-except:
-    MAXFD = 256
-
-# True/False does not exist on 2.2.0
-try:
-    False
-except NameError:
-    False = 0
-    True = 1
-
-_active = []
-
-def _cleanup():
-    for inst in _active[:]:
-        inst.poll()
-
-PIPE = -1
-STDOUT = -2
-
-
-def call(*args, **kwargs):
-    """Run command with arguments.  Wait for command to complete, then
-    return the returncode attribute.
-
-    The arguments are the same as for the Popen constructor.  Example:
-
-    retcode = call(["ls", "-l"])
-    """
-    return Popen(*args, **kwargs).wait()
-
-
-def list2cmdline(seq):
-    """
-    Translate a sequence of arguments into a command line
-    string, using the same rules as the MS C runtime:
-
-    1) Arguments are delimited by white space, which is either a
-       space or a tab.
-
-    2) A string surrounded by double quotation marks is
-       interpreted as a single argument, regardless of white space
-       contained within.  A quoted string can be embedded in an
-       argument.
-
-    3) A double quotation mark preceded by a backslash is
-       interpreted as a literal double quotation mark.
-
-    4) Backslashes are interpreted literally, unless they
-       immediately precede a double quotation mark.
-
-    5) If backslashes immediately precede a double quotation mark,
-       every pair of backslashes is interpreted as a literal
-       backslash.  If the number of backslashes is odd, the last
-       backslash escapes the next double quotation mark as
-       described in rule 3.
-    """
-
-    # See
-    # http://msdn.microsoft.com/library/en-us/vccelng/htm/progs_12.asp
-    result = []
-    needquote = False
-    for arg in seq:
-        bs_buf = []
-
-        # Add a space to separate this argument from the others
-        if result:
-            result.append(' ')
-
-        needquote = (" " in arg) or ("\t" in arg)
-        if needquote:
-            result.append('"')
-
-        for c in arg:
-            if c == '\\':
-                # Don't know if we need to double yet.
-                bs_buf.append(c)
-            elif c == '"':
-                # Double backspaces.
-                result.append('\\' * len(bs_buf)*2)
-                bs_buf = []
-                result.append('\\"')
-            else:
-                # Normal char
-                if bs_buf:
-                    result.extend(bs_buf)
-                    bs_buf = []
-                result.append(c)
-
-        # Add remaining backspaces, if any.
-        if bs_buf:
-            result.extend(bs_buf)
-
-        if needquote:
-            result.extend(bs_buf)
-            result.append('"')
-
-    return ''.join(result)
-
-
-class Popen(object):
-    def __init__(self, args, bufsize=0, executable=None,
-                 stdin=None, stdout=None, stderr=None,
-                 preexec_fn=None, close_fds=False, shell=False,
-                 cwd=None, env=None, universal_newlines=False,
-                 startupinfo=None, creationflags=0):
-        """Create new Popen instance."""
-        _cleanup()
-
-        if not isinstance(bufsize, (int, long)):
-            raise TypeError("bufsize must be an integer")
-
-        if mswindows:
-            if preexec_fn is not None:
-                raise ValueError("preexec_fn is not supported on Windows "
-                                 "platforms")
-            if close_fds:
-                raise ValueError("close_fds is not supported on Windows "
-                                 "platforms")
-        else:
-            # POSIX
-            if startupinfo is not None:
-                raise ValueError("startupinfo is only supported on Windows "
-                                 "platforms")
-            if creationflags != 0:
-                raise ValueError("creationflags is only supported on Windows "
-                                 "platforms")
-
-        self.stdin = None
-        self.stdout = None
-        self.stderr = None
-        self.pid = None
-        self.returncode = None
-        self.universal_newlines = universal_newlines
-
-        # Input and output objects. The general principle is like
-        # this:
-        #
-        # Parent                   Child
-        # ------                   -----
-        # p2cwrite   ---stdin--->  p2cread
-        # c2pread    <--stdout---  c2pwrite
-        # errread    <--stderr---  errwrite
-        #
-        # On POSIX, the child objects are file descriptors.  On
-        # Windows, these are Windows file handles.  The parent objects
-        # are file descriptors on both platforms.  The parent objects
-        # are None when not using PIPEs. The child objects are None
-        # when not redirecting.
-
-        (p2cread, p2cwrite,
-         c2pread, c2pwrite,
-         errread, errwrite) = self._get_handles(stdin, stdout, stderr)
-
-        self._execute_child(args, executable, preexec_fn, close_fds,
-                            cwd, env, universal_newlines,
-                            startupinfo, creationflags, shell,
-                            p2cread, p2cwrite,
-                            c2pread, c2pwrite,
-                            errread, errwrite)
-
-        if p2cwrite:
-            self.stdin = os.fdopen(p2cwrite, 'wb', bufsize)
-        if c2pread:
-            if universal_newlines:
-                self.stdout = os.fdopen(c2pread, 'rU', bufsize)
-            else:
-                self.stdout = os.fdopen(c2pread, 'rb', bufsize)
-        if errread:
-            if universal_newlines:
-                self.stderr = os.fdopen(errread, 'rU', bufsize)
-            else:
-                self.stderr = os.fdopen(errread, 'rb', bufsize)
-
-        _active.append(self)
-
-
-    def _translate_newlines(self, data):
-        data = data.replace("\r\n", "\n")
-        data = data.replace("\r", "\n")
-        return data
-
-
-    if mswindows:
-        #
-        # Windows methods
-        #
-        def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tuple with IO objects:
-            p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
-            """
-            if stdin == None and stdout == None and stderr == None:
-                return (None, None, None, None, None, None)
-
-            p2cread, p2cwrite = None, None
-            c2pread, c2pwrite = None, None
-            errread, errwrite = None, None
-
-            if stdin == None:
-                p2cread = GetStdHandle(STD_INPUT_HANDLE)
-            elif stdin == PIPE:
-                p2cread, p2cwrite = CreatePipe(None, 0)
-                # Detach and turn into fd
-                p2cwrite = p2cwrite.Detach()
-                p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0)
-            elif type(stdin) == types.IntType:
-                p2cread = msvcrt.get_osfhandle(stdin)
-            else:
-                # Assuming file-like object
-                p2cread = msvcrt.get_osfhandle(stdin.fileno())
-            p2cread = self._make_inheritable(p2cread)
-
-            if stdout == None:
-                c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE)
-            elif stdout == PIPE:
-                c2pread, c2pwrite = CreatePipe(None, 0)
-                # Detach and turn into fd
-                c2pread = c2pread.Detach()
-                c2pread = msvcrt.open_osfhandle(c2pread, 0)
-            elif type(stdout) == types.IntType:
-                c2pwrite = msvcrt.get_osfhandle(stdout)
-            else:
-                # Assuming file-like object
-                c2pwrite = msvcrt.get_osfhandle(stdout.fileno())
-            c2pwrite = self._make_inheritable(c2pwrite)
-
-            if stderr == None:
-                errwrite = GetStdHandle(STD_ERROR_HANDLE)
-            elif stderr == PIPE:
-                errread, errwrite = CreatePipe(None, 0)
-                # Detach and turn into fd
-                errread = errread.Detach()
-                errread = msvcrt.open_osfhandle(errread, 0)
-            elif stderr == STDOUT:
-                errwrite = c2pwrite
-            elif type(stderr) == types.IntType:
-                errwrite = msvcrt.get_osfhandle(stderr)
-            else:
-                # Assuming file-like object
-                errwrite = msvcrt.get_osfhandle(stderr.fileno())
-            errwrite = self._make_inheritable(errwrite)
-
-            return (p2cread, p2cwrite,
-                    c2pread, c2pwrite,
-                    errread, errwrite)
-
-
-        def _make_inheritable(self, handle):
-            """Return a duplicate of handle, which is inheritable"""
-            return DuplicateHandle(GetCurrentProcess(), handle,
-                                   GetCurrentProcess(), 0, 1,
-                                   DUPLICATE_SAME_ACCESS)
-
-
-        def _find_w9xpopen(self):
-            """Find and return absolute path to w9xpopen.exe"""
-            w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
-                                    "w9xpopen.exe")
-            if not os.path.exists(w9xpopen):
-                # Eeek - file-not-found - possibly an embedding
-                # situation - see if we can locate it in sys.exec_prefix
-                w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix),
-                                        "w9xpopen.exe")
-                if not os.path.exists(w9xpopen):
-                    raise RuntimeError("Cannot locate w9xpopen.exe, which is "
-                                       "needed for Popen to work with your "
-                                       "shell or platform.")
-            return w9xpopen
-
-
-        def _execute_child(self, args, executable, preexec_fn, close_fds,
-                           cwd, env, universal_newlines,
-                           startupinfo, creationflags, shell,
-                           p2cread, p2cwrite,
-                           c2pread, c2pwrite,
-                           errread, errwrite):
-            """Execute program (MS Windows version)"""
-
-            if not isinstance(args, types.StringTypes):
-                args = list2cmdline(args)
-
-            # Process startup details
-            default_startupinfo = STARTUPINFO()
-            if startupinfo == None:
-                startupinfo = default_startupinfo
-            if not None in (p2cread, c2pwrite, errwrite):
-                startupinfo.dwFlags |= STARTF_USESTDHANDLES
-                startupinfo.hStdInput = p2cread
-                startupinfo.hStdOutput = c2pwrite
-                startupinfo.hStdError = errwrite
-
-            if shell:
-                default_startupinfo.dwFlags |= STARTF_USESHOWWINDOW
-                default_startupinfo.wShowWindow = SW_HIDE
-                comspec = os.environ.get("COMSPEC", "cmd.exe")
-                args = comspec + " /c " + args
-                if (GetVersion() >= 0x80000000L or
-                        os.path.basename(comspec).lower() == "command.com"):
-                    # Win9x, or using command.com on NT. We need to
-                    # use the w9xpopen intermediate program. For more
-                    # information, see KB Q150956
-                    # (http://web.archive.org/web/20011105084002/http://support.microsoft.com/support/kb/articles/Q150/9/56.asp)
-                    w9xpopen = self._find_w9xpopen()
-                    args = '"%s" %s' % (w9xpopen, args)
-                    # Not passing CREATE_NEW_CONSOLE has been known to
-                    # cause random failures on win9x.  Specifically a
-                    # dialog: "Your program accessed mem currently in
-                    # use at xxx" and a hopeful warning about the
-                    # stability of your system.  Cost is Ctrl+C wont
-                    # kill children.
-                    creationflags |= CREATE_NEW_CONSOLE
-
-            # Start the process
-            try:
-                hp, ht, pid, tid = CreateProcess(executable, args,
-                                         # no special security
-                                         None, None,
-                                         # must inherit handles to pass std
-                                         # handles
-                                         1,
-                                         creationflags,
-                                         env,
-                                         cwd,
-                                         startupinfo)
-            except pywintypes.error, e:
-                # Translate pywintypes.error to WindowsError, which is
-                # a subclass of OSError.  FIXME: We should really
-                # translate errno using _sys_errlist (or simliar), but
-                # how can this be done from Python?
-                raise WindowsError(*e.args)
-
-            # Retain the process handle, but close the thread handle
-            self._handle = hp
-            self.pid = pid
-            ht.Close()
-
-            # Child is launched. Close the parent's copy of those pipe
-            # handles that only the child should have open.  You need
-            # to make sure that no handles to the write end of the
-            # output pipe are maintained in this process or else the
-            # pipe will not close when the child process exits and the
-            # ReadFile will hang.
-            if p2cread != None:
-                p2cread.Close()
-            if c2pwrite != None:
-                c2pwrite.Close()
-            if errwrite != None:
-                errwrite.Close()
-
-
-        def poll(self):
-            """Check if child process has terminated.  Returns returncode
-            attribute."""
-            if self.returncode == None:
-                if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0:
-                    self.returncode = GetExitCodeProcess(self._handle)
-                    _active.remove(self)
-            return self.returncode
-
-
-        def wait(self):
-            """Wait for child process to terminate.  Returns returncode
-            attribute."""
-            if self.returncode == None:
-                obj = WaitForSingleObject(self._handle, INFINITE)
-                self.returncode = GetExitCodeProcess(self._handle)
-                _active.remove(self)
-            return self.returncode
-
-
-        def _readerthread(self, fh, buffer):
-            buffer.append(fh.read())
-
-
-        def communicate(self, input=None):
-            """Interact with process: Send data to stdin.  Read data from
-            stdout and stderr, until end-of-file is reached.  Wait for
-            process to terminate.  The optional input argument should be a
-            string to be sent to the child process, or None, if no data
-            should be sent to the child.
-
-            communicate() returns a tuple (stdout, stderr)."""
-            stdout = None # Return
-            stderr = None # Return
-
-            if self.stdout:
-                stdout = []
-                stdout_thread = threading.Thread(target=self._readerthread,
-                                                 args=(self.stdout, stdout))
-                stdout_thread.setDaemon(True)
-                stdout_thread.start()
-            if self.stderr:
-                stderr = []
-                stderr_thread = threading.Thread(target=self._readerthread,
-                                                 args=(self.stderr, stderr))
-                stderr_thread.setDaemon(True)
-                stderr_thread.start()
-
-            if self.stdin:
-                if input != None:
-                    self.stdin.write(input)
-                self.stdin.close()
-
-            if self.stdout:
-                stdout_thread.join()
-            if self.stderr:
-                stderr_thread.join()
-
-            # All data exchanged.  Translate lists into strings.
-            if stdout != None:
-                stdout = stdout[0]
-            if stderr != None:
-                stderr = stderr[0]
-
-            # Translate newlines, if requested.  We cannot let the file
-            # object do the translation: It is based on stdio, which is
-            # impossible to combine with select (unless forcing no
-            # buffering).
-            if self.universal_newlines and hasattr(open, 'newlines'):
-                if stdout:
-                    stdout = self._translate_newlines(stdout)
-                if stderr:
-                    stderr = self._translate_newlines(stderr)
-
-            self.wait()
-            return (stdout, stderr)
-
-    else:
-        #
-        # POSIX methods
-        #
-        def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tuple with IO objects:
-            p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
-            """
-            p2cread, p2cwrite = None, None
-            c2pread, c2pwrite = None, None
-            errread, errwrite = None, None
-
-            if stdin == None:
-                pass
-            elif stdin == PIPE:
-                p2cread, p2cwrite = os.pipe()
-            elif type(stdin) == types.IntType:
-                p2cread = stdin
-            else:
-                # Assuming file-like object
-                p2cread = stdin.fileno()
-
-            if stdout == None:
-                pass
-            elif stdout == PIPE:
-                c2pread, c2pwrite = os.pipe()
-            elif type(stdout) == types.IntType:
-                c2pwrite = stdout
-            else:
-                # Assuming file-like object
-                c2pwrite = stdout.fileno()
-
-            if stderr == None:
-                pass
-            elif stderr == PIPE:
-                errread, errwrite = os.pipe()
-            elif stderr == STDOUT:
-                errwrite = c2pwrite
-            elif type(stderr) == types.IntType:
-                errwrite = stderr
-            else:
-                # Assuming file-like object
-                errwrite = stderr.fileno()
-
-            return (p2cread, p2cwrite,
-                    c2pread, c2pwrite,
-                    errread, errwrite)
-
-
-        def _set_cloexec_flag(self, fd):
-            try:
-                cloexec_flag = fcntl.FD_CLOEXEC
-            except AttributeError:
-                cloexec_flag = 1
-
-            old = fcntl.fcntl(fd, fcntl.F_GETFD)
-            fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag)
-
-
-        def _close_fds(self, but):
-            for i in range(3, MAXFD):
-                if i == but:
-                    continue
-                try:
-                    os.close(i)
-                except:
-                    pass
-
-
-        def _execute_child(self, args, executable, preexec_fn, close_fds,
-                           cwd, env, universal_newlines,
-                           startupinfo, creationflags, shell,
-                           p2cread, p2cwrite,
-                           c2pread, c2pwrite,
-                           errread, errwrite):
-            """Execute program (POSIX version)"""
-
-            if isinstance(args, types.StringTypes):
-                args = [args]
-
-            if shell:
-                args = ["/bin/sh", "-c"] + args
-
-            if executable == None:
-                executable = args[0]
-
-            # For transferring possible exec failure from child to parent
-            # The first char specifies the exception type: 0 means
-            # OSError, 1 means some other error.
-            errpipe_read, errpipe_write = os.pipe()
-            self._set_cloexec_flag(errpipe_write)
-
-            self.pid = os.fork()
-            if self.pid == 0:
-                # Child
-                try:
-                    # Close parent's pipe ends
-                    if p2cwrite:
-                        os.close(p2cwrite)
-                    if c2pread:
-                        os.close(c2pread)
-                    if errread:
-                        os.close(errread)
-                    os.close(errpipe_read)
-
-                    # Dup fds for child
-                    if p2cread:
-                        os.dup2(p2cread, 0)
-                    if c2pwrite:
-                        os.dup2(c2pwrite, 1)
-                    if errwrite:
-                        os.dup2(errwrite, 2)
-
-                    # Close pipe fds.  Make sure we doesn't close the same
-                    # fd more than once.
-                    if p2cread:
-                        os.close(p2cread)
-                    if c2pwrite and c2pwrite not in (p2cread,):
-                        os.close(c2pwrite)
-                    if errwrite and errwrite not in (p2cread, c2pwrite):
-                        os.close(errwrite)
-
-                    # Close all other fds, if asked for
-                    if close_fds:
-                        self._close_fds(but=errpipe_write)
-
-                    if cwd != None:
-                        os.chdir(cwd)
-
-                    if preexec_fn:
-                        apply(preexec_fn)
-
-                    if env == None:
-                        os.execvp(executable, args)
-                    else:
-                        os.execvpe(executable, args, env)
-
-                except:
-                    exc_type, exc_value, tb = sys.exc_info()
-                    # Save the traceback and attach it to the exception object
-                    exc_lines = traceback.format_exception(exc_type,
-                                                           exc_value,
-                                                           tb)
-                    exc_value.child_traceback = ''.join(exc_lines)
-                    os.write(errpipe_write, pickle.dumps(exc_value))
-
-                # This exitcode won't be reported to applications, so it
-                # really doesn't matter what we return.
-                os._exit(255)
-
-            # Parent
-            os.close(errpipe_write)
-            if p2cread and p2cwrite:
-                os.close(p2cread)
-            if c2pwrite and c2pread:
-                os.close(c2pwrite)
-            if errwrite and errread:
-                os.close(errwrite)
-
-            # Wait for exec to fail or succeed; possibly raising exception
-            data = os.read(errpipe_read, 1048576) # Exceptions limited to 1 MB
-            os.close(errpipe_read)
-            if data != "":
-                os.waitpid(self.pid, 0)
-                child_exception = pickle.loads(data)
-                raise child_exception
-
-
-        def _handle_exitstatus(self, sts):
-            if os.WIFSIGNALED(sts):
-                self.returncode = -os.WTERMSIG(sts)
-            elif os.WIFEXITED(sts):
-                self.returncode = os.WEXITSTATUS(sts)
-            else:
-                # Should never happen
-                raise RuntimeError("Unknown child exit status!")
-
-            _active.remove(self)
-
-
-        def poll(self):
-            """Check if child process has terminated.  Returns returncode
-            attribute."""
-            if self.returncode == None:
-                try:
-                    pid, sts = os.waitpid(self.pid, os.WNOHANG)
-                    if pid == self.pid:
-                        self._handle_exitstatus(sts)
-                except os.error:
-                    pass
-            return self.returncode
-
-
-        def wait(self):
-            """Wait for child process to terminate.  Returns returncode
-            attribute."""
-            if self.returncode == None:
-                pid, sts = os.waitpid(self.pid, 0)
-                self._handle_exitstatus(sts)
-            return self.returncode
-
-
-        def communicate(self, input=None):
-            """Interact with process: Send data to stdin.  Read data from
-            stdout and stderr, until end-of-file is reached.  Wait for
-            process to terminate.  The optional input argument should be a
-            string to be sent to the child process, or None, if no data
-            should be sent to the child.
-
-            communicate() returns a tuple (stdout, stderr)."""
-            read_set = []
-            write_set = []
-            stdout = None # Return
-            stderr = None # Return
-
-            if self.stdin:
-                # Flush stdio buffer.  This might block, if the user has
-                # been writing to .stdin in an uncontrolled fashion.
-                self.stdin.flush()
-                if input:
-                    write_set.append(self.stdin)
-                else:
-                    self.stdin.close()
-            if self.stdout:
-                read_set.append(self.stdout)
-                stdout = []
-            if self.stderr:
-                read_set.append(self.stderr)
-                stderr = []
-
-            while read_set or write_set:
-                rlist, wlist, xlist = select.select(read_set, write_set, [])
-
-                if self.stdin in wlist:
-                    # When select has indicated that the file is writable,
-                    # we can write up to PIPE_BUF bytes without risk
-                    # blocking.  POSIX defines PIPE_BUF >= 512
-                    bytes_written = os.write(self.stdin.fileno(), input[:512])
-                    input = input[bytes_written:]
-                    if not input:
-                        self.stdin.close()
-                        write_set.remove(self.stdin)
-
-                if self.stdout in rlist:
-                    data = os.read(self.stdout.fileno(), 1024)
-                    if data == "":
-                        self.stdout.close()
-                        read_set.remove(self.stdout)
-                    stdout.append(data)
-
-                if self.stderr in rlist:
-                    data = os.read(self.stderr.fileno(), 1024)
-                    if data == "":
-                        self.stderr.close()
-                        read_set.remove(self.stderr)
-                    stderr.append(data)
-
-            # All data exchanged.  Translate lists into strings.
-            if stdout != None:
-                stdout = ''.join(stdout)
-            if stderr != None:
-                stderr = ''.join(stderr)
-
-            # Translate newlines, if requested.  We cannot let the file
-            # object do the translation: It is based on stdio, which is
-            # impossible to combine with select (unless forcing no
-            # buffering).
-            if self.universal_newlines and hasattr(open, 'newlines'):
-                if stdout:
-                    stdout = self._translate_newlines(stdout)
-                if stderr:
-                    stderr = self._translate_newlines(stderr)
-
-            self.wait()
-            return (stdout, stderr)
-
-
-def _demo_posix():
-    #
-    # Example 1: Simple redirection: Get process list
-    #
-    plist = Popen(["ps"], stdout=PIPE).communicate()[0]
-    print "Process list:"
-    print plist
-
-    #
-    # Example 2: Change uid before executing child
-    #
-    if os.getuid() == 0:
-        p = Popen(["id"], preexec_fn=lambda: os.setuid(100))
-        p.wait()
-
-    #
-    # Example 3: Connecting several subprocesses
-    #
-    print "Looking for 'hda'..."
-    p1 = Popen(["dmesg"], stdout=PIPE)
-    p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
-    print repr(p2.communicate()[0])
-
-    #
-    # Example 4: Catch execution error
-    #
-    print
-    print "Trying a weird file..."
-    try:
-        print Popen(["/this/path/does/not/exist"]).communicate()
-    except OSError, e:
-        if e.errno == errno.ENOENT:
-            print "The file didn't exist.  I thought so..."
-            print "Child traceback:"
-            print e.child_traceback
-        else:
-            print "Error", e.errno
-    else:
-        print >>sys.stderr, "Gosh.  No error."
-
-
-def _demo_windows():
-    #
-    # Example 1: Connecting several subprocesses
-    #
-    print "Looking for 'PROMPT' in set output..."
-    p1 = Popen("set", stdout=PIPE, shell=True)
-    p2 = Popen('find "PROMPT"', stdin=p1.stdout, stdout=PIPE)
-    print repr(p2.communicate()[0])
-
-    #
-    # Example 2: Simple execution of program
-    #
-    print "Executing calc..."
-    p = Popen("calc")
-    p.wait()
-
-
-if __name__ == "__main__":
-    if mswindows:
-        _demo_windows()
-    else:
-        _demo_posix()
diff --git a/config.mak.in b/config.mak.in
index 1cafa19ed4..9a578405d8 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -13,7 +13,6 @@ bindir = @bindir@
 #gitexecdir = @libexecdir@/git-core/
 datarootdir = @datarootdir@
 template_dir = @datadir@/git-core/templates/
-GIT_PYTHON_DIR = @datadir@/git-core/python
 
 mandir=@mandir@
 
@@ -23,7 +22,6 @@ VPATH = @srcdir@
 export exec_prefix mandir
 export srcdir VPATH
 
-NO_PYTHON=@NO_PYTHON@
 NEEDS_SSL_WITH_CRYPTO=@NEEDS_SSL_WITH_CRYPTO@
 NO_OPENSSL=@NO_OPENSSL@
 NO_CURL=@NO_CURL@
diff --git a/configure.ac b/configure.ac
index cff5722eb9..34e34789bd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -75,20 +75,6 @@ GIT_ARG_SET_PATH(shell)
 # Define PERL_PATH to provide path to Perl.
 GIT_ARG_SET_PATH(perl)
 #
-# Define PYTHON_PATH to provide path to Python.
-AC_ARG_WITH(python,[AS_HELP_STRING([--with-python=PATH], [provide PATH to python])
-AS_HELP_STRING([--without-python], [don't use python scripts])],
- [if test "$withval" = "no"; then \
-    NO_PYTHON=YesPlease; \
-  elif test "$withval" = "yes"; then \
-    NO_PYTHON=; \
-  else \
-    NO_PYTHON=; \
-    PYTHON_PATH=$withval; \
-  fi; \
- ])
-AC_SUBST(NO_PYTHON)
-AC_SUBST(PYTHON_PATH)
 
 
 ## Checks for programs.
@@ -98,18 +84,6 @@ AC_PROG_CC([cc gcc])
 #AC_PROG_INSTALL		# needs install-sh or install.sh in sources
 AC_CHECK_TOOL(AR, ar, :)
 AC_CHECK_PROGS(TAR, [gtar tar])
-#
-# Define PYTHON_PATH to provide path to Python.
-if test -z "$NO_PYTHON"; then
-	if test -z "$PYTHON_PATH"; then
-		AC_PATH_PROGS(PYTHON_PATH, [python python2.4 python2.3 python2])
-	fi
-	if test -n "$PYTHON_PATH"; then
-		GIT_CONF_APPEND_LINE([PYTHON_PATH=@PYTHON_PATH@])
-		NO_PYTHON=""
-	fi
-fi
-
 
 ## Checks for libraries.
 AC_MSG_NOTICE([CHECKS for libraries])
@@ -262,22 +236,9 @@ AC_SUBST(NO_SETENV)
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
 #
-# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
-AC_CACHE_CHECK([for subprocess.py],
- [ac_cv_python_has_subprocess_py],
-[if $PYTHON_PATH -c 'import subprocess' 2>/dev/null; then
-	ac_cv_python_has_subprocess_py=yes
-else
-	ac_cv_python_has_subprocess_py=no
-fi])
-if test $ac_cv_python_has_subprocess_py != yes; then
-	GIT_CONF_APPEND_LINE([WITH_OWN_SUBPROCESS_PY=YesPlease])
-fi
-#
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 
-
 ## Site configuration (override autodetection)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE
 AC_MSG_NOTICE([CHECKS for site configuration])
diff --git a/git-merge-recursive-old.py b/git-merge-recursive-old.py
deleted file mode 100755
index 4039435ce4..0000000000
--- a/git-merge-recursive-old.py
+++ /dev/null
@@ -1,944 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2005 Fredrik Kuivinen
-#
-
-import sys
-sys.path.append('''@@GIT_PYTHON_PATH@@''')
-
-import math, random, os, re, signal, tempfile, stat, errno, traceback
-from heapq import heappush, heappop
-from sets import Set
-
-from gitMergeCommon import *
-
-outputIndent = 0
-def output(*args):
-    sys.stdout.write('  '*outputIndent)
-    printList(args)
-
-originalIndexFile = os.environ.get('GIT_INDEX_FILE',
-                                   os.environ.get('GIT_DIR', '.git') + '/index')
-temporaryIndexFile = os.environ.get('GIT_DIR', '.git') + \
-                     '/merge-recursive-tmp-index'
-def setupIndex(temporary):
-    try:
-        os.unlink(temporaryIndexFile)
-    except OSError:
-        pass
-    if temporary:
-        newIndex = temporaryIndexFile
-    else:
-        newIndex = originalIndexFile
-    os.environ['GIT_INDEX_FILE'] = newIndex
-
-# This is a global variable which is used in a number of places but
-# only written to in the 'merge' function.
-
-# cacheOnly == True  => Don't leave any non-stage 0 entries in the cache and
-#                       don't update the working directory.
-#              False => Leave unmerged entries in the cache and update
-#                       the working directory.
-
-cacheOnly = False
-
-# The entry point to the merge code
-# ---------------------------------
-
-def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0, ancestor=None):
-    '''Merge the commits h1 and h2, return the resulting virtual
-    commit object and a flag indicating the cleanness of the merge.'''
-    assert(isinstance(h1, Commit) and isinstance(h2, Commit))
-
-    global outputIndent
-
-    output('Merging:')
-    output(h1)
-    output(h2)
-    sys.stdout.flush()
-
-    if ancestor:
-        ca = [ancestor]
-    else:
-        assert(isinstance(graph, Graph))
-        ca = getCommonAncestors(graph, h1, h2)
-    output('found', len(ca), 'common ancestor(s):')
-    for x in ca:
-        output(x)
-    sys.stdout.flush()
-
-    mergedCA = ca[0]
-    for h in ca[1:]:
-        outputIndent = callDepth+1
-        [mergedCA, dummy] = merge(mergedCA, h,
-                                  'Temporary merge branch 1',
-                                  'Temporary merge branch 2',
-                                  graph, callDepth+1)
-        outputIndent = callDepth
-        assert(isinstance(mergedCA, Commit))
-
-    global cacheOnly
-    if callDepth == 0:
-        setupIndex(False)
-        cacheOnly = False
-    else:
-        setupIndex(True)
-        runProgram(['git-read-tree', h1.tree()])
-        cacheOnly = True
-
-    [shaRes, clean] = mergeTrees(h1.tree(), h2.tree(), mergedCA.tree(),
-                                 branch1Name, branch2Name)
-
-    if graph and (clean or cacheOnly):
-        res = Commit(None, [h1, h2], tree=shaRes)
-        graph.addNode(res)
-    else:
-        res = None
-
-    return [res, clean]
-
-getFilesRE = re.compile(r'^([0-7]+) (\S+) ([0-9a-f]{40})\t(.*)$', re.S)
-def getFilesAndDirs(tree):
-    files = Set()
-    dirs = Set()
-    out = runProgram(['git-ls-tree', '-r', '-z', '-t', tree])
-    for l in out.split('\0'):
-        m = getFilesRE.match(l)
-        if m:
-            if m.group(2) == 'tree':
-                dirs.add(m.group(4))
-            elif m.group(2) == 'blob':
-                files.add(m.group(4))
-
-    return [files, dirs]
-
-# Those two global variables are used in a number of places but only
-# written to in 'mergeTrees' and 'uniquePath'. They keep track of
-# every file and directory in the two branches that are about to be
-# merged.
-currentFileSet = None
-currentDirectorySet = None
-
-def mergeTrees(head, merge, common, branch1Name, branch2Name):
-    '''Merge the trees 'head' and 'merge' with the common ancestor
-    'common'. The name of the head branch is 'branch1Name' and the name of
-    the merge branch is 'branch2Name'. Return a tuple (tree, cleanMerge)
-    where tree is the resulting tree and cleanMerge is True iff the
-    merge was clean.'''
-    
-    assert(isSha(head) and isSha(merge) and isSha(common))
-
-    if common == merge:
-        output('Already uptodate!')
-        return [head, True]
-
-    if cacheOnly:
-        updateArg = '-i'
-    else:
-        updateArg = '-u'
-
-    [out, code] = runProgram(['git-read-tree', updateArg, '-m',
-                                common, head, merge], returnCode = True)
-    if code != 0:
-        die('git-read-tree:', out)
-
-    [tree, code] = runProgram('git-write-tree', returnCode=True)
-    tree = tree.rstrip()
-    if code != 0:
-        global currentFileSet, currentDirectorySet
-        [currentFileSet, currentDirectorySet] = getFilesAndDirs(head)
-        [filesM, dirsM] = getFilesAndDirs(merge)
-        currentFileSet.union_update(filesM)
-        currentDirectorySet.union_update(dirsM)
-
-        entries = unmergedCacheEntries()
-        renamesHead =  getRenames(head, common, head, merge, entries)
-        renamesMerge = getRenames(merge, common, head, merge, entries)
-
-        cleanMerge = processRenames(renamesHead, renamesMerge,
-                                    branch1Name, branch2Name)
-        for entry in entries:
-            if entry.processed:
-                continue
-            if not processEntry(entry, branch1Name, branch2Name):
-                cleanMerge = False
-                
-        if cleanMerge or cacheOnly:
-            tree = runProgram('git-write-tree').rstrip()
-        else:
-            tree = None
-    else:
-        cleanMerge = True
-
-    return [tree, cleanMerge]
-
-# Low level file merging, update and removal
-# ------------------------------------------
-
-def mergeFile(oPath, oSha, oMode, aPath, aSha, aMode, bPath, bSha, bMode,
-              branch1Name, branch2Name):
-
-    merge = False
-    clean = True
-
-    if stat.S_IFMT(aMode) != stat.S_IFMT(bMode):
-        clean = False
-        if stat.S_ISREG(aMode):
-            mode = aMode
-            sha = aSha
-        else:
-            mode = bMode
-            sha = bSha
-    else:
-        if aSha != oSha and bSha != oSha:
-            merge = True
-
-        if aMode == oMode:
-            mode = bMode
-        else:
-            mode = aMode
-
-        if aSha == oSha:
-            sha = bSha
-        elif bSha == oSha:
-            sha = aSha
-        elif stat.S_ISREG(aMode):
-            assert(stat.S_ISREG(bMode))
-
-            orig = runProgram(['git-unpack-file', oSha]).rstrip()
-            src1 = runProgram(['git-unpack-file', aSha]).rstrip()
-            src2 = runProgram(['git-unpack-file', bSha]).rstrip()
-            try:
-                [out, code] = runProgram(['merge',
-                                          '-L', branch1Name + '/' + aPath,
-                                          '-L', 'orig/' + oPath,
-                                          '-L', branch2Name + '/' + bPath,
-                                          src1, orig, src2], returnCode=True)
-            except ProgramError, e:
-                print >>sys.stderr, e
-                die("Failed to execute 'merge'. merge(1) is used as the "
-                    "file-level merge tool. Is 'merge' in your path?")
-
-            sha = runProgram(['git-hash-object', '-t', 'blob', '-w',
-                              src1]).rstrip()
-
-            os.unlink(orig)
-            os.unlink(src1)
-            os.unlink(src2)
-
-            clean = (code == 0)
-        else:
-            assert(stat.S_ISLNK(aMode) and stat.S_ISLNK(bMode))
-            sha = aSha
-
-            if aSha != bSha:
-                clean = False
-
-    return [sha, mode, clean, merge]
-
-def updateFile(clean, sha, mode, path):
-    updateCache = cacheOnly or clean
-    updateWd = not cacheOnly
-
-    return updateFileExt(sha, mode, path, updateCache, updateWd)
-
-def updateFileExt(sha, mode, path, updateCache, updateWd):
-    if cacheOnly:
-        updateWd = False
-
-    if updateWd:
-        pathComponents = path.split('/')
-        for x in xrange(1, len(pathComponents)):
-            p = '/'.join(pathComponents[0:x])
-
-            try:
-                createDir = not stat.S_ISDIR(os.lstat(p).st_mode)
-            except OSError:
-                createDir = True
-            
-            if createDir:
-                try:
-                    os.mkdir(p)
-                except OSError, e:
-                    die("Couldn't create directory", p, e.strerror)
-
-        prog = ['git-cat-file', 'blob', sha]
-        if stat.S_ISREG(mode):
-            try:
-                os.unlink(path)
-            except OSError:
-                pass
-            if mode & 0100:
-                mode = 0777
-            else:
-                mode = 0666
-            fd = os.open(path, os.O_WRONLY | os.O_TRUNC | os.O_CREAT, mode)
-            proc = subprocess.Popen(prog, stdout=fd)
-            proc.wait()
-            os.close(fd)
-        elif stat.S_ISLNK(mode):
-            linkTarget = runProgram(prog)
-            os.symlink(linkTarget, path)
-        else:
-            assert(False)
-
-    if updateWd and updateCache:
-        runProgram(['git-update-index', '--add', '--', path])
-    elif updateCache:
-        runProgram(['git-update-index', '--add', '--cacheinfo',
-                    '0%o' % mode, sha, path])
-
-def setIndexStages(path,
-                   oSHA1, oMode,
-                   aSHA1, aMode,
-                   bSHA1, bMode,
-                   clear=True):
-    istring = []
-    if clear:
-        istring.append("0 " + ("0" * 40) + "\t" + path + "\0")
-    if oMode:
-        istring.append("%o %s %d\t%s\0" % (oMode, oSHA1, 1, path))
-    if aMode:
-        istring.append("%o %s %d\t%s\0" % (aMode, aSHA1, 2, path))
-    if bMode:
-        istring.append("%o %s %d\t%s\0" % (bMode, bSHA1, 3, path))
-
-    runProgram(['git-update-index', '-z', '--index-info'],
-               input="".join(istring))
-
-def removeFile(clean, path):
-    updateCache = cacheOnly or clean
-    updateWd = not cacheOnly
-
-    if updateCache:
-        runProgram(['git-update-index', '--force-remove', '--', path])
-
-    if updateWd:
-        try:
-            os.unlink(path)
-        except OSError, e:
-            if e.errno != errno.ENOENT and e.errno != errno.EISDIR:
-                raise
-        try:
-            os.removedirs(os.path.dirname(path))
-        except OSError:
-            pass
-
-def uniquePath(path, branch):
-    def fileExists(path):
-        try:
-            os.lstat(path)
-            return True
-        except OSError, e:
-            if e.errno == errno.ENOENT:
-                return False
-            else:
-                raise
-
-    branch = branch.replace('/', '_')
-    newPath = path + '~' + branch
-    suffix = 0
-    while newPath in currentFileSet or \
-          newPath in currentDirectorySet  or \
-          fileExists(newPath):
-        suffix += 1
-        newPath = path + '~' + branch + '_' + str(suffix)
-    currentFileSet.add(newPath)
-    return newPath
-
-# Cache entry management
-# ----------------------
-
-class CacheEntry:
-    def __init__(self, path):
-        class Stage:
-            def __init__(self):
-                self.sha1 = None
-                self.mode = None
-
-            # Used for debugging only
-            def __str__(self):
-                if self.mode != None:
-                    m = '0%o' % self.mode
-                else:
-                    m = 'None'
-
-                if self.sha1:
-                    sha1 = self.sha1
-                else:
-                    sha1 = 'None'
-                return 'sha1: ' + sha1 + ' mode: ' + m
-        
-        self.stages = [Stage(), Stage(), Stage(), Stage()]
-        self.path = path
-        self.processed = False
-
-    def __str__(self):
-        return 'path: ' + self.path + ' stages: ' + repr([str(x) for x in self.stages])
-
-class CacheEntryContainer:
-    def __init__(self):
-        self.entries = {}
-
-    def add(self, entry):
-        self.entries[entry.path] = entry
-
-    def get(self, path):
-        return self.entries.get(path)
-
-    def __iter__(self):
-        return self.entries.itervalues()
-    
-unmergedRE = re.compile(r'^([0-7]+) ([0-9a-f]{40}) ([1-3])\t(.*)$', re.S)
-def unmergedCacheEntries():
-    '''Create a dictionary mapping file names to CacheEntry
-    objects. The dictionary contains one entry for every path with a
-    non-zero stage entry.'''
-
-    lines = runProgram(['git-ls-files', '-z', '--unmerged']).split('\0')
-    lines.pop()
-
-    res = CacheEntryContainer()
-    for l in lines:
-        m = unmergedRE.match(l)
-        if m:
-            mode = int(m.group(1), 8)
-            sha1 = m.group(2)
-            stage = int(m.group(3))
-            path = m.group(4)
-
-            e = res.get(path)
-            if not e:
-                e = CacheEntry(path)
-                res.add(e)
-
-            e.stages[stage].mode = mode
-            e.stages[stage].sha1 = sha1
-        else:
-            die('Error: Merge program failed: Unexpected output from',
-                'git-ls-files:', l)
-    return res
-
-lsTreeRE = re.compile(r'^([0-7]+) (\S+) ([0-9a-f]{40})\t(.*)\n$', re.S)
-def getCacheEntry(path, origTree, aTree, bTree):
-    '''Returns a CacheEntry object which doesn't have to correspond to
-    a real cache entry in Git's index.'''
-    
-    def parse(out):
-        if out == '':
-            return [None, None]
-        else:
-            m = lsTreeRE.match(out)
-            if not m:
-                die('Unexpected output from git-ls-tree:', out)
-            elif m.group(2) == 'blob':
-                return [m.group(3), int(m.group(1), 8)]
-            else:
-                return [None, None]
-
-    res = CacheEntry(path)
-
-    [oSha, oMode] = parse(runProgram(['git-ls-tree', origTree, '--', path]))
-    [aSha, aMode] = parse(runProgram(['git-ls-tree', aTree, '--', path]))
-    [bSha, bMode] = parse(runProgram(['git-ls-tree', bTree, '--', path]))
-
-    res.stages[1].sha1 = oSha
-    res.stages[1].mode = oMode
-    res.stages[2].sha1 = aSha
-    res.stages[2].mode = aMode
-    res.stages[3].sha1 = bSha
-    res.stages[3].mode = bMode
-
-    return res
-
-# Rename detection and handling
-# -----------------------------
-
-class RenameEntry:
-    def __init__(self,
-                 src, srcSha, srcMode, srcCacheEntry,
-                 dst, dstSha, dstMode, dstCacheEntry,
-                 score):
-        self.srcName = src
-        self.srcSha = srcSha
-        self.srcMode = srcMode
-        self.srcCacheEntry = srcCacheEntry
-        self.dstName = dst
-        self.dstSha = dstSha
-        self.dstMode = dstMode
-        self.dstCacheEntry = dstCacheEntry
-        self.score = score
-
-        self.processed = False
-
-class RenameEntryContainer:
-    def __init__(self):
-        self.entriesSrc = {}
-        self.entriesDst = {}
-
-    def add(self, entry):
-        self.entriesSrc[entry.srcName] = entry
-        self.entriesDst[entry.dstName] = entry
-
-    def getSrc(self, path):
-        return self.entriesSrc.get(path)
-
-    def getDst(self, path):
-        return self.entriesDst.get(path)
-
-    def __iter__(self):
-        return self.entriesSrc.itervalues()
-
-parseDiffRenamesRE = re.compile('^:([0-7]+) ([0-7]+) ([0-9a-f]{40}) ([0-9a-f]{40}) R([0-9]*)$')
-def getRenames(tree, oTree, aTree, bTree, cacheEntries):
-    '''Get information of all renames which occured between 'oTree' and
-    'tree'. We need the three trees in the merge ('oTree', 'aTree' and
-    'bTree') to be able to associate the correct cache entries with
-    the rename information. 'tree' is always equal to either aTree or bTree.'''
-
-    assert(tree == aTree or tree == bTree)
-    inp = runProgram(['git-diff-tree', '-M', '--diff-filter=R', '-r',
-                      '-z', oTree, tree])
-
-    ret = RenameEntryContainer()
-    try:
-        recs = inp.split("\0")
-        recs.pop() # remove last entry (which is '')
-        it = recs.__iter__()
-        while True:
-            rec = it.next()
-            m = parseDiffRenamesRE.match(rec)
-
-            if not m:
-                die('Unexpected output from git-diff-tree:', rec)
-
-            srcMode = int(m.group(1), 8)
-            dstMode = int(m.group(2), 8)
-            srcSha = m.group(3)
-            dstSha = m.group(4)
-            score = m.group(5)
-            src = it.next()
-            dst = it.next()
-
-            srcCacheEntry = cacheEntries.get(src)
-            if not srcCacheEntry:
-                srcCacheEntry = getCacheEntry(src, oTree, aTree, bTree)
-                cacheEntries.add(srcCacheEntry)
-
-            dstCacheEntry = cacheEntries.get(dst)
-            if not dstCacheEntry:
-                dstCacheEntry = getCacheEntry(dst, oTree, aTree, bTree)
-                cacheEntries.add(dstCacheEntry)
-
-            ret.add(RenameEntry(src, srcSha, srcMode, srcCacheEntry,
-                                dst, dstSha, dstMode, dstCacheEntry,
-                                score))
-    except StopIteration:
-        pass
-    return ret
-
-def fmtRename(src, dst):
-    srcPath = src.split('/')
-    dstPath = dst.split('/')
-    path = []
-    endIndex = min(len(srcPath), len(dstPath)) - 1
-    for x in range(0, endIndex):
-        if srcPath[x] == dstPath[x]:
-            path.append(srcPath[x])
-        else:
-            endIndex = x
-            break
-
-    if len(path) > 0:
-        return '/'.join(path) + \
-               '/{' + '/'.join(srcPath[endIndex:]) + ' => ' + \
-               '/'.join(dstPath[endIndex:]) + '}'
-    else:
-        return src + ' => ' + dst
-
-def processRenames(renamesA, renamesB, branchNameA, branchNameB):
-    srcNames = Set()
-    for x in renamesA:
-        srcNames.add(x.srcName)
-    for x in renamesB:
-        srcNames.add(x.srcName)
-
-    cleanMerge = True
-    for path in srcNames:
-        if renamesA.getSrc(path):
-            renames1 = renamesA
-            renames2 = renamesB
-            branchName1 = branchNameA
-            branchName2 = branchNameB
-        else:
-            renames1 = renamesB
-            renames2 = renamesA
-            branchName1 = branchNameB
-            branchName2 = branchNameA
-        
-        ren1 = renames1.getSrc(path)
-        ren2 = renames2.getSrc(path)
-
-        ren1.dstCacheEntry.processed = True
-        ren1.srcCacheEntry.processed = True
-
-        if ren1.processed:
-            continue
-
-        ren1.processed = True
-
-        if ren2:
-            # Renamed in 1 and renamed in 2
-            assert(ren1.srcName == ren2.srcName)
-            ren2.dstCacheEntry.processed = True
-            ren2.processed = True
-
-            if ren1.dstName != ren2.dstName:
-                output('CONFLICT (rename/rename): Rename',
-                       fmtRename(path, ren1.dstName), 'in branch', branchName1,
-                       'rename', fmtRename(path, ren2.dstName), 'in',
-                       branchName2)
-                cleanMerge = False
-
-                if ren1.dstName in currentDirectorySet:
-                    dstName1 = uniquePath(ren1.dstName, branchName1)
-                    output(ren1.dstName, 'is a directory in', branchName2,
-                           'adding as', dstName1, 'instead.')
-                    removeFile(False, ren1.dstName)
-                else:
-                    dstName1 = ren1.dstName
-
-                if ren2.dstName in currentDirectorySet:
-                    dstName2 = uniquePath(ren2.dstName, branchName2)
-                    output(ren2.dstName, 'is a directory in', branchName1,
-                           'adding as', dstName2, 'instead.')
-                    removeFile(False, ren2.dstName)
-                else:
-                    dstName2 = ren2.dstName
-                setIndexStages(dstName1,
-                               None, None,
-                               ren1.dstSha, ren1.dstMode,
-			       None, None)
-                setIndexStages(dstName2,
-                               None, None,
-                               None, None,
-                               ren2.dstSha, ren2.dstMode)
-
-            else:
-                removeFile(True, ren1.srcName)
-
-                [resSha, resMode, clean, merge] = \
-                         mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode,
-                                   ren1.dstName, ren1.dstSha, ren1.dstMode,
-                                   ren2.dstName, ren2.dstSha, ren2.dstMode,
-                                   branchName1, branchName2)
-
-                if merge or not clean:
-                    output('Renaming', fmtRename(path, ren1.dstName))
-
-                if merge:
-                    output('Auto-merging', ren1.dstName)
-
-                if not clean:
-                    output('CONFLICT (content): merge conflict in',
-                           ren1.dstName)
-                    cleanMerge = False
-
-                    if not cacheOnly:
-                        setIndexStages(ren1.dstName,
-                                       ren1.srcSha, ren1.srcMode,
-                                       ren1.dstSha, ren1.dstMode,
-                                       ren2.dstSha, ren2.dstMode)
-
-                updateFile(clean, resSha, resMode, ren1.dstName)
-        else:
-            removeFile(True, ren1.srcName)
-
-            # Renamed in 1, maybe changed in 2
-            if renamesA == renames1:
-                stage = 3
-            else:
-                stage = 2
-                
-            srcShaOtherBranch  = ren1.srcCacheEntry.stages[stage].sha1
-            srcModeOtherBranch = ren1.srcCacheEntry.stages[stage].mode
-
-            dstShaOtherBranch  = ren1.dstCacheEntry.stages[stage].sha1
-            dstModeOtherBranch = ren1.dstCacheEntry.stages[stage].mode
-
-            tryMerge = False
-            
-            if ren1.dstName in currentDirectorySet:
-                newPath = uniquePath(ren1.dstName, branchName1)
-                output('CONFLICT (rename/directory): Rename',
-                       fmtRename(ren1.srcName, ren1.dstName), 'in', branchName1,
-                       'directory', ren1.dstName, 'added in', branchName2)
-                output('Renaming', ren1.srcName, 'to', newPath, 'instead')
-                cleanMerge = False
-                removeFile(False, ren1.dstName)
-                updateFile(False, ren1.dstSha, ren1.dstMode, newPath)
-            elif srcShaOtherBranch == None:
-                output('CONFLICT (rename/delete): Rename',
-                       fmtRename(ren1.srcName, ren1.dstName), 'in',
-                       branchName1, 'and deleted in', branchName2)
-                cleanMerge = False
-                updateFile(False, ren1.dstSha, ren1.dstMode, ren1.dstName)
-            elif dstShaOtherBranch:
-                newPath = uniquePath(ren1.dstName, branchName2)
-                output('CONFLICT (rename/add): Rename',
-                       fmtRename(ren1.srcName, ren1.dstName), 'in',
-                       branchName1 + '.', ren1.dstName, 'added in', branchName2)
-                output('Adding as', newPath, 'instead')
-                updateFile(False, dstShaOtherBranch, dstModeOtherBranch, newPath)
-                cleanMerge = False
-                tryMerge = True
-            elif renames2.getDst(ren1.dstName):
-                dst2 = renames2.getDst(ren1.dstName)
-                newPath1 = uniquePath(ren1.dstName, branchName1)
-                newPath2 = uniquePath(dst2.dstName, branchName2)
-                output('CONFLICT (rename/rename): Rename',
-                       fmtRename(ren1.srcName, ren1.dstName), 'in',
-                       branchName1+'. Rename',
-                       fmtRename(dst2.srcName, dst2.dstName), 'in', branchName2)
-                output('Renaming', ren1.srcName, 'to', newPath1, 'and',
-                       dst2.srcName, 'to', newPath2, 'instead')
-                removeFile(False, ren1.dstName)
-                updateFile(False, ren1.dstSha, ren1.dstMode, newPath1)
-                updateFile(False, dst2.dstSha, dst2.dstMode, newPath2)
-                dst2.processed = True
-                cleanMerge = False
-            else:
-                tryMerge = True
-
-            if tryMerge:
-
-                oName, oSHA1, oMode = ren1.srcName, ren1.srcSha, ren1.srcMode
-                aName, bName = ren1.dstName, ren1.srcName
-                aSHA1, bSHA1 = ren1.dstSha, srcShaOtherBranch
-                aMode, bMode = ren1.dstMode, srcModeOtherBranch
-                aBranch, bBranch = branchName1, branchName2
-
-                if renamesA != renames1:
-                    aName, bName = bName, aName
-                    aSHA1, bSHA1 = bSHA1, aSHA1
-                    aMode, bMode = bMode, aMode
-                    aBranch, bBranch = bBranch, aBranch
-
-                [resSha, resMode, clean, merge] = \
-                         mergeFile(oName, oSHA1, oMode,
-                                   aName, aSHA1, aMode,
-                                   bName, bSHA1, bMode,
-                                   aBranch, bBranch);
-
-                if merge or not clean:
-                    output('Renaming', fmtRename(ren1.srcName, ren1.dstName))
-
-                if merge:
-                    output('Auto-merging', ren1.dstName)
-
-                if not clean:
-                    output('CONFLICT (rename/modify): Merge conflict in',
-                           ren1.dstName)
-                    cleanMerge = False
-
-                    if not cacheOnly:
-                        setIndexStages(ren1.dstName,
-                                       oSHA1, oMode,
-                                       aSHA1, aMode,
-                                       bSHA1, bMode)
-
-                updateFile(clean, resSha, resMode, ren1.dstName)
-
-    return cleanMerge
-
-# Per entry merge function
-# ------------------------
-
-def processEntry(entry, branch1Name, branch2Name):
-    '''Merge one cache entry.'''
-
-    debug('processing', entry.path, 'clean cache:', cacheOnly)
-
-    cleanMerge = True
-
-    path = entry.path
-    oSha = entry.stages[1].sha1
-    oMode = entry.stages[1].mode
-    aSha = entry.stages[2].sha1
-    aMode = entry.stages[2].mode
-    bSha = entry.stages[3].sha1
-    bMode = entry.stages[3].mode
-
-    assert(oSha == None or isSha(oSha))
-    assert(aSha == None or isSha(aSha))
-    assert(bSha == None or isSha(bSha))
-
-    assert(oMode == None or type(oMode) is int)
-    assert(aMode == None or type(aMode) is int)
-    assert(bMode == None or type(bMode) is int)
-
-    if (oSha and (not aSha or not bSha)):
-    #
-    # Case A: Deleted in one
-    #
-        if (not aSha     and not bSha) or \
-           (aSha == oSha and not bSha) or \
-           (not aSha     and bSha == oSha):
-    # Deleted in both or deleted in one and unchanged in the other
-            if aSha:
-                output('Removing', path)
-            removeFile(True, path)
-        else:
-    # Deleted in one and changed in the other
-            cleanMerge = False
-            if not aSha:
-                output('CONFLICT (delete/modify):', path, 'deleted in',
-                       branch1Name, 'and modified in', branch2Name + '.',
-                       'Version', branch2Name, 'of', path, 'left in tree.')
-                mode = bMode
-                sha = bSha
-            else:
-                output('CONFLICT (modify/delete):', path, 'deleted in',
-                       branch2Name, 'and modified in', branch1Name + '.',
-                       'Version', branch1Name, 'of', path, 'left in tree.')
-                mode = aMode
-                sha = aSha
-
-            updateFile(False, sha, mode, path)
-
-    elif (not oSha and aSha     and not bSha) or \
-         (not oSha and not aSha and bSha):
-    #
-    # Case B: Added in one.
-    #
-        if aSha:
-            addBranch = branch1Name
-            otherBranch = branch2Name
-            mode = aMode
-            sha = aSha
-            conf = 'file/directory'
-        else:
-            addBranch = branch2Name
-            otherBranch = branch1Name
-            mode = bMode
-            sha = bSha
-            conf = 'directory/file'
-    
-        if path in currentDirectorySet:
-            cleanMerge = False
-            newPath = uniquePath(path, addBranch)
-            output('CONFLICT (' + conf + '):',
-                   'There is a directory with name', path, 'in',
-                   otherBranch + '. Adding', path, 'as', newPath)
-
-            removeFile(False, path)
-            updateFile(False, sha, mode, newPath)
-        else:
-            output('Adding', path)
-            updateFile(True, sha, mode, path)
-    
-    elif not oSha and aSha and bSha:
-    #
-    # Case C: Added in both (check for same permissions).
-    #
-        if aSha == bSha:
-            if aMode != bMode:
-                cleanMerge = False
-                output('CONFLICT: File', path,
-                       'added identically in both branches, but permissions',
-                       'conflict', '0%o' % aMode, '->', '0%o' % bMode)
-                output('CONFLICT: adding with permission:', '0%o' % aMode)
-
-                updateFile(False, aSha, aMode, path)
-            else:
-                # This case is handled by git-read-tree
-                assert(False)
-        else:
-            cleanMerge = False
-            newPath1 = uniquePath(path, branch1Name)
-            newPath2 = uniquePath(path, branch2Name)
-            output('CONFLICT (add/add): File', path,
-                   'added non-identically in both branches. Adding as',
-                   newPath1, 'and', newPath2, 'instead.')
-            removeFile(False, path)
-            updateFile(False, aSha, aMode, newPath1)
-            updateFile(False, bSha, bMode, newPath2)
-
-    elif oSha and aSha and bSha:
-    #
-    # case D: Modified in both, but differently.
-    #
-        output('Auto-merging', path)
-        [sha, mode, clean, dummy] = \
-              mergeFile(path, oSha, oMode,
-                        path, aSha, aMode,
-                        path, bSha, bMode,
-                        branch1Name, branch2Name)
-        if clean:
-            updateFile(True, sha, mode, path)
-        else:
-            cleanMerge = False
-            output('CONFLICT (content): Merge conflict in', path)
-
-            if cacheOnly:
-                updateFile(False, sha, mode, path)
-            else:
-                updateFileExt(sha, mode, path, updateCache=False, updateWd=True)
-    else:
-        die("ERROR: Fatal merge failure, shouldn't happen.")
-
-    return cleanMerge
-
-def usage():
-    die('Usage:', sys.argv[0], ' <base>... -- <head> <remote>..')
-
-# main entry point as merge strategy module
-# The first parameters up to -- are merge bases, and the rest are heads.
-
-if len(sys.argv) < 4:
-    usage()
-
-bases = []
-for nextArg in xrange(1, len(sys.argv)):
-    if sys.argv[nextArg] == '--':
-        if len(sys.argv) != nextArg + 3:
-            die('Not handling anything other than two heads merge.')
-        try:
-            h1 = firstBranch = sys.argv[nextArg + 1]
-            h2 = secondBranch = sys.argv[nextArg + 2]
-        except IndexError:
-            usage()
-        break
-    else:
-        bases.append(sys.argv[nextArg])
-
-print 'Merging', h1, 'with', h2
-
-try:
-    h1 = runProgram(['git-rev-parse', '--verify', h1 + '^0']).rstrip()
-    h2 = runProgram(['git-rev-parse', '--verify', h2 + '^0']).rstrip()
-
-    if len(bases) == 1:
-        base = runProgram(['git-rev-parse', '--verify',
-                           bases[0] + '^0']).rstrip()
-        ancestor = Commit(base, None)
-        [dummy, clean] = merge(Commit(h1, None), Commit(h2, None),
-                               firstBranch, secondBranch, None, 0,
-                               ancestor)
-    else:
-        graph = buildGraph([h1, h2])
-        [dummy, clean] = merge(graph.shaMap[h1], graph.shaMap[h2],
-                               firstBranch, secondBranch, graph)
-
-    print ''
-except:
-    if isinstance(sys.exc_info()[1], SystemExit):
-        raise
-    else:
-        traceback.print_exc(None, sys.stderr)
-        sys.exit(2)
-
-if clean:
-    sys.exit(0)
-else:
-    sys.exit(1)
diff --git a/git-merge.sh b/git-merge.sh
index cb094388bb..84c3acfe63 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -9,16 +9,13 @@ USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <
 LF='
 '
 
-all_strategies='recur recursive recursive-old octopus resolve stupid ours'
+all_strategies='recur recursive octopus resolve stupid ours'
 default_twohead_strategies='recursive'
 default_octopus_strategies='octopus'
 no_trivial_merge_strategies='ours'
 use_strategies=
 
 index_merge=t
-if test "@@NO_PYTHON@@"; then
-	all_strategies='recur recursive resolve octopus stupid ours'
-fi
 
 dropsave() {
 	rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" \
diff --git a/git-rebase.sh b/git-rebase.sh
index 546fa446fc..25530dfdc5 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -302,15 +302,6 @@ then
 	exit $?
 fi
 
-if test "@@NO_PYTHON@@" && test "$strategy" = "recursive-old"
-then
-	die 'The recursive-old merge strategy is written in Python,
-which this installation of git was not configured with.  Please consider
-a different merge strategy (e.g. recursive, resolve, or stupid)
-or install Python and git with Python support.'
-
-fi
-
 # start doing a rebase with git-merge
 # this is rename-aware if the recursive (default) strategy is used
 
diff --git a/git.spec.in b/git.spec.in
index 83268fc9d9..f2374b7331 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -24,7 +24,7 @@ This is a dummy package which brings in all subpackages.
 %package core
 Summary:	Core git tools
 Group:		Development/Tools
-Requires:	zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, python >= 2.3, expat
+Requires:	zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, expat
 %description core
 This is a stupid (but extremely fast) directory content manager.  It
 doesn't do a whole lot, but what it _does_ do is track directory
diff --git a/gitMergeCommon.py b/gitMergeCommon.py
deleted file mode 100644
index fdbf9e4778..0000000000
--- a/gitMergeCommon.py
+++ /dev/null
@@ -1,275 +0,0 @@
-#
-# Copyright (C) 2005 Fredrik Kuivinen
-#
-
-import sys, re, os, traceback
-from sets import Set
-
-def die(*args):
-    printList(args, sys.stderr)
-    sys.exit(2)
-
-def printList(list, file=sys.stdout):
-    for x in list:
-        file.write(str(x))
-        file.write(' ')
-    file.write('\n')
-
-import subprocess
-
-# Debugging machinery
-# -------------------
-
-DEBUG = 0
-functionsToDebug = Set()
-
-def addDebug(func):
-    if type(func) == str:
-        functionsToDebug.add(func)
-    else:
-        functionsToDebug.add(func.func_name)
-
-def debug(*args):
-    if DEBUG:
-        funcName = traceback.extract_stack()[-2][2]
-        if funcName in functionsToDebug:
-            printList(args)
-
-# Program execution
-# -----------------
-
-class ProgramError(Exception):
-    def __init__(self, progStr, error):
-        self.progStr = progStr
-        self.error = error
-
-    def __str__(self):
-        return self.progStr + ': ' + self.error
-
-addDebug('runProgram')
-def runProgram(prog, input=None, returnCode=False, env=None, pipeOutput=True):
-    debug('runProgram prog:', str(prog), 'input:', str(input))
-    if type(prog) is str:
-        progStr = prog
-    else:
-        progStr = ' '.join(prog)
-    
-    try:
-        if pipeOutput:
-            stderr = subprocess.STDOUT
-            stdout = subprocess.PIPE
-        else:
-            stderr = None
-            stdout = None
-        pop = subprocess.Popen(prog,
-                               shell = type(prog) is str,
-                               stderr=stderr,
-                               stdout=stdout,
-                               stdin=subprocess.PIPE,
-                               env=env)
-    except OSError, e:
-        debug('strerror:', e.strerror)
-        raise ProgramError(progStr, e.strerror)
-
-    if input != None:
-        pop.stdin.write(input)
-    pop.stdin.close()
-
-    if pipeOutput:
-        out = pop.stdout.read()
-    else:
-        out = ''
-
-    code = pop.wait()
-    if returnCode:
-        ret = [out, code]
-    else:
-        ret = out
-    if code != 0 and not returnCode:
-        debug('error output:', out)
-        debug('prog:', prog)
-        raise ProgramError(progStr, out)
-#    debug('output:', out.replace('\0', '\n'))
-    return ret
-
-# Code for computing common ancestors
-# -----------------------------------
-
-currentId = 0
-def getUniqueId():
-    global currentId
-    currentId += 1
-    return currentId
-
-# The 'virtual' commit objects have SHAs which are integers
-shaRE = re.compile('^[0-9a-f]{40}$')
-def isSha(obj):
-    return (type(obj) is str and bool(shaRE.match(obj))) or \
-           (type(obj) is int and obj >= 1)
-
-class Commit(object):
-    __slots__ = ['parents', 'firstLineMsg', 'children', '_tree', 'sha',
-                 'virtual']
-
-    def __init__(self, sha, parents, tree=None):
-        self.parents = parents
-        self.firstLineMsg = None
-        self.children = []
-
-        if tree:
-            tree = tree.rstrip()
-            assert(isSha(tree))
-        self._tree = tree
-
-        if not sha:
-            self.sha = getUniqueId()
-            self.virtual = True
-            self.firstLineMsg = 'virtual commit'
-            assert(isSha(tree))
-        else:
-            self.virtual = False
-            self.sha = sha.rstrip()
-        assert(isSha(self.sha))
-
-    def tree(self):
-        self.getInfo()
-        assert(self._tree != None)
-        return self._tree
-
-    def shortInfo(self):
-        self.getInfo()
-        return str(self.sha) + ' ' + self.firstLineMsg
-
-    def __str__(self):
-        return self.shortInfo()
-
-    def getInfo(self):
-        if self.virtual or self.firstLineMsg != None:
-            return
-        else:
-            info = runProgram(['git-cat-file', 'commit', self.sha])
-            info = info.split('\n')
-            msg = False
-            for l in info:
-                if msg:
-                    self.firstLineMsg = l
-                    break
-                else:
-                    if l.startswith('tree'):
-                        self._tree = l[5:].rstrip()
-                    elif l == '':
-                        msg = True
-
-class Graph:
-    def __init__(self):
-        self.commits = []
-        self.shaMap = {}
-
-    def addNode(self, node):
-        assert(isinstance(node, Commit))
-        self.shaMap[node.sha] = node
-        self.commits.append(node)
-        for p in node.parents:
-            p.children.append(node)
-        return node
-
-    def reachableNodes(self, n1, n2):
-        res = {}
-        def traverse(n):
-            res[n] = True
-            for p in n.parents:
-                traverse(p)
-
-        traverse(n1)
-        traverse(n2)
-        return res
-
-    def fixParents(self, node):
-        for x in range(0, len(node.parents)):
-            node.parents[x] = self.shaMap[node.parents[x]]
-
-# addDebug('buildGraph')
-def buildGraph(heads):
-    debug('buildGraph heads:', heads)
-    for h in heads:
-        assert(isSha(h))
-
-    g = Graph()
-
-    out = runProgram(['git-rev-list', '--parents'] + heads)
-    for l in out.split('\n'):
-        if l == '':
-            continue
-        shas = l.split(' ')
-
-        # This is a hack, we temporarily use the 'parents' attribute
-        # to contain a list of SHA1:s. They are later replaced by proper
-        # Commit objects.
-        c = Commit(shas[0], shas[1:])
-
-        g.commits.append(c)
-        g.shaMap[c.sha] = c
-
-    for c in g.commits:
-        g.fixParents(c)
-
-    for c in g.commits:
-        for p in c.parents:
-            p.children.append(c)
-    return g
-
-# Write the empty tree to the object database and return its SHA1
-def writeEmptyTree():
-    tmpIndex = os.environ.get('GIT_DIR', '.git') + '/merge-tmp-index'
-    def delTmpIndex():
-        try:
-            os.unlink(tmpIndex)
-        except OSError:
-            pass
-    delTmpIndex()
-    newEnv = os.environ.copy()
-    newEnv['GIT_INDEX_FILE'] = tmpIndex
-    res = runProgram(['git-write-tree'], env=newEnv).rstrip()
-    delTmpIndex()
-    return res
-
-def addCommonRoot(graph):
-    roots = []
-    for c in graph.commits:
-        if len(c.parents) == 0:
-            roots.append(c)
-
-    superRoot = Commit(sha=None, parents=[], tree=writeEmptyTree())
-    graph.addNode(superRoot)
-    for r in roots:
-        r.parents = [superRoot]
-    superRoot.children = roots
-    return superRoot
-
-def getCommonAncestors(graph, commit1, commit2):
-    '''Find the common ancestors for commit1 and commit2'''
-    assert(isinstance(commit1, Commit) and isinstance(commit2, Commit))
-
-    def traverse(start, set):
-        stack = [start]
-        while len(stack) > 0:
-            el = stack.pop()
-            set.add(el)
-            for p in el.parents:
-                if p not in set:
-                    stack.append(p)
-    h1Set = Set()
-    h2Set = Set()
-    traverse(commit1, h1Set)
-    traverse(commit2, h2Set)
-    shared = h1Set.intersection(h2Set)
-
-    if len(shared) == 0:
-        shared = [addCommonRoot(graph)]
-        
-    res = Set()
-
-    for s in shared:
-        if len([c for c in s.children if c in shared]) == 0:
-            res.add(s)
-    return list(res)
diff --git a/t/Makefile b/t/Makefile
index 89835093fb..e1c6c92d9c 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -13,10 +13,6 @@ SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 TSVN = $(wildcard t91[0-9][0-9]-*.sh)
 
-ifdef NO_PYTHON
-	GIT_TEST_OPTS += --no-python
-endif
-
 all: $(T) clean
 
 $(T):
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 6aff0b808c..81f3bedc90 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -20,10 +20,10 @@ modification *should* take notice and update the test vectors here.
 
 ################################################################
 # It appears that people are getting bitten by not installing
-# 'merge' (usually part of RCS package in binary distributions)
-# or have too old python without subprocess.  Check them and error
-# out before running any tests.  Also catch the bogosity of trying
-# to run tests without building while we are at it.
+# 'merge' (usually part of RCS package in binary distributions).
+# Check this and error out before running any tests.  Also catch
+# the bogosity of trying to run tests without building while we
+# are at it.
 
 ../git >/dev/null
 if test $? != 1
@@ -42,12 +42,6 @@ fi
 
 . ./test-lib.sh
 
-test "$no_python" || "$PYTHON" -c 'import subprocess' || {
-	echo >&2 'Your python seem to lack "subprocess" module.
-Please check INSTALL document.'
-	exit 1
-}
-
 ################################################################
 # init-db has been done in an empty repository.
 # make sure it is empty.
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 3895f16709..ac7be769b4 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -76,7 +76,8 @@ do
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 		verbose=t; shift ;;
 	--no-python)
-		no_python=t; shift ;;
+		# noop now...
+		shift ;;
 	*)
 		break ;;
 	esac
@@ -210,18 +211,6 @@ GIT_EXEC_PATH=$(pwd)/..
 HOME=$(pwd)/trash
 export PATH GIT_EXEC_PATH HOME
 
-# Similarly use ../compat/subprocess.py if our python does not
-# have subprocess.py on its own.
-PYTHON=`sed -e '1{
-	s/^#!//
-	q
-}' ../git-merge-recursive-old` || {
-	error "You haven't built things yet, have you?"
-}
-"$PYTHON" -c 'import subprocess' 2>/dev/null || {
-	PYTHONPATH=$(pwd)/../compat
-	export PYTHONPATH
-}
 GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 export GITPERLLIB
 test -d ../templates/blt || {

From 17bcdad3b7baa3b12c662663372f1e3cd560dd8e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 20 Nov 2006 01:06:09 -0800
Subject: [PATCH 009/330] git-merge: make it usable as the first class UI

This teaches the oft-requested syntax

	git merge $commit

to implement merging the named commit to the current branch.
This hopefully would make "git merge" usable as the first class
UI instead of being a mere backend for "git pull".

Most notably, $commit above can be any committish, so you can
say for example:

	git merge js/shortlog~2

to merge early part of a topic branch without merging the rest
of it.

A custom merge message can be given with the new --message=<msg>
parameter.  The message is prepended in front of the usual
"Merge ..." message autogenerated with fmt-merge-message.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-merge.txt | 18 ++++++++---
 git-merge.sh                | 61 ++++++++++++++++++++++++++++++++-----
 2 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index bebf30ad3d..e2954aa76e 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -8,12 +8,14 @@ git-merge - Grand Unified Merge Driver
 
 SYNOPSIS
 --------
-'git-merge' [-n] [--no-commit] [-s <strategy>]... <msg> <head> <remote> <remote>...
-
+[verse]
+'git-merge' [-n] [--no-commit] [--squash] [-s <strategy>]...
+	[--reflog-action=<action>]
+	-m=<msg> <remote> <remote>...
 
 DESCRIPTION
 -----------
-This is the top-level user interface to the merge machinery
+This is the top-level interface to the merge machinery
 which drives multiple merge strategy scripts.
 
 
@@ -27,13 +29,19 @@ include::merge-options.txt[]
 	to give a good default for automated `git-merge` invocations.
 
 <head>::
-	our branch head commit.
+	Our branch head commit.  This has to be `HEAD`, so new
+	syntax does not require it
 
 <remote>::
-	other branch head merged into our branch.  You need at
+	Other branch head merged into our branch.  You need at
 	least one <remote>.  Specifying more than one <remote>
 	obviously means you are trying an Octopus.
 
+--reflog-action=<action>::
+	This is used internally when `git-pull` calls this command
+	to record that the merge was created by `pull` command
+	in the `ref-log` entry that results from the merge.
+
 include::merge-strategies.txt[]
 
 
diff --git a/git-merge.sh b/git-merge.sh
index 84c3acfe63..25deb1e867 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -3,7 +3,8 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
-USAGE='[-n] [--no-commit] [--squash] [-s <strategy>]... <merge-message> <head> <remote>+'
+USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [--reflog-action=<action>] [-m=<merge-message>] <commit>+'
+
 . git-sh-setup
 
 LF='
@@ -92,7 +93,7 @@ finish () {
 
 case "$#" in 0) usage ;; esac
 
-rloga=
+rloga= have_message=
 while case "$#" in 0) break ;; esac
 do
 	case "$1" in
@@ -125,17 +126,63 @@ do
 	--reflog-action=*)
 		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 		;;
+	-m=*|--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
+		merge_msg=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		have_message=t
+		;;
+	-m|--m|--me|--mes|--mess|--messa|--messag|--message)
+		shift
+		case "$#" in
+		1)	usage ;;
+		esac
+		merge_msg="$1"
+		have_message=t
+		;;
 	-*)	usage ;;
 	*)	break ;;
 	esac
 	shift
 done
 
-merge_msg="$1"
-shift
-head_arg="$1"
-head=$(git-rev-parse --verify "$1"^0) || usage
-shift
+# This could be traditional "merge <msg> HEAD <commit>..."  and the
+# way we can tell it is to see if the second token is HEAD, but some
+# people might have misused the interface and used a committish that
+# is the same as HEAD there instead.  Traditional format never would
+# have "-m" so it is an additional safety measure to check for it.
+
+if test -z "$have_message" &&
+	second_token=$(git-rev-parse --verify "$2^0" 2>/dev/null) &&
+	head_commit=$(git-rev-parse --verify "HEAD" 2>/dev/null) &&
+	test "$second_token" = "$head_commit"
+then
+	merge_msg="$1"
+	shift
+	head_arg="$1"
+	shift
+else
+	# We are invoked directly as the first-class UI.
+	head_arg=HEAD
+
+	# All the rest are the commits being merged; prepare
+	# the standard merge summary message to be appended to
+	# the given message.  If remote is invalid we will die
+	# later in the common codepath so we discard the error
+	# in this loop.
+	merge_name=$(for remote
+		do
+			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null)
+			if git show-ref -q --verify "refs/heads/$remote"
+			then
+				what=branch
+			else
+				what=commit
+			fi
+			echo "$rh		$what '$remote'"
+		done | git-fmt-merge-msg
+	)
+	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"
+fi
+head=$(git-rev-parse --verify "$head_arg"^0) || usage
 
 # All the rest are remote heads
 test "$#" = 0 && usage ;# we need at least one remote head.

From 8092c7f6af044836abf83ed26d542327a4b95c08 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 21 Nov 2006 21:13:28 -0800
Subject: [PATCH 010/330] merge: allow merging into a yet-to-be-born branch.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/git-merge.sh b/git-merge.sh
index 25deb1e867..dd4e83dede 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -159,6 +159,24 @@ then
 	shift
 	head_arg="$1"
 	shift
+elif ! git-rev-parse --verify HEAD 2>/dev/null
+then
+	# If the merged head is a valid one there is no reason to
+	# forbid "git merge" into a branch yet to be born.  We do
+	# the same for "git pull".
+	if test 1 -ne $#
+	then
+		echo >&2 "Can merge only exactly one commit into empty head"
+		exit 1
+	fi
+
+	rh=$(git rev-parse --verify "$1^0") ||
+		die "$1 - not something we can merge"
+
+	git-update-ref -m "initial pull" HEAD "$rh" "" &&
+	git-read-tree --reset -u HEAD
+	exit
+
 else
 	# We are invoked directly as the first-class UI.
 	head_arg=HEAD

From f4204ab9f6a192cdb9a68150e031d7183688bfeb Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 21 Nov 2006 23:36:35 -0800
Subject: [PATCH 011/330] Store peeled refs in packed-refs (take 2).

This fixes the previous implementation which failed to optimize
repositories with tons of lightweight tags.  The updated
packed-refs format begins with "# packed-refs with:" line that
lists the kind of extended data the file records.  Currently,
there is only one such extension defined, "peeled".  This stores
the "peeled tag" on a line that immediately follows a line for a
tag object itself in the format "^<sha-1>".

The header line itself and any extended data are ignored by
older implementation, so packed-refs file generated with this
version can still be used by older git.  packed-refs made by
older git can of course be used with this version.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-refs.c |   8 +++-
 builtin-show-ref.c  |   6 ++-
 refs.c              | 111 +++++++++++++++++++++++++-------------------
 refs.h              |   7 ++-
 4 files changed, 77 insertions(+), 55 deletions(-)

diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
index ee5a5565af..8dc5b9efff 100644
--- a/builtin-pack-refs.c
+++ b/builtin-pack-refs.c
@@ -46,8 +46,8 @@ static int handle_one_ref(const char *path, const unsigned char *sha1,
 		if (o->type == OBJ_TAG) {
 			o = deref_tag(o, path, 0);
 			if (o)
-				fprintf(cb->refs_file, "%s  %s^{}\n",
-					sha1_to_hex(o->sha1), path);
+				fprintf(cb->refs_file, "^%s\n",
+					sha1_to_hex(o->sha1));
 		}
 	}
 
@@ -111,6 +111,10 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
 	if (!cbdata.refs_file)
 		die("unable to create ref-pack file structure (%s)",
 		    strerror(errno));
+
+	/* perhaps other traits later as well */
+	fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
+
 	for_each_ref(handle_one_ref, &cbdata);
 	fflush(cbdata.refs_file);
 	fsync(fd);
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 9ae3d08546..073979855b 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -67,8 +67,10 @@ match:
 		return 0;
 
 	if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
-		hex = find_unique_abbrev(peeled, abbrev);
-		printf("%s %s^{}\n", hex, refname);
+		if (!is_null_sha1(peeled)) {
+			hex = find_unique_abbrev(peeled, abbrev);
+			printf("%s %s^{}\n", hex, refname);
+		}
 	}
 	else {
 		obj = parse_object(sha1);
diff --git a/refs.c b/refs.c
index 75cbc0e7ef..96ea8b6907 100644
--- a/refs.c
+++ b/refs.c
@@ -5,14 +5,18 @@
 
 #include <errno.h>
 
+/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
+#define REF_KNOWS_PEELED 04
+
 struct ref_list {
 	struct ref_list *next;
-	unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
+	unsigned char flag; /* ISSYMREF? ISPACKED? */
 	unsigned char sha1[20];
+	unsigned char peeled[20];
 	char name[FLEX_ARRAY];
 };
 
-static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
+static const char *parse_ref_line(char *line, unsigned char *sha1)
 {
 	/*
 	 * 42: the answer to everything.
@@ -23,7 +27,6 @@ static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
 	 *  +1 (newline at the end of the line)
 	 */
 	int len = strlen(line) - 42;
-	int peeled = 0;
 
 	if (len <= 0)
 		return NULL;
@@ -32,29 +35,18 @@ static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
 	if (!isspace(line[40]))
 		return NULL;
 	line += 41;
-
-	if (isspace(*line)) {
-		/* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
-		line++;
-		len--;
-		peeled = 1;
-	}
+	if (isspace(*line))
+		return NULL;
 	if (line[len] != '\n')
 		return NULL;
 	line[len] = 0;
 
-	if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
-		return NULL;
-
-	if (!peeled)
-		*flag &= ~REF_ISPEELED;
-	else
-		*flag |= REF_ISPEELED;
 	return line;
 }
 
 static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
-				int flag, struct ref_list *list)
+				int flag, struct ref_list *list,
+				struct ref_list **new_entry)
 {
 	int len;
 	struct ref_list **p = &list, *entry;
@@ -66,8 +58,11 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
 			break;
 
 		/* Same as existing entry? */
-		if (!cmp)
+		if (!cmp) {
+			if (new_entry)
+				*new_entry = entry;
 			return list;
+		}
 		p = &entry->next;
 	}
 
@@ -75,10 +70,13 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
 	len = strlen(name) + 1;
 	entry = xmalloc(sizeof(struct ref_list) + len);
 	hashcpy(entry->sha1, sha1);
+	hashclr(entry->peeled);
 	memcpy(entry->name, name, len);
 	entry->flag = flag;
 	entry->next = *p;
 	*p = entry;
+	if (new_entry)
+		*new_entry = entry;
 	return list;
 }
 
@@ -114,27 +112,50 @@ static void invalidate_cached_refs(void)
 	ca->did_loose = ca->did_packed = 0;
 }
 
+static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
+{
+	struct ref_list *list = NULL;
+	struct ref_list *last = NULL;
+	char refline[PATH_MAX];
+	int flag = REF_ISPACKED;
+
+	while (fgets(refline, sizeof(refline), f)) {
+		unsigned char sha1[20];
+		const char *name;
+		static const char header[] = "# pack-refs with:";
+
+		if (!strncmp(refline, header, sizeof(header)-1)) {
+			const char *traits = refline + sizeof(header) - 1;
+			if (strstr(traits, " peeled "))
+				flag |= REF_KNOWS_PEELED;
+			/* perhaps other traits later as well */
+			continue;
+		}
+
+		name = parse_ref_line(refline, sha1);
+		if (name) {
+			list = add_ref(name, sha1, flag, list, &last);
+			continue;
+		}
+		if (last &&
+		    refline[0] == '^' &&
+		    strlen(refline) == 42 &&
+		    refline[41] == '\n' &&
+		    !get_sha1_hex(refline + 1, sha1))
+			hashcpy(last->peeled, sha1);
+	}
+	cached_refs->packed = list;
+}
+
 static struct ref_list *get_packed_refs(void)
 {
 	if (!cached_refs.did_packed) {
-		struct ref_list *refs = NULL;
 		FILE *f = fopen(git_path("packed-refs"), "r");
+		cached_refs.packed = NULL;
 		if (f) {
-			struct ref_list *list = NULL;
-			char refline[PATH_MAX];
-			while (fgets(refline, sizeof(refline), f)) {
-				unsigned char sha1[20];
-				int flag = REF_ISPACKED;
-				const char *name =
-					parse_ref_line(refline, sha1, &flag);
-				if (!name)
-					continue;
-				list = add_ref(name, sha1, flag, list);
-			}
+			read_packed_refs(f, &cached_refs);
 			fclose(f);
-			refs = list;
 		}
-		cached_refs.packed = refs;
 		cached_refs.did_packed = 1;
 	}
 	return cached_refs.packed;
@@ -177,7 +198,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)
 				error("%s points nowhere!", ref);
 				continue;
 			}
-			list = add_ref(ref, sha1, flag, list);
+			list = add_ref(ref, sha1, flag, list, NULL);
 		}
 		free(ref);
 		closedir(dir);
@@ -225,8 +246,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
 		if (lstat(path, &st) < 0) {
 			struct ref_list *list = get_packed_refs();
 			while (list) {
-				if (!(list->flag & REF_ISPEELED) &&
-				    !strcmp(ref, list->name)) {
+				if (!strcmp(ref, list->name)) {
 					hashcpy(sha1, list->sha1);
 					if (flag)
 						*flag |= REF_ISPACKED;
@@ -348,8 +368,6 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 		return 0;
 	if (is_null_sha1(entry->sha1))
 		return 0;
-	if (entry->flag & REF_ISPEELED)
-		return 0;
 	if (!has_sha1_file(entry->sha1)) {
 		error("%s does not point to a valid object!", entry->name);
 		return 0;
@@ -368,22 +386,21 @@ int peel_ref(const char *ref, unsigned char *sha1)
 
 	if ((flag & REF_ISPACKED)) {
 		struct ref_list *list = get_packed_refs();
-		int len = strlen(ref);
 
 		while (list) {
-			if ((list->flag & REF_ISPEELED) &&
-			    !strncmp(list->name, ref, len) &&
-			    strlen(list->name) == len + 3 &&
-			    !strcmp(list->name + len, "^{}")) {
-				hashcpy(sha1, list->sha1);
-				return 0;
+			if (!strcmp(list->name, ref)) {
+				if (list->flag & REF_KNOWS_PEELED) {
+					hashcpy(sha1, list->peeled);
+					return 0;
+				}
+				/* older pack-refs did not leave peeled ones */
+				break;
 			}
 			list = list->next;
 		}
-		/* older pack-refs did not leave peeled ones in */
 	}
 
-	/* otherwise ... */
+	/* fallback - callers should not call this for unpacked refs */
 	o = parse_object(base);
 	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, ref, 0);
diff --git a/refs.h b/refs.h
index 40048a6919..cd1e1d620e 100644
--- a/refs.h
+++ b/refs.h
@@ -10,14 +10,13 @@ struct ref_lock {
 	int force_write;
 };
 
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
  */
-#define REF_ISSYMREF 01
-#define REF_ISPACKED 02
-#define REF_ISPEELED 04 /* internal use */
-
 typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);
 extern int head_ref(each_ref_fn, void *);
 extern int for_each_ref(each_ref_fn, void *);

From 3cd204e518ce832402c291be73292baa149d0240 Mon Sep 17 00:00:00 2001
From: Paul Mackerras <paulus@samba.org>
Date: Thu, 23 Nov 2006 21:06:16 +1100
Subject: [PATCH 012/330] gitk: Fix enabling/disabling of menu items on Mac OS
 X

It seems that under Mac OS X, the menus get some extra entries (or
possibly fewer entries), leading to references to entries by an
absolute number being off.  This leads to an error when invoking
gitk --all under Mac OS X, because the "Edit view" and "Delete view"
entries aren't were gitk expects them, and so enabling them gives an
error.

This changes the code so it refers to menu entries by their content,
which should solve the problem.

Signed-off-by: Paul Mackerras <paulus@samba.org>
---
 gitk | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/gitk b/gitk
index ab383b3ad2..3dabc69516 100755
--- a/gitk
+++ b/gitk
@@ -554,7 +554,7 @@ proc makewindow {} {
     pack .ctop.top.lbar.vlabel -side left -fill y
     global viewhlmenu selectedhlview
     set viewhlmenu [tk_optionMenu .ctop.top.lbar.vhl selectedhlview None]
-    $viewhlmenu entryconf 0 -command delvhighlight
+    $viewhlmenu entryconf None -command delvhighlight
     $viewhlmenu conf -font $uifont
     .ctop.top.lbar.vhl conf -font $uifont
     pack .ctop.top.lbar.vhl -side left -fill y
@@ -1474,7 +1474,7 @@ proc doviewmenu {m first cmd op argv} {
 proc allviewmenus {n op args} {
     global viewhlmenu
 
-    doviewmenu .bar.view 7 [list showview $n] $op $args
+    doviewmenu .bar.view 5 [list showview $n] $op $args
     doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
 }
 
@@ -1516,7 +1516,7 @@ proc newviewok {top n} {
 	set viewperm($n) $newviewperm($n)
 	if {$newviewname($n) ne $viewname($n)} {
 	    set viewname($n) $newviewname($n)
-	    doviewmenu .bar.view 7 [list showview $n] \
+	    doviewmenu .bar.view 5 [list showview $n] \
 		entryconf [list -label $viewname($n)]
 	    doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
 		entryconf [list -label $viewname($n) -value $viewname($n)]
@@ -1632,8 +1632,8 @@ proc showview {n} {
 
     set curview $n
     set selectedview $n
-    .bar.view entryconf 2 -state [expr {$n == 0? "disabled": "normal"}]
-    .bar.view entryconf 3 -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
+    .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
 
     if {![info exists viewdata($n)]} {
 	set pending_select $selid
@@ -4899,9 +4899,9 @@ proc rowmenu {x y id} {
     } else {
 	set state normal
     }
-    $rowctxmenu entryconfigure 0 -state $state
-    $rowctxmenu entryconfigure 1 -state $state
-    $rowctxmenu entryconfigure 2 -state $state
+    $rowctxmenu entryconfigure "Diff this*" -state $state
+    $rowctxmenu entryconfigure "Diff selected*" -state $state
+    $rowctxmenu entryconfigure "Make patch" -state $state
     set rowmenuid $id
     tk_popup $rowctxmenu $x $y
 }
@@ -6305,8 +6305,8 @@ if {$cmdline_files ne {} || $revtreeargs ne {}} {
     set viewargs(1) $revtreeargs
     set viewperm(1) 0
     addviewmenu 1
-    .bar.view entryconf 2 -state normal
-    .bar.view entryconf 3 -state normal
+    .bar.view entryconf Edit* -state normal
+    .bar.view entryconf Delete* -state normal
 }
 
 if {[info exists permviews]} {

From 28b8e61fc63b6776a91e8afd03c7171fbf0779b0 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 22 Nov 2006 21:57:14 -0800
Subject: [PATCH 013/330] git-fetch: reuse ls-remote result.

This will become necessary to update the dumb protocol
transports to fetch from a repository with packed and then
pruned tags.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-fetch.sh | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/git-fetch.sh b/git-fetch.sh
index eb32476bbd..170c2cb048 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -88,6 +88,10 @@ then
 	: >"$GIT_DIR/FETCH_HEAD"
 fi
 
+# Global that is reused later
+ls_remote_result=$(git ls-remote $upload_pack "$remote") ||
+	die "Cannot find the reflist at $remote"
+
 append_fetch_head () {
     head_="$1"
     remote_="$2"
@@ -233,10 +237,7 @@ reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
 	taglist=`IFS="	" &&
-		  (
-			git-ls-remote $upload_pack --tags "$remote" ||
-			echo fail ouch
-		  ) |
+		  echo "$ls_remote_result" |
 	          while read sha1 name
 		  do
 			case "$sha1" in
@@ -245,6 +246,8 @@ then
 			esac
 			case "$name" in
 			*^*) continue ;;
+			refs/tags/*) ;;
+			*) continue ;;
 			esac
 		  	if git-check-ref-format "$name"
 			then
@@ -431,7 +434,7 @@ case "$no_tags$tags" in
 		# effective only when we are following remote branch
 		# using local tracking branch.
 		taglist=$(IFS=" " &&
-		git-ls-remote $upload_pack --tags "$remote" |
+		echo "$ls_remote_result" |
 		sed -n	-e 's|^\('"$_x40"'\)	\(refs/tags/.*\)^{}$|\1 \2|p' \
 			-e 's|^\('"$_x40"'\)	\(refs/tags/.*\)$|\1 \2|p' |
 		while read sha1 name

From 2986c02217f98809d8990e7679edf0f5d99f904d Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 22 Nov 2006 22:24:09 -0800
Subject: [PATCH 014/330] git-fetch: fix dumb protocol transport to fetch from
 pack-pruned ref

Earlier, commit walkers downloaded loose refs from refs/ hierarchy
of the remote side to find where to start walking; this would
not work for a repository whose refs are packed and then pruned.

With the previous change, we have ls-remote output from the
remote in-core; we can use the value from there without
requiring loose refs anymore.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-fetch.sh | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/git-fetch.sh b/git-fetch.sh
index 170c2cb048..06b66b968d 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -307,22 +307,20 @@ fetch_main () {
 		"`git-repo-config --bool http.noEPSV`" = true ]; then
 	      noepsv_opt="--disable-epsv"
 	  fi
-	  max_depth=5
-	  depth=0
-	  head="ref: $remote_name"
-	  while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
-	  do
-	    remote_name_quoted=$(@@PERL@@ -e '
-	      my $u = $ARGV[0];
-              $u =~ s/^ref:\s*//;
-	      $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
-	      print "$u";
-	  ' "$head")
-	    head=$(curl -nsfL $curl_extra_args $noepsv_opt "$remote/$remote_name_quoted")
-	    depth=$( expr \( $depth + 1 \) )
-	  done
+
+	  # Find $remote_name from ls-remote output.
+	  head=$(
+		IFS='	'
+		echo "$ls_remote_result" |
+		while read sha1 name
+		do
+			test "z$name" = "z$remote_name" || continue
+			echo "$sha1"
+			break
+		done
+	  )
 	  expr "z$head" : "z$_x40\$" >/dev/null ||
-	      die "Failed to fetch $remote_name from $remote"
+		die "No such ref $remote_name at $remote"
 	  echo >&2 "Fetching $remote_name from $remote using $proto"
 	  git-http-fetch -v -a "$head" "$remote/" || exit
 	  ;;

From 5677882be721be5e2706a546d90804da8d8d0bd5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 22 Nov 2006 23:15:00 -0800
Subject: [PATCH 015/330] git-fetch: allow glob pattern in refspec

This adds Andy's refspec glob.  You can have a single line:

	Pull: refs/heads/*:refs/remotes/origin/*

in your ".git/remotes/origin" and say "git fetch" to retrieve
all refs under heads/ at the remote side to remotes/origin/ in
the local repository.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index c325ef761e..e281b7c6eb 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -90,6 +90,39 @@ get_remote_default_refs_for_push () {
 	esac
 }
 
+# Called from canon_refs_list_for_fetch -d "$remote", which
+# is called from get_remote_default_refs_for_fetch to grok
+# refspecs that are retrieved from the configuration, but not
+# from get_remote_refs_for_fetch when it deals with refspecs
+# supplied on the command line.  $ls_remote_result has the list
+# of refs available at remote.
+expand_refs_wildcard () {
+	for ref
+	do
+		# a non glob pattern is given back as-is.
+		expr "z$ref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
+			echo "$ref"
+			continue
+		}
+		from=`expr "z$ref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
+		to=`expr "z$ref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
+		echo "$ls_remote_result" |
+		(
+			IFS='	'
+			while read sha1 name
+			do
+				mapped=${name#"$from"}
+				if test "z$name" != "z${name#'^{}'}" ||
+					test "z$name" = "z$mapped"
+				then
+					continue
+				fi
+				echo "${name}:${to}${mapped}"
+			done
+		)
+	done
+}
+
 # Subroutine to canonicalize remote:local notation.
 canon_refs_list_for_fetch () {
 	# If called from get_remote_default_refs_for_fetch
@@ -107,6 +140,8 @@ canon_refs_list_for_fetch () {
 			merge_branches=$(git-repo-config \
 			    --get-all "branch.${curr_branch}.merge")
 		fi
+		set x $(expand_refs_wildcard "$@")
+		shift
 	fi
 	for ref
 	do

From d4f694ba89857a87e259557d0f236c761b4041ef Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 24 Nov 2006 00:26:49 -0800
Subject: [PATCH 016/330] Allow git push to delete remote ref.

This allows you to say

	git send-pack $URL :refs/heads/$branch

to delete the named remote branch.  The refspec $src:$dst means
replace the destination ref with the object known as $src on the
local side, so this is a natural extension to make an empty $src
mean "No object" to delete the target.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 connect.c            | 11 ++++++++++-
 receive-pack.c       | 45 +++++++++++++++++++++++++++++++++-----------
 send-pack.c          | 41 +++++++++++++++++++++++++++++-----------
 t/t5400-send-pack.sh | 10 ++++++++++
 4 files changed, 84 insertions(+), 23 deletions(-)

diff --git a/connect.c b/connect.c
index b9666cc0d8..f7edba82c4 100644
--- a/connect.c
+++ b/connect.c
@@ -144,6 +144,7 @@ struct refspec {
  * +A:B means overwrite remote B with local A.
  * +A is a shorthand for +A:A.
  * A is a shorthand for A:A.
+ * :B means delete remote B.
  */
 static struct refspec *parse_ref_spec(int nr_refspec, char **refspec)
 {
@@ -240,6 +241,13 @@ static struct ref *try_explicit_object_name(const char *name)
 	unsigned char sha1[20];
 	struct ref *ref;
 	int len;
+
+	if (!*name) {
+		ref = xcalloc(1, sizeof(*ref) + 20);
+		strcpy(ref->name, "(delete)");
+		hashclr(ref->new_sha1);
+		return ref;
+	}
 	if (get_sha1(name, sha1))
 		return NULL;
 	len = strlen(name) + 1;
@@ -262,7 +270,8 @@ static int match_explicit_refs(struct ref *src, struct ref *dst,
 			break;
 		case 0:
 			/* The source could be in the get_sha1() format
-			 * not a reference name.
+			 * not a reference name.  :refs/other is a
+			 * way to delete 'other' ref at the remote end.
 			 */
 			matched_src = try_explicit_object_name(rs[i].src);
 			if (matched_src)
diff --git a/receive-pack.c b/receive-pack.c
index d56898c9b2..1a141dc1e5 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -14,7 +14,7 @@ static int deny_non_fast_forwards = 0;
 static int unpack_limit = 5000;
 static int report_status;
 
-static char capabilities[] = "report-status";
+static char capabilities[] = " report-status delete-refs ";
 static int capabilities_sent;
 
 static int receive_pack_config(const char *var, const char *value)
@@ -113,12 +113,14 @@ static int update(struct command *cmd)
 
 	strcpy(new_hex, sha1_to_hex(new_sha1));
 	strcpy(old_hex, sha1_to_hex(old_sha1));
-	if (!has_sha1_file(new_sha1)) {
+
+	if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
 		cmd->error_string = "bad pack";
 		return error("unpack should have generated %s, "
 			     "but I can't find it!", new_hex);
 	}
-	if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
+	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
+	    !is_null_sha1(old_sha1)) {
 		struct commit *old_commit, *new_commit;
 		struct commit_list *bases, *ent;
 
@@ -138,14 +140,22 @@ static int update(struct command *cmd)
 		return error("hook declined to update %s", name);
 	}
 
-	lock = lock_any_ref_for_update(name, old_sha1);
-	if (!lock) {
-		cmd->error_string = "failed to lock";
-		return error("failed to lock %s", name);
+	if (is_null_sha1(new_sha1)) {
+		if (delete_ref(name, old_sha1)) {
+			cmd->error_string = "failed to delete";
+			return error("failed to delete %s", name);
+		}
+		fprintf(stderr, "%s: %s -> deleted\n", name, old_hex);
+	}
+	else {
+		lock = lock_any_ref_for_update(name, old_sha1);
+		if (!lock) {
+			cmd->error_string = "failed to lock";
+			return error("failed to lock %s", name);
+		}
+		write_ref_sha1(lock, new_sha1, "push");
+		fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 	}
-	write_ref_sha1(lock, new_sha1, "push");
-
-	fprintf(stderr, "%s: %s -> %s\n", name, old_hex, new_hex);
 	return 0;
 }
 
@@ -375,6 +385,16 @@ static void report(const char *unpack_status)
 	packet_flush(1);
 }
 
+static int delete_only(struct command *cmd)
+{
+	while (cmd) {
+		if (!is_null_sha1(cmd->new_sha1))
+			return 0;
+		cmd = cmd->next;
+	}
+	return 1;
+}
+
 int main(int argc, char **argv)
 {
 	int i;
@@ -408,7 +428,10 @@ int main(int argc, char **argv)
 
 	read_head_info();
 	if (commands) {
-		const char *unpack_status = unpack();
+		const char *unpack_status = NULL;
+
+		if (!delete_only(commands))
+			unpack_status = unpack();
 		if (!unpack_status)
 			execute_commands();
 		if (pack_lockfile)
diff --git a/send-pack.c b/send-pack.c
index 447666665b..328dbbc16a 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -271,6 +271,7 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 	int new_refs;
 	int ret = 0;
 	int ask_for_status_report = 0;
+	int allow_deleting_refs = 0;
 	int expect_status_report = 0;
 
 	/* No funny business with the matcher */
@@ -280,6 +281,8 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 	/* Does the other end support the reporting? */
 	if (server_supports("report-status"))
 		ask_for_status_report = 1;
+	if (server_supports("delete-refs"))
+		allow_deleting_refs = 1;
 
 	/* match them up */
 	if (!remote_tail)
@@ -299,9 +302,19 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
 		char old_hex[60], *new_hex;
+		int delete_ref;
+
 		if (!ref->peer_ref)
 			continue;
-		if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+
+		delete_ref = is_null_sha1(ref->peer_ref->new_sha1);
+		if (delete_ref && !allow_deleting_refs) {
+			error("remote does not support deleting refs");
+			ret = -2;
+			continue;
+		}
+		if (!delete_ref &&
+		    !hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
 			if (verbose)
 				fprintf(stderr, "'%s': up-to-date\n", ref->name);
 			continue;
@@ -321,9 +334,13 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 		 *
 		 * (3) if both new and old are commit-ish, and new is a
 		 *     descendant of old, it is OK.
+		 *
+		 * (4) regardless of all of the above, removing :B is
+		 *     always allowed.
 		 */
 
 		if (!force_update &&
+		    !delete_ref &&
 		    !is_zero_sha1(ref->old_sha1) &&
 		    !ref->force) {
 			if (!has_sha1_file(ref->old_sha1) ||
@@ -347,12 +364,8 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 			}
 		}
 		hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
-		if (is_zero_sha1(ref->new_sha1)) {
-			error("cannot happen anymore");
-			ret = -3;
-			continue;
-		}
-		new_refs++;
+		if (!delete_ref)
+			new_refs++;
 		strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 		new_hex = sha1_to_hex(ref->new_sha1);
 
@@ -366,10 +379,16 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 		else
 			packet_write(out, "%s %s %s",
 				     old_hex, new_hex, ref->name);
-		fprintf(stderr, "updating '%s'", ref->name);
-		if (strcmp(ref->name, ref->peer_ref->name))
-			fprintf(stderr, " using '%s'", ref->peer_ref->name);
-		fprintf(stderr, "\n  from %s\n  to   %s\n", old_hex, new_hex);
+		if (delete_ref)
+			fprintf(stderr, "deleting '%s'\n", ref->name);
+		else {
+			fprintf(stderr, "updating '%s'", ref->name);
+			if (strcmp(ref->name, ref->peer_ref->name))
+				fprintf(stderr, " using '%s'",
+					ref->peer_ref->name);
+			fprintf(stderr, "\n  from %s\n  to   %s\n",
+				old_hex, new_hex);
+		}
 	}
 
 	packet_flush(out);
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 8afb899717..28744b35e1 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -64,6 +64,16 @@ test_expect_success \
 	cmp victim/.git/refs/heads/master .git/refs/heads/master
 '
 
+test_expect_success \
+        'push can be used to delete a ref' '
+	cd victim &&
+	git branch extra master &&
+	cd .. &&
+	test -f victim/.git/refs/heads/extra &&
+	git-send-pack ./victim/.git/ :extra master &&
+	! test -f victim/.git/refs/heads/extra
+'
+
 unset GIT_CONFIG GIT_CONFIG_LOCAL
 HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.

From 983d2ee284936e8bf14823863d3945b4d8740b94 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 24 Nov 2006 19:07:24 -0800
Subject: [PATCH 017/330] git-clone: stop dumb protocol from copying refs
 outside heads/ and tags/.

Most notably, the original code first copied refs/remotes/ that
remote side had to local, and overwrote them by mapping refs/heads/
from the remote when a dumb protocol transport was used.

This makes the clone behaviour by dumb protocol in line with the
git native and rsync transports.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-clone.sh | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/git-clone.sh b/git-clone.sh
index 9ed4135544..d4ee93f75b 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -48,6 +48,10 @@ Perhaps git-update-server-info needs to be run there?"
 		case "$name" in
 		*^*)	continue;;
 		esac
+		case "$bare,$name" in
+		yes,* | ,heads/* | ,tags/*) ;;
+		*)	continue ;;
+		esac
 		if test -n "$use_separate_remote" &&
 		   branch_name=`expr "z$name" : 'zheads/\(.*\)'`
 		then

From 75e6e2132006770156d9df1881b4114862919c94 Mon Sep 17 00:00:00 2001
From: Lars Hjemli <hjemli@gmail.com>
Date: Fri, 24 Nov 2006 14:45:10 +0100
Subject: [PATCH 018/330] Add -v and --abbrev options to git-branch

The new -v option makes git-branch show the abbreviated sha1 + subjectline
for each branch.

Additionally, minimum abbreviation length can be specified with
--abbrev=<length>

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-branch.txt |  9 ++++++-
 builtin-branch.c             | 48 ++++++++++++++++++++++++++++++++----
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 5376760813..4f5b5d5028 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,7 +8,7 @@ git-branch - List, create, or delete branches.
 SYNOPSIS
 --------
 [verse]
-'git-branch' [-r] [-a]
+'git-branch' [-r] [-a] [-v] [--abbrev=<length>]
 'git-branch' [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-d | -D) <branchname>...
 
@@ -52,6 +52,13 @@ OPTIONS
 -a::
 	List both remote-tracking branches and local branches.
 
+-v::
+	Show sha1 and subject message for each head.
+
+--abbrev=<length>::
+	Alter minimum display length for sha1 in output listing,
+	default value is 7.
+
 <branchname>::
 	The name of the branch to create or delete.
 	The new branch name must pass all checks defined by
diff --git a/builtin-branch.c b/builtin-branch.c
index 22e3285a4c..69b7b55d86 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -11,7 +11,7 @@
 #include "builtin.h"
 
 static const char builtin_branch_usage[] =
-"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r] | [-a]";
+"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r | -a] [-v] [--abbrev=<length>] ";
 
 
 static const char *head;
@@ -87,10 +87,11 @@ static void delete_branches(int argc, const char **argv, int force)
 struct ref_item {
 	char *name;
 	unsigned int kind;
+	unsigned char sha1[20];
 };
 
 struct ref_list {
-	int index, alloc;
+	int index, alloc, maxwidth;
 	struct ref_item *list;
 	int kinds;
 };
@@ -100,6 +101,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	struct ref_list *ref_list = (struct ref_list*)(cb_data);
 	struct ref_item *newitem;
 	int kind = REF_UNKNOWN_TYPE;
+	int len;
 
 	/* Detect kind */
 	if (!strncmp(refname, "refs/heads/", 11)) {
@@ -128,6 +130,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
 	newitem = &(ref_list->list[ref_list->index++]);
 	newitem->name = xstrdup(refname);
 	newitem->kind = kind;
+	hashcpy(newitem->sha1, sha1);
+	len = strlen(newitem->name);
+	if (len > ref_list->maxwidth)
+		ref_list->maxwidth = len;
 
 	return 0;
 }
@@ -151,7 +157,24 @@ static int ref_cmp(const void *r1, const void *r2)
 	return strcmp(c1->name, c2->name);
 }
 
-static void print_ref_list(int kinds)
+static void print_ref_info(const unsigned char *sha1, int abbrev)
+{
+	struct commit *commit;
+	char subject[256];
+
+
+	commit = lookup_commit(sha1);
+	if (commit && !parse_commit(commit))
+		pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+				    subject, sizeof(subject), 0,
+				    NULL, NULL, 0);
+	else
+		strcpy(subject, " **** invalid ref ****");
+
+	printf(" %s %s\n", find_unique_abbrev(sha1, abbrev), subject);
+}
+
+static void print_ref_list(int kinds, int verbose, int abbrev)
 {
 	int i;
 	char c;
@@ -169,7 +192,13 @@ static void print_ref_list(int kinds)
 				!strcmp(ref_list.list[i].name, head))
 			c = '*';
 
-		printf("%c %s\n", c, ref_list.list[i].name);
+		if (verbose) {
+			printf("%c %-*s", c, ref_list.maxwidth,
+			       ref_list.list[i].name);
+			print_ref_info(ref_list.list[i].sha1, abbrev);
+		}
+		else
+			printf("%c %s\n", c, ref_list.list[i].name);
 	}
 
 	free_ref_list(&ref_list);
@@ -215,6 +244,7 @@ static void create_branch(const char *name, const char *start,
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, force_delete = 0, force_create = 0;
+	int verbose = 0, abbrev = DEFAULT_ABBREV;
 	int reflog = 0;
 	int kinds = REF_LOCAL_BRANCH;
 	int i;
@@ -255,6 +285,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			reflog = 1;
 			continue;
 		}
+		if (!strncmp(arg, "--abbrev=", 9)) {
+			abbrev = atoi(arg+9);
+			continue;
+		}
+		if (!strcmp(arg, "-v")) {
+			verbose = 1;
+			continue;
+		}
 		usage(builtin_branch_usage);
 	}
 
@@ -268,7 +306,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	if (delete)
 		delete_branches(argc - i, argv + i, force_delete);
 	else if (i == argc)
-		print_ref_list(kinds);
+		print_ref_list(kinds, verbose, abbrev);
 	else if (i == argc - 1)
 		create_branch(argv[i], head, force_create, reflog);
 	else if (i == argc - 2)

From 67affd5173da059ca60aab7896985331acacd9b4 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 24 Nov 2006 23:10:23 -0800
Subject: [PATCH 019/330] git-branch -D: make it work even when on a
 yet-to-be-born branch

This makes "git branch -D other_branch" work even when HEAD
points at a yet-to-be-born branch.

Earlier, we checked the HEAD ref for the purpose of "subset"
check even when the deletion was forced (i.e. not -d but -D).
Because of this, you cannot delete a branch even with -D while
on a yet-to-be-born branch.

With this change, the following sequence that now works:

	mkdir newdir && cd newdir
	git init-db
	git fetch -k $other_repo refs/heads/master:refs/heads/othre
	# oops, typo
	git branch other othre
	git branch -D othre

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 69b7b55d86..3d5cb0e4b2 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -38,12 +38,16 @@ static int in_merge_bases(const unsigned char *sha1,
 
 static void delete_branches(int argc, const char **argv, int force)
 {
-	struct commit *rev, *head_rev;
+	struct commit *rev, *head_rev = head_rev;
 	unsigned char sha1[20];
 	char *name;
 	int i;
 
-	head_rev = lookup_commit_reference(head_sha1);
+	if (!force) {
+		head_rev = lookup_commit_reference(head_sha1);
+		if (!head_rev)
+			die("Couldn't look up commit object for HEAD");
+	}
 	for (i = 0; i < argc; i++) {
 		if (!strcmp(head, argv[i]))
 			die("Cannot delete the branch you are currently on.");
@@ -53,8 +57,8 @@ static void delete_branches(int argc, const char **argv, int force)
 			die("Branch '%s' not found.", argv[i]);
 
 		rev = lookup_commit_reference(sha1);
-		if (!rev || !head_rev)
-			die("Couldn't look up commit objects.");
+		if (!rev)
+			die("Couldn't look up commit object for '%s'", name);
 
 		/* This checks whether the merge bases of branch and
 		 * HEAD contains branch -- which means that the HEAD

From 747fa12cef73b6ca04fffaddaad7326cf546cdea Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 24 Nov 2006 22:38:17 -0800
Subject: [PATCH 020/330] git-svn: correctly access repos when only given
 partial read permissions

Sometimes users are given only read access to a subtree inside a
repository, and git-svn could not read log information (and thus
fetch commits) when connecting a session to the root of the
repository.  We now start an SVN::Ra session with the full URL
of what we're tracking, and not the repository root as before.

This change was made much easier with a cleanup of
repo_path_split() usage as well as improving the accounting of
authentication batons.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 137 ++++++++++++++++++++++++---------------------------
 1 file changed, 65 insertions(+), 72 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 47cd3e27fe..5d67771c2c 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -39,7 +39,7 @@ memoize('revisions_eq');
 memoize('cmt_metadata');
 memoize('get_commit_time');
 
-my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib, $AUTH_BATON, $AUTH_CALLBACKS);
+my ($SVN, $_use_lib);
 
 sub nag_lib {
 	print STDERR <<EOF;
@@ -381,10 +381,7 @@ sub fetch_cmd {
 sub fetch_lib {
 	my (@parents) = @_;
 	$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
-	my $repo;
-	($repo, $SVN_PATH) = repo_path_split($SVN_URL);
-	$SVN_LOG ||= libsvn_connect($repo);
-	$SVN ||= libsvn_connect($repo);
+	$SVN ||= libsvn_connect($SVN_URL);
 	my ($last_rev, $last_commit) = svn_grab_base_rev();
 	my ($base, $head) = libsvn_parse_revision($last_rev);
 	if ($base > $head) {
@@ -426,7 +423,7 @@ sub fetch_lib {
 			# performance sucks with it enabled, so it's much
 			# faster to fetch revision ranges instead of relying
 			# on the limiter.
-			libsvn_get_log($SVN_LOG, '/'.$SVN_PATH,
+			libsvn_get_log(libsvn_dup_ra($SVN), [''],
 					$min, $max, 0, 1, 1,
 				sub {
 					my $log_msg;
@@ -528,7 +525,6 @@ sub commit_lib {
 	my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
 
 	my $repo;
-	($repo, $SVN_PATH) = repo_path_split($SVN_URL);
 	set_svn_commit_env();
 	foreach my $c (@revs) {
 		my $log_msg = get_commit_message($c, $commit_msg);
@@ -537,13 +533,11 @@ sub commit_lib {
 		# can't track down... (it's probably in the SVN code)
 		defined(my $pid = open my $fh, '-|') or croak $!;
 		if (!$pid) {
-			$SVN_LOG = libsvn_connect($repo);
-			$SVN = libsvn_connect($repo);
 			my $ed = SVN::Git::Editor->new(
 					{	r => $r_last,
-						ra => $SVN_LOG,
+						ra => libsvn_dup_ra($SVN),
 						c => $c,
-						svn_path => $SVN_PATH
+						svn_path => $SVN->{svn_path},
 					},
 					$SVN->get_commit_editor(
 						$log_msg->{msg},
@@ -661,10 +655,9 @@ sub show_ignore_cmd {
 
 sub show_ignore_lib {
 	my $repo;
-	($repo, $SVN_PATH) = repo_path_split($SVN_URL);
-	$SVN ||= libsvn_connect($repo);
+	$SVN ||= libsvn_connect($SVN_URL);
 	my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
-	libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r);
+	libsvn_traverse_ignore(\*STDOUT, $SVN->{svn_path}, $r);
 }
 
 sub graft_branches {
@@ -790,7 +783,7 @@ sub show_log {
 		} elsif (/^:\d{6} \d{6} $sha1_short/o) {
 			push @{$c->{raw}}, $_;
 		} elsif (/^[ACRMDT]\t/) {
-			# we could add $SVN_PATH here, but that requires
+			# we could add $SVN->{svn_path} here, but that requires
 			# remote access at the moment (repo_path_split)...
 			s#^([ACRMDT])\t#   $1 #;
 			push @{$c->{changed}}, $_;
@@ -856,10 +849,7 @@ sub commit_diff {
 		$_message ||= get_commit_message($tb,
 					"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
 	}
-	my $repo;
-	($repo, $SVN_PATH) = repo_path_split($SVN_URL);
-	$SVN_LOG ||= libsvn_connect($repo);
-	$SVN ||= libsvn_connect($repo);
+	$SVN ||= libsvn_connect($SVN_URL);
 	if ($r eq 'HEAD') {
 		$r = $SVN->get_latest_revnum;
 	} elsif ($r !~ /^\d+$/) {
@@ -868,8 +858,9 @@ sub commit_diff {
 	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
 	my $rev_committed;
 	my $ed = SVN::Git::Editor->new({	r => $r,
-						ra => $SVN_LOG, c => $tb,
-						svn_path => $SVN_PATH
+						ra => libsvn_dup_ra($SVN),
+						c => $tb,
+						svn_path => $SVN->{svn_path}
 					},
 				$SVN->get_commit_editor($_message,
 					sub {
@@ -1147,8 +1138,7 @@ sub graft_file_copy_lib {
 	my $tree_paths = $l_map->{$u};
 	my $pfx = common_prefix([keys %$tree_paths]);
 	my ($repo, $path) = repo_path_split($u.$pfx);
-	$SVN_LOG ||= libsvn_connect($repo);
-	$SVN ||= libsvn_connect($repo);
+	$SVN = libsvn_connect($repo);
 
 	my ($base, $head) = libsvn_parse_revision();
 	my $inc = 1000;
@@ -1157,7 +1147,8 @@ sub graft_file_copy_lib {
 	$SVN::Error::handler = \&libsvn_skip_unknown_revs;
 	while (1) {
 		my $pool = SVN::Pool->new;
-		libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1,
+		libsvn_get_log(libsvn_dup_ra($SVN), [$path],
+		               $min, $max, 0, 1, 1,
 			sub {
 				libsvn_graft_file_copies($grafts, $tree_paths,
 							$path, @_);
@@ -1267,13 +1258,9 @@ sub repo_path_split {
 			return ($u, $full_url);
 		}
 	}
-
 	if ($_use_lib) {
 		my $tmp = libsvn_connect($full_url);
-		my $url = $tmp->get_repos_root;
-		$full_url =~ s#^\Q$url\E/*##;
-		push @repo_path_split_cache, qr/^(\Q$url\E)/;
-		return ($url, $full_url);
+		return ($tmp->{repos_root}, $tmp->{svn_path});
 	} else {
 		my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
 		$path =~ s#^/+##;
@@ -2815,34 +2802,41 @@ sub _read_password {
 
 sub libsvn_connect {
 	my ($url) = @_;
-	if (!$AUTH_BATON || !$AUTH_CALLBACKS) {
-		SVN::_Core::svn_config_ensure($_config_dir, undef);
-		($AUTH_BATON, $AUTH_CALLBACKS) = SVN::Core::auth_open_helper([
-		    SVN::Client::get_simple_provider(),
-		    SVN::Client::get_ssl_server_trust_file_provider(),
-		    SVN::Client::get_simple_prompt_provider(
-		      \&_simple_prompt, 2),
-		    SVN::Client::get_ssl_client_cert_prompt_provider(
-		      \&_ssl_client_cert_prompt, 2),
-		    SVN::Client::get_ssl_client_cert_pw_prompt_provider(
-		      \&_ssl_client_cert_pw_prompt, 2),
-		    SVN::Client::get_username_provider(),
-		    SVN::Client::get_ssl_server_trust_prompt_provider(
-		      \&_ssl_server_trust_prompt),
-		    SVN::Client::get_username_prompt_provider(
-		      \&_username_prompt, 2),
-		  ]);
-	}
-	SVN::Ra->new(url => $url, auth => $AUTH_BATON,
-	             auth_provider_callbacks => $AUTH_CALLBACKS);
+	SVN::_Core::svn_config_ensure($_config_dir, undef);
+	my ($baton, $callbacks) = SVN::Core::auth_open_helper([
+	    SVN::Client::get_simple_provider(),
+	    SVN::Client::get_ssl_server_trust_file_provider(),
+	    SVN::Client::get_simple_prompt_provider(
+	      \&_simple_prompt, 2),
+	    SVN::Client::get_ssl_client_cert_prompt_provider(
+	      \&_ssl_client_cert_prompt, 2),
+	    SVN::Client::get_ssl_client_cert_pw_prompt_provider(
+	      \&_ssl_client_cert_pw_prompt, 2),
+	    SVN::Client::get_username_provider(),
+	    SVN::Client::get_ssl_server_trust_prompt_provider(
+	      \&_ssl_server_trust_prompt),
+	    SVN::Client::get_username_prompt_provider(
+	      \&_username_prompt, 2),
+	  ]);
+	my $ra = SVN::Ra->new(url => $url, auth => $baton,
+	                      pool => SVN::Pool->new,
+	                      auth_provider_callbacks => $callbacks);
+	$ra->{svn_path} = $url;
+	$ra->{repos_root} = $ra->get_repos_root;
+	$ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
+	push @repo_path_split_cache, qr/^(\Q$ra->{repos_root}\E)/;
+	return $ra;
+}
+
+sub libsvn_dup_ra {
+	my ($ra) = @_;
+	SVN::Ra->new(map { $_ => $ra->{$_} }
+	             qw/url auth auth_provider_callbacks repos_root svn_path/);
 }
 
 sub libsvn_get_file {
 	my ($gui, $f, $rev, $chg) = @_;
-	my $p = $f;
-	if (length $SVN_PATH > 0) {
-		return unless ($p =~ s#^\Q$SVN_PATH\E/##);
-	}
+	$f =~ s#^/##;
 	print "\t$chg\t$f\n" unless $_q;
 
 	my ($hash, $pid, $in, $out);
@@ -2879,7 +2873,7 @@ sub libsvn_get_file {
 		waitpid $pid, 0;
 		$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
 	}
-	print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!;
+	print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
 }
 
 sub libsvn_log_entry {
@@ -2897,7 +2891,6 @@ sub libsvn_log_entry {
 
 sub process_rm {
 	my ($gui, $last_commit, $f) = @_;
-	$f =~ s#^\Q$SVN_PATH\E/?## or return;
 	# remove entire directories.
 	if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
 		defined(my $pid = open my $ls, '-|') or croak $!;
@@ -2919,9 +2912,11 @@ sub libsvn_fetch {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
 	my @amr;
+	my $p = $SVN->{svn_path};
 	foreach my $f (keys %$paths) {
 		my $m = $paths->{$f}->action();
-		$f =~ s#^/+##;
+		$f =~ s#^/\Q$p\E/##;
+		next if $f =~ m#^/#;
 		if ($m =~ /^[DR]$/) {
 			print "\t$m\t$f\n" unless $_q;
 			process_rm($gui, $last_commit, $f);
@@ -3011,9 +3006,9 @@ sub libsvn_parse_revision {
 
 sub libsvn_traverse {
 	my ($gui, $pfx, $path, $rev, $files) = @_;
-	my $cwd = "$pfx/$path";
+	my $cwd = length $pfx ? "$pfx/$path" : $path;
 	my $pool = SVN::Pool->new;
-	$cwd =~ s#^/+##g;
+	$cwd =~ s#^\Q$SVN->{svn_path}\E##;
 	my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
 	foreach my $d (keys %$dirent) {
 		my $t = $dirent->{$d}->kind;
@@ -3037,7 +3032,7 @@ sub libsvn_traverse_ignore {
 	my $pool = SVN::Pool->new;
 	my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
 	my $p = $path;
-	$p =~ s#^\Q$SVN_PATH\E/?##;
+	$p =~ s#^\Q$SVN->{svn_path}\E/##;
 	print $fh length $p ? "\n# $p\n" : "\n# /\n";
 	if (my $s = $props->{'svn:ignore'}) {
 		$s =~ s/[\r\n]+/\n/g;
@@ -3064,7 +3059,7 @@ sub revisions_eq {
 	if ($_use_lib) {
 		# should be OK to use Pool here (r1 - r0) should be small
 		my $pool = SVN::Pool->new;
-		libsvn_get_log($SVN, "/$path", $r0, $r1,
+		libsvn_get_log($SVN, [$path], $r0, $r1,
 				0, 1, 1, sub {$nr++}, $pool);
 		$pool->clear;
 	} else {
@@ -3079,7 +3074,7 @@ sub revisions_eq {
 
 sub libsvn_find_parent_branch {
 	my ($paths, $rev, $author, $date, $msg) = @_;
-	my $svn_path = '/'.$SVN_PATH;
+	my $svn_path = '/'.$SVN->{svn_path};
 
 	# look for a parent from another branch:
 	my $i = $paths->{$svn_path} or return;
@@ -3090,7 +3085,7 @@ sub libsvn_find_parent_branch {
 	$branch_from =~ s#^/##;
 	my $l_map = {};
 	read_url_paths_all($l_map, '', "$GIT_DIR/svn");
-	my $url = $SVN->{url};
+	my $url = $SVN->{repos_root};
 	defined $l_map->{$url} or return;
 	my $id = $l_map->{$url}->{$branch_from};
 	if (!defined $id && $_follow_parent) {
@@ -3112,7 +3107,7 @@ sub libsvn_find_parent_branch {
 			$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
 			init_vars();
 			$SVN_URL = "$url/$branch_from";
-			$SVN_LOG = $SVN = undef;
+			$SVN = undef;
 			setup_git_svn();
 			# we can't assume SVN_URL exists at r+1:
 			$_revision = "0:$r";
@@ -3149,7 +3144,7 @@ sub libsvn_new_tree {
 	}
 	my ($paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
-	libsvn_traverse($gui, '', $SVN_PATH, $rev);
+	libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
 	close $gui or croak $?;
 	return libsvn_log_entry($rev, $author, $date, $msg);
 }
@@ -3234,11 +3229,10 @@ sub libsvn_commit_cb {
 
 sub libsvn_ls_fullurl {
 	my $fullurl = shift;
-	my ($repo, $path) = repo_path_split($fullurl);
-	$SVN ||= libsvn_connect($repo);
+	$SVN ||= libsvn_connect($fullurl);
 	my @ret;
 	my $pool = SVN::Pool->new;
-	my ($dirent, undef, undef) = $SVN->get_dir($path,
+	my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
 						$SVN->get_latest_revnum, $pool);
 	foreach my $d (keys %$dirent) {
 		if ($dirent->{$d}->kind == $SVN::Node::dir) {
@@ -3260,8 +3254,9 @@ sub libsvn_skip_unknown_revs {
 	# Wonderfully consistent library, eh?
 	# 160013 - svn:// and file://
 	# 175002 - http(s)://
+	# 175007 - http(s):// (this repo required authorization, too...)
 	#   More codes may be discovered later...
-	if ($errno == 175002 || $errno == 160013) {
+	if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
 		return;
 	}
 	croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
@@ -3349,8 +3344,7 @@ sub split_path {
 }
 
 sub repo_path {
-	(defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]"
-					: $_[0]->{svn_path}
+	(defined $_[1] && length $_[1]) ? $_[1] : ''
 }
 
 sub url_path {
@@ -3382,10 +3376,9 @@ sub rmdirs {
 		exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
 	}
 	local $/ = "\0";
-	my @svn_path = split m#/#, $self->{svn_path};
 	while (<$fh>) {
 		chomp;
-		my @dn = (@svn_path, (split m#/#, $_));
+		my @dn = split m#/#, $_;
 		while (pop @dn) {
 			delete $rm->{join '/', @dn};
 		}

From d25c26e771fdf771f264dc85be348719886d354f Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 24 Nov 2006 22:38:18 -0800
Subject: [PATCH 021/330] git-svn: exit with status 1 for test failures

Some versions of the SVN libraries cause die() to exit with 255,
and 40cf043389ef4cdf3e56e7c4268d6f302e387fa0 tightened up
test_expect_failure to reject return values >128.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 5d67771c2c..0a47b1f9fd 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,6 +21,7 @@ $ENV{TZ} = 'UTC';
 $ENV{LC_ALL} = 'C';
 $| = 1; # unbuffer STDOUT
 
+sub fatal (@) { print STDERR $@; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
 # use eval { require SVN::... } to make it lazy load
@@ -569,7 +570,7 @@ sub commit_lib {
 				$no = 1;
 			}
 		}
-		close $fh or croak $?;
+		close $fh or exit 1;
 		if (! defined $r_new && ! defined $cmt_new) {
 			unless ($no) {
 				die "Failed to parse revision information\n";
@@ -868,13 +869,16 @@ sub commit_diff {
 						print "Committed $_[0]\n";
 					}, @lock)
 				);
-	my $mods = libsvn_checkout_tree($ta, $tb, $ed);
-	if (@$mods == 0) {
-		print "No changes\n$ta == $tb\n";
-		$ed->abort_edit;
-	} else {
-		$ed->close_edit;
-	}
+	eval {
+		my $mods = libsvn_checkout_tree($ta, $tb, $ed);
+		if (@$mods == 0) {
+			print "No changes\n$ta == $tb\n";
+			$ed->abort_edit;
+		} else {
+			$ed->close_edit;
+		}
+	};
+	fatal "$@\n" if $@;
 	$_message = $_file = undef;
 	return $rev_committed;
 }

From c95044d4f3c98b52f16e32cfe09f3ff988a80d2a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 25 Nov 2006 00:01:27 -0800
Subject: [PATCH 022/330] git-shortlog: fix common repository prefix
 abbreviation.

The code to abbreviate the common repository prefix was totally
borked.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index b760b477ea..bdd952c252 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -130,12 +130,17 @@ static void insert_author_oneline(struct path_list *list,
 	memcpy(buffer, oneline, onelinelen);
 	buffer[onelinelen] = '\0';
 
-	while ((p = strstr(buffer, dot3)) != NULL) {
-		memcpy(p, "...", 3);
-		strcpy(p + 2, p + sizeof(dot3) - 1);
+	if (dot3) {
+		int dot3len = strlen(dot3);
+		if (dot3len > 5) {
+			while ((p = strstr(buffer, dot3)) != NULL) {
+				int taillen = strlen(p) - dot3len;
+				memcpy(p, "/.../", 5);
+				memmove(p + 5, p + dot3len, taillen + 1);
+			}
+		}
 	}
 
-
 	onelines = item->util;
 	if (onelines->nr >= onelines->alloc) {
 		onelines->alloc = alloc_nr(onelines->nr);

From 7595e2ee6ef9b35ebc8dc45543723e1d89765ce3 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 25 Nov 2006 00:07:54 -0800
Subject: [PATCH 023/330] git-shortlog: make common repository prefix
 configurable with .mailmap

The code had "/pub/scm/linux/kernel/git/" hardcoded which was
too specific to the kernel project.

With this, a line in the .mailmap file:

	# repo-abbrev: /pub/scm/linux/kernel/git/

can be used to cause the substring to be abbreviated to /.../
on the title line of the commit message.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c    | 24 ++++++++++++++++++++++--
 contrib/mailmap.linux |  2 ++
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index bdd952c252..b5b13dee3b 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -9,6 +9,8 @@
 static const char shortlog_usage[] =
 "git-shortlog [-n] [-s] [<commit-id>... ]";
 
+static char *common_repo_prefix;
+
 static int compare_by_number(const void *a1, const void *a2)
 {
 	const struct path_list_item *i1 = a1, *i2 = a2;
@@ -35,8 +37,26 @@ static int read_mailmap(const char *filename)
 		char *end_of_name, *left_bracket, *right_bracket;
 		char *name, *email;
 		int i;
-		if (buffer[0] == '#')
+		if (buffer[0] == '#') {
+			static const char abbrev[] = "# repo-abbrev:";
+			int abblen = sizeof(abbrev) - 1;
+			int len = strlen(buffer);
+
+			if (len && buffer[len - 1] == '\n')
+				buffer[--len] = 0;
+			if (!strncmp(buffer, abbrev, abblen)) {
+				char *cp;
+
+				if (common_repo_prefix)
+					free(common_repo_prefix);
+				common_repo_prefix = xmalloc(len);
+
+				for (cp = buffer + abblen; isspace(*cp); cp++)
+					; /* nothing */
+				strcpy(common_repo_prefix, cp);
+			}
 			continue;
+		}
 		if ((left_bracket = strchr(buffer, '<')) == NULL)
 			continue;
 		if ((right_bracket = strchr(left_bracket + 1, '>')) == NULL)
@@ -87,7 +107,7 @@ static void insert_author_oneline(struct path_list *list,
 		const char *author, int authorlen,
 		const char *oneline, int onelinelen)
 {
-	const char *dot3 = "/pub/scm/linux/kernel/git/";
+	const char *dot3 = common_repo_prefix;
 	char *buffer, *p;
 	struct path_list_item *item;
 	struct path_list *onelines;
diff --git a/contrib/mailmap.linux b/contrib/mailmap.linux
index 83927c94ee..e4907f80f1 100644
--- a/contrib/mailmap.linux
+++ b/contrib/mailmap.linux
@@ -3,6 +3,8 @@
 # So have an email->real name table to translate the
 # (hopefully few) missing names
 #
+# repo-abbrev: /pub/scm/linux/kernel/git/
+#
 Adrian Bunk <bunk@stusta.de>
 Andreas Herrmann <aherrman@de.ibm.com>
 Andrew Morton <akpm@osdl.org>

From 61f5cb7f0d9ea6990f331bd7082630691c88abd2 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 24 Oct 2006 21:48:55 -0700
Subject: [PATCH 024/330] git-commit: show --summary after successful commit.

Sometimes people accidentally commit files in wrong mode bits.
Show --summary output for the HEAD commit after successful commit
as a final sanity check.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/tutorial-2.txt | 1 +
 git-commit.sh                | 3 +++
 2 files changed, 4 insertions(+)

diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index 42b6e7d7d2..6389de5ef7 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -23,6 +23,7 @@ $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
 Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe
+ create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
 ------------------------------------------------
diff --git a/git-commit.sh b/git-commit.sh
index 81c3a0cb61..7e9742d5e7 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -629,4 +629,7 @@ if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
 then
 	"$GIT_DIR"/hooks/post-commit
 fi
+
+test "$ret" = 0 && git-diff-tree --summary --root --no-commit-id HEAD
+
 exit "$ret"

From f64d7fd267c501f501e18a888e3e1e0c5b56458f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 25 Nov 2006 01:04:28 -0800
Subject: [PATCH 025/330] git-fetch: exit with non-zero status when
 fast-forward check fails

When update_local_ref() refuses to update a branch head due to
fast-forward check, it was not propagated properly in the call
chain and the command did not exit with non-zero status as a
result.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-fetch.sh | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/git-fetch.sh b/git-fetch.sh
index eb32476bbd..4425562098 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -359,7 +359,7 @@ fetch_main () {
       esac
 
       append_fetch_head "$head" "$remote" \
-	  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
+	  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge" || exit
 
   done
 
@@ -413,15 +413,16 @@ fetch_main () {
 	  done
 	  local_name=$(expr "z$found" : 'z[^:]*:\(.*\)')
 	  append_fetch_head "$sha1" "$remote" \
-		  "$remote_name" "$remote_nick" "$local_name" "$not_for_merge"
-      done
+		  "$remote_name" "$remote_nick" "$local_name" \
+		  "$not_for_merge" || exit
+      done &&
       if [ "$pack_lockfile" ]; then rm -f "$pack_lockfile"; fi
     ) || exit ;;
   esac
 
 }
 
-fetch_main "$reflist"
+fetch_main "$reflist" || exit
 
 # automated tag following
 case "$no_tags$tags" in
@@ -449,7 +450,7 @@ case "$no_tags$tags" in
 	case "$taglist" in
 	'') ;;
 	?*)
-		fetch_main "$taglist" ;;
+		fetch_main "$taglist" || exit ;;
 	esac
 esac
 

From d945d4be20d577868646f1b676b605cd9fdadf86 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 25 Nov 2006 01:10:10 -0800
Subject: [PATCH 026/330] git-fetch: allow forcing glob pattern in refspec

Building on top of the earlier refspec glob pattern enhancement,
this allows a glob pattern to say the updates should be forced
by prefixing it with '+' as usual, like this:

	Pull: +refs/heads/*:refs/remotes/origin/*

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index e281b7c6eb..19bc3857d1 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -99,13 +99,17 @@ get_remote_default_refs_for_push () {
 expand_refs_wildcard () {
 	for ref
 	do
+		lref=${ref#'+'}
 		# a non glob pattern is given back as-is.
-		expr "z$ref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
+		expr "z$lref" : 'zrefs/.*/\*:refs/.*/\*$' >/dev/null || {
 			echo "$ref"
 			continue
 		}
-		from=`expr "z$ref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
-		to=`expr "z$ref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
+
+		from=`expr "z$lref" : 'z\(refs/.*/\)\*:refs/.*/\*$'`
+		to=`expr "z$lref" : 'zrefs/.*/\*:\(refs/.*/\)\*$'`
+		local_force=
+		test "z$lref" = "z$ref" || local_force='+'
 		echo "$ls_remote_result" |
 		(
 			IFS='	'
@@ -117,7 +121,7 @@ expand_refs_wildcard () {
 				then
 					continue
 				fi
-				echo "${name}:${to}${mapped}"
+				echo "${local_force}${name}:${to}${mapped}"
 			done
 		)
 	done

From 310b86d48091ebb6a71782678769b2cb8fe2ecd5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 25 Nov 2006 01:33:06 -0800
Subject: [PATCH 027/330] fetch-pack: do not barf when duplicate re patterns
 are given

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 fetch-pack.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/fetch-pack.c b/fetch-pack.c
index 0a169dce85..743eab7efa 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -566,6 +566,29 @@ static int fetch_pack(int fd[2], int nr_match, char **match)
 	return 0;
 }
 
+static int remove_duplicates(int nr_heads, char **heads)
+{
+	int src, dst;
+
+	for (src = dst = 0; src < nr_heads; src++) {
+		/* If heads[src] is different from any of
+		 * heads[0..dst], push it in.
+		 */
+		int i;
+		for (i = 0; i < dst; i++) {
+			if (!strcmp(heads[i], heads[src]))
+				break;
+		}
+		if (i < dst)
+			continue;
+		if (src != dst)
+			heads[dst] = heads[src];
+		dst++;
+	}
+	heads[dst] = 0;
+	return dst;
+}
+
 int main(int argc, char **argv)
 {
 	int i, ret, nr_heads;
@@ -617,6 +640,8 @@ int main(int argc, char **argv)
 	pid = git_connect(fd, dest, exec);
 	if (pid < 0)
 		return 1;
+	if (heads && nr_heads)
+		nr_heads = remove_duplicates(nr_heads, heads);
 	ret = fetch_pack(fd, nr_heads, heads);
 	close(fd[0]);
 	close(fd[1]);

From 391862e34571c0e7e88a5f6e84211b7b8bf55440 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sat, 25 Nov 2006 09:43:59 +0100
Subject: [PATCH 028/330] gitweb: Do not use esc_html in esc_path

Do not use esc_html in esc_path subroutine to avoid double quoting;
expand esc_html body (except quoting) in esc_path.

Move esc_path before quot_cec and quot_upr. Add some comments.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 28 +++++++++++++++++-----------
 1 file changed, 17 insertions(+), 11 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6ae7e80351..38c94372f5 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -585,7 +585,21 @@ sub esc_html ($;%) {
 	return $str;
 }
 
-# Make control characterss "printable".
+# quote control characters and escape filename to HTML
+sub esc_path {
+	my $str = shift;
+	my %opts = @_;
+
+	$str = to_utf8($str);
+	$str = escapeHTML($str);
+	if ($opts{'-nbsp'}) {
+		$str =~ s/ /&nbsp;/g;
+	}
+	$str =~ s|([[:cntrl:]])|quot_cec($1)|eg;
+	return $str;
+}
+
+# Make control characters "printable", using character escape codes (CEC)
 sub quot_cec {
 	my $cntrl = shift;
 	my %es = ( # character escape codes, aka escape sequences
@@ -605,22 +619,14 @@ sub quot_cec {
 	return "<span class=\"cntrl\">$chr</span>";
 }
 
-# Alternatively use unicode control pictures codepoints.
+# Alternatively use unicode control pictures codepoints,
+# Unicode "printable representation" (PR)
 sub quot_upr {
 	my $cntrl = shift;
 	my $chr = sprintf('&#%04d;', 0x2400+ord($cntrl));
 	return "<span class=\"cntrl\">$chr</span>";
 }
 
-# quote control characters and escape filename to HTML
-sub esc_path {
-	my $str = shift;
-
-	$str = esc_html($str);
-	$str =~ s|([[:cntrl:]])|quot_cec($1)|eg;
-	return $str;
-}
-
 # git may return quoted and escaped filenames
 sub unquote {
 	my $str = shift;

From 28b9d9f7c67cfd199c4bc9e1ac5197cb17349b15 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sat, 25 Nov 2006 11:32:08 +0100
Subject: [PATCH 029/330] gitweb: Use git-show-ref instead of git-peek-remote

Use "git show-ref --dereference" instead of "git peek-remote
$projectroot/project" in git_get_references. git-show-ref is faster
than git-peek-remote (40ms vs 56ms user+sys for git.git repository);
even faster is reading info/refs file (if it exists), but the
information in info/refs can be stale; that and the fact that
info/refs is meant for dumb protocol transports, not for gitweb.

git-show-ref is available since v1.4.4; the output format is slightly
different than git-peek-remote output format, but we accept both.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 38c94372f5..26fc3a689d 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1154,14 +1154,15 @@ sub git_get_last_activity {
 sub git_get_references {
 	my $type = shift || "";
 	my %refs;
-	# 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c	refs/tags/v2.6.11
-	# c39ae07f393806ccf406ef966e9a15afc43cc36a	refs/tags/v2.6.11^{}
-	open my $fd, "-|", $GIT, "peek-remote", "$projectroot/$project/"
+	# 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c refs/tags/v2.6.11
+	# c39ae07f393806ccf406ef966e9a15afc43cc36a refs/tags/v2.6.11^{}
+	open my $fd, "-|", git_cmd(), "show-ref", "--dereference",
+		($type ? ("--", "refs/$type") : ()) # use -- <pattern> if $type
 		or return;
 
 	while (my $line = <$fd>) {
 		chomp $line;
-		if ($line =~ m/^([0-9a-fA-F]{40})\trefs\/($type\/?[^\^]+)/) {
+		if ($line =~ m!^([0-9a-fA-F]{40})\srefs/($type/?[^^]+)!) {
 			if (defined $refs{$1}) {
 				push @{$refs{$1}}, $2;
 			} else {

From ba00b8c1edafcc414cfe13f8a4addac3893c2a29 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sat, 25 Nov 2006 15:54:32 +0100
Subject: [PATCH 030/330] gitweb: Add author and committer email extraction to
 parse_commit

Extract author email to 'author_email' key, and comitter mail to
'committer_mail' key; uniquify committer and author lines handling
by the way.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 26fc3a689d..85a896b619 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1294,8 +1294,9 @@ sub parse_commit {
 			$co{'author'} = $1;
 			$co{'author_epoch'} = $2;
 			$co{'author_tz'} = $3;
-			if ($co{'author'} =~ m/^([^<]+) </) {
-				$co{'author_name'} = $1;
+			if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
+				$co{'author_name'}  = $1;
+				$co{'author_email'} = $2;
 			} else {
 				$co{'author_name'} = $co{'author'};
 			}
@@ -1304,7 +1305,12 @@ sub parse_commit {
 			$co{'committer_epoch'} = $2;
 			$co{'committer_tz'} = $3;
 			$co{'committer_name'} = $co{'committer'};
-			$co{'committer_name'} =~ s/ <.*//;
+			if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
+				$co{'committer_name'}  = $1;
+				$co{'committer_email'} = $2;
+			} else {
+				$co{'committer_name'} = $co{'committer'};
+			}
 		}
 	}
 	if (!defined $co{'tree'}) {

From ab23c19d67d283567fdf18966e347a78ade56c22 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sat, 25 Nov 2006 15:54:33 +0100
Subject: [PATCH 031/330] gitweb: Add author and contributor email to Atom feed

Add author email (from 'author_email') and contributor email (from
'committer_email') to items in the Atom format gitweb feed.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 85a896b619..fb7026d321 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4323,9 +4323,19 @@ XML
 			print "<entry>\n" .
 			      "<title type=\"html\">" . esc_html($co{'title'}) . "</title>\n" .
 			      "<updated>$cd{'iso-8601'}</updated>\n" .
-			      "<author><name>" . esc_html($co{'author_name'}) . "</name></author>\n" .
+			      "<author>\n" .
+			      "  <name>" . esc_html($co{'author_name'}) . "</name>\n";
+			if ($co{'author_email'}) {
+				print "  <email>" . esc_html($co{'author_email'}) . "</email>\n";
+			}
+			print "</author>\n" .
 			      # use committer for contributor
-			      "<contributor><name>" . esc_html($co{'committer_name'}) . "</name></contributor>\n" .
+			      "<contributor>\n" .
+			      "  <name>" . esc_html($co{'committer_name'}) . "</name>\n";
+			if ($co{'committer_email'}) {
+				print "  <email>" . esc_html($co{'committer_email'}) . "</email>\n";
+			}
+			print "</contributor>\n" .
 			      "<published>$cd{'iso-8601'}</published>\n" .
 			      "<link rel=\"alternate\" type=\"text/html\" href=\"$co_url\" />\n" .
 			      "<id>$co_url</id>\n" .

From 91fd2bf3fdc72351532a8fd74cdd0da37b036ed1 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sat, 25 Nov 2006 15:54:34 +0100
Subject: [PATCH 032/330] gitweb: Use author_epoch for pubdate in gitweb feeds

Use creation date (author_epoch) instead of former commit date
(committer_epoch) as publish date in gitweb feeds (RSS, Atom).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index fb7026d321..1c5b85443b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4201,7 +4201,7 @@ sub git_feed {
 	}
 	if (defined($revlist[0])) {
 		%latest_commit = parse_commit($revlist[0]);
-		%latest_date   = parse_date($latest_commit{'committer_epoch'});
+		%latest_date   = parse_date($latest_commit{'author_epoch'});
 		print $cgi->header(
 			-type => $content_type,
 			-charset => 'utf-8',
@@ -4294,10 +4294,10 @@ XML
 		my $commit = $revlist[$i];
 		my %co = parse_commit($commit);
 		# we read 150, we always show 30 and the ones more recent than 48 hours
-		if (($i >= 20) && ((time - $co{'committer_epoch'}) > 48*60*60)) {
+		if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
 			last;
 		}
-		my %cd = parse_date($co{'committer_epoch'});
+		my %cd = parse_date($co{'author_epoch'});
 
 		# get list of changed files
 		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,

From efe4631def181d32f932672a7ea31e52ee0ab308 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sat, 25 Nov 2006 17:38:41 -0800
Subject: [PATCH 033/330] git-svn: allow SVN:: lib users to track the root of
 the repository (again)

I broke this again in 747fa12cef73b6ca04fffaddaad7326cf546cdea.

Thanks to merlyn for pointing this out to me on IRC.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 0a47b1f9fd..de4e74a60d 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2919,8 +2919,12 @@ sub libsvn_fetch {
 	my $p = $SVN->{svn_path};
 	foreach my $f (keys %$paths) {
 		my $m = $paths->{$f}->action();
-		$f =~ s#^/\Q$p\E/##;
-		next if $f =~ m#^/#;
+		if (length $p) {
+			$f =~ s#^/\Q$p\E/##;
+			next if $f =~ m#^/#;
+		} else {
+			$f =~ s#^/##;
+		}
 		if ($m =~ /^[DR]$/) {
 			print "\t$m\t$f\n" unless $_q;
 			process_rm($gui, $last_commit, $f);

From e88ce8a45656f750551ee21abf3be5576f6b0be4 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 26 Nov 2006 02:18:26 +0100
Subject: [PATCH 034/330] gitweb: Make project description in projects list
 link to summary view

Make (shortened) project description in the "projects list" view
hyperlink to the "summary" view of the project. Project names are
sometimes short; having project description be hyperling gives larger
are to click. While at it, display full description on mouseover via
'title' attribute to introduced link.

Additionally, fix whitespace usage in modified git_project_list_body
subroutine: tabs are for indent, spaces are for align.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 1c5b85443b..093bd72058 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2441,6 +2441,7 @@ sub git_project_list_body {
 		($pr->{'age'}, $pr->{'age_string'}) = @aa;
 		if (!defined $pr->{'descr'}) {
 			my $descr = git_get_project_description($pr->{'path'}) || "";
+			$pr->{'descr_long'} = to_utf8($descr);
 			$pr->{'descr'} = chop_str($descr, 25, 5);
 		}
 		if (!defined $pr->{'owner'}) {
@@ -2476,7 +2477,7 @@ sub git_project_list_body {
 		} else {
 			print "<th>" .
 			      $cgi->a({-href => href(project=>undef, order=>'project'),
-				       -class => "header"}, "Project") .
+			               -class => "header"}, "Project") .
 			      "</th>\n";
 		}
 		if ($order eq "descr") {
@@ -2485,7 +2486,7 @@ sub git_project_list_body {
 		} else {
 			print "<th>" .
 			      $cgi->a({-href => href(project=>undef, order=>'descr'),
-				       -class => "header"}, "Description") .
+			               -class => "header"}, "Description") .
 			      "</th>\n";
 		}
 		if ($order eq "owner") {
@@ -2494,7 +2495,7 @@ sub git_project_list_body {
 		} else {
 			print "<th>" .
 			      $cgi->a({-href => href(project=>undef, order=>'owner'),
-				       -class => "header"}, "Owner") .
+			               -class => "header"}, "Owner") .
 			      "</th>\n";
 		}
 		if ($order eq "age") {
@@ -2503,7 +2504,7 @@ sub git_project_list_body {
 		} else {
 			print "<th>" .
 			      $cgi->a({-href => href(project=>undef, order=>'age'),
-				       -class => "header"}, "Last Change") .
+			               -class => "header"}, "Last Change") .
 			      "</th>\n";
 		}
 		print "<th></th>\n" .
@@ -2528,7 +2529,9 @@ sub git_project_list_body {
 		}
 		print "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
 		                        -class => "list"}, esc_html($pr->{'path'})) . "</td>\n" .
-		      "<td>" . esc_html($pr->{'descr'}) . "</td>\n" .
+		      "<td>" . $cgi->a({-href => href(project=>$pr->{'path'}, action=>"summary"),
+		                        -class => "list", -title => $pr->{'descr_long'}},
+		                        esc_html($pr->{'descr'})) . "</td>\n" .
 		      "<td><i>" . chop_str($pr->{'owner'}, 15) . "</i></td>\n";
 		print "<td class=\"". age_class($pr->{'age'}) . "\">" .
 		      $pr->{'age_string'} . "</td>\n" .

From 93ee7823c0a48b7a51ac0e145b5bcc450c805ea4 Mon Sep 17 00:00:00 2001
From: "J. Bruce Fields" <bfields@fieldses.org>
Date: Sat, 25 Nov 2006 22:45:02 -0500
Subject: [PATCH 035/330] Documentation: clarify tutorial pull/merge discussion

Attempt to clarify somewhat the difference between pull and merge,
and give a little more details on the pull syntax.

I'm still not happy with this section: the explanation of the origin
branch isn't great, but maybe that should be left alone pending the
use-separate-remotes change; and we need to explain how to set up a
public repository and push to it.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/tutorial.txt | 41 ++++++++++++++++++++++++--------------
 1 file changed, 26 insertions(+), 15 deletions(-)

diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 1e4ddfbd11..35af81a3de 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -209,29 +209,28 @@ at /home/bob/myrepo.  She does this with:
 
 ------------------------------------------------
 $ cd /home/alice/project
-$ git pull /home/bob/myrepo
+$ git pull /home/bob/myrepo master
 ------------------------------------------------
 
-This actually pulls changes from the branch in Bob's repository named
-"master".  Alice could request a different branch by adding the name
-of the branch to the end of the git pull command line.
+This merges the changes from Bob's "master" branch into Alice's
+current branch.  If Alice has made her own changes in the meantime,
+then she may need to manually fix any conflicts.  (Note that the
+"master" argument in the above command is actually unnecessary, as it
+is the default.)
 
-This merges Bob's changes into her repository; "git log" will
-now show the new commits.  If Alice has made her own changes in the
-meantime, then Bob's changes will be merged in, and she will need to
-manually fix any conflicts.
+The "pull" command thus performs two operations: it fetches changes
+from a remote branch, then merges them into the current branch.
 
-A more cautious Alice might wish to examine Bob's changes before
-pulling them.  She can do this by creating a temporary branch just
-for the purpose of studying Bob's changes:
+You can perform the first operation alone using the "git fetch"
+command.  For example, Alice could create a temporary branch just to
+track Bob's changes, without merging them with her own, using:
 
 -------------------------------------
 $ git fetch /home/bob/myrepo master:bob-incoming
 -------------------------------------
 
 which fetches the changes from Bob's master branch into a new branch
-named bob-incoming.  (Unlike git pull, git fetch just fetches a copy
-of Bob's line of development without doing any merging).  Then
+named bob-incoming.  Then
 
 -------------------------------------
 $ git log -p master..bob-incoming
@@ -240,8 +239,8 @@ $ git log -p master..bob-incoming
 shows a list of all the changes that Bob made since he branched from
 Alice's master branch.
 
-After examining those changes, and possibly fixing things, Alice can
-pull the changes into her master branch:
+After examining those changes, and possibly fixing things, Alice
+could pull the changes into her master branch:
 
 -------------------------------------
 $ git checkout master
@@ -251,6 +250,18 @@ $ git pull . bob-incoming
 The last command is a pull from the "bob-incoming" branch in Alice's
 own repository.
 
+Alice could also perform both steps at once with:
+
+-------------------------------------
+$ git pull /home/bob/myrepo master:bob-incoming
+-------------------------------------
+
+This is just like the "git pull /home/bob/myrepo master" that we saw
+before, except that it also stores the unmerged changes from bob's
+master branch in bob-incoming before merging them into Alice's
+current branch.  Note that git pull always merges into the current
+branch, regardless of what else is given on the commandline.
+
 Later, Bob can update his repo with Alice's latest changes using
 
 -------------------------------------

From aabd76930fdc585dd3e198a3351d426583af4081 Mon Sep 17 00:00:00 2001
From: Han-Wen Nienhuys <hanwen@xs4all.nl>
Date: Sun, 26 Nov 2006 17:42:49 +0100
Subject: [PATCH 036/330] git-tag: allow empty tag message if -m is given
 explicitly.

Signed-off-by: Han-Wen Nienhuys <hanwen@xs4all.nl>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-tag.sh | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/git-tag.sh b/git-tag.sh
index ac269e3277..d53f94cd9c 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -5,6 +5,7 @@ USAGE='-l [<pattern>] | [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <tagname> [
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
+message_given=
 annotate=
 signed=
 force=
@@ -37,6 +38,12 @@ do
     	annotate=1
 	shift
 	message="$1"
+	if test "$#" = "0"; then
+	    die "error: option -m needs an argument"
+	    exit 2
+	else
+	    message_given=1
+	fi
 	;;
     -u)
 	annotate=1
@@ -83,7 +90,7 @@ tagger=$(git-var GIT_COMMITTER_IDENT) || exit 1
 trap 'rm -f "$GIT_DIR"/TAG_TMP* "$GIT_DIR"/TAG_FINALMSG "$GIT_DIR"/TAG_EDITMSG' 0
 
 if [ "$annotate" ]; then
-    if [ -z "$message" ]; then
+    if [ -z "$message_given" ]; then
         ( echo "#"
           echo "# Write a tag message"
           echo "#" ) > "$GIT_DIR"/TAG_EDITMSG
@@ -95,7 +102,7 @@ if [ "$annotate" ]; then
     grep -v '^#' <"$GIT_DIR"/TAG_EDITMSG |
     git-stripspace >"$GIT_DIR"/TAG_FINALMSG
 
-    [ -s "$GIT_DIR"/TAG_FINALMSG ] || {
+    [ -s "$GIT_DIR"/TAG_FINALMSG -o -n "$message_given" ] || {
 	echo >&2 "No tag message?"
 	exit 1
     }

From 36f2587ffb6802cb38071510810f48cddfc4f34a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 26 Nov 2006 12:47:52 -0800
Subject: [PATCH 037/330] grep: do not skip unmerged entries when grepping in
 the working tree.

We used to skip unmerged entries, which made sense for grepping
in the cached copies, but not for grepping in the working tree.

Noticed by Johannes Sixt.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-grep.c | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/builtin-grep.c b/builtin-grep.c
index ad7dc00cde..9873e3d1db 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -268,7 +268,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 	for (i = 0; i < active_nr; i++) {
 		struct cache_entry *ce = active_cache[i];
 		char *name;
-		if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
+		if (!S_ISREG(ntohl(ce->ce_mode)))
 			continue;
 		if (!pathspec_matches(paths, ce->name))
 			continue;
@@ -280,12 +280,19 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 			memcpy(name + 2, ce->name, len + 1);
 		}
 		argv[argc++] = name;
-		if (argc < MAXARGS)
+		if (argc < MAXARGS && !ce_stage(ce))
 			continue;
 		status = exec_grep(argc, argv);
 		if (0 < status)
 			hit = 1;
 		argc = nr;
+		if (ce_stage(ce)) {
+			do {
+				i++;
+			} while (i < active_nr &&
+				 !strcmp(ce->name, active_cache[i]->name));
+			i--; /* compensate for loop control */
+		}
 	}
 	if (argc > nr) {
 		status = exec_grep(argc, argv);
@@ -316,14 +323,24 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
 
 	for (nr = 0; nr < active_nr; nr++) {
 		struct cache_entry *ce = active_cache[nr];
-		if (ce_stage(ce) || !S_ISREG(ntohl(ce->ce_mode)))
+		if (!S_ISREG(ntohl(ce->ce_mode)))
 			continue;
 		if (!pathspec_matches(paths, ce->name))
 			continue;
-		if (cached)
+		if (cached) {
+			if (ce_stage(ce))
+				continue;
 			hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
+		}
 		else
 			hit |= grep_file(opt, ce->name);
+		if (ce_stage(ce)) {
+			do {
+				nr++;
+			} while (nr < active_nr &&
+				 !strcmp(ce->name, active_cache[nr]->name));
+			nr--; /* compensate for loop control */
+		}
 	}
 	free_grep_patterns(opt);
 	return hit;

From 51901e96bf592877f7b266d0d9c6cb83b363edce Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 26 Nov 2006 22:16:31 -0800
Subject: [PATCH 038/330] git-merge: tighten error checking.

If a branch name to be merged is misspelled, the command leaked error
messages from underlying plumbing commands, which were helpful only
to people who know how the command are implemented to diagnose the
breakage, but simply puzzling and unhelpful for the end users.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index dd4e83dede..5fa8b0d577 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -188,13 +188,13 @@ else
 	# in this loop.
 	merge_name=$(for remote
 		do
-			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null)
+			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
 			if git show-ref -q --verify "refs/heads/$remote"
 			then
 				what=branch
 			else
 				what=commit
-			fi
+			fi &&
 			echo "$rh		$what '$remote'"
 		done | git-fmt-merge-msg
 	)
@@ -209,7 +209,7 @@ test "$rloga" = '' && rloga="merge: $@"
 remoteheads=
 for remote
 do
-	remotehead=$(git-rev-parse --verify "$remote"^0) ||
+	remotehead=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
 	    die "$remote - not something we can merge"
 	remoteheads="${remoteheads}$remotehead "
 done

From c1751616381b6e8c8eae311107b5969a0233a6d8 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 26 Nov 2006 22:19:42 -0800
Subject: [PATCH 039/330] git-merge: do not leak rev-parse output used for
 checking internally.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-merge.sh b/git-merge.sh
index 5fa8b0d577..75af10d3e4 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -159,7 +159,7 @@ then
 	shift
 	head_arg="$1"
 	shift
-elif ! git-rev-parse --verify HEAD 2>/dev/null
+elif ! git-rev-parse --verify HEAD >/dev/null 2>&1
 then
 	# If the merged head is a valid one there is no reason to
 	# forbid "git merge" into a branch yet to be born.  We do

From fde97d8ac63efa466dd48436d2178bd364b846de Mon Sep 17 00:00:00 2001
From: Sean <seanlkml@sympatico.ca>
Date: Mon, 27 Nov 2006 14:37:43 -0500
Subject: [PATCH 040/330] Update documentation to remove incorrect
 GIT_DIFF_OPTS example.

Git no longer calls an external diff program to generate patches.
Remove the documentation which suggests that you can pass
arbitrary diff options via the GIT_DIFF_OPTS environment variable.

Signed-off-by: Sean Estabrooks <seanlkml@sympatico.ca>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/diff-format.txt | 57 ++++-------------------------------
 Documentation/git.txt         | 32 +++++++++++++++++---
 2 files changed, 34 insertions(+), 55 deletions(-)

diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
index e4520e28e5..883c1bb0a6 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -65,62 +65,17 @@ Generating patches with -p
 
 When "git-diff-index", "git-diff-tree", or "git-diff-files" are run
 with a '-p' option, they do not produce the output described above;
-instead they produce a patch file.
+instead they produce a patch file.  You can customize the creation
+of such patches via the GIT_EXTERNAL_DIFF and the GIT_DIFF_OPTS
+environment variables.
 
-The patch generation can be customized at two levels.
-
-1. When the environment variable 'GIT_EXTERNAL_DIFF' is not set,
-   these commands internally invoke "diff" like this:
-
-      diff -L a/<path> -L b/<path> -pu <old> <new>
-+
-For added files, `/dev/null` is used for <old>.  For removed
-files, `/dev/null` is used for <new>
-+
-The "diff" formatting options can be customized via the
-environment variable 'GIT_DIFF_OPTS'.  For example, if you
-prefer context diff:
-
-      GIT_DIFF_OPTS=-c git-diff-index -p HEAD
-
-
-2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
-   program named by it is called, instead of the diff invocation
-   described above.
-+
-For a path that is added, removed, or modified,
-'GIT_EXTERNAL_DIFF' is called with 7 parameters:
-
-     path old-file old-hex old-mode new-file new-hex new-mode
-+
-where:
-
-     <old|new>-file:: are files GIT_EXTERNAL_DIFF can use to read the
-		      contents of <old|new>,
-     <old|new>-hex:: are the 40-hexdigit SHA1 hashes,
-     <old|new>-mode:: are the octal representation of the file modes.
-
-+ 
-The file parameters can point at the user's working file
-(e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
-when a new file is added), or a temporary file (e.g. `old-file` in the
-index).  'GIT_EXTERNAL_DIFF' should not worry about unlinking the
-temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
-
-For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
-parameter, <path>.
-
-
-git specific extension to diff format
--------------------------------------
-
-What -p option produces is slightly different from the
-traditional diff format.
+What the -p option produces is slightly different from the traditional
+diff format.
 
 1.   It is preceded with a "git diff" header, that looks like
      this:
 
-     diff --git a/file1 b/file2
+       diff --git a/file1 b/file2
 +
 The `a/` and `b/` filenames are the same unless rename/copy is
 involved.  Especially, even for a creation or a deletion,
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 619d65685e..6382ef0a02 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -639,11 +639,35 @@ git Commits
 git Diffs
 ~~~~~~~~~
 'GIT_DIFF_OPTS'::
+	Only valid setting is "--unified=??" or "-u??" to set the
+	number of context lines shown when a unified diff is created.
+	This takes precedence over any "-U" or "--unified" option
+	value passed on the git diff command line.
+
 'GIT_EXTERNAL_DIFF'::
-	see the "generating patches" section in :
-	gitlink:git-diff-index[1];
-	gitlink:git-diff-files[1];
-	gitlink:git-diff-tree[1]
+	When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
+	program named by it is called, instead of the diff invocation
+	described above.  For a path that is added, removed, or modified,
+        'GIT_EXTERNAL_DIFF' is called with 7 parameters:
+
+	path old-file old-hex old-mode new-file new-hex new-mode
++
+where:
+
+	<old|new>-file:: are files GIT_EXTERNAL_DIFF can use to read the
+                         contents of <old|new>,
+	<old|new>-hex:: are the 40-hexdigit SHA1 hashes,
+	<old|new>-mode:: are the octal representation of the file modes.
+
++
+The file parameters can point at the user's working file
+(e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
+when a new file is added), or a temporary file (e.g. `old-file` in the
+index).  'GIT_EXTERNAL_DIFF' should not worry about unlinking the
+temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
++
+For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
+parameter, <path>.
 
 other
 ~~~~~

From 4ad91321ee95598c2488ab5e39afec13575d4e3f Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:40:47 -0500
Subject: [PATCH 041/330] Teach git-completion.bash how to complete git-merge.

Now that git-merge is high-level Porcelain users are going to expect
to be able to use it from the command line, in which case we really
should also be able to complete ref names as parameters.

I'm also including completion support for the merge strategies
that are supported by git-merge.sh, should the user wish to use a
different strategy than their default.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 30 ++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index a43a177160..28bd0e3ba7 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -81,6 +81,16 @@ __git_remotes ()
 	done
 }
 
+__git_merge_strategies ()
+{
+	sed -n "/^all_strategies='/{
+		s/^all_strategies='//
+		s/'//
+		p
+		q
+		}" "$(git --exec-path)/git-merge"
+}
+
 __git_complete_file ()
 {
 	local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
@@ -240,6 +250,24 @@ _git_log ()
 	esac
 }
 
+_git_merge ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--no-commit --no-summary --squash
+			" -- "$cur"))
+		return
+	esac
+	if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ]
+	then
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
+	else
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
+	fi
+}
+
 _git_merge_base ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -348,6 +376,7 @@ _git ()
 	log)         _git_log ;;
 	ls-remote)   _git_ls_remote ;;
 	ls-tree)     _git_ls_tree ;;
+	merge)       _git_merge;;
 	merge-base)  _git_merge_base ;;
 	pull)        _git_pull ;;
 	push)        _git_push ;;
@@ -376,6 +405,7 @@ complete -o default -o nospace -F _git_fetch git-fetch
 complete -o default -o nospace -F _git_log git-log
 complete -o default            -F _git_ls_remote git-ls-remote
 complete -o default -o nospace -F _git_ls_tree git-ls-tree
+complete -o default            -F _git_merge git-merge
 complete -o default            -F _git_merge_base git-merge-base
 complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push

From f2bb9f88805f370b9de1c116f42e3ceb30321c80 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:41:01 -0500
Subject: [PATCH 042/330] Hide plumbing/transport commands from bash
 completion.

Users generally are not going to need to invoke plumbing-level commands
from within one line shell commands.  If they are invoking these commands
then it is likely that they are glueing them together into a shell script
to perform an action, in which case bash completion for these commands is
of relatively little use.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 62 +++++++++++++++++++++++---
 1 file changed, 57 insertions(+), 5 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 28bd0e3ba7..b55431fbf2 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -125,6 +125,58 @@ __git_complete_file ()
 	esac
 }
 
+__git_commands ()
+{
+	local i IFS=" "$'\n'
+	for i in $(git help -a|egrep '^ ')
+	do
+		case $i in
+		check-ref-format) : plumbing;;
+		commit-tree)      : plumbing;;
+		convert-objects)  : plumbing;;
+		cvsserver)        : daemon;;
+		daemon)           : daemon;;
+		fetch-pack)       : plumbing;;
+		hash-object)      : plumbing;;
+		http-*)           : transport;;
+		index-pack)       : plumbing;;
+		local-fetch)      : plumbing;;
+		mailinfo)         : plumbing;;
+		mailsplit)        : plumbing;;
+		merge-*)          : plumbing;;
+		mktree)           : plumbing;;
+		mktag)            : plumbing;;
+		pack-objects)     : plumbing;;
+		pack-redundant)   : plumbing;;
+		pack-refs)        : plumbing;;
+		parse-remote)     : plumbing;;
+		patch-id)         : plumbing;;
+		peek-remote)      : plumbing;;
+		read-tree)        : plumbing;;
+		receive-pack)     : plumbing;;
+		rerere)           : plumbing;;
+		rev-list)         : plumbing;;
+		rev-parse)        : plumbing;;
+		runstatus)        : plumbing;;
+		sh-setup)         : internal;;
+		shell)            : daemon;;
+		send-pack)        : plumbing;;
+		show-index)       : plumbing;;
+		ssh-*)            : transport;;
+		stripspace)       : plumbing;;
+		symbolic-ref)     : plumbing;;
+		unpack-file)      : plumbing;;
+		unpack-objects)   : plumbing;;
+		update-ref)       : plumbing;;
+		update-server-info) : daemon;;
+		upload-archive)   : plumbing;;
+		upload-pack)      : plumbing;;
+		write-tree)       : plumbing;;
+		*) echo $i;;
+		esac
+	done
+}
+
 __git_aliases ()
 {
 	local i IFS=$'\n'
@@ -355,11 +407,11 @@ _git ()
 	done
 
 	if [ $c -eq $COMP_CWORD -a -z "$command" ]; then
-		COMPREPLY=($(compgen \
-			-W "--git-dir= --version \
-				$(git help -a|egrep '^ ') \
-			    $(__git_aliases)" \
-			-- "${COMP_WORDS[COMP_CWORD]}"))
+		COMPREPLY=($(compgen -W "
+			--git-dir= --version --exec-path
+			$(__git_commands)
+			$(__git_aliases)
+			" -- "${COMP_WORDS[COMP_CWORD]}"))
 		return;
 	fi
 

From d33909bf6e78e6ab8a45bfdd19bc19213fde5501 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:41:12 -0500
Subject: [PATCH 043/330] Teach bash how to complete options for git-name-rev.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index b55431fbf2..1dfb59207a 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -326,6 +326,12 @@ _git_merge_base ()
 	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
+_git_name_rev ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	COMPREPLY=($(compgen -W "--tags --all --stdin" -- "$cur"))
+}
+
 _git_pull ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -430,6 +436,7 @@ _git ()
 	ls-tree)     _git_ls_tree ;;
 	merge)       _git_merge;;
 	merge-base)  _git_merge_base ;;
+	name-rev)    _git_name_rev ;;
 	pull)        _git_pull ;;
 	push)        _git_push ;;
 	reset)       _git_reset ;;
@@ -459,6 +466,7 @@ complete -o default            -F _git_ls_remote git-ls-remote
 complete -o default -o nospace -F _git_ls_tree git-ls-tree
 complete -o default            -F _git_merge git-merge
 complete -o default            -F _git_merge_base git-merge-base
+complete -o default            -F _git_name_rev git-name-rev
 complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push
 complete -o default            -F _git_reset git-reset
@@ -479,6 +487,7 @@ complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
 complete -o default -o nospace -F _git_log git-log.exe
 complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 complete -o default            -F _git_merge_base git-merge-base.exe
+complete -o default            -F _git_name_rev git-name-rev.exe
 complete -o default -o nospace -F _git_push git-push.exe
 complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe

From d3d717a4ad0c8d7329e79f7d0313baec57c6b585 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:41:28 -0500
Subject: [PATCH 044/330] Add current branch in PS1 support to
 git-completion.bash.

Many users want to display the current branch name of the current git
repository as part of their PS1 prompt, much as their PS1 prompt might
also display the current working directory name.

We don't force our own PS1 onto the user.  Instead we let them craft
their own PS1 string and offer them the function __git_ps1 which they
can invoke to obtain either "" (when not in a git repository) or
"(%s)" where %s is the name of the current branch, as read from HEAD,
with the leading refs/heads/ removed.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 1dfb59207a..a740d05e28 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -18,12 +18,31 @@
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
+#    3) Consider changing your PS1 to also show the current branch:
+#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
+#
+#       The argument to __git_ps1 will be displayed only if you
+#       are currently in a git repository.  The %s token will be
+#       the name of the current branch.
+#
 
 __gitdir ()
 {
 	echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
 }
 
+__git_ps1 ()
+{
+	local b="$(git symbolic-ref HEAD 2>/dev/null)"
+	if [ -n "$b" ]; then
+		if [ -n "$1" ]; then
+			printf "$1" "${b##refs/heads/}"
+		else
+			printf " (%s)" "${b##refs/heads/}"
+		fi
+	fi
+}
+
 __git_refs ()
 {
 	local cmd i is_hash=y dir="${1:-$(__gitdir)}"

From f53352fbaf2bcc6d209388bb2a68d888ceb8014e Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:41:43 -0500
Subject: [PATCH 045/330] Teach bash how to complete git-format-patch.

Provide completion for currently known long options supported by
git-format-patch as well as the revision list specification argument,
which is generally either a refname or in the form a..b.

Since _git_log was the only code that knew how to complete a..b, but
we want to start adding option support to _git_log also refactor the
a..b completion logic out into its own function.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 60 +++++++++++++++++++-------
 1 file changed, 44 insertions(+), 16 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index a740d05e28..dfdc3968b8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -144,6 +144,26 @@ __git_complete_file ()
 	esac
 }
 
+__git_complete_revlist ()
+{
+	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	*...*)
+		pfx="${cur%...*}..."
+		cur="${cur#*...}"
+		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
+		;;
+	*..*)
+		pfx="${cur%..*}.."
+		cur="${cur#*..}"
+		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
+		;;
+	*)
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
+		;;
+	esac
+}
+
 __git_commands ()
 {
 	local i IFS=" "$'\n'
@@ -290,6 +310,26 @@ _git_fetch ()
 	esac
 }
 
+_git_format_patch ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--stdout --attach --thread
+			--output-directory
+			--numbered --start-number
+			--keep-subject
+			--signoff
+			--in-reply-to=
+			--full-index --binary
+			" -- "$cur"))
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
 _git_ls_remote ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -303,22 +343,7 @@ _git_ls_tree ()
 
 _git_log ()
 {
-	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
-	case "$cur" in
-	*...*)
-		pfx="${cur%...*}..."
-		cur="${cur#*...}"
-		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
-		;;
-	*..*)
-		pfx="${cur%..*}.."
-		cur="${cur#*..}"
-		COMPREPLY=($(compgen -P "$pfx" -W "$(__git_refs)" -- "$cur"))
-		;;
-	*)
-		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
-		;;
-	esac
+	__git_complete_revlist
 }
 
 _git_merge ()
@@ -450,6 +475,7 @@ _git ()
 	diff)        _git_diff ;;
 	diff-tree)   _git_diff_tree ;;
 	fetch)       _git_fetch ;;
+	format-patch) _git_format_patch ;;
 	log)         _git_log ;;
 	ls-remote)   _git_ls_remote ;;
 	ls-tree)     _git_ls_tree ;;
@@ -480,6 +506,7 @@ complete -o default            -F _git_checkout git-checkout
 complete -o default -o nospace -F _git_diff git-diff
 complete -o default            -F _git_diff_tree git-diff-tree
 complete -o default -o nospace -F _git_fetch git-fetch
+complete -o default -o nospace -F _git_format_patch git-format-patch
 complete -o default -o nospace -F _git_log git-log
 complete -o default            -F _git_ls_remote git-ls-remote
 complete -o default -o nospace -F _git_ls_tree git-ls-tree
@@ -503,6 +530,7 @@ complete -o default            -F _git_branch git-branch.exe
 complete -o default -o nospace -F _git_cat_file git-cat-file.exe
 complete -o default -o nospace -F _git_diff git-diff.exe
 complete -o default -o nospace -F _git_diff_tree git-diff-tree.exe
+complete -o default -o nospace -F _git_format_patch git-format-patch.exe
 complete -o default -o nospace -F _git_log git-log.exe
 complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 complete -o default            -F _git_merge_base git-merge-base.exe

From 1273231ee9c7a576a3654d8f2ba77267393564ab Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:41:59 -0500
Subject: [PATCH 046/330] Teach bash how to complete git-cherry-pick.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index dfdc3968b8..5582561acd 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -269,6 +269,21 @@ _git_checkout ()
 	COMPREPLY=($(compgen -W "-l -b $(__git_refs)" -- "$cur"))
 }
 
+_git_cherry_pick ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--edit --no-commit
+			" -- "$cur"))
+		;;
+	*)
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
+		;;
+	esac
+}
+
 _git_diff ()
 {
 	__git_complete_file
@@ -472,6 +487,7 @@ _git ()
 	branch)      _git_branch ;;
 	cat-file)    _git_cat_file ;;
 	checkout)    _git_checkout ;;
+	cherry-pick) _git_cherry_pick ;;
 	diff)        _git_diff ;;
 	diff-tree)   _git_diff_tree ;;
 	fetch)       _git_fetch ;;
@@ -503,6 +519,7 @@ complete -o default            -F _gitk gitk
 complete -o default            -F _git_branch git-branch
 complete -o default -o nospace -F _git_cat_file git-cat-file
 complete -o default            -F _git_checkout git-checkout
+complete -o default            -F _git_cherry_pick git-cherry-pick
 complete -o default -o nospace -F _git_diff git-diff
 complete -o default            -F _git_diff_tree git-diff-tree
 complete -o default -o nospace -F _git_fetch git-fetch

From 61d926a3cdb8f03147580de53e448fc22370cbb1 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:42:07 -0500
Subject: [PATCH 047/330] Teach bash how to complete git-rebase.

As git-rebase is a popular command bash should know how to complete
reference names and its long options.  We only support completions
which make sense given the current state of the repository, that
way users don't get shown --continue/--skip/--abort on the first
execution.

Also added support for long option --strategy to git-merge, as I
missed that option earlier and just noticed it while implementing
git-rebase.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 38 ++++++++++++++++++++++----
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 5582561acd..9f63ff62f8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -367,16 +367,16 @@ _git_merge ()
 	case "$cur" in
 	--*)
 		COMPREPLY=($(compgen -W "
-			--no-commit --no-summary --squash
+			--no-commit --no-summary --squash --strategy
 			" -- "$cur"))
 		return
 	esac
-	if [ $COMP_CWORD -gt 1 -a X-s = "X${COMP_WORDS[COMP_CWORD-1]}" ]
-	then
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
 		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
-	else
-		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
-	fi
+		return
+	esac
+	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
 _git_merge_base ()
@@ -443,6 +443,30 @@ _git_push ()
 	esac
 }
 
+_git_rebase ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	if [ -d .dotest ]; then
+		COMPREPLY=($(compgen -W "
+			--continue --skip --abort
+			" -- "$cur"))
+		return
+	fi
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--onto --merge --strategy
+			" -- "$cur"))
+		return
+	esac
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
+		return
+	esac
+	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
+}
+
 _git_reset ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -500,6 +524,7 @@ _git ()
 	name-rev)    _git_name_rev ;;
 	pull)        _git_pull ;;
 	push)        _git_push ;;
+	rebase)      _git_rebase ;;
 	reset)       _git_reset ;;
 	show)        _git_show ;;
 	show-branch) _git_log ;;
@@ -532,6 +557,7 @@ complete -o default            -F _git_merge_base git-merge-base
 complete -o default            -F _git_name_rev git-name-rev
 complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push
+complete -o default            -F _git_rebase git-rebase
 complete -o default            -F _git_reset git-reset
 complete -o default            -F _git_show git-show
 complete -o default -o nospace -F _git_log git-show-branch

From 6e31b866e4c8e6fc432e6087d56be73c08cf0f83 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:42:18 -0500
Subject: [PATCH 048/330] Teach bash about git log/show/whatchanged options.

Typing out options to git log/show/whatchanged can take a while, but
we can easily complete them with bash.  So list the most common ones,
especially --pretty=online|short|medium|... so that users don't need
to type everything out.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 34 ++++++++++++++++++++------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9f63ff62f8..3852f467d8 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -358,6 +358,29 @@ _git_ls_tree ()
 
 _git_log ()
 {
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--pretty=*)
+		COMPREPLY=($(compgen -W "
+			oneline short medium full fuller email raw
+			" -- "${cur##--pretty=}"))
+		return
+		;;
+	--*)
+		COMPREPLY=($(compgen -W "
+			--max-count= --max-age= --since= --after=
+			--min-age= --before= --until=
+			--root --not --topo-order --date-order
+			--no-merges
+			--abbrev-commit --abbrev=
+			--relative-date
+			--author= --committer= --grep=
+			--all-match
+			--pretty= --name-status --name-only
+			" -- "$cur"))
+		return
+		;;
+	esac
 	__git_complete_revlist
 }
 
@@ -474,12 +497,6 @@ _git_reset ()
 	COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 }
 
-_git_show ()
-{
-	local cur="${COMP_WORDS[COMP_CWORD]}"
-	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
-}
-
 _git ()
 {
 	local i c=1 command __git_dir
@@ -526,7 +543,7 @@ _git ()
 	push)        _git_push ;;
 	rebase)      _git_rebase ;;
 	reset)       _git_reset ;;
-	show)        _git_show ;;
+	show)        _git_log ;;
 	show-branch) _git_log ;;
 	whatchanged) _git_log ;;
 	*)           COMPREPLY=() ;;
@@ -559,7 +576,7 @@ complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push
 complete -o default            -F _git_rebase git-rebase
 complete -o default            -F _git_reset git-reset
-complete -o default            -F _git_show git-show
+complete -o default            -F _git_log git-show
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_log git-whatchanged
 
@@ -579,6 +596,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 complete -o default            -F _git_merge_base git-merge-base.exe
 complete -o default            -F _git_name_rev git-name-rev.exe
 complete -o default -o nospace -F _git_push git-push.exe
+complete -o default -o nospace -F _git_log git-show.exe
 complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe
 fi

From 35e65ecca78ceeca8eca72149e7546de94ed8607 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 03:42:32 -0500
Subject: [PATCH 049/330] Support bash completion of refs/remote.

Now that people are really likely to start using separate remotes
(due to the default in git-clone changing) we should support ref
completion for these refs in as many commands as possible.

While we are working on this routine we should use for-each-ref
to obtain a list of local refs, as this should run faster than
peek-remote as it does not need to dereference tag objects in
order to produce the list of refs back to us.  It should also
be more friendly to users of StGIT as we won't generate a list
of the StGIT metadata refs.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3852f467d8..a9c456f61b 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -47,16 +47,26 @@ __git_refs ()
 {
 	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
 	if [ -d "$dir" ]; then
-		cmd=git-peek-remote
-	else
-		cmd=git-ls-remote
+		if [ -e "$dir/HEAD" ]; then echo HEAD; fi
+		for i in $(git --git-dir="$dir" \
+			for-each-ref --format='%(refname)' \
+			refs/tags refs/heads refs/remotes); do
+			case "$i" in
+				refs/tags/*)    echo "${i#refs/tags/}" ;;
+				refs/heads/*)   echo "${i#refs/heads/}" ;;
+				refs/remotes/*) echo "${i#refs/remotes/}" ;;
+				*)              echo "$i" ;;
+			esac
+		done
+		return
 	fi
-	for i in $($cmd "$dir" 2>/dev/null); do
+	for i in $(git-ls-remote "$dir" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
 		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
 		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+		n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
 		n,*) is_hash=y; echo "$i" ;;
 		esac
 	done

From 5de40f59d4954738448e238b06eed72f73cca740 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 04:44:47 -0500
Subject: [PATCH 050/330] Teach bash about git-repo-config.

This is a really ugly completion script for git-repo-config, but it has
some nice properties.  I've added all of the documented configuration
parameters from Documentation/config.txt to the script, allowing the
user to complete any standard configuration parameter name.

We also have some intelligence for the remote.*.* and branch.*.* keys
by completing not only the key name (e.g. remote.origin) but also the
values (e.g. remote.*.fetch completes to the branches available on the
corresponding remote).

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 154 +++++++++++++++++++++++++
 1 file changed, 154 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index a9c456f61b..73c67691c9 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -43,6 +43,27 @@ __git_ps1 ()
 	fi
 }
 
+__git_heads ()
+{
+	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+	if [ -d "$dir" ]; then
+		for i in $(git --git-dir="$dir" \
+			for-each-ref --format='%(refname)' \
+			refs/heads ); do
+			echo "${i#refs/heads/}"
+		done
+		return
+	fi
+	for i in $(git-ls-remote "$dir" 2>/dev/null); do
+		case "$is_hash,$i" in
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+		n,*) is_hash=y; echo "$i" ;;
+		esac
+	done
+}
+
 __git_refs ()
 {
 	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
@@ -91,6 +112,23 @@ __git_refs2 ()
 	done
 }
 
+__git_refs_remotes ()
+{
+	local cmd i is_hash=y
+	for i in $(git-ls-remote "$1" 2>/dev/null); do
+		case "$is_hash,$i" in
+		n,refs/heads/*)
+			is_hash=y
+			echo "$i:refs/remotes/$1/${i#refs/heads/}"
+			;;
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/tags/*) is_hash=y;;
+		n,*) is_hash=y; ;;
+		esac
+	done
+}
+
 __git_remotes ()
 {
 	local i ngoff IFS=$'\n' d="$(__gitdir)"
@@ -500,6 +538,119 @@ _git_rebase ()
 	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
+_git_repo_config ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local prv="${COMP_WORDS[COMP_CWORD-1]}"
+	case "$prv" in
+	branch.*.remote)
+		COMPREPLY=($(compgen -W "$(__git_remotes)" -- "$cur"))
+		return
+		;;
+	branch.*.merge)
+		COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
+		return
+		;;
+	remote.*.fetch)
+		local remote="${prv#remote.}"
+		remote="${remote%.fetch}"
+		COMPREPLY=($(compgen -W "$(__git_refs_remotes "$remote")" \
+			-- "$cur"))
+		return
+		;;
+	remote.*.push)
+		local remote="${prv#remote.}"
+		remote="${remote%.push}"
+		COMPREPLY=($(compgen -W "$(git --git-dir="$(__gitdir)" \
+			for-each-ref --format='%(refname):%(refname)' \
+			refs/heads)" -- "$cur"))
+		return
+		;;
+	*.*)
+		COMPREPLY=()
+		return
+		;;
+	esac
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--global --list --replace-all
+			--get --get-all --get-regexp
+			--unset --unset-all
+			" -- "$cur"))
+		return
+		;;
+	branch.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		COMPREPLY=($(compgen -P "$pfx" -W "remote merge" -- "$cur"))
+		return
+		;;
+	branch.*)
+		local pfx="${cur%.*}."
+		cur="${cur#*.}"
+		COMPREPLY=($(compgen -P "$pfx" -S . \
+			-W "$(__git_heads)" -- "$cur"))
+		return
+		;;
+	remote.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		COMPREPLY=($(compgen -P "$pfx" -W "url fetch push" -- "$cur"))
+		return
+		;;
+	remote.*)
+		local pfx="${cur%.*}."
+		cur="${cur#*.}"
+		COMPREPLY=($(compgen -P "$pfx" -S . \
+			-W "$(__git_remotes)" -- "$cur"))
+		return
+		;;
+	esac
+	COMPREPLY=($(compgen -W "
+		apply.whitespace
+		core.fileMode
+		core.gitProxy
+		core.ignoreStat
+		core.preferSymlinkRefs
+		core.logAllRefUpdates
+		core.repositoryFormatVersion
+		core.sharedRepository
+		core.warnAmbiguousRefs
+		core.compression
+		core.legacyHeaders
+		i18n.commitEncoding
+		diff.color
+		diff.renameLimit
+		diff.renames
+		pager.color
+		status.color
+		log.showroot
+		show.difftree
+		showbranch.default
+		whatchanged.difftree
+		http.sslVerify
+		http.sslCert
+		http.sslKey
+		http.sslCAInfo
+		http.sslCAPath
+		http.maxRequests
+		http.lowSpeedLimit http.lowSpeedTime
+		http.noEPSV
+		pack.window
+		repack.useDeltaBaseOffset
+		pull.octopus pull.twohead
+		merge.summary
+		receive.unpackLimit
+		receive.denyNonFastForwards
+		user.name user.email
+		tar.umask
+		gitcvs.enabled
+		gitcvs.logfile
+		branch. remote.
+	" -- "$cur"))
+}
+
 _git_reset ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -552,6 +703,7 @@ _git ()
 	pull)        _git_pull ;;
 	push)        _git_push ;;
 	rebase)      _git_rebase ;;
+	repo-config) _git_repo_config ;;
 	reset)       _git_reset ;;
 	show)        _git_log ;;
 	show-branch) _git_log ;;
@@ -585,6 +737,7 @@ complete -o default            -F _git_name_rev git-name-rev
 complete -o default -o nospace -F _git_pull git-pull
 complete -o default -o nospace -F _git_push git-push
 complete -o default            -F _git_rebase git-rebase
+complete -o default            -F _git_repo_config git-repo-config
 complete -o default            -F _git_reset git-reset
 complete -o default            -F _git_log git-show
 complete -o default -o nospace -F _git_log git-show-branch
@@ -606,6 +759,7 @@ complete -o default -o nospace -F _git_ls_tree git-ls-tree.exe
 complete -o default            -F _git_merge_base git-merge-base.exe
 complete -o default            -F _git_name_rev git-name-rev.exe
 complete -o default -o nospace -F _git_push git-push.exe
+complete -o default            -F _git_repo_config git-repo-config
 complete -o default -o nospace -F _git_log git-show.exe
 complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe

From ce1e39d29ee373786ceda9e79d0906a6451ab5a5 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 15:10:42 -0500
Subject: [PATCH 051/330] Support --strategy=x completion in addition to
 --strategy x.

Because git-merge and git-rebase both accept -s, --strategy or --strategy=
we should recognize all three formats in the bash completion functions and
issue back all merge strategies on demand.

I also moved the prior word testing to be before the current word testing,
as the current word cannot be completed with -- if the prior word was an
option which requires a parameter, such as -s or --strategy.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 30 +++++++++++++++++---------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 73c67691c9..16b8dda17d 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -435,18 +435,23 @@ _git_log ()
 _git_merge ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
+		return
+	esac
 	case "$cur" in
+	--strategy=*)
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \
+			-- "${cur##--strategy=}"))
+		return
+		;;
 	--*)
 		COMPREPLY=($(compgen -W "
 			--no-commit --no-summary --squash --strategy
 			" -- "$cur"))
 		return
 	esac
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
-		return
-	esac
 	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 
@@ -523,18 +528,23 @@ _git_rebase ()
 			" -- "$cur"))
 		return
 	fi
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
+		return
+	esac
 	case "$cur" in
+	--strategy=*)
+		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" \
+			-- "${cur##--strategy=}"))
+		return
+		;;
 	--*)
 		COMPREPLY=($(compgen -W "
 			--onto --merge --strategy
 			" -- "$cur"))
 		return
 	esac
-	case "${COMP_WORDS[COMP_CWORD-1]}" in
-	-s|--strategy)
-		COMPREPLY=($(compgen -W "$(__git_merge_strategies)" -- "$cur"))
-		return
-	esac
 	COMPREPLY=($(compgen -W "$(__git_refs)" -- "$cur"))
 }
 

From b51ec6bddb572def384a21a18d5919a488e06ffb Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 15:11:52 -0500
Subject: [PATCH 052/330] Cache the list of merge strategies and available
 commands during load.

Since the user's git installation is not likely to grow a new command
or merge strategy in the lifespan of the current shell process we can
save time during completion operations by caching these lists during
sourcing of the completion support.

If the git executable is not available or we run into errors while
caching at load time then we defer these to runtime and generate
the list on the fly.  This might happen if the user doesn't put git
into their PATH until after the completion script gets sourced.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 16b8dda17d..902f80b937 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -18,7 +18,13 @@
 #    2) Added the following line to your .bashrc:
 #        source ~/.git-completion.sh
 #
-#    3) Consider changing your PS1 to also show the current branch:
+#    3) You may want to make sure the git executable is available
+#       in your PATH before this script is sourced, as some caching
+#       is performed while the script loads.  If git isn't found
+#       at source time then all lookups will be done on demand,
+#       which may be slightly slower.
+#
+#    4) Consider changing your PS1 to also show the current branch:
 #        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
 #
 #       The argument to __git_ps1 will be displayed only if you
@@ -150,6 +156,10 @@ __git_remotes ()
 
 __git_merge_strategies ()
 {
+	if [ -n "$__git_merge_strategylist" ]; then
+		echo "$__git_merge_strategylist"
+		return
+	fi
 	sed -n "/^all_strategies='/{
 		s/^all_strategies='//
 		s/'//
@@ -157,6 +167,8 @@ __git_merge_strategies ()
 		q
 		}" "$(git --exec-path)/git-merge"
 }
+__git_merge_strategylist=
+__git_merge_strategylist="$(__git_merge_strategies 2>/dev/null)"
 
 __git_complete_file ()
 {
@@ -214,6 +226,10 @@ __git_complete_revlist ()
 
 __git_commands ()
 {
+	if [ -n "$__git_commandlist" ]; then
+		echo "$__git_commandlist"
+		return
+	fi
 	local i IFS=" "$'\n'
 	for i in $(git help -a|egrep '^ ')
 	do
@@ -263,6 +279,8 @@ __git_commands ()
 		esac
 	done
 }
+__git_commandlist=
+__git_commandlist="$(__git_commands 2>/dev/null)"
 
 __git_aliases ()
 {

From 88329195159865c0fcc57a6017c959d13d7a1ad6 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Mon, 27 Nov 2006 15:12:03 -0500
Subject: [PATCH 053/330] Teach bash about git-am/git-apply and their
 whitespace options.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 53 ++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 902f80b937..d8ae4d7886 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -307,6 +307,54 @@ __git_aliased_command ()
 	done
 }
 
+__git_whitespacelist="nowarn warn error error-all strip"
+
+_git_am ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	if [ -d .dotest ]; then
+		COMPREPLY=($(compgen -W "
+			--skip --resolved
+			" -- "$cur"))
+		return
+	fi
+	case "$cur" in
+	--whitespace=*)
+		COMPREPLY=($(compgen -W "$__git_whitespacelist" \
+			-- "${cur##--whitespace=}"))
+		return
+		;;
+	--*)
+		COMPREPLY=($(compgen -W "
+			--signoff --utf8 --binary --3way --interactive
+			--whitespace=
+			" -- "$cur"))
+		return
+	esac
+	COMPREPLY=()
+}
+
+_git_apply ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--whitespace=*)
+		COMPREPLY=($(compgen -W "$__git_whitespacelist" \
+			-- "${cur##--whitespace=}"))
+		return
+		;;
+	--*)
+		COMPREPLY=($(compgen -W "
+			--stat --numstat --summary --check --index
+			--cached --index-info --reverse --reject --unidiff-zero
+			--apply --no-add --exclude=
+			--whitespace= --inaccurate-eof --verbose
+			" -- "$cur"))
+		return
+	esac
+	COMPREPLY=()
+}
+
 _git_branch ()
 {
 	local cur="${COMP_WORDS[COMP_CWORD]}"
@@ -714,6 +762,8 @@ _git ()
 	[ "$expansion" ] && command="$expansion"
 
 	case "$command" in
+	am)          _git_am ;;
+	apply)       _git_apply ;;
 	branch)      _git_branch ;;
 	cat-file)    _git_cat_file ;;
 	checkout)    _git_checkout ;;
@@ -748,6 +798,8 @@ _gitk ()
 
 complete -o default -o nospace -F _git git
 complete -o default            -F _gitk gitk
+complete -o default            -F _git_am git-am
+complete -o default            -F _git_apply git-apply
 complete -o default            -F _git_branch git-branch
 complete -o default -o nospace -F _git_cat_file git-cat-file
 complete -o default            -F _git_checkout git-checkout
@@ -776,6 +828,7 @@ complete -o default -o nospace -F _git_log git-whatchanged
 # included the '.exe' suffix.
 #
 if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
+complete -o default            -F _git_apply git-apply.exe
 complete -o default -o nospace -F _git git.exe
 complete -o default            -F _git_branch git-branch.exe
 complete -o default -o nospace -F _git_cat_file git-cat-file.exe

From 6f23ebf600188fe4246c2fb118f0c977ba1a2ed6 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Mon, 27 Nov 2006 13:20:53 -0800
Subject: [PATCH 054/330] git-svn: use ~/.subversion config files when using
 SVN:: libraries

This allows users to use HTTP proxy information (among other settings)
from ~/.subversion/servers and ~/.subversion/config

--config-dir (as before) may be passed to git-svn to override the
default choice of '~/.subversion' for the configuration directory.

Thanks to tko on #git for pointing this out.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index de4e74a60d..d5d9c49fd6 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2822,7 +2822,9 @@ sub libsvn_connect {
 	    SVN::Client::get_username_prompt_provider(
 	      \&_username_prompt, 2),
 	  ]);
+	my $config = SVN::Core::config_get_config($_config_dir);
 	my $ra = SVN::Ra->new(url => $url, auth => $baton,
+	                      config => $config,
 	                      pool => SVN::Pool->new,
 	                      auth_provider_callbacks => $callbacks);
 	$ra->{svn_path} = $url;
@@ -2834,8 +2836,8 @@ sub libsvn_connect {
 
 sub libsvn_dup_ra {
 	my ($ra) = @_;
-	SVN::Ra->new(map { $_ => $ra->{$_} }
-	             qw/url auth auth_provider_callbacks repos_root svn_path/);
+	SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
+	             auth auth_provider_callbacks repos_root svn_path/);
 }
 
 sub libsvn_get_file {

From 86d11cf264c55d570484cbdfff073092c77a6342 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 27 Nov 2006 14:21:30 -0800
Subject: [PATCH 055/330] cvsimport: style fixup.

This should not change any functionality, but just makes it readable by
having a space between syntactic construct keyword and open parenthesis
(e.g. "if (expr", not "if(expr") and between close parenthesis and open
brace (e.g. "if (expr) {" not "if (expr){").

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-cvsimport.perl | 168 ++++++++++++++++++++++-----------------------
 1 file changed, 84 insertions(+), 84 deletions(-)

diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 4310dea132..c5bf2d19cd 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -29,7 +29,7 @@ use IPC::Open2;
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
+our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L);
 my (%conv_author_name, %conv_author_email);
 
 sub usage() {
@@ -90,15 +90,15 @@ usage if $opt_h;
 
 @ARGV <= 1 or usage();
 
-if($opt_d) {
+if ($opt_d) {
 	$ENV{"CVSROOT"} = $opt_d;
-} elsif(-f 'CVS/Root') {
+} elsif (-f 'CVS/Root') {
 	open my $f, '<', 'CVS/Root' or die 'Failed to open CVS/Root';
 	$opt_d = <$f>;
 	chomp $opt_d;
 	close $f;
 	$ENV{"CVSROOT"} = $opt_d;
-} elsif($ENV{"CVSROOT"}) {
+} elsif ($ENV{"CVSROOT"}) {
 	$opt_d = $ENV{"CVSROOT"};
 } else {
 	die "CVSROOT needs to be set";
@@ -141,7 +141,7 @@ use File::Temp qw(tempfile);
 use POSIX qw(strftime dup2);
 
 sub new {
-	my($what,$repo,$subdir) = @_;
+	my ($what,$repo,$subdir) = @_;
 	$what=ref($what) if ref($what);
 
 	my $self = {};
@@ -161,38 +161,38 @@ sub new {
 sub conn {
 	my $self = shift;
 	my $repo = $self->{'fullrep'};
-	if($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
-		my($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
+	if ($repo =~ s/^:pserver(?:([^:]*)):(?:(.*?)(?::(.*?))?@)?([^:\/]*)(?::(\d*))?//) {
+		my ($param,$user,$pass,$serv,$port) = ($1,$2,$3,$4,$5);
 
-		my($proxyhost,$proxyport);
-		if($param && ($param =~ m/proxy=([^;]+)/)) {
+		my ($proxyhost,$proxyport);
+		if ($param && ($param =~ m/proxy=([^;]+)/)) {
 			$proxyhost = $1;
 			# Default proxyport, if not specified, is 8080.
 			$proxyport = 8080;
-			if($ENV{"CVS_PROXY_PORT"}) {
+			if ($ENV{"CVS_PROXY_PORT"}) {
 				$proxyport = $ENV{"CVS_PROXY_PORT"};
 			}
-			if($param =~ m/proxyport=([^;]+)/){
+			if ($param =~ m/proxyport=([^;]+)/) {
 				$proxyport = $1;
 			}
 		}
 
 		$user="anonymous" unless defined $user;
 		my $rr2 = "-";
-		unless($port) {
+		unless ($port) {
 			$rr2 = ":pserver:$user\@$serv:$repo";
 			$port=2401;
 		}
 		my $rr = ":pserver:$user\@$serv:$port$repo";
 
-		unless($pass) {
+		unless ($pass) {
 			open(H,$ENV{'HOME'}."/.cvspass") and do {
 				# :pserver:cvs@mea.tmt.tele.fi:/cvsroot/zmailer Ah<Z
-				while(<H>) {
+				while (<H>) {
 					chomp;
 					s/^\/\d+\s+//;
 					my ($w,$p) = split(/\s/,$_,2);
-					if($w eq $rr or $w eq $rr2) {
+					if ($w eq $rr or $w eq $rr2) {
 						$pass = $p;
 						last;
 					}
@@ -202,7 +202,7 @@ sub conn {
 		$pass="A" unless $pass;
 
 		my ($s, $rep);
-		if($proxyhost) {
+		if ($proxyhost) {
 
 			# Use a HTTP Proxy. Only works for HTTP proxies that
 			# don't require user authentication
@@ -218,7 +218,7 @@ sub conn {
 			$rep = <$s>;
 
 			# The answer should look like 'HTTP/1.x 2yy ....'
-			if(!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
+			if (!($rep =~ m#^HTTP/1\.. 2[0-9][0-9]#)) {
 				die "Proxy connect: $rep\n";
 			}
 			# Skip up to the empty line of the proxy server output
@@ -239,7 +239,7 @@ sub conn {
 
 		$rep = <$s>;
 
-		if($rep ne "I LOVE YOU\n") {
+		if ($rep ne "I LOVE YOU\n") {
 			$rep="<unknown>" unless $rep;
 			die "AuthReply: $rep\n";
 		}
@@ -271,7 +271,7 @@ sub conn {
 		    }
 		}
 
-		unless($pid) {
+		unless ($pid) {
 			$pr->writer();
 			$pw->reader();
 			dup2($pw->fileno(),0);
@@ -294,7 +294,7 @@ sub conn {
 	$self->{'socketo'}->flush();
 
 	chomp(my $rep=$self->readline());
-	if($rep !~ s/^Valid-requests\s*//) {
+	if ($rep !~ s/^Valid-requests\s*//) {
 		$rep="<unknown>" unless $rep;
 		die "Expected Valid-requests from server, but got: $rep\n";
 	}
@@ -306,14 +306,14 @@ sub conn {
 }
 
 sub readline {
-	my($self) = @_;
+	my ($self) = @_;
 	return $self->{'socketi'}->getline();
 }
 
 sub _file {
 	# Request a file with a given revision.
 	# Trial and error says this is a good way to do it. :-/
-	my($self,$fn,$rev) = @_;
+	my ($self,$fn,$rev) = @_;
 	$self->{'socketo'}->write("Argument -N\n") or return undef;
 	$self->{'socketo'}->write("Argument -P\n") or return undef;
 	# -kk: Linus' version doesn't use it - defaults to off
@@ -335,12 +335,12 @@ sub _file {
 sub _line {
 	# Read a line from the server.
 	# ... except that 'line' may be an entire file. ;-)
-	my($self, $fh) = @_;
+	my ($self, $fh) = @_;
 	die "Not in lines" unless defined $self->{'lines'};
 
 	my $line;
 	my $res=0;
-	while(defined($line = $self->readline())) {
+	while (defined($line = $self->readline())) {
 		# M U gnupg-cvs-rep/AUTHORS
 		# Updated gnupg-cvs-rep/
 		# /daten/src/rsync/gnupg-cvs-rep/AUTHORS
@@ -349,7 +349,7 @@ sub _line {
 		# 0
 		# ok
 
-		if($line =~ s/^(?:Created|Updated) //) {
+		if ($line =~ s/^(?:Created|Updated) //) {
 			$line = $self->readline(); # path
 			$line = $self->readline(); # Entries line
 			my $mode = $self->readline(); chomp $mode;
@@ -360,12 +360,12 @@ sub _line {
 			die "Duh: Filesize $cnt" if $cnt !~ /^\d+$/;
 			$line="";
 			$res = $self->_fetchfile($fh, $cnt);
-		} elsif($line =~ s/^ //) {
+		} elsif ($line =~ s/^ //) {
 			print $fh $line;
 			$res += length($line);
-		} elsif($line =~ /^M\b/) {
+		} elsif ($line =~ /^M\b/) {
 			# output, do nothing
-		} elsif($line =~ /^Mbinary\b/) {
+		} elsif ($line =~ /^Mbinary\b/) {
 			my $cnt;
 			die "EOF from server after 'Mbinary'" unless defined ($cnt = $self->readline());
 			chomp $cnt;
@@ -374,12 +374,12 @@ sub _line {
 			$res += $self->_fetchfile($fh, $cnt);
 		} else {
 			chomp $line;
-			if($line eq "ok") {
+			if ($line eq "ok") {
 				# print STDERR "S: ok (".length($res).")\n";
 				return $res;
-			} elsif($line =~ s/^E //) {
+			} elsif ($line =~ s/^E //) {
 				# print STDERR "S: $line\n";
-			} elsif($line =~ /^(Remove-entry|Removed) /i) {
+			} elsif ($line =~ /^(Remove-entry|Removed) /i) {
 				$line = $self->readline(); # filename
 				$line = $self->readline(); # OK
 				chomp $line;
@@ -393,7 +393,7 @@ sub _line {
 	return undef;
 }
 sub file {
-	my($self,$fn,$rev) = @_;
+	my ($self,$fn,$rev) = @_;
 	my $res;
 
 	my ($fh, $name) = tempfile('gitcvs.XXXXXX', 
@@ -417,7 +417,7 @@ sub _fetchfile {
 	my ($self, $fh, $cnt) = @_;
 	my $res = 0;
 	my $bufsize = 1024 * 1024;
-	while($cnt) {
+	while ($cnt) {
 	    if ($bufsize > $cnt) {
 		$bufsize = $cnt;
 	    }
@@ -438,7 +438,7 @@ my $cvs = CVSconn->new($opt_d, $cvs_tree);
 
 
 sub pdate($) {
-	my($d) = @_;
+	my ($d) = @_;
 	m#(\d{2,4})/(\d\d)/(\d\d)\s(\d\d):(\d\d)(?::(\d\d))?#
 		or die "Unparseable date: $d\n";
 	my $y=$1; $y-=1900 if $y>1900;
@@ -446,22 +446,22 @@ sub pdate($) {
 }
 
 sub pmode($) {
-	my($mode) = @_;
+	my ($mode) = @_;
 	my $m = 0;
 	my $mm = 0;
 	my $um = 0;
 	for my $x(split(//,$mode)) {
-		if($x eq ",") {
+		if ($x eq ",") {
 			$m |= $mm&$um;
 			$mm = 0;
 			$um = 0;
-		} elsif($x eq "u") { $um |= 0700;
-		} elsif($x eq "g") { $um |= 0070;
-		} elsif($x eq "o") { $um |= 0007;
-		} elsif($x eq "r") { $mm |= 0444;
-		} elsif($x eq "w") { $mm |= 0222;
-		} elsif($x eq "x") { $mm |= 0111;
-		} elsif($x eq "=") { # do nothing
+		} elsif ($x eq "u") { $um |= 0700;
+		} elsif ($x eq "g") { $um |= 0070;
+		} elsif ($x eq "o") { $um |= 0007;
+		} elsif ($x eq "r") { $mm |= 0444;
+		} elsif ($x eq "w") { $mm |= 0222;
+		} elsif ($x eq "x") { $mm |= 0111;
+		} elsif ($x eq "=") { # do nothing
 		} else { die "Unknown mode: $mode\n";
 		}
 	}
@@ -485,7 +485,7 @@ sub get_headref ($$) {
     my $git_dir = shift; 
     
     my $f = "$git_dir/refs/heads/$name";
-    if(open(my $fh, $f)) {
+    if (open(my $fh, $f)) {
 	    chomp(my $r = <$fh>);
 	    is_sha1($r) or die "Cannot get head id for $name ($r): $!";
 	    return $r;
@@ -512,7 +512,7 @@ $orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
 
 my %index; # holds filenames of one index per branch
 
-unless(-d $git_dir) {
+unless (-d $git_dir) {
 	system("git-init-db");
 	die "Cannot init the GIT db at $git_tree: $?\n" if $?;
 	system("git-read-tree");
@@ -531,7 +531,7 @@ unless(-d $git_dir) {
 	chomp ($last_branch = <F>);
 	$last_branch = basename($last_branch);
 	close(F);
-	unless($last_branch) {
+	unless ($last_branch) {
 		warn "Cannot read the last branch name: $! -- assuming 'master'\n";
 		$last_branch = "master";
 	}
@@ -542,7 +542,7 @@ unless(-d $git_dir) {
 	my $fmt = '($ref, $author) = (%(refname), %(author));';
 	open(H, "git-for-each-ref --perl --format='$fmt' refs/heads |") or
 		die "Cannot run git-for-each-ref: $!\n";
-	while(defined(my $entry = <H>)) {
+	while (defined(my $entry = <H>)) {
 		my ($ref, $author);
 		eval($entry) || die "cannot eval refs list: $@";
 		my ($head) = ($ref =~ m|^refs/heads/(.*)|);
@@ -572,7 +572,7 @@ unless ($opt_P) {
 	print "Running cvsps...\n" if $opt_v;
 	my $pid = open(CVSPS,"-|");
 	die "Cannot fork: $!\n" unless defined $pid;
-	unless($pid) {
+	unless ($pid) {
 		my @opt;
 		@opt = split(/,/,$opt_p) if defined $opt_p;
 		unshift @opt, '-z', $opt_z if defined $opt_z;
@@ -642,8 +642,8 @@ sub write_tree () {
 	return $tree;
 }
 
-my($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
-my(@old,@new,@skipped,%ignorebranch);
+my ($patchset,$date,$author_name,$author_email,$branch,$ancestor,$tag,$logmsg);
+my (@old,@new,@skipped,%ignorebranch);
 
 # commits that cvsps cannot place anywhere...
 $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
@@ -684,7 +684,7 @@ sub commit {
 	foreach my $rx (@mergerx) {
 		next unless $logmsg =~ $rx && $1;
 		my $mparent = $1 eq 'HEAD' ? $opt_o : $1;
-		if(my $sha1 = get_headref($mparent, $git_dir)) {
+		if (my $sha1 = get_headref($mparent, $git_dir)) {
 			push @commit_args, '-p', $mparent;
 			print "Merge parent branch: $mparent\n" if $opt_v;
 		}
@@ -725,9 +725,9 @@ sub commit {
 	system("git-update-ref refs/heads/$branch $cid") == 0
 		or die "Cannot write branch $branch for update: $!\n";
 
-	if($tag) {
-		my($in, $out) = ('','');
-	        my($xtag) = $tag;
+	if ($tag) {
+		my ($in, $out) = ('','');
+	        my ($xtag) = $tag;
 		$xtag =~ s/\s+\*\*.*$//; # Remove stuff like ** INVALID ** and ** FUNKY **
 		$xtag =~ tr/_/\./ if ( $opt_u );
 		$xtag =~ s/[\/]/$opt_s/g;
@@ -762,25 +762,25 @@ sub commit {
 };
 
 my $commitcount = 1;
-while(<CVS>) {
+while (<CVS>) {
 	chomp;
-	if($state == 0 and /^-+$/) {
+	if ($state == 0 and /^-+$/) {
 		$state = 1;
-	} elsif($state == 0) {
+	} elsif ($state == 0) {
 		$state = 1;
 		redo;
-	} elsif(($state==0 or $state==1) and s/^PatchSet\s+//) {
+	} elsif (($state==0 or $state==1) and s/^PatchSet\s+//) {
 		$patchset = 0+$_;
 		$state=2;
-	} elsif($state == 2 and s/^Date:\s+//) {
+	} elsif ($state == 2 and s/^Date:\s+//) {
 		$date = pdate($_);
-		unless($date) {
+		unless ($date) {
 			print STDERR "Could not parse date: $_\n";
 			$state=0;
 			next;
 		}
 		$state=3;
-	} elsif($state == 3 and s/^Author:\s+//) {
+	} elsif ($state == 3 and s/^Author:\s+//) {
 		s/\s+$//;
 		if (/^(.*?)\s+<(.*)>/) {
 		    ($author_name, $author_email) = ($1, $2);
@@ -791,34 +791,34 @@ while(<CVS>) {
 		    $author_name = $author_email = $_;
 		}
 		$state = 4;
-	} elsif($state == 4 and s/^Branch:\s+//) {
+	} elsif ($state == 4 and s/^Branch:\s+//) {
 		s/\s+$//;
 		s/[\/]/$opt_s/g;
 		$branch = $_;
 		$state = 5;
-	} elsif($state == 5 and s/^Ancestor branch:\s+//) {
+	} elsif ($state == 5 and s/^Ancestor branch:\s+//) {
 		s/\s+$//;
 		$ancestor = $_;
 		$ancestor = $opt_o if $ancestor eq "HEAD";
 		$state = 6;
-	} elsif($state == 5) {
+	} elsif ($state == 5) {
 		$ancestor = undef;
 		$state = 6;
 		redo;
-	} elsif($state == 6 and s/^Tag:\s+//) {
+	} elsif ($state == 6 and s/^Tag:\s+//) {
 		s/\s+$//;
-		if($_ eq "(none)") {
+		if ($_ eq "(none)") {
 			$tag = undef;
 		} else {
 			$tag = $_;
 		}
 		$state = 7;
-	} elsif($state == 7 and /^Log:/) {
+	} elsif ($state == 7 and /^Log:/) {
 		$logmsg = "";
 		$state = 8;
-	} elsif($state == 8 and /^Members:/) {
+	} elsif ($state == 8 and /^Members:/) {
 		$branch = $opt_o if $branch eq "HEAD";
-		if(defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
+		if (defined $branch_date{$branch} and $branch_date{$branch} >= $date) {
 			# skip
 			print "skip patchset $patchset: $date before $branch_date{$branch}\n" if $opt_v;
 			$state = 11;
@@ -829,17 +829,17 @@ while(<CVS>) {
 			$state = 11;
 			next;
 		}
-		if($ancestor) {
-			if($ancestor eq $branch) {
+		if ($ancestor) {
+			if ($ancestor eq $branch) {
 				print STDERR "Branch $branch erroneously stems from itself -- changed ancestor to $opt_o\n";
 				$ancestor = $opt_o;
 			}
-			if(-f "$git_dir/refs/heads/$branch") {
+			if (-f "$git_dir/refs/heads/$branch") {
 				print STDERR "Branch $branch already exists!\n";
 				$state=11;
 				next;
 			}
-			unless(open(H,"$git_dir/refs/heads/$ancestor")) {
+			unless (open(H,"$git_dir/refs/heads/$ancestor")) {
 				print STDERR "Branch $ancestor does not exist!\n";
 				$ignorebranch{$branch} = 1;
 				$state=11;
@@ -847,7 +847,7 @@ while(<CVS>) {
 			}
 			chomp(my $id = <H>);
 			close(H);
-			unless(open(H,"> $git_dir/refs/heads/$branch")) {
+			unless (open(H,"> $git_dir/refs/heads/$branch")) {
 				print STDERR "Could not create branch $branch: $!\n";
 				$ignorebranch{$branch} = 1;
 				$state=11;
@@ -860,9 +860,9 @@ while(<CVS>) {
 		}
 		$last_branch = $branch if $branch ne $last_branch;
 		$state = 9;
-	} elsif($state == 8) {
+	} elsif ($state == 8) {
 		$logmsg .= "$_\n";
-	} elsif($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
+	} elsif ($state == 9 and /^\s+(.+?):(INITIAL|\d+(?:\.\d+)+)->(\d+(?:\.\d+)+)\s*$/) {
 #	VERSION:1.96->1.96.2.1
 		my $init = ($2 eq "INITIAL");
 		my $fn = $1;
@@ -875,7 +875,7 @@ while(<CVS>) {
 		}
 		print "Fetching $fn   v $rev\n" if $opt_v;
 		my ($tmpname, $size) = $cvs->file($fn,$rev);
-		if($size == -1) {
+		if ($size == -1) {
 			push(@old,$fn);
 			print "Drop $fn\n" if $opt_v;
 		} else {
@@ -893,14 +893,14 @@ while(<CVS>) {
 			push(@new,[$mode, $sha, $fn]); # may be resurrected!
 		}
 		unlink($tmpname);
-	} elsif($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
+	} elsif ($state == 9 and /^\s+(.+?):\d+(?:\.\d+)+->(\d+(?:\.\d+)+)\(DEAD\)\s*$/) {
 		my $fn = $1;
 		$fn =~ s#^/+##;
 		push(@old,$fn);
 		print "Delete $fn\n" if $opt_v;
-	} elsif($state == 9 and /^\s*$/) {
+	} elsif ($state == 9 and /^\s*$/) {
 		$state = 10;
-	} elsif(($state == 9 or $state == 10) and /^-+$/) {
+	} elsif (($state == 9 or $state == 10) and /^-+$/) {
 		$commitcount++;
 		if ($opt_L && $commitcount > $opt_L) {
 			last;
@@ -910,11 +910,11 @@ while(<CVS>) {
 			system("git repack -a -d");
 		}
 		$state = 1;
-	} elsif($state == 11 and /^-+$/) {
+	} elsif ($state == 11 and /^-+$/) {
 		$state = 1;
-	} elsif(/^-+$/) { # end of unknown-line processing
+	} elsif (/^-+$/) { # end of unknown-line processing
 		$state = 1;
-	} elsif($state != 11) { # ignore stuff when skipping
+	} elsif ($state != 11) { # ignore stuff when skipping
 		print "* UNKNOWN LINE * $_\n";
 	}
 }
@@ -943,7 +943,7 @@ if (defined $orig_git_index) {
 }
 
 # Now switch back to the branch we were in before all of this happened
-if($orig_branch) {
+if ($orig_branch) {
 	print "DONE.\n" if $opt_v;
 	if ($opt_i) {
 		exit 0;

From 1d541c120b4de5c70e73f8a20e1d961324cc55fe Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 28 Nov 2006 00:29:21 +0100
Subject: [PATCH 056/330] shortlog: use pager

On request of the kingpenguin, shortlog now uses the pager if output
goes to a tty.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git.c b/git.c
index f97de602d2..357330e02c 100644
--- a/git.c
+++ b/git.c
@@ -260,7 +260,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "rm", cmd_rm, RUN_SETUP },
 		{ "runstatus", cmd_runstatus, RUN_SETUP },
-		{ "shortlog", cmd_shortlog, RUN_SETUP },
+		{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
 		{ "show-branch", cmd_show_branch, RUN_SETUP },
 		{ "show", cmd_show, RUN_SETUP | USE_PAGER },
 		{ "stripspace", cmd_stripspace },

From f0df4ed5629c25e9978708115ad654b9f83f55df Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 28 Nov 2006 00:18:55 +0100
Subject: [PATCH 057/330] sha1_object_info(): be consistent with
 read_sha1_file()

We used to try loose objects first with sha1_object_info(), but packed
objects first with read_sha1_file(). Now, prefer packed objects over loose
ones with sha1_object_info(), too.

Usually the old behaviour would pose no problem, but when you tried to fix
a fscked up repository by inserting a known-good pack,

	git cat-file $(git cat-file -t <sha1>) <sha1>

could fail, even when

	git cat-file blob <sha1>

would _not_ fail. Worse, a repack would fail, too.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 sha1_file.c | 35 ++++++++++++++++++++---------------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 09456d23f8..63f416bb5a 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1261,7 +1261,7 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1,
 	
 }
 
-int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
+static int sha1_loose_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
 {
 	int status;
 	unsigned long mapsize, size;
@@ -1270,20 +1270,8 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
 	char hdr[128];
 
 	map = map_sha1_file(sha1, &mapsize);
-	if (!map) {
-		struct pack_entry e;
-
-		if (!find_pack_entry(sha1, &e, NULL)) {
-			reprepare_packed_git();
-			if (!find_pack_entry(sha1, &e, NULL))
-				return error("unable to find %s", sha1_to_hex(sha1));
-		}
-		if (use_packed_git(e.p))
-			die("cannot map packed file");
-		status = packed_object_info(e.p, e.offset, type, sizep);
-		unuse_packed_git(e.p);
-		return status;
-	}
+	if (!map)
+		return error("unable to find %s", sha1_to_hex(sha1));
 	if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
 		status = error("unable to unpack %s header",
 			       sha1_to_hex(sha1));
@@ -1299,6 +1287,23 @@ int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep
 	return status;
 }
 
+int sha1_object_info(const unsigned char *sha1, char *type, unsigned long *sizep)
+{
+	int status;
+	struct pack_entry e;
+
+	if (!find_pack_entry(sha1, &e, NULL)) {
+		reprepare_packed_git();
+		if (!find_pack_entry(sha1, &e, NULL))
+			return sha1_loose_object_info(sha1, type, sizep);
+	}
+	if (use_packed_git(e.p))
+		die("cannot map packed file");
+	status = packed_object_info(e.p, e.offset, type, sizep);
+	unuse_packed_git(e.p);
+	return status;
+}
+
 static void *read_packed_sha1(const unsigned char *sha1, char *type, unsigned long *size)
 {
 	struct pack_entry e;

From 255cae88bcc8fcb0088459df13b55fa3d311b9e2 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Sun, 26 Nov 2006 12:10:52 +0000
Subject: [PATCH 058/330] Use .git/config for storing "origin" shortcut
 repository

Rather than use a separate config .git/remotes/ for remote shortcuts, this
patch adds the analagous definitions to .git/config using git-repo-config
calls.

For example what was previously .git/remotes/origin
  URL: proto://host/path
  Pull: refs/heads/master:refs/heads/origin
Is now added to .git/config as
  [remote "origin"]
  url = proto://host/path
  fetch = refs/heads/master:refs/heads/origin

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-clone.sh | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/git-clone.sh b/git-clone.sh
index d4ee93f75b..b2d0f08e67 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -377,9 +377,9 @@ then
 		*)	origin_track="$remote_top/$origin"
 			git-update-ref "refs/heads/$origin" "$head_sha1" ;;
 		esac &&
-		echo >"$GIT_DIR/remotes/$origin" \
-		"URL: $repo
-Pull: refs/heads/$head_points_at:$origin_track" &&
+		git-repo-config remote."$origin".url "$repo" &&
+		git-repo-config remote."$origin".fetch \
+			"refs/heads/$head_points_at:$origin_track" &&
 		(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
 		while read dotslref
 		do
@@ -393,8 +393,8 @@ Pull: refs/heads/$head_points_at:$origin_track" &&
 			then
 				continue
 			fi
-			echo "Pull: refs/heads/${name}:$remote_top/${name}"
-		done >>"$GIT_DIR/remotes/$origin" &&
+			git-repo-config remote."$origin".fetch "refs/heads/${name}:$remote_top/${name}" '^$'
+		done &&
 		case "$use_separate_remote" in
 		t)
 			rm -f "refs/remotes/$origin/HEAD"

From 27a1a8014b842c0d70fdc91c68dd361ca2dfb34c Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Mon, 27 Nov 2006 21:44:48 -0800
Subject: [PATCH 059/330] git-svn: enable delta transfers during fetches when
 using SVN:: libs

This should drastically reduce bandwidth used for network
transfers.  This is not enabled for file:// repositories by
default because of the increased CPU usage and I/O needed.

GIT_SVN_DELTA_FETCH may be set to a true value to enable or
false (0) to disable delta transfers regardless of the
repository type.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 189 insertions(+), 5 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index d5d9c49fd6..9b86d91266 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -68,7 +68,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
 	$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
 	$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
 	$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
-	$_username, $_config_dir, $_no_auth_cache);
+	$_username, $_config_dir, $_no_auth_cache, $_xfer_delta);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_co_url_revs, $_svn_pg_peg_revs);
 my @repo_path_split_cache;
@@ -2675,6 +2675,9 @@ sub libsvn_load {
 		require SVN::Ra;
 		require SVN::Delta;
 		push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
+		push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
+		*SVN::Git::Fetcher::process_rm = *process_rm;
+		*SVN::Git::Fetcher::safe_qx = *safe_qx;
 		my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
 					$SVN::Node::dir.$SVN::Node::unknown.
 					$SVN::Node::none.$SVN::Node::file.
@@ -2827,6 +2830,13 @@ sub libsvn_connect {
 	                      config => $config,
 	                      pool => SVN::Pool->new,
 	                      auth_provider_callbacks => $callbacks);
+
+	my $df = $ENV{GIT_SVN_DELTA_FETCH};
+	if (defined $df) {
+		$_xfer_delta = $df;
+	} else {
+		$_xfer_delta = ($url =~ m#^file://#) ? undef : 1;
+	}
 	$ra->{svn_path} = $url;
 	$ra->{repos_root} = $ra->get_repos_root;
 	$ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
@@ -2915,6 +2925,24 @@ sub process_rm {
 }
 
 sub libsvn_fetch {
+	$_xfer_delta ? libsvn_fetch_delta(@_) : libsvn_fetch_full(@_);
+}
+
+sub libsvn_fetch_delta {
+	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
+	my $pool = SVN::Pool->new;
+	my $ed = SVN::Git::Fetcher->new({ c => $last_commit, ra => $SVN,
+	                                  paths => $paths });
+	my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
+	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+	my (undef, $last_rev, undef) = cmt_metadata($last_commit);
+	$reporter->set_path('', $last_rev, 0, @lock, $pool);
+	$reporter->finish_report($pool);
+	$pool->clear;
+	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
+}
+
+sub libsvn_fetch_full {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
 	my @amr;
@@ -3133,7 +3161,11 @@ sub libsvn_find_parent_branch {
 		unlink $GIT_SVN_INDEX;
 		print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
 		sys(qw/git-read-tree/, $parent);
-		return libsvn_fetch($parent, $paths, $rev,
+		# I can't seem to get do_switch() to work correctly with
+		# the SWIG interface (TypeError when passing switch_url...),
+		# so we'll unconditionally bypass the delta interface here
+		# for now
+		return libsvn_fetch_full($parent, $paths, $rev,
 					$author, $date, $msg);
 	}
 	print STDERR "Nope, branch point not imported or unknown\n";
@@ -3153,9 +3185,19 @@ sub libsvn_new_tree {
 		return $log_entry;
 	}
 	my ($paths, $rev, $author, $date, $msg) = @_;
-	open my $gui, '| git-update-index -z --index-info' or croak $!;
-	libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
-	close $gui or croak $?;
+	if ($_xfer_delta) {
+		my $pool = SVN::Pool->new;
+		my $ed = SVN::Git::Fetcher->new({paths => $paths, ra => $SVN});
+		my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
+		my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+		$reporter->set_path('', $rev, 1, @lock, $pool);
+		$reporter->finish_report($pool);
+		$pool->clear;
+	} else {
+		open my $gui, '| git-update-index -z --index-info' or croak $!;
+		libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
+		close $gui or croak $?;
+	}
 	return libsvn_log_entry($rev, $author, $date, $msg);
 }
 
@@ -3325,6 +3367,148 @@ sub copy_remote_ref {
 				"refs/remotes/$GIT_SVN on $origin\n";
 	}
 }
+package SVN::Git::Fetcher;
+use vars qw/@ISA/;
+use strict;
+use warnings;
+use Carp qw/croak/;
+use IO::File qw//;
+
+# file baton members: path, mode_a, mode_b, pool, fh, blob, base
+sub new {
+	my ($class, $git_svn) = @_;
+	my $self = SVN::Delta::Editor->new;
+	bless $self, $class;
+	open my $gui, '| git-update-index -z --index-info' or croak $!;
+	$self->{gui} = $gui;
+	$self->{c} = $git_svn->{c} if exists $git_svn->{c};
+	if (my $p = $git_svn->{paths} && $git_svn->{ra}) {
+		my $s = $git_svn->{ra}->{svn_path};
+		$s = length $s ? qr#^/\Q$s\E/# : qr#^/#;
+		$self->{paths} = { map { my $x = $_;
+		                         $x =~ s/$s//;
+		                         $x => $p->{$_} } keys %$p };
+	}
+	require Digest::MD5;
+	$self;
+}
+
+sub delete_entry {
+	my ($self, $path, $rev, $pb) = @_;
+	process_rm($self->{gui}, $self->{c}, $path);
+	undef;
+}
+
+sub open_file {
+	my ($self, $path, $pb, $rev) = @_;
+	my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
+	                     =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
+	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
+	  pool => SVN::Pool->new };
+}
+
+sub add_file {
+	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
+	{ path => $path, mode_a => 100644, mode_b => 100644,
+	  pool => SVN::Pool->new };
+}
+
+sub change_file_prop {
+	my ($self, $fb, $prop, $value) = @_;
+	if ($prop eq 'svn:executable') {
+		if ($fb->{mode_b} != 120000) {
+			$fb->{mode_b} = defined $value ? 100755 : 100644;
+		}
+	} elsif ($prop eq 'svn:special') {
+		$fb->{mode_b} = defined $value ? 120000 : 100644;
+	}
+	undef;
+}
+
+sub apply_textdelta {
+	my ($self, $fb, $exp) = @_;
+	my $fh = IO::File->new_tmpfile;
+	$fh->autoflush(1);
+	# $fh gets auto-closed() by SVN::TxDelta::apply(),
+	# (but $base does not,) so dup() it for reading in close_file
+	open my $dup, '<&', $fh or croak $!;
+	my $base = IO::File->new_tmpfile;
+	$base->autoflush(1);
+	if ($fb->{blob}) {
+		defined (my $pid = fork) or croak $!;
+		if (!$pid) {
+			open STDOUT, '>&', $base or croak $!;
+			print STDOUT 'link ' if ($fb->{mode_a} == 120000);
+			exec qw/git-cat-file blob/, $fb->{blob} or croak $!;
+		}
+		waitpid $pid, 0;
+		croak $? if $?;
+
+		if (defined $exp) {
+			seek $base, 0, 0 or croak $!;
+			my $md5 = Digest::MD5->new;
+			$md5->addfile($base);
+			my $got = $md5->hexdigest;
+			die "Checksum mismatch: $fb->{path} $fb->{blob}\n",
+			    "expected: $exp\n",
+			    "     got: $got\n" if ($got ne $exp);
+		}
+	}
+	seek $base, 0, 0 or croak $!;
+	$fb->{fh} = $dup;
+	$fb->{base} = $base;
+	[ SVN::TxDelta::apply($base, $fh, undef, $fb->{path}, $fb->{pool}) ];
+}
+
+sub close_file {
+	my ($self, $fb, $exp) = @_;
+	my $hash;
+	my $path = $fb->{path};
+	if (my $fh = $fb->{fh}) {
+		seek($fh, 0, 0) or croak $!;
+		my $md5 = Digest::MD5->new;
+		$md5->addfile($fh);
+		my $got = $md5->hexdigest;
+		die "Checksum mismatch: $path\n",
+		    "expected: $exp\n    got: $got\n" if ($got ne $exp);
+		seek($fh, 0, 0) or croak $!;
+		if ($fb->{mode_b} == 120000) {
+			read($fh, my $buf, 5) == 5 or croak $!;
+			$buf eq 'link ' or die "$path has mode 120000",
+			                       "but is not a link\n";
+		}
+		defined(my $pid = open my $out,'-|') or die "Can't fork: $!\n";
+		if (!$pid) {
+			open STDIN, '<&', $fh or croak $!;
+			exec qw/git-hash-object -w --stdin/ or croak $!;
+		}
+		chomp($hash = do { local $/; <$out> });
+		close $out or croak $!;
+		close $fh or croak $!;
+		$hash =~ /^[a-f\d]{40}$/ or die "not a sha1: $hash\n";
+		close $fb->{base} or croak $!;
+	} else {
+		$hash = $fb->{blob} or die "no blob information\n";
+	}
+	$fb->{pool}->clear;
+	my $gui = $self->{gui};
+	print $gui "$fb->{mode_b} $hash\t$path\0" or croak $!;
+	print "\t", $self->{paths}->{$path}->action,
+	      "\t$path\n" if defined $self->{paths}->{$path};
+	undef;
+}
+
+sub abort_edit {
+	my $self = shift;
+	close $self->{gui};
+	$self->SUPER::abort_edit(@_);
+}
+
+sub close_edit {
+	my $self = shift;
+	close $self->{gui} or croak;
+	$self->SUPER::close_edit(@_);
+}
 
 package SVN::Git::Editor;
 use vars qw/@ISA/;

From e15161198a381334b8c8ccee707392163f3a1ab7 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Mon, 27 Nov 2006 21:46:50 -0800
Subject: [PATCH 060/330] git-svn: update tests for recent changes

* Enable test for delta transfers in full-svn-test.

* Run tests against the root of the repository so we won't have
  to revisit 308906fa6e98132cab839a4f42701386fba368ef and
  efe4631def181d32f932672a7ea31e52ee0ab308 again.
  The graft-branches test still runs as before.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/Makefile                        | 3 ++-
 t/lib-git-svn.sh                  | 2 +-
 t/t9100-git-svn-basic.sh          | 5 +++++
 t/t9103-git-svn-graft-branches.sh | 2 ++
 4 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/t/Makefile b/t/Makefile
index e1c6c92d9c..c9bd9a4690 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -23,8 +23,9 @@ clean:
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
+	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_DELTA_FETCH=1 \
+					GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
 	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
 	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
 							LC_ALL=en_US.UTF-8
 	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 29a1e72c61..63c670304f 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -45,6 +45,6 @@ else
 	svnadmin create "$svnrepo"
 fi
 
-svnrepo="file://$svnrepo/test-git-svn"
+svnrepo="file://$svnrepo"
 
 
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 34a3ccd31c..f9de232366 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -228,6 +228,11 @@ tree 56a30b966619b863674f5978696f4a3594f2fca9
 tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
 tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
 EOF
+
+if test -z "$GIT_SVN_NO_LIB" || test "$GIT_SVN_NO_LIB" -eq 0; then
+	echo tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 >> expected
+fi
+
 test_expect_success "$name" "diff -u a expected"
 
 test_done
diff --git a/t/t9103-git-svn-graft-branches.sh b/t/t9103-git-svn-graft-branches.sh
index cc62d4ece8..293b98f928 100755
--- a/t/t9103-git-svn-graft-branches.sh
+++ b/t/t9103-git-svn-graft-branches.sh
@@ -1,6 +1,8 @@
 test_description='git-svn graft-branches'
 . ./lib-git-svn.sh
 
+svnrepo="$svnrepo/test-git-svn"
+
 test_expect_success 'initialize repo' "
 	mkdir import &&
 	cd import &&

From c3e43938839752b48ff3a36862ae59f1cd1e630d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ren=C3=A9=20Scharfe?= <rene.scharfe@lsrfire.ath.cx>
Date: Tue, 28 Nov 2006 22:49:17 +0100
Subject: [PATCH 061/330] shortlog: remove range check

Don't force the user to specify more than one revision parameter,
thus making git-shortlog behave more like git-log.
'git-shortlog master' will now produce the expected results; the
other end of the range simply is the (oldest) root commit.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index b5b13dee3b..f1124e261b 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -298,9 +298,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	if (!access(".mailmap", R_OK))
 		read_mailmap(".mailmap");
 
-	if (rev.pending.nr == 1)
-		die ("Need a range!");
-	else if (rev.pending.nr == 0)
+	if (rev.pending.nr == 0)
 		read_from_stdin(&list);
 	else
 		get_from_rev(&rev, &list);

From dad73c0bb9f33323ec1aacf560a6263f1d85f81a Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 28 Nov 2006 14:06:05 -0800
Subject: [PATCH 062/330] git-svn: error out when the SVN connection fails
 during a fetch

finish_report does seem to return a useful value indicating success
or failure, so we'll just set a flag when close_edit is called
(it is not called on failures, nor is abort_edit) and check
the flag before proceeding.

Thanks to Pazu for pointing this out.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index 9b86d91266..cefa7b021d 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2939,6 +2939,9 @@ sub libsvn_fetch_delta {
 	$reporter->set_path('', $last_rev, 0, @lock, $pool);
 	$reporter->finish_report($pool);
 	$pool->clear;
+	unless ($ed->{git_commit_ok}) {
+		die "SVN connection failed somewhere...\n";
+	}
 	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
 }
 
@@ -3193,6 +3196,9 @@ sub libsvn_new_tree {
 		$reporter->set_path('', $rev, 1, @lock, $pool);
 		$reporter->finish_report($pool);
 		$pool->clear;
+		unless ($ed->{git_commit_ok}) {
+			die "SVN connection failed somewhere...\n";
+		}
 	} else {
 		open my $gui, '| git-update-index -z --index-info' or croak $!;
 		libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
@@ -3506,7 +3512,8 @@ sub abort_edit {
 
 sub close_edit {
 	my $self = shift;
-	close $self->{gui} or croak;
+	close $self->{gui} or croak $!;
+	$self->{git_commit_ok} = 1;
 	$self->SUPER::close_edit(@_);
 }
 

From 0864e3ba12ed946f0082297530584a77e2a7097b Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 28 Nov 2006 02:50:17 -0800
Subject: [PATCH 063/330] git-svn: fix output reporting from the delta fetcher

There was nothing printed in the code originally because I left
out a pair of parentheses.  Nevertheless, the affected code has
been replaced with a more efficient version that respects the -q
flag as well as requiring less bandwidth.

We save some bandwidth by not requesting changed paths
information when calling get_log() since we're using the delta
fetcher.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 35 +++++++++++++++--------------------
 1 file changed, 15 insertions(+), 20 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index cefa7b021d..c3ad5ec603 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -1152,7 +1152,7 @@ sub graft_file_copy_lib {
 	while (1) {
 		my $pool = SVN::Pool->new;
 		libsvn_get_log(libsvn_dup_ra($SVN), [$path],
-		               $min, $max, 0, 1, 1,
+		               $min, $max, 0, 2, 1,
 			sub {
 				libsvn_graft_file_copies($grafts, $tree_paths,
 							$path, @_);
@@ -2906,7 +2906,7 @@ sub libsvn_log_entry {
 }
 
 sub process_rm {
-	my ($gui, $last_commit, $f) = @_;
+	my ($gui, $last_commit, $f, $q) = @_;
 	# remove entire directories.
 	if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
 		defined(my $pid = open my $ls, '-|') or croak $!;
@@ -2917,10 +2917,13 @@ sub process_rm {
 		local $/ = "\0";
 		while (<$ls>) {
 			print $gui '0 ',0 x 40,"\t",$_ or croak $!;
+			print "\tD\t$_\n" unless $q;
 		}
+		print "\tD\t$f/\n" unless $q;
 		close $ls or croak $?;
 	} else {
 		print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
+		print "\tD\t$f\n" unless $q;
 	}
 }
 
@@ -2931,8 +2934,7 @@ sub libsvn_fetch {
 sub libsvn_fetch_delta {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
 	my $pool = SVN::Pool->new;
-	my $ed = SVN::Git::Fetcher->new({ c => $last_commit, ra => $SVN,
-	                                  paths => $paths });
+	my $ed = SVN::Git::Fetcher->new({ c => $last_commit, q => $_q });
 	my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
 	my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
 	my (undef, $last_rev, undef) = cmt_metadata($last_commit);
@@ -2959,8 +2961,7 @@ sub libsvn_fetch_full {
 			$f =~ s#^/##;
 		}
 		if ($m =~ /^[DR]$/) {
-			print "\t$m\t$f\n" unless $_q;
-			process_rm($gui, $last_commit, $f);
+			process_rm($gui, $last_commit, $f, $_q);
 			next if $m eq 'D';
 			# 'R' can be file replacements, too, right?
 		}
@@ -3101,7 +3102,7 @@ sub revisions_eq {
 		# should be OK to use Pool here (r1 - r0) should be small
 		my $pool = SVN::Pool->new;
 		libsvn_get_log($SVN, [$path], $r0, $r1,
-				0, 1, 1, sub {$nr++}, $pool);
+				0, 0, 1, sub {$nr++}, $pool);
 		$pool->clear;
 	} else {
 		my ($url, undef) = repo_path_split($SVN_URL);
@@ -3177,6 +3178,7 @@ sub libsvn_find_parent_branch {
 
 sub libsvn_get_log {
 	my ($ra, @args) = @_;
+	$args[4]-- if $args[4] && $_xfer_delta && ! $_follow_parent;
 	if ($SVN::Core::VERSION le '1.2.0') {
 		splice(@args, 3, 1);
 	}
@@ -3190,7 +3192,7 @@ sub libsvn_new_tree {
 	my ($paths, $rev, $author, $date, $msg) = @_;
 	if ($_xfer_delta) {
 		my $pool = SVN::Pool->new;
-		my $ed = SVN::Git::Fetcher->new({paths => $paths, ra => $SVN});
+		my $ed = SVN::Git::Fetcher->new({q => $_q});
 		my $reporter = $SVN->do_update($rev, '', 1, $ed, $pool);
 		my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
 		$reporter->set_path('', $rev, 1, @lock, $pool);
@@ -3388,20 +3390,14 @@ sub new {
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
 	$self->{gui} = $gui;
 	$self->{c} = $git_svn->{c} if exists $git_svn->{c};
-	if (my $p = $git_svn->{paths} && $git_svn->{ra}) {
-		my $s = $git_svn->{ra}->{svn_path};
-		$s = length $s ? qr#^/\Q$s\E/# : qr#^/#;
-		$self->{paths} = { map { my $x = $_;
-		                         $x =~ s/$s//;
-		                         $x => $p->{$_} } keys %$p };
-	}
+	$self->{q} = $git_svn->{q};
 	require Digest::MD5;
 	$self;
 }
 
 sub delete_entry {
 	my ($self, $path, $rev, $pb) = @_;
-	process_rm($self->{gui}, $self->{c}, $path);
+	process_rm($self->{gui}, $self->{c}, $path, $self->{q});
 	undef;
 }
 
@@ -3410,13 +3406,13 @@ sub open_file {
 	my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
 	                     =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
 	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
-	  pool => SVN::Pool->new };
+	  pool => SVN::Pool->new, action => 'M' };
 }
 
 sub add_file {
 	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
 	{ path => $path, mode_a => 100644, mode_b => 100644,
-	  pool => SVN::Pool->new };
+	  pool => SVN::Pool->new, action => 'A' };
 }
 
 sub change_file_prop {
@@ -3499,8 +3495,7 @@ sub close_file {
 	$fb->{pool}->clear;
 	my $gui = $self->{gui};
 	print $gui "$fb->{mode_b} $hash\t$path\0" or croak $!;
-	print "\t", $self->{paths}->{$path}->action,
-	      "\t$path\n" if defined $self->{paths}->{$path};
+	print "\t$fb->{action}\t$path\n" if $fb->{action} && ! $self->{q};
 	undef;
 }
 

From 4548e855e4600a0f76329cf4f0dd9e8d17d66b08 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Tue, 28 Nov 2006 12:12:08 -0500
Subject: [PATCH 064/330] Teach bash how to complete long options for
 git-commit.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d8ae4d7886..be978cf3d3 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -398,6 +398,20 @@ _git_cherry_pick ()
 	esac
 }
 
+_git_commit ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		COMPREPLY=($(compgen -W "
+			--all --author= --signoff --verify --no-verify
+			--edit --amend --include --only
+			" -- "$cur"))
+		return
+	esac
+	COMPREPLY=()
+}
+
 _git_diff ()
 {
 	__git_complete_file
@@ -768,6 +782,7 @@ _git ()
 	cat-file)    _git_cat_file ;;
 	checkout)    _git_checkout ;;
 	cherry-pick) _git_cherry_pick ;;
+	commit)      _git_commit ;;
 	diff)        _git_diff ;;
 	diff-tree)   _git_diff_tree ;;
 	fetch)       _git_fetch ;;
@@ -804,6 +819,7 @@ complete -o default            -F _git_branch git-branch
 complete -o default -o nospace -F _git_cat_file git-cat-file
 complete -o default            -F _git_checkout git-checkout
 complete -o default            -F _git_cherry_pick git-cherry-pick
+complete -o default            -F _git_commit git-commit
 complete -o default -o nospace -F _git_diff git-diff
 complete -o default            -F _git_diff_tree git-diff-tree
 complete -o default -o nospace -F _git_fetch git-fetch

From 67ffa1142585742601011440a17528ef841bbf44 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Tue, 28 Nov 2006 12:12:26 -0500
Subject: [PATCH 065/330] Fix broken bash completion of local refs.

Commit 35e65ecc broke completion of local refs, e.g. "git pull . fo<tab>"
no longer would complete to "foo".  Instead it printed out an internal
git error ("fatal: Not a git repository: '.'").

The break occurred when I tried to improve performance by switching from
git-peek-remote to git-for-each-ref.  Apparently git-peek-remote will
drop into directory "$1/.git" (where $1 is its first parameter) if it
is given a repository with a working directory.  This allowed the bash
completion code to work properly even though it was not handing over
the true repository directory.

So now we do a stat in bash to see if we need to add "/.git" to the
path string before running any command with --git-dir.

I also tried to optimize away two "git rev-parse --git-dir" invocations
in common cases like "git log fo<tab>" as typically the user is in the
top level directory of their project and therefore the .git subdirectory
is in the current working directory.  This should make a difference on
systems where fork+exec might take a little while.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 37 +++++++++++++-------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index be978cf3d3..447ec20467 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -34,7 +34,19 @@
 
 __gitdir ()
 {
-	echo "${__git_dir:-$(git rev-parse --git-dir 2>/dev/null)}"
+	if [ -z "$1" ]; then
+		if [ -n "$__git_dir" ]; then
+			echo "$__git_dir"
+		elif [ -d .git ]; then
+			echo .git
+		else
+			git rev-parse --git-dir 2>/dev/null
+		fi
+	elif [ -d "$1/.git" ]; then
+		echo "$1/.git"
+	else
+		echo "$1"
+	fi
 }
 
 __git_ps1 ()
@@ -51,7 +63,7 @@ __git_ps1 ()
 
 __git_heads ()
 {
-	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+	local cmd i is_hash=y dir="$(__gitdir "$1")"
 	if [ -d "$dir" ]; then
 		for i in $(git --git-dir="$dir" \
 			for-each-ref --format='%(refname)' \
@@ -60,7 +72,7 @@ __git_heads ()
 		done
 		return
 	fi
-	for i in $(git-ls-remote "$dir" 2>/dev/null); do
+	for i in $(git-ls-remote "$1" 2>/dev/null); do
 		case "$is_hash,$i" in
 		y,*) is_hash=n ;;
 		n,*^{}) is_hash=y ;;
@@ -72,7 +84,7 @@ __git_heads ()
 
 __git_refs ()
 {
-	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
+	local cmd i is_hash=y dir="$(__gitdir "$1")"
 	if [ -d "$dir" ]; then
 		if [ -e "$dir/HEAD" ]; then echo HEAD; fi
 		for i in $(git --git-dir="$dir" \
@@ -101,20 +113,9 @@ __git_refs ()
 
 __git_refs2 ()
 {
-	local cmd i is_hash=y dir="${1:-$(__gitdir)}"
-	if [ -d "$dir" ]; then
-		cmd=git-peek-remote
-	else
-		cmd=git-ls-remote
-	fi
-	for i in $($cmd "$dir" 2>/dev/null); do
-		case "$is_hash,$i" in
-		y,*) is_hash=n ;;
-		n,*^{}) is_hash=y ;;
-		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}:${i#refs/tags/}" ;;
-		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}:${i#refs/heads/}" ;;
-		n,*) is_hash=y; echo "$i:$i" ;;
-		esac
+	local i
+	for i in $(__git_refs "$1"); do
+		echo "$i:$i"
 	done
 }
 

From d5cc2de9ff57d2f6734f6e6e34addc2fae1b5b0e Mon Sep 17 00:00:00 2001
From: Han-Wen Nienhuys <hanwen@xs4all.nl>
Date: Tue, 28 Nov 2006 11:27:39 +0100
Subject: [PATCH 066/330] ident.c: Trim hint printed when gecos is empty.

Also remove asterisks for readability, and suggest use of
git-config for easy cut & pasting.

Signed-off-by: Han-Wen Nienhuys <hanwen@xs4all.nl>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 ident.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/ident.c b/ident.c
index efec97ffd0..e415fd3588 100644
--- a/ident.c
+++ b/ident.c
@@ -158,12 +158,17 @@ static int copy(char *buf, int size, int offset, const char *src)
 static const char au_env[] = "GIT_AUTHOR_NAME";
 static const char co_env[] = "GIT_COMMITTER_NAME";
 static const char *env_hint =
-"\n*** Environment problem:\n"
+"\n"
 "*** Your name cannot be determined from your system services (gecos).\n"
-"*** You would need to set %s and %s\n"
-"*** environment variables; otherwise you won't be able to perform\n"
-"*** certain operations because of \"empty ident\" errors.\n"
-"*** Alternatively, you can use user.name configuration variable.\n\n";
+"\n"
+"Run\n"
+"\n"
+"  git repo-config user.email \"you@email.com\"\n"
+"  git repo-config user.name \"Your Name\"\n"
+"\n"
+"To set the identity in this repository.\n"
+"Add --global to set your account\'s default\n"
+"\n";
 
 static const char *get_ident(const char *name, const char *email,
 			     const char *date_str, int error_on_no_name)

From 9aca025849d417b04b49fead126c915f6ca47526 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 28 Nov 2006 18:51:40 -0800
Subject: [PATCH 067/330] git-svn: color support for the log command

 * match LESS environment settings to those in pager.c
 * parse diff.color and pager.color settings in the
   config file, and pass --color to git-log
 * --color and --pager= settings are supported

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 67 ++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 47 insertions(+), 20 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index c3ad5ec603..d8d8716d38 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -60,6 +60,7 @@ nag_lib() unless $_use_lib;
 my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
 my $sha1 = qr/[a-f\d]{40}/;
 my $sha1_short = qr/[a-f\d]{4,40}/;
+my $_esc_color = qr/(?:\033\[(?:(?:\d+;)*\d*)?m)*/;
 my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
 	$_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
 	$_repack, $_repack_nr, $_repack_flags, $_q,
@@ -68,7 +69,8 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
 	$_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
 	$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
 	$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
-	$_username, $_config_dir, $_no_auth_cache, $_xfer_delta);
+	$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
+	$_pager, $_color);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
 my ($_svn_co_url_revs, $_svn_pg_peg_revs);
 my @repo_path_split_cache;
@@ -135,6 +137,8 @@ my %cmd = (
 			  'show-commit' => \$_show_commit,
 			  'non-recursive' => \$_non_recursive,
 			  'authors-file|A=s' => \$_authors,
+			  'color' => \$_color,
+			  'pager=s' => \$_pager,
 			} ],
 	'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
 			{ 'message|m=s' => \$_message,
@@ -759,16 +763,17 @@ sub show_log {
 		}
 	}
 
+	config_pager();
 	my $pid = open(my $log,'-|');
 	defined $pid or croak $!;
 	if (!$pid) {
 		exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
 	}
-	setup_pager();
+	run_pager();
 	my (@k, $c, $d);
 
 	while (<$log>) {
-		if (/^commit ($sha1_short)/o) {
+		if (/^${_esc_color}commit ($sha1_short)/o) {
 			my $cmt = $1;
 			if ($c && cmt_showable($c) && $c->{r} != $r_last) {
 				$r_last = $c->{r};
@@ -777,25 +782,25 @@ sub show_log {
 			}
 			$d = undef;
 			$c = { c => $cmt };
-		} elsif (/^author (.+) (\d+) ([\-\+]?\d+)$/) {
+		} elsif (/^${_esc_color}author (.+) (\d+) ([\-\+]?\d+)$/) {
 			get_author_info($c, $1, $2, $3);
-		} elsif (/^(?:tree|parent|committer) /) {
+		} elsif (/^${_esc_color}(?:tree|parent|committer) /) {
 			# ignore
-		} elsif (/^:\d{6} \d{6} $sha1_short/o) {
+		} elsif (/^${_esc_color}:\d{6} \d{6} $sha1_short/o) {
 			push @{$c->{raw}}, $_;
-		} elsif (/^[ACRMDT]\t/) {
+		} elsif (/^${_esc_color}[ACRMDT]\t/) {
 			# we could add $SVN->{svn_path} here, but that requires
 			# remote access at the moment (repo_path_split)...
-			s#^([ACRMDT])\t#   $1 #;
+			s#^(${_esc_color})([ACRMDT])\t#$1   $2 #;
 			push @{$c->{changed}}, $_;
-		} elsif (/^diff /) {
+		} elsif (/^${_esc_color}diff /) {
 			$d = 1;
 			push @{$c->{diff}}, $_;
 		} elsif ($d) {
 			push @{$c->{diff}}, $_;
-		} elsif (/^    (git-svn-id:.+)$/) {
+		} elsif (/^${_esc_color}    (git-svn-id:.+)$/) {
 			($c->{url}, $c->{r}, undef) = extract_metadata($1);
-		} elsif (s/^    //) {
+		} elsif (s/^${_esc_color}    //) {
 			push @{$c->{l}}, $_;
 		}
 	}
@@ -901,12 +906,30 @@ sub cmt_showable {
 	return defined $c->{r};
 }
 
+sub log_use_color {
+	return 1 if $_color;
+	my $dc;
+	chomp($dc = `git-repo-config --get diff.color`);
+	if ($dc eq 'auto') {
+		if (-t *STDOUT || (defined $_pager &&
+		    `git-repo-config --bool --get pager.color` !~ /^false/)) {
+			return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
+		}
+		return 0;
+	}
+	return 0 if $dc eq 'never';
+	return 1 if $dc eq 'always';
+	chomp($dc = `git-repo-config --bool --get diff.color`);
+	$dc eq 'true';
+}
+
 sub git_svn_log_cmd {
 	my ($r_min, $r_max) = @_;
 	my @cmd = (qw/git-log --abbrev-commit --pretty=raw
 			--default/, "refs/remotes/$GIT_SVN");
 	push @cmd, '-r' unless $_non_recursive;
 	push @cmd, qw/--raw --name-status/ if $_verbose;
+	push @cmd, '--color' if log_use_color();
 	return @cmd unless defined $r_max;
 	if ($r_max == $r_min) {
 		push @cmd, '--max-count=1';
@@ -2533,14 +2556,18 @@ sub tz_to_s_offset {
 	return ($1 * 60) + ($tz * 3600);
 }
 
-sub setup_pager { # translated to Perl from pager.c
-	return unless (-t *STDOUT);
-	my $pager = $ENV{PAGER};
-	if (!defined $pager) {
-		$pager = 'less';
-	} elsif (length $pager == 0 || $pager eq 'cat') {
-		return;
+# adapted from pager.c
+sub config_pager {
+	$_pager ||= $ENV{GIT_PAGER} || $ENV{PAGER};
+	if (!defined $_pager) {
+		$_pager = 'less';
+	} elsif (length $_pager == 0 || $_pager eq 'cat') {
+		$_pager = undef;
 	}
+}
+
+sub run_pager {
+	return unless -t *STDOUT;
 	pipe my $rfd, my $wfd or return;
 	defined(my $pid = fork) or croak $!;
 	if (!$pid) {
@@ -2548,8 +2575,8 @@ sub setup_pager { # translated to Perl from pager.c
 		return;
 	}
 	open STDIN, '<&', $rfd or croak $!;
-	$ENV{LESS} ||= '-S';
-	exec $pager or croak "Can't run pager: $!\n";;
+	$ENV{LESS} ||= 'FRSX';
+	exec $_pager or croak "Can't run pager: $! ($_pager)\n";
 }
 
 sub get_author_info {

From 4511c899e64cbda934ba864c359a2a7a04909264 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 28 Nov 2006 18:51:41 -0800
Subject: [PATCH 068/330] git-svn: documentation updates

Eliminate 'commit' from some places and plug 'dcommit' more.
Also update the section --id (GIT_SVN_ID) usage since we
have multi-init/multi-fetch now.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-svn.txt | 40 +++++++++++++++++++--------------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index a764d1f8ee..a45067e164 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -49,7 +49,7 @@ latest revision.
 
 Note: You should never attempt to modify the remotes/git-svn
 branch outside of git-svn.  Instead, create a branch from
-remotes/git-svn and work on that branch.  Use the 'commit'
+remotes/git-svn and work on that branch.  Use the 'dcommit'
 command (see below) to write git commits back to
 remotes/git-svn.
 
@@ -274,7 +274,7 @@ ADVANCED OPTIONS
 
 -b<refname>::
 --branch <refname>::
-Used with 'fetch' or 'commit'.
+Used with 'fetch', 'dcommit' or 'commit'.
 
 This can be used to join arbitrary git branches to remotes/git-svn
 on new commits where the tree object is equivalent.
@@ -368,7 +368,7 @@ SVN was very wrong.
 Basic Examples
 ~~~~~~~~~~~~~~
 
-Tracking and contributing to an Subversion managed-project:
+Tracking and contributing to a Subversion-managed project:
 
 ------------------------------------------------------------------------
 # Initialize a repo (like git init-db):
@@ -377,10 +377,9 @@ Tracking and contributing to an Subversion managed-project:
 	git-svn fetch
 # Create your own branch to hack on:
 	git checkout -b my-branch remotes/git-svn
-# Commit only the git commits you want to SVN:
-	git-svn commit <tree-ish> [<tree-ish_2> ...]
-# Commit all the git commits from my-branch that don't exist in SVN:
-	git-svn commit remotes/git-svn..my-branch
+# Do some work, and then commit your new changes to SVN, as well as
+# automatically updating your working HEAD:
+	git-svn dcommit
 # Something is committed to SVN, rebase the latest into your branch:
 	git-svn fetch && git rebase remotes/git-svn
 # Append svn:ignore settings to the default git exclude file:
@@ -404,26 +403,24 @@ which can lead to merge commits reversing previous commits in SVN.
 DESIGN PHILOSOPHY
 -----------------
 Merge tracking in Subversion is lacking and doing branched development
-with Subversion is cumbersome as a result.  git-svn completely forgoes
-any automated merge/branch tracking on the Subversion side and leaves it
-entirely up to the user on the git side.  It's simply not worth it to do
-a useful translation when the original signal is weak.
+with Subversion is cumbersome as a result.  git-svn does not do
+automated merge/branch tracking by default and leaves it entirely up to
+the user on the git side.
 
 [[tracking-multiple-repos]]
 TRACKING MULTIPLE REPOSITORIES OR BRANCHES
 ------------------------------------------
-This is for advanced users, most users should ignore this section.
-
 Because git-svn does not care about relationships between different
 branches or directories in a Subversion repository, git-svn has a simple
 hack to allow it to track an arbitrary number of related _or_ unrelated
-SVN repositories via one git repository.  Simply set the GIT_SVN_ID
-environment variable to a name other other than "git-svn" (the default)
-and git-svn will ignore the contents of the $GIT_DIR/svn/git-svn directory
-and instead do all of its work in $GIT_DIR/svn/$GIT_SVN_ID for that
-invocation.  The interface branch will be remotes/$GIT_SVN_ID, instead of
-remotes/git-svn.  Any remotes/$GIT_SVN_ID branch should never be modified
-by the user outside of git-svn commands.
+SVN repositories via one git repository.  Simply use the --id/-i flag or
+set the GIT_SVN_ID environment variable to a name other other than
+"git-svn" (the default) and git-svn will ignore the contents of the
+$GIT_DIR/svn/git-svn directory and instead do all of its work in
+$GIT_DIR/svn/$GIT_SVN_ID for that invocation.  The interface branch will
+be remotes/$GIT_SVN_ID, instead of remotes/git-svn.  Any
+remotes/$GIT_SVN_ID branch should never be modified by the user outside
+of git-svn commands.
 
 [[fetch-args]]
 ADDITIONAL FETCH ARGUMENTS
@@ -486,7 +483,8 @@ If you are not using the SVN::* Perl libraries and somebody commits a
 conflicting changeset to SVN at a bad moment (right before you commit)
 causing a conflict and your commit to fail, your svn working tree
 ($GIT_DIR/git-svn/tree) may be dirtied.  The easiest thing to do is
-probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.
+probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'.   You
+can avoid this problem entirely by using 'dcommit'.
 
 We ignore all SVN properties except svn:executable.  Too difficult to
 map them since we rely heavily on git write-tree being _exactly_ the

From 1ca7558dd838e82f6f6b8611b981654fa4ecde2b Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 28 Nov 2006 18:51:42 -0800
Subject: [PATCH 069/330] git-svn: fix multi-init

After the bugfix to connect to repositories where the user has
limited read permissions, multi-init was broken due to our
SVN::Ra connection being limited to working in a subdirectory;
so we now create a new Ra connection for init-ing branches
and another for tags

Along with that fix, allow the user to use the command-line
option flags for multi-init (--revision being the most notable;
but also --no-auth-cache, --config-dir, --username (for passing
to SVN), and --shared/--template for passing to git-init-db

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index d8d8716d38..3891122d73 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -124,7 +124,12 @@ my %cmd = (
 			  'no-graft-copy' => \$_no_graft_copy } ],
 	'multi-init' => [ \&multi_init,
 			'Initialize multiple trees (like git-svnimport)',
-			{ %multi_opts, %fc_opts } ],
+			{ %multi_opts, %init_opts,
+			 'revision|r=i' => \$_revision,
+			 'username=s' => \$_username,
+			 'config-dir=s' => \$_config_dir,
+			 'no-auth-cache' => \$_no_auth_cache,
+			} ],
 	'multi-fetch' => [ \&multi_fetch,
 			'Fetch multiple trees (like git-svnimport)',
 			\%fc_opts ],
@@ -3316,11 +3321,11 @@ sub libsvn_commit_cb {
 
 sub libsvn_ls_fullurl {
 	my $fullurl = shift;
-	$SVN ||= libsvn_connect($fullurl);
+	my $ra = libsvn_connect($fullurl);
 	my @ret;
 	my $pool = SVN::Pool->new;
-	my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
-						$SVN->get_latest_revnum, $pool);
+	my $r = defined $_revision ? $_revision : $ra->get_latest_revnum;
+	my ($dirent, undef, undef) = $ra->get_dir('', $r, $pool);
 	foreach my $d (keys %$dirent) {
 		if ($dirent->{$d}->kind == $SVN::Node::dir) {
 			push @ret, "$d/"; # add '/' for compat with cli svn

From ab3bb800b46cf66249ead9e13c22cbc48d790c9a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 28 Nov 2006 22:29:18 -0800
Subject: [PATCH 070/330] git blame -C: fix output format tweaks when crossing
 file boundary.

We used to get the case that more than two paths came from the
same commit wrong when computing the output width and deciding
to turn on --show-name option automatically.  When we find that
lines that came from a path that is different from what we
started digging from, we should always turn --show-name on, and
we should count the name length for all files involved.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-blame.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin-blame.c b/builtin-blame.c
index 066dee743e..53fed4501a 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1435,14 +1435,14 @@ static void find_alignment(struct scoreboard *sb, int *option)
 		struct commit_info ci;
 		int num;
 
+		if (strcmp(suspect->path, sb->path))
+			*option |= OUTPUT_SHOW_NAME;
+		num = strlen(suspect->path);
+		if (longest_file < num)
+			longest_file = num;
 		if (!(suspect->commit->object.flags & METAINFO_SHOWN)) {
 			suspect->commit->object.flags |= METAINFO_SHOWN;
 			get_commit_info(suspect->commit, &ci, 1);
-			if (strcmp(suspect->path, sb->path))
-				*option |= OUTPUT_SHOW_NAME;
-			num = strlen(suspect->path);
-			if (longest_file < num)
-				longest_file = num;
 			num = strlen(ci.author);
 			if (longest_author < num)
 				longest_author = num;

From 6bee4e408c097a4f0c4a091f13dacabe7c766025 Mon Sep 17 00:00:00 2001
From: Alex Riesen <raa.lkml@gmail.com>
Date: Wed, 15 Nov 2006 22:52:25 +0100
Subject: [PATCH 071/330] git-blame: fix rev parameter handling.

We lacked "--" termination in the underlying init_revisions() call
which made it impossible to specify a revision that happens to
have the same name as an existing file.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-blame.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/builtin-blame.c b/builtin-blame.c
index 53fed4501a..dc3ffeaff8 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1787,6 +1787,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	/* Now we got rev and path.  We do not want the path pruning
 	 * but we may want "bottom" processing.
 	 */
+	argv[unk++] = "--"; /* terminate the rev name */
 	argv[unk] = NULL;
 
 	init_revisions(&revs, NULL);

From 665892307013bccacb35dd619ae6951c7b209379 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 29 Nov 2006 00:17:01 -0800
Subject: [PATCH 072/330] tutorial: talk about user.name early and don't start
 with commit -a

Introducing yourself to git early would be a good idea; otherwise
the user may not find the mistake until much later when "git log"
is learned.

Teaching "commit -a" without saying that it is a shortcut for
listing the paths to commit leaves the user puzzled.  Teach the
form with explicit paths first.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/tutorial.txt | 29 ++++++++++++++++++++++++-----
 1 file changed, 24 insertions(+), 5 deletions(-)

diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 1e4ddfbd11..6555e58882 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -11,6 +11,18 @@ diff" with:
 $ man git-diff
 ------------------------------------------------
 
+It is a good idea to introduce yourself to git before doing any
+operation.  The easiest way to do so is:
+
+------------------------------------------------
+$ cat >~/.gitconfig <<\EOF
+[user]
+	name = Your Name Comes Here
+	email = you@yourdomain.example.com
+EOF
+------------------------------------------------
+
+
 Importing a new project
 -----------------------
 
@@ -31,7 +43,8 @@ defaulting to local storage area
 
 You've now initialized the working directory--you may notice a new
 directory created, named ".git".  Tell git that you want it to track
-every file under the current directory with
+every file under the current directory with (notice the dot '.'
+that means the current directory):
 
 ------------------------------------------------
 $ git add .
@@ -40,7 +53,7 @@ $ git add .
 Finally,
 
 ------------------------------------------------
-$ git commit -a
+$ git commit
 ------------------------------------------------
 
 will prompt you for a commit message, then record the current state
@@ -55,11 +68,17 @@ $ git diff
 to review your changes.  When you're done,
 
 ------------------------------------------------
-$ git commit -a
+$ git commit file1 file2...
 ------------------------------------------------
 
 will again prompt your for a message describing the change, and then
-record the new versions of the modified files.
+record the new versions of the files you listed.  It is cumbersome
+to list all files and you can say `-a` (which stands for 'all')
+instead.
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
 
 A note on commit messages: Though not required, it's a good idea to
 begin the commit message with a single short (less than 50 character)
@@ -75,7 +94,7 @@ $ git add path/to/new/file
 ------------------------------------------------
 
 then commit as usual.  No special command is required when removing a
-file; just remove it, then commit.
+file; just remove it, then tell `commit` about the file as usual.
 
 At any point you can view the history of your changes using
 

From eb07fd59acae0f043b9fac2a8a1cb427036c6f71 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Wed, 29 Nov 2006 08:25:40 +0000
Subject: [PATCH 073/330] Document git-repo-config --bool/--int options.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-repo-config.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 8199615dde..5bede9ac22 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -77,6 +77,12 @@ OPTIONS
 -l, --list::
 	List all variables set in config file.
 
+--bool::
+	git-repo-config will ensure that the output is "true" or "false"
+
+--int::
+	git-repo-config will ensure that the output is a simple decimal number
+
 
 ENVIRONMENT
 -----------

From 67c08ce14fb488562666ab896541ad75f1bdcca6 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Wed, 29 Nov 2006 17:15:48 -0500
Subject: [PATCH 074/330] pack-objects: remove redundent status information

The final 'nr_result' and 'written' values must always be the same
otherwise we're in deep trouble.  So let's remove a redundent report.

And for paranoia sake let's make sure those two variables are actually
equal after all objects are written (one never knows).

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-objects.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 753bcd57b0..a2dc7d1d9d 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -514,6 +514,8 @@ static void write_pack_file(void)
 	if (do_progress)
 		fputc('\n', stderr);
  done:
+	if (written != nr_result)
+		die("wrote %d objects while expecting %d", written, nr_result);
 	sha1close(f, pack_file_sha1, 1);
 }
 
@@ -1662,7 +1664,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		}
 	}
 	if (progress)
-		fprintf(stderr, "Total %d, written %d (delta %d), reused %d (delta %d)\n",
-			nr_result, written, written_delta, reused, reused_delta);
+		fprintf(stderr, "Total %d (delta %d), reused %d (delta %d)\n",
+			written, written_delta, reused, reused_delta);
 	return 0;
 }

From ced7b828fadbf3d6de49d75392f1516b4ceb4491 Mon Sep 17 00:00:00 2001
From: Andreas Ericsson <ae@op5.se>
Date: Thu, 30 Nov 2006 12:28:28 +0100
Subject: [PATCH 075/330] ls-files: Give hints when errors happen.

Without this patch "git commit file.c file2.c" produces the not
so stellar output:

	error: pathspec 'file.c' did not match any.
	error: pathspec 'file2.c' did not match any.

With this patch, the output is changed to:

	error: pathspec 'file.c' did not match any file(s) known to git.
	error: pathspec 'file2.c' did not match any file(s) known to git.
	Did you forget to 'git add'?

Signed-off-by: Andreas Ericsson <ae@op5.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-ls-files.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index ad8c41e731..bc79ce40fc 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -487,10 +487,14 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
 		for (num = 0; pathspec[num]; num++) {
 			if (ps_matched[num])
 				continue;
-			error("pathspec '%s' did not match any.",
+			error("pathspec '%s' did not match any file(s) known to git.",
 			      pathspec[num] + prefix_offset);
 			errors++;
 		}
+
+		if (errors)
+			fprintf(stderr, "Did you forget to 'git add'?\n");
+
 		return errors ? 1 : 0;
 	}
 

From 4c81c213a479e4aae0653a56ad6e8db5c31f019c Mon Sep 17 00:00:00 2001
From: Andreas Ericsson <ae@op5.se>
Date: Thu, 30 Nov 2006 12:43:13 +0100
Subject: [PATCH 076/330] git-diff: Introduce --index and deprecate --cached.

'git diff --cached' still works, but its use is discouraged
in the documentation. 'git diff --index' does the same thing
and is consistent with how 'git apply --index' works.

Signed-off-by: Andreas Ericsson <ae@op5.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-diff.txt | 6 ++++--
 builtin-diff.c             | 2 +-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 228c4d95bd..3144864d85 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -22,8 +22,10 @@ the number of trees given to the command.
 
 * When one <tree-ish> is given, the working tree and the named
   tree are compared, using `git-diff-index`.  The option
-  `--cached` can be given to compare the index file and
+  `--index` can be given to compare the index file and
   the named tree.
+  `--cached` is a deprecated alias for `--index`. It's use is
+  discouraged.
 
 * When two <tree-ish>s are given, these two trees are compared
   using `git-diff-tree`.
@@ -47,7 +49,7 @@ Various ways to check your working tree::
 +
 ------------
 $ git diff            <1>
-$ git diff --cached   <2>
+$ git diff --index    <2>
 $ git diff HEAD       <3>
 ------------
 +
diff --git a/builtin-diff.c b/builtin-diff.c
index a6590205e8..1c535b1dd6 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -137,7 +137,7 @@ static int builtin_diff_index(struct rev_info *revs,
 	int cached = 0;
 	while (1 < argc) {
 		const char *arg = argv[1];
-		if (!strcmp(arg, "--cached"))
+		if (!strcmp(arg, "--index") || !strcmp(arg, "--cached"))
 			cached = 1;
 		else
 			usage(builtin_diff_usage);

From 22b1c7ee01ef5bc7f81e620bb88a6fad79c1c605 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Thu, 30 Nov 2006 10:50:28 +0000
Subject: [PATCH 077/330] De-emphasise the symbolic link documentation.

The fact that git has previously used symbolic links for representing
symbolic refs doesn't seem relevant to the current function of
git-symbolic-ref.  This patch makes less of a big deal about the
symbolic link history and instead focuses on what git does now.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-symbolic-ref.txt | 29 +++++++++++------------------
 1 file changed, 11 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 68ac6a65df..4bc35a1d4b 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -19,29 +19,22 @@ argument to see on which branch your working tree is on.
 Give two arguments, create or update a symbolic ref <name> to
 point at the given branch <ref>.
 
-Traditionally, `.git/HEAD` is a symlink pointing at
-`refs/heads/master`.  When we want to switch to another branch,
-we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we want
+A symbolic ref is a regular file that stores a string that
+begins with `ref: refs/`.  For example, your `.git/HEAD` is
+a regular file whose contents is `ref: refs/heads/master`.
+
+NOTES
+-----
+In the past, `.git/HEAD` was a symbolic link pointing at
+`refs/heads/master`.  When we wanted to switch to another branch,
+we did `ln -sf refs/heads/newbranch .git/HEAD`, and when we wanted
 to find out which branch we are on, we did `readlink .git/HEAD`.
 This was fine, and internally that is what still happens by
 default, but on platforms that do not have working symlinks,
 or that do not have the `readlink(1)` command, this was a bit
 cumbersome.  On some platforms, `ln -sf` does not even work as
-advertised (horrors).
-
-A symbolic ref can be a regular file that stores a string that
-begins with `ref: refs/`.  For example, your `.git/HEAD` *can*
-be a regular file whose contents is `ref: refs/heads/master`.
-This can be used on a filesystem that does not support symbolic
-links.  Instead of doing `readlink .git/HEAD`, `git-symbolic-ref
-HEAD` can be used to find out which branch we are on.  To point
-the HEAD to `newbranch`, instead of `ln -sf refs/heads/newbranch
-.git/HEAD`, `git-symbolic-ref HEAD refs/heads/newbranch` can be
-used.
-
-Currently, .git/HEAD uses a regular file symbolic ref on Cygwin,
-and everywhere else it is implemented as a symlink.  This can be
-changed at compilation time.
+advertised (horrors).  Therefore symbolic links are now deprecated
+and symbolic refs are used by default.
 
 Author
 ------

From 3683dc5a9afaf88d00e55c9e6c67a2160ca7fc9c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 2 Dec 2006 16:58:30 -0800
Subject: [PATCH 078/330] git-merge: fix confusion between tag and branch

In a repository with core.warnambiguousrefs turned off, and with
a branch and a tag that have the same name 'frotz',

	git merge frotz

would merge the commit pointed at by the tag 'frotz' but
incorrectly would identify what was merged as 'branch frotz' in
the merge message.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index 75af10d3e4..272f004622 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -189,13 +189,13 @@ else
 	merge_name=$(for remote
 		do
 			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
-			if git show-ref -q --verify "refs/heads/$remote"
+			bh=$(git show-ref -s --verify "refs/heads/$remote") &&
+			if test "$rh" = "$bh"
 			then
-				what=branch
+				echo "$rh		branch '$remote' of ."
 			else
-				what=commit
-			fi &&
-			echo "$rh		$what '$remote'"
+				echo "$rh		commit '$remote'"
+			fi
 		done | git-fmt-merge-msg
 	)
 	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"

From 6173c197c9a23fa8594f18fd2c856407d4af31c1 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sat, 2 Dec 2006 16:19:31 -0800
Subject: [PATCH 079/330] git-svn: avoid fetching files twice in the same
 revision

SVN is not entirely consistent in returning log information and
sometimes returns file information when adding subdirectories,
and sometimes it does not (only returning information about the
directory that was added).  This caused git-svn to occasionally
add a file to the list of files to be fetched twice.  Now we
change the data structure to be hash to avoid repeated fetches.

As of now (in master), this only affects repositories fetched
without deltas enabled (file://, and when manually overriden
with GIT_SVN_DELTA_FETCH=0); so this bug mainly affects users of
1.4.4.1 and maint.

Thanks to Florian Weimer for reporting this bug.

[jc: backported for maint]

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index bb8935afe6..b53273eaea 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2778,7 +2778,7 @@ sub process_rm {
 sub libsvn_fetch {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
-	my @amr;
+	my %amr;
 	foreach my $f (keys %$paths) {
 		my $m = $paths->{$f}->action();
 		$f =~ s#^/+##;
@@ -2792,7 +2792,7 @@ sub libsvn_fetch {
 		my $t = $SVN->check_path($f, $rev, $pool);
 		if ($t == $SVN::Node::file) {
 			if ($m =~ /^[AMR]$/) {
-				push @amr, [ $m, $f ];
+				$amr{$f} = $m;
 			} else {
 				die "Unrecognized action: $m, ($f r$rev)\n";
 			}
@@ -2800,13 +2800,13 @@ sub libsvn_fetch {
 			my @traversed = ();
 			libsvn_traverse($gui, '', $f, $rev, \@traversed);
 			foreach (@traversed) {
-				push @amr, [ $m, $_ ]
+				$amr{$_} = $m;
 			}
 		}
 		$pool->clear;
 	}
-	foreach (@amr) {
-		libsvn_get_file($gui, $_->[1], $rev, $_->[0]);
+	foreach (keys %amr) {
+		libsvn_get_file($gui, $_, $rev, $amr{$_});
 	}
 	close $gui or croak $?;
 	return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);

From 857b933e04bc21ce02043c3107c148f8dcbb4a01 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 21 Nov 2006 23:24:34 +0100
Subject: [PATCH 080/330] xdiff: add xdl_merge()

This new function implements the functionality of RCS merge, but
in-memory. It returns < 0 on error, otherwise the number of conflicts.

Finding the conflicting lines can be a very expensive task. You can
control the eagerness of this algorithm:

- a level value of 0 means that all overlapping changes are treated
  as conflicts,
- a value of 1 means that if the overlapping changes are identical,
  it is not treated as a conflict.
- If you set level to 2, overlapping changes will be analyzed, so that
  almost identical changes will not result in huge conflicts. Rather,
  only the conflicting lines will be shown inside conflict markers.

With each increasing level, the algorithm gets slower, but more accurate.
Note that the code for level 2 depends on the simple definition of
mmfile_t specific to git, and therefore it will be harder to port that
to LibXDiff.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile       |   3 +-
 xdiff/xdiff.h  |   7 +
 xdiff/xdiffi.c |   3 +-
 xdiff/xdiffi.h |   1 +
 xdiff/xmerge.c | 433 +++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 444 insertions(+), 3 deletions(-)
 create mode 100644 xdiff/xmerge.c

diff --git a/Makefile b/Makefile
index de3e9f38ec..23bbb90683 100644
--- a/Makefile
+++ b/Makefile
@@ -723,7 +723,8 @@ $(DIFF_OBJS): diffcore.h
 $(LIB_FILE): $(LIB_OBJS)
 	rm -f $@ && $(AR) rcs $@ $(LIB_OBJS)
 
-XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o
+XDIFF_OBJS=xdiff/xdiffi.o xdiff/xprepare.o xdiff/xutils.o xdiff/xemit.o \
+	xdiff/xmerge.o
 $(XDIFF_OBJS): xdiff/xinclude.h xdiff/xmacros.h xdiff/xdiff.h xdiff/xtypes.h \
 	xdiff/xutils.h xdiff/xprepare.h xdiff/xdiffi.h xdiff/xemit.h
 
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index c9f817818a..fa409d5234 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -49,6 +49,9 @@ extern "C" {
 #define XDL_BDOP_CPY 2
 #define XDL_BDOP_INSB 3
 
+#define XDL_MERGE_MINIMAL 0
+#define XDL_MERGE_EAGER 1
+#define XDL_MERGE_ZEALOUS 2
 
 typedef struct s_mmfile {
 	char *ptr;
@@ -90,6 +93,10 @@ long xdl_mmfile_size(mmfile_t *mmf);
 int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 	     xdemitconf_t const *xecfg, xdemitcb_t *ecb);
 
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
+		mmfile_t *mf2, const char *name2,
+		xpparam_t const *xpp, int level, mmbuffer_t *result);
+
 #ifdef __cplusplus
 }
 #endif /* #ifdef __cplusplus */
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index d76e76a0e6..9aeebc473b 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -45,7 +45,6 @@ static long xdl_split(unsigned long const *ha1, long off1, long lim1,
 		      long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
 		      xdalgoenv_t *xenv);
 static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
-static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
 
 
 
@@ -397,7 +396,7 @@ static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1,
 }
 
 
-static int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
 	long ix, ixo, ixs, ixref, grpsiz, nrec = xdf->nrec;
 	char *rchg = xdf->rchg, *rchgo = xdfo->rchg;
 	xrecord_t **recs = xdf->recs;
diff --git a/xdiff/xdiffi.h b/xdiff/xdiffi.h
index d3b72716b5..472aeaecfa 100644
--- a/xdiff/xdiffi.h
+++ b/xdiff/xdiffi.h
@@ -50,6 +50,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
 		 long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
 int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
 		xdfenv_t *xe);
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
 int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
 void xdl_free_script(xdchange_t *xscr);
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
new file mode 100644
index 0000000000..7b85aa5906
--- /dev/null
+++ b/xdiff/xmerge.c
@@ -0,0 +1,433 @@
+/*
+ *  LibXDiff by Davide Libenzi ( File Differential Library )
+ *  Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+typedef struct s_xdmerge {
+	struct s_xdmerge *next;
+	/*
+	 * 0 = conflict,
+	 * 1 = no conflict, take first,
+	 * 2 = no conflict, take second.
+	 */
+	int mode;
+	long i1, i2;
+	long chg1, chg2;
+} xdmerge_t;
+
+static int xdl_append_merge(xdmerge_t **merge, int mode,
+		long i1, long chg1, long i2, long chg2)
+{
+	xdmerge_t *m = *merge;
+	if (m && mode == m->mode &&
+			(i1 == m->i1 + m->chg1 || i2 == m->i2 + m->chg2)) {
+		m->chg1 = i1 + chg1 - m->i1;
+		m->chg2 = i2 + chg2 - m->i2;
+	} else {
+		m = xdl_malloc(sizeof(xdmerge_t));
+		if (!m)
+			return -1;
+		m->next = NULL;
+		m->mode = mode;
+		m->i1 = i1;
+		m->chg1 = chg1;
+		m->i2 = i2;
+		m->chg2 = chg2;
+		if (*merge)
+			(*merge)->next = m;
+		*merge = m;
+	}
+	return 0;
+}
+
+static int xdl_cleanup_merge(xdmerge_t *c)
+{
+	int count = 0;
+	xdmerge_t *next_c;
+
+	/* were there conflicts? */
+	for (; c; c = next_c) {
+		if (c->mode == 0)
+			count++;
+		next_c = c->next;
+		free(c);
+	}
+	return count;
+}
+
+static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
+		int line_count, long flags)
+{
+	int i;
+	xrecord_t **rec1 = xe1->xdf2.recs + i1;
+	xrecord_t **rec2 = xe2->xdf2.recs + i2;
+
+	for (i = 0; i < line_count; i++) {
+		int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
+			rec2[i]->ptr, rec2[i]->size, flags);
+		if (!result)
+			return -1;
+	}
+	return 0;
+}
+
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+{
+	xrecord_t **recs = xe->xdf2.recs + i;
+	int size = 0;
+
+	if (count < 1)
+		return 0;
+
+	for (i = 0; i < count; size += recs[i++]->size)
+		if (dest)
+			memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+	if (add_nl) {
+		i = recs[count - 1]->size;
+		if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+			if (dest)
+				dest[size] = '\n';
+			size++;
+		}
+	}
+	return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+		xdfenv_t *xe2, const char *name2, xdmerge_t *m, char *dest)
+{
+	const int marker_size = 7;
+	int marker1_size = (name1 ? strlen(name1) + 1 : 0);
+	int marker2_size = (name2 ? strlen(name2) + 1 : 0);
+	int conflict_marker_size = 3 * (marker_size + 1)
+		+ marker1_size + marker2_size;
+	int size, i1, j;
+
+	for (size = i1 = 0; m; m = m->next) {
+		if (m->mode == 0) {
+			size += xdl_recs_copy(xe1, i1, m->i1 - i1, 0,
+					dest ? dest + size : NULL);
+			if (dest) {
+				for (j = 0; j < marker_size; j++)
+					dest[size++] = '<';
+				if (marker1_size) {
+					dest[size] = ' ';
+					memcpy(dest + size + 1, name1,
+							marker1_size - 1);
+					size += marker1_size;
+				}
+				dest[size++] = '\n';
+			} else
+				size += conflict_marker_size;
+			size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
+					dest ? dest + size : NULL);
+			if (dest) {
+				for (j = 0; j < marker_size; j++)
+					dest[size++] = '=';
+				dest[size++] = '\n';
+			}
+			size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
+					dest ? dest + size : NULL);
+			if (dest) {
+				for (j = 0; j < marker_size; j++)
+					dest[size++] = '>';
+				if (marker2_size) {
+					dest[size] = ' ';
+					memcpy(dest + size + 1, name2,
+							marker2_size - 1);
+					size += marker2_size;
+				}
+				dest[size++] = '\n';
+			}
+		} else if (m->mode == 1)
+			size += xdl_recs_copy(xe1, i1, m->i1 + m->chg1 - i1, 0,
+					dest ? dest + size : NULL);
+		else if (m->mode == 2)
+			size += xdl_recs_copy(xe2, m->i2 - m->i1 + i1,
+					m->i1 + m->chg2 - i1, 0,
+					dest ? dest + size : NULL);
+		i1 = m->i1 + m->chg1;
+	}
+	size += xdl_recs_copy(xe1, i1, xe1->xdf2.nrec - i1, 0,
+			dest ? dest + size : NULL);
+	return size;
+}
+
+/*
+ * Sometimes, changes are not quite identical, but differ in only a few
+ * lines. Try hard to show only these few lines as conflicting.
+ */
+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+		xpparam_t const *xpp)
+{
+	for (; m; m = m->next) {
+		mmfile_t t1, t2;
+		xdfenv_t xe;
+		xdchange_t *xscr, *x;
+		int i1 = m->i1, i2 = m->i2;
+
+		/* let's handle just the conflicts */
+		if (m->mode)
+			continue;
+
+		/*
+		 * This probably does not work outside git, since
+		 * we have a very simple mmfile structure.
+		 */
+		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
+		t1.size = xe1->xdf2.recs[m->i1 + m->chg1]->ptr
+			+ xe1->xdf2.recs[m->i1 + m->chg1]->size - t1.ptr;
+		t2.ptr = (char *)xe2->xdf2.recs[m->i1]->ptr;
+		t2.size = xe2->xdf2.recs[m->i1 + m->chg1]->ptr
+			+ xe2->xdf2.recs[m->i1 + m->chg1]->size - t2.ptr;
+		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
+			return -1;
+		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+		    xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+		    xdl_build_script(&xe, &xscr) < 0) {
+			xdl_free_env(&xe);
+			return -1;
+		}
+		if (!xscr) {
+			/* If this happens, it's a bug. */
+			xdl_free_env(&xe);
+			return -2;
+		}
+		x = xscr;
+		m->i1 = xscr->i1 + i1;
+		m->chg1 = xscr->chg1;
+		m->i2 = xscr->i2 + i2;
+		m->chg2 = xscr->chg2;
+		while (xscr->next) {
+			xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
+			if (!m2) {
+				xdl_free_env(&xe);
+				xdl_free_script(x);
+				return -1;
+			}
+			xscr = xscr->next;
+			m2->next = m->next;
+			m->next = m2;
+			m = m2;
+			m->mode = 0;
+			m->i1 = xscr->i1 + i1;
+			m->chg1 = xscr->chg1;
+			m->i2 = xscr->i2 + i2;
+			m->chg2 = xscr->chg2;
+		}
+		xdl_free_env(&xe);
+		xdl_free_script(x);
+	}
+	return 0;
+}
+
+/*
+ * level == 0: mark all overlapping changes as conflict
+ * level == 1: mark overlapping changes as conflict only if not identical
+ * level == 2: analyze non-identical changes for minimal conflict set
+ *
+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts
+ */
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
+		xdfenv_t *xe2, xdchange_t *xscr2, const char *name2,
+		int level, xpparam_t const *xpp, mmbuffer_t *result) {
+	xdmerge_t *changes, *c;
+	int i1, i2, chg1, chg2;
+
+	c = changes = NULL;
+
+	while (xscr1 && xscr2) {
+		if (!changes)
+			changes = c;
+		if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+			i1 = xscr1->i2;
+			i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+			chg1 = xscr1->chg2;
+			chg2 = xscr1->chg1;
+			if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+			xscr1 = xscr1->next;
+			continue;
+		}
+		if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+			i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
+			i2 = xscr2->i2;
+			chg1 = xscr2->chg1;
+			chg2 = xscr2->chg2;
+			if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+			xscr2 = xscr2->next;
+			continue;
+		}
+		if (level < 1 || xscr1->i1 != xscr2->i1 ||
+				xscr1->chg1 != xscr2->chg1 ||
+				xscr1->chg2 != xscr2->chg2 ||
+				xdl_merge_cmp_lines(xe1, xscr1->i2,
+					xe2, xscr2->i2,
+					xscr1->chg2, xpp->flags)) {
+			/* conflict */
+			int off = xscr1->i1 - xscr2->i1;
+			int ffo = off + xscr1->chg1 - xscr2->chg1;
+
+			i1 = xscr1->i2;
+			i2 = xscr2->i2;
+			if (off > 0)
+				i1 -= off;
+			else
+				i2 += off;
+			chg1 = xscr1->i2 + xscr1->chg2 - i1;
+			chg2 = xscr2->i2 + xscr2->chg2 - i2;
+			if (ffo > 0)
+				chg2 += ffo;
+			else
+				chg1 -= ffo;
+			if (xdl_append_merge(&c, 0, i1, chg1, i2, chg2)) {
+				xdl_cleanup_merge(changes);
+				return -1;
+			}
+		}
+
+		i1 = xscr1->i1 + xscr1->chg1;
+		i2 = xscr2->i1 + xscr2->chg1;
+
+		if (i1 > i2) {
+			xscr1->chg1 -= i1 - i2;
+			xscr1->i1 = i2;
+			xscr1->i2 += xscr1->chg2;
+			xscr1->chg2 = 0;
+			xscr1 = xscr1->next;
+		} else if (i2 > i1) {
+			xscr2->chg1 -= i2 - i1;
+			xscr2->i1 = i1;
+			xscr2->i2 += xscr2->chg2;
+			xscr2->chg2 = 0;
+			xscr2 = xscr2->next;
+		} else {
+			xscr1 = xscr1->next;
+			xscr2 = xscr2->next;
+		}
+	}
+	while (xscr1) {
+		if (!changes)
+			changes = c;
+		i1 = xscr1->i2;
+		i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+		chg1 = xscr1->chg2;
+		chg2 = xscr1->chg1;
+		if (xdl_append_merge(&c, 1, i1, chg1, i2, chg2)) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		xscr1 = xscr1->next;
+	}
+	while (xscr2) {
+		if (!changes)
+			changes = c;
+		i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+		i2 = xscr2->i2;
+		chg1 = xscr2->chg1;
+		chg2 = xscr2->chg2;
+		if (xdl_append_merge(&c, 2, i1, chg1, i2, chg2)) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		xscr2 = xscr2->next;
+	}
+	if (!changes)
+		changes = c;
+	/* refine conflicts */
+	if (level > 1 && xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0) {
+		xdl_cleanup_merge(changes);
+		return -1;
+	}
+	/* output */
+	if (result) {
+		int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+			changes, NULL);
+		result->ptr = xdl_malloc(size);
+		if (!result->ptr) {
+			xdl_cleanup_merge(changes);
+			return -1;
+		}
+		result->size = size;
+		xdl_fill_merge_buffer(xe1, name1, xe2, name2, changes,
+				result->ptr);
+	}
+	return xdl_cleanup_merge(changes);
+}
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
+		mmfile_t *mf2, const char *name2,
+		xpparam_t const *xpp, int level, mmbuffer_t *result) {
+	xdchange_t *xscr1, *xscr2;
+	xdfenv_t xe1, xe2;
+
+	result->ptr = NULL;
+	result->size = 0;
+
+	if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0 ||
+			xdl_do_diff(orig, mf2, xpp, &xe2) < 0) {
+		return -1;
+	}
+	if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
+	    xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
+	    xdl_build_script(&xe1, &xscr1) < 0) {
+		xdl_free_env(&xe1);
+		return -1;
+	}
+	if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
+	    xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
+	    xdl_build_script(&xe2, &xscr2) < 0) {
+		xdl_free_env(&xe2);
+		return -1;
+	}
+	if (xscr1 || xscr2) {
+		if (!xscr1) {
+			result->ptr = xdl_malloc(mf2->size);
+			memcpy(result->ptr, mf2->ptr, mf2->size);
+			result->size = mf2->size;
+		} else if (!xscr2) {
+			result->ptr = xdl_malloc(mf1->size);
+			memcpy(result->ptr, mf1->ptr, mf1->size);
+			result->size = mf1->size;
+		} else if (xdl_do_merge(&xe1, xscr1, name1,
+					&xe2, xscr2, name2,
+					level, xpp, result) < 0) {
+			xdl_free_script(xscr1);
+			xdl_free_script(xscr2);
+			xdl_free_env(&xe1);
+			xdl_free_env(&xe2);
+			return -1;
+		}
+		xdl_free_script(xscr1);
+		xdl_free_script(xscr2);
+	}
+	xdl_free_env(&xe1);
+	xdl_free_env(&xe2);
+
+	return 0;
+}
+

From 53a7085a104936aef29400369aab15e0d1ccbb28 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 29 Nov 2006 12:06:49 -0800
Subject: [PATCH 081/330] xmerge: make return value from xdl_merge() more
 usable.

The callers would want to know if the resulting merge is clean;
do not discard that information away after calling xdl_do_merge().

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 xdiff/xmerge.c | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 7b85aa5906..286f177b54 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -384,6 +384,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		xpparam_t const *xpp, int level, mmbuffer_t *result) {
 	xdchange_t *xscr1, *xscr2;
 	xdfenv_t xe1, xe2;
+	int status;
 
 	result->ptr = NULL;
 	result->size = 0;
@@ -404,6 +405,7 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 		xdl_free_env(&xe2);
 		return -1;
 	}
+	status = 0;
 	if (xscr1 || xscr2) {
 		if (!xscr1) {
 			result->ptr = xdl_malloc(mf2->size);
@@ -413,14 +415,10 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 			result->ptr = xdl_malloc(mf1->size);
 			memcpy(result->ptr, mf1->ptr, mf1->size);
 			result->size = mf1->size;
-		} else if (xdl_do_merge(&xe1, xscr1, name1,
-					&xe2, xscr2, name2,
-					level, xpp, result) < 0) {
-			xdl_free_script(xscr1);
-			xdl_free_script(xscr2);
-			xdl_free_env(&xe1);
-			xdl_free_env(&xe2);
-			return -1;
+		} else {
+			status = xdl_do_merge(&xe1, xscr1, name1,
+					      &xe2, xscr2, name2,
+					      level, xpp, result);
 		}
 		xdl_free_script(xscr1);
 		xdl_free_script(xscr2);
@@ -428,6 +426,5 @@ int xdl_merge(mmfile_t *orig, mmfile_t *mf1, const char *name1,
 	xdl_free_env(&xe1);
 	xdl_free_env(&xe2);
 
-	return 0;
+	return status;
 }
-

From c2b4faea4522b28a7611ddab5eb6a5c221cc3ee5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 29 Nov 2006 12:07:37 -0800
Subject: [PATCH 082/330] merge-recursive: use xdl_merge().

This seem to pass the existing tests already.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 merge-recursive.c | 86 +++++++++++++++++++----------------------------
 1 file changed, 35 insertions(+), 51 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index cd2cc77bf4..6e13b8ed5b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -21,6 +21,7 @@
 #include "tag.h"
 #include "unpack-trees.h"
 #include "path-list.h"
+#include "xdiff-interface.h"
 
 /*
  * A virtual commit has
@@ -604,24 +605,15 @@ struct merge_file_info
 		 merge:1;
 };
 
-static char *git_unpack_file(const unsigned char *sha1, char *path)
+static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
 {
-	void *buf;
-	char type[20];
 	unsigned long size;
-	int fd;
+	char type[20];
 
-	buf = read_sha1_file(sha1, type, &size);
-	if (!buf || strcmp(type, blob_type))
+	mm->ptr = read_sha1_file(sha1, type, &size);
+	if (!mm->ptr || strcmp(type, blob_type))
 		die("unable to read blob object %s", sha1_to_hex(sha1));
-
-	strcpy(path, ".merge_file_XXXXXX");
-	fd = mkstemp(path);
-	if (fd < 0)
-		die("unable to create temp-file");
-	flush_buffer(fd, buf, size);
-	close(fd);
-	return path;
+	mm->size = size;
 }
 
 static struct merge_file_info merge_file(struct diff_filespec *o,
@@ -652,49 +644,41 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 		else if (sha_eq(b->sha1, o->sha1))
 			hashcpy(result.sha, a->sha1);
 		else if (S_ISREG(a->mode)) {
-			int code = 1, fd;
-			struct stat st;
-			char orig[PATH_MAX];
-			char src1[PATH_MAX];
-			char src2[PATH_MAX];
-			const char *argv[] = {
-				"merge", "-L", NULL, "-L", NULL, "-L", NULL,
-				NULL, NULL, NULL,
-				NULL
-			};
-			char *la, *lb, *lo;
+			mmfile_t orig, src1, src2;
+			mmbuffer_t result_buf;
+			xpparam_t xpp;
+			char *name1, *name2;
+			int merge_status;
 
-			git_unpack_file(o->sha1, orig);
-			git_unpack_file(a->sha1, src1);
-			git_unpack_file(b->sha1, src2);
+			name1 = xstrdup(mkpath("%s/%s", branch1, a->path));
+			name2 = xstrdup(mkpath("%s/%s", branch2, b->path));
 
-			argv[2] = la = xstrdup(mkpath("%s/%s", branch1, a->path));
-			argv[6] = lb = xstrdup(mkpath("%s/%s", branch2, b->path));
-			argv[4] = lo = xstrdup(mkpath("orig/%s", o->path));
-			argv[7] = src1;
-			argv[8] = orig;
-			argv[9] = src2,
+			fill_mm(o->sha1, &orig);
+			fill_mm(a->sha1, &src1);
+			fill_mm(b->sha1, &src2);
 
-			code = run_command_v(10, argv);
+			memset(&xpp, 0, sizeof(xpp));
+			merge_status = xdl_merge(&orig,
+						 &src1, name1,
+						 &src2, name2,
+						 &xpp, XDL_MERGE_ZEALOUS,
+						 &result_buf);
+			free(name1);
+			free(name2);
+			free(orig.ptr);
+			free(src1.ptr);
+			free(src2.ptr);
 
-			free(la);
-			free(lb);
-			free(lo);
-			if (code && code < -256) {
-				die("Failed to execute 'merge'. merge(1) is used as the "
-				    "file-level merge tool. Is 'merge' in your path?");
-			}
-			fd = open(src1, O_RDONLY);
-			if (fd < 0 || fstat(fd, &st) < 0 ||
-					index_fd(result.sha, fd, &st, 1,
-						"blob"))
-				die("Unable to add %s to database", src1);
+			if ((merge_status < 0) || !result_buf.ptr)
+				die("Failed to execute internal merge");
 
-			unlink(orig);
-			unlink(src1);
-			unlink(src2);
+			if (write_sha1_file(result_buf.ptr, result_buf.size,
+					    blob_type, result.sha))
+				die("Unable to add %s to database",
+				    a->path);
 
-			result.clean = WEXITSTATUS(code) == 0;
+			free(result_buf.ptr);
+			result.clean = (merge_status == 0);
 		} else {
 			if (!(S_ISLNK(a->mode) || S_ISLNK(b->mode)))
 				die("cannot merge modes?");

From 875b8ce47650d712c8f464cfedb9147673fe3ff7 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 30 Nov 2006 00:24:32 +0100
Subject: [PATCH 083/330] xdl_merge(): fix an off-by-one bug

The line range is i1 .. (i1 + chg1 - 1), not i1 .. (i1 + chg1).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 xdiff/xmerge.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 286f177b54..3f5dc87fd2 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -194,11 +194,11 @@ static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
 		 * we have a very simple mmfile structure.
 		 */
 		t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
-		t1.size = xe1->xdf2.recs[m->i1 + m->chg1]->ptr
-			+ xe1->xdf2.recs[m->i1 + m->chg1]->size - t1.ptr;
-		t2.ptr = (char *)xe2->xdf2.recs[m->i1]->ptr;
-		t2.size = xe2->xdf2.recs[m->i1 + m->chg1]->ptr
-			+ xe2->xdf2.recs[m->i1 + m->chg1]->size - t2.ptr;
+		t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+			+ xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+		t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+		t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+			+ xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
 		if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
 			return -1;
 		if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||

From 710daa83fc76f79b8f2ee9a765d297187c2c1aeb Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 30 Nov 2006 00:25:11 +0100
Subject: [PATCH 084/330] xdl_merge(): fix thinko

If one side's block (of changed lines) ends later than the other
side's block, the former should be tested against the next block
of the other side, not vice versa.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 xdiff/xmerge.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 3f5dc87fd2..1fe7a1b060 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -318,13 +318,13 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 			xscr1->i1 = i2;
 			xscr1->i2 += xscr1->chg2;
 			xscr1->chg2 = 0;
-			xscr1 = xscr1->next;
+			xscr2 = xscr2->next;
 		} else if (i2 > i1) {
 			xscr2->chg1 -= i2 - i1;
 			xscr2->i1 = i1;
 			xscr2->i2 += xscr2->chg2;
 			xscr2->chg2 = 0;
-			xscr2 = xscr2->next;
+			xscr1 = xscr1->next;
 		} else {
 			xscr1 = xscr1->next;
 			xscr2 = xscr2->next;

From aca085e577688108a2480b96a2f7077424a74e4d Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 3 Dec 2006 20:42:47 +0100
Subject: [PATCH 085/330] git-mv: search more precisely for source directory in
 index

A move of a directory should find the entries in the index by
searching for the name _including_ the slash. Otherwise, the
directory can be shadowed by a file when it matches the prefix
and is lexicographically smaller, e.g. "ab.c" shadows "ab/".

Noticed by Sergey Vlasov.

[jc: added Sergey's original reproduction recipe as a test case
 at the end of t7001.]

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-mv.c  | 11 +++++++----
 t/t7001-mv.sh | 13 +++++++++++++
 2 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/builtin-mv.c b/builtin-mv.c
index 54dd3bfe8a..d14a4a7f5c 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -146,21 +146,24 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
 				&& lstat(dst, &st) == 0)
 			bad = "cannot move directory over file";
 		else if (src_is_dir) {
+			const char *src_w_slash = add_slash(src);
+			int len_w_slash = length + 1;
 			int first, last;
 
 			modes[i] = WORKING_DIRECTORY;
 
-			first = cache_name_pos(src, length);
+			first = cache_name_pos(src_w_slash, len_w_slash);
 			if (first >= 0)
-				die ("Huh? %s/ is in index?", src);
+				die ("Huh? %.*s is in index?",
+						len_w_slash, src_w_slash);
 
 			first = -1 - first;
 			for (last = first; last < active_nr; last++) {
 				const char *path = active_cache[last]->name;
-				if (strncmp(path, src, length)
-						|| path[length] != '/')
+				if (strncmp(path, src_w_slash, len_w_slash))
 					break;
 			}
+			free((char *)src_w_slash);
 
 			if (last - first < 1)
 				bad = "source directory is empty";
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 23a1eff3bb..2f4ff82e14 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -105,4 +105,17 @@ test_expect_success "Michael Cassar's test case" '
 	}
 '
 
+rm -fr papers partA path?
+
+test_expect_success "Sergey Vlasov's test case" '
+	rm -fr .git &&
+	git init-db &&
+	mkdir ab &&
+	date >ab.c &&
+	date >ab/d &&
+	git add ab.c ab &&
+	git commit -m 'initial' &&
+	git mv ab a
+'
+
 test_done

From 7c0f7028ee04f135c7481671f05ca4a66072c78f Mon Sep 17 00:00:00 2001
From: Jim Meyering <jim@meyering.net>
Date: Mon, 4 Dec 2006 08:44:08 +0100
Subject: [PATCH 086/330] Set permissions of each new file before "cvs add"ing
 it.

Otherwise, an executable script in git would end up being
checked into the CVS repository without the execute bit.

[jc: with an additional test script from Robin Rosenberg.]

Signed-off-by: Jim Meyering <jim@meyering.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-cvsexportcommit.perl       | 13 +++++++++++++
 t/t9200-git-cvsexportcommit.sh | 16 ++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index 7bac16e946..c9d1d88f2e 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -116,6 +116,7 @@ if ($opt_a) {
 close MSG;
 
 my (@afiles, @dfiles, @mfiles, @dirs);
+my %amodes;
 my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
 #print @files;
 $? && die "Error in git-diff-tree";
@@ -124,6 +125,7 @@ foreach my $f (@files) {
     my @fields = split(m!\s+!, $f);
     if ($fields[4] eq 'A') {
         my $path = $fields[5];
+	$amodes{$path} = $fields[1];
 	push @afiles, $path;
         # add any needed parent directories
 	$path = dirname $path;
@@ -268,6 +270,7 @@ if (($? >> 8) == 2) {
 }
 
 foreach my $f (@afiles) {
+    set_new_file_permissions($f, $amodes{$f});
     if (grep { $_ eq $f } @bfiles) {
       system('cvs', 'add','-kb',$f);
     } else {
@@ -342,3 +345,13 @@ sub safe_pipe_capture {
     }
     return wantarray ? @output : join('',@output);
 }
+
+# For any file we want to add to cvs, we must first set its permissions
+# properly, *before* the "cvs add ..." command.  Otherwise, it is impossible
+# to change the permission of the file in the CVS repository using only cvs
+# commands.  This should be fixed in cvs-1.12.14.
+sub set_new_file_permissions {
+    my ($file, $perm) = @_;
+    chmod oct($perm), $file
+      or die "failed to set permissions of \"$file\": $!\n";
+}
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 6e566d4409..c1024790e4 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -142,4 +142,20 @@ test_expect_success \
      diff F/newfile6.png ../F/newfile6.png
      )'
 
+test_expect_success 'Retain execute bit' '
+	mkdir G &&
+	echo executeon >G/on &&
+	chmod +x G/on &&
+	echo executeoff >G/off &&
+	git add G/on &&
+	git add G/off &&
+	git commit -a -m "Execute test" &&
+	(
+		cd "$CVSWORK" &&
+		git-cvsexportcommit -c HEAD
+		test -x G/on &&
+		! test -x G/off
+	)
+'
+
 test_done

From 396db813f2e9b56460cd783c73daf15150e36b41 Mon Sep 17 00:00:00 2001
From: David Miller <davem@davemloft.net>
Date: Sun, 3 Dec 2006 23:17:00 -0800
Subject: [PATCH 087/330] Pass -M to diff in request-pull

Linus recommended this, otherwise any renames cause the
diffstat output to be ridiculous in some circumstances.

Because the corresponding "git-pull" done when the requestee
actually makes pull shows the stat with rename detection
enabled, it makes sense to match what the request message
includes to that output, to make the result easier to verify.

Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-request-pull.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-request-pull.sh b/git-request-pull.sh
index 4319e35c62..4eacc3a059 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -30,4 +30,4 @@ echo "  $url"
 echo
 
 git log  $baserev..$headrev | git-shortlog ;
-git diff --stat --summary $baserev..$headrev
+git diff -M --stat --summary $baserev..$headrev

From f848718a6980ebda0eb5afb2ca49c3bc1e7b2b1d Mon Sep 17 00:00:00 2001
From: Alex Riesen <raa.lkml@gmail.com>
Date: Mon, 4 Dec 2006 10:50:04 +0100
Subject: [PATCH 088/330] Make perl/ build procedure ActiveState friendly.

On Cygwin + ActivateState Perl, Makefile generated with
MakeMaker is not usable because of line-endings and
back-slashes.

This teaches perl/Makefile to write a handcrafted equivalent
perl.mak file with 'make NO_PERL_MAKEMAKER=NoThanks'.

Signed-off-by: Alex Riesen <raa.lkml@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile        | 22 ++++++++++++++++------
 perl/.gitignore |  3 ++-
 perl/Makefile   | 39 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 57 insertions(+), 7 deletions(-)
 create mode 100644 perl/Makefile

diff --git a/Makefile b/Makefile
index de3e9f38ec..a1861de2cc 100644
--- a/Makefile
+++ b/Makefile
@@ -91,6 +91,10 @@ all:
 #
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
+#
+# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
+# MakeMaker (e.g. using ActiveState under Cygwin).
+#
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -539,6 +543,9 @@ endif
 ifdef NO_ACCURATE_DIFF
 	BASIC_CFLAGS += -DNO_ACCURATE_DIFF
 endif
+ifdef NO_PERL_MAKEMAKER
+	export NO_PERL_MAKEMAKER
+endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
@@ -568,8 +575,8 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 
 all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
 
-all: perl/Makefile
-	$(MAKE) -C perl
+all:
+	$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
 	$(MAKE) -C templates
 
 strip: $(PROGRAMS) git$X
@@ -602,7 +609,11 @@ $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	chmod +x $@+
 	mv $@+ $@
 
-$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/Makefile
+$(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
+
+perl/perl.mak: GIT-CFLAGS
+	$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' $(@F)
+
 $(patsubst %.perl,%,$(SCRIPT_PERL)): % : %.perl
 	rm -f $@ $@+
 	INSTLIBDIR=`$(MAKE) -C perl -s --no-print-directory instlibdir` && \
@@ -796,7 +807,7 @@ install: all
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 	$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
-	$(MAKE) -C perl install
+	$(MAKE) -C perl prefix='$(prefix_SQ)' install
 	if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
 	then \
 		ln -f '$(DESTDIR_SQ)$(bindir_SQ)/git$X' \
@@ -866,8 +877,7 @@ clean:
 	rm -f $(htmldocs).tar.gz $(manpages).tar.gz
 	rm -f gitweb/gitweb.cgi
 	$(MAKE) -C Documentation/ clean
-	[ ! -f perl/Makefile ] || $(MAKE) -C perl/ clean || $(MAKE) -C perl/ clean
-	rm -f perl/ppport.h perl/Makefile.old
+	$(MAKE) -C perl clean
 	$(MAKE) -C templates/ clean
 	$(MAKE) -C t/ clean
 	rm -f GIT-VERSION-FILE GIT-CFLAGS
diff --git a/perl/.gitignore b/perl/.gitignore
index e990caeea7..98b24772c7 100644
--- a/perl/.gitignore
+++ b/perl/.gitignore
@@ -1,4 +1,5 @@
-Makefile
+perl.mak
+perl.mak.old
 blib
 blibdirs
 pm_to_blib
diff --git a/perl/Makefile b/perl/Makefile
new file mode 100644
index 0000000000..bd483b0997
--- /dev/null
+++ b/perl/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile for perl support modules and routine
+#
+makfile:=perl.mak
+
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+prefix_SQ = $(subst ','\'',$(prefix))
+
+all install instlibdir: $(makfile)
+	$(MAKE) -f $(makfile) $@
+
+clean:
+	test -f $(makfile) && $(MAKE) -f $(makfile) $@ || exit 0
+	$(RM) ppport.h
+	$(RM) $(makfile)
+	$(RM) $(makfile).old
+
+ifdef NO_PERL_MAKEMAKER
+instdir_SQ = $(subst ','\'',$(prefix)/lib)
+$(makfile): ../GIT-CFLAGS Makefile
+	echo all: > $@
+	echo '	:' >> $@
+	echo install: >> $@
+	echo '	mkdir -p $(instdir_SQ)' >> $@
+	echo '	$(RM) $(instdir_SQ)/Git.pm; cp Git.pm $(instdir_SQ)' >> $@
+	echo '	$(RM) $(instdir_SQ)/Error.pm; \
+	cp private-Error.pm $(instdir_SQ)/Error.pm' >> $@
+	echo instlibdir: >> $@
+	echo '	echo $(instdir_SQ)' >> $@
+else
+$(makfile): Makefile.PL ../GIT-CFLAGS
+	'$(PERL_PATH_SQ)' $< FIRST_MAKEFILE='$@' PREFIX='$(prefix_SQ)'
+endif
+
+# this is just added comfort for calling make directly in perl dir
+# (even though GIT-CFLAGS aren't used yet. If ever)
+../GIT-CFLAGS:
+	$(MAKE) -C .. GIT-CFLAGS
+

From e1147267afc0afa269884767c4045847d9a2be8a Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Mon, 4 Dec 2006 14:09:43 +0100
Subject: [PATCH 089/330] gitweb: Fix Atom feed <logo>: it is $logo, not
 $logo_url

Fix contents of Atom feed <logo> element; it should be URL
of $logo, not URL pointed by logo link.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 093bd72058..ffe8ce13ff 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4282,7 +4282,7 @@ XML
 		}
 		if (defined $logo_url) {
 			# not twice as wide as tall: 72 x 27 pixels
-			print "<logo>" . esc_url($logo_url) . "</logo>\n";
+			print "<logo>" . esc_url($logo) . "</logo>\n";
 		}
 		if (! %latest_date) {
 			# dummy date to keep the feed valid until commits trickle in:

From b360cca0b100e14abffa4cae78521b493c783738 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Mon, 4 Dec 2006 14:29:09 +0100
Subject: [PATCH 090/330] git-clone: Rename --use-immingled-remote option to
 --no-separate-remote

With making --use-separate-remote default when creating non-bare
clone, there was need for the flag which would turn off this behavior.
It was called --use-immingled-remote.

Immingle means to blend, to combine into one, to intermingle, but it
is a bit obscure word. I think it would be better to use simply
--no-separate-remote as the opposite to --use-separate-remote
option to git clone.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-clone.txt | 4 ++--
 git-clone.sh                | 6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 4cb42237b5..d5efa00dea 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -11,7 +11,7 @@ SYNOPSIS
 [verse]
 'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
 	  [-o <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--use-separate-remote | --use-immingled-remote] <repository>
+	  [--use-separate-remote | --no-separate-remote] <repository>
 	  [<directory>]
 
 DESCRIPTION
@@ -105,7 +105,7 @@ OPTIONS
 	of `$GIT_DIR/refs/heads/`.  Only the local master branch is
 	saved in the latter. This is the default.
 
---use-immingled-remote::
+--no-separate-remote::
 	Save remotes heads in the same namespace as the local
 	heads, `$GIT_DIR/refs/heads/'.  In regular repositories,
 	this is a legacy setup git-clone created by default in
diff --git a/git-clone.sh b/git-clone.sh
index d4ee93f75b..89640396e9 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -14,7 +14,7 @@ die() {
 }
 
 usage() {
-	die "Usage: $0 [--template=<template_directory>] [--use-immingled-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+	die "Usage: $0 [--template=<template_directory>] [--no-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
 }
 
 get_repo_base() {
@@ -140,7 +140,7 @@ while
 	*,--use-separate-remote)
 		# default
 		use_separate_remote=t ;;
-	*,--use-immingled-remote)
+	*,--no-separate-remote)
 		use_separate_remote= ;;
 	1,--reference) usage ;;
 	*,--reference)
@@ -176,7 +176,7 @@ repo="$1"
 test -n "$repo" ||
     die 'you must specify a repository to clone.'
 
-# --bare implies --no-checkout and --use-immingled-remote
+# --bare implies --no-checkout and --no-separate-remote
 if test yes = "$bare"
 then
 	if test yes = "$origin_override"

From 4cd75359ad5d4c90ba6ae6d68ffb6d00e5092b8a Mon Sep 17 00:00:00 2001
From: Michael Loeffler <zvpunry@zvpunry.de>
Date: Mon, 4 Dec 2006 20:34:34 +0100
Subject: [PATCH 091/330] git-fetch: ignore dereferenced tags in
 expand_refs_wildcard

There was a little bug in the brace expansion which should remove
the ^{} from the tagname. It used ${name#'^{}'} instead of $(name%'^{}'},
the difference is that '#' will remove the given pattern only from the
beginning of a string and '%' only from the end of a string.

Signed-off-by: Michael Loeffler <zvpunry@zvpunry.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 19bc3857d1..da064a53f6 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -116,7 +116,7 @@ expand_refs_wildcard () {
 			while read sha1 name
 			do
 				mapped=${name#"$from"}
-				if test "z$name" != "z${name#'^{}'}" ||
+				if test "z$name" != "z${name%'^{}'}" ||
 					test "z$name" = "z$mapped"
 				then
 					continue

From 562cefbdbfaeb92f91c961c67960a93a7772220c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 4 Dec 2006 14:24:12 -0800
Subject: [PATCH 092/330] receive-pack: do not insist on fast-forward outside
 refs/heads/

Especially refs/tags/ hierarchy should match what git-fetch
checks.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 receive-pack.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/receive-pack.c b/receive-pack.c
index d56898c9b2..f18915117e 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -118,7 +118,8 @@ static int update(struct command *cmd)
 		return error("unpack should have generated %s, "
 			     "but I can't find it!", new_hex);
 	}
-	if (deny_non_fast_forwards && !is_null_sha1(old_sha1)) {
+	if (deny_non_fast_forwards && !is_null_sha1(old_sha1) &&
+	    !strncmp(name, "refs/heads/", 11)) {
 		struct commit *old_commit, *new_commit;
 		struct commit_list *bases, *ent;
 

From 0fb1eaa8850557249a8d1c43a4f0f3ac5a5f75ce Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 4 Dec 2006 02:11:39 -0800
Subject: [PATCH 093/330] unpack-trees: make sure "df_conflict_entry.name" is
 NUL terminated.

The structure that ends with a flexible array member (or 0
length array with older GCC) "char name[FLEX_ARRAY]" is
allocated on the stack and we use it after clearing its entire
size with memset.  That does not guarantee that "name" is
properly NUL terminated as we intended on platforms with more
forgiving structure alignment requirements.

Reported breakage on m68k by Roman Zippel.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 unpack-trees.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/unpack-trees.c b/unpack-trees.c
index 7cfd628d8e..47aa804a86 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -370,7 +370,7 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
 	int i;
 	struct object_list *posn = trees;
 	struct tree_entry_list df_conflict_list;
-	struct cache_entry df_conflict_entry;
+	static struct cache_entry *dfc;
 
 	memset(&df_conflict_list, 0, sizeof(df_conflict_list));
 	df_conflict_list.next = &df_conflict_list;
@@ -381,8 +381,10 @@ int unpack_trees(struct object_list *trees, struct unpack_trees_options *o)
 	state.refresh_cache = 1;
 
 	o->merge_size = len;
-	memset(&df_conflict_entry, 0, sizeof(df_conflict_entry));
-	o->df_conflict_entry = &df_conflict_entry;
+
+	if (!dfc)
+		dfc = xcalloc(1, sizeof(struct cache_entry) + 1);
+	o->df_conflict_entry = dfc;
 
 	if (len) {
 		posns = xmalloc(len * sizeof(struct tree_entry_list *));

From c7c24889bb694f93a12f41a28fab26d30e571c17 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sun, 3 Dec 2006 19:25:34 +0100
Subject: [PATCH 094/330] diff -b: ignore whitespace at end of line

This is _not_ the same as "treat eol as whitespace", since that would mean
that multiple empty lines would be treated as equal to e.g. a space.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t4015-diff-whitespace.sh | 6 ++----
 xdiff/xutils.c             | 3 ++-
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 1bc5b7a412..adf4993bac 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -109,12 +109,10 @@ index d99af23..8b32fb5 100644
 +	whitespace at beginning
  whitespace change
 -whitespace in the middle
--whitespace at end
 +white space in the middle
-+whitespace at end  
+ whitespace at end
  unchanged line
--CR at endQ
-+CR at end
+ CR at endQ
 EOF
 git-diff -b > out
 test_expect_success 'another test, with -b' 'diff -u expect out'
diff --git a/xdiff/xutils.c b/xdiff/xutils.c
index 9e4bb47ee9..1b899f32c4 100644
--- a/xdiff/xutils.c
+++ b/xdiff/xutils.c
@@ -230,7 +230,8 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
 			while (ptr + 1 < top && isspace(ptr[1])
 					&& ptr[1] != '\n')
 				ptr++;
-			if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+			if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+					&& ptr[1] != '\n') {
 				ha += (ha << 5);
 				ha ^= (unsigned long) ' ';
 			}

From 8ebe185bbf3f1f4f59bcc61e3d1849a76f6af983 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 3 Dec 2006 17:24:41 +0100
Subject: [PATCH 095/330] Document git-diff whitespace flags -b and -w

Document git diff options -b / --ignore-space-change and
-w / --ignore-all-space, introduced by Johannes Schindelin
in commit 0d21efa5, "Teach diff about -b and -w flags".

The description of options is taken from GNU diff man page and
GNU Diffutils info documentation.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/diff-options.txt | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index e112172ca5..9cdd171af7 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -129,5 +129,21 @@
 -a::
 	Shorthand for "--text".
 
+--ignore-space-change::
+	Ignore changes in amount of white space.  This ignores white
+	space at line end, and consider all other sequences of one or
+	more white space characters to be equivalent.
+
+-b::
+	Shorthand for "--ignore-space-change".
+
+--ignore-all-space::
+	Ignore white space when comparing lines.  This ignores
+	difference even if one line has white space where the other
+	line has none.
+
+-w::
+	Shorthand for "--ignore-all-space".
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].

From 366bfcb68f4d98a43faaf17893a1aa0a7a9e2c58 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Mon, 4 Dec 2006 11:13:39 -0500
Subject: [PATCH 096/330] make 'git add' a first class user friendly interface
 to the index

This brings the power of the index up front using a proper mental model
without talking about the index at all. See for example how all the
technical discussion has been evacuated from the git-add man page.

   Any content to be committed must be added together.  Whether that
   content comes from new files or modified files doesn't matter.  You
   just need to "add" it, either with git-add, or by providing
   git-commit with -a (for already known files only of course).

No need for a separate command to distinguish new vs modified files
please. That would only screw the mental model everybody should have
when using GIT.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-add.txt  | 53 ++++++++++++++++++++------------------
 Documentation/tutorial.txt | 46 ++++++++++++++++++++++++++++-----
 builtin-add.c              |  6 ++---
 wt-status.c                |  2 +-
 4 files changed, 72 insertions(+), 35 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 6342ea33e4..d86c0e7f19 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -3,7 +3,7 @@ git-add(1)
 
 NAME
 ----
-git-add - Add files to the index file
+git-add - Add file contents to the changeset to be committed next
 
 SYNOPSIS
 --------
@@ -11,16 +11,31 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-A simple wrapper for git-update-index to add files to the index,
-for people used to do "cvs add".
+All the changed file contents to be committed together in a single set
+of changes must be "added" with the 'add' command before using the
+'commit' command.  This is not only for adding new files.  Even modified
+files must be added to the set of changes about to be committed.
 
-It only adds non-ignored files, to add ignored files use
+This command can be performed multiple times before a commit. The added
+content corresponds to the state of specified file(s) at the time the
+'add' command is used. This means the 'commit' command will not consider
+subsequent changes to already added content if it is not added again before
+the commit.
+
+The 'git status' command can be used to obtain a summary of what is included
+for the next commit.
+
+This command only adds non-ignored files, to add ignored files use
 "git update-index --add".
 
+Please see gitlink:git-commit[1] for alternative ways to add content to a
+commit.
+
+
 OPTIONS
 -------
 <file>...::
-	Files to add to the index (see gitlink:git-ls-files[1]).
+	Files to add content from.
 
 -n::
         Don't actually add the file(s), just show if they exist.
@@ -34,27 +49,12 @@ OPTIONS
 	for command-line options).
 
 
-DISCUSSION
-----------
-
-The list of <file> given to the command is fed to `git-ls-files`
-command to list files that are not registered in the index and
-are not ignored/excluded by `$GIT_DIR/info/exclude` file or
-`.gitignore` file in each directory.  This means two things:
-
-. You can put the name of a directory on the command line, and
-  the command will add all files in it and its subdirectories;
-
-. Giving the name of a file that is already in index does not
-  run `git-update-index` on that path.
-
-
 EXAMPLES
 --------
 git-add Documentation/\\*.txt::
 
-	Adds all `\*.txt` files that are not in the index under
-	`Documentation` directory and its subdirectories.
+	Adds content from all `\*.txt` files under `Documentation`
+	directory and its subdirectories.
 +
 Note that the asterisk `\*` is quoted from the shell in this
 example; this lets the command to include the files from
@@ -62,15 +62,18 @@ subdirectories of `Documentation/` directory.
 
 git-add git-*.sh::
 
-	Adds all git-*.sh scripts that are not in the index.
+	Considers adding content from all git-*.sh scripts.
 	Because this example lets shell expand the asterisk
 	(i.e. you are listing the files explicitly), it does not
-	add `subdir/git-foo.sh` to the index.
+	consider `subdir/git-foo.sh`.
 
 See Also
 --------
+gitlink:git-status[1]
 gitlink:git-rm[1]
-gitlink:git-ls-files[1]
+gitlink:git-mv[1]
+gitlink:git-commit[1]
+gitlink:git-update-index[1]
 
 Author
 ------
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index fe4491de41..02dede320c 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -87,14 +87,48 @@ thorough description.  Tools that turn commits into email, for
 example, use the first line on the Subject line and the rest of the
 commit in the body.
 
-To add a new file, first create the file, then
 
-------------------------------------------------
-$ git add path/to/new/file
-------------------------------------------------
+Git tracks content not files
+----------------------------
 
-then commit as usual.  No special command is required when removing a
-file; just remove it, then tell `commit` about the file as usual.
+With git you have to explicitly "add" all the changed _content_ you
+want to commit together. This can be done in a few different ways:
+
+1) By using 'git add <file_spec>...'
+
+   This can be performed multiple times before a commit.  Note that this
+   is not only for adding new files.  Even modified files must be
+   added to the set of changes about to be committed.  The "git status"
+   command gives you a summary of what is included so far for the
+   next commit.  When done you should use the 'git commit' command to
+   make it real.
+
+   Note: don't forget to 'add' a file again if you modified it after the
+   first 'add' and before 'commit'. Otherwise only the previous added
+   state of that file will be committed. This is because git tracks
+   content, so what you're really 'add'ing to the commit is the *content*
+   of the file in the state it is in when you 'add' it.
+
+2) By using 'git commit -a' directly
+
+   This is a quick way to automatically 'add' the content from all files
+   that were modified since the previous commit, and perform the actual
+   commit without having to separately 'add' them beforehand.  This will
+   not add content from new files i.e. files that were never added before.
+   Those files still have to be added explicitly before performing a
+   commit.
+
+But here's a twist. If you do 'git commit <file1> <file2> ...' then only
+the  changes belonging to those explicitly specified files will be
+committed, entirely bypassing the current "added" changes. Those "added"
+changes will still remain available for a subsequent commit though.
+
+However, for normal usage you only have to remember 'git add' + 'git commit'
+and/or 'git commit -a'.
+
+
+Viewing the changelog
+---------------------
 
 At any point you can view the history of your changes using
 
diff --git a/builtin-add.c b/builtin-add.c
index febb75ed99..b3f920676a 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -94,9 +94,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 	newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
 
-	if (read_cache() < 0)
-		die("index file corrupt");
-
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
@@ -131,6 +128,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		return 0;
 	}
 
+	if (read_cache() < 0)
+		die("index file corrupt");
+
 	for (i = 0; i < dir.nr; i++)
 		add_file_to_index(dir.entries[i]->name, verbose);
 
diff --git a/wt-status.c b/wt-status.c
index de1be5bc66..4b8b5705b4 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -163,7 +163,7 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
 	int i;
 	if (q->nr)
 		wt_status_print_header("Changed but not updated",
-				"use git-update-index to mark for commit");
+				"use git-add on files to include for commit");
 	for (i = 0; i < q->nr; i++)
 		wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
 	if (q->nr)

From 98e6da8a360b77af2924e8056fd951013835699b Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 5 Dec 2006 22:15:35 +0100
Subject: [PATCH 097/330] xdl_merge(): fix and simplify conflict handling

Suppose you have changes in new1 to the original lines 10-20,
and changes in new2 to the original lines 15-25, then the
changes to 10-25 conflict. But it is possible that the next
changes in new1 still overlap with this change to new2.

So, in the next iteration we have to look at the same change
to new2 again.

The old code tried to be a bit too clever. The new code is
shorter and more to the point: do not fiddle with the ranges
at all.

Also, xdl_append_merge() tries harder to combine conflicts.
This is necessary, because with the above simplification,
some conflicts would not be recognized as conflicts otherwise:

In the above scenario, it is possible that there is no other
change to new1. Absent the combine logic, the change in new2
would be recorded _again_, but as a non-conflict.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 xdiff/xmerge.c | 21 +++++----------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/xdiff/xmerge.c b/xdiff/xmerge.c
index 1fe7a1b060..352207e516 100644
--- a/xdiff/xmerge.c
+++ b/xdiff/xmerge.c
@@ -38,8 +38,9 @@ static int xdl_append_merge(xdmerge_t **merge, int mode,
 		long i1, long chg1, long i2, long chg2)
 {
 	xdmerge_t *m = *merge;
-	if (m && mode == m->mode &&
-			(i1 == m->i1 + m->chg1 || i2 == m->i2 + m->chg2)) {
+	if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+		if (mode != m->mode)
+			m->mode = 0;
 		m->chg1 = i1 + chg1 - m->i1;
 		m->chg2 = i2 + chg2 - m->i2;
 	} else {
@@ -313,22 +314,10 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1, const char *name1,
 		i1 = xscr1->i1 + xscr1->chg1;
 		i2 = xscr2->i1 + xscr2->chg1;
 
-		if (i1 > i2) {
-			xscr1->chg1 -= i1 - i2;
-			xscr1->i1 = i2;
-			xscr1->i2 += xscr1->chg2;
-			xscr1->chg2 = 0;
+		if (i1 >= i2)
 			xscr2 = xscr2->next;
-		} else if (i2 > i1) {
-			xscr2->chg1 -= i2 - i1;
-			xscr2->i1 = i1;
-			xscr2->i2 += xscr2->chg2;
-			xscr2->chg2 = 0;
+		if (i2 >= i1)
 			xscr1 = xscr1->next;
-		} else {
-			xscr1 = xscr1->next;
-			xscr2 = xscr2->next;
-		}
 	}
 	while (xscr1) {
 		if (!changes)

From f8a9d4287277ed15d3f0d61004f4510c59f1f392 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 4 Dec 2006 16:00:46 -0800
Subject: [PATCH 098/330] read-tree: further loosen "working file will be lost"
 check.

This follows up commit ed93b449 where we removed overcautious
"working file will be lost" check.

A new option "--exclude-per-directory=.gitignore" can be used to
tell the "git-read-tree" command that the user does not mind
losing contents in untracked files in the working tree, if they
need to be overwritten by a merge (either a two-way "switch
branches" merge, or a three-way merge).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-read-tree.c         | 20 +++++++++
 dir.c                       |  4 +-
 dir.h                       |  3 ++
 t/t1004-read-tree-m-u-wf.sh | 82 +++++++++++++++++++++++++++++++++----
 unpack-trees.c              | 24 ++++++++---
 unpack-trees.h              |  1 +
 6 files changed, 119 insertions(+), 15 deletions(-)

diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index c1867d2a00..3f6cae36ce 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -10,6 +10,7 @@
 #include "tree-walk.h"
 #include "cache-tree.h"
 #include "unpack-trees.h"
+#include "dir.h"
 #include "builtin.h"
 
 static struct object_list *trees;
@@ -178,6 +179,23 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 			continue;
 		}
 
+		if (!strncmp(arg, "--exclude-per-directory=", 24)) {
+			struct dir_struct *dir;
+
+			if (opts.dir)
+				die("more than one --exclude-per-directory are given.");
+
+			dir = calloc(1, sizeof(*opts.dir));
+			dir->show_ignored = 1;
+			dir->exclude_per_dir = arg + 24;
+			opts.dir = dir;
+			/* We do not need to nor want to do read-directory
+			 * here; we are merely interested in reusing the
+			 * per directory ignore stack mechanism.
+			 */
+			continue;
+		}
+
 		/* using -u and -i at the same time makes no sense */
 		if (1 < opts.index_only + opts.update)
 			usage(read_tree_usage);
@@ -190,6 +208,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 	}
 	if ((opts.update||opts.index_only) && !opts.merge)
 		usage(read_tree_usage);
+	if ((opts.dir && !opts.update))
+		die("--exclude-per-directory is meaningless unless -u");
 
 	if (opts.prefix) {
 		int pfxlen = strlen(opts.prefix);
diff --git a/dir.c b/dir.c
index 96389b32e6..e6a61ee913 100644
--- a/dir.c
+++ b/dir.c
@@ -156,7 +156,7 @@ void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 		die("cannot use %s as an exclude file", fname);
 }
 
-static int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
+int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
 {
 	char exclude_file[PATH_MAX];
 	struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
@@ -170,7 +170,7 @@ static int push_exclude_per_directory(struct dir_struct *dir, const char *base,
 	return current_nr;
 }
 
-static void pop_exclude_per_directory(struct dir_struct *dir, int stk)
+void pop_exclude_per_directory(struct dir_struct *dir, int stk)
 {
 	struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
 
diff --git a/dir.h b/dir.h
index 313f8ab64e..550551ab25 100644
--- a/dir.h
+++ b/dir.h
@@ -43,6 +43,9 @@ extern int common_prefix(const char **pathspec);
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
 extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);
+extern int push_exclude_per_directory(struct dir_struct *, const char *, int);
+extern void pop_exclude_per_directory(struct dir_struct *, int);
+
 extern int excluded(struct dir_struct *, const char *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
diff --git a/t/t1004-read-tree-m-u-wf.sh b/t/t1004-read-tree-m-u-wf.sh
index 018fbea450..4f664f6adf 100755
--- a/t/t1004-read-tree-m-u-wf.sh
+++ b/t/t1004-read-tree-m-u-wf.sh
@@ -8,23 +8,27 @@ test_description='read-tree -m -u checks working tree files'
 
 test_expect_success 'two-way setup' '
 
+	mkdir subdir &&
 	echo >file1 file one &&
 	echo >file2 file two &&
-	git update-index --add file1 file2 &&
+	echo >subdir/file1 file one in subdirectory &&
+	echo >subdir/file2 file two in subdirectory &&
+	git update-index --add file1 file2 subdir/file1 subdir/file2 &&
 	git commit -m initial &&
 
 	git branch side &&
 	git tag -f branch-point &&
 
 	echo file2 is not tracked on the master anymore &&
-	rm -f file2 &&
-	git update-index --remove file2 &&
-	git commit -a -m "master removes file2"
+	rm -f file2 subdir/file2 &&
+	git update-index --remove file2 subdir/file2 &&
+	git commit -a -m "master removes file2 and subdir/file2"
 '
 
 test_expect_success 'two-way not clobbering' '
 
 	echo >file2 master creates untracked file2 &&
+	echo >subdir/file2 master creates untracked subdir/file2 &&
 	if err=`git read-tree -m -u master side 2>&1`
 	then
 		echo should have complained
@@ -34,20 +38,82 @@ test_expect_success 'two-way not clobbering' '
 	fi
 '
 
+echo file2 >.gitignore
+
+test_expect_success 'two-way with incorrect --exclude-per-directory (1)' '
+
+	if err=`git read-tree -m --exclude-per-directory=.gitignore master side 2>&1`
+	then
+		echo should have complained
+		false
+	else
+		echo "happy to see $err"
+	fi
+'
+
+test_expect_success 'two-way with incorrect --exclude-per-directory (2)' '
+
+	if err=`git read-tree -m -u --exclude-per-directory=foo --exclude-per-directory=.gitignore master side 2>&1`
+	then
+		echo should have complained
+		false
+	else
+		echo "happy to see $err"
+	fi
+'
+
+test_expect_success 'two-way clobbering a ignored file' '
+
+	git read-tree -m -u --exclude-per-directory=.gitignore master side
+'
+
+rm -f .gitignore
+
 # three-tree test
 
-test_expect_success 'three-way not complaining' '
+test_expect_success 'three-way not complaining on an untracked path in both' '
 
-	rm -f file2 &&
+	rm -f file2 subdir/file2 &&
 	git checkout side &&
 	echo >file3 file three &&
-	git update-index --add file3 &&
-	git commit -a -m "side adds file3" &&
+	echo >subdir/file3 file three &&
+	git update-index --add file3 subdir/file3 &&
+	git commit -a -m "side adds file3 and removes file2" &&
 
 	git checkout master &&
 	echo >file2 file two is untracked on the master side &&
+	echo >subdir/file2 file two is untracked on the master side &&
 
 	git-read-tree -m -u branch-point master side
 '
 
+test_expect_success 'three-way not cloberring a working tree file' '
+
+	git reset --hard &&
+	rm -f file2 subdir/file2 file3 subdir/file3 &&
+	git checkout master &&
+	echo >file3 file three created in master, untracked &&
+	echo >subdir/file3 file three created in master, untracked &&
+	if err=`git read-tree -m -u branch-point master side 2>&1`
+	then
+		echo should have complained
+		false
+	else
+		echo "happy to see $err"
+	fi
+'
+
+echo >.gitignore file3
+
+test_expect_success 'three-way not complaining on an untracked file' '
+
+	git reset --hard &&
+	rm -f file2 subdir/file2 file3 subdir/file3 &&
+	git checkout master &&
+	echo >file3 file three created in master, untracked &&
+	echo >subdir/file3 file three created in master, untracked &&
+
+	git read-tree -m -u --exclude-per-directory=.gitignore branch-point master side
+'
+
 test_done
diff --git a/unpack-trees.c b/unpack-trees.c
index 7cfd628d8e..79d21e2e8d 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,6 +1,7 @@
 #include <signal.h>
 #include <sys/time.h>
 #include "cache.h"
+#include "dir.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
@@ -77,6 +78,12 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 {
 	int baselen = strlen(base);
 	int src_size = len + 1;
+	int i_stk = i_stk;
+	int retval = 0;
+
+	if (o->dir)
+		i_stk = push_exclude_per_directory(o->dir, base, strlen(base));
+
 	do {
 		int i;
 		const char *first;
@@ -143,7 +150,7 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 		}
 		/* No name means we're done */
 		if (!first)
-			return 0;
+			goto leave_directory;
 
 		pathlen = strlen(first);
 		ce_size = cache_entry_size(baselen + pathlen);
@@ -240,13 +247,20 @@ static int unpack_trees_rec(struct tree_entry_list **posns, int len,
 			newbase[baselen + pathlen] = '/';
 			newbase[baselen + pathlen + 1] = '\0';
 			if (unpack_trees_rec(subposns, len, newbase, o,
-					     indpos, df_conflict_list))
-				return -1;
+					     indpos, df_conflict_list)) {
+				retval = -1;
+				goto leave_directory;
+			}
 			free(newbase);
 		}
 		free(subposns);
 		free(src);
 	} while (1);
+
+ leave_directory:
+	if (o->dir)
+		pop_exclude_per_directory(o->dir, i_stk);
+	return retval;
 }
 
 /* Unlink the last component and attempt to remove leading
@@ -456,7 +470,7 @@ static void invalidate_ce_path(struct cache_entry *ce)
 
 /*
  * We do not want to remove or overwrite a working tree file that
- * is not tracked.
+ * is not tracked, unless it is ignored.
  */
 static void verify_absent(const char *path, const char *action,
 		struct unpack_trees_options *o)
@@ -465,7 +479,7 @@ static void verify_absent(const char *path, const char *action,
 
 	if (o->index_only || o->reset || !o->update)
 		return;
-	if (!lstat(path, &st))
+	if (!lstat(path, &st) && !(o->dir && excluded(o->dir, path)))
 		die("Untracked working tree file '%s' "
 		    "would be %s by merge.", path, action);
 }
diff --git a/unpack-trees.h b/unpack-trees.h
index c4601621cd..191f7442f1 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -16,6 +16,7 @@ struct unpack_trees_options {
 	int verbose_update;
 	int aggressive;
 	const char *prefix;
+	struct dir_struct *dir;
 	merge_fn_t fn;
 
 	int head_idx;

From 1127148089234a6f84754f2f0ec36cbbcae06fa9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 4 Dec 2006 16:07:57 -0800
Subject: [PATCH 099/330] Loosen "working file will be lost" check in
 Porcelain-ish

This uses the previous update to read-tree in Porcelain-ish
commands "git checkout" and "git merge" to loosen the check
when switching branches.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-checkout.sh | 5 +++--
 git-merge.sh    | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/git-checkout.sh b/git-checkout.sh
index 737abd0c09..4192a99fec 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -161,7 +161,7 @@ then
     git-read-tree --reset -u $new
 else
     git-update-index --refresh >/dev/null
-    merge_error=$(git-read-tree -m -u $old $new 2>&1) || (
+    merge_error=$(git-read-tree -m -u --exclude-per-directory=.gitignore $old $new 2>&1) || (
 	case "$merge" in
 	'')
 		echo >&2 "$merge_error"
@@ -172,7 +172,8 @@ else
     	git diff-files --name-only | git update-index --remove --stdin &&
 	work=`git write-tree` &&
 	git read-tree --reset -u $new &&
-	git read-tree -m -u --aggressive $old $new $work || exit
+	git read-tree -m -u --aggressive --exclude-per-directory=.gitignore $old $new $work ||
+	exit
 
 	if result=`git write-tree 2>/dev/null`
 	then
diff --git a/git-merge.sh b/git-merge.sh
index 272f004622..397b33f8d3 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -264,7 +264,7 @@ f,*)
 	echo "Updating $(git-rev-parse --short $head)..$(git-rev-parse --short $1)"
 	git-update-index --refresh 2>/dev/null
 	new_head=$(git-rev-parse --verify "$1^0") &&
-	git-read-tree -u -v -m $head "$new_head" &&
+	git-read-tree -v -m -u --exclude-per-directory=.gitignore $head "$new_head" &&
 	finish "$new_head" "Fast forward"
 	dropsave
 	exit 0

From 22f741dab7cc93f00f7cf96cd65d7481677cd855 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 5 Dec 2006 23:44:23 -0800
Subject: [PATCH 100/330] read-tree: document --exclude-per-directory

This documents the new option to read-tree that is used for the
improved "branch switching" code.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-read-tree.txt | 16 +++++++++++++++-
 builtin-read-tree.c             |  2 +-
 2 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 11bd9c0adc..0ff2890c7f 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,7 +8,7 @@ git-read-tree - Reads tree information into the index
 
 SYNOPSIS
 --------
-'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git-read-tree' (<tree-ish> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
 
 
 DESCRIPTION
@@ -71,6 +71,20 @@ OPTIONS
 	directory.  Note that the `<prefix>/` value must end
 	with a slash.
 
+--exclude-per-directory=<gitignore>::
+	When running the command with `-u` and `-m` options, the
+	merge result may need to overwrite paths that are not
+	tracked in the current branch.  The command usually
+	refuses to proceed with the merge to avoid losing such a
+	path.  However this safety valve sometimes gets in the
+	way.  For example, it often happens that the other
+	branch added a file that used to be a generated file in
+	your branch, and the safety valve triggers when you try
+	to switch to that branch after you ran `make` but before
+	running `make clean` to remove the generated file.  This
+	option tells the command to read per-directory exclude
+	file (usually '.gitignore') and allows such an untracked
+	but explicitly ignored file to be overwritten.
 
 <tree-ish#>::
 	The id of the tree object(s) to be read/merged.
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 3f6cae36ce..8ba436dbac 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -85,7 +85,7 @@ static void prime_cache_tree(void)
 
 }
 
-static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] <sha1> [<sha2> [<sha3>]])";
+static const char read_tree_usage[] = "git-read-tree (<sha> | [[-m [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] <sha1> [<sha2> [<sha3>]])";
 
 static struct lock_file lock_file;
 

From c976d415e5352886f0650f8e2edba81866c38587 Mon Sep 17 00:00:00 2001
From: Lars Hjemli <hjemli@gmail.com>
Date: Tue, 28 Nov 2006 15:47:40 +0100
Subject: [PATCH 101/330] git-branch: add options and tests for branch renaming

Extend git-branch with the following options:

  git-branch -m|-M [<oldbranch>] newbranch

The -M variation is required to force renaming over an exsisting
branchname.

This also indroduces $GIT_DIR/RENAME_REF which is a "metabranch"
used when renaming branches. It will always hold the original sha1
for the latest renamed branch.

Additionally, if $GIT_DIR/logs/RENAME_REF exists, all branch rename
events are logged there.

Finally, some testcases are added to verify the new options.

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-branch.txt |  23 ++++-
 builtin-branch.c             |  47 +++++++++-
 refs.c                       | 169 ++++++++++++++++++++++++++++++-----
 refs.h                       |   3 +
 t/t3200-branch.sh            |  34 +++++++
 5 files changed, 250 insertions(+), 26 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 4f5b5d5028..71417feba8 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -8,8 +8,9 @@ git-branch - List, create, or delete branches.
 SYNOPSIS
 --------
 [verse]
-'git-branch' [-r] [-a] [-v] [--abbrev=<length>]
+'git-branch' [-r | -a] [-v [--abbrev=<length>]]
 'git-branch' [-l] [-f] <branchname> [<start-point>]
+'git-branch' (-m | -M) [<oldbranch>] <newbranch>
 'git-branch' (-d | -D) <branchname>...
 
 DESCRIPTION
@@ -24,6 +25,12 @@ It will start out with a head equal to the one given as <start-point>.
 If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
 
+With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
+If <oldbranch> had a corresponding reflog, it is renamed to match
+<newbranch>, and a reflog entry is created to remember the branch
+renaming. If <newbranch> exists, -M must be used to force the rename
+to happen.
+
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
 has a ref log then the ref log will also be deleted.
@@ -46,6 +53,12 @@ OPTIONS
 	Force the creation of a new branch even if it means deleting
 	a branch that already exists with the same name.
 
+-m::
+	Move/rename a branch and the corresponding reflog.
+
+-M::
+	Move/rename a branch even if the new branchname already exists.
+
 -r::
 	List the remote-tracking branches.
 
@@ -53,7 +66,7 @@ OPTIONS
 	List both remote-tracking branches and local branches.
 
 -v::
-	Show sha1 and subject message for each head.
+	Show sha1 and commit subjectline for each head.
 
 --abbrev=<length>::
 	Alter minimum display length for sha1 in output listing,
@@ -70,6 +83,12 @@ OPTIONS
 	be given as a branch name, a commit-id, or a tag.  If this option
 	is omitted, the current branch is assumed.
 
+<oldbranch>::
+	The name of an existing branch to rename.
+
+<newbranch>::
+	The new name for an existing branch. The same restrictions as for
+	<branchname> applies.
 
 
 Examples
diff --git a/builtin-branch.c b/builtin-branch.c
index 3d5cb0e4b2..153682601e 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -11,7 +11,7 @@
 #include "builtin.h"
 
 static const char builtin_branch_usage[] =
-"git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | [-r | -a] [-v] [--abbrev=<length>] ";
+  "git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [-r | -a] [-v [--abbrev=<length>]]";
 
 
 static const char *head;
@@ -245,9 +245,37 @@ static void create_branch(const char *name, const char *start,
 		die("Failed to write ref: %s.", strerror(errno));
 }
 
+static void rename_branch(const char *oldname, const char *newname, int force)
+{
+	char oldref[PATH_MAX], newref[PATH_MAX];
+	unsigned char sha1[20];
+
+	if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
+		die("Old branchname too long");
+
+	if (check_ref_format(oldref))
+		die("Invalid branch name: %s", oldref);
+
+	if (snprintf(newref, sizeof(newref), "refs/heads/%s", newname) > sizeof(newref))
+		die("New branchname too long");
+
+	if (check_ref_format(newref))
+		die("Invalid branch name: %s", newref);
+
+	if (resolve_ref(newref, sha1, 1, NULL) && !force)
+		die("A branch named '%s' already exists.", newname);
+
+	if (rename_ref(oldref, newref))
+		die("Branch rename failed");
+
+	if (!strcmp(oldname, head) && create_symref("HEAD", newref))
+		die("Branch renamed to %s, but HEAD is not updated!", newname);
+}
+
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
 	int delete = 0, force_delete = 0, force_create = 0;
+	int rename = 0, force_rename = 0;
 	int verbose = 0, abbrev = DEFAULT_ABBREV;
 	int reflog = 0;
 	int kinds = REF_LOCAL_BRANCH;
@@ -277,6 +305,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			force_create = 1;
 			continue;
 		}
+		if (!strcmp(arg, "-m")) {
+			rename = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-M")) {
+			rename = 1;
+			force_rename = 1;
+			continue;
+		}
 		if (!strcmp(arg, "-r")) {
 			kinds = REF_REMOTE_BRANCH;
 			continue;
@@ -300,6 +337,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		usage(builtin_branch_usage);
 	}
 
+	if ((delete && rename) || (delete && force_create) ||
+	    (rename && force_create))
+		usage(builtin_branch_usage);
+
 	head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
 	if (!head)
 		die("Failed to resolve HEAD as a valid ref.");
@@ -311,6 +352,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 		delete_branches(argc - i, argv + i, force_delete);
 	else if (i == argc)
 		print_ref_list(kinds, verbose, abbrev);
+	else if (rename && (i == argc - 1))
+		rename_branch(head, argv[i], force_rename);
+	else if (rename && (i == argc - 2))
+		rename_branch(argv[i], argv[i + 1], force_rename);
 	else if (i == argc - 1)
 		create_branch(argv[i], head, force_create, reflog);
 	else if (i == argc - 2)
diff --git a/refs.c b/refs.c
index 96ea8b6907..cdedb45f00 100644
--- a/refs.c
+++ b/refs.c
@@ -610,6 +610,29 @@ static int remove_empty_directories(char *file)
 	return remove_empty_dir_recursive(path, len);
 }
 
+static int is_refname_available(const char *ref, const char *oldref,
+				struct ref_list *list, int quiet)
+{
+	int namlen = strlen(ref); /* e.g. 'foo/bar' */
+	while (list) {
+		/* list->name could be 'foo' or 'foo/bar/baz' */
+		if (!oldref || strcmp(oldref, list->name)) {
+			int len = strlen(list->name);
+			int cmplen = (namlen < len) ? namlen : len;
+			const char *lead = (namlen < len) ? list->name : ref;
+			if (!strncmp(ref, list->name, cmplen) &&
+			    lead[cmplen] == '/') {
+				if (!quiet)
+					error("'%s' exists; cannot create '%s'",
+					      list->name, ref);
+				return 0;
+			}
+		}
+		list = list->next;
+	}
+	return 1;
+}
+
 static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)
 {
 	char *ref_file;
@@ -643,29 +666,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char
 			orig_ref, strerror(errno));
 		goto error_return;
 	}
-	if (is_null_sha1(lock->old_sha1)) {
-		/* The ref did not exist and we are creating it.
-		 * Make sure there is no existing ref that is packed
-		 * whose name begins with our refname, nor a ref whose
-		 * name is a proper prefix of our refname.
-		 */
-		int namlen = strlen(ref); /* e.g. 'foo/bar' */
-		struct ref_list *list = get_packed_refs();
-		while (list) {
-			/* list->name could be 'foo' or 'foo/bar/baz' */
-			int len = strlen(list->name);
-			int cmplen = (namlen < len) ? namlen : len;
-			const char *lead = (namlen < len) ? list->name : ref;
-
-			if (!strncmp(ref, list->name, cmplen) &&
-			    lead[cmplen] == '/') {
-				error("'%s' exists; cannot create '%s'",
-				      list->name, ref);
-				goto error_return;
-			}
-			list = list->next;
-		}
-	}
+	/* When the ref did not exist and we are creating it,
+	 * make sure there is no existing ref that is packed
+	 * whose name begins with our refname, nor a ref whose
+	 * name is a proper prefix of our refname.
+	 */
+	if (is_null_sha1(lock->old_sha1) &&
+            !is_refname_available(ref, NULL, get_packed_refs(), 0))
+		goto error_return;
 
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
 
@@ -776,6 +784,121 @@ int delete_ref(const char *refname, unsigned char *sha1)
 	return ret;
 }
 
+int rename_ref(const char *oldref, const char *newref)
+{
+	static const char renamed_ref[] = "RENAMED-REF";
+	unsigned char sha1[20], orig_sha1[20];
+	int flag = 0, logmoved = 0;
+	struct ref_lock *lock;
+	char msg[PATH_MAX*2 + 100];
+	struct stat loginfo;
+	int log = !stat(git_path("logs/%s", oldref), &loginfo);
+
+	if (S_ISLNK(loginfo.st_mode))
+		return error("reflog for %s is a symlink", oldref);
+
+	if (!resolve_ref(oldref, orig_sha1, 1, &flag))
+		return error("refname %s not found", oldref);
+
+	if (!is_refname_available(newref, oldref, get_packed_refs(), 0))
+		return 1;
+
+	if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
+		return 1;
+
+	if (snprintf(msg, sizeof(msg), "renamed %s to %s", oldref, newref) > sizeof(msg))
+		return error("Refnames to long");
+
+	lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
+	if (!lock)
+		return error("unable to lock %s", renamed_ref);
+	lock->force_write = 1;
+	if (write_ref_sha1(lock, orig_sha1, msg))
+		return error("unable to save current sha1 in %s", renamed_ref);
+
+	if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
+		return error("unable to move logfile logs/%s to tmp-renamed-log: %s",
+			oldref, strerror(errno));
+
+	if (delete_ref(oldref, orig_sha1)) {
+		error("unable to delete old %s", oldref);
+		goto rollback;
+	}
+
+	if (resolve_ref(newref, sha1, 1, &flag) && delete_ref(newref, sha1)) {
+		if (errno==EISDIR) {
+			if (remove_empty_directories(git_path("%s", newref))) {
+				error("Directory not empty: %s", newref);
+				goto rollback;
+			}
+		} else {
+			error("unable to delete existing %s", newref);
+			goto rollback;
+		}
+	}
+
+	if (log && safe_create_leading_directories(git_path("logs/%s", newref))) {
+		error("unable to create directory for %s", newref);
+		goto rollback;
+	}
+
+ retry:
+	if (log && rename(git_path("tmp-renamed-log"), git_path("logs/%s", newref))) {
+		if (errno==EISDIR) {
+			if (remove_empty_directories(git_path("logs/%s", newref))) {
+				error("Directory not empty: logs/%s", newref);
+				goto rollback;
+			}
+			goto retry;
+		} else {
+			error("unable to move logfile tmp-renamed-log to logs/%s: %s",
+				newref, strerror(errno));
+			goto rollback;
+		}
+	}
+	logmoved = log;
+
+	lock = lock_ref_sha1_basic(newref, NULL, NULL);
+	if (!lock) {
+		error("unable to lock %s for update", newref);
+		goto rollback;
+	}
+
+	lock->force_write = 1;
+	hashcpy(lock->old_sha1, orig_sha1);
+	if (write_ref_sha1(lock, orig_sha1, msg)) {
+		error("unable to write current sha1 into %s", newref);
+		goto rollback;
+	}
+
+	return 0;
+
+ rollback:
+	lock = lock_ref_sha1_basic(oldref, NULL, NULL);
+	if (!lock) {
+		error("unable to lock %s for rollback", oldref);
+		goto rollbacklog;
+	}
+
+	lock->force_write = 1;
+	flag = log_all_ref_updates;
+	log_all_ref_updates = 0;
+	if (write_ref_sha1(lock, orig_sha1, NULL))
+		error("unable to write current sha1 into %s", oldref);
+	log_all_ref_updates = flag;
+
+ rollbacklog:
+	if (logmoved && rename(git_path("logs/%s", newref), git_path("logs/%s", oldref)))
+		error("unable to restore logfile %s from %s: %s",
+			oldref, newref, strerror(errno));
+	if (!logmoved && log &&
+	    rename(git_path("tmp-renamed-log"), git_path("logs/%s", oldref)))
+		error("unable to restore logfile %s from tmp-renamed-log: %s",
+			oldref, strerror(errno));
+
+	return 1;
+}
+
 void unlock_ref(struct ref_lock *lock)
 {
 	if (lock->lock_fd >= 0) {
diff --git a/refs.h b/refs.h
index cd1e1d620e..ce73d5c7ae 100644
--- a/refs.h
+++ b/refs.h
@@ -47,4 +47,7 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
 
+/** rename ref, return 0 on success **/
+extern int rename_ref(const char *oldref, const char *newref);
+
 #endif /* REFS_H */
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index acb54b6a07..afaa8536a9 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -70,4 +70,38 @@ test_expect_success \
         git-branch -d l/m &&
         git-branch l'
 
+test_expect_success \
+    'git branch -m m m/m should work' \
+       'git-branch -l m &&
+        git-branch -m m m/m &&
+        test -f .git/logs/refs/heads/m/m'
+
+test_expect_success \
+    'git branch -m n/n n should work' \
+       'git-branch -l n/n &&
+        git-branch -m n/n n
+        test -f .git/logs/refs/heads/n'
+
+test_expect_failure \
+    'git branch -m o/o o should fail when o/p exists' \
+       'git-branch o/o &&
+        git-branch o/p &&
+        git-branch -m o/o o'
+
+test_expect_failure \
+    'git branch -m q r/q should fail when r exists' \
+       'git-branch q &&
+         git-branch r &&
+         git-branch -m q r/q'
+
+test_expect_success \
+    'git branch -m s/s s should work when s/t is deleted' \
+       'git-branch -l s/s &&
+        test -f .git/logs/refs/heads/s/s &&
+        git-branch -l s/t &&
+        test -f .git/logs/refs/heads/s/t &&
+        git-branch -d s/t &&
+        git-branch -m s/s s &&
+        test -f .git/logs/refs/heads/s'
+
 test_done

From 16c2bfbb449a90db00a46984d7dd7f735caa1d56 Mon Sep 17 00:00:00 2001
From: Lars Hjemli <hjemli@gmail.com>
Date: Wed, 29 Nov 2006 21:44:56 +0100
Subject: [PATCH 102/330] rename_ref: use lstat(2) when testing for symlink

The current check for symlinked reflogs was based on stat(2), which is
utterly embarrassing.

Fix it, and add a matching testcase.

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c            | 2 +-
 t/t3200-branch.sh | 7 +++++++
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/refs.c b/refs.c
index cdedb45f00..c23561e158 100644
--- a/refs.c
+++ b/refs.c
@@ -792,7 +792,7 @@ int rename_ref(const char *oldref, const char *newref)
 	struct ref_lock *lock;
 	char msg[PATH_MAX*2 + 100];
 	struct stat loginfo;
-	int log = !stat(git_path("logs/%s", oldref), &loginfo);
+	int log = !lstat(git_path("logs/%s", oldref), &loginfo);
 
 	if (S_ISLNK(loginfo.st_mode))
 		return error("reflog for %s is a symlink", oldref);
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index afaa8536a9..5782c30b03 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -104,4 +104,11 @@ test_expect_success \
         git-branch -m s/s s &&
         test -f .git/logs/refs/heads/s'
 
+test_expect_failure \
+    'git-branch -m u v should fail when the reflog for u is a symlink' \
+    'git-branch -l u &&
+     mv .git/logs/refs/heads/u real-u &&
+     ln -s real-u .git/logs/refs/heads/u &&
+     git-branch -m u v'
+
 test_done

From 678d0f4cbfa7a3b529c6e894f2977bef6a2d3e4c Mon Sep 17 00:00:00 2001
From: Lars Hjemli <hjemli@gmail.com>
Date: Thu, 30 Nov 2006 03:16:56 +0100
Subject: [PATCH 103/330] git-branch: let caller specify logmsg

This changes the signature of rename_ref() in refs.[hc] to include a
logmessage for the reflogs.

Also, builtin-branch.c is modified to provide a proper logmessage + call
setup_ident() before any logmessages are written.

Signed-off-by: Lars Hjemli <hjemli@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c |  8 ++++++--
 refs.c           | 10 +++-------
 refs.h           |  2 +-
 3 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 153682601e..3fc6f84773 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -247,7 +247,7 @@ static void create_branch(const char *name, const char *start,
 
 static void rename_branch(const char *oldname, const char *newname, int force)
 {
-	char oldref[PATH_MAX], newref[PATH_MAX];
+	char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
 	unsigned char sha1[20];
 
 	if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
@@ -265,7 +265,10 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 	if (resolve_ref(newref, sha1, 1, NULL) && !force)
 		die("A branch named '%s' already exists.", newname);
 
-	if (rename_ref(oldref, newref))
+	snprintf(logmsg, sizeof(logmsg), "Branch: renamed %s to %s",
+		 oldref, newref);
+
+	if (rename_ref(oldref, newref, logmsg))
 		die("Branch rename failed");
 
 	if (!strcmp(oldname, head) && create_symref("HEAD", newref))
@@ -281,6 +284,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	int kinds = REF_LOCAL_BRANCH;
 	int i;
 
+	setup_ident();
 	git_config(git_default_config);
 
 	for (i = 1; i < argc; i++) {
diff --git a/refs.c b/refs.c
index c23561e158..2ac8273ea4 100644
--- a/refs.c
+++ b/refs.c
@@ -784,13 +784,12 @@ int delete_ref(const char *refname, unsigned char *sha1)
 	return ret;
 }
 
-int rename_ref(const char *oldref, const char *newref)
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 {
 	static const char renamed_ref[] = "RENAMED-REF";
 	unsigned char sha1[20], orig_sha1[20];
 	int flag = 0, logmoved = 0;
 	struct ref_lock *lock;
-	char msg[PATH_MAX*2 + 100];
 	struct stat loginfo;
 	int log = !lstat(git_path("logs/%s", oldref), &loginfo);
 
@@ -806,14 +805,11 @@ int rename_ref(const char *oldref, const char *newref)
 	if (!is_refname_available(newref, oldref, get_loose_refs(), 0))
 		return 1;
 
-	if (snprintf(msg, sizeof(msg), "renamed %s to %s", oldref, newref) > sizeof(msg))
-		return error("Refnames to long");
-
 	lock = lock_ref_sha1_basic(renamed_ref, NULL, NULL);
 	if (!lock)
 		return error("unable to lock %s", renamed_ref);
 	lock->force_write = 1;
-	if (write_ref_sha1(lock, orig_sha1, msg))
+	if (write_ref_sha1(lock, orig_sha1, logmsg))
 		return error("unable to save current sha1 in %s", renamed_ref);
 
 	if (log && rename(git_path("logs/%s", oldref), git_path("tmp-renamed-log")))
@@ -866,7 +862,7 @@ int rename_ref(const char *oldref, const char *newref)
 
 	lock->force_write = 1;
 	hashcpy(lock->old_sha1, orig_sha1);
-	if (write_ref_sha1(lock, orig_sha1, msg)) {
+	if (write_ref_sha1(lock, orig_sha1, logmsg)) {
 		error("unable to write current sha1 into %s", newref);
 		goto rollback;
 	}
diff --git a/refs.h b/refs.h
index ce73d5c7ae..51aab1e6bf 100644
--- a/refs.h
+++ b/refs.h
@@ -48,6 +48,6 @@ extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned
 extern int check_ref_format(const char *target);
 
 /** rename ref, return 0 on success **/
-extern int rename_ref(const char *oldref, const char *newref);
+extern int rename_ref(const char *oldref, const char *newref, const char *logmsg);
 
 #endif /* REFS_H */

From 3a9f1a55eec9cc508abccda6a3fee795b812d66d Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 6 Dec 2006 13:27:40 +0100
Subject: [PATCH 104/330] cvs-migration document: make the need for "push" more
 obvious

It really is an important concept to grasp for people coming
from CVS. Even if it is briefly mentioned, it is not obvious
enough to sink in.

[jc: with wording updates from J. Bruce Fields]

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/cvs-migration.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 6812683a16..9c2a395e5a 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -24,6 +24,11 @@ First, note some ways that git differs from CVS:
     single shared repository which people can synchronize with; see below
     for details.
 
+  * Since every working tree contains a repository, a commit in your
+    private repository will not publish your changes; it will only create
+    a revision. You have to "push" your changes to a public repository to
+    make them visible to others.
+
 Importing a CVS archive
 -----------------------
 

From 4003a58e415e5f51a3becac0079505b72299a7bc Mon Sep 17 00:00:00 2001
From: "J. Bruce Fields" <bfields@fieldses.org>
Date: Wed, 6 Dec 2006 12:19:50 -0500
Subject: [PATCH 105/330] cvs-migration: improved section titles, better
 push/commit explanation

Rename the section titles to make the "how-to" content of the section
obvious.  Also clarify that changes have to be commited before they can
be pushed.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/cvs-migration.txt | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 9c2a395e5a..a436180dd4 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -81,8 +81,8 @@ variants of this model.
 With a small group, developers may just pull changes from each other's
 repositories without the need for a central maintainer.
 
-Emulating the CVS Development Model
------------------------------------
+Creating a Shared Repository
+----------------------------
 
 Start with an ordinary git working directory containing the project, and
 remove the checked-out files, keeping just the bare .git directory:
@@ -110,7 +110,10 @@ $ GIT_DIR=repo.git git repo-config core.sharedrepository true
 Make sure committers have a umask of at most 027, so that the directories
 they create are writable and searchable by other group members.
 
-Suppose this repository is now set up in /pub/repo.git on the host
+Performing Development on a Shared Repository
+---------------------------------------------
+
+Suppose a repository is now set up in /pub/repo.git on the host
 foo.com.  Then as an individual committer you can clone the shared
 repository:
 
@@ -139,15 +142,17 @@ Pull: master:origin
 ------------
 ================================
 
-You can update the shared repository with your changes using:
+You can update the shared repository with your changes by first commiting
+your changes, and then using:
 
 ------------------------------------------------
 $ git push origin master
 ------------------------------------------------
 
-If someone else has updated the repository more recently, `git push`, like
-`cvs commit`, will complain, in which case you must pull any changes
-before attempting the push again.
+to "push" those commits to the shared repository.  If someone else has
+updated the repository more recently, `git push`, like `cvs commit`, will
+complain, in which case you must pull any changes before attempting the
+push again.
 
 In the `git push` command above we specify the name of the remote branch
 to update (`master`).  If we leave that out, `git push` tries to update

From ba1f5f353775ddbf97bc0d543888783630693023 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 6 Dec 2006 16:26:06 +0100
Subject: [PATCH 106/330] Add builtin merge-file, a minimal replacement for RCS
 merge

merge-file has the same syntax as RCS merge, but supports only the
"-L" option.

For good measure, a test is added, which is quite minimal, though.

[jc: further fix for compliation errors included.]

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile              |   1 +
 builtin-merge-file.c  |  72 ++++++++++++++++++++++++++
 builtin.h             |   1 +
 git.c                 |   1 +
 t/t6023-merge-file.sh | 116 ++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 191 insertions(+)
 create mode 100644 builtin-merge-file.c
 create mode 100644 t/t6023-merge-file.sh

diff --git a/Makefile b/Makefile
index 23bbb90683..79cb91feec 100644
--- a/Makefile
+++ b/Makefile
@@ -275,6 +275,7 @@ BUILTIN_OBJS = \
 	builtin-ls-tree.o \
 	builtin-mailinfo.o \
 	builtin-mailsplit.o \
+	builtin-merge-file.o \
 	builtin-mv.o \
 	builtin-name-rev.o \
 	builtin-pack-objects.o \
diff --git a/builtin-merge-file.c b/builtin-merge-file.c
new file mode 100644
index 0000000000..41fb92edfb
--- /dev/null
+++ b/builtin-merge-file.c
@@ -0,0 +1,72 @@
+#include "cache.h"
+#include "xdiff/xdiff.h"
+
+static const char merge_file_usage[] =
+"git merge-file [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+
+static int read_file(mmfile_t *ptr, const char *filename)
+{
+	struct stat st;
+	FILE *f;
+
+	if (stat(filename, &st))
+		return error("Could not stat %s", filename);
+	if ((f = fopen(filename, "rb")) == NULL)
+		return error("Could not open %s", filename);
+	ptr->ptr = xmalloc(st.st_size);
+	if (fread(ptr->ptr, st.st_size, 1, f) != 1)
+		return error("Could not read %s", filename);
+	fclose(f);
+	ptr->size = st.st_size;
+	return 0;
+}
+
+int cmd_merge_file(int argc, char **argv, char **envp)
+{
+	char *names[3];
+	mmfile_t mmfs[3];
+	mmbuffer_t result = {NULL, 0};
+	xpparam_t xpp = {XDF_NEED_MINIMAL};
+	int ret = 0, i = 0;
+
+	while (argc > 4) {
+		if (!strcmp(argv[1], "-L")) {
+			names[i++] = argv[2];
+			argc -= 2;
+			argv += 2;
+			continue;
+		}
+		usage(merge_file_usage);
+	}
+
+	if (argc != 4)
+		usage(merge_file_usage);
+
+	for (; i < 3; i++)
+		names[i] = argv[i + 1];
+
+	for (i = 0; i < 3; i++)
+		if (read_file(mmfs + i, argv[i + 1]))
+			return -1;
+
+	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
+			&xpp, XDL_MERGE_ZEALOUS, &result);
+
+	for (i = 0; i < 3; i++)
+		free(mmfs[i].ptr);
+
+	if (ret >= 0) {
+		char *filename = argv[1];
+		FILE *f = fopen(filename, "wb");
+
+		if (!f)
+			ret = error("Could not open %s for writing", filename);
+		else if (fwrite(result.ptr, result.size, 1, f) != 1)
+			ret = error("Could not write to %s", filename);
+		else if (fclose(f))
+			ret = error("Could not close %s", filename);
+		free(result.ptr);
+	}
+
+	return ret;
+}
diff --git a/builtin.h b/builtin.h
index b5116f30e4..08519e7c82 100644
--- a/builtin.h
+++ b/builtin.h
@@ -42,6 +42,7 @@ extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
 extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
+extern int cmd_merge_file(int argc, const char **argv, const char *prefix);
 extern int cmd_mv(int argc, const char **argv, const char *prefix);
 extern int cmd_name_rev(int argc, const char **argv, const char *prefix);
 extern int cmd_pack_objects(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index 357330e02c..016ee8adb7 100644
--- a/git.c
+++ b/git.c
@@ -247,6 +247,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "ls-tree", cmd_ls_tree, RUN_SETUP },
 		{ "mailinfo", cmd_mailinfo },
 		{ "mailsplit", cmd_mailsplit },
+		{ "merge-file", cmd_merge_file },
 		{ "mv", cmd_mv, RUN_SETUP },
 		{ "name-rev", cmd_name_rev, RUN_SETUP },
 		{ "pack-objects", cmd_pack_objects, RUN_SETUP },
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
new file mode 100644
index 0000000000..5d9b6f34b8
--- /dev/null
+++ b/t/t6023-merge-file.sh
@@ -0,0 +1,116 @@
+#!/bin/sh
+
+test_description='RCS merge replacement: merge-file'
+. ./test-lib.sh
+
+cat > orig.txt << EOF
+Dominus regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+
+cat > new1.txt << EOF
+Dominus regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+cat > new2.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+
+cat > new3.txt << EOF
+DOMINUS regit me,
+et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+EOF
+
+cat > new4.txt << EOF
+Dominus regit me, et nihil mihi deerit.
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+EOF
+echo -n "propter nomen suum." >> new4.txt
+
+cp new1.txt test.txt
+test_expect_success "merge without conflict" \
+	"git-merge-file test.txt orig.txt new2.txt"
+
+cp new1.txt test2.txt
+test_expect_success "merge without conflict (missing LF at EOF)" \
+	"git-merge-file test2.txt orig.txt new2.txt"
+
+test_expect_success "merge result added missing LF" \
+	"diff -u test.txt test2.txt"
+
+cp test.txt backup.txt
+test_expect_failure "merge with conflicts" \
+	"git-merge-file test.txt orig.txt new3.txt"
+
+cat > expect.txt << EOF
+<<<<<<< test.txt
+Dominus regit me, et nihil mihi deerit.
+=======
+DOMINUS regit me,
+et nihil mihi deerit.
+>>>>>>> new3.txt
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success "expected conflict markers" "diff -u test.txt expect.txt"
+
+cp backup.txt test.txt
+test_expect_failure "merge with conflicts, using -L" \
+	"git-merge-file -L 1 -L 2 test.txt orig.txt new3.txt"
+
+cat > expect.txt << EOF
+<<<<<<< 1
+Dominus regit me, et nihil mihi deerit.
+=======
+DOMINUS regit me,
+et nihil mihi deerit.
+>>>>>>> new3.txt
+In loco pascuae ibi me collocavit,
+super aquam refectionis educavit me;
+animam meam convertit,
+deduxit me super semitas jusitiae,
+propter nomen suum.
+Nam et si ambulavero in medio umbrae mortis,
+non timebo mala, quoniam tu mecum es:
+virga tua et baculus tuus ipsa me consolata sunt.
+EOF
+
+test_expect_success "expected conflict markers, with -L" \
+	"diff -u test.txt expect.txt"
+
+test_done
+

From fbe0b24ca53111208b8ecd920cc112d0be13f93c Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 6 Dec 2006 16:45:42 +0100
Subject: [PATCH 107/330] merge-file: support -p and -q; fix compile warnings

Now merge-file also understands --stdout and --quiet options. While
at it, two compile warnings were fixed.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-merge-file.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 41fb92edfb..6c4c3a3513 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -2,7 +2,7 @@
 #include "xdiff/xdiff.h"
 
 static const char merge_file_usage[] =
-"git merge-file [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
+"git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
 static int read_file(mmfile_t *ptr, const char *filename)
 {
@@ -27,16 +27,23 @@ int cmd_merge_file(int argc, char **argv, char **envp)
 	mmfile_t mmfs[3];
 	mmbuffer_t result = {NULL, 0};
 	xpparam_t xpp = {XDF_NEED_MINIMAL};
-	int ret = 0, i = 0;
+	int ret = 0, i = 0, to_stdout = 0;
 
 	while (argc > 4) {
-		if (!strcmp(argv[1], "-L")) {
+		if (!strcmp(argv[1], "-L") && i < 3) {
 			names[i++] = argv[2];
-			argc -= 2;
-			argv += 2;
-			continue;
-		}
-		usage(merge_file_usage);
+			argc--;
+			argv++;
+		} else if (!strcmp(argv[1], "-p") ||
+				!strcmp(argv[1], "--stdout"))
+			to_stdout = 1;
+		else if (!strcmp(argv[1], "-q") ||
+				!strcmp(argv[1], "--quiet"))
+			freopen("/dev/null", "w", stderr);
+		else
+			usage(merge_file_usage);
+		argc--;
+		argv++;
 	}
 
 	if (argc != 4)
@@ -57,7 +64,7 @@ int cmd_merge_file(int argc, char **argv, char **envp)
 
 	if (ret >= 0) {
 		char *filename = argv[1];
-		FILE *f = fopen(filename, "wb");
+		FILE *f = to_stdout ? stdout : fopen(filename, "wb");
 
 		if (!f)
 			ret = error("Could not open %s for writing", filename);

From 49ed2bc4660c7cd0592cf21cc514080574d06320 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 4 Dec 2006 19:44:40 -0800
Subject: [PATCH 108/330] git-reset to remove "$GIT_DIR/MERGE_MSG"

An earlier commit a9cb3c6e changed git-commit to use the
contents of MERGE_MSG even when we do not have MERGE_HEAD (the
rationale is in its log message).

However, the change tricks the following sequence to include a
merge message in a completely unrelated commit:

	$ git pull somewhere
	: oops, the conflicts are too much.  forget it.
        $ git reset --hard
        : work work work
        $ git commit

To fix this confusion, this patch makes "git reset" to remove
the leftover MERGE_MSG that was prepared when the user abandoned
the merge.

Signed-off-by: Junio C Hamano <junkio@cox.net>
Acked-by: Luben Tuikov <ltuikov@yahoo.com
---
 git-reset.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/git-reset.sh b/git-reset.sh
index 3133b5bd25..c0feb4435d 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -63,6 +63,7 @@ case "$reset_type" in
 	;;
 esac
 
-rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" \
+	"$GIT_DIR/SQUASH_MSG" "$GIT_DIR/MERGE_MSG"
 
 exit $update_ref_status

From 211e6eb513f372ad79fecc3db19413b3573ae962 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 6 Dec 2006 10:52:04 -0800
Subject: [PATCH 109/330] git-merge: squelch needless error message.

While deciding if the new style command line argument is a tag
or a branch, we checked it with "git show-ref -s --verify" to
see if results in an error, but when it is not a branch, the
check leaked the error message out, which was not needed to be
shown to the end user.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-merge.sh b/git-merge.sh
index 272f004622..efdbabf775 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -189,7 +189,7 @@ else
 	merge_name=$(for remote
 		do
 			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
-			bh=$(git show-ref -s --verify "refs/heads/$remote") &&
+			bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) &&
 			if test "$rh" = "$bh"
 			then
 				echo "$rh		branch '$remote' of ."

From 5a4cf3346d6c37007a7f5f94697868a5b2f2fa29 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Mon, 4 Dec 2006 23:47:22 +0100
Subject: [PATCH 110/330] gitweb: Allow PNG, GIF, JPEG images to be displayed
 in "blob" view

Allow images in one of web formats (PNG, GIF, JPEG) - actually files
with mimetype of image/png, image/git, image/jpeg - to be displayed in
"blob" view using <img /> element, instead of using "blob_plain" view
for them, like for all other files except also text/* mimetype files.

This makes possible to easily go to file history, to HEAD version of
the file, to appropriate commit etc; all of those are not available
in "blob_plain" (raw) view.

Only text files can have "blame" view link in the formats part of
navbar.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index ffe8ce13ff..61e2ab2900 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3229,10 +3229,13 @@ sub git_blob {
 	open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
 		or die_error(undef, "Couldn't cat $file_name, $hash");
 	my $mimetype = blob_mimetype($fd, $file_name);
-	if ($mimetype !~ m/^text\//) {
+	if ($mimetype !~ m!^(?:text/|image/(?:gif|png|jpeg)$)!) {
 		close $fd;
 		return git_blob_plain($mimetype);
 	}
+	# we can have blame only for text/* mimetype
+	$have_blame &&= ($mimetype =~ m!^text/!);
+
 	git_header_html(undef, $expires);
 	my $formats_nav = '';
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
@@ -3269,13 +3272,24 @@ sub git_blob {
 	}
 	git_print_page_path($file_name, "blob", $hash_base);
 	print "<div class=\"page_body\">\n";
-	my $nr;
-	while (my $line = <$fd>) {
-		chomp $line;
-		$nr++;
-		$line = untabify($line);
-		printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
-		       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+	if ($mimetype =~ m!^text/!) {
+		my $nr;
+		while (my $line = <$fd>) {
+			chomp $line;
+			$nr++;
+			$line = untabify($line);
+			printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n",
+			       $nr, $nr, $nr, esc_html($line, -nbsp=>1);
+		}
+	} elsif ($mimetype =~ m!^image/!) {
+		print qq!<img type="$mimetype"!;
+		if ($file_name) {
+			print qq! alt="$file_name" title="$file_name"!;
+		}
+		print qq! src="! .
+		      href(action=>"blob_plain", hash=>$hash,
+		           hash_base=>$hash_base, file_name=>$file_name) .
+		      qq!" />\n!;
 	}
 	close $fd
 		or print "Reading blob failed.\n";

From ebdf7b952215946eff863e4da28f924178acea4f Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Mon, 4 Dec 2006 00:51:16 -0800
Subject: [PATCH 111/330] git-svn: avoid network timeouts for long-running
 fetches

Long-running fetches run inside children to avoid memory leaks.
When we refork, the connection in the parent can be idle for a
long time; attempting to reuse it in the next child can result
in timeouts.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/git-svn.perl b/git-svn.perl
index d0bd0bdeb8..747daf0181 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -459,6 +459,7 @@ sub fetch_lib {
 		$min = $max + 1;
 		$max += $inc;
 		$max = $head if ($max > $head);
+		$SVN = libsvn_connect($SVN_URL);
 	}
 	restore_index($index);
 	return { revision => $last_rev, commit => $last_commit };

From de51faf3888505fa3d661d4c35f32ecaf9fa1087 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 6 Dec 2006 11:22:55 -0800
Subject: [PATCH 112/330] git-merge: fix "fix confusion between tag and branch"
 for real

An earlier commit 3683dc5a broke the merge message generation with
a careless use of && where it was not needed, breaking the merge
message for cases where non branches are given.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index efdbabf775..a948878b91 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -188,8 +188,9 @@ else
 	# in this loop.
 	merge_name=$(for remote
 		do
-			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) &&
-			bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null) &&
+			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
+			continue ;# not something we can merge
+			bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
 			if test "$rh" = "$bh"
 			then
 				echo "$rh		branch '$remote' of ."

From 955289bf92f0513377763c9aacfe426d5151b05a Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Wed, 6 Dec 2006 12:07:23 +0000
Subject: [PATCH 113/330] Explicitly add the default "git pull" behaviour to
 .git/config on clone

Without any specification in the .git/config file, git-pull will execute
"git-pull origin"; which in turn defaults to pull from the first "pull"
definition for the remote, "origin".

This is a difficult set of defaults to track for a new user, and it's
difficult to see what tells git to do this (especially when it is
actually hard-coded behaviour).  To ameliorate this slightly, this patch
explicitly specifies the default behaviour during a clone using the
"branch" section of the config.

For example, a clone of a typical repository would create a .git/config
containing:
  [remote "origin"]
  url = proto://host/repo.git
  fetch = refs/heads/master:refs/remotes/origin/master
  [branch "master"]
  remote = origin
  merge = refs/heads/master

The [branch "master"] section is such that there is no change to the
functionality of git-pull, but that functionality is now explicitly
documented.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
---
 git-clone.sh | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/git-clone.sh b/git-clone.sh
index 0ace989fde..1f5d07a057 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -400,7 +400,9 @@ then
 			rm -f "refs/remotes/$origin/HEAD"
 			git-symbolic-ref "refs/remotes/$origin/HEAD" \
 				"refs/remotes/$origin/$head_points_at"
-		esac
+		esac &&
+		git-repo-config branch."$head_points_at".remote "$origin" &&
+		git-repo-config branch."$head_points_at".merge "refs/heads/$head_points_at"
 	esac
 
 	case "$no_checkout" in

From cd976f5c52694acb4b23c3f2425ed4f0a47ec799 Mon Sep 17 00:00:00 2001
From: "J. Bruce Fields" <bfields@fieldses.org>
Date: Wed, 6 Dec 2006 23:18:05 -0500
Subject: [PATCH 114/330] Documentation: reorganize cvs-migration.txt

Modify cvs-migration.txt so it explains first how to develop against a
shared repository, then how to set up a shared repository, then how to
import a repository from cvs.  Though this seems chronologically
backwards, it's still readable in this order, and it puts the more
commonly needed material closer to the front.

Remove the annotate/pickaxe section; perhaps it can find a place elsewhere
in the future.  Remove most of the "why git is better than cvs" stuff from
the introduction.

Add some minor clarifications, including two that have come up several
times on the mailing list:

	1. Recommend committing any changes before running pull.
	2. Note that changes must be commited before they can be pushed.

Update the clone discussion to reflect the new --use-separate-remotes
default, and add a brief mention of git-cvsserver.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/cvs-migration.txt | 371 ++++++++++----------------------
 1 file changed, 115 insertions(+), 256 deletions(-)

diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index a436180dd4..47846bdab2 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -1,33 +1,106 @@
 git for CVS users
 =================
 
-So you're a CVS user. That's OK, it's a treatable condition.  The job of
-this document is to put you on the road to recovery, by helping you
-convert an existing cvs repository to git, and by showing you how to use a
-git repository in a cvs-like fashion.
+Git differs from CVS in that every working tree contains a repository with
+a full copy of the project history, and no repository is inherently more
+important than any other.  However, you can emulate the CVS model by
+designating a single shared repository which people can synchronize with;
+this document explains how to do that.
 
 Some basic familiarity with git is required.  This
 link:tutorial.html[tutorial introduction to git] should be sufficient.
 
-First, note some ways that git differs from CVS:
+Developing against a shared repository
+--------------------------------------
 
-  * Commits are atomic and project-wide, not per-file as in CVS.
+Suppose a shared repository is set up in /pub/repo.git on the host
+foo.com.  Then as an individual committer you can clone the shared
+repository over ssh with:
 
-  * Offline work is supported: you can make multiple commits locally,
-    then submit them when you're ready.
+------------------------------------------------
+$ git clone foo.com:/pub/repo.git/ my-project
+$ cd my-project
+------------------------------------------------
 
-  * Branching is fast and easy.
+and hack away.  The equivalent of `cvs update` is
 
-  * Every working tree contains a repository with a full copy of the
-    project history, and no repository is inherently more important than
-    any other.  However, you can emulate the CVS model by designating a
-    single shared repository which people can synchronize with; see below
-    for details.
+------------------------------------------------
+$ git pull origin
+------------------------------------------------
 
-  * Since every working tree contains a repository, a commit in your
-    private repository will not publish your changes; it will only create
-    a revision. You have to "push" your changes to a public repository to
-    make them visible to others.
+which merges in any work that others might have done since the clone
+operation.  If there are uncommitted changes in your working tree, commit
+them first before running git pull.
+
+[NOTE]
+================================
+The first `git clone` places the following in the
+`my-project/.git/remotes/origin` file, and that's why the previous step
+and the next step both work.
+------------
+URL: foo.com:/pub/project.git/
+Pull: refs/heads/master:refs/remotes/origin/master
+------------
+================================
+
+You can update the shared repository with your changes by first commiting
+your changes, and then using:
+
+------------------------------------------------
+$ git push origin master
+------------------------------------------------
+
+to "push" those commits to the shared repository.  If someone else has
+updated the repository more recently, `git push`, like `cvs commit`, will
+complain, in which case you must pull any changes before attempting the
+push again.
+
+In the `git push` command above we specify the name of the remote branch
+to update (`master`).  If we leave that out, `git push` tries to update
+any branches in the remote repository that have the same name as a branch
+in the local repository.  So the last `push` can be done with either of:
+
+------------
+$ git push origin
+$ git push foo.com:/pub/project.git/
+------------
+
+as long as the shared repository does not have any branches
+other than `master`.
+
+Setting Up a Shared Repository
+------------------------------
+
+We assume you have already created a git repository for your project,
+possibly created from scratch or from a tarball (see the
+link:tutorial.html[tutorial]), or imported from an already existing CVS
+repository (see the next section).
+
+If your project's working directory is /home/alice/myproject, you can
+create a shared repository at /pub/repo.git with:
+
+------------------------------------------------
+$ git clone -bare /home/alice/myproject /pub/repo.git
+------------------------------------------------
+
+Next, give every team member read/write access to this repository.  One
+easy way to do this is to give all the team members ssh access to the
+machine where the repository is hosted.  If you don't want to give them a
+full shell on the machine, there is a restricted shell which only allows
+users to do git pushes and pulls; see gitlink:git-shell[1].
+
+Put all the committers in the same group, and make the repository
+writable by that group:
+
+------------------------------------------------
+$ cd /pub
+$ chgrp -R $group repo.git
+$ find repo.git -mindepth 1 -type d |xargs chmod ug+rwx,g+s
+$ GIT_DIR=repo.git git repo-config core.sharedrepository true
+------------------------------------------------
+
+Make sure committers have a umask of at most 027, so that the directories
+they create are writable and searchable by other group members.
 
 Importing a CVS archive
 -----------------------
@@ -60,14 +133,32 @@ work, you must not modify the imported branches; instead, create new
 branches for your own changes, and merge in the imported branches as
 necessary.
 
-Development Models
-------------------
+Advanced Shared Repository Management
+-------------------------------------
+
+Git allows you to specify scripts called "hooks" to be run at certain
+points.  You can use these, for example, to send all commits to the shared
+repository to a mailing list.  See link:hooks.html[Hooks used by git].
+
+You can enforce finer grained permissions using update hooks.  See
+link:howto/update-hook-example.txt[Controlling access to branches using
+update hooks].
+
+Providing CVS Access to a git Repository
+----------------------------------------
+
+It is also possible to provide true CVS access to a git repository, so
+that developers can still use CVS; see gitlink:git-cvsserver[1] for
+details.
+
+Alternative Development Models
+------------------------------
 
 CVS users are accustomed to giving a group of developers commit access to
-a common repository.  In the next section we'll explain how to do this
-with git.  However, the distributed nature of git allows other development
-models, and you may want to first consider whether one of them might be a
-better fit for your project.
+a common repository.  As we've seen, this is also possible with git.
+However, the distributed nature of git allows other development models,
+and you may want to first consider whether one of them might be a better
+fit for your project.
 
 For example, you can choose a single person to maintain the project's
 primary public repository.  Other developers then clone this repository
@@ -80,235 +171,3 @@ variants of this model.
 
 With a small group, developers may just pull changes from each other's
 repositories without the need for a central maintainer.
-
-Creating a Shared Repository
-----------------------------
-
-Start with an ordinary git working directory containing the project, and
-remove the checked-out files, keeping just the bare .git directory:
-
-------------------------------------------------
-$ mv project/.git /pub/repo.git
-$ rm -r project/
-------------------------------------------------
-
-Next, give every team member read/write access to this repository.  One
-easy way to do this is to give all the team members ssh access to the
-machine where the repository is hosted.  If you don't want to give them a
-full shell on the machine, there is a restricted shell which only allows
-users to do git pushes and pulls; see gitlink:git-shell[1].
-
-Put all the committers in the same group, and make the repository
-writable by that group:
-
-------------------------------------------------
-$ chgrp -R $group repo.git
-$ find repo.git -mindepth 1 -type d |xargs chmod ug+rwx,g+s
-$ GIT_DIR=repo.git git repo-config core.sharedrepository true
-------------------------------------------------
-
-Make sure committers have a umask of at most 027, so that the directories
-they create are writable and searchable by other group members.
-
-Performing Development on a Shared Repository
----------------------------------------------
-
-Suppose a repository is now set up in /pub/repo.git on the host
-foo.com.  Then as an individual committer you can clone the shared
-repository:
-
-------------------------------------------------
-$ git clone foo.com:/pub/repo.git/ my-project
-$ cd my-project
-------------------------------------------------
-
-and hack away.  The equivalent of `cvs update` is
-
-------------------------------------------------
-$ git pull origin
-------------------------------------------------
-
-which merges in any work that others might have done since the clone
-operation.
-
-[NOTE]
-================================
-The first `git clone` places the following in the
-`my-project/.git/remotes/origin` file, and that's why the previous step
-and the next step both work.
-------------
-URL: foo.com:/pub/project.git/ my-project
-Pull: master:origin
-------------
-================================
-
-You can update the shared repository with your changes by first commiting
-your changes, and then using:
-
-------------------------------------------------
-$ git push origin master
-------------------------------------------------
-
-to "push" those commits to the shared repository.  If someone else has
-updated the repository more recently, `git push`, like `cvs commit`, will
-complain, in which case you must pull any changes before attempting the
-push again.
-
-In the `git push` command above we specify the name of the remote branch
-to update (`master`).  If we leave that out, `git push` tries to update
-any branches in the remote repository that have the same name as a branch
-in the local repository.  So the last `push` can be done with either of:
-
-------------
-$ git push origin
-$ git push repo.shared.xz:/pub/scm/project.git/
-------------
-
-as long as the shared repository does not have any branches
-other than `master`.
-
-[NOTE]
-============
-Because of this behavior, if the shared repository and the developer's
-repository both have branches named `origin`, then a push like the above
-attempts to update the `origin` branch in the shared repository from the
-developer's `origin` branch.  The results may be unexpected, so it's
-usually best to remove any branch named `origin` from the shared
-repository.
-============
-
-Advanced Shared Repository Management
--------------------------------------
-
-Git allows you to specify scripts called "hooks" to be run at certain
-points.  You can use these, for example, to send all commits to the shared
-repository to a mailing list.  See link:hooks.html[Hooks used by git].
-
-You can enforce finer grained permissions using update hooks.  See
-link:howto/update-hook-example.txt[Controlling access to branches using
-update hooks].
-
-CVS annotate
-------------
-
-So, something has gone wrong, and you don't know whom to blame, and
-you're an ex-CVS user and used to do "cvs annotate" to see who caused
-the breakage. You're looking for the "git annotate", and it's just
-claiming not to find such a script. You're annoyed.
-
-Yes, that's right.  Core git doesn't do "annotate", although it's
-technically possible, and there are at least two specialized scripts out
-there that can be used to get equivalent information (see the git
-mailing list archives for details). 
-
-git has a couple of alternatives, though, that you may find sufficient
-or even superior depending on your use.  One is called "git-whatchanged"
-(for obvious reasons) and the other one is called "pickaxe" ("a tool for
-the software archaeologist"). 
-
-The "git-whatchanged" script is a truly trivial script that can give you
-a good overview of what has changed in a file or a directory (or an
-arbitrary list of files or directories).  The "pickaxe" support is an
-additional layer that can be used to further specify exactly what you're
-looking for, if you already know the specific area that changed.
-
-Let's step back a bit and think about the reason why you would
-want to do "cvs annotate a-file.c" to begin with.
-
-You would use "cvs annotate" on a file when you have trouble
-with a function (or even a single "if" statement in a function)
-that happens to be defined in the file, which does not do what
-you want it to do.  And you would want to find out why it was
-written that way, because you are about to modify it to suit
-your needs, and at the same time you do not want to break its
-current callers.  For that, you are trying to find out why the
-original author did things that way in the original context.
-
-Many times, it may be enough to see the commit log messages of
-commits that touch the file in question, possibly along with the
-patches themselves, like this:
-
-	$ git-whatchanged -p a-file.c
-
-This will show log messages and patches for each commit that
-touches a-file.
-
-This, however, may not be very useful when this file has many
-modifications that are not related to the piece of code you are
-interested in.  You would see many log messages and patches that
-do not have anything to do with the piece of code you are
-interested in.  As an example, assuming that you have this piece
-of code that you are interested in in the HEAD version:
-
-	if (frotz) {
-		nitfol();
-	}
-
-you would use git-rev-list and git-diff-tree like this:
-
-	$ git-rev-list HEAD |
-	  git-diff-tree --stdin -v -p -S'if (frotz) {
-		nitfol();
-	}'
-
-We have already talked about the "\--stdin" form of git-diff-tree
-command that reads the list of commits and compares each commit
-with its parents (otherwise you should go back and read the tutorial).
-The git-whatchanged command internally runs
-the equivalent of the above command, and can be used like this:
-
-	$ git-whatchanged -p -S'if (frotz) {
-		nitfol();
-	}'
-
-When the -S option is used, git-diff-tree command outputs
-differences between two commits only if one tree has the
-specified string in a file and the corresponding file in the
-other tree does not.  The above example looks for a commit that
-has the "if" statement in it in a file, but its parent commit
-does not have it in the same shape in the corresponding file (or
-the other way around, where the parent has it and the commit
-does not), and the differences between them are shown, along
-with the commit message (thanks to the -v flag).  It does not
-show anything for commits that do not touch this "if" statement.
-
-Also, in the original context, the same statement might have
-appeared at first in a different file and later the file was
-renamed to "a-file.c".  CVS annotate would not help you to go
-back across such a rename, but git would still help you in such
-a situation.  For that, you can give the -C flag to
-git-diff-tree, like this:
-
-	$ git-whatchanged -p -C -S'if (frotz) {
-		nitfol();
-	}'
-
-When the -C flag is used, file renames and copies are followed.
-So if the "if" statement in question happens to be in "a-file.c"
-in the current HEAD commit, even if the file was originally
-called "o-file.c" and then renamed in an earlier commit, or if
-the file was created by copying an existing "o-file.c" in an
-earlier commit, you will not lose track.  If the "if" statement
-did not change across such a rename or copy, then the commit that
-does rename or copy would not show in the output, and if the
-"if" statement was modified while the file was still called
-"o-file.c", it would find the commit that changed the statement
-when it was in "o-file.c".
-
-NOTE: The current version of "git-diff-tree -C" is not eager
-  enough to find copies, and it will miss the fact that a-file.c
-  was created by copying o-file.c unless o-file.c was somehow
-  changed in the same commit.
-
-You can use the --pickaxe-all flag in addition to the -S flag.
-This causes the differences from all the files contained in
-those two commits, not just the differences between the files
-that contain this changed "if" statement:
-
-	$ git-whatchanged -p -C -S'if (frotz) {
-		nitfol();
-	}' --pickaxe-all
-
-NOTE: This option is called "--pickaxe-all" because -S
-  option is internally called "pickaxe", a tool for software
-  archaeologists.

From 46732fae3d049254f4f12b8a716cf56159277eda Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Wed, 6 Dec 2006 23:01:00 -0500
Subject: [PATCH 115/330] change the unpack limit treshold to a saner value

Currently the treshold is 5000.  The likelihood of this value to ever be
crossed for a single push is really small making it not really useful.

The optimal treshold for a pure space saving on a filesystem with 4kb
blocks is 3.  However this is likely to create many small packs
concentrating a large number of files in a single directory compared to
the same objects which are spread over 256 directories when loose.  This
means we would need 512 objects per pack on average to approximagte the
same directory cost (a pack has 2 files because of the index).

But 512 is a really high value just like 5000 since most pushes are
unlikely to have that many objects.  So let's try with a value of 100
which should have a good balance between small pushes going to be
exploded into loose objects and large pushes kept as whole packs.

This is not a replacement for periodic repacks of course.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 receive-pack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/receive-pack.c b/receive-pack.c
index a20bc924d6..e76d9aea31 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -11,7 +11,7 @@
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
 static int deny_non_fast_forwards = 0;
-static int unpack_limit = 5000;
+static int unpack_limit = 100;
 static int report_status;
 
 static char capabilities[] = " report-status delete-refs ";

From 4f88d3e0cbf443cd309c2c881209f3366f14023d Mon Sep 17 00:00:00 2001
From: Martin Langhoff <martin@catalyst.net.nz>
Date: Thu, 7 Dec 2006 16:38:50 +1300
Subject: [PATCH 116/330] cvsserver: Avoid miscounting bytes in Perl v5.8.x

At some point between v5.6 and 5.8 Perl started to assume its input,
output and filehandles are UTF-8. This breaks the counting of bytes
for the CVS protocol, resulting in the client expecting less data
than we actually send, and storing truncated files.

Signed-off-by: Martin Langhoff <martin@catalyst.net.nz>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-cvsserver.perl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ca519b7e49..197014d9e6 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -17,6 +17,7 @@
 
 use strict;
 use warnings;
+use bytes;
 
 use Fcntl;
 use File::Temp qw/tempdir tempfile/;

From db9819a40a56b4747931e637c1c22a104dcab902 Mon Sep 17 00:00:00 2001
From: "J. Bruce Fields" <bfields@fieldses.org>
Date: Fri, 8 Dec 2006 01:27:21 -0500
Subject: [PATCH 117/330] Documentation: update git-clone man page with new
 behavior

Update git-clone man page to reflect recent changes
(--use-separate-remote default and use of .git/config instead of
remotes files), and rewrite introduction.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-clone.txt | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index d5efa00dea..985043faca 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -16,22 +16,21 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Clones a repository into a newly created directory.  All remote
-branch heads are copied under `$GIT_DIR/refs/heads/`, except
-that the remote `master` is also copied to `origin` branch.
 
-In addition, `$GIT_DIR/remotes/origin` file is set up to have
-this line:
+Clones a repository into a newly created directory, creates
+remote-tracking branches for each branch in the cloned repository
+(visible using `git branch -r`), and creates and checks out a master
+branch equal to the cloned repository's master branch.
 
-	Pull: master:origin
-
-This is to help the typical workflow of working off of the
-remote `master` branch.  Every time `git pull` without argument
-is run, the progress on the remote `master` branch is tracked by
-copying it into the local `origin` branch, and merged into the
-branch you are currently working on.  Remote branches other than
-`master` are also added there to be tracked.
+After the clone, a plain `git fetch` without arguments will update
+all the remote-tracking branches, and a `git pull` without
+arguments will in addition merge the remote master branch into the
+current branch.
 
+This default configuration is achieved by creating references to
+the remote branch heads under `$GIT_DIR/refs/remotes/origin` and
+by initializing `remote.origin.url` and `remote.origin.fetch`
+configuration variables.
 
 OPTIONS
 -------

From 006ede5e860717ff1ec68125393bcd4e74507e5b Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 8 Dec 2006 01:55:19 -0800
Subject: [PATCH 118/330] git-svn: extra error check to ensure we open a file
 correctly

This may be an issue with repositories imported with commit
27a1a8014b842c0d70fdc91c68dd361ca2dfb34c or later, but before
commit dad73c0bb9f33323ec1aacf560a6263f1d85f81a.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/git-svn.perl b/git-svn.perl
index 747daf0181..ff61b9201f 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -3438,6 +3438,9 @@ sub open_file {
 	my ($self, $path, $pb, $rev) = @_;
 	my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
 	                     =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
+	unless (defined $mode && defined $blob) {
+		die "$path was not found in commit $self->{c} (r$rev)\n";
+	}
 	{ path => $path, mode_a => $mode, mode_b => $mode, blob => $blob,
 	  pool => SVN::Pool->new, action => 'M' };
 }

From bbee1d971dc07c29f840b439aa2a2c890a12cf9f Mon Sep 17 00:00:00 2001
From: Uwe Zeisberger <zeisberg@informatik.uni-freiburg.de>
Date: Fri, 8 Dec 2006 12:44:31 +0100
Subject: [PATCH 119/330] Fix documentation copy&paste typo
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This was introduced in 45a3b12cfd3eaa05bbb0954790d5be5b8240a7b5

Signed-off-by: Uwe Kleine-K,AC6(Bnig <zeisberg@informatik.uni-freiburg.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 61e2ab2900..5ea3fda540 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -120,7 +120,7 @@ our %feature = (
 	# To disable system wide have in $GITWEB_CONFIG
 	# $feature{'snapshot'}{'default'} = [undef];
 	# To have project specific config enable override in $GITWEB_CONFIG
-	# $feature{'blame'}{'override'} = 1;
+	# $feature{'snapshot'}{'override'} = 1;
 	# and in project config gitweb.snapshot = none|gzip|bzip2;
 	'snapshot' => {
 		'sub' => \&feature_snapshot,

From a552db3a64464f1b514b074fbc43aaf599106087 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 8 Dec 2006 02:20:17 -0800
Subject: [PATCH 120/330] git-svn: use do_switch for --follow-parent if the SVN
 library supports it

do_switch works with the SVN Perl bindings after r22312 in the
Subversion trunk.  Since no released version of SVN currently
supports it; we'll just autodetect it and enable its usage
when a user has a recent-enough version of SVN.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 46 +++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 39 insertions(+), 7 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index ff61b9201f..1f8a3b0e07 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -72,7 +72,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
 	$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
 	$_pager, $_color);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
-my ($_svn_co_url_revs, $_svn_pg_peg_revs);
+my ($_svn_co_url_revs, $_svn_pg_peg_revs, $_svn_can_do_switch);
 my @repo_path_split_cache;
 
 my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
@@ -2877,6 +2877,24 @@ sub libsvn_connect {
 	return $ra;
 }
 
+sub libsvn_can_do_switch {
+	unless (defined $_svn_can_do_switch) {
+		my $pool = SVN::Pool->new;
+		my $rep = eval {
+			$SVN->do_switch(1, '', 0, $SVN->{url},
+			                SVN::Delta::Editor->new, $pool);
+		};
+		if ($@) {
+			$_svn_can_do_switch = 0;
+		} else {
+			$rep->abort_report($pool);
+			$_svn_can_do_switch = 1;
+		}
+		$pool->clear;
+	}
+	$_svn_can_do_switch;
+}
+
 sub libsvn_dup_ra {
 	my ($ra) = @_;
 	SVN::Ra->new(map { $_ => $ra->{$_} } qw/config url
@@ -3198,12 +3216,26 @@ sub libsvn_find_parent_branch {
 		unlink $GIT_SVN_INDEX;
 		print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
 		sys(qw/git-read-tree/, $parent);
-		# I can't seem to get do_switch() to work correctly with
-		# the SWIG interface (TypeError when passing switch_url...),
-		# so we'll unconditionally bypass the delta interface here
-		# for now
-		return libsvn_fetch_full($parent, $paths, $rev,
-					$author, $date, $msg);
+		unless (libsvn_can_do_switch()) {
+			return libsvn_fetch_full($parent, $paths, $rev,
+						$author, $date, $msg);
+		}
+		# do_switch works with svn/trunk >= r22312, but that is not
+		# included with SVN 1.4.2 (the latest version at the moment),
+		# so we can't rely on it.
+		my $ra = libsvn_connect("$url/$branch_from");
+		my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q});
+		my $pool = SVN::Pool->new;
+		my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url},
+		                              $ed, $pool);
+		my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+		$reporter->set_path('', $r0, 0, @lock, $pool);
+		$reporter->finish_report($pool);
+		$pool->clear;
+		unless ($ed->{git_commit_ok}) {
+			die "SVN connection failed somewhere...\n";
+		}
+		return libsvn_log_entry($rev, $author, $date, $msg, [$parent]);
 	}
 	print STDERR "Nope, branch point not imported or unknown\n";
 	return undef;

From 2cdf87ebd9976d98d544669d94b111fea731d2ba Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 8 Dec 2006 14:07:45 -0800
Subject: [PATCH 121/330] Fix perl/ build.

An earlier commit f848718a broke the build in perl/ directory by
allowing the Makefile.PL to overwrite the now-tracked Makefile.
Fix this by forcing Makefile.PL to produce its output in
perl.mak as the broken commit originally intended.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 perl/Makefile    | 2 +-
 perl/Makefile.PL | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/perl/Makefile b/perl/Makefile
index bd483b0997..099beda873 100644
--- a/perl/Makefile
+++ b/perl/Makefile
@@ -29,7 +29,7 @@ $(makfile): ../GIT-CFLAGS Makefile
 	echo '	echo $(instdir_SQ)' >> $@
 else
 $(makfile): Makefile.PL ../GIT-CFLAGS
-	'$(PERL_PATH_SQ)' $< FIRST_MAKEFILE='$@' PREFIX='$(prefix_SQ)'
+	'$(PERL_PATH_SQ)' $< PREFIX='$(prefix_SQ)'
 endif
 
 # this is just added comfort for calling make directly in perl dir
diff --git a/perl/Makefile.PL b/perl/Makefile.PL
index de73235e4c..41687757a7 100644
--- a/perl/Makefile.PL
+++ b/perl/Makefile.PL
@@ -24,5 +24,6 @@ WriteMakefile(
 	NAME            => 'Git',
 	VERSION_FROM    => 'Git.pm',
 	PM		=> \%pm,
+	MAKEFILE	=> 'perl.mak',
 	%extra
 );

From 62b339a544b1fa5199de7571c460d770cb286e65 Mon Sep 17 00:00:00 2001
From: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
Date: Sat, 9 Dec 2006 02:28:26 +0100
Subject: [PATCH 122/330] Add branch.*.merge warning and documentation update

This patch clarifies the meaning of the branch.*.merge option.
Previously, if branch.*.merge was specified but did not match any
ref, the message "No changes." was not really helpful regarding
the misconfiguration. This patch adds a warning for this.

Signed-off-by: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt | 11 +++++++++--
 git-parse-remote.sh      | 11 +++++++++++
 2 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 9090762819..21ec55797b 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -125,10 +125,17 @@ apply.whitespace::
 
 branch.<name>.remote::
 	When in branch <name>, it tells `git fetch` which remote to fetch.
+	If this option is not given, `git fetch` defaults to remote "origin".
 
 branch.<name>.merge::
-	When in branch <name>, it tells `git fetch` the default remote branch
-	to be merged.
+	When in branch <name>, it tells `git fetch` the default refspec to
+	be marked for merging in FETCH_HEAD. The value has exactly to match
+	a remote part of one of the refspecs which are fetched from the remote
+	given by "branch.<name>.remote".
+	The merge information is used by `git pull` (which at first calls
+	`git fetch`) to lookup the default branch for merging. Without
+	this option, `git pull` defaults to merge the first refspec fetched.
+	Specify multiple values to get an octopus merge.
 
 pager.color::
 	A boolean to enable/disable colored output when the pager is in
diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index da064a53f6..6ae534bf89 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -134,6 +134,8 @@ canon_refs_list_for_fetch () {
 	# or the first one otherwise; add prefix . to the rest
 	# to prevent the secondary branches to be merged by default.
 	merge_branches=
+	found_mergeref=
+	curr_branch=
 	if test "$1" = "-d"
 	then
 		shift ; remote="$1" ; shift
@@ -171,6 +173,10 @@ canon_refs_list_for_fetch () {
 			    dot_prefix= && break
 			done
 		fi
+		if test -z $dot_prefix
+		then
+			found_mergeref=true
+		fi
 		case "$remote" in
 		'') remote=HEAD ;;
 		refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@@ -191,6 +197,11 @@ canon_refs_list_for_fetch () {
 		fi
 		echo "${dot_prefix}${force}${remote}:${local}"
 	done
+	if test -z "$found_mergeref" -a "$curr_branch"
+	then
+		echo >&2 "Warning: No merge candidate found because value of config option
+         \"branch.${curr_branch}.merge\" does not match any remote branch fetched."
+	fi
 }
 
 # Returns list of src: (no store), or src:dst (store)

From 90ffefe564cd849f88b1d1b5817eb25e3d57521b Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Fri, 8 Dec 2006 23:04:21 -0500
Subject: [PATCH 123/330] shortlog: fix segfault on empty authorname

The old code looked backwards from the email address to parse the name,
allowing an arbitrary number of spaces between the two. However, in the case
of no name, we looked back too far to the 'author' (or 'Author:') header.
Instead, remove at most one space between name and address.

The bug was triggered by commit febf7ea4bed from linux-2.6.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index f1124e261b..7a2ddfe797 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -188,7 +188,7 @@ static void read_from_stdin(struct path_list *list)
 				bob = buffer + strlen(buffer);
 			else {
 				offset = 8;
-				while (isspace(bob[-1]))
+				if (isspace(bob[-1]))
 					bob--;
 			}
 
@@ -236,7 +236,7 @@ static void get_from_rev(struct rev_info *rev, struct path_list *list)
 					author = scratch;
 					authorlen = strlen(scratch);
 				} else {
-					while (bracket[-1] == ' ')
+					if (bracket[-1] == ' ')
 						bracket--;
 
 					author = buffer + 7;

From 4cfeccc75d6ab1ccc433770bac6bf3b15ab486d6 Mon Sep 17 00:00:00 2001
From: "J. Bruce Fields" <bfields@fieldses.org>
Date: Fri, 8 Dec 2006 22:58:50 -0500
Subject: [PATCH 124/330] Documentation: simpler shared repository creation

Take Johannes Schindelin's suggestions for a further simplification of
the shared repository creation using git --bare init-db --shared, and
for a simplified cvsimport using an existing CVS working directory.

Also insert more man page references.

Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>

 cvs-migration.txt |   27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/cvs-migration.txt | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 47846bdab2..b657f4589f 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -43,8 +43,8 @@ Pull: refs/heads/master:refs/remotes/origin/master
 ------------
 ================================
 
-You can update the shared repository with your changes by first commiting
-your changes, and then using:
+You can update the shared repository with your changes by first committing
+your changes, and then using the gitlink:git-push[1] command:
 
 ------------------------------------------------
 $ git push origin master
@@ -76,11 +76,15 @@ possibly created from scratch or from a tarball (see the
 link:tutorial.html[tutorial]), or imported from an already existing CVS
 repository (see the next section).
 
-If your project's working directory is /home/alice/myproject, you can
-create a shared repository at /pub/repo.git with:
+Assume your existing repo is at /home/alice/myproject.  Create a new "bare"
+repository (a repository without a working tree) and fetch your project into
+it:
 
 ------------------------------------------------
-$ git clone -bare /home/alice/myproject /pub/repo.git
+$ mkdir /pub/my-repo.git
+$ cd /pub/my-repo.git
+$ git --bare init-db --shared
+$ git --bare fetch /home/alice/myproject master:master
 ------------------------------------------------
 
 Next, give every team member read/write access to this repository.  One
@@ -93,10 +97,7 @@ Put all the committers in the same group, and make the repository
 writable by that group:
 
 ------------------------------------------------
-$ cd /pub
-$ chgrp -R $group repo.git
-$ find repo.git -mindepth 1 -type d |xargs chmod ug+rwx,g+s
-$ GIT_DIR=repo.git git repo-config core.sharedrepository true
+$ chgrp -R $group /pub/my-repo.git
 ------------------------------------------------
 
 Make sure committers have a umask of at most 027, so that the directories
@@ -107,15 +108,15 @@ Importing a CVS archive
 
 First, install version 2.1 or higher of cvsps from
 link:http://www.cobite.com/cvsps/[http://www.cobite.com/cvsps/] and make
-sure it is in your path.  The magic command line is then
+sure it is in your path.  Then cd to a checked out CVS working directory
+of the project you are interested in and run gitlink:git-cvsimport[1]:
 
 -------------------------------------------
-$ git cvsimport -v -d <cvsroot> -C <destination> <module>
+$ git cvsimport -C <destination>
 -------------------------------------------
 
 This puts a git archive of the named CVS module in the directory
-<destination>, which will be created if necessary.  The -v option makes
-the conversion script very chatty.
+<destination>, which will be created if necessary.
 
 The import checks out from CVS every revision of every file.  Reportedly
 cvsimport can average some twenty revisions per second, so for a

From d9671b75ad3bbf2f95f11a8571e9beaa12ccf6dd Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 8 Dec 2006 13:29:55 -0800
Subject: [PATCH 125/330] rerere: add clear, diff, and status commands

git-am and git-rebase will be updated to use 'clear', and
diff/status can be used to aid the user in tracking progress in
the resolution process.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-rerere.txt | 27 +++++++++++++++++++++++++--
 git-rerere.perl              | 32 ++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 8b6b651237..22494b2b2e 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -7,8 +7,7 @@ git-rerere - Reuse recorded resolve
 
 SYNOPSIS
 --------
-'git-rerere'
-
+'git-rerere' [clear|diff|status]
 
 DESCRIPTION
 -----------
@@ -167,6 +166,30 @@ would conflict the same way the test merge you resolved earlier.
 `git-rerere` is run by `git rebase` to help you resolve this
 conflict.
 
+COMMANDS
+--------
+
+Normally, git-rerere is run without arguments or user-intervention.
+However, it has several commands that allow it to interact with
+its working state.
+
+'clear'::
+
+This resets the metadata used by rerere if a merge resolution is to be
+is aborted.  Calling gitlink:git-am[1] --skip or gitlink:git-rebase[1]
+[--skip|--abort] will automatcally invoke this command.
+
+'diff'::
+
+This displays diffs for the current state of the resolution.  It is
+useful for tracking what has changed while the user is resolving
+conflicts.  Additional arguments are passed directly to the system
+diff(1) command installed in PATH.
+
+'status'::
+
+Like diff, but this only prints the filenames that will be tracked
+for resolutions.
 
 Author
 ------
diff --git a/git-rerere.perl b/git-rerere.perl
index d3664ff491..b2550bb2ef 100755
--- a/git-rerere.perl
+++ b/git-rerere.perl
@@ -172,6 +172,38 @@ sub merge {
 -d "$rr_dir" || exit(0);
 
 read_rr();
+
+if (@ARGV) {
+	my $arg = shift @ARGV;
+	if ($arg eq 'clear') {
+		for my $path (keys %merge_rr) {
+			my $name = $merge_rr{$path};
+			if (-d "$rr_dir/$name" &&
+			    ! -f "$rr_dir/$name/postimage") {
+				rmtree(["$rr_dir/$name"]);
+			}
+		}
+		unlink $merge_rr;
+	}
+	elsif ($arg eq 'status') {
+		for my $path (keys %merge_rr) {
+			print $path, "\n";
+		}
+	}
+	elsif ($arg eq 'diff') {
+		for my $path (keys %merge_rr) {
+			my $name = $merge_rr{$path};
+			system('diff', ((@ARGV == 0) ? ('-u') : @ARGV),
+				'-L', "a/$path", '-L', "b/$path",
+				"$rr_dir/$name/preimage", $path);
+		}
+	}
+	else {
+		die "$0 unknown command: $arg\n";
+	}
+	exit 0;
+}
+
 my %conflict = map { $_ => 1 } find_conflict();
 
 # MERGE_RR records paths with conflicts immediately after merge

From cda2d3c112a03079af9019c7d6617e65ab88ae7e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 8 Dec 2006 13:03:12 -0800
Subject: [PATCH 126/330] git-rerere: add 'gc' command.

Over time, unresolved rr-cache entries are accumulated and they
tend to get less and less likely to be useful as the tips of
branches advance.

Reorder documentation page to show the subcommand section earlier
than the discussion section.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-rerere.txt | 56 ++++++++++++++++++++----------------
 git-rerere.perl              | 25 ++++++++++++++++
 2 files changed, 57 insertions(+), 24 deletions(-)

diff --git a/Documentation/git-rerere.txt b/Documentation/git-rerere.txt
index 22494b2b2e..116dca4c06 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -26,6 +26,38 @@ results and applying the previously recorded hand resolution.
 You need to create `$GIT_DIR/rr-cache` directory to enable this
 command.
 
+
+COMMANDS
+--------
+
+Normally, git-rerere is run without arguments or user-intervention.
+However, it has several commands that allow it to interact with
+its working state.
+
+'clear'::
+
+This resets the metadata used by rerere if a merge resolution is to be
+is aborted.  Calling gitlink:git-am[1] --skip or gitlink:git-rebase[1]
+[--skip|--abort] will automatcally invoke this command.
+
+'diff'::
+
+This displays diffs for the current state of the resolution.  It is
+useful for tracking what has changed while the user is resolving
+conflicts.  Additional arguments are passed directly to the system
+diff(1) command installed in PATH.
+
+'status'::
+
+Like diff, but this only prints the filenames that will be tracked
+for resolutions.
+
+'gc'::
+
+This command is used to prune records of conflicted merge that
+occurred long time ago.
+
+
 DISCUSSION
 ----------
 
@@ -166,30 +198,6 @@ would conflict the same way the test merge you resolved earlier.
 `git-rerere` is run by `git rebase` to help you resolve this
 conflict.
 
-COMMANDS
---------
-
-Normally, git-rerere is run without arguments or user-intervention.
-However, it has several commands that allow it to interact with
-its working state.
-
-'clear'::
-
-This resets the metadata used by rerere if a merge resolution is to be
-is aborted.  Calling gitlink:git-am[1] --skip or gitlink:git-rebase[1]
-[--skip|--abort] will automatcally invoke this command.
-
-'diff'::
-
-This displays diffs for the current state of the resolution.  It is
-useful for tracking what has changed while the user is resolving
-conflicts.  Additional arguments are passed directly to the system
-diff(1) command installed in PATH.
-
-'status'::
-
-Like diff, but this only prints the filenames that will be tracked
-for resolutions.
 
 Author
 ------
diff --git a/git-rerere.perl b/git-rerere.perl
index b2550bb2ef..61eef575da 100755
--- a/git-rerere.perl
+++ b/git-rerere.perl
@@ -169,6 +169,28 @@ sub merge {
 	return 0;
 }
 
+sub garbage_collect_rerere {
+	# We should allow specifying these from the command line and
+	# that is why the caller gives @ARGV to us, but I am lazy.
+
+	my $cutoff_noresolve = 15; # two weeks
+	my $cutoff_resolve = 60; # two months
+	my @to_remove;
+	while (<$rr_dir/*/preimage>) {
+		my ($dir) = /^(.*)\/preimage$/;
+		my $cutoff = ((-f "$dir/postimage")
+			      ? $cutoff_resolve
+			      : $cutoff_noresolve);
+		my $age = -M "$_";
+		if ($cutoff <= $age) {
+			push @to_remove, $dir;
+		}
+	}
+	if (@to_remove) {
+		rmtree(\@to_remove);
+	}
+}
+
 -d "$rr_dir" || exit(0);
 
 read_rr();
@@ -198,6 +220,9 @@ if (@ARGV) {
 				"$rr_dir/$name/preimage", $path);
 		}
 	}
+	elsif ($arg eq 'gc') {
+		garbage_collect_rerere(@ARGV);
+	}
 	else {
 		die "$0 unknown command: $arg\n";
 	}

From f131dd492f098f9f565df93df13e35c734284590 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 8 Dec 2006 13:29:56 -0800
Subject: [PATCH 127/330] rerere: record (or avoid misrecording) resolved,
 skipped or aborted rebase/am

Data in rr-cache isn't valid after a patch application is
skipped or and aborted, so our next commit could be misrecorded
as a resolution of that skipped/failed commit, which is wrong.

git-am --skip, git-rebase --skip/--abort will automatically
invoke git-rerere clear to avoid this.

Also, since git-am --resolved indicates a resolution was
succesful, remember to run git-rerere to record the resolution
(and not surprise the user when the next commit is made).

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-am.sh     | 8 ++++++++
 git-rebase.sh | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/git-am.sh b/git-am.sh
index afe322b20f..5df6787a3f 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -246,6 +246,10 @@ last=`cat "$dotest/last"`
 this=`cat "$dotest/next"`
 if test "$skip" = t
 then
+	if test -d "$GIT_DIR/rr-cache"
+	then
+		git-rerere clear
+	fi
 	this=`expr "$this" + 1`
 	resume=
 fi
@@ -408,6 +412,10 @@ do
 			stop_here_user_resolve $this
 		fi
 		apply_status=0
+		if test -d "$GIT_DIR/rr-cache"
+		then
+			git rerere
+		fi
 		;;
 	esac
 
diff --git a/git-rebase.sh b/git-rebase.sh
index 25530dfdc5..2b4f3477fa 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -139,6 +139,10 @@ do
 	--skip)
 		if test -d "$dotest"
 		then
+			if test -d "$GIT_DIR/rr-cache"
+			then
+				git-rerere clear
+			fi
 			prev_head="`cat $dotest/prev_head`"
 			end="`cat $dotest/end`"
 			msgnum="`cat $dotest/msgnum`"
@@ -157,6 +161,10 @@ do
 		exit
 		;;
 	--abort)
+		if test -d "$GIT_DIR/rr-cache"
+		then
+			git-rerere clear
+		fi
 		if test -d "$dotest"
 		then
 			rm -r "$dotest"

From 6c96753df9db7f790a2ac4d95ec2a868394cd5ff Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 8 Dec 2006 21:48:07 -0800
Subject: [PATCH 128/330] Documentation/git-commit: rewrite to make it more
 end-user friendly.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-commit.txt | 217 ++++++++++++++++++++++++-----------
 1 file changed, 149 insertions(+), 68 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 517a86b238..97d66ef4d2 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -14,25 +14,41 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-Updates the index file for given paths, or all modified files if
-'-a' is specified, and makes a commit object.  The command specified
-by either the VISUAL or EDITOR environment variables are used to edit
-the commit log message.
+Use 'git commit' when you want to record your changes into the repository
+along with a log message describing what the commit is about. All changes
+to be committed must be explicitly identified using one of the following
+methods:
 
-Several environment variable are used during commits.  They are
-documented in gitlink:git-commit-tree[1].
+1. by using gitlink:git-add[1] to incrementally "add" changes to the
+   next commit before using the 'commit' command (Note: even modified
+   files must be "added");
 
+2. by using gitlink:git-rm[1] to identify content removal for the next
+   commit, again before using the 'commit' command;
+
+3. by directly listing files containing changes to be committed as arguments
+   to the 'commit' command, in which cases only those files alone will be
+   considered for the commit;
+
+4. by using the -a switch with the 'commit' command to automatically "add"
+   changes from all known files i.e. files that have already been committed
+   before, and perform the actual commit.
+
+The gitlink:git-status[1] command can be used to obtain a
+summary of what is included by any of the above for the next
+commit by giving the same set of parameters you would give to
+this command.
+
+If you make a commit and then found a mistake immediately after
+that, you can recover from it with gitlink:git-reset[1].
 
-This command can run `commit-msg`, `pre-commit`, and
-`post-commit` hooks.  See link:hooks.html[hooks] for more
-information.
 
 OPTIONS
 -------
 -a|--all::
-	Update all paths in the index file.  This flag notices
-	files that have been modified and deleted, but new files
-	you have not told git about are not affected.
+	Tell the command to automatically stage files that have
+	been modified and deleted, but new files you have not
+	told git about are not affected.
 
 -c or -C <commit>::
 	Take existing commit object, and reuse the log message
@@ -55,16 +71,13 @@ OPTIONS
 -s|--signoff::
 	Add Signed-off-by line at the end of the commit message.
 
--v|--verify::
-	Look for suspicious lines the commit introduces, and
-	abort committing if there is one.  The definition of
-	'suspicious lines' is currently the lines that has
-	trailing whitespaces, and the lines whose indentation
-	has a SP character immediately followed by a TAB
-	character.  This is the default.
-
--n|--no-verify::
-	The opposite of `--verify`.
+--no-verify::
+	By default, the command looks for suspicious lines the
+	commit introduces, and aborts committing if there is one.
+	The definition of 'suspicious lines' is currently the
+	lines that has trailing whitespaces, and the lines whose
+	indentation has a SP character immediately followed by a
+	TAB character.  This option turns off the check.
 
 -e|--edit::
 	The message taken from file with `-F`, command line with
@@ -95,69 +108,137 @@ but can be used to amend a merge commit.
 --
 
 -i|--include::
-	Instead of committing only the files specified on the
-	command line, update them in the index file and then
-	commit the whole index.  This is the traditional
-	behavior.
-
--o|--only::
-	Commit only the files specified on the command line.
-	This format cannot be used during a merge, nor when the
-	index and the latest commit does not match on the
-	specified paths to avoid confusion.
+	Before making a commit out of staged contents so far,
+	stage the contents of paths given on the command line
+	as well.  This is usually not what you want unless you
+	are concluding a conflicted merge.
 
 \--::
 	Do not interpret any more arguments as options.
 
 <file>...::
-	Files to be committed.  The meaning of these is
-	different between `--include` and `--only`.  Without
-	either, it defaults `--only` semantics.
-
-If you make a commit and then found a mistake immediately after
-that, you can recover from it with gitlink:git-reset[1].
+	When files are given on the command line, the command
+	commits the contents of the named files, without
+	recording the changes already staged.  The contents of
+	these files are also staged for the next commit on top
+	of what have been staged before.
 
 
-Discussion
-----------
+EXAMPLES
+--------
+When recording your own work, the contents of modified files in
+your working tree are temporarily stored to a staging area
+called the "index" with gitlink:git-add[1].  Removal
+of a file is staged with gitlink:git-rm[1].  After building the
+state to be committed incrementally with these commands, `git
+commit` (without any pathname parameter) is used to record what
+has been staged so far.  This is the most basic form of the
+command.  An example:
 
-`git commit` without _any_ parameter commits the tree structure
-recorded by the current index file.  This is a whole-tree commit
-even the command is invoked from a subdirectory.
+------------
+$ edit hello.c
+$ git rm goodbye.c
+$ git add hello.c
+$ git commit
+------------
 
-`git commit --include paths...` is equivalent to
+////////////
+We should fix 'git rm' to remove goodbye.c from both index and
+working tree for the above example.
+////////////
 
-	git update-index --remove paths...
-	git commit
+Instead of staging files after each individual change, you can
+tell `git commit` to notice the changes to the files whose
+contents are tracked in
+your working tree and do corresponding `git add` and `git rm`
+for you.  That is, this example does the same as the earlier
+example if there is no other change in your working tree:
 
-That is, update the specified paths to the index and then commit
-the whole tree.
+------------
+$ edit hello.c
+$ rm goodbye.c
+$ git commit -a
+------------
 
-`git commit paths...` largely bypasses the index file and
-commits only the changes made to the specified paths.  It has
-however several safety valves to prevent confusion.
+The command `git commit -a` first looks at your working tree,
+notices that you have modified hello.c and removed goodbye.c,
+and performs necessary `git add` and `git rm` for you.
 
-. It refuses to run during a merge (i.e. when
-  `$GIT_DIR/MERGE_HEAD` exists), and reminds trained git users
-  that the traditional semantics now needs -i flag.
+After staging changes to many files, you can alter the order the
+changes are recorded in, by giving pathnames to `git commit`.
+When pathnames are given, the command makes a commit that
+only records the changes made to the named paths:
 
-. It refuses to run if named `paths...` are different in HEAD
-  and the index (ditto about reminding).  Added paths are OK.
-  This is because an earlier `git diff` (not `git diff HEAD`)
-  would have shown the differences since the last `git
-  update-index paths...` to the user, and an inexperienced user
-  may mistakenly think that the changes between the index and
-  the HEAD (i.e. earlier changes made before the last `git
-  update-index paths...` was done) are not being committed.
+------------
+$ edit hello.c hello.h
+$ git add hello.c hello.h
+$ edit Makefile
+$ git commit Makefile
+------------
 
-. It reads HEAD commit into a temporary index file, updates the
-  specified `paths...` and makes a commit.  At the same time,
-  the real index file is also updated with the same `paths...`.
+This makes a commit that records the modification to `Makefile`.
+The changes staged for `hello.c` and `hello.h` are not included
+in the resulting commit.  However, their changes are not lost --
+they are still staged and merely held back.  After the above
+sequence, if you do:
 
-`git commit --all` updates the index file with _all_ changes to
-the working tree, and makes a whole-tree commit, regardless of
-which subdirectory the command is invoked in.
+------------
+$ git commit
+------------
 
+this second commit would record the changes to `hello.c` and
+`hello.h` as expected.
+
+After a merge (initiated by either gitlink:git-merge[1] or
+gitlink:git-pull[1]) stops because of conflicts, cleanly merged
+paths are already staged to be committed for you, and paths that
+conflicted are left in unmerged state.  You would have to first
+check which paths are conflicting with gitlink:git-status[1]
+and after fixing them manually in your working tree, you would
+stage the result as usual with gitlink:git-add[1]:
+
+------------
+$ git status | grep unmerged
+unmerged: hello.c
+$ edit hello.c
+$ git add hello.c
+------------
+
+After resolving conflicts and staging the result, `git ls-files -u`
+would stop mentioning the conflicted path.  When you are done,
+run `git commit` to finally record the merge:
+
+------------
+$ git commit
+------------
+
+As with the case to record your own changes, you can use `-a`
+option to save typing.  One difference is that during a merge
+resolution, you cannot use `git commit` with pathnames to
+alter the order the changes are committed, because the merge
+should be recorded as a single commit.  In fact, the command
+refuses to run when given pathnames (but see `-i` option).
+
+
+ENVIRONMENT VARIABLES
+---------------------
+The command specified by either the VISUAL or EDITOR environment
+variables is used to edit the commit log message.
+
+HOOKS
+-----
+This command can run `commit-msg`, `pre-commit`, and
+`post-commit` hooks.  See link:hooks.html[hooks] for more
+information.
+
+
+SEE ALSO
+--------
+gitlink:git-add[1],
+gitlink:git-rm[1],
+gitlink:git-mv[1],
+gitlink:git-merge[1],
+gitlink:git-commit-tree[1]
 
 Author
 ------

From 158d0577891441c01457bbcaf45585d3b50f5d75 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 9 Dec 2006 22:32:43 -0800
Subject: [PATCH 129/330] git-commit: allow --only to lose what was staged
 earlier.

The command used to have a safety valve to prevent this sequence:

	edit foo
	git update-index foo
	edit foo
	git diff foo
	git commit --only foo

The reason for this was because an inexperienced user might
mistakenly think what is shown with the last-minute diff
contains all the change that is being committed (instead, what
the user asked to check was an incremental diff since what has
been staged so far).  However, this turns out to only annoy
people who know what they are doing.  Inexperienced people
would not be using the first "update-index" anyway, in which
case they would see the full changes in the "git diff".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-commit.sh | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/git-commit.sh b/git-commit.sh
index 81c3a0cb61..c829791171 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -350,19 +350,9 @@ t,)
 			refuse_partial "Cannot do a partial commit during a merge."
 		fi
 		TMP_INDEX="$GIT_DIR/tmp-index$$"
-		if test -z "$initial_commit"
-		then
-			# make sure index is clean at the specified paths, or
-			# they are additions.
-			dirty_in_index=`git-diff-index --cached --name-status \
-				--diff-filter=DMTU HEAD -- "$@"`
-			test -z "$dirty_in_index" ||
-			refuse_partial "Different in index and the last commit:
-$dirty_in_index"
-		fi
 		commit_only=`git-ls-files --error-unmatch -- "$@"` || exit
 
-		# Build the temporary index and update the real index
+		# Build a temporary index and update the real index
 		# the same way.
 		if test -z "$initial_commit"
 		then

From d44c92d6ab4ded7a1960bb0b4a1da0c2fc102b89 Mon Sep 17 00:00:00 2001
From: Chris Wright <chrisw@sous-sol.org>
Date: Sun, 10 Dec 2006 23:39:32 -0800
Subject: [PATCH 130/330] no need to install manpages as executable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

No need to install manpages as executable.  Noticed by Ville Skytt,Ad(B.

Signed-off-by: Chris Wright <chrisw@sous-sol.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/Makefile | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/Makefile b/Documentation/Makefile
index c00f5f62b7..d68bc4a788 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -56,8 +56,8 @@ man7: $(DOC_MAN7)
 
 install: man
 	$(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
-	$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir)
-	$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir)
+	$(INSTALL) -m644 $(DOC_MAN1) $(DESTDIR)$(man1dir)
+	$(INSTALL) -m644 $(DOC_MAN7) $(DESTDIR)$(man7dir)
 
 
 #

From 554a2636f7c5125a83bb07194632445467d46c83 Mon Sep 17 00:00:00 2001
From: Jim Meyering <jim@meyering.net>
Date: Mon, 11 Dec 2006 19:06:34 +0100
Subject: [PATCH 131/330] Don't use memcpy when source and dest. buffers may
 overlap

git-index-pack can call memcpy with overlapping source and destination
buffers.  The patch below makes it use memmove instead.

If you want to demonstrate a failure, add the following two lines

+               if (input_offset < input_len)
+                 abort ();

before the existing memcpy call (shown in the patch below),
and then run this:

  (cd t; sh ./t5500-fetch-pack.sh)

Signed-off-by: Jim Meyering <jim@meyering.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 index-pack.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index-pack.c b/index-pack.c
index 8331d99a62..6d6c92bf14 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -96,7 +96,7 @@ static void flush(void)
 		if (output_fd >= 0)
 			write_or_die(output_fd, input_buffer, input_offset);
 		SHA1_Update(&input_ctx, input_buffer, input_offset);
-		memcpy(input_buffer, input_buffer + input_offset, input_len);
+		memmove(input_buffer, input_buffer + input_offset, input_len);
 		input_offset = 0;
 	}
 }

From 9abd46a3471c2d58976e06a00e937b03672b98bc Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 7 Dec 2006 05:17:07 -0500
Subject: [PATCH 132/330] Make sure the empty tree exists when needed in
 merge-recursive.

There are some baseless merge cases where git-merge-recursive will
try to compare one of the branches against the empty tree.  However
most projects won't have the empty tree object in their object database
as Git does not normally create empty tree objects.  If the empty tree
object is missing then the merge process will die, as it cannot load the
object from the database.  The error message may make the user think that
their database is corrupt when its actually not.

So instead we should just create the empty tree object whenever it is
needed.  If the object already exists as a loose object then no harm
done.  Otherwise that loose object will be pruned away later by either
git-prune or git-prune-packed.

Thanks goes to Junio for suggesting this fix.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 merge-recursive.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index cd2cc77bf4..32e186c15e 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1238,7 +1238,7 @@ static int merge(struct commit *h1,
 
 		tree->object.parsed = 1;
 		tree->object.type = OBJ_TREE;
-		hash_sha1_file(NULL, 0, tree_type, tree->object.sha1);
+		write_sha1_file(NULL, 0, tree_type, tree->object.sha1);
 		merged_common_ancestors = make_virtual_commit(tree, "ancestor");
 	}
 

From bca73251da5cc3e4bea71e28e0096a5cd662bbd9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 10 Dec 2006 15:55:07 -0800
Subject: [PATCH 133/330] shortlog: remove "[PATCH]" prefix from shortlog
 output

Originally noticed by Nicolas Pitre; the real cause was the code
was prepared to deal with [PATCH] (and [PATCH n/m whatever])
prefixes but forgot that the string can be indented while acting
as a filter.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 7a2ddfe797..3322c3a2ee 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -195,11 +195,17 @@ static void read_from_stdin(struct path_list *list)
 			while (fgets(buffer2, sizeof(buffer2), stdin) &&
 					buffer2[0] != '\n')
 				; /* chomp input */
-			if (fgets(buffer2, sizeof(buffer2), stdin))
+			if (fgets(buffer2, sizeof(buffer2), stdin)) {
+				int l2 = strlen(buffer2);
+				int i;
+				for (i = 0; i < l2; i++)
+					if (!isspace(buffer2[i]))
+						break;
 				insert_author_oneline(list,
 						buffer + offset,
 						bob - buffer - offset,
-						buffer2, strlen(buffer2));
+						buffer2 + i, l2 - i);
+			}
 		}
 	}
 }

From 6f9872582246b9b8ee4bdc9f6a563b409aab1ecb Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 10 Dec 2006 15:51:54 -0800
Subject: [PATCH 134/330] shortlog: fix segfault on empty authorname

The old code looked backwards from the email address to parse
the name, allowing an arbitrary number of spaces between the
two. However, in the case of no name, we looked back too far to
the 'author' (or 'Author:') header.

The bug was triggered by commit febf7ea4bed from linux-2.6.

Jeff King originally fixed it by looking back only one
character; Johannes Schindelin pointed out that we could try
harder while at it to cope with commits with broken headers.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-shortlog.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 3322c3a2ee..3fc43dd7dd 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -188,7 +188,8 @@ static void read_from_stdin(struct path_list *list)
 				bob = buffer + strlen(buffer);
 			else {
 				offset = 8;
-				if (isspace(bob[-1]))
+				while (buffer + offset < bob &&
+				       isspace(bob[-1]))
 					bob--;
 			}
 

From e86ad71fe5f53ae4434566bd09ea4256090e5a3a Mon Sep 17 00:00:00 2001
From: Robin Rosenberg <robin.rosenberg@dewire.com>
Date: Mon, 11 Dec 2006 00:30:06 +0100
Subject: [PATCH 135/330] Make cvsexportcommit work with filenames with spaces
 and non-ascii characters.

This patch uses git-apply to do the patching which simplifies the code a lot
and also uses one pass to git-diff. git-apply gives information on added,
removed files as well as which files are binary.

Removed the test for checking for matching binary files when deleting them
since git-apply happily deletes the file. This is matter of taste since we
allow some fuzz for text patches also.

Error handling was cleaned up, but not much tested.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-cvsexportcommit.perl       | 203 +++++++++++----------------------
 t/t9200-git-cvsexportcommit.sh | 108 +++++++++++++-----
 2 files changed, 145 insertions(+), 166 deletions(-)

diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index c9d1d88f2e..4863c91fe3 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -2,9 +2,8 @@
 
 # Known limitations:
 # - does not propagate permissions
-# - tells "ready for commit" even when things could not be completed
-#   (not sure this is true anymore, more testing is needed)
-# - does not handle whitespace in pathnames at all.
+# - error handling has not been extensively tested
+#
 
 use strict;
 use Getopt::Std;
@@ -115,49 +114,40 @@ if ($opt_a) {
 }
 close MSG;
 
-my (@afiles, @dfiles, @mfiles, @dirs);
-my %amodes;
-my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
-#print @files;
-$? && die "Error in git-diff-tree";
-foreach my $f (@files) {
-    chomp $f;
-    my @fields = split(m!\s+!, $f);
-    if ($fields[4] eq 'A') {
-        my $path = $fields[5];
-	$amodes{$path} = $fields[1];
-	push @afiles, $path;
-        # add any needed parent directories
-	$path = dirname $path;
-	while (!-d $path and ! grep { $_ eq $path } @dirs) {
-	    unshift @dirs, $path;
-	    $path = dirname $path;
-	}
-    }
-    if ($fields[4] eq 'M') {
-	push @mfiles, $fields[5];
-    }
-    if ($fields[4] eq 'D') {
-	push @dfiles, $fields[5];
-    }
-}
-my (@binfiles, @abfiles, @dbfiles, @bfiles, @mbfiles);
-@binfiles = grep m/^Binary files/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit);
-map { chomp } @binfiles;
-@abfiles = grep s/^Binary files \/dev\/null and b\/(.*) differ$/$1/, @binfiles;
-@dbfiles = grep s/^Binary files a\/(.*) and \/dev\/null differ$/$1/, @binfiles;
-@mbfiles = grep s/^Binary files a\/(.*) and b\/(.*) differ$/$1/, @binfiles;
-push @bfiles, @abfiles;
-push @bfiles, @dbfiles;
-push @bfiles, @mbfiles;
-push @mfiles, @mbfiles;
+`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
 
-$opt_v && print "The commit affects:\n ";
-$opt_v && print join ("\n ", @afiles,@mfiles,@dfiles) . "\n\n";
-undef @files; # don't need it anymore
+## apply non-binary changes
+my $fuzz = $opt_p ? 0 : 2;
+
+print "Checking if patch will apply\n";
+
+my @stat;
+open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+@stat=<APPLY>;
+close APPLY || die "Cannot patch";
+my (@bfiles,@files,@afiles,@dfiles);
+chomp @stat;
+foreach (@stat) {
+	push (@bfiles,$1) if m/^-\t-\t(.*)$/;
+	push (@files, $1) if m/^-\t-\t(.*)$/;
+	push (@files, $1) if m/^\d+\t\d+\t(.*)$/;
+	push (@afiles,$1) if m/^ create mode [0-7]+ (.*)$/;
+	push (@dfiles,$1) if m/^ delete mode [0-7]+ (.*)$/;
+}
+map { s/^"(.*)"$/$1/g } @bfiles,@files;
+map { s/\\([0-7]{3})/sprintf('%c',oct $1)/eg } @bfiles,@files;
 
 # check that the files are clean and up to date according to cvs
 my $dirty;
+my @dirs;
+foreach my $p (@afiles) {
+    my $path = dirname $p;
+    while (!-d $path and ! grep { $_ eq $path } @dirs) {
+	unshift @dirs, $path;
+	$path = dirname $path;
+    }
+}
+
 foreach my $d (@dirs) {
     if (-e $d) {
 	$dirty = 1;
@@ -180,7 +170,8 @@ foreach my $f (@afiles) {
     }
 }
 
-foreach my $f (@mfiles, @dfiles) {
+foreach my $f (@files) {
+    next if grep { $_ eq $f } @afiles;
     # TODO:we need to handle removed in cvs
     my @status = grep(m/^File/,  safe_pipe_capture('cvs', '-q', 'status' ,$f));
     if (@status > 1) { warn 'Strange! cvs status returned more than one line?'};
@@ -197,87 +188,26 @@ if ($dirty) {
     }
 }
 
-###
-### NOTE: if you are planning to die() past this point
-###       you MUST call cleanupcvs(@files) before die()
-###
+print "Applying\n";
+`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
 
-
-print "Creating new directories\n";
+print "Patch applied successfully. Adding new files and directories to CVS\n";
+my $dirtypatch = 0;
 foreach my $d (@dirs) {
-    unless (mkdir $d) {
-        warn "Could not mkdir $d: $!";
-	$dirty = 1;
-    }
-    `cvs add $d`;
-    if ($?) {
-	$dirty = 1;
+    if (system('cvs','add',$d)) {
+	$dirtypatch = 1;
 	warn "Failed to cvs add directory $d -- you may need to do it manually";
     }
 }
 
-print "'Patching' binary files\n";
-
-foreach my $f (@bfiles) {
-    # check that the file in cvs matches the "old" file
-    # extract the file to $tmpdir and compare with cmp
-    if (not(grep { $_ eq $f } @afiles)) {
-        my $tree = safe_pipe_capture('git-rev-parse', "$parent^{tree}");
-	chomp $tree;
-	my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
-	chomp $blob;
-        `git-cat-file blob $blob > $tmpdir/blob`;
-        if (system('cmp', '-s', $f, "$tmpdir/blob")) {
-	    warn "Binary file $f in CVS does not match parent.\n";
-	    if (not $opt_f) {
-	        $dirty = 1;
-		next;
-	    }
-        }
-    }
-    if (not(grep { $_ eq $f } @dfiles)) {
-	my $tree = safe_pipe_capture('git-rev-parse', "$commit^{tree}");
-	chomp $tree;
-	my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
-	chomp $blob;
-	# replace with the new file
-	`git-cat-file blob $blob > $f`;
-    }
-
-    # TODO: something smart with file modes
-
-}
-if ($dirty) {
-    cleanupcvs(@files);
-    die "Exiting: Binary files in CVS do not match parent";
-}
-
-## apply non-binary changes
-my $fuzz = $opt_p ? 0 : 2;
-
-print "Patching non-binary files\n";
-
-if (scalar(@afiles)+scalar(@dfiles)+scalar(@mfiles) != scalar(@bfiles)) {
-    print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`;
-}
-
-my $dirtypatch = 0;
-if (($? >> 8) == 2) {
-    cleanupcvs(@files);
-    die "Exiting: Patch reported serious trouble -- you will have to apply this patch manually";
-} elsif (($? >> 8) == 1) { # some hunks failed to apply
-    $dirtypatch = 1;
-}
-
 foreach my $f (@afiles) {
-    set_new_file_permissions($f, $amodes{$f});
     if (grep { $_ eq $f } @bfiles) {
       system('cvs', 'add','-kb',$f);
     } else {
       system('cvs', 'add', $f);
     }
     if ($?) {
-	$dirty = 1;
+	$dirtypatch = 1;
 	warn "Failed to cvs add $f -- you may need to do it manually";
     }
 }
@@ -285,35 +215,40 @@ foreach my $f (@afiles) {
 foreach my $f (@dfiles) {
     system('cvs', 'rm', '-f', $f);
     if ($?) {
-	$dirty = 1;
+	$dirtypatch = 1;
 	warn "Failed to cvs rm -f $f -- you may need to do it manually";
     }
 }
 
 print "Commit to CVS\n";
-print "Patch: $title\n";
-my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
-my $cmd = "cvs commit -F .msg $commitfiles";
+print "Patch title (first comment line): $title\n";
+my @commitfiles = map { unless (m/\s/) { '\''.$_.'\''; } else { $_; }; } (@files);
+my $cmd = "cvs commit -F .msg @commitfiles";
 
 if ($dirtypatch) {
     print "NOTE: One or more hunks failed to apply cleanly.\n";
-    print "Resolve the conflicts and then commit using:\n";
+    print "You'll need to apply the patch in .cvsexportcommit.diff manually\n";
+    print "using a patch program. After applying the patch and resolving the\n";
+    print "problems you may commit using:";
     print "\n    $cmd\n\n";
     exit(1);
 }
 
-
 if ($opt_c) {
     print "Autocommit\n  $cmd\n";
-    print safe_pipe_capture('cvs', 'commit', '-F', '.msg', @afiles, @mfiles, @dfiles);
+    print safe_pipe_capture('cvs', 'commit', '-F', '.msg', @files);
     if ($?) {
-	cleanupcvs(@files);
 	die "Exiting: The commit did not succeed";
     }
     print "Committed successfully to CVS\n";
 } else {
     print "Ready for you to commit, just run:\n\n   $cmd\n";
 }
+
+# clean up
+unlink(".cvsexportcommit.diff");
+unlink(".msg");
+
 sub usage {
 	print STDERR <<END;
 Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
@@ -321,17 +256,6 @@ END
 	exit(1);
 }
 
-# ensure cvs is clean before we die
-sub cleanupcvs {
-    my @files = @_;
-    foreach my $f (@files) {
-	system('cvs', '-q', 'update', '-C', $f);
-	if ($?) {
-	    warn "Warning! Failed to cleanup state of $f\n";
-	}
-    }
-}
-
 # An alternative to `command` that allows input to be passed as an array
 # to work around shell problems with weird characters in arguments
 # if the exec returns non-zero we die
@@ -346,12 +270,15 @@ sub safe_pipe_capture {
     return wantarray ? @output : join('',@output);
 }
 
-# For any file we want to add to cvs, we must first set its permissions
-# properly, *before* the "cvs add ..." command.  Otherwise, it is impossible
-# to change the permission of the file in the CVS repository using only cvs
-# commands.  This should be fixed in cvs-1.12.14.
-sub set_new_file_permissions {
-    my ($file, $perm) = @_;
-    chmod oct($perm), $file
-      or die "failed to set permissions of \"$file\": $!\n";
+sub safe_pipe_capture_blob {
+    my $output;
+    if (my $pid = open my $child, '-|') {
+        local $/;
+	undef $/;
+	$output = (<$child>);
+	close $child or die join(' ',@_).": $! $?";
+    } else {
+	exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
+    }
+    return $output;
 }
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index c1024790e4..ca0513b162 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -89,18 +89,17 @@ test_expect_success \
      ! git cvsexportcommit -c $id
      )'
 
-# Should fail, but only on the git-cvsexportcommit stage
-test_expect_success \
-    'Fail to remove binary file more than one generation old' \
-    'git reset --hard HEAD^ &&
-     cat F/newfile6.png >>D/newfile4.png &&
-     git commit -a -m "generation 2 (again)" &&
-     rm -f D/newfile4.png &&
-     git commit -a -m "generation 3" &&
-     id=$(git rev-list --max-count=1 HEAD) &&
-     (cd "$CVSWORK" &&
-     ! git cvsexportcommit -c $id
-     )'
+#test_expect_success \
+#    'Fail to remove binary file more than one generation old' \
+#    'git reset --hard HEAD^ &&
+#     cat F/newfile6.png >>D/newfile4.png &&
+#     git commit -a -m "generation 2 (again)" &&
+#     rm -f D/newfile4.png &&
+#     git commit -a -m "generation 3" &&
+#     id=$(git rev-list --max-count=1 HEAD) &&
+#     (cd "$CVSWORK" &&
+#     ! git cvsexportcommit -c $id
+#     )'
 
 # We reuse the state from two tests back here
 
@@ -108,7 +107,7 @@ test_expect_success \
 # fail with gnu patch, so cvsexportcommit must handle that.
 test_expect_success \
     'Remove only binary files' \
-    'git reset --hard HEAD^^^ &&
+    'git reset --hard HEAD^^ &&
      rm -f D/newfile4.png &&
      git commit -a -m "test: remove only a binary file" &&
      id=$(git rev-list --max-count=1 HEAD) &&
@@ -142,20 +141,73 @@ test_expect_success \
      diff F/newfile6.png ../F/newfile6.png
      )'
 
-test_expect_success 'Retain execute bit' '
-	mkdir G &&
-	echo executeon >G/on &&
-	chmod +x G/on &&
-	echo executeoff >G/off &&
-	git add G/on &&
-	git add G/off &&
-	git commit -a -m "Execute test" &&
-	(
-		cd "$CVSWORK" &&
-		git-cvsexportcommit -c HEAD
-		test -x G/on &&
-		! test -x G/off
-	)
-'
+test_expect_success \
+     'New file with spaces in file name' \
+     'mkdir "G g" &&
+      echo ok then >"G g/with spaces.txt" &&
+      git add "G g/with spaces.txt" && \
+      cp ../test9200a.png "G g/with spaces.png" && \
+      git add "G g/with spaces.png" &&
+      git commit -a -m "With spaces" &&
+      id=$(git rev-list --max-count=1 HEAD) &&
+      (cd "$CVSWORK" &&
+      git-cvsexportcommit -c $id &&
+      test "$(echo $(sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.1/-kb with spaces.txt/1.1/"
+      )'
+
+test_expect_success \
+     'Update file with spaces in file name' \
+     'echo Ok then >>"G g/with spaces.txt" &&
+      cat ../test9200a.png >>"G g/with spaces.png" && \
+      git add "G g/with spaces.png" &&
+      git commit -a -m "Update with spaces" &&
+      id=$(git rev-list --max-count=1 HEAD) &&
+      (cd "$CVSWORK" &&
+      git-cvsexportcommit -c $id
+      test "$(echo $(sort "G g/CVS/Entries"|cut -d/ -f2,3,5))" = "with spaces.png/1.2/-kb with spaces.txt/1.2/"
+      )'
+
+# This test contains ISO-8859-1 characters
+test_expect_success \
+     'File with non-ascii file name' \
+     'mkdir -p �/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/� &&
+      echo Foo >�/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/�/g�rdets�g�rdet.txt &&
+      git add �/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/�/g�rdets�g�rdet.txt &&
+      cp ../test9200a.png �/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/�/g�rdets�g�rdet.png &&
+      git add �/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/�/g�rdets�g�rdet.png &&
+      git commit -a -m "G�r det s� g�r det" && \
+      id=$(git rev-list --max-count=1 HEAD) &&
+      (cd "$CVSWORK" &&
+      git-cvsexportcommit -v -c $id &&
+      test "$(echo $(sort �/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/�/�/�/CVS/Entries|cut -d/ -f2,3,5))" = "g�rdets�g�rdet.png/1.1/-kb g�rdets�g�rdet.txt/1.1/"
+      )'
+
+test_expect_success \
+     'Mismatching patch should fail' \
+     'date >>"E/newfile5.txt" &&
+      git add "E/newfile5.txt" &&
+      git commit -a -m "Update one" &&
+      date >>"E/newfile5.txt" &&
+      git add "E/newfile5.txt" &&
+      git commit -a -m "Update two" &&
+      id=$(git rev-list --max-count=1 HEAD) &&
+      (cd "$CVSWORK" &&
+      ! git-cvsexportcommit -c $id
+      )'
+
+test_expect_success \
+     'Retain execute bit' \
+     'mkdir G &&
+      echo executeon >G/on &&
+      chmod +x G/on &&
+      echo executeoff >G/off &&
+      git add G/on &&
+      git add G/off &&
+      git commit -a -m "Execute test" &&
+      (cd "$CVSWORK" &&
+      git-cvsexportcommit -c HEAD
+      test -x G/on &&
+      ! test -x G/off
+      )'
 
 test_done

From bfddbc5e1e68373ba96441ca228236667912265c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 10 Dec 2006 13:50:59 -0800
Subject: [PATCH 136/330] diff --numstat: show binary with '-' to match "apply
 --numstat"

This changes the --numstat output for binary files from "0 0" to
"- -" to match what "apply --numstat" does.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index 33153787b8..d6d17f4734 100644
--- a/diff.c
+++ b/diff.c
@@ -802,7 +802,10 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
 	for (i = 0; i < data->nr; i++) {
 		struct diffstat_file *file = data->files[i];
 
-		printf("%d\t%d\t", file->added, file->deleted);
+		if (file->is_binary)
+			printf("-\t-\t");
+		else
+			printf("%d\t%d\t", file->added, file->deleted);
 		if (options->line_termination &&
 		    quote_c_style(file->name, NULL, NULL, 0))
 			quote_c_style(file->name, NULL, stdout, 0);

From 9aa1757382002655cb72bad6a163ff430e08c962 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Mon, 11 Dec 2006 18:09:58 +0100
Subject: [PATCH 137/330] gitweb: Don't use Content-Encoding: header in
 git_snapshot

Do not use Content-Encoding: HTTP header in git_snapshot, using
instead type according to the snapshot type (compression type).
Some of web browser take Content-Encoding: to be _transparent_
also for downloading, and store decompressed file (with incorrect
compression suffix) on download.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 5ea3fda540..145d5b53b8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3414,8 +3414,7 @@ sub git_snapshot {
 	my $filename = basename($project) . "-$hash.tar.$suffix";
 
 	print $cgi->header(
-		-type => 'application/x-tar',
-		-content_encoding => $ctype,
+		-type => "application/$ctype",
 		-content_disposition => 'inline; filename="' . "$filename" . '"',
 		-status => '200 OK');
 

From e33fba4c5a33bce748d2c8a1c7a38b78a1fd2cda Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 10 Dec 2006 13:25:46 +0100
Subject: [PATCH 138/330] gitweb: Show target of symbolic link in "tree" view

In "tree" view (git_print_tree_entry subroutine), for entries which are
symbolic links, add " -> link_target" after file name (a la "ls -l").

Link target is _not_ hyperlinked.

While at it, correct whitespaces (tabs are for aling, spaces are for indent)
in modified git_print_tree_entry subroutine.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 42 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 34 insertions(+), 8 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 145d5b53b8..b706f7435c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1989,12 +1989,31 @@ sub git_print_log ($;%) {
 	}
 }
 
+# return link target (what link points to)
+sub git_get_link_target {
+	my $hash = shift;
+	my $link_target;
+
+	# read link
+	open my $fd, "-|", git_cmd(), "cat-file", "blob", $hash
+		or return;
+	{
+		local $/;
+		$link_target = <$fd>;
+	}
+	close $fd
+		or return;
+
+	return $link_target;
+}
+
+
 # print tree entry (row of git_tree), but without encompassing <tr> element
 sub git_print_tree_entry {
 	my ($t, $basedir, $hash_base, $have_blame) = @_;
 
 	my %base_key = ();
-	$base_key{hash_base} = $hash_base if defined $hash_base;
+	$base_key{'hash_base'} = $hash_base if defined $hash_base;
 
 	# The format of a table row is: mode list link.  Where mode is
 	# the mode of the entry, list is the name of the entry, an href,
@@ -2005,16 +2024,23 @@ sub git_print_tree_entry {
 		print "<td class=\"list\">" .
 			$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
 			                       file_name=>"$basedir$t->{'name'}", %base_key),
-			        -class => "list"}, esc_path($t->{'name'})) . "</td>\n";
+			        -class => "list"}, esc_path($t->{'name'}));
+		if (S_ISLNK(oct $t->{'mode'})) {
+			my $link_target = git_get_link_target($t->{'hash'});
+			if ($link_target) {
+				print " -> " . esc_path($link_target);
+			}
+		}
+		print "</td>\n";
 		print "<td class=\"link\">";
 		print $cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
-					     file_name=>"$basedir$t->{'name'}", %base_key)},
-			      "blob");
+		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		              "blob");
 		if ($have_blame) {
 			print " | " .
 			      $cgi->a({-href => href(action=>"blame", hash=>$t->{'hash'},
-				                           file_name=>"$basedir$t->{'name'}", %base_key)},
-				            "blame");
+			                             file_name=>"$basedir$t->{'name'}", %base_key)},
+			              "blame");
 		}
 		if (defined $hash_base) {
 			print " | " .
@@ -2036,8 +2062,8 @@ sub git_print_tree_entry {
 		print "</td>\n";
 		print "<td class=\"link\">";
 		print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
-					     file_name=>"$basedir$t->{'name'}", %base_key)},
-			      "tree");
+		                             file_name=>"$basedir$t->{'name'}", %base_key)},
+		              "tree");
 		if (defined $hash_base) {
 			print " | " .
 			      $cgi->a({-href => href(action=>"history", hash_base=>$hash_base,

From ca94601c8fcc4ddda4b48f05c748b3d52c54809c Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 10 Dec 2006 13:25:47 +0100
Subject: [PATCH 139/330] gitweb: Add generic git_object subroutine to display
 object of any type

Add generic "object" view implemented in git_object subroutine, which is
used to display object of any type; to be more exact it redirects to the
view of correct type: "blob", "tree", "commit" or "tag".  To identify object
you have to provide either hash (identifier of an object), or (in the case of
tree and blob objects) hash of commit object (hash_base) and path (file_name).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b706f7435c..d3816b8841 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -434,6 +434,7 @@ my %actions = (
 	"tags" => \&git_tags,
 	"tree" => \&git_tree,
 	"snapshot" => \&git_snapshot,
+	"object" => \&git_object,
 	# those below don't need $project
 	"opml" => \&git_opml,
 	"project_list" => \&git_project_list,
@@ -3619,6 +3620,53 @@ sub git_commit {
 	git_footer_html();
 }
 
+sub git_object {
+	# object is defined by:
+	# - hash or hash_base alone
+	# - hash_base and file_name
+	my $type;
+
+	# - hash or hash_base alone
+	if ($hash || ($hash_base && !defined $file_name)) {
+		my $object_id = $hash || $hash_base;
+
+		my $git_command = git_cmd_str();
+		open my $fd, "-|", "$git_command cat-file -t $object_id 2>/dev/null"
+			or die_error('404 Not Found', "Object does not exist");
+		$type = <$fd>;
+		chomp $type;
+		close $fd
+			or die_error('404 Not Found', "Object does not exist");
+
+	# - hash_base and file_name
+	} elsif ($hash_base && defined $file_name) {
+		$file_name =~ s,/+$,,;
+
+		system(git_cmd(), "cat-file", '-e', $hash_base) == 0
+			or die_error('404 Not Found', "Base object does not exist");
+
+		# here errors should not hapen
+		open my $fd, "-|", git_cmd(), "ls-tree", $hash_base, "--", $file_name
+			or die_error(undef, "Open git-ls-tree failed");
+		my $line = <$fd>;
+		close $fd;
+
+		#'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa	panic.c'
+		unless ($line && $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t/) {
+			die_error('404 Not Found', "File or directory for given base does not exist");
+		}
+		$type = $2;
+		$hash = $3;
+	} else {
+		die_error('404 Not Found', "Not enough information to find object");
+	}
+
+	print $cgi->redirect(-uri => href(action=>$type, -full=>1,
+	                                  hash=>$hash, hash_base=>$hash_base,
+	                                  file_name=>$file_name),
+	                     -status => '302 Found');
+}
+
 sub git_blobdiff {
 	my $format = shift || 'html';
 

From 3bf9d57051845cee996f73b5402a07cbeda84c88 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 10 Dec 2006 13:25:48 +0100
Subject: [PATCH 140/330] gitweb: Hyperlink target of symbolic link in "tree"
 view (if possible)

Make symbolic link target in "tree" view into hyperlink to generic
"object" view (as we don't know if the link target is file (blob) or
directory (tree), and if it exist at all).

Target of link is made into hyperlink when:
 * hash_base is provided (otherwise we cannot find hash
   of link target)
 * link is relative
 * in no place link goes out of root tree (top dir)

Full path of symlink target from the root dir is provided in the title
attribute of hyperlink.

Currently symbolic link name uses ordinary file style (hidden
hyperlink), while the hyperlink to symlink target uses default
hyperlink style, so it is underlined while link target which is not
made into hyperlink is not underlined.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 52 +++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 51 insertions(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d3816b8841..d902913966 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2008,6 +2008,48 @@ sub git_get_link_target {
 	return $link_target;
 }
 
+# given link target, and the directory (basedir) the link is in,
+# return target of link relative to top directory (top tree);
+# return undef if it is not possible (including absolute links).
+sub normalize_link_target {
+	my ($link_target, $basedir, $hash_base) = @_;
+
+	# we can normalize symlink target only if $hash_base is provided
+	return unless $hash_base;
+
+	# absolute symlinks (beginning with '/') cannot be normalized
+	return if (substr($link_target, 0, 1) eq '/');
+
+	# normalize link target to path from top (root) tree (dir)
+	my $path;
+	if ($basedir) {
+		$path = $basedir . '/' . $link_target;
+	} else {
+		# we are in top (root) tree (dir)
+		$path = $link_target;
+	}
+
+	# remove //, /./, and /../
+	my @path_parts;
+	foreach my $part (split('/', $path)) {
+		# discard '.' and ''
+		next if (!$part || $part eq '.');
+		# handle '..'
+		if ($part eq '..') {
+			if (@path_parts) {
+				pop @path_parts;
+			} else {
+				# link leads outside repository (outside top dir)
+				return;
+			}
+		} else {
+			push @path_parts, $part;
+		}
+	}
+	$path = join('/', @path_parts);
+
+	return $path;
+}
 
 # print tree entry (row of git_tree), but without encompassing <tr> element
 sub git_print_tree_entry {
@@ -2029,7 +2071,15 @@ sub git_print_tree_entry {
 		if (S_ISLNK(oct $t->{'mode'})) {
 			my $link_target = git_get_link_target($t->{'hash'});
 			if ($link_target) {
-				print " -> " . esc_path($link_target);
+				my $norm_target = normalize_link_target($link_target, $basedir, $hash_base);
+				if (defined $norm_target) {
+					print " -> " .
+					      $cgi->a({-href => href(action=>"object", hash_base=>$hash_base,
+					                             file_name=>$norm_target),
+					               -title => $norm_target}, esc_path($link_target));
+				} else {
+					print " -> " . esc_path($link_target);
+				}
 			}
 		}
 		print "</td>\n";

From bfe2191f79aa4e74eafc709ea41cc0999c9f5be5 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Sun, 10 Dec 2006 13:25:49 +0100
Subject: [PATCH 141/330] gitweb: SHA-1 in commit log message links to "object"
 view

Instead of checking if explicit SHA-1 in commit log message is sha1 of
commit and making link to "commit" view, make [fragment of] explicit
SHA-1 in commit log message link to "object" view.  While at it allow
to hyperlink also shortened SHA-1, from 8 characters up to full SHA-1,
instead of requiring full 40 characters of SHA-1.

This makes the following changes:

 * SHA-1 of objects which no longer exists, for example in commit
   cherry-picked from no longer existing temporary branch, or revert
   of commit in rebased branch, are no longer marked as such by not
   being made into hyperlink (and not having default hyperlink view:
   being underlined among others).  On the other hand it makes gitweb
   to not write error messages when object is not found to web serwer
   log; it also moves cost of getting type and SHA-1 validation to
   when link is clicked, and not only viewed.

 * SHA-1 of other objects: blobs, trees, tags are also hyperlinked
   and lead to appropriate view (although in the case of tags it is
   more natural to just use tag name).

 * You can put shortened SHA-1 of commit in the commit message, and it
   would be hyperlinked; it would be checked on clicking if abbrev is
   unique.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d902913966..040ee719f0 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -828,14 +828,12 @@ sub format_log_line_html {
 	my $line = shift;
 
 	$line = esc_html($line, -nbsp=>1);
-	if ($line =~ m/([0-9a-fA-F]{40})/) {
+	if ($line =~ m/([0-9a-fA-F]{8,40})/) {
 		my $hash_text = $1;
-		if (git_get_type($hash_text) eq "commit") {
-			my $link =
-				$cgi->a({-href => href(action=>"commit", hash=>$hash_text),
-				        -class => "text"}, $hash_text);
-			$line =~ s/$hash_text/$link/;
-		}
+		my $link =
+			$cgi->a({-href => href(action=>"object", hash=>$hash_text),
+			        -class => "text"}, $hash_text);
+		$line =~ s/$hash_text/$link/;
 	}
 	return $line;
 }

From a1158caeadec4d48b185068f4120d85423de5970 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Tue, 12 Dec 2006 06:41:52 +0000
Subject: [PATCH 142/330] Colourise git-branch output

I wanted to have a visual indication of which branches are local and
which are remote in git-branch -a output; however Junio was concerned
that someone might be using the output in a script.  This patch
addresses the problem by colouring the git-branch output - which in
"auto" mode won't be activated.

I've based it off the colouring code for builtin-diff.c; which means
there is a branch color configuration variable that needs setting to
something before the color will appear.

The colour parameter is "color.branch" rather than "branch.color" to
avoid clashing with the default namespace for default branch merge
definitions.

This patch chooses green for local, red for remote and bold green for
current.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c | 92 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 87 insertions(+), 5 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 3d5cb0e4b2..7c87b8d579 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -5,6 +5,7 @@
  * Based on git-branch.sh by Junio C Hamano.
  */
 
+#include "color.h"
 #include "cache.h"
 #include "refs.h"
 #include "commit.h"
@@ -17,6 +18,58 @@ static const char builtin_branch_usage[] =
 static const char *head;
 static unsigned char head_sha1[20];
 
+static int branch_use_color;
+static char branch_colors[][COLOR_MAXLEN] = {
+	"\033[m",	/* reset */
+	"",		/* PLAIN (normal) */
+	"\033[31m",	/* REMOTE (red) */
+	"\033[32m",	/* LOCAL (green) */
+	"\033[1;32m",	/* CURRENT (boldgreen) */
+};
+enum color_branch {
+	COLOR_BRANCH_RESET = 0,
+	COLOR_BRANCH_PLAIN = 1,
+	COLOR_BRANCH_REMOTE = 2,
+	COLOR_BRANCH_LOCAL = 3,
+	COLOR_BRANCH_CURRENT = 4,
+};
+
+static int parse_branch_color_slot(const char *var, int ofs)
+{
+	if (!strcasecmp(var+ofs, "plain"))
+		return COLOR_BRANCH_PLAIN;
+	if (!strcasecmp(var+ofs, "reset"))
+		return COLOR_BRANCH_RESET;
+	if (!strcasecmp(var+ofs, "remote"))
+		return COLOR_BRANCH_REMOTE;
+	if (!strcasecmp(var+ofs, "local"))
+		return COLOR_BRANCH_LOCAL;
+	if (!strcasecmp(var+ofs, "current"))
+		return COLOR_BRANCH_CURRENT;
+	die("bad config variable '%s'", var);
+}
+
+int git_branch_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "color.branch")) {
+		branch_use_color = git_config_colorbool(var, value);
+		return 0;
+	}
+	if (!strncmp(var, "color.branch.", 13)) {
+		int slot = parse_branch_color_slot(var, 13);
+		color_parse(value, var, branch_colors[slot]);
+		return 0;
+	}
+	return git_default_config(var, value);
+}
+
+const char *branch_get_color(enum color_branch ix)
+{
+	if (branch_use_color)
+		return branch_colors[ix];
+	return "";
+}
+
 static int in_merge_bases(const unsigned char *sha1,
 			  struct commit *rev1,
 			  struct commit *rev2)
@@ -183,6 +236,7 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
 	int i;
 	char c;
 	struct ref_list ref_list;
+	int color;
 
 	memset(&ref_list, 0, sizeof(ref_list));
 	ref_list.kinds = kinds;
@@ -191,18 +245,38 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
 	qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
 	for (i = 0; i < ref_list.index; i++) {
+		switch( ref_list.list[i].kind ) {
+			case REF_LOCAL_BRANCH:
+				color = COLOR_BRANCH_LOCAL;
+				break;
+			case REF_REMOTE_BRANCH:
+				color = COLOR_BRANCH_REMOTE;
+				break;
+			default:
+				color = COLOR_BRANCH_PLAIN;
+				break;
+		}
+
 		c = ' ';
 		if (ref_list.list[i].kind == REF_LOCAL_BRANCH &&
-				!strcmp(ref_list.list[i].name, head))
+				!strcmp(ref_list.list[i].name, head)) {
 			c = '*';
+			color = COLOR_BRANCH_CURRENT;
+		}
 
 		if (verbose) {
-			printf("%c %-*s", c, ref_list.maxwidth,
-			       ref_list.list[i].name);
+			printf("%c %s%-*s%s", c,
+					branch_get_color(color),
+					ref_list.maxwidth,
+					ref_list.list[i].name,
+					branch_get_color(COLOR_BRANCH_RESET));
 			print_ref_info(ref_list.list[i].sha1, abbrev);
 		}
 		else
-			printf("%c %s\n", c, ref_list.list[i].name);
+			printf("%c %s%s%s\n", c,
+					branch_get_color(color),
+					ref_list.list[i].name,
+					branch_get_color(COLOR_BRANCH_RESET));
 	}
 
 	free_ref_list(&ref_list);
@@ -253,7 +327,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	int kinds = REF_LOCAL_BRANCH;
 	int i;
 
-	git_config(git_default_config);
+	git_config(git_branch_config);
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
@@ -297,6 +371,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 			verbose = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--color")) {
+			branch_use_color = 1;
+			continue;
+		}
+		if (!strcmp(arg, "--no-color")) {
+			branch_use_color = 0;
+			continue;
+		}
 		usage(builtin_branch_usage);
 	}
 

From 59f867400650b39568e4a7f96bd60f3a0072dbda Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Tue, 12 Dec 2006 12:01:47 -0500
Subject: [PATCH 143/330] Move Fink and Ports check to after config file

Putting NO_FINK or NO_DARWIN_PORTS in config.mak is ignored because the
checks are done before the config is included.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile | 27 +++++++++++++++------------
 1 file changed, 15 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index 36ce8cd606..c5a180490b 100644
--- a/Makefile
+++ b/Makefile
@@ -334,18 +334,6 @@ ifeq ($(uname_S),Darwin)
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_STRLCPY = YesPlease
-	ifndef NO_FINK
-		ifeq ($(shell test -d /sw/lib && echo y),y)
-			BASIC_CFLAGS += -I/sw/include
-			BASIC_LDFLAGS += -L/sw/lib
-		endif
-	endif
-	ifndef NO_DARWIN_PORTS
-		ifeq ($(shell test -d /opt/local/lib && echo y),y)
-			BASIC_CFLAGS += -I/opt/local/include
-			BASIC_LDFLAGS += -L/opt/local/lib
-		endif
-	endif
 endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
@@ -423,6 +411,21 @@ endif
 -include config.mak.autogen
 -include config.mak
 
+ifeq ($(uname_S),Darwin)
+	ifndef NO_FINK
+		ifeq ($(shell test -d /sw/lib && echo y),y)
+			BASIC_CFLAGS += -I/sw/include
+			BASIC_LDFLAGS += -L/sw/lib
+		endif
+	endif
+	ifndef NO_DARWIN_PORTS
+		ifeq ($(shell test -d /opt/local/lib && echo y),y)
+			BASIC_CFLAGS += -I/opt/local/include
+			BASIC_LDFLAGS += -L/opt/local/lib
+		endif
+	endif
+endif
+
 ifdef WITH_OWN_SUBPROCESS_PY
 	PYMODULES += compat/subprocess.py
 else

From 0d7a6e4ef9e2dc458a9a56ab73638d97f4e75d87 Mon Sep 17 00:00:00 2001
From: Alex Riesen <raa.lkml@gmail.com>
Date: Tue, 12 Dec 2006 18:34:02 +0100
Subject: [PATCH 144/330] Clarify fetch error for missing objects.

Otherwise there're such things like:

    Cannot obtain needed none 9a6e87b60dbd2305c95cecce7d9d60f849a0658d
    while processing commit 0000000000000000000000000000000000000000.

which while looks weird. What is the none needed for?

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 fetch.c | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/fetch.c b/fetch.c
index c426c04997..663b4b2f42 100644
--- a/fetch.c
+++ b/fetch.c
@@ -22,14 +22,15 @@ void pull_say(const char *fmt, const char *hex)
 		fprintf(stderr, fmt, hex);
 }
 
-static void report_missing(const char *what, const unsigned char *missing)
+static void report_missing(const struct object *obj)
 {
 	char missing_hex[41];
-
-	strcpy(missing_hex, sha1_to_hex(missing));;
-	fprintf(stderr,
-		"Cannot obtain needed %s %s\nwhile processing commit %s.\n",
-		what, missing_hex, sha1_to_hex(current_commit_sha1));
+	strcpy(missing_hex, sha1_to_hex(obj->sha1));;
+	fprintf(stderr, "Cannot obtain needed %s %s\n",
+		obj->type ? typename(obj->type): "object", missing_hex);
+	if (!is_null_sha1(current_commit_sha1))
+		fprintf(stderr, "while processing commit %s.\n",
+			sha1_to_hex(current_commit_sha1));
 }
 
 static int process(struct object *obj);
@@ -177,7 +178,7 @@ static int loop(void)
 		 */
 		if (! (obj->flags & TO_SCAN)) {
 			if (fetch(obj->sha1)) {
-				report_missing(typename(obj->type), obj->sha1);
+				report_missing(obj);
 				return -1;
 			}
 		}

From d2a9a87b8a98e3b3797c6c1e5aa2269f36c2b47a Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 12 Dec 2006 14:47:00 -0800
Subject: [PATCH 145/330] git-svn: enable logging of information not supported
 by git

The changes are now tracked in
  $GIT_DIR/svn/$GIT_SVN_ID/untracked.log

Information in the untracked.log include:
  * the addition and removal of empty directories
    (changes of these will also warn the user)
  * file and directory property changes, including (but not
    limited to) svk:merge and svn:externals
  * revision properties (revprops) are also tracked
  * users will be warned of 'absent' file and directories
    (if users are forbidden access)

Fields in entries are separated by spaces; "unsafe" characters
are URI-encoded so that each entry takes exactly one line.

There is currently no automated parser for dealing with the data
in untracked.log, but it should be possible to write one to
create empty directories on checkout and manage
externals/subprojects.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 202 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 184 insertions(+), 18 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 1f8a3b0e07..06e89ffecf 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,6 +21,16 @@ $ENV{TZ} = 'UTC';
 $ENV{LC_ALL} = 'C';
 $| = 1; # unbuffer STDOUT
 
+# properties that we do not log:
+my %SKIP = ( 'svn:wc:ra_dav:version-url' => 1,
+             'svn:special' => 1,
+             'svn:executable' => 1,
+             'svn:entry:committed-rev' => 1,
+             'svn:entry:last-author' => 1,
+             'svn:entry:uuid' => 1,
+             'svn:entry:committed-date' => 1,
+);
+
 sub fatal (@) { print STDERR $@; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
@@ -2902,7 +2912,7 @@ sub libsvn_dup_ra {
 }
 
 sub libsvn_get_file {
-	my ($gui, $f, $rev, $chg) = @_;
+	my ($gui, $f, $rev, $chg, $untracked) = @_;
 	$f =~ s#^/##;
 	print "\t$chg\t$f\n" unless $_q;
 
@@ -2940,11 +2950,25 @@ sub libsvn_get_file {
 		waitpid $pid, 0;
 		$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
 	}
+	%{$untracked->{file_prop}->{$f}} = %$props;
 	print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
 }
 
+sub uri_encode {
+	my ($f) = @_;
+	$f =~ s#([^a-zA-Z0-9\*!\:_\./\-])#uc sprintf("%%%02x",ord($1))#eg;
+	$f
+}
+
+sub uri_decode {
+	my ($f) = @_;
+	$f =~ tr/+/ /;
+	$f =~ s/%([A-F0-9]{2})/chr hex($1)/ge;
+	$f
+}
+
 sub libsvn_log_entry {
-	my ($rev, $author, $date, $msg, $parents) = @_;
+	my ($rev, $author, $date, $msg, $parents, $untracked) = @_;
 	my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
 					 (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
 				or die "Unable to parse date: $date\n";
@@ -2952,8 +2976,65 @@ sub libsvn_log_entry {
 		die "Author: $author not defined in $_authors file\n";
 	}
 	$msg = '' if ($rev == 0 && !defined $msg);
-	return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
-		author => $author, msg => $msg."\n", parents => $parents || [] }
+
+	open my $un, '>>', "$GIT_SVN_DIR/unhandled.log" or croak $!;
+	my $h;
+	print $un "r$rev\n" or croak $!;
+	$h = $untracked->{empty};
+	foreach (sort keys %$h) {
+		my $act = $h->{$_} ? '+empty_dir' : '-empty_dir';
+		print $un "  $act: ", uri_encode($_), "\n" or croak $!;
+		warn "W: $act: $_\n";
+	}
+	foreach my $t (qw/dir_prop file_prop/) {
+		$h = $untracked->{$t} or next;
+		foreach my $path (sort keys %$h) {
+			my $ppath = $path eq '' ? '.' : $path;
+			foreach my $prop (sort keys %{$h->{$path}}) {
+				next if $SKIP{$prop};
+				my $v = $h->{$path}->{$prop};
+				if (defined $v) {
+					print $un "  +$t: ",
+						  uri_encode($ppath), ' ',
+						  uri_encode($prop), ' ',
+						  uri_encode($v), "\n"
+						  or croak $!;
+				} else {
+					print $un "  -$t: ",
+						  uri_encode($ppath), ' ',
+						  uri_encode($prop), "\n"
+						  or croak $!;
+				}
+			}
+		}
+	}
+	foreach my $t (qw/absent_file absent_directory/) {
+		$h = $untracked->{$t} or next;
+		foreach my $parent (sort keys %$h) {
+			foreach my $path (sort @{$h->{$parent}}) {
+				print $un "  $t: ",
+				      uri_encode("$parent/$path"), "\n"
+				      or croak $!;
+				warn "W: $t: $parent/$path ",
+				     "Insufficient permissions?\n";
+			}
+		}
+	}
+
+	# revprops (make this optional? it's an extra network trip...)
+	my $pool = SVN::Pool->new;
+	my $rp = $SVN->rev_proplist($rev, $pool);
+	foreach (sort keys %$rp) {
+		next if /^svn:(?:author|date|log)$/;
+		print $un "  rev_prop: ", uri_encode($_), ' ',
+		          uri_encode($rp->{$_}), "\n";
+	}
+	$pool->clear;
+	close $un or croak $!;
+
+	{ revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
+	  author => $author, msg => $msg."\n", parents => $parents || [],
+	  revprops => $rp }
 }
 
 sub process_rm {
@@ -2972,9 +3053,11 @@ sub process_rm {
 		}
 		print "\tD\t$f/\n" unless $q;
 		close $ls or croak $?;
+		return $SVN::Node::dir;
 	} else {
 		print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
 		print "\tD\t$f\n" unless $q;
+		return $SVN::Node::file;
 	}
 }
 
@@ -2995,13 +3078,14 @@ sub libsvn_fetch_delta {
 	unless ($ed->{git_commit_ok}) {
 		die "SVN connection failed somewhere...\n";
 	}
-	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
+	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ed);
 }
 
 sub libsvn_fetch_full {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
 	my %amr;
+	my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
 	my $p = $SVN->{svn_path};
 	foreach my $f (keys %$paths) {
 		my $m = $paths->{$f}->action();
@@ -3012,8 +3096,11 @@ sub libsvn_fetch_full {
 			$f =~ s#^/##;
 		}
 		if ($m =~ /^[DR]$/) {
-			process_rm($gui, $last_commit, $f, $_q);
-			next if $m eq 'D';
+			my $t = process_rm($gui, $last_commit, $f, $_q);
+			if ($m eq 'D') {
+				$ut->{empty}->{$f} = 0 if $t == $SVN::Node::dir;
+				next;
+			}
 			# 'R' can be file replacements, too, right?
 		}
 		my $pool = SVN::Pool->new;
@@ -3026,18 +3113,32 @@ sub libsvn_fetch_full {
 			}
 		} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
 			my @traversed = ();
-			libsvn_traverse($gui, '', $f, $rev, \@traversed);
-			foreach (@traversed) {
-				$amr{$_} = $m;
+			libsvn_traverse($gui, '', $f, $rev, \@traversed, $ut);
+			if (@traversed) {
+				foreach (@traversed) {
+					$amr{$_} = $m;
+				}
+			} else {
+				my ($dir, $file) = ($f =~ m#^(.*?)/?([^/]+)$#);
+				delete $ut->{empty}->{$dir};
+				$ut->{empty}->{$f} = 1;
 			}
 		}
 		$pool->clear;
 	}
 	foreach (keys %amr) {
-		libsvn_get_file($gui, $_, $rev, $amr{$_});
+		libsvn_get_file($gui, $_, $rev, $amr{$_}, $ut);
+		my ($d) = ($_ =~ m#^(.*?)/?(?:[^/]+)$#);
+		delete $ut->{empty}->{$d};
+	}
+	unless (exists $ut->{dir_prop}->{''}) {
+		my $pool = SVN::Pool->new;
+		my (undef, undef, $props) = $SVN->get_dir('', $rev, $pool);
+		%{$ut->{dir_prop}->{''}} = %$props;
+		$pool->clear;
 	}
 	close $gui or croak $?;
-	return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);
+	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut);
 }
 
 sub svn_grab_base_rev {
@@ -3098,25 +3199,38 @@ sub libsvn_parse_revision {
 }
 
 sub libsvn_traverse {
-	my ($gui, $pfx, $path, $rev, $files) = @_;
+	my ($gui, $pfx, $path, $rev, $files, $untracked) = @_;
 	my $cwd = length $pfx ? "$pfx/$path" : $path;
 	my $pool = SVN::Pool->new;
 	$cwd =~ s#^\Q$SVN->{svn_path}\E##;
+	my $nr = 0;
 	my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
+	%{$untracked->{dir_prop}->{$cwd}} = %$props;
 	foreach my $d (keys %$dirent) {
 		my $t = $dirent->{$d}->kind;
 		if ($t == $SVN::Node::dir) {
-			libsvn_traverse($gui, $cwd, $d, $rev, $files);
+			my $i = libsvn_traverse($gui, $cwd, $d, $rev,
+			                        $files, $untracked);
+			if ($i) {
+				$nr += $i;
+			} else {
+				$untracked->{empty}->{"$cwd/$d"} = 1;
+			}
 		} elsif ($t == $SVN::Node::file) {
+			$nr++;
 			my $file = "$cwd/$d";
 			if (defined $files) {
 				push @$files, $file;
 			} else {
-				libsvn_get_file($gui, $file, $rev, 'A');
+				libsvn_get_file($gui, $file, $rev, 'A',
+				                $untracked);
+				my ($dir) = ($file =~ m#^(.*?)/?(?:[^/]+)$#);
+				delete $untracked->{empty}->{$dir};
 			}
 		}
 	}
 	$pool->clear;
+	$nr;
 }
 
 sub libsvn_traverse_ignore {
@@ -3255,6 +3369,7 @@ sub libsvn_new_tree {
 		return $log_entry;
 	}
 	my ($paths, $rev, $author, $date, $msg) = @_;
+	my $ut;
 	if ($_xfer_delta) {
 		my $pool = SVN::Pool->new;
 		my $ed = SVN::Git::Fetcher->new({q => $_q});
@@ -3266,12 +3381,14 @@ sub libsvn_new_tree {
 		unless ($ed->{git_commit_ok}) {
 			die "SVN connection failed somewhere...\n";
 		}
+		$ut = $ed;
 	} else {
+		$ut = { empty => {}, dir_prop => {}, file_prop => {} };
 		open my $gui, '| git-update-index -z --index-info' or croak $!;
-		libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
+		libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut);
 		close $gui or croak $?;
 	}
-	return libsvn_log_entry($rev, $author, $date, $msg);
+	libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
 }
 
 sub find_graft_path_commit {
@@ -3456,13 +3573,28 @@ sub new {
 	$self->{gui} = $gui;
 	$self->{c} = $git_svn->{c} if exists $git_svn->{c};
 	$self->{q} = $git_svn->{q};
+	$self->{empty} = {};
+	$self->{dir_prop} = {};
+	$self->{file_prop} = {};
+	$self->{absent_dir} = {};
+	$self->{absent_file} = {};
 	require Digest::MD5;
 	$self;
 }
 
+sub open_root {
+	{ path => '' };
+}
+
+sub open_directory {
+	my ($self, $path, $pb, $rev) = @_;
+	{ path => $path };
+}
+
 sub delete_entry {
 	my ($self, $path, $rev, $pb) = @_;
-	process_rm($self->{gui}, $self->{c}, $path, $self->{q});
+	my $t = process_rm($self->{gui}, $self->{c}, $path, $self->{q});
+	$self->{empty}->{$path} = 0 if $t == $SVN::Node::dir;
 	undef;
 }
 
@@ -3479,10 +3611,41 @@ sub open_file {
 
 sub add_file {
 	my ($self, $path, $pb, $cp_path, $cp_rev) = @_;
+	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
+	delete $self->{empty}->{$dir};
 	{ path => $path, mode_a => 100644, mode_b => 100644,
 	  pool => SVN::Pool->new, action => 'A' };
 }
 
+sub add_directory {
+	my ($self, $path, $cp_path, $cp_rev) = @_;
+	my ($dir, $file) = ($path =~ m#^(.*?)/?([^/]+)$#);
+	delete $self->{empty}->{$dir};
+	$self->{empty}->{$path} = 1;
+	{ path => $path };
+}
+
+sub change_dir_prop {
+	my ($self, $db, $prop, $value) = @_;
+	$self->{dir_prop}->{$db->{path}} ||= {};
+	$self->{dir_prop}->{$db->{path}}->{$prop} = $value;
+	undef;
+}
+
+sub absent_directory {
+	my ($self, $path, $pb) = @_;
+	$self->{absent_dir}->{$pb->{path}} ||= [];
+	push @{$self->{absent_dir}->{$pb->{path}}}, $path;
+	undef;
+}
+
+sub absent_file {
+	my ($self, $path, $pb) = @_;
+	$self->{absent_file}->{$pb->{path}} ||= [];
+	push @{$self->{absent_file}->{$pb->{path}}}, $path;
+	undef;
+}
+
 sub change_file_prop {
 	my ($self, $fb, $prop, $value) = @_;
 	if ($prop eq 'svn:executable') {
@@ -3491,6 +3654,9 @@ sub change_file_prop {
 		}
 	} elsif ($prop eq 'svn:special') {
 		$fb->{mode_b} = defined $value ? 120000 : 100644;
+	} else {
+		$self->{file_prop}->{$fb->{path}} ||= {};
+		$self->{file_prop}->{$fb->{path}}->{$prop} = $value;
 	}
 	undef;
 }

From dd31da2fdc199132c9fd42023aea5b33672d73cc Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 12 Dec 2006 14:47:01 -0800
Subject: [PATCH 146/330] git-svn: allow dcommit to take an alternate head

Previously dcommit would unconditionally commit all patches
up-to and including the current HEAD.  Now if an optional
command-line argument is specified, it will only commit
up to the specified revision.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-svn.txt |  6 ++++--
 git-svn.perl              | 11 ++++++-----
 2 files changed, 10 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index a45067e164..c589a98630 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -57,11 +57,13 @@ See '<<fetch-args,Additional Fetch Arguments>>' if you are interested in
 manually joining branches on commit.
 
 'dcommit'::
-	Commit all diffs from the current HEAD directly to the SVN
+	Commit all diffs from a specified head directly to the SVN
 	repository, and then rebase or reset (depending on whether or
-	not there is a diff between SVN and HEAD).  It is recommended
+	not there is a diff between SVN and head).  It is recommended
 	that you run git-svn fetch and rebase (not pull) your commits
 	against the latest changes in the SVN repository.
+	An optional command-line argument may be specified as an
+	alternative to HEAD.
 	This is advantageous over 'commit' (below) because it produces
 	cleaner, more linear history.
 
diff --git a/git-svn.perl b/git-svn.perl
index 06e89ffecf..819584baf5 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -604,8 +604,9 @@ sub commit_lib {
 }
 
 sub dcommit {
+	my $head = shift || 'HEAD';
 	my $gs = "refs/remotes/$GIT_SVN";
-	chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD"));
+	chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..$head"));
 	my $last_rev;
 	foreach my $d (reverse @refs) {
 		if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
@@ -632,16 +633,16 @@ sub dcommit {
 	}
 	return if $_dry_run;
 	fetch();
-	my @diff = safe_qx(qw/git-diff-tree HEAD/, $gs);
+	my @diff = safe_qx('git-diff-tree', $head, $gs);
 	my @finish;
 	if (@diff) {
 		@finish = qw/rebase/;
 		push @finish, qw/--merge/ if $_merge;
 		push @finish, "--strategy=$_strategy" if $_strategy;
-		print STDERR "W: HEAD and $gs differ, using @finish:\n", @diff;
+		print STDERR "W: $head and $gs differ, using @finish:\n", @diff;
 	} else {
-		print "No changes between current HEAD and $gs\n",
-		      "Hard resetting to the latest $gs\n";
+		print "No changes between current $head and $gs\n",
+		      "Resetting to the latest $gs\n";
 		@finish = qw/reset --mixed/;
 	}
 	sys('git', @finish, $gs);

From 6fda05aebe6e36bfe87113f85b6e70f2b9b73e42 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 12 Dec 2006 14:47:02 -0800
Subject: [PATCH 147/330] git-svn: correctly display fatal() error messages

If I wanted to print $@, I'd pass $@ to fatal().  This looks like
a stupid typo on my part.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index 819584baf5..c746a3c62a 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -31,7 +31,7 @@ my %SKIP = ( 'svn:wc:ra_dav:version-url' => 1,
              'svn:entry:committed-date' => 1,
 );
 
-sub fatal (@) { print STDERR $@; exit 1 }
+sub fatal (@) { print STDERR @_; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
 # use eval { require SVN::... } to make it lazy load

From c93be3b539da06e2d89d613448dfadf83c48de53 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 12 Dec 2006 16:36:16 -0800
Subject: [PATCH 148/330] add test case for recursive merge

This test case is based on the bug report by Shawn Pearce.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t6024-recursive-merge.sh | 70 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
 create mode 100755 t/t6024-recursive-merge.sh

diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
new file mode 100755
index 0000000000..9416c271eb
--- /dev/null
+++ b/t/t6024-recursive-merge.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='Test merge without common ancestors'
+. ./test-lib.sh
+
+# This scenario is based on a real-world repository of Shawn Pearce.
+
+# 1 - A - D - F
+#   \   X   /
+#     B   X
+#       X   \
+# 2 - C - E - G
+
+export GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
+echo 1 > a1
+git add a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1
+
+git checkout -b A master
+echo A > a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:01" git commit -m A a1
+
+git checkout -b B master
+echo B > a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:02" git commit -m B a1
+
+git checkout -b D A
+git-rev-parse B > .git/MERGE_HEAD
+echo D > a1
+git update-index a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:03" git commit -m D
+
+git symbolic-ref HEAD refs/heads/other
+echo 2 > a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:04" git commit -m 2 a1
+
+git checkout -b C
+echo C > a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:05" git commit -m C a1
+
+git checkout -b E C
+git-rev-parse B > .git/MERGE_HEAD
+echo E > a1
+git update-index a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:06" git commit -m E
+
+git checkout -b G E
+git-rev-parse A > .git/MERGE_HEAD
+echo G > a1
+git update-index a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:07" git commit -m G
+
+git checkout -b F D
+git-rev-parse C > .git/MERGE_HEAD
+echo F > a1
+git update-index a1
+GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
+
+test_expect_failure "combined merge conflicts" "git merge -m final G"
+
+git ls-files --stage > out
+cat > expect << EOF
+100644 f70f10e4db19068f79bc43844b49f3eece45c4e8 1	a1
+100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2	a1
+100644 fd7923529855d0b274795ae3349c5e0438333979 3	a1
+EOF
+
+test_expect_success "virtual trees were processed" "diff -u expect out"
+
+test_done

From c53d696bcc2894b0df277e617740b15bac794df9 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 12 Dec 2006 16:45:00 -0800
Subject: [PATCH 149/330] git-svn: correctly handle packed-refs in
 refs/remotes/

We now use git-rev-parse universally to read refs, instead
of our own file_to_s function (which I plan on removing).

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index c746a3c62a..15254e4795 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2027,9 +2027,17 @@ sub git_commit {
 
 	# just in case we clobber the existing ref, we still want that ref
 	# as our parent:
-	if (my $cur = eval { file_to_s("$GIT_DIR/refs/remotes/$GIT_SVN") }) {
+	open my $null, '>', '/dev/null' or croak $!;
+	open my $stderr, '>&', \*STDERR or croak $!;
+	open STDERR, '>&', $null or croak $!;
+	if (my $cur = eval { safe_qx('git-rev-parse',
+	                             "refs/remotes/$GIT_SVN^0") }) {
+		chomp $cur;
 		push @tmp_parents, $cur;
 	}
+	open STDERR, '>&', $stderr or croak $!;
+	close $stderr or croak $!;
+	close $null or croak $!;
 
 	if (exists $tree_map{$tree}) {
 		foreach my $p (@{$tree_map{$tree}}) {

From e2b7008752d85874919ea718d098fec01b4a9019 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 13 Dec 2006 00:01:41 +0100
Subject: [PATCH 150/330] Get rid of the dependency on RCS' merge program

Now that we have git-merge-file, an RCS merge lookalike, we no longer
need it. So long, merge, and thanks for all the fish!

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore                        |  1 +
 Documentation/git-merge-index.txt |  4 +-
 INSTALL                           |  9 ----
 git-cvsserver.perl                |  2 +-
 git-rerere.perl                   |  2 +-
 git.spec.in                       |  2 +-
 merge-file.c                      | 73 +++++--------------------------
 t/t0000-basic.sh                  | 14 +-----
 8 files changed, 19 insertions(+), 88 deletions(-)

diff --git a/.gitignore b/.gitignore
index 7f2cd55088..d706dd92c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,6 +60,7 @@ git-mailsplit
 git-merge
 git-merge-base
 git-merge-index
+git-merge-file
 git-merge-tree
 git-merge-octopus
 git-merge-one-file
diff --git a/Documentation/git-merge-index.txt b/Documentation/git-merge-index.txt
index 6cd0601082..0cf505ea84 100644
--- a/Documentation/git-merge-index.txt
+++ b/Documentation/git-merge-index.txt
@@ -40,8 +40,8 @@ If "git-merge-index" is called with multiple <file>s (or -a) then it
 processes them in turn only stopping if merge returns a non-zero exit
 code.
 
-Typically this is run with the a script calling the merge command from
-the RCS package.
+Typically this is run with the a script calling git's imitation of
+the merge command from the RCS package.
 
 A sample script called "git-merge-one-file" is included in the
 distribution.
diff --git a/INSTALL b/INSTALL
index 8f69039fb9..b5dd9f0abb 100644
--- a/INSTALL
+++ b/INSTALL
@@ -82,15 +82,6 @@ Issues of note:
 	  do that even if it wasn't for git.  There's no point in living
 	  in the dark ages any more. 
 
-	- "merge", the standard UNIX three-way merge program.  It usually
-	  comes with the "rcs" package on most Linux distributions, so if
-	  you have a developer install you probably have it already, but a
-	  "graphical user desktop" install might have left it out.
-
-	  You'll only need the merge program if you do development using
-	  git, and if you only use git to track other peoples work you'll
-	  never notice the lack of it. 
-
         - "wish", the Tcl/Tk windowing shell is used in gitk to show the
           history graphically
 
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index ca519b7e49..55ff83b1a3 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -945,7 +945,7 @@ sub req_update
 
             $log->debug("Temporary directory for merge is $dir");
 
-            my $return = system("merge", $file_local, $file_old, $file_new);
+            my $return = system("git merge-file", $file_local, $file_old, $file_new);
             $return >>= 8;
 
             if ( $return == 0 )
diff --git a/git-rerere.perl b/git-rerere.perl
index d3664ff491..2e8dbbd4ea 100755
--- a/git-rerere.perl
+++ b/git-rerere.perl
@@ -154,7 +154,7 @@ sub find_conflict {
 sub merge {
 	my ($name, $path) = @_;
 	record_preimage($path, "$rr_dir/$name/thisimage");
-	unless (system('merge', map { "$rr_dir/$name/${_}image" }
+	unless (system('git merge-file', map { "$rr_dir/$name/${_}image" }
 		       qw(this pre post))) {
 		my $in;
 		open $in, "<$rr_dir/$name/thisimage" or
diff --git a/git.spec.in b/git.spec.in
index f2374b7331..fb95e37594 100644
--- a/git.spec.in
+++ b/git.spec.in
@@ -24,7 +24,7 @@ This is a dummy package which brings in all subpackages.
 %package core
 Summary:	Core git tools
 Group:		Development/Tools
-Requires:	zlib >= 1.2, rsync, rcs, curl, less, openssh-clients, expat
+Requires:	zlib >= 1.2, rsync, curl, less, openssh-clients, expat
 %description core
 This is a stupid (but extremely fast) directory content manager.  It
 doesn't do a whole lot, but what it _does_ do is track directory
diff --git a/merge-file.c b/merge-file.c
index fc9b148993..69dc1ebbf7 100644
--- a/merge-file.c
+++ b/merge-file.c
@@ -3,52 +3,6 @@
 #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 = xstrdup(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;
@@ -72,22 +26,19 @@ static void free_mmfile(mmfile_t *f)
 
 static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
 {
-	void *res;
-	const char *t1, *t2, *t3;
+	mmbuffer_t res;
+	xpparam_t xpp;
+	int merge_status;
 
-	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;
+	memset(&xpp, 0, sizeof(xpp));
+	merge_status = xdl_merge(base, our, ".our", their, ".their",
+		&xpp, XDL_MERGE_ZEALOUS, &res);
+
+	if (merge_status < 0)
+		return NULL;
+
+	*size = res.size;
+	return res.ptr;
 }
 
 static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf)
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 81f3bedc90..3260d1d7a7 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -19,11 +19,7 @@ modification *should* take notice and update the test vectors here.
 '
 
 ################################################################
-# It appears that people are getting bitten by not installing
-# 'merge' (usually part of RCS package in binary distributions).
-# Check this and error out before running any tests.  Also catch
-# the bogosity of trying to run tests without building while we
-# are at it.
+# It appears that people try to run tests without building...
 
 ../git >/dev/null
 if test $? != 1
@@ -32,14 +28,6 @@ then
 	exit 1
 fi
 
-merge >/dev/null 2>/dev/null
-if test $? = 127
-then
-	echo >&2 'You do not seem to have "merge" installed.
-Please check INSTALL document.'
-	exit 1
-fi
-
 . ./test-lib.sh
 
 ################################################################

From f953831e030d3ece7346bdb5c4fde4fde43c925e Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 13 Dec 2006 04:05:39 +0100
Subject: [PATCH 151/330] merge-recursive: add/add really is modify/modify with
 an empty base

Unify the handling for cases C (add/add) and D (modify/modify).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 merge-recursive.c          | 44 +++++++++++++-------------------------
 t/t6024-recursive-merge.sh | 12 ++++++++++-
 2 files changed, 26 insertions(+), 30 deletions(-)
 mode change 100755 => 100644 t/t6024-recursive-merge.sh

diff --git a/merge-recursive.c b/merge-recursive.c
index 9d53bcd5d6..58f2cb4ed3 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -610,6 +610,12 @@ static void fill_mm(const unsigned char *sha1, mmfile_t *mm)
 	unsigned long size;
 	char type[20];
 
+	if (!hashcmp(sha1, null_sha1)) {
+		mm->ptr = xstrdup("");
+		mm->size = 0;
+		return;
+	}
+
 	mm->ptr = read_sha1_file(sha1, type, &size);
 	if (!mm->ptr || strcmp(type, blob_type))
 		die("unable to read blob object %s", sha1_to_hex(sha1));
@@ -1045,38 +1051,17 @@ static int process_entry(const char *path, struct stage_data *entry,
 			output("Adding %s", path);
 			update_file(1, sha, mode, path);
 		}
-	} else if (!o_sha && a_sha && b_sha) {
-		/* Case C: Added in both (check for same permissions). */
-		if (sha_eq(a_sha, b_sha)) {
-			if (a_mode != b_mode) {
-				clean_merge = 0;
-				output("CONFLICT: File %s added identically in both branches, "
-				       "but permissions conflict %06o->%06o",
-				       path, a_mode, b_mode);
-				output("CONFLICT: adding with permission: %06o", a_mode);
-				update_file(0, a_sha, a_mode, path);
-			} else {
-				/* This case is handled by git-read-tree */
-				assert(0 && "This case must be handled by git-read-tree");
-			}
-		} else {
-			const char *new_path1, *new_path2;
-			clean_merge = 0;
-			new_path1 = unique_path(path, branch1);
-			new_path2 = unique_path(path, branch2);
-			output("CONFLICT (add/add): File %s added non-identically "
-			       "in both branches. Adding as %s and %s instead.",
-			       path, new_path1, new_path2);
-			remove_file(0, path, 0);
-			update_file(0, a_sha, a_mode, new_path1);
-			update_file(0, b_sha, b_mode, new_path2);
-		}
-
-	} else if (o_sha && a_sha && b_sha) {
+	} else if (a_sha && b_sha) {
+		/* Case C: Added in both (check for same permissions) and */
 		/* case D: Modified in both, but differently. */
+		const char *reason = "content";
 		struct merge_file_info mfi;
 		struct diff_filespec o, a, b;
 
+		if (!o_sha) {
+			reason = "add/add";
+			o_sha = (unsigned char *)null_sha1;
+		}
 		output("Auto-merging %s", path);
 		o.path = a.path = b.path = (char *)path;
 		hashcpy(o.sha1, o_sha);
@@ -1093,7 +1078,8 @@ static int process_entry(const char *path, struct stage_data *entry,
 			update_file(1, mfi.sha, mfi.mode, path);
 		else {
 			clean_merge = 0;
-			output("CONFLICT (content): Merge conflict in %s", path);
+			output("CONFLICT (%s): Merge conflict in %s",
+					reason, path);
 
 			if (index_only)
 				update_file(0, mfi.sha, mfi.mode, path);
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
old mode 100755
new mode 100644
index 9416c271eb..964010e764
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -58,9 +58,19 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
 
 test_expect_failure "combined merge conflicts" "git merge -m final G"
 
+cat > expect << EOF
+<<<<<<< HEAD/a1
+F
+=======
+G
+>>>>>>> 26f86b677eb03d4d956dbe108b29cb77061c1e73/a1
+EOF
+
+test_expect_success "result contains a conflict" "diff -u expect a1"
+
 git ls-files --stage > out
 cat > expect << EOF
-100644 f70f10e4db19068f79bc43844b49f3eece45c4e8 1	a1
+100644 f16f906ab60483c100d1241dfc39868de9ec9fcb 1	a1
 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2	a1
 100644 fd7923529855d0b274795ae3349c5e0438333979 3	a1
 EOF

From 25fb62905890d7860f742b4f2215fdf754ae7fee Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 00:59:58 -0800
Subject: [PATCH 152/330] git-push: document removal of remote ref with :<dst>
 pathspec

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-push.txt | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index d4ae99fa53..197f4b512f 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -49,12 +49,14 @@ corresponding remotes file---see below), then all the
 refs that exist both on the local side and on the remote
 side are updated.
 +
-Some short-cut notations are also supported.
+`tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
 +
-* `tag <tag>` means the same as `refs/tags/<tag>:refs/tags/<tag>`.
-* A parameter <ref> without a colon is equivalent to
-  <ref>`:`<ref>, hence updates <ref> in the destination from <ref>
-  in the source.
+A parameter <ref> without a colon is equivalent to
+<ref>`:`<ref>, hence updates <ref> in the destination from <ref>
+in the source.
++
+Pushing an empty <src> allows you to delete the <dst> ref from
+the remote repository.
 
 \--all::
 	Instead of naming each ref to push, specifies that all
@@ -75,7 +77,8 @@ include::urls.txt[]
 
 Author
 ------
-Written by Junio C Hamano <junkio@cox.net>
+Written by Junio C Hamano <junkio@cox.net>, later rewritten in C
+by Linus Torvalds <torvalds@osdl.org>
 
 Documentation
 --------------

From a159ca0cb7e0acdd37cb066327dcb020d95602d0 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Wed, 13 Dec 2006 09:13:28 +0000
Subject: [PATCH 153/330] Allow subcommand.color and color.subcommand color
 configuration

While adding colour to the branch command it was pointed out that a
config option like "branch.color" conflicts with the pre-existing
"branch.something" namespace used for specifying default merge urls and
branches.  The suggested solution was to flip the order of the
components to "color.branch", which I did for colourising branch.

This patch does the same thing for
  - git-log (color.diff)
  - git-status (color.status)
  - git-diff (color.diff)
  - pager (color.pager)

I haven't removed the old config options; but they should probably be
deprecated and eventually removed to prevent future namespace
collisions.  I've done this deprecation by changing the documentation
for the config file to match the new names; and adding the "color.XXX"
options to contrib/completion/git-completion.bash.

Unfortunately git-svn reads "diff.color" and "pager.color"; which I
don't like to change unilaterally.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt               | 12 ++++++------
 builtin-log.c                          |  2 +-
 config.c                               |  2 +-
 contrib/completion/git-completion.bash |  3 +++
 diff.c                                 |  4 ++--
 wt-status.c                            |  4 ++--
 6 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 21ec55797b..f5a552ee87 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -137,16 +137,16 @@ branch.<name>.merge::
 	this option, `git pull` defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
 
-pager.color::
+color.pager::
 	A boolean to enable/disable colored output when the pager is in
 	use (default is true).
 
-diff.color::
+color.diff::
 	When true (or `always`), always use colors in patch.
 	When false (or `never`), never.  When set to `auto`, use
 	colors only when the output is to the terminal.
 
-diff.color.<slot>::
+color.diff.<slot>::
 	Use customized color for diff colorization.  `<slot>`
 	specifies which part of the patch to use the specified
 	color, and is one of `plain` (context text), `meta`
@@ -271,19 +271,19 @@ showbranch.default::
 	The default set of branches for gitlink:git-show-branch[1].
 	See gitlink:git-show-branch[1].
 
-status.color::
+color.status::
 	A boolean to enable/disable color in the output of
 	gitlink:git-status[1]. May be set to `true` (or `always`),
 	`false` (or `never`) or `auto`, in which case colors are used
 	only when the output is to a terminal. Defaults to false.
 
-status.color.<slot>::
+color.status.<slot>::
 	Use customized color for status colorization. `<slot>` is
 	one of `header` (the header text of the status message),
 	`updated` (files which are updated but not committed),
 	`changed` (files which are changed but not updated in the index),
 	or `untracked` (files which are not tracked by git). The values of
-	these variables may be specified as in diff.color.<slot>.
+	these variables may be specified as in color.diff.<slot>.
 
 tar.umask::
 	By default, gitlink:git-tar-tree[1] sets file and directories modes
diff --git a/builtin-log.c b/builtin-log.c
index 7acf5d3b0c..6821a08442 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -118,7 +118,7 @@ static int git_format_config(const char *var, const char *value)
 		strcat(extra_headers, value);
 		return 0;
 	}
-	if (!strcmp(var, "diff.color")) {
+	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		return 0;
 	}
 	return git_log_config(var, value);
diff --git a/config.c b/config.c
index 3cae3901aa..1bdef44a3a 100644
--- a/config.c
+++ b/config.c
@@ -314,7 +314,7 @@ int git_default_config(const char *var, const char *value)
 		return 0;
 	}
 
-	if (!strcmp(var, "pager.color")) {
+	if (!strcmp(var, "pager.color") || !strcmp(var, "color.pager")) {
 		pager_use_color = git_config_bool(var,value);
 		return 0;
 	}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 447ec20467..9c4d23a23c 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -712,10 +712,13 @@ _git_repo_config ()
 		core.legacyHeaders
 		i18n.commitEncoding
 		diff.color
+		color.diff
 		diff.renameLimit
 		diff.renames
 		pager.color
+		color.pager
 		status.color
+		color.status
 		log.showroot
 		show.difftree
 		showbranch.default
diff --git a/diff.c b/diff.c
index 33153787b8..726b01e75e 100644
--- a/diff.c
+++ b/diff.c
@@ -60,7 +60,7 @@ int git_diff_ui_config(const char *var, const char *value)
 		diff_rename_limit_default = git_config_int(var, value);
 		return 0;
 	}
-	if (!strcmp(var, "diff.color")) {
+	if (!strcmp(var, "diff.color") || !strcmp(var, "color.diff")) {
 		diff_use_color_default = git_config_colorbool(var, value);
 		return 0;
 	}
@@ -74,7 +74,7 @@ int git_diff_ui_config(const char *var, const char *value)
 			diff_detect_rename_default = DIFF_DETECT_RENAME;
 		return 0;
 	}
-	if (!strncmp(var, "diff.color.", 11)) {
+	if (!strncmp(var, "diff.color.", 11) || !strncmp(var, "color.diff.", 11)) {
 		int slot = parse_diff_color_slot(var, 11);
 		color_parse(value, var, diff_colors[slot]);
 		return 0;
diff --git a/wt-status.c b/wt-status.c
index de1be5bc66..df582a03ef 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -297,11 +297,11 @@ void wt_status_print(struct wt_status *s)
 
 int git_status_config(const char *k, const char *v)
 {
-	if (!strcmp(k, "status.color")) {
+	if (!strcmp(k, "status.color") || !strcmp(k, "color.status")) {
 		wt_status_use_color = git_config_colorbool(k, v);
 		return 0;
 	}
-	if (!strncmp(k, "status.color.", 13)) {
+	if (!strncmp(k, "status.color.", 13) || !strncmp(k, "color.status", 13)) {
 		int slot = parse_status_slot(k, 13);
 		color_parse(v, k, wt_status_colors[slot]);
 	}

From b42a044f5942c64bbc94aa2f4637d8290748e634 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 24 Nov 2006 22:38:18 -0800
Subject: [PATCH 154/330] git-svn: exit with status 1 for test failures

Some versions of the SVN libraries cause die() to exit with 255,
and 40cf043389ef4cdf3e56e7c4268d6f302e387fa0 tightened up
test_expect_failure to reject return values >128.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index b53273eaea..e8b5c0966b 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,6 +21,7 @@ $ENV{TZ} = 'UTC';
 $ENV{LC_ALL} = 'C';
 $| = 1; # unbuffer STDOUT
 
+sub fatal (@) { print STDERR $@; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
 # use eval { require SVN::... } to make it lazy load
@@ -571,7 +572,7 @@ sub commit_lib {
 				$no = 1;
 			}
 		}
-		close $fh or croak $?;
+		close $fh or exit 1;
 		if (! defined $r_new && ! defined $cmt_new) {
 			unless ($no) {
 				die "Failed to parse revision information\n";
@@ -873,13 +874,16 @@ sub commit_diff {
 						print "Committed $_[0]\n";
 					}, @lock)
 				);
-	my $mods = libsvn_checkout_tree($ta, $tb, $ed);
-	if (@$mods == 0) {
-		print "No changes\n$ta == $tb\n";
-		$ed->abort_edit;
-	} else {
-		$ed->close_edit;
-	}
+	eval {
+		my $mods = libsvn_checkout_tree($ta, $tb, $ed);
+		if (@$mods == 0) {
+			print "No changes\n$ta == $tb\n";
+			$ed->abort_edit;
+		} else {
+			$ed->close_edit;
+		}
+	};
+	fatal "$@\n" if $@;
 	$_message = $_file = undef;
 	return $rev_committed;
 }

From 155bd0ce23144e5c7067965a22646523f1a38b51 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 12 Dec 2006 14:47:02 -0800
Subject: [PATCH 155/330] git-svn: correctly display fatal() error messages

If I wanted to print $@, I'd pass $@ to fatal().  This looks like
a stupid typo on my part.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index e8b5c0966b..599edc3dd5 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,7 +21,7 @@ $ENV{TZ} = 'UTC';
 $ENV{LC_ALL} = 'C';
 $| = 1; # unbuffer STDOUT
 
-sub fatal (@) { print STDERR $@; exit 1 }
+sub fatal (@) { print STDERR @_; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
 # use eval { require SVN::... } to make it lazy load

From f2dd1c9adfe961ea611545b018bc67e5d83ee3db Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 01:33:43 -0800
Subject: [PATCH 156/330] Revert "git-diff: Introduce --index and deprecate
 --cached."

This reverts commit 4c81c213a479e4aae0653a56ad6e8db5c31f019c.

Although --cached and --index are confusing wording, the use of
word --cached for git-diff is consistent with git-apply.  It means
"work with index without looking at the working tree".

We should probably come up with better wording for --cached, if
somebody wants to deprecate it.  But making --index and --cached
synonyms for diff while leaving them mean different things for
apply is no good.
---
 Documentation/git-diff.txt | 6 ++----
 builtin-diff.c             | 2 +-
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 3144864d85..228c4d95bd 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -22,10 +22,8 @@ the number of trees given to the command.
 
 * When one <tree-ish> is given, the working tree and the named
   tree are compared, using `git-diff-index`.  The option
-  `--index` can be given to compare the index file and
+  `--cached` can be given to compare the index file and
   the named tree.
-  `--cached` is a deprecated alias for `--index`. It's use is
-  discouraged.
 
 * When two <tree-ish>s are given, these two trees are compared
   using `git-diff-tree`.
@@ -49,7 +47,7 @@ Various ways to check your working tree::
 +
 ------------
 $ git diff            <1>
-$ git diff --index    <2>
+$ git diff --cached   <2>
 $ git diff HEAD       <3>
 ------------
 +
diff --git a/builtin-diff.c b/builtin-diff.c
index 1c535b1dd6..a6590205e8 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -137,7 +137,7 @@ static int builtin_diff_index(struct rev_info *revs,
 	int cached = 0;
 	while (1 < argc) {
 		const char *arg = argv[1];
-		if (!strcmp(arg, "--index") || !strcmp(arg, "--cached"))
+		if (!strcmp(arg, "--cached"))
 			cached = 1;
 		else
 			usage(builtin_diff_usage);

From 8371234ecaaf6e14fe3f2082a855eff1bbd79ae9 Mon Sep 17 00:00:00 2001
From: Shawn Pearce <spearce@spearce.org>
Date: Wed, 13 Dec 2006 05:42:44 -0500
Subject: [PATCH 157/330] Remove uncontested renamed files during merge.

Prior to 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae we deleted a file
from the working directory during a merge if the file existed before
the merge started but was renamed by the branch being merged in.
This broke in 65ac6e as git-merge-recursive did not actually update
the working directory on an uncontested rename.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 merge-recursive.c               |  2 +-
 t/t6023-merge-rename-nocruft.sh | 97 +++++++++++++++++++++++++++++++++
 2 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100755 t/t6023-merge-rename-nocruft.sh

diff --git a/merge-recursive.c b/merge-recursive.c
index 32e186c15e..866a4e480b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -889,7 +889,7 @@ static int process_renames(struct path_list *a_renames,
 			struct diff_filespec src_other, dst_other;
 			int try_merge, stage = a_renames == renames1 ? 3: 2;
 
-			remove_file(1, ren1_src, 1);
+			remove_file(1, ren1_src, index_only);
 
 			hashcpy(src_other.sha1, ren1->src_entry->stages[stage].sha);
 			src_other.mode = ren1->src_entry->stages[stage].mode;
diff --git a/t/t6023-merge-rename-nocruft.sh b/t/t6023-merge-rename-nocruft.sh
new file mode 100755
index 0000000000..69c66cf6fa
--- /dev/null
+++ b/t/t6023-merge-rename-nocruft.sh
@@ -0,0 +1,97 @@
+#!/bin/sh
+
+test_description='Merge-recursive merging renames'
+. ./test-lib.sh
+
+test_expect_success setup \
+'
+cat >A <<\EOF &&
+a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+c cccccccccccccccccccccccccccccccccccccccccccccccc
+d dddddddddddddddddddddddddddddddddddddddddddddddd
+e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+f ffffffffffffffffffffffffffffffffffffffffffffffff
+g gggggggggggggggggggggggggggggggggggggggggggggggg
+h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
+i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
+j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
+k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
+l llllllllllllllllllllllllllllllllllllllllllllllll
+m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
+o oooooooooooooooooooooooooooooooooooooooooooooooo
+EOF
+
+cat >M <<\EOF &&
+A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
+C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
+D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
+E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
+F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
+G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
+H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
+I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
+J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
+K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
+L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
+M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
+N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
+O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
+EOF
+
+git add A M &&
+git commit -m "initial has A and M" &&
+git branch white &&
+git branch red &&
+
+git checkout white &&
+sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
+sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
+rm -f A M &&
+git update-index --add --remove A B M N &&
+git commit -m "white renames A->B, M->N" &&
+
+git checkout red &&
+echo created by red >R &&
+git update-index --add R &&
+git commit -m "red creates R" &&
+
+git checkout master'
+
+# This test broke in 65ac6e9c3f47807cb603af07a6a9e1a43bc119ae
+test_expect_success 'merge white into red (A->B,M->N)' \
+'
+	git checkout -b red-white red &&
+	git merge white &&
+	git write-tree >/dev/null || {
+		echo "BAD: merge did not complete"
+		return 1
+	}
+
+	test -f B || {
+		echo "BAD: B does not exist in working directory"
+		return 1
+	}
+	test -f N || {
+		echo "BAD: N does not exist in working directory"
+		return 1
+	}
+	test -f R || {
+		echo "BAD: R does not exist in working directory"
+		return 1
+	}
+
+	test -f A && {
+		echo "BAD: A still exists in working directory"
+		return 1
+	}
+	test -f M && {
+		echo "BAD: M still exists in working directory"
+		return 1
+	}
+	return 0
+'
+
+test_done

From 0c4e95d083f77de03a64b65f5633ed0ba082a26e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 09:32:40 -0800
Subject: [PATCH 158/330] git merge: reword failure message.

99.9999% of the time, the command is used with a single
strategy; after a merge failure, saying "No strategy handled the
merge" is technically correct, but there is no point stressing
we tried and failed all the possibilities the user has given.

Just say that it failed.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/git-merge.sh b/git-merge.sh
index a948878b91..2f3d936b9c 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -400,7 +400,14 @@ fi
 case "$best_strategy" in
 '')
 	restorestate
-	echo >&2 "No merge strategy handled the merge."
+	case "$use_strategies" in
+	?*' '?*)
+		echo >&2 "No merge strategy handled the merge."
+		;;
+	*)
+		echo >&2 "Merge with strategy $use_strategies failed."
+		;;
+	esac
 	exit 2
 	;;
 "$wt_strategy")

From 7ef0435088f41165ece95b6f226d3c15438505a5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 00:58:28 -0800
Subject: [PATCH 159/330] spurious .sp in manpages

This is just a random hack to work around problems people seem
to be seeing in manpage backend of xmlto (it appears we are
getting ".sp" at the end of line without line break).

Could people test this out?

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/callouts.xsl | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl
index ad03755d8f..6a361a2136 100644
--- a/Documentation/callouts.xsl
+++ b/Documentation/callouts.xsl
@@ -13,4 +13,18 @@
 	<xsl:apply-templates/>
 	<xsl:text>.br&#10;</xsl:text>
 </xsl:template>
+
+<!-- sorry, this is not about callouts, but attempts to work around
+ spurious .sp at the tail of the line docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+  <xsl:variable name="content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($content)"/>
+  <xsl:if test="not(ancestor::authorblurb) and
+                not(ancestor::personblurb)">
+    <xsl:text>&#10;&#10;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
 </xsl:stylesheet>

From 411fb8baa6862b76f7bdd9fc0d5844855a4db589 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 10:03:39 -0800
Subject: [PATCH 160/330] git-push: accept tag <tag> as advertised.

The documentation talked about "git push $URL tag <tag>" as a short-hand
for refs/tags/<tag>:refs/tags/<tag> for a long time but that was never
the case (the short-hand was for "git fetch").  Instead of fixing the
documentation, just add a bit of code to match it since it is easy to do
and would make it more consistent.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-push.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/builtin-push.c b/builtin-push.c
index d23974e708..b7412e8293 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -57,11 +57,36 @@ static void expand_refspecs(void)
 static void set_refspecs(const char **refs, int nr)
 {
 	if (nr) {
-		size_t bytes = nr * sizeof(char *);
-
-		refspec = xrealloc(refspec, bytes);
-		memcpy(refspec, refs, bytes);
-		refspec_nr = nr;
+		int pass;
+		for (pass = 0; pass < 2; pass++) {
+			/* pass 0 counts and allocates, pass 1 fills */
+			int i, cnt;
+			for (i = cnt = 0; i < nr; i++) {
+				if (!strcmp("tag", refs[i])) {
+					int len;
+					char *tag;
+					if (nr <= ++i)
+						die("tag <tag> shorthand without <tag>");
+					if (pass) {
+						len = strlen(refs[i]) + 11;
+						tag = xmalloc(len);
+						strcpy(tag, "refs/tags/");
+						strcat(tag, refs[i]);
+						refspec[cnt] = tag;
+					}
+					cnt++;
+					continue;
+				}
+				if (pass)
+					refspec[cnt] = refs[i];
+				cnt++;
+			}
+			if (!pass) {
+				size_t bytes = cnt * sizeof(char *);
+				refspec_nr = cnt;
+				refspec = xrealloc(refspec, bytes);
+			}
+		}
 	}
 	expand_refspecs();
 }

From 37adac765a469f8f8495e2befe7afeda65a2b272 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 10:30:11 -0800
Subject: [PATCH 161/330] send-pack: tighten checks for remote names

"git push $URL HEAD~6" created a bogus ref HEAD~6 immediately
under $GIT_DIR of the remote repository.  While we should keep
refspecs that have arbitrary extended SHA-1 expression on the
source side working (e.g. "HEAD~6:refs/tags/yesterday"), we
should not create bogus ref on the other end.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c      |  2 +-
 send-pack.c | 21 +++++++++++++++++++++
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/refs.c b/refs.c
index 96ea8b6907..e56abb8585 100644
--- a/refs.c
+++ b/refs.c
@@ -534,7 +534,7 @@ int check_ref_format(const char *ref)
 		level++;
 		if (!ch) {
 			if (level < 2)
-				return -1; /* at least of form "heads/blah" */
+				return -2; /* at least of form "heads/blah" */
 			return 0;
 		}
 	}
diff --git a/send-pack.c b/send-pack.c
index 328dbbc16a..cc884f3b2d 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -406,6 +406,25 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
 	return ret;
 }
 
+static void verify_remote_names(int nr_heads, char **heads)
+{
+	int i;
+
+	for (i = 0; i < nr_heads; i++) {
+		const char *remote = strchr(heads[i], ':');
+
+		remote = remote ? (remote + 1) : heads[i];
+		switch (check_ref_format(remote)) {
+		case 0: /* ok */
+		case -2: /* ok but a single level -- that is fine for
+			  * a match pattern.
+			  */
+			continue;
+		}
+		die("remote part of refspec is not a valid name in %s",
+		    heads[i]);
+	}
+}
 
 int main(int argc, char **argv)
 {
@@ -457,6 +476,8 @@ int main(int argc, char **argv)
 		usage(send_pack_usage);
 	if (heads && send_all)
 		usage(send_pack_usage);
+	verify_remote_names(nr_heads, heads);
+
 	pid = git_connect(fd, dest, exec);
 	if (pid < 0)
 		return 1;

From 753f96a455534ad60b670376fb3d89179281e541 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 10:55:21 -0800
Subject: [PATCH 162/330] branch --color: change default color selection.

Showing local and remote branches in green and red was simply
overkill, as all we wanted was to make it easy to tell them
apart (local ones can be built on top by committing, but the
remote tracking ones can't).

Use plain coloring for local branches and paint remotes in red.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 7c87b8d579..d1c243d372 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -23,8 +23,8 @@ static char branch_colors[][COLOR_MAXLEN] = {
 	"\033[m",	/* reset */
 	"",		/* PLAIN (normal) */
 	"\033[31m",	/* REMOTE (red) */
-	"\033[32m",	/* LOCAL (green) */
-	"\033[1;32m",	/* CURRENT (boldgreen) */
+	"",		/* LOCAL (normal) */
+	"\033[32m",	/* CURRENT (green) */
 };
 enum color_branch {
 	COLOR_BRANCH_RESET = 0,

From b11121d9e330c40f5d089636f176d089e5bb1885 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 1 Dec 2006 20:45:45 -0800
Subject: [PATCH 163/330] git-blame: show lines attributed to boundary commits
 differently.

When blaming with revision ranges, often many lines are attributed
to different commits at the boundary, but they are not interesting
for the purpose of finding project history during that revision range.

This outputs the lines blamed on boundary commits differently. When
showing "human format" output, their SHA-1 are shown with '^' prefixed.
In "porcelain format", the commit will be shown with an extra attribute
line "boundary".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-blame.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/builtin-blame.c b/builtin-blame.c
index dc3ffeaff8..a250724463 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -1090,6 +1090,11 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
 		if (!(commit->object.flags & UNINTERESTING) &&
 		    !(revs->max_age != -1 && commit->date  < revs->max_age))
 			pass_blame(sb, suspect, opt);
+		else {
+			commit->object.flags |= UNINTERESTING;
+			if (commit->object.parsed)
+				mark_parents_uninteresting(commit);
+		}
 
 		/* Take responsibility for the remaining entries */
 		for (ent = sb->ent; ent; ent = ent->next)
@@ -1273,6 +1278,8 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
 		printf("committer-tz %s\n", ci.committer_tz);
 		printf("filename %s\n", suspect->path);
 		printf("summary %s\n", ci.summary);
+		if (suspect->commit->object.flags & UNINTERESTING)
+			printf("boundary\n");
 	}
 	else if (suspect->commit->object.flags & MORE_THAN_ONE_PATH)
 		printf("filename %s\n", suspect->path);
@@ -1308,8 +1315,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
 	cp = nth_line(sb, ent->lno);
 	for (cnt = 0; cnt < ent->num_lines; cnt++) {
 		char ch;
+		int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
 
-		printf("%.*s", (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8, hex);
+		if (suspect->commit->object.flags & UNINTERESTING) {
+			length--;
+			putchar('^');
+		}
+
+		printf("%.*s", length, hex);
 		if (opt & OUTPUT_ANNOTATE_COMPAT)
 			printf("\t(%10s\t%10s\t%d)", ci.author,
 			       format_time(ci.author_time, ci.author_tz,

From 359850041e8158f6aeb70ad611ef1ba8834b8349 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Mon, 11 Dec 2006 20:25:58 -0800
Subject: [PATCH 164/330] git-svn: correctly handle "(no author)" when using an
 authors file

The low-level parts of the SVN library return NULL/undef for
author-less revisions, whereas "(no author)" is a (svn) client
convention.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index 15254e4795..ec92b440b9 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -2981,7 +2981,8 @@ sub libsvn_log_entry {
 	my ($Y,$m,$d,$H,$M,$S) = ($date =~ /^(\d{4})\-(\d\d)\-(\d\d)T
 					 (\d\d)\:(\d\d)\:(\d\d).\d+Z$/x)
 				or die "Unable to parse date: $date\n";
-	if (defined $_authors && ! defined $users{$author}) {
+	if (defined $author && length $author > 0 &&
+	    defined $_authors && ! defined $users{$author}) {
 		die "Author: $author not defined in $_authors file\n";
 	}
 	$msg = '' if ($rev == 0 && !defined $msg);

From 1d77043b005921cf7fcebfe680777df23ad10119 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 12:11:03 -0800
Subject: [PATCH 165/330] config documentation: group color items together.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index f5a552ee87..a3587f8397 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -137,10 +137,6 @@ branch.<name>.merge::
 	this option, `git pull` defaults to merge the first refspec fetched.
 	Specify multiple values to get an octopus merge.
 
-color.pager::
-	A boolean to enable/disable colored output when the pager is in
-	use (default is true).
-
 color.diff::
 	When true (or `always`), always use colors in patch.
 	When false (or `never`), never.  When set to `auto`, use
@@ -157,6 +153,24 @@ color.diff.<slot>::
 	`red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or
 	`white`.
 
+color.pager::
+	A boolean to enable/disable colored output when the pager is in
+	use (default is true).
+
+color.status::
+	A boolean to enable/disable color in the output of
+	gitlink:git-status[1]. May be set to `true` (or `always`),
+	`false` (or `never`) or `auto`, in which case colors are used
+	only when the output is to a terminal. Defaults to false.
+
+color.status.<slot>::
+	Use customized color for status colorization. `<slot>` is
+	one of `header` (the header text of the status message),
+	`updated` (files which are updated but not committed),
+	`changed` (files which are changed but not updated in the index),
+	or `untracked` (files which are not tracked by git). The values of
+	these variables may be specified as in color.diff.<slot>.
+
 diff.renameLimit::
 	The number of files to consider when performing the copy/rename
 	detection; equivalent to the git diff option '-l'.
@@ -271,20 +285,6 @@ showbranch.default::
 	The default set of branches for gitlink:git-show-branch[1].
 	See gitlink:git-show-branch[1].
 
-color.status::
-	A boolean to enable/disable color in the output of
-	gitlink:git-status[1]. May be set to `true` (or `always`),
-	`false` (or `never`) or `auto`, in which case colors are used
-	only when the output is to a terminal. Defaults to false.
-
-color.status.<slot>::
-	Use customized color for status colorization. `<slot>` is
-	one of `header` (the header text of the status message),
-	`updated` (files which are updated but not committed),
-	`changed` (files which are changed but not updated in the index),
-	or `untracked` (files which are not tracked by git). The values of
-	these variables may be specified as in color.diff.<slot>.
-
 tar.umask::
 	By default, gitlink:git-tar-tree[1] sets file and directories modes
 	to 0666 or 0777. While this is both useful and acceptable for projects

From ad2c82c0e1a543f4475a948ccb3eb4afcce86f26 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Wed, 13 Dec 2006 16:25:26 -0500
Subject: [PATCH 166/330] repacked packs should be read-only

... just like the other pack creating tools do.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-repack.sh | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/git-repack.sh b/git-repack.sh
index f150a558ca..067898f120 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -67,6 +67,8 @@ name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
 if [ -z "$name" ]; then
 	echo Nothing new to pack.
 else
+	chmod a-w "$PACKTMP-$name.pack"
+	chmod a-w "$PACKTMP-$name.idx"
 	if test "$quiet" != '-q'; then
 	    echo "Pack pack-$name created."
 	fi

From 62e1aeabc095a02fd3fb7ef46a6884d76da5ce67 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 15:58:41 -0800
Subject: [PATCH 167/330] git-svn: allow both diff.color and color.diff

The list concensus is to group color related configuration under
"color.*" so let's be consistent.

Inspired by Andy Parkins's patch to do the same for diff/log
family.  With fixes from Eric Wong.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index ec92b440b9..73ab8d873f 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -925,19 +925,38 @@ sub cmt_showable {
 
 sub log_use_color {
 	return 1 if $_color;
-	my $dc;
-	chomp($dc = `git-repo-config --get diff.color`);
+	my ($dc, $dcvar);
+	$dcvar = 'color.diff';
+	$dc = `git-repo-config --get $dcvar`;
+	if ($dc eq '') {
+		# nothing at all; fallback to "diff.color"
+		$dcvar = 'diff.color';
+		$dc = `git-repo-config --get $dcvar`;
+	}
+	chomp($dc);
 	if ($dc eq 'auto') {
-		if (-t *STDOUT || (defined $_pager &&
-		    `git-repo-config --bool --get pager.color` !~ /^false/)) {
+		my $pc;
+		$pc = `git-repo-config --get color.pager`;
+		if ($pc eq '') {
+			# does not have it -- fallback to pager.color
+			$pc = `git-repo-config --bool --get pager.color`;
+		}
+		else {
+			$pc = `git-repo-config --bool --get color.pager`;
+			if ($?) {
+				$pc = 'false';
+			}
+		}
+		chomp($pc);
+		if (-t *STDOUT || (defined $_pager && $pc eq 'true')) {
 			return ($ENV{TERM} && $ENV{TERM} ne 'dumb');
 		}
 		return 0;
 	}
 	return 0 if $dc eq 'never';
 	return 1 if $dc eq 'always';
-	chomp($dc = `git-repo-config --bool --get diff.color`);
-	$dc eq 'true';
+	chomp($dc = `git-repo-config --bool --get $dcvar`);
+	return ($dc eq 'true');
 }
 
 sub git_svn_log_cmd {

From f5e6b89b3a4420481a6ecdc05df325cb6d69d114 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 14 Dec 2006 00:03:18 -0800
Subject: [PATCH 168/330] Update git-diff documentation

Porcelain documentation should talk in terms of end-user workflow, not
in terms of implementation details.  Do not suggest update-index, but
git-add instead.  Explain differences among 0-, 1- and 2-tree cases
not as differences of number of trees given to the command, but say
why user would want to give these number of trees to the command in
what situation.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-diff.txt | 52 +++++++++++++++++++++++++-------------
 1 file changed, 35 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 228c4d95bd..127d68c962 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -14,30 +14,48 @@ DESCRIPTION
 -----------
 Show changes between two trees, a tree and the working tree, a
 tree and the index file, or the index file and the working tree.
-The combination of what is compared with what is determined by
-the number of trees given to the command.
 
-* When no <tree-ish> is given, the working tree and the index
-  file are compared, using `git-diff-files`.
+'git-diff' [--options] [--] [<path>...]::
 
-* When one <tree-ish> is given, the working tree and the named
-  tree are compared, using `git-diff-index`.  The option
-  `--cached` can be given to compare the index file and
-  the named tree.
+	This form is to view the changes you made relative to
+	the index (staging area for the next commit).  In other
+	words, the differences are what you _could_ tell git to
+	further add to the index but you still haven't.  You can
+	stage these changes by using gitlink:git-add[1].
+
+'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
+
+	This form is to view the changes you staged for the next
+	commit relative to the named <tree-ish>.  Typically you
+	would want comparison with the latest commit, so if you
+	do not give <commit>, it defaults to HEAD.
+
+'git-diff' [--options] <commit> -- [<path>...]::
+
+	This form is to view the changes you have in your
+	working tree relative to the named <commit>.  You can
+	use HEAD to compare it with the latest commit, or a
+	branch name to compare with the tip of a different
+	branch.
+
+'git-diff' [--options] <commit> <commit> -- [<path>...]::
+
+	This form is to view the changes between two <commit>,
+	for example, tips of two branches.
+
+Just in case if you are doing something exotic, it should be
+noted that all of the <commit> in the above description can be
+any <tree-ish>.
 
-* When two <tree-ish>s are given, these two trees are compared
-  using `git-diff-tree`.
 
 OPTIONS
 -------
---diff-options::
-	'--diff-options' are passed to the `git-diff-files`,
-	`git-diff-index`, and `git-diff-tree` commands.  See the
-	documentation for these commands for description.
+include::diff-options.txt[]
 
 <path>...::
-	The <path> arguments are also passed to `git-diff-\*`
-	commands.
+	The <paths> parameters, when given, are used to limit
+	the diff to the named paths (you can give directory
+	names and get diff for all files under them).
 
 
 EXAMPLES
@@ -51,7 +69,7 @@ $ git diff --cached   <2>
 $ git diff HEAD       <3>
 ------------
 +
-<1> changes in the working tree since your last git-update-index.
+<1> changes in the working tree not yet staged for the next commit.
 <2> changes between the index and your last commit; what you
 would be committing if you run "git commit" without "-a" option.
 <3> changes in the working tree since your last commit; what you

From 7da41f48c8acea834e8204917fe59da2b975903b Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 14 Dec 2006 05:07:46 -0500
Subject: [PATCH 169/330] Bypass expensive content comparsion during rename
 detection.

When comparing file contents during the second loop through a rename
detection attempt we can skip the expensive byte-by-byte comparsion
if both source and destination files have valid SHA1 values.  This
improves performance by avoiding either an expensive open/mmap to
read the working tree copy, or an expensive inflate of a blob object.

Unfortunately we still have to at least initialize the sizes of the
source and destination files even if the SHA1 values don't match.
Failing to initialize the sizes causes a number of test cases to fail
and start reporting different copy/rename behavior than was expected.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diffcore-rename.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/diffcore-rename.c b/diffcore-rename.c
index 57a74b6bb8..91fa2bea51 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -109,6 +109,8 @@ static int is_exact_match(struct diff_filespec *src,
 		return 0;
 	if (src->size != dst->size)
 		return 0;
+	if (src->sha1_valid && dst->sha1_valid)
+	    return !hashcmp(src->sha1, dst->sha1);
 	if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
 		return 0;
 	if (src->size == dst->size &&

From 02c9e93547d4c21635beb30895ebb6e37f67833c Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 14 Dec 2006 11:40:21 +0100
Subject: [PATCH 170/330] INSTALL: no need to have GNU diff installed

Since a long time, we have inbuilt diff generation.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 INSTALL | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/INSTALL b/INSTALL
index b5dd9f0abb..e7aea60e92 100644
--- a/INSTALL
+++ b/INSTALL
@@ -72,16 +72,6 @@ Issues of note:
 	- expat library; git-http-push uses it for remote lock
 	  management over DAV.  Similar to "curl" above, this is optional.
 
-	- "GNU diff" to generate patches.  Of course, you don't _have_ to
-	  generate patches if you don't want to, but let's face it, you'll
-	  be wanting to. Or why did you get git in the first place?
-
-	  Non-GNU versions of the diff/patch programs don't generally support
-	  the unified patch format (which is the one git uses), so you
-	  really do want to get the GNU one.  Trust me, you will want to
-	  do that even if it wasn't for git.  There's no point in living
-	  in the dark ages any more. 
-
         - "wish", the Tcl/Tk windowing shell is used in gitk to show the
           history graphically
 

From 4da9028578ffaaf8985e1436e2e1cf16bd3b9023 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 14 Dec 2006 00:36:23 -0800
Subject: [PATCH 171/330] git-fetch: make it work from within a subdirectory.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-fetch.sh | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/git-fetch.sh b/git-fetch.sh
index 4eecf148ea..fb35815a5f 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -2,7 +2,13 @@
 #
 
 USAGE='<fetch-options> <repository> <refspec>...'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
+TOP=$(git-rev-parse --show-cdup)
+if test ! -z "$TOP"
+then
+	cd "$TOP"
+fi
 . git-parse-remote
 _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
 _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"

From a81c311f23a5fadd6c1da38d46781644cd9db6e8 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 14 Dec 2006 00:40:15 -0800
Subject: [PATCH 172/330] git-reset: make it work from within a subdirectory.

If you typically sit in, say "src/", it's annoying to have to
change directory to do a reset.

This may need to be reworked when we add "git reset -- paths..."
to encapsulate the "ls-tree $tree | update-index --index-info"
pattern.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-reset.sh | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/git-reset.sh b/git-reset.sh
index c0feb4435d..03d2c3b937 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -1,8 +1,15 @@
 #!/bin/sh
 
 USAGE='[--mixed | --soft | --hard]  [<commit-ish>]'
+SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
+TOP=$(git-rev-parse --show-cdup)
+if test ! -z "$TOP"
+then
+	cd "$TOP"
+fi
+
 update=
 reset_type=--mixed
 case "$1" in

From 2ce633b928f78224a37308f45810e76fefe8df56 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 14 Dec 2006 01:19:19 -0800
Subject: [PATCH 173/330] git-reset [--mixed] <tree> [--] <paths>...

Sometimes it is asked on the list how to revert selected path in
the index from a tree, most often HEAD, without affecting the
files in the working tree.  A similar operation that also
affects the working tree files has been available in the form of
"git checkout <tree> -- <paths>...".

By definition --soft would never affect either the index nor the
working tree files, and --hard is the way to make the working
tree files as close to pristine, so this new option is available
only for the default --mixed case.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-reset.sh | 69 +++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 22 deletions(-)

diff --git a/git-reset.sh b/git-reset.sh
index 03d2c3b937..8d95e3748d 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -1,35 +1,60 @@
 #!/bin/sh
-
-USAGE='[--mixed | --soft | --hard]  [<commit-ish>]'
+#
+# Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+#
+USAGE='[--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]'
 SUBDIRECTORY_OK=Yes
 . git-sh-setup
 
+update= reset_type=--mixed
+unset rev
+
+while case $# in 0) break ;; esac
+do
+	case "$1" in
+	--mixed | --soft | --hard)
+		reset_type="$1"
+		;;
+	--)
+		break
+		;;
+	-*)
+		usage
+		;;
+	*)
+		rev=$(git-rev-parse --verify "$1") || exit
+		shift
+		break
+		;;
+	esac
+	shift
+done
+
+: ${rev=HEAD}
+rev=$(git-rev-parse --verify $rev^0) || exit
+
+# Skip -- in "git reset HEAD -- foo" and "git reset -- foo".
+case "$1" in --) shift ;; esac
+
+# git reset --mixed tree [--] paths... can be used to
+# load chosen paths from the tree into the index without
+# affecting the working tree nor HEAD.
+if test $# != 0
+then
+	test "$reset_type" == "--mixed" ||
+		die "Cannot do partial $reset_type reset."
+	git ls-tree -r --full-name $rev -- "$@" |
+	git update-index --add --index-info || exit
+	git update-index --refresh
+	exit
+fi
+
 TOP=$(git-rev-parse --show-cdup)
 if test ! -z "$TOP"
 then
 	cd "$TOP"
 fi
 
-update=
-reset_type=--mixed
-case "$1" in
---mixed | --soft | --hard)
-	reset_type="$1"
-	shift
-	;;
--*)
-        usage ;;
-esac
-
-case $# in
-0) rev=HEAD ;;
-1) rev=$(git-rev-parse --verify "$1") || exit ;;
-*) usage ;;
-esac
-rev=$(git-rev-parse --verify $rev^0) || exit
-
-# We need to remember the set of paths that _could_ be left
-# behind before a hard reset, so that we can remove them.
 if test "$reset_type" = "--hard"
 then
 	update=-u

From 5d7eeee2ac64e277e47ce2cdabd1af0d2501a96f Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 14 Dec 2006 11:31:05 +0100
Subject: [PATCH 174/330] git-show: grok blobs, trees and tags, too

Since git-show is pure Porcelain, it is the ideal candidate to
pretty print other things than commits, too.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-show.txt | 40 +++++++++++++++----
 builtin-log.c              | 82 +++++++++++++++++++++++++++++++++++++-
 generate-cmdlist.sh        |  2 +-
 3 files changed, 115 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index 4c880a8717..98dea6125d 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -3,20 +3,27 @@ git-show(1)
 
 NAME
 ----
-git-show - Show one commit with difference it introduces
+git-show - Show various types of objects
 
 
 SYNOPSIS
 --------
-'git-show' <option>...
+'git-show' [options] <object>...
 
 DESCRIPTION
 -----------
-Shows commit log and textual diff for a single commit.  The
-command internally invokes 'git-rev-list' piped to
-'git-diff-tree', and takes command line options for both of
-these commands. It also presents the merge commit in a special
-format as produced by 'git-diff-tree --cc'.
+Shows one or more objects (blobs, trees, tags and commits).
+
+For commits it shows the log message and textual diff. It also
+presents the merge commit in a special format as produced by
+'git-diff-tree --cc'.
+
+For tags, it shows the tag message and the referenced objects.
+
+For trees, it shows the names (equivalent to gitlink:git-ls-tree[1]
+with \--name-only).
+
+For plain blobs, it shows the plain contents.
 
 This manual page describes only the most frequently used options.
 
@@ -28,6 +35,25 @@ OPTIONS
 
 include::pretty-formats.txt[]
 
+
+EXAMPLES
+--------
+
+git show v1.0.0::
+	Shows the tag `v1.0.0`.
+
+git show v1.0.0^{tree}::
+	Shows the tree pointed to by the tag `v1.0.0`.
+
+git show next~10:Documentation/README
+	Shows the contents of the file `Documentation/README` as
+	they were current in the 10th last commit of the branch
+	`next`.
+
+git show master:Makefile master:t/Makefile
+	Concatenates the contents of said Makefiles in the head
+	of the branch `master`.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
diff --git a/builtin-log.c b/builtin-log.c
index 6821a08442..17014f70a2 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -10,6 +10,7 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include "tag.h"
 #include <time.h>
 #include <sys/time.h>
 
@@ -71,9 +72,43 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	return cmd_log_walk(&rev);
 }
 
+static int show_object(const unsigned char *sha1, int suppress_header)
+{
+	unsigned long size;
+	char type[20];
+	char *buf = read_sha1_file(sha1, type, &size);
+	int offset = 0;
+
+	if (!buf)
+		return error("Could not read object %s", sha1_to_hex(sha1));
+
+	if (suppress_header)
+		while (offset < size && buf[offset++] != '\n') {
+			int new_offset = offset;
+			while (new_offset < size && buf[new_offset++] != '\n')
+				; /* do nothing */
+			offset = new_offset;
+		}
+
+	if (offset < size)
+		fwrite(buf + offset, size - offset, 1, stdout);
+	free(buf);
+	return 0;
+}
+
+static int show_tree_object(const unsigned char *sha1,
+		const char *base, int baselen,
+		const char *pathname, unsigned mode, int stage)
+{
+	printf("%s%s\n", pathname, S_ISDIR(mode) ? "/" : "");
+	return 0;
+}
+
 int cmd_show(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
+	struct object_array_entry *objects;
+	int i, count, ret = 0;
 
 	git_config(git_log_config);
 	init_revisions(&rev, prefix);
@@ -85,7 +120,52 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	rev.ignore_merges = 0;
 	rev.no_walk = 1;
 	cmd_log_init(argc, argv, prefix, &rev);
-	return cmd_log_walk(&rev);
+
+	count = rev.pending.nr;
+	objects = rev.pending.objects;
+	for (i = 0; i < count && !ret; i++) {
+		struct object *o = objects[i].item;
+		const char *name = objects[i].name;
+		switch (o->type) {
+		case OBJ_BLOB:
+			ret = show_object(o->sha1, 0);
+			break;
+		case OBJ_TAG: {
+			struct tag *t = (struct tag *)o;
+
+			printf("%stag %s%s\n\n",
+					diff_get_color(rev.diffopt.color_diff,
+						DIFF_COMMIT),
+					t->tag,
+					diff_get_color(rev.diffopt.color_diff,
+						DIFF_RESET));
+			ret = show_object(o->sha1, 1);
+			objects[i].item = (struct object *)t->tagged;
+			i--;
+			break;
+		}
+		case OBJ_TREE:
+			printf("%stree %s%s\n\n",
+					diff_get_color(rev.diffopt.color_diff,
+						DIFF_COMMIT),
+					name,
+					diff_get_color(rev.diffopt.color_diff,
+						DIFF_RESET));
+			read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
+					show_tree_object);
+			break;
+		case OBJ_COMMIT:
+			rev.pending.nr = rev.pending.alloc = 0;
+			rev.pending.objects = NULL;
+			add_object_array(o, name, &rev.pending);
+			ret = cmd_log_walk(&rev);
+			break;
+		default:
+			ret = error("Unknown type: %d", o->type);
+		}
+	}
+	free(objects);
+	return ret;
 }
 
 int cmd_log(int argc, const char **argv, const char *prefix)
diff --git a/generate-cmdlist.sh b/generate-cmdlist.sh
index 5450918be3..06c42b042d 100755
--- a/generate-cmdlist.sh
+++ b/generate-cmdlist.sh
@@ -4,7 +4,7 @@ echo "/* Automatically generated by $0 */
 struct cmdname_help
 {
     char name[16];
-    char help[64];
+    char help[80];
 };
 
 struct cmdname_help common_cmds[] = {"

From 1510fea781cb0517eeba8c378964f7bc4f9577ab Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 14 Dec 2006 06:15:57 -0500
Subject: [PATCH 175/330] Avoid accessing a slow working copy during diffcore
 operations.

The Cygwin folks have done a fine job at creating a POSIX layer
on Windows That Just Works(tm).  However it comes with a penalty;
accessing files in the working tree by way of stat/open/mmap can
be slower for diffcore than inflating the data from a blob which
is stored in a packfile.

This performance problem is especially an issue in merge-recursive
when dealing with nearly 7000 added files, as we are loading
each file's content from the working directory to perform rename
detection.  I have literally seen (and sadly watched) paint dry in
less time than it takes for merge-recursive to finish such a merge.
On the other hand this very same merge runs very fast on Solaris.

If Git is compiled with NO_FAST_WORKING_DIRECTORY set then we will
avoid looking at the working directory when the blob in question
is available within a packfile and the caller doesn't need the data
unpacked into a temporary file.

We don't use loose objects as they have the same open/mmap/close
costs as the working directory file access, but have the additional
CPU overhead of needing to inflate the content before use.  So it
is still faster to use the working tree file over the loose object.

If the caller needs the file data unpacked into a temporary file
its likely because they are going to call an external diff program,
passing the file as a parameter.  In this case reusing the working
tree file will be faster as we don't need to inflate the data and
write it out to a temporary file.

The NO_FAST_WORKING_DIRECTORY feature is enabled by default on
Cygwin, as that is the platform which currently appears to benefit
the most from this option.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile |  7 +++++++
 diff.c   | 24 +++++++++++++++++++++---
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 2d17fa7027..676d426a9e 100644
--- a/Makefile
+++ b/Makefile
@@ -69,6 +69,9 @@ all:
 #
 # Define NO_MMAP if you want to avoid mmap.
 #
+# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
+# generally faster on your platform than accessing the working directory.
+#
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
 # Define NO_SOCKADDR_STORAGE if your platform does not have struct
@@ -355,6 +358,7 @@ ifeq ($(uname_O),Cygwin)
 	NO_SYMLINK_HEAD = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_C99_FORMAT = YesPlease
+	NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
 	# There are conflicting reports about this.
 	# On some boxes NO_MMAP is needed, and not so elsewhere.
 	# Try uncommenting this if you see things break -- YMMV.
@@ -506,6 +510,9 @@ ifdef NO_MMAP
 	COMPAT_CFLAGS += -DNO_MMAP
 	COMPAT_OBJS += compat/mmap.o
 endif
+ifdef NO_FAST_WORKING_DIRECTORY
+	BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
+endif
 ifdef NO_IPV6
 	BASIC_CFLAGS += -DNO_IPV6
 endif
diff --git a/diff.c b/diff.c
index 2df14b2469..77ba641aa4 100644
--- a/diff.c
+++ b/diff.c
@@ -12,6 +12,12 @@
 #include "xdiff-interface.h"
 #include "color.h"
 
+#ifdef NO_FAST_WORKING_DIRECTORY
+#define FAST_WORKING_DIRECTORY 0
+#else
+#define FAST_WORKING_DIRECTORY 1
+#endif
+
 static int use_size_cache;
 
 static int diff_detect_rename_default;
@@ -1158,7 +1164,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
  * the work tree has that object contents, return true, so that
  * prepare_temp_file() does not have to inflate and extract.
  */
-static int work_tree_matches(const char *name, const unsigned char *sha1)
+static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
 {
 	struct cache_entry *ce;
 	struct stat st;
@@ -1179,6 +1185,18 @@ static int work_tree_matches(const char *name, const unsigned char *sha1)
 	if (!active_cache)
 		return 0;
 
+	/* We want to avoid the working directory if our caller
+	 * doesn't need the data in a normal file, this system
+	 * is rather slow with its stat/open/mmap/close syscalls,
+	 * and the object is contained in a pack file.  The pack
+	 * is probably already open and will be faster to obtain
+	 * the data through than the working directory.  Loose
+	 * objects however would tend to be slower as they need
+	 * to be individually opened and inflated.
+	 */
+	if (FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL))
+		return 0;
+
 	len = strlen(name);
 	pos = cache_name_pos(name, len);
 	if (pos < 0)
@@ -1265,7 +1283,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
 	if (s->data)
 		return err;
 	if (!s->sha1_valid ||
-	    work_tree_matches(s->path, s->sha1)) {
+	    reuse_worktree_file(s->path, s->sha1, 0)) {
 		struct stat st;
 		int fd;
 		if (lstat(s->path, &st) < 0) {
@@ -1372,7 +1390,7 @@ static void prepare_temp_file(const char *name,
 	}
 
 	if (!one->sha1_valid ||
-	    work_tree_matches(name, one->sha1)) {
+	    reuse_worktree_file(name, one->sha1, 1)) {
 		struct stat st;
 		if (lstat(name, &st) < 0) {
 			if (errno == ENOENT)

From ebd124c6783da5e064963611ee17741cd173f6b5 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Thu, 14 Dec 2006 23:15:44 -0500
Subject: [PATCH 176/330] make commit message a little more consistent and
 conforting

It is nicer to let the user know when a commit succeeded all the time,
not only the first time.  Also the commit sha1 is much more useful than
the tree sha1 in this case.

This patch also introduces a -q switch to supress this message as well
as the summary of created/deleted files.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/core-tutorial.txt | 14 +++---------
 Documentation/diff-options.txt  |  5 +++++
 Documentation/git-commit.txt    |  3 +++
 Documentation/tutorial-2.txt    | 29 +++++++++++++++++--------
 builtin-commit-tree.c           |  2 --
 diff.c                          | 38 ++++++++++++++++++++++++++++++++-
 diff.h                          |  1 +
 git-commit.sh                   | 19 +++++++++++++----
 t/t1200-tutorial.sh             |  2 --
 9 files changed, 84 insertions(+), 29 deletions(-)

diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 47505aa20a..1c311590c7 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -336,17 +336,9 @@ $ commit=$(echo 'Initial commit' | git-commit-tree $tree)
 $ git-update-ref HEAD $commit
 ------------------------------------------------
 
-which will say:
-
-----------------
-Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
-----------------
-
-just to warn you about the fact that it created a totally new commit
-that is not related to anything else. Normally you do this only *once*
-for a project ever, and all later commits will be parented on top of an
-earlier commit, and you'll never see this "Committing initial tree"
-message ever again.
+In this case this creates a totally new commit that is not related to
+anything else. Normally you do this only *once* for a project ever, and
+all later commits will be parented on top of an earlier commit.
 
 Again, normally you'd never actually do this by hand. There is a
 helpful script called `git commit` that will do all of this for you. So
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 9cdd171af7..f12082e134 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -21,6 +21,11 @@
 	deleted lines in decimal notation and pathname without
 	abbreviation, to make it more machine friendly.
 
+--shortstat::
+	Output only the last line of the --stat format containing total
+	number of modified files, as well as number of added and deleted
+	lines.
+
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 97d66ef4d2..0b74cd708e 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -113,6 +113,9 @@ but can be used to amend a merge commit.
 	as well.  This is usually not what you want unless you
 	are concluding a conflicted merge.
 
+-q|--quiet::
+	Supress commit summary message.
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index 6389de5ef7..8606381e6c 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -22,14 +22,14 @@ defaulting to local storage area
 $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
-Committing initial tree 92b8b694ffb1675e5975148e1121810081dbdffe
+Created initial commit 54196cc2703dc165cbd373a65a4dcf22d50ae7f7
  create mode 100644 file.txt
 $ echo 'hello world!' >file.txt
 $ git commit -a -m "add emphasis"
+Created commit c4d59f390b9cfd4318117afde11d601c1085f241
 ------------------------------------------------
 
-What are the 40 digits of hex that git responded to the first commit
-with?
+What are the 40 digits of hex that git responded to the commit with?
 
 We saw in part one of the tutorial that commits have names like this.
 It turns out that every object in the git history is stored under
@@ -39,13 +39,25 @@ the same data twice (since identical data is given an identical SHA1
 name), and that the contents of a git object will never change (since
 that would change the object's name as well).
 
+It is expected that the content of the commit object you created while
+following the example above generates a different SHA1 hash than
+the one shown above because the commit object records the time when
+it was created and the name of the person performing the commit.
+
 We can ask git about this particular object with the cat-file
-command--just cut-and-paste from the reply to the initial commit, to
-save yourself typing all 40 hex digits:
+command. Don't copy the 40 hex digits from this example but use those
+from your own version. Note that you can shorten it to only a few
+characters to save yourself typing all 40 hex digits:
 
 ------------------------------------------------
-$ git cat-file -t 92b8b694ffb1675e5975148e1121810081dbdffe
-tree
+$ git-cat-file -t 54196cc2
+commit
+$ git-cat-file commit 54196cc2
+tree 92b8b694ffb1675e5975148e1121810081dbdffe
+author J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
+committer J. Bruce Fields <bfields@puzzle.fieldses.org> 1143414668 -0500
+
+initial commit
 ------------------------------------------------
 
 A tree can refer to one or more "blob" objects, each corresponding to
@@ -102,8 +114,7 @@ $ find .git/objects/
 
 and the contents of these files is just the compressed data plus a
 header identifying their length and their type.  The type is either a
-blob, a tree, a commit, or a tag.  We've seen a blob and a tree now,
-so next we should look at a commit.
+blob, a tree, a commit, or a tag.
 
 The simplest commit to find is the HEAD commit, which we can find
 from .git/HEAD:
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index e2e690a1ae..856f3cd841 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -107,8 +107,6 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 		if (new_parent(parents))
 			parents++;
 	}
-	if (!parents)
-		fprintf(stderr, "Committing initial tree %s\n", argv[1]);
 
 	init_buffer(&buffer, &size);
 	add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
diff --git a/diff.c b/diff.c
index 77ba641aa4..99744354a7 100644
--- a/diff.c
+++ b/diff.c
@@ -801,6 +801,35 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
 	       set, total_files, adds, dels, reset);
 }
 
+static void show_shortstats(struct diffstat_t* data)
+{
+	int i, adds = 0, dels = 0, total_files = data->nr;
+
+	if (data->nr == 0)
+		return;
+
+	for (i = 0; i < data->nr; i++) {
+		if (!data->files[i]->is_binary &&
+		    !data->files[i]->is_unmerged) {
+			int added = data->files[i]->added;
+			int deleted= data->files[i]->deleted;
+			if (!data->files[i]->is_renamed &&
+			    (added + deleted == 0)) {
+				total_files--;
+			} else {
+				adds += added;
+				dels += deleted;
+			}
+		}
+		free(data->files[i]->name);
+		free(data->files[i]);
+	}
+	free(data->files);
+
+	printf(" %d files changed, %d insertions(+), %d deletions(-)\n",
+	       total_files, adds, dels);
+}
+
 static void show_numstat(struct diffstat_t* data, struct diff_options *options)
 {
 	int i;
@@ -1771,6 +1800,7 @@ int diff_setup_done(struct diff_options *options)
 		options->output_format &= ~(DIFF_FORMAT_RAW |
 					    DIFF_FORMAT_NUMSTAT |
 					    DIFF_FORMAT_DIFFSTAT |
+					    DIFF_FORMAT_SHORTSTAT |
 					    DIFF_FORMAT_SUMMARY |
 					    DIFF_FORMAT_PATCH);
 
@@ -1781,6 +1811,7 @@ int diff_setup_done(struct diff_options *options)
 	if (options->output_format & (DIFF_FORMAT_PATCH |
 				      DIFF_FORMAT_NUMSTAT |
 				      DIFF_FORMAT_DIFFSTAT |
+				      DIFF_FORMAT_SHORTSTAT |
 				      DIFF_FORMAT_SUMMARY |
 				      DIFF_FORMAT_CHECKDIFF))
 		options->recursive = 1;
@@ -1872,6 +1903,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 	else if (!strcmp(arg, "--numstat")) {
 		options->output_format |= DIFF_FORMAT_NUMSTAT;
 	}
+	else if (!strcmp(arg, "--shortstat")) {
+		options->output_format |= DIFF_FORMAT_SHORTSTAT;
+	}
 	else if (!strncmp(arg, "--stat", 6)) {
 		char *end;
 		int width = options->stat_width;
@@ -2646,7 +2680,7 @@ void diff_flush(struct diff_options *options)
 		separator++;
 	}
 
-	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_NUMSTAT)) {
+	if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -2660,6 +2694,8 @@ void diff_flush(struct diff_options *options)
 			show_numstat(&diffstat, options);
 		if (output_format & DIFF_FORMAT_DIFFSTAT)
 			show_stats(&diffstat, options);
+		else if (output_format & DIFF_FORMAT_SHORTSTAT)
+			show_shortstats(&diffstat);
 		separator++;
 	}
 
diff --git a/diff.h b/diff.h
index 101b2b505d..eff445596d 100644
--- a/diff.h
+++ b/diff.h
@@ -29,6 +29,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
 #define DIFF_FORMAT_NUMSTAT	0x0004
 #define DIFF_FORMAT_SUMMARY	0x0008
 #define DIFF_FORMAT_PATCH	0x0010
+#define DIFF_FORMAT_SHORTSTAT	0x0020
 
 /* These override all above */
 #define DIFF_FORMAT_NAME	0x0100
diff --git a/git-commit.sh b/git-commit.sh
index 05828bb113..a30bda19d5 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -80,6 +80,7 @@ no_edit=
 log_given=
 log_message=
 verify=t
+quiet=
 verbose=
 signoff=
 force_author=
@@ -241,6 +242,10 @@ $1"
 		signoff=t
 		shift
 		;;
+	-q|--q|--qu|--qui|--quie|--quiet)
+		quiet=t
+		shift
+		;;
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 		verbose=t
 		shift
@@ -615,11 +620,17 @@ then
 	git-rerere
 fi
 
-if test -x "$GIT_DIR"/hooks/post-commit && test "$ret" = 0
+if test "$ret" = 0
 then
-	"$GIT_DIR"/hooks/post-commit
+	if test -x "$GIT_DIR"/hooks/post-commit
+	then
+		"$GIT_DIR"/hooks/post-commit
+	fi
+	if test -z "$quiet"
+	then
+		echo "Created${initial_commit:+ initial} commit $commit"
+		git-diff-tree --shortstat --summary --root --no-commit-id HEAD
+	fi
 fi
 
-test "$ret" = 0 && git-diff-tree --summary --root --no-commit-id HEAD
-
 exit "$ret"
diff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh
index 0272dd4293..eebe643bda 100755
--- a/t/t1200-tutorial.sh
+++ b/t/t1200-tutorial.sh
@@ -37,8 +37,6 @@ test_expect_success 'tree' "test 8988da15d077d4829fc51d8544c097def6644dbb = $tre
 
 output="$(echo "Initial commit" | git-commit-tree $(git-write-tree) 2>&1 > .git/refs/heads/master)"
 
-test_expect_success 'commit' "test 'Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb' = \"$output\""
-
 git-diff-index -p HEAD > diff.output
 test_expect_success 'git-diff-index -p HEAD' 'cmp diff.expect diff.output'
 

From ef0a89a604acb7fc2e3c08566641b95b23d87fd6 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Fri, 15 Dec 2006 00:44:58 -0500
Subject: [PATCH 177/330] Provide more meaningful output from 'git init-db'.

Back in the old days of Git when people messed around with their
GIT_DIR environment variable more often it was nice to know whether
or not git-init-db created a .git directory or used GIT_DIR.
As most users at that time were rather technical UNIXy folk the
message "defaulting to local storage area" made sense to some and
seemed reasonable.

But it doesn't really convey any meaning to the new Git user,
as they don't know what a 'local storage area is' nor do they
know enough about Git to care.  It also really doesn't tell the
experienced Git user a whole lot about the command they just ran,
especially if they might be reinitializing an existing repository
(e.g. to update hooks).

So now we print out what we did ("Initialized empty" or
"Reinitialized existing"), what type of repository ("" or "shared"),
and what location the repository will be in ("$GIT_DIR").

Suggested in part by Andy Parkins in his Git 'niggles' list
(<200612132237.10051.andyparkins@gmail.com>).

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/core-tutorial.txt |  2 +-
 Documentation/tutorial-2.txt    |  2 +-
 Documentation/tutorial.txt      |  2 +-
 builtin-init-db.c               | 21 ++++++++++++++-------
 4 files changed, 17 insertions(+), 10 deletions(-)

diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
index 1c311590c7..5ea611748c 100644
--- a/Documentation/core-tutorial.txt
+++ b/Documentation/core-tutorial.txt
@@ -57,7 +57,7 @@ $ git-init-db
 to which git will reply
 
 ----------------
-defaulting to local storage area
+Initialized empty Git repository in .git/
 ----------------
 
 which is just git's way of saying that you haven't been doing anything
diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index 8606381e6c..aa62e13de9 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -18,7 +18,7 @@ Let's start a new project and create a small amount of history:
 $ mkdir test-project
 $ cd test-project
 $ git init-db
-defaulting to local storage area
+Initialized empty Git repository in .git/
 $ echo 'hello world' > file.txt
 $ git add .
 $ git commit -a -m "initial commit"
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
index 02dede320c..cb808d924b 100644
--- a/Documentation/tutorial.txt
+++ b/Documentation/tutorial.txt
@@ -38,7 +38,7 @@ $ git init-db
 Git will reply
 
 ------------------------------------------------
-defaulting to local storage area
+Initialized empty Git repository in .git/
 ------------------------------------------------
 
 You've now initialized the working directory--you may notice a new
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 235a0ee48f..8f2b7500dc 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -164,13 +164,14 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
 	closedir(dir);
 }
 
-static void create_default_files(const char *git_dir, const char *template_path)
+static int create_default_files(const char *git_dir, const char *template_path)
 {
 	unsigned len = strlen(git_dir);
 	static char path[PATH_MAX];
 	unsigned char sha1[20];
 	struct stat st1;
 	char repo_version_string[10];
+	int reinit;
 
 	if (len > sizeof(path)-50)
 		die("insane git directory %s", git_dir);
@@ -218,7 +219,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
 	 * branch, if it does not exist yet.
 	 */
 	strcpy(path + len, "HEAD");
-	if (read_ref("HEAD", sha1) < 0) {
+	reinit = !read_ref("HEAD", sha1);
+	if (!reinit) {
 		if (create_symref("HEAD", "refs/heads/master") < 0)
 			exit(1);
 	}
@@ -239,6 +241,8 @@ static void create_default_files(const char *git_dir, const char *template_path)
 		git_config_set("core.filemode",
 			       filemode ? "true" : "false");
 	}
+
+	return reinit;
 }
 
 static const char init_db_usage[] =
@@ -256,7 +260,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	const char *sha1_dir;
 	const char *template_dir = NULL;
 	char *path;
-	int len, i;
+	int len, i, reinit;
 
 	for (i = 1; i < argc; i++, argv++) {
 		const char *arg = argv[1];
@@ -274,10 +278,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	 * Set up the default .git directory contents
 	 */
 	git_dir = getenv(GIT_DIR_ENVIRONMENT);
-	if (!git_dir) {
+	if (!git_dir)
 		git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
-		fprintf(stderr, "defaulting to local storage area\n");
-	}
 	safe_create_dir(git_dir, 0);
 
 	/* Check to see if the repository version is right.
@@ -287,7 +289,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	 */
 	check_repository_format();
 
-	create_default_files(git_dir, template_dir);
+	reinit = create_default_files(git_dir, template_dir);
 
 	/*
 	 * And set up the object store.
@@ -314,5 +316,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 		git_config_set("receive.denyNonFastforwards", "true");
 	}
 
+	printf("%s%s Git repository in %s/\n",
+		reinit ? "Reinitialized existing" : "Initialized empty",
+		shared_repository ? " shared" : "",
+		git_dir);
+
 	return 0;
 }

From 0bee59186976b1d9e6b2dd77332480c9480131d5 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 14 Dec 2006 17:41:17 -0500
Subject: [PATCH 178/330] Enable reflogs by default in any repository with a
 working directory.

New and experienced Git users alike are finding out too late that
they forgot to enable reflogs in the current repository, and cannot
use the information stored within it to recover from an incorrectly
entered command such as `git reset --hard HEAD^^^` when they really
meant HEAD^^ (aka HEAD~2).

So enable reflogs by default in all future versions of Git, unless
the user specifically disables it with:

  [core]
    logAllRefUpdates = false

in their .git/config or ~/.gitconfig.

We only enable reflogs in repositories that have a working directory
associated with them, as shared/bare repositories do not have
an easy means to prune away old log entries, or may fail logging
entirely if the user's gecos information is not valid during a push.
This heuristic was suggested on the mailing list by Junio.

Documentation was also updated to indicate the new default behavior.
We probably should start to teach usuing the reflog to recover
from mistakes in some of the tutorial material, as new users are
likely to make a few along the way and will feel better knowing
they can recover from them quickly and easily, without fsck-objects'
lost+found features.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt |  7 +++++--
 builtin-init-db.c        |  3 +++
 cache.h                  |  1 +
 environment.c            | 10 ++++++++++
 t/t1400-update-ref.sh    |  4 ++--
 5 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index a3587f8397..8abb0820ff 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -79,8 +79,11 @@ core.logAllRefUpdates::
 	file is automatically created for branch heads.
 
 	This information can be used to determine what commit
-	was the tip of a branch "2 days ago".  This value is
-	false by default (no automated creation of log files).
+	was the tip of a branch "2 days ago".
+
+	This value is true by default in a repository that has
+	a working directory associated with it, and false by
+	default in a bare repository.
 
 core.repositoryFormatVersion::
 	Internal variable identifying the repository format and layout
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 8f2b7500dc..1d7d15e8d5 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -242,6 +242,9 @@ static int create_default_files(const char *git_dir, const char *template_path)
 			       filemode ? "true" : "false");
 	}
 
+	/* Enable logAllRefUpdates if a working tree is attached */
+	if (!is_bare_git_dir(git_dir))
+		git_config_set("core.logallrefupdates", "true");
 	return reinit;
 }
 
diff --git a/cache.h b/cache.h
index f2ec5c8c14..2d3df98dc4 100644
--- a/cache.h
+++ b/cache.h
@@ -123,6 +123,7 @@ extern int cache_errno;
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
 
+extern int is_bare_git_dir(const char *dir);
 extern const char *get_git_dir(void);
 extern char *get_object_directory(void);
 extern char *get_refs_directory(void);
diff --git a/environment.c b/environment.c
index 84d870ca4e..f8c7dbcead 100644
--- a/environment.c
+++ b/environment.c
@@ -48,6 +48,16 @@ static void setup_git_env(void)
 	git_graft_file = getenv(GRAFT_ENVIRONMENT);
 	if (!git_graft_file)
 		git_graft_file = xstrdup(git_path("info/grafts"));
+	log_all_ref_updates = !is_bare_git_dir(git_dir);
+}
+
+int is_bare_git_dir (const char *dir)
+{
+	const char *s;
+	if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
+		return 0;
+	s = strrchr(dir, '/');
+	return !s || strcmp(s + 1, DEFAULT_GIT_DIR_ENVIRONMENT);
 }
 
 const char *get_git_dir(void)
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 6a917f2ff4..5637cb5eac 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -63,8 +63,8 @@ test_expect_failure \
 	"test $B"' = $(cat .git/'"$m"')'
 rm -f .git/$m
 
-mkdir -p .git/logs/refs/heads
-touch .git/logs/refs/heads/master
+: a repository with working tree always has reflog these days...
+: >.git/logs/refs/heads/master
 test_expect_success \
 	"create $m (logged by touch)" \
 	'GIT_COMMITTER_DATE="2005-05-26 23:30" \

From 3a79347862f9864e580af2b9daeebac28be9c7a6 Mon Sep 17 00:00:00 2001
From: Luben Tuikov <ltuikov@yahoo.com>
Date: Thu, 14 Dec 2006 23:03:03 -0800
Subject: [PATCH 179/330] Export PERL_PATH

PERL_PATH is used by perl/Makefile so export it.

Signed-off-by: Luben Tuikov <ltuikov@yahoo.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Makefile b/Makefile
index 676d426a9e..05cfe45b10 100644
--- a/Makefile
+++ b/Makefile
@@ -228,6 +228,8 @@ ifndef PERL_PATH
 	PERL_PATH = /usr/bin/perl
 endif
 
+export PERL_PATH
+
 LIB_FILE=libgit.a
 XDIFF_LIB=xdiff/lib.a
 

From 9013192449ed1148da4805e33a2644fca92dd1c7 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Fri, 15 Dec 2006 02:20:03 -0500
Subject: [PATCH 180/330] Teach bash the new features of 'git show'.

Now that 'git show' accepts ref:path as an argument to specify a
tree or blob we should use the same completion logic as we support
for cat-file's object identifier.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/completion/git-completion.bash | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 9c4d23a23c..234cd0954b 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -752,6 +752,24 @@ _git_reset ()
 	COMPREPLY=($(compgen -W "$opt $(__git_refs)" -- "$cur"))
 }
 
+_git_show ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--pretty=*)
+		COMPREPLY=($(compgen -W "
+			oneline short medium full fuller email raw
+			" -- "${cur##--pretty=}"))
+		return
+		;;
+	--*)
+		COMPREPLY=($(compgen -W "--pretty=" -- "$cur"))
+		return
+		;;
+	esac
+	__git_complete_file
+}
+
 _git ()
 {
 	local i c=1 command __git_dir
@@ -802,7 +820,7 @@ _git ()
 	rebase)      _git_rebase ;;
 	repo-config) _git_repo_config ;;
 	reset)       _git_reset ;;
-	show)        _git_log ;;
+	show)        _git_show ;;
 	show-branch) _git_log ;;
 	whatchanged) _git_log ;;
 	*)           COMPREPLY=() ;;
@@ -839,7 +857,7 @@ complete -o default -o nospace -F _git_push git-push
 complete -o default            -F _git_rebase git-rebase
 complete -o default            -F _git_repo_config git-repo-config
 complete -o default            -F _git_reset git-reset
-complete -o default            -F _git_log git-show
+complete -o default -o nospace -F _git_show git-show
 complete -o default -o nospace -F _git_log git-show-branch
 complete -o default -o nospace -F _git_log git-whatchanged
 
@@ -861,7 +879,7 @@ complete -o default            -F _git_merge_base git-merge-base.exe
 complete -o default            -F _git_name_rev git-name-rev.exe
 complete -o default -o nospace -F _git_push git-push.exe
 complete -o default            -F _git_repo_config git-repo-config
-complete -o default -o nospace -F _git_log git-show.exe
+complete -o default -o nospace -F _git_show git-show.exe
 complete -o default -o nospace -F _git_log git-show-branch.exe
 complete -o default -o nospace -F _git_log git-whatchanged.exe
 fi

From 89c4afe0d011b71d0bbcd9ad43f367f6a0dc3d83 Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Fri, 15 Dec 2006 07:39:04 -0500
Subject: [PATCH 181/330] Add --add option to git-repo-config

For multivars, the "git-repo-config name value ^$" is useful but
nonintuitive and troublesome to do repeatedly (since the value is not
at the end of the command line).  This commit simply adds an --add
option that adds a new value to a multivar.  Particularly useful for
tracking a new branch on a remote:

git-repo-config --add remote.origin.fetch +next:origin/next

Includes documentation and test.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-repo-config.txt | 14 +++++++++++++-
 builtin-repo-config.c             |  6 ++++--
 t/t1300-repo-config.sh            | 10 ++++++++++
 3 files changed, 27 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 5bede9ac22..b379ec5075 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-repo-config' [--global] [type] name [value [value_regex]]
+'git-repo-config' [--global] [type] --add name value
 'git-repo-config' [--global] [type] --replace-all name [value [value_regex]]
 'git-repo-config' [--global] [type] --get name [value_regex]
 'git-repo-config' [--global] [type] --get-all name [value_regex]
@@ -23,7 +24,8 @@ You can query/set/replace/unset options with this command. The name is
 actually the section and the key separated by a dot, and the value will be
 escaped.
 
-If you want to set/unset an option which can occur on multiple
+Multiple lines can be added to an option by using the '--add' option.
+If you want to update or unset an option which can occur on multiple
 lines, a POSIX regexp `value_regex` needs to be given.  Only the
 existing values that match the regexp are updated or unset.  If
 you want to handle the lines that do *not* match the regex, just
@@ -53,6 +55,10 @@ OPTIONS
 	Default behavior is to replace at most one line. This replaces
 	all lines matching the key (and optionally the value_regex).
 
+--add::
+	Adds a new line to the option without altering any existing
+	values.  This is the same as providing '^$' as the value_regex.
+
 --get::
 	Get the value for a given key (optionally filtered by a regex
 	matching the value). Returns error code 1 if the key was not
@@ -194,6 +200,12 @@ To actually match only values with an exclamation mark, you have to
 % git repo-config section.key value '[!]'
 ------------
 
+To add a new proxy, without altering any of the existing ones, use
+
+------------
+% git repo-config core.gitproxy '"proxy" for example.com'
+------------
+
 
 include::config.txt[]
 
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index 7b6e5725ae..64fbdb7b24 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -3,7 +3,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --unset | --unset-all] name [value [value_regex]] | --list";
+"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --list";
 
 static char *key;
 static regex_t *key_regexp;
@@ -190,7 +190,9 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			use_key_regexp = 1;
 			do_all = 1;
 			return get_value(argv[2], argv[3]);
-		} else if (!strcmp(argv[1], "--replace-all"))
+		} else if (!strcmp(argv[1], "--add"))
+			return git_config_set_multivar(argv[2], argv[3], "^$", 0);
+		else if (!strcmp(argv[1], "--replace-all"))
 
 			return git_config_set_multivar(argv[2], argv[3], NULL, 1);
 		else
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 0de2497746..16cd642610 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -265,6 +265,16 @@ EOF
 test_expect_success '--get-regexp' \
 	'git-repo-config --get-regexp in > output && cmp output expect'
 
+git-repo-config --add nextsection.nonewline "wow4 for you"
+
+cat > expect << EOF
+wow2 for me
+wow4 for you
+EOF
+
+test_expect_success '--add' \
+	'git-repo-config --get-all nextsection.nonewline > output && cmp output expect'
+
 cat > .git/config << EOF
 [novalue]
 	variable

From e697e4cd1fc492e312a396211bafc629d6297ebd Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Fri, 15 Dec 2006 07:39:33 -0500
Subject: [PATCH 182/330] Make git-diff documentation use [--] when it should.

Two of the cases has "[--] [<path>...]" and two had "-- [<path>...]".
Not terribly consistent and potentially confusing.  Also add "[--]" to
the synopsis so that it's obvious you can use it from the very
beginning.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-diff.txt | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 127d68c962..10fdf88dce 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -8,7 +8,7 @@ git-diff - Show changes between commits, commit and working tree, etc
 
 SYNOPSIS
 --------
-'git-diff' [ --diff-options ] <tree-ish>{0,2} [<path>...]
+'git-diff' [ --diff-options ] <tree-ish>{0,2} [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -30,7 +30,7 @@ tree and the index file, or the index file and the working tree.
 	would want comparison with the latest commit, so if you
 	do not give <commit>, it defaults to HEAD.
 
-'git-diff' [--options] <commit> -- [<path>...]::
+'git-diff' [--options] <commit> [--] [<path>...]::
 
 	This form is to view the changes you have in your
 	working tree relative to the named <commit>.  You can
@@ -38,7 +38,7 @@ tree and the index file, or the index file and the working tree.
 	branch name to compare with the tip of a different
 	branch.
 
-'git-diff' [--options] <commit> <commit> -- [<path>...]::
+'git-diff' [--options] <commit> <commit> [--] [<path>...]::
 
 	This form is to view the changes between two <commit>,
 	for example, tips of two branches.

From aeb80c70ec2baebbf107de0ea5293984798ddd24 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Fri, 15 Dec 2006 21:53:09 -0500
Subject: [PATCH 183/330] Suggest use of "git add file1 file2" when there is
 nothing to commit.

If a user modifies files and runs 'git commit' (without the very
useful -a option) and they have not yet updated the index they
are probably coming from another SCM-like tool which would perform
the same as 'git commit -a' in this case.  Showing the user their
current status and a final line of "nothing to commit" is not very
reassuring, as the user might believe that Git did not recognize
their files were modified.

Instead we can suggest as part of the 'nothing to commit' message
that the user invoke 'git add' to add files to their next commit.

Suggested by Andy Parkins' Git 'niggles' list
(<200612132237.10051.andyparkins@gmail.com>).

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-commit.sh |  2 +-
 wt-status.c   | 11 ++++++-----
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/git-commit.sh b/git-commit.sh
index a30bda19d5..6bce41af4d 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -520,7 +520,7 @@ then
 	current="$(git-rev-parse --verify HEAD)"
 else
 	if [ -z "$(git-ls-files)" ]; then
-		echo >&2 Nothing to commit
+		echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
 		exit 1
 	fi
 	PARENTS=""
diff --git a/wt-status.c b/wt-status.c
index 6e9414dbb0..2173d4f668 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -15,6 +15,7 @@ static char wt_status_colors[][COLOR_MAXLEN] = {
 	"\033[31m", /* WT_STATUS_CHANGED: red */
 	"\033[31m", /* WT_STATUS_UNTRACKED: red */
 };
+static const char* use_add_msg = "use \"git add file1 file2\" to include for commit";
 
 static int parse_status_slot(const char *var, int offset)
 {
@@ -162,8 +163,7 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
 {
 	int i;
 	if (q->nr)
-		wt_status_print_header("Changed but not updated",
-				"use git-add on files to include for commit");
+		wt_status_print_header("Changed but not updated", use_add_msg);
 	for (i = 0; i < q->nr; i++)
 		wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
 	if (q->nr)
@@ -249,8 +249,7 @@ static void wt_status_print_untracked(const struct wt_status *s)
 				continue;
 		}
 		if (!shown_header) {
-			wt_status_print_header("Untracked files",
-				"use \"git add\" to add to commit");
+			wt_status_print_header("Untracked files", use_add_msg);
 			shown_header = 1;
 		}
 		color_printf(color(WT_STATUS_HEADER), "#\t");
@@ -292,7 +291,9 @@ void wt_status_print(struct wt_status *s)
 	if (s->verbose && !s->is_initial)
 		wt_status_print_verbose(s);
 	if (!s->commitable)
-		printf("%s\n", s->amend ? "# No changes" : "nothing to commit");
+		printf("%s (%s)\n",
+			s->amend ? "# No changes" : "nothing to commit",
+			use_add_msg);
 }
 
 int git_status_config(const char *k, const char *v)

From 82dca84871637ac9812c0dec27f56d07cfba524c Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Fri, 15 Dec 2006 21:53:13 -0500
Subject: [PATCH 184/330] Align section headers of 'git status' to new 'git
 add'.

Now that 'git add' is considered a first-class UI for 'update-index'
and that the 'git add' documentation states "Even modified files
must be added to the set of changes about to be committed" we should
make the output of 'git status' align with that documentation and
common usage.

So now we see a status output such as:

  # Added but not yet committed:
  #   (will commit)
  #
  #       new file: x
  #
  # Changed but not added:
  #   (use "git add file1 file2" to include for commit)
  #
  #       modified:   x
  #
  # Untracked files:
  #   (use "git add" on files to include for commit)
  #
  #       y

which just reads better in the context of using 'git add' to
manipulate a commit (and not a checkin, whatever the heck that is).

We also now support 'color.status.added' as an alias for the existing
'color.status.updated', as this alias more closely aligns with the
current output and documentation.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt     |  4 ++--
 Documentation/git-reset.txt  |  2 +-
 Documentation/tutorial-2.txt | 10 +++++-----
 wt-status.c                  |  9 +++++----
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 8abb0820ff..ceac54b024 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -169,8 +169,8 @@ color.status::
 color.status.<slot>::
 	Use customized color for status colorization. `<slot>` is
 	one of `header` (the header text of the status message),
-	`updated` (files which are updated but not committed),
-	`changed` (files which are changed but not updated in the index),
+	`added` or `updated` (files which are added but not committed),
+	`changed` (files which are changed but not added in the index),
 	or `untracked` (files which are not tracked by git). The values of
 	these variables may be specified as in color.diff.<slot>.
 
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 73a0ffc410..4a4ceb6201 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -31,7 +31,7 @@ OPTIONS
 --soft::
 	Does not touch the index file nor the working tree at all, but
 	requires them to be in a good order. This leaves all your changed
-	files "Updated but not checked in", as gitlink:git-status[1] would
+	files "Added but not yet committed", as gitlink:git-status[1] would
 	put it.
 
 --hard::
diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index aa62e13de9..60e54777dc 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -353,23 +353,23 @@ situation:
 ------------------------------------------------
 $ git status
 #
-# Updated but not checked in:
+# Added but not yet committed:
 #   (will commit)
 #
 #       new file: closing.txt
 #
 #
-# Changed but not updated:
-#   (use git-update-index to mark for commit)
+# Changed but not added:
+#   (use "git add file1 file2" to include for commit)
 #
 #       modified: file.txt
 #
 ------------------------------------------------
 
 Since the current state of closing.txt is cached in the index file,
-it is listed as "updated but not checked in".  Since file.txt has
+it is listed as "added but not yet committed".  Since file.txt has
 changes in the working directory that aren't reflected in the index,
-it is marked "changed but not updated".  At this point, running "git
+it is marked "changed but not added".  At this point, running "git
 commit" would create a commit that added closing.txt (with its new
 contents), but that didn't modify file.txt.
 
diff --git a/wt-status.c b/wt-status.c
index 2173d4f668..c388ce1293 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -21,7 +21,8 @@ static int parse_status_slot(const char *var, int offset)
 {
 	if (!strcasecmp(var+offset, "header"))
 		return WT_STATUS_HEADER;
-	if (!strcasecmp(var+offset, "updated"))
+	if (!strcasecmp(var+offset, "updated")
+		|| !strcasecmp(var+offset, "added"))
 		return WT_STATUS_UPDATED;
 	if (!strcasecmp(var+offset, "changed"))
 		return WT_STATUS_CHANGED;
@@ -146,7 +147,7 @@ static void wt_status_print_updated_cb(struct diff_queue_struct *q,
 		if (q->queue[i]->status == 'U')
 			continue;
 		if (!shown_header) {
-			wt_status_print_header("Updated but not checked in",
+			wt_status_print_header("Added but not yet committed",
 					"will commit");
 			s->commitable = 1;
 			shown_header = 1;
@@ -163,7 +164,7 @@ static void wt_status_print_changed_cb(struct diff_queue_struct *q,
 {
 	int i;
 	if (q->nr)
-		wt_status_print_header("Changed but not updated", use_add_msg);
+		wt_status_print_header("Changed but not added", use_add_msg);
 	for (i = 0; i < q->nr; i++)
 		wt_status_print_filepair(WT_STATUS_CHANGED, q->queue[i]);
 	if (q->nr)
@@ -178,7 +179,7 @@ void wt_status_print_initial(struct wt_status *s)
 	read_cache();
 	if (active_nr) {
 		s->commitable = 1;
-		wt_status_print_header("Updated but not checked in",
+		wt_status_print_header("Added but not yet committed",
 				"will commit");
 	}
 	for (i = 0; i < active_nr; i++) {

From aef4e921a01350600071bf18fc2706b8fca55e47 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 15 Dec 2006 10:59:54 -0800
Subject: [PATCH 185/330] git-svn: convert to using Git.pm

Thanks to Git.pm, I've been able to greatly reduce the amount
of extra work that needs to be done to manage input/output
pipes in Perl.

chomp usage has also been greatly reduced, too.

All tests (including full-svn-test) still pass, but this has
not been tested extensively in the real-world.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 328 +++++++++++++++++++++------------------------------
 1 file changed, 135 insertions(+), 193 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 73ab8d873f..f453c9ab98 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -46,6 +46,8 @@ use File::Copy qw/copy/;
 use POSIX qw/strftime/;
 use IPC::Open3;
 use Memoize;
+use Git qw/command command_oneline command_noisy
+           command_output_pipe command_input_pipe command_close_pipe/;
 memoize('revisions_eq');
 memoize('cmt_metadata');
 memoize('get_commit_time');
@@ -232,28 +234,27 @@ sub version {
 }
 
 sub rebuild {
-	if (quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0")) {
+	if (!verify_ref("refs/remotes/$GIT_SVN^0")) {
 		copy_remote_ref();
 	}
 	$SVN_URL = shift or undef;
 	my $newest_rev = 0;
 	if ($_upgrade) {
-		sys('git-update-ref',"refs/remotes/$GIT_SVN","$GIT_SVN-HEAD");
+		command_noisy('update-ref',"refs/remotes/$GIT_SVN","
+		              $GIT_SVN-HEAD");
 	} else {
 		check_upgrade_needed();
 	}
 
-	my $pid = open(my $rev_list,'-|');
-	defined $pid or croak $!;
-	if ($pid == 0) {
-		exec("git-rev-list","refs/remotes/$GIT_SVN") or croak $!;
-	}
+	my ($rev_list, $ctx) = command_output_pipe("rev-list",
+	                                           "refs/remotes/$GIT_SVN");
 	my $latest;
 	while (<$rev_list>) {
 		chomp;
 		my $c = $_;
 		croak "Non-SHA1: $c\n" unless $c =~ /^$sha1$/o;
-		my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`);
+		my @commit = grep(/^git-svn-id: /,
+		                  command(qw/cat-file commit/, $c));
 		next if (!@commit); # skip merges
 		my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]);
 		if (!defined $rev || !$uuid) {
@@ -279,7 +280,7 @@ sub rebuild {
 		print "r$rev = $c\n";
 		$newest_rev = $rev if ($rev > $newest_rev);
 	}
-	close $rev_list or croak $?;
+	command_close_pipe($rev_list, $ctx);
 
 	goto out if $_use_lib;
 	if (!chdir $SVN_WC) {
@@ -287,7 +288,7 @@ sub rebuild {
 		chdir $SVN_WC or croak $!;
 	}
 
-	$pid = fork;
+	my $pid = fork;
 	defined $pid or croak $!;
 	if ($pid == 0) {
 		my @svn_up = qw(svn up);
@@ -323,10 +324,10 @@ sub init {
 
 	$SVN_URL = $url;
 	unless (-d $GIT_DIR) {
-		my @init_db = ('git-init-db');
+		my @init_db = ('init-db');
 		push @init_db, "--template=$_template" if defined $_template;
 		push @init_db, "--shared" if defined $_shared;
-		sys(@init_db);
+		command_noisy(@init_db);
 	}
 	setup_git_svn();
 }
@@ -335,9 +336,8 @@ sub fetch {
 	check_upgrade_needed();
 	$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
 	my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_);
-	if ($ret->{commit} && quiet_run(qw(git-rev-parse --verify
-						refs/heads/master^0))) {
-		sys(qw(git-update-ref refs/heads/master),$ret->{commit});
+	if ($ret->{commit} && !verify_ref('refs/heads/master^0')) {
+		command_noisy(qw(update-ref refs/heads/master),$ret->{commit});
 	}
 	return $ret;
 }
@@ -416,16 +416,16 @@ sub fetch_lib {
 	read_uuid();
 	if (defined $last_commit) {
 		unless (-e $GIT_SVN_INDEX) {
-			sys(qw/git-read-tree/, $last_commit);
+			command_noisy('read-tree', $last_commit);
 		}
-		chomp (my $x = `git-write-tree`);
-		my ($y) = (`git-cat-file commit $last_commit`
+		my $x = command_oneline('write-tree');
+		my ($y) = (command(qw/cat-file commit/, $last_commit)
 							=~ /^tree ($sha1)/m);
 		if ($y ne $x) {
 			unlink $GIT_SVN_INDEX or croak $!;
-			sys(qw/git-read-tree/, $last_commit);
+			command_noisy('read-tree', $last_commit);
 		}
-		chomp ($x = `git-write-tree`);
+		$x = command_oneline('write-tree');
 		if ($y ne $x) {
 			print STDERR "trees ($last_commit) $y != $x\n",
 				 "Something is seriously wrong...\n";
@@ -489,16 +489,15 @@ sub commit {
 	}
 	my @revs;
 	foreach my $c (@commits) {
-		chomp(my @tmp = safe_qx('git-rev-parse',$c));
+		my @tmp = command('rev-parse',$c);
 		if (scalar @tmp == 1) {
 			push @revs, $tmp[0];
 		} elsif (scalar @tmp > 1) {
-			push @revs, reverse (safe_qx('git-rev-list',@tmp));
+			push @revs, reverse(command('rev-list',@tmp));
 		} else {
 			die "Failed to rev-parse $c\n";
 		}
 	}
-	chomp @revs;
 	$_use_lib ? commit_lib(@revs) : commit_cmd(@revs);
 	print "Done committing ",scalar @revs," revisions to SVN\n";
 }
@@ -606,10 +605,10 @@ sub commit_lib {
 sub dcommit {
 	my $head = shift || 'HEAD';
 	my $gs = "refs/remotes/$GIT_SVN";
-	chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..$head"));
+	my @refs = command(qw/rev-list --no-merges/, "$gs..$head");
 	my $last_rev;
 	foreach my $d (reverse @refs) {
-		if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
+		if (!verify_ref("$d~1")) {
 			die "Commit $d\n",
 			    "has no parent commit, and therefore ",
 			    "nothing to diff against.\n",
@@ -633,7 +632,7 @@ sub dcommit {
 	}
 	return if $_dry_run;
 	fetch();
-	my @diff = safe_qx('git-diff-tree', $head, $gs);
+	my @diff = command('diff-tree', $head, $gs, '--');
 	my @finish;
 	if (@diff) {
 		@finish = qw/rebase/;
@@ -645,7 +644,7 @@ sub dcommit {
 		      "Resetting to the latest $gs\n";
 		@finish = qw/reset --mixed/;
 	}
-	sys('git', @finish, $gs);
+	command_noisy(@finish, $gs);
 }
 
 sub show_ignore {
@@ -689,7 +688,7 @@ sub graft_branches {
 
 	if (%$grafts) {
 		# temporarily disable our grafts file to make this idempotent
-		chomp($gr_sha1 = safe_qx(qw/git-hash-object -w/,$gr_file));
+		chomp($gr_sha1 = command(qw/hash-object -w/,$gr_file));
 		rename $gr_file, "$gr_file~$gr_sha1" or croak $!;
 	}
 
@@ -743,7 +742,7 @@ sub multi_init {
 	unless (-d $GIT_SVN_DIR) {
 		print "GIT_SVN_ID set to 'trunk' for $_trunk\n" if $ch_id;
 		init($_trunk);
-		sys('git-repo-config', 'svn.trunk', $_trunk);
+		command_noisy('repo-config', 'svn.trunk', $_trunk);
 	}
 	complete_url_ls_init($url, $_branches, '--branches/-b', '');
 	complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
@@ -781,11 +780,8 @@ sub show_log {
 	}
 
 	config_pager();
-	my $pid = open(my $log,'-|');
-	defined $pid or croak $!;
-	if (!$pid) {
-		exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
-	}
+	@args = (git_svn_log_cmd($r_min, $r_max), @args);
+	my $log = command_output_pipe(@args);
 	run_pager();
 	my (@k, $c, $d);
 
@@ -832,7 +828,7 @@ sub show_log {
 		process_commit($_, $r_min, $r_max) foreach reverse @k;
 	}
 out:
-	close $log;
+	eval { command_close_pipe($log) };
 	print '-' x72,"\n" unless $_incremental || $_oneline;
 }
 
@@ -912,7 +908,7 @@ sub cmt_showable {
 	return 1 if defined $c->{r};
 	if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
 				$c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
-		my @msg = safe_qx(qw/git-cat-file commit/, $c->{c});
+		my @msg = command(qw/cat-file commit/, $c->{c});
 		shift @msg while ($msg[0] ne "\n");
 		shift @msg;
 		@{$c->{l}} = grep !/^git-svn-id: /, @msg;
@@ -961,7 +957,7 @@ sub log_use_color {
 
 sub git_svn_log_cmd {
 	my ($r_min, $r_max) = @_;
-	my @cmd = (qw/git-log --abbrev-commit --pretty=raw
+	my @cmd = (qw/log --abbrev-commit --pretty=raw
 			--default/, "refs/remotes/$GIT_SVN");
 	push @cmd, '-r' unless $_non_recursive;
 	push @cmd, qw/--raw --name-status/ if $_verbose;
@@ -1071,7 +1067,7 @@ sub complete_url_ls_init {
 	waitpid $pid, 0;
 	croak $? if $?;
 	my ($n) = ($switch =~ /^--(\w+)/);
-	sys('git-repo-config', "svn.$n", $var);
+	command_noisy('repo-config', "svn.$n", $var);
 }
 
 sub common_prefix {
@@ -1103,11 +1099,8 @@ sub graft_tree_joins {
 
 	git_svn_each(sub {
 		my $i = shift;
-		defined(my $pid = open my $fh, '-|') or croak $!;
-		if (!$pid) {
-			exec qw/git-rev-list --pretty=raw/,
-					"refs/remotes/$i" or croak $!;
-		}
+		my @args = (qw/rev-list --pretty=raw/, "refs/remotes/$i");
+		my ($fh, $ctx) = command_output_pipe(@args);
 		while (<$fh>) {
 			next unless /^commit ($sha1)$/o;
 			my $c = $1;
@@ -1130,9 +1123,7 @@ sub graft_tree_joins {
 
 			foreach my $p (@{$tree_map{$t}}) {
 				next if $p eq $c;
-				my $mb = eval {
-					safe_qx('git-merge-base', $c, $p)
-				};
+				my $mb = eval { command('merge-base', $c, $p) };
 				next unless ($@ || $?);
 				if (defined $r_a) {
 					# see if SVN says it's a relative
@@ -1161,7 +1152,7 @@ sub graft_tree_joins {
 				# what should we do when $ct == $s ?
 			}
 		}
-		close $fh or croak $?;
+		command_close_pipe($fh, $ctx);
 	});
 }
 
@@ -1297,6 +1288,11 @@ sub read_uuid {
 	}
 }
 
+sub verify_ref {
+	my ($ref) = @_;
+	eval { command_oneline([ 'rev-parse', $ref ], { STDERR => 0 }) };
+}
+
 sub quiet_run {
 	my $pid = fork;
 	defined $pid or croak $!;
@@ -1379,13 +1375,14 @@ sub assert_svn_wc_clean {
 sub get_tree_from_treeish {
 	my ($treeish) = @_;
 	croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o;
-	chomp(my $type = `git-cat-file -t $treeish`);
+	my $type = command_oneline(qw/cat-file -t/, $treeish);
 	my $expected;
 	while ($type eq 'tag') {
-		chomp(($treeish, $type) = `git-cat-file tag $treeish`);
+		($treeish, $type) = command(qw/cat-file tag/, $treeish);
 	}
 	if ($type eq 'commit') {
-		$expected = (grep /^tree /,`git-cat-file commit $treeish`)[0];
+		$expected = (grep /^tree /, command(qw/cat-file commit/,
+		                                    $treeish))[0];
 		($expected) = ($expected =~ /^tree ($sha1)$/);
 		die "Unable to get tree from $treeish\n" unless $expected;
 	} elsif ($type eq 'tree') {
@@ -1407,7 +1404,7 @@ sub assert_tree {
 	}
 	my $old_index = set_index($tmpindex);
 	index_changes(1);
-	chomp(my $tree = `git-write-tree`);
+	my $tree = command_oneline('write-tree');
 	restore_index($old_index);
 	if ($tree ne $expected) {
 		croak "Tree mismatch, Got: $tree, Expected: $expected\n";
@@ -1415,8 +1412,20 @@ sub assert_tree {
 	unlink $tmpindex;
 }
 
-sub parse_diff_tree {
-	my $diff_fh = shift;
+sub get_diff {
+	my ($from, $treeish) = @_;
+	assert_tree($from);
+	print "diff-tree $from $treeish\n";
+	my @diff_tree = qw(diff-tree -z -r);
+	if ($_cp_similarity) {
+		push @diff_tree, "-C$_cp_similarity";
+	} else {
+		push @diff_tree, '-C';
+	}
+	push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
+	push @diff_tree, "-l$_l" if defined $_l;
+	push @diff_tree, $from, $treeish;
+	my ($diff_fh, $ctx) = command_output_pipe(@diff_tree);
 	local $/ = "\0";
 	my $state = 'meta';
 	my @mods;
@@ -1452,8 +1461,7 @@ sub parse_diff_tree {
 			croak "Error parsing $_\n";
 		}
 	}
-	close $diff_fh or croak $?;
-
+	command_close_pipe($diff_fh, $ctx);
 	return \@mods;
 }
 
@@ -1547,26 +1555,6 @@ sub precommit_check {
 }
 
 
-sub get_diff {
-	my ($from, $treeish) = @_;
-	assert_tree($from);
-	print "diff-tree $from $treeish\n";
-	my $pid = open my $diff_fh, '-|';
-	defined $pid or croak $!;
-	if ($pid == 0) {
-		my @diff_tree = qw(git-diff-tree -z -r);
-		if ($_cp_similarity) {
-			push @diff_tree, "-C$_cp_similarity";
-		} else {
-			push @diff_tree, '-C';
-		}
-		push @diff_tree, '--find-copies-harder' if $_find_copies_harder;
-		push @diff_tree, "-l$_l" if defined $_l;
-		exec(@diff_tree, $from, $treeish) or croak $!;
-	}
-	return parse_diff_tree($diff_fh);
-}
-
 sub svn_checkout_tree {
 	my ($from, $treeish) = @_;
 	my $mods = get_diff($from->{commit}, $treeish);
@@ -1676,14 +1664,10 @@ sub get_commit_message {
 	my %log_msg = ( msg => '' );
 	open my $msg, '>', $commit_msg or croak $!;
 
-	chomp(my $type = `git-cat-file -t $commit`);
+	my $type = command_oneline(qw/cat-file -t/, $commit);
 	if ($type eq 'commit' || $type eq 'tag') {
-		my $pid = open my $msg_fh, '-|';
-		defined $pid or croak $!;
-
-		if ($pid == 0) {
-			exec('git-cat-file', $type, $commit) or croak $!;
-		}
+		my ($msg_fh, $ctx) = command_output_pipe('cat-file',
+		                                         $type, $commit);
 		my $in_msg = 0;
 		while (<$msg_fh>) {
 			if (!$in_msg) {
@@ -1695,7 +1679,7 @@ sub get_commit_message {
 				print $msg $_ or croak $!;
 			}
 		}
-		close $msg_fh or croak $?;
+		command_close_pipe($msg_fh, $ctx);
 	}
 	close $msg or croak $!;
 
@@ -1771,13 +1755,8 @@ sub svn_commit_tree {
 }
 
 sub rev_list_raw {
-	my (@args) = @_;
-	my $pid = open my $fh, '-|';
-	defined $pid or croak $!;
-	if (!$pid) {
-		exec(qw/git-rev-list --pretty=raw/, @args) or croak $!;
-	}
-	return { fh => $fh, t => { } };
+	my ($fh, $c) = command_output_pipe(qw/rev-list --pretty=raw/, @_);
+	return { fh => $fh, ctx => $c, t => { } };
 }
 
 sub next_rev_list_entry {
@@ -1799,6 +1778,7 @@ sub next_rev_list_entry {
 			$x->{m} .= $_;
 		}
 	}
+	command_close_pipe($fh, $rl->{ctx});
 	return ($x != $rl->{t}) ? $x : undef;
 }
 
@@ -1924,15 +1904,10 @@ sub sys { system(@_) == 0 or croak $? }
 sub do_update_index {
 	my ($z_cmd, $cmd, $no_text_base) = @_;
 
-	my $z = open my $p, '-|';
-	defined $z or croak $!;
-	unless ($z) { exec @$z_cmd or croak $! }
+	my ($p, $pctx) = command_output_pipe(@$z_cmd);
 
-	my $pid = open my $ui, '|-';
-	defined $pid or croak $!;
-	unless ($pid) {
-		exec('git-update-index',"--$cmd",'-z','--stdin') or croak $!;
-	}
+	my ($ui, $uctx) = command_input_pipe('update-index',
+	                                     "--$cmd",'-z','--stdin');
 	local $/ = "\0";
 	while (my $x = <$p>) {
 		chomp $x;
@@ -1956,7 +1931,8 @@ sub do_update_index {
 		}
 		print $ui $x,"\0";
 	}
-	close $ui or croak $?;
+	command_close_pipe($p, $pctx);
+	command_close_pipe($ui, $uctx);
 }
 
 sub index_changes {
@@ -1968,10 +1944,10 @@ sub index_changes {
 		close $fd or croak $!;
 	}
 	my $no_text_base = shift;
-	do_update_index([qw/git-diff-files --name-only -z/],
+	do_update_index([qw/diff-files --name-only -z/],
 			'remove',
 			$no_text_base);
-	do_update_index([qw/git-ls-files -z --others/,
+	do_update_index([qw/ls-files -z --others/,
 				"--exclude-from=$GIT_SVN_DIR/info/exclude"],
 			'add',
 			$no_text_base);
@@ -2004,10 +1980,10 @@ sub assert_revision_unknown {
 
 sub trees_eq {
 	my ($x, $y) = @_;
-	my @x = safe_qx('git-cat-file','commit',$x);
-	my @y = safe_qx('git-cat-file','commit',$y);
-	if (($y[0] ne $x[0]) || $x[0] !~ /^tree $sha1\n$/
-				|| $y[0] !~ /^tree $sha1\n$/) {
+	my @x = command(qw/cat-file commit/,$x);
+	my @y = command(qw/cat-file commit/,$y);
+	if (($y[0] ne $x[0]) || $x[0] ne "tree $sha1"
+		             || $y[0] ne "tree $sha1") {
 		print STDERR "Trees not equal: $y[0] != $x[0]\n";
 		return 0
 	}
@@ -2039,33 +2015,24 @@ sub git_commit {
 	if (!defined $tree) {
 		my $index = set_index($GIT_SVN_INDEX);
 		index_changes();
-		chomp($tree = `git-write-tree`);
+		$tree = command_oneline('write-tree');
 		croak $? if $?;
 		restore_index($index);
 	}
 
 	# just in case we clobber the existing ref, we still want that ref
 	# as our parent:
-	open my $null, '>', '/dev/null' or croak $!;
-	open my $stderr, '>&', \*STDERR or croak $!;
-	open STDERR, '>&', $null or croak $!;
-	if (my $cur = eval { safe_qx('git-rev-parse',
-	                             "refs/remotes/$GIT_SVN^0") }) {
+	if (my $cur = verify_ref("refs/remotes/$GIT_SVN^0")) {
 		chomp $cur;
 		push @tmp_parents, $cur;
 	}
-	open STDERR, '>&', $stderr or croak $!;
-	close $stderr or croak $!;
-	close $null or croak $!;
 
 	if (exists $tree_map{$tree}) {
 		foreach my $p (@{$tree_map{$tree}}) {
 			my $skip;
 			foreach (@tmp_parents) {
 				# see if a common parent is found
-				my $mb = eval {
-					safe_qx('git-merge-base', $_, $p)
-				};
+				my $mb = eval { command('merge-base', $_, $p) };
 				next if ($@ || $?);
 				$skip = 1;
 				last;
@@ -2107,7 +2074,7 @@ sub git_commit {
 	if ($commit !~ /^$sha1$/o) {
 		die "Failed to commit, invalid sha1: $commit\n";
 	}
-	sys('git-update-ref',"refs/remotes/$GIT_SVN",$commit);
+	command_noisy('update-ref',"refs/remotes/$GIT_SVN",$commit);
 	revdb_set($REVDB, $log_msg->{revision}, $commit);
 
 	# this output is read via pipe, do not change:
@@ -2119,7 +2086,8 @@ sub git_commit {
 sub check_repack {
 	if ($_repack && (--$_repack_nr == 0)) {
 		$_repack_nr = $_repack;
-		sys("git repack $_repack_flags");
+		# repack doesn't use any arguments with spaces in them, does it?
+		command_noisy('repack', split(/\s+/, $_repack_flags));
 	}
 }
 
@@ -2238,20 +2206,11 @@ sub check_upgrade_needed {
 		open my $fh, '>>',$REVDB or croak $!;
 		close $fh;
 	}
-	my $old = eval {
-		my $pid = open my $child, '-|';
-		defined $pid or croak $!;
-		if ($pid == 0) {
-			close STDERR;
-			exec('git-rev-parse',"$GIT_SVN-HEAD") or croak $!;
-		}
-		my @ret = (<$child>);
-		close $child or croak $?;
-		die $? if $?; # just in case close didn't error out
-		return wantarray ? @ret : join('',@ret);
+	return unless eval {
+		command([qw/rev-parse --verify/,"$GIT_SVN-HEAD^0"],
+		        {STDERR => 0});
 	};
-	return unless $old;
-	my $head = eval { safe_qx('git-rev-parse',"refs/remotes/$GIT_SVN") };
+	my $head = eval { command('rev-parse',"refs/remotes/$GIT_SVN") };
 	if ($@ || !$head) {
 		print STDERR "Please run: $0 rebuild --upgrade\n";
 		exit 1;
@@ -2263,12 +2222,8 @@ sub check_upgrade_needed {
 sub map_tree_joins {
 	my %seen;
 	foreach my $br (@_branch_from) {
-		my $pid = open my $pipe, '-|';
-		defined $pid or croak $!;
-		if ($pid == 0) {
-			exec(qw(git-rev-list --topo-order --pretty=raw), $br)
-								or croak $!;
-		}
+		my $pipe = command_output_pipe(qw/rev-list
+		                            --topo-order --pretty=raw/, $br);
 		while (<$pipe>) {
 			if (/^commit ($sha1)$/o) {
 				my $commit = $1;
@@ -2284,7 +2239,7 @@ sub map_tree_joins {
 				$seen{$commit} = 1;
 			}
 		}
-		close $pipe; # we could be breaking the pipe early
+		eval { command_close_pipe($pipe) };
 	}
 }
 
@@ -2296,7 +2251,7 @@ sub load_all_refs {
 
 	# don't worry about rev-list on non-commit objects/tags,
 	# it shouldn't blow up if a ref is a blob or tree...
-	chomp(@_branch_from = `git-rev-parse --symbolic --all`);
+	@_branch_from = command(qw/rev-parse --symbolic --all/);
 }
 
 # '<svn username> = real-name <email address>' mapping based on git-svnimport:
@@ -2330,7 +2285,7 @@ sub svn_propget_base {
 
 sub git_svn_each {
 	my $sub = shift;
-	foreach (`git-rev-parse --symbolic --all`) {
+	foreach (command(qw/rev-parse --symbolic --all/)) {
 		next unless s#^refs/remotes/##;
 		chomp $_;
 		next unless -f "$GIT_DIR/svn/$_/info/url";
@@ -2371,7 +2326,7 @@ sub migration_check {
 				"$GIT_SVN_DIR\n\t(required for this version ",
 				"($VERSION) of git-svn) does not.\n";
 
-	foreach my $x (`git-rev-parse --symbolic --all`) {
+	foreach my $x (command(qw/rev-parse --symbolic --all/)) {
 		next unless $x =~ s#^refs/remotes/##;
 		chomp $x;
 		next unless -f "$GIT_DIR/$x/info/url";
@@ -2476,11 +2431,7 @@ sub write_grafts {
 		my $p = $grafts->{$c};
 		my %x; # real parents
 		delete $p->{$c}; # commits are not self-reproducing...
-		my $pid = open my $ch, '-|';
-		defined $pid or croak $!;
-		if (!$pid) {
-			exec(qw/git-cat-file commit/, $c) or croak $!;
-		}
+		my $ch = command_output_pipe(qw/cat-file commit/, $c);
 		while (<$ch>) {
 			if (/^parent ($sha1)/) {
 				$x{$1} = $p->{$1} = 1;
@@ -2488,7 +2439,7 @@ sub write_grafts {
 				last unless /^\S/;
 			}
 		}
-		close $ch; # breaking the pipe
+		eval { command_close_pipe($ch) }; # breaking the pipe
 
 		# if real parents are the only ones in the grafts, drop it
 		next if join(' ',sort keys %$p) eq join(' ',sort keys %x);
@@ -2500,7 +2451,7 @@ sub write_grafts {
 			next if $del{$i} || $p->{$i} == 2;
 			foreach my $j (@jp) {
 				next if $i eq $j || $del{$j} || $p->{$j} == 2;
-				$mb = eval { safe_qx('git-merge-base',$i,$j) };
+				$mb = eval { command('merge-base', $i, $j) };
 				next unless $mb;
 				chomp $mb;
 				next if $x{$mb};
@@ -2571,15 +2522,12 @@ sub extract_metadata {
 
 sub cmt_metadata {
 	return extract_metadata((grep(/^git-svn-id: /,
-		safe_qx(qw/git-cat-file commit/, shift)))[-1]);
+		command(qw/cat-file commit/, shift)))[-1]);
 }
 
 sub get_commit_time {
 	my $cmt = shift;
-	defined(my $pid = open my $fh, '-|') or croak $!;
-	if (!$pid) {
-		exec qw/git-rev-list --pretty=raw -n1/, $cmt or croak $!;
-	}
+	my $fh = command_output_pipe(qw/rev-list --pretty=raw -n1/, $cmt);
 	while (<$fh>) {
 		/^committer\s(?:.+) (\d+) ([\-\+]?\d+)$/ or next;
 		my ($s, $tz) = ($1, $2);
@@ -2588,7 +2536,7 @@ sub get_commit_time {
 		} elsif ($tz =~ s/^\-//) {
 			$s -= tz_to_s_offset($tz);
 		}
-		close $fh;
+		eval { command_close_pipe($fh) };
 		return $s;
 	}
 	die "Can't get commit time for commit: $cmt\n";
@@ -2748,7 +2696,6 @@ sub libsvn_load {
 		push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
 		push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
 		*SVN::Git::Fetcher::process_rm = *process_rm;
-		*SVN::Git::Fetcher::safe_qx = *safe_qx;
 		my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
 					$SVN::Node::dir.$SVN::Node::unknown.
 					$SVN::Node::none.$SVN::Node::file.
@@ -2965,7 +2912,7 @@ sub libsvn_get_file {
 	my $mode = exists $props->{'svn:executable'} ? '100755' : '100644';
 	if (exists $props->{'svn:special'}) {
 		$mode = '120000';
-		my $link = `git-cat-file blob $hash`;
+		my $link = `git-cat-file blob $hash`; # no chomping symlinks
 		$link =~ s/^link // or die "svn:special file with contents: <",
 						$link, "> is not understood\n";
 		defined($pid = open3($in, $out, '>&STDERR',
@@ -3069,19 +3016,17 @@ sub libsvn_log_entry {
 sub process_rm {
 	my ($gui, $last_commit, $f, $q) = @_;
 	# remove entire directories.
-	if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
-		defined(my $pid = open my $ls, '-|') or croak $!;
-		if (!$pid) {
-			exec(qw/git-ls-tree -r --name-only -z/,
-				$last_commit,'--',$f) or croak $!;
-		}
+	if (command('ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
+		my ($ls, $ctx) = command_output_pipe(qw/ls-tree
+		                                     -r --name-only -z/,
+				                     $last_commit,'--',$f);
 		local $/ = "\0";
 		while (<$ls>) {
 			print $gui '0 ',0 x 40,"\t",$_ or croak $!;
 			print "\tD\t$_\n" unless $q;
 		}
 		print "\tD\t$f/\n" unless $q;
-		close $ls or croak $?;
+		command_close_pipe($ls, $ctx);
 		return $SVN::Node::dir;
 	} else {
 		print $gui '0 ',0 x 40,"\t",$f,"\0" or croak $!;
@@ -3112,7 +3057,7 @@ sub libsvn_fetch_delta {
 
 sub libsvn_fetch_full {
 	my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
-	open my $gui, '| git-update-index -z --index-info' or croak $!;
+	my ($gui, $ctx) = command_input_pipe(qw/update-index -z --index-info/);
 	my %amr;
 	my $ut = { empty => {}, dir_prop => {}, file_prop => {} };
 	my $p = $SVN->{svn_path};
@@ -3166,20 +3111,14 @@ sub libsvn_fetch_full {
 		%{$ut->{dir_prop}->{''}} = %$props;
 		$pool->clear;
 	}
-	close $gui or croak $?;
+	command_close_pipe($gui, $ctx);
 	libsvn_log_entry($rev, $author, $date, $msg, [$last_commit], $ut);
 }
 
 sub svn_grab_base_rev {
-	defined(my $pid = open my $fh, '-|') or croak $!;
-	if (!$pid) {
-		open my $null, '>', '/dev/null' or croak $!;
-		open STDERR, '>&', $null or croak $!;
-		exec qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0"
-								or croak $!;
-	}
-	chomp(my $c = do { local $/; <$fh> });
-	close $fh;
+	my $c = eval { command_oneline([qw/rev-parse --verify/,
+	                                "refs/remotes/$GIT_SVN^0"],
+				        { STDERR => 0 }) };
 	if (defined $c && length $c) {
 		my ($url, $rev, $uuid) = cmt_metadata($c);
 		return ($rev, $c) if defined $rev;
@@ -3358,7 +3297,7 @@ sub libsvn_find_parent_branch {
 	if (revisions_eq($branch_from, $r0, $r)) {
 		unlink $GIT_SVN_INDEX;
 		print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
-		sys(qw/git-read-tree/, $parent);
+		command_noisy('read-tree', $parent);
 		unless (libsvn_can_do_switch()) {
 			return libsvn_fetch_full($parent, $paths, $rev,
 						$author, $date, $msg);
@@ -3367,7 +3306,7 @@ sub libsvn_find_parent_branch {
 		# included with SVN 1.4.2 (the latest version at the moment),
 		# so we can't rely on it.
 		my $ra = libsvn_connect("$url/$branch_from");
-		my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q});
+		my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q });
 		my $pool = SVN::Pool->new;
 		my $reporter = $ra->do_switch($rev, '', 1, $SVN->{url},
 		                              $ed, $pool);
@@ -3413,9 +3352,10 @@ sub libsvn_new_tree {
 		$ut = $ed;
 	} else {
 		$ut = { empty => {}, dir_prop => {}, file_prop => {} };
-		open my $gui, '| git-update-index -z --index-info' or croak $!;
+	        my ($gui, $ctx) = command_input_pipe(qw/update-index
+	                                             -z --index-info/);
 		libsvn_traverse($gui, '', $SVN->{svn_path}, $rev, undef, $ut);
-		close $gui or croak $?;
+		command_close_pipe($gui, $ctx);
 	}
 	libsvn_log_entry($rev, $author, $date, $msg, [], $ut);
 }
@@ -3487,7 +3427,7 @@ sub libsvn_commit_cb {
 		my $log = libsvn_log_entry($rev,$committer,$date,$msg);
 		$log->{tree} = get_tree_from_treeish($c);
 		my $cmt = git_commit($log, $cmt_last, $c);
-		my @diff = safe_qx('git-diff-tree', $cmt, $c);
+		my @diff = command('diff-tree', $cmt, $c);
 		if (@diff) {
 			print STDERR "Trees differ: $cmt $c\n",
 					join('',@diff),"\n";
@@ -3579,8 +3519,8 @@ sub revdb_get {
 sub copy_remote_ref {
 	my $origin = $_cp_remote ? $_cp_remote : 'origin';
 	my $ref = "refs/remotes/$GIT_SVN";
-	if (safe_qx('git-ls-remote', $origin, $ref)) {
-		sys(qw/git fetch/, $origin, "$ref:$ref");
+	if (command('ls-remote', $origin, $ref)) {
+		command_noisy('fetch', $origin, "$ref:$ref");
 	} elsif ($_cp_remote && !$_upgrade) {
 		die "Unable to find remote reference: ",
 				"refs/remotes/$GIT_SVN on $origin\n";
@@ -3592,14 +3532,14 @@ use strict;
 use warnings;
 use Carp qw/croak/;
 use IO::File qw//;
+use Git qw/command command_oneline command_noisy
+           command_output_pipe command_input_pipe command_close_pipe/;
 
 # file baton members: path, mode_a, mode_b, pool, fh, blob, base
 sub new {
 	my ($class, $git_svn) = @_;
 	my $self = SVN::Delta::Editor->new;
 	bless $self, $class;
-	open my $gui, '| git-update-index -z --index-info' or croak $!;
-	$self->{gui} = $gui;
 	$self->{c} = $git_svn->{c} if exists $git_svn->{c};
 	$self->{q} = $git_svn->{q};
 	$self->{empty} = {};
@@ -3607,6 +3547,8 @@ sub new {
 	$self->{file_prop} = {};
 	$self->{absent_dir} = {};
 	$self->{absent_file} = {};
+	($self->{gui}, $self->{ctx}) = command_input_pipe(
+	                                     qw/update-index -z --index-info/);
 	require Digest::MD5;
 	$self;
 }
@@ -3629,7 +3571,7 @@ sub delete_entry {
 
 sub open_file {
 	my ($self, $path, $pb, $rev) = @_;
-	my ($mode, $blob) = (safe_qx('git-ls-tree',$self->{c},'--',$path)
+	my ($mode, $blob) = (command('ls-tree', $self->{c}, '--',$path)
 	                     =~ /^(\d{6}) blob ([a-f\d]{40})\t/);
 	unless (defined $mode && defined $blob) {
 		die "$path was not found in commit $self->{c} (r$rev)\n";
@@ -3764,13 +3706,13 @@ sub close_file {
 
 sub abort_edit {
 	my $self = shift;
-	close $self->{gui};
+	eval { command_close_pipe($self->{gui}, $self->{ctx}) };
 	$self->SUPER::abort_edit(@_);
 }
 
 sub close_edit {
 	my $self = shift;
-	close $self->{gui} or croak $!;
+	command_close_pipe($self->{gui}, $self->{ctx});
 	$self->{git_commit_ok} = 1;
 	$self->SUPER::close_edit(@_);
 }
@@ -3781,6 +3723,8 @@ use strict;
 use warnings;
 use Carp qw/croak/;
 use IO::File;
+use Git qw/command command_oneline command_noisy
+           command_output_pipe command_input_pipe command_close_pipe/;
 
 sub new {
 	my $class = shift;
@@ -3830,10 +3774,8 @@ sub rmdirs {
 	delete $rm->{''}; # we never delete the url we're tracking
 	return unless %$rm;
 
-	defined(my $pid = open my $fh,'-|') or croak $!;
-	if (!$pid) {
-		exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
-	}
+	my ($fh, $ctx) = command_output_pipe(
+	                           qw/ls-tree --name-only -r -z/, $self->{c});
 	local $/ = "\0";
 	while (<$fh>) {
 		chomp;
@@ -3842,11 +3784,11 @@ sub rmdirs {
 			delete $rm->{join '/', @dn};
 		}
 		unless (%$rm) {
-			close $fh;
+			eval { command_close_pipe($fh) };
 			return;
 		}
 	}
-	close $fh;
+	command_close_pipe($fh, $ctx);
 
 	my ($r, $p, $bat) = ($self->{r}, $self->{pool}, $self->{bat});
 	foreach my $d (sort { $b =~ tr#/#/# <=> $a =~ tr#/#/# } keys %$rm) {

From a990999e0df3d0518a2fef60feb1ec269e36ada6 Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Thu, 14 Dec 2006 15:25:23 +0000
Subject: [PATCH 186/330] git-status always says what branch it's on

If the current branch was "master" then git-status wouldn't say

 # On branch XXXX

In its output.  This patch makes it so that this message is always
output; regardless of branch name.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 wt-status.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/wt-status.c b/wt-status.c
index c388ce1293..cface6ca88 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -271,7 +271,7 @@ static void wt_status_print_verbose(struct wt_status *s)
 
 void wt_status_print(struct wt_status *s)
 {
-	if (s->branch && strcmp(s->branch, "refs/heads/master"))
+	if (s->branch)
 		color_printf_ln(color(WT_STATUS_HEADER),
 			"# On branch %s", s->branch);
 

From a2e88b35808a8c0334f169c9cbb2301764fb9e5a Mon Sep 17 00:00:00 2001
From: Quy Tonthat <qtonthat@gmail.com>
Date: Fri, 15 Dec 2006 01:17:53 +1100
Subject: [PATCH 187/330] git-clone documentation

When --use-separate-remote is used on git-clone, the remote
heads are saved under $GIT_DIR/refs/remotes/origin/, not
"$GIT_DIR/remotes/origin/"

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-clone.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 985043faca..bfddb21fee 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -100,7 +100,7 @@ OPTIONS
 	defined default, typically `/usr/share/git-core/templates`.
 
 --use-separate-remote::
-	Save remotes heads under `$GIT_DIR/remotes/origin/` instead
+	Save remotes heads under `$GIT_DIR/refs/remotes/origin/` instead
 	of `$GIT_DIR/refs/heads/`.  Only the local master branch is
 	saved in the latter. This is the default.
 

From 9abaa7f033283f84ba8372192f9d4a165fb09ce4 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sat, 16 Dec 2006 12:22:18 +0100
Subject: [PATCH 188/330] Document git-merge-file

Most of this is derived from the documentation of RCS merge.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-merge-file.txt | 92 ++++++++++++++++++++++++++++++++
 Documentation/git.txt            |  3 ++
 2 files changed, 95 insertions(+)
 create mode 100644 Documentation/git-merge-file.txt

diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
new file mode 100644
index 0000000000..0b41d66a70
--- /dev/null
+++ b/Documentation/git-merge-file.txt
@@ -0,0 +1,92 @@
+git-merge-file(1)
+============
+
+NAME
+----
+git-merge-file - threeway file merge
+
+
+SYNOPSIS
+--------
+[verse]
+'git-merge-file' [-L <current-name> [-L <base-name> [-L <other-name>]]]
+	[-p|--stdout] [-q|--quiet] <current-file> <base-file> <other-file>
+
+
+DESCRIPTION
+-----------
+git-file-merge incorporates all changes that lead from the `<base-file>`
+to `<other-file>` into `<current-file>`. The result ordinarily goes into
+`<current-file>`. git-merge-file is useful for combining separate changes
+to an original. Suppose `<base-file>` is the original, and both
+`<current-file>` and `<other-file>` are modifications of `<base-file>`.
+Then git-merge-file combines both changes.
+
+A conflict occurs if both `<current-file>` and `<other-file>` have changes
+in a common segment of lines. If a conflict is found, git-merge-file
+normally outputs a warning and brackets the conflict with <<<<<<< and
+>>>>>>> lines. A typical conflict will look like this:
+
+	<<<<<<< A
+	lines in file A
+	=======
+	lines in file B
+	>>>>>>> B
+
+If there are conflicts, the user should edit the result and delete one of
+the alternatives.
+
+The exit value of this program is negative on error, and the number of
+conflicts otherwise. If the merge was clean, the exit value is 0.
+
+git-merge-file is designed to be a minimal clone of RCS merge, that is, it
+implements all of RCS merge's functionality which is needed by
+gitlink:git[1].
+
+
+OPTIONS
+-------
+
+-L <label>::
+	This option may be given up to three times, and
+	specifies labels to be used in place of the
+	corresponding file names in conflict reports. That is,
+	`git-merge-file -L x -L y -L z a b c` generates output that
+	looks like it came from files x, y and z instead of
+	from files a, b and c.
+
+-p::
+	Send results to standard output instead of overwriting
+	`<current-file>`.
+
+-q::
+	Quiet;  do  not  warn about conflicts.
+
+
+EXAMPLES
+--------
+
+git merge-file README.my README README.upstream::
+
+	combines the changes of README.my and README.upstream since README,
+	tries to merge them and writes the result into README.my.
+
+git merge-file -L a -L b -L c tmp/a123 tmp/b234 tmp/c345::
+
+	merges tmp/a123 and tmp/c345 with the base tmp/b234, but uses labels
+	`a` and `c` instead of `tmp/a123` and `tmp/c345`.
+
+
+Author
+------
+Written by Johannes Schindelin <johannes.schindelin@gmx.de>
+
+
+Documentation
+--------------
+Documentation by Johannes Schindelin and the git-list <git@vger.kernel.org>,
+with parts copied from the original documentation of RCS merge.
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6382ef0a02..2a9e97dac1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -351,6 +351,9 @@ gitlink:git-init-db[1]::
 	Creates an empty git object database, or reinitialize an
 	existing one.
 
+gitlink:git-merge-file[1]::
+	Runs a threeway merge.
+
 gitlink:git-merge-index[1]::
 	Runs a merge for files needing merging.
 

From b9c8518722687fae6182162f9a244915ba94db02 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 15 Dec 2006 23:58:07 -0800
Subject: [PATCH 189/330] git-svn: remove support for the svn command-line
 client

Using the command-line client was great for prototyping and
getting something working quickly.  Eventually I found time
to study the library documentation and add support for using
the libraries which are much faster and more flexible when
it comes to supporting new features.

Note that we require version 1.1 of the SVN libraries, whereas
we supported the command-line svn client down to version 1.0.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl                           | 883 ++-----------------------
 t/Makefile                             |  10 +-
 t/lib-git-svn.sh                       |  15 +-
 t/t9100-git-svn-basic.sh               |   4 +-
 t/t9104-git-svn-follow-parent.sh       |   7 -
 t/t9105-git-svn-commit-diff.sh         |   7 -
 t/t9106-git-svn-commit-diff-clobber.sh |   7 -
 7 files changed, 51 insertions(+), 882 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index f453c9ab98..077e9200be 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -32,17 +32,20 @@ my %SKIP = ( 'svn:wc:ra_dav:version-url' => 1,
 );
 
 sub fatal (@) { print STDERR @_; exit 1 }
-# If SVN:: library support is added, please make the dependencies
-# optional and preserve the capability to use the command-line client.
-# use eval { require SVN::... } to make it lazy load
-# We don't use any modules not in the standard Perl distribution:
+require SVN::Core; # use()-ing this causes segfaults for me... *shrug*
+require SVN::Ra;
+require SVN::Delta;
+if ($SVN::Core::VERSION lt '1.1.0') {
+	fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)\n";
+}
+push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
+push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
+*SVN::Git::Fetcher::process_rm = *process_rm;
 use Carp qw/croak/;
 use IO::File qw//;
 use File::Basename qw/dirname basename/;
 use File::Path qw/mkpath/;
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
-use File::Spec qw//;
-use File::Copy qw/copy/;
 use POSIX qw/strftime/;
 use IPC::Open3;
 use Memoize;
@@ -52,22 +55,7 @@ memoize('revisions_eq');
 memoize('cmt_metadata');
 memoize('get_commit_time');
 
-my ($SVN, $_use_lib);
-
-sub nag_lib {
-	print STDERR <<EOF;
-! Please consider installing the SVN Perl libraries (version 1.1.0 or
-! newer).  You will generally get better performance and fewer bugs,
-! especially if you:
-! 1) have a case-insensitive filesystem
-! 2) replace symlinks with files (and vice-versa) in commits
-
-EOF
-}
-
-$_use_lib = 1 unless $ENV{GIT_SVN_NO_LIB};
-libsvn_load();
-nag_lib() unless $_use_lib;
+my ($SVN);
 
 my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
 my $sha1 = qr/[a-f\d]{40}/;
@@ -84,7 +72,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
 	$_username, $_config_dir, $_no_auth_cache, $_xfer_delta,
 	$_pager, $_color);
 my (@_branch_from, %tree_map, %users, %rusers, %equiv);
-my ($_svn_co_url_revs, $_svn_pg_peg_revs, $_svn_can_do_switch);
+my ($_svn_can_do_switch);
 my @repo_path_split_cache;
 
 my %fc_opts = ( 'no-ignore-externals' => \$_no_ignore_ext,
@@ -193,7 +181,6 @@ usage(1) unless defined $cmd;
 init_vars();
 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|commit-diff)$/;
 $cmd{$cmd}->[0]->(@ARGV);
 exit 0;
@@ -281,32 +268,6 @@ sub rebuild {
 		$newest_rev = $rev if ($rev > $newest_rev);
 	}
 	command_close_pipe($rev_list, $ctx);
-
-	goto out if $_use_lib;
-	if (!chdir $SVN_WC) {
-		svn_cmd_checkout($SVN_URL, $latest, $SVN_WC);
-		chdir $SVN_WC or croak $!;
-	}
-
-	my $pid = fork;
-	defined $pid or croak $!;
-	if ($pid == 0) {
-		my @svn_up = qw(svn up);
-		push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
-		sys(@svn_up,"-r$newest_rev");
-		$ENV{GIT_INDEX_FILE} = $GIT_SVN_INDEX;
-		index_changes();
-		exec('git-write-tree') or croak $!;
-	}
-	waitpid $pid, 0;
-	croak $? if $?;
-out:
-	if ($_upgrade) {
-		print STDERR <<"";
-Keeping deprecated refs/head/$GIT_SVN-HEAD for now.  Please remove it
-when you have upgraded your tools and habits to use refs/remotes/$GIT_SVN
-
-	}
 }
 
 sub init {
@@ -335,69 +296,13 @@ sub init {
 sub fetch {
 	check_upgrade_needed();
 	$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
-	my $ret = $_use_lib ? fetch_lib(@_) : fetch_cmd(@_);
+	my $ret = fetch_lib(@_);
 	if ($ret->{commit} && !verify_ref('refs/heads/master^0')) {
 		command_noisy(qw(update-ref refs/heads/master),$ret->{commit});
 	}
 	return $ret;
 }
 
-sub fetch_cmd {
-	my (@parents) = @_;
-	my @log_args = -d $SVN_WC ? ($SVN_WC) : ($SVN_URL);
-	unless ($_revision) {
-		$_revision = -d $SVN_WC ? 'BASE:HEAD' : '0:HEAD';
-	}
-	push @log_args, "-r$_revision";
-	push @log_args, '--stop-on-copy' unless $_no_stop_copy;
-
-	my $svn_log = svn_log_raw(@log_args);
-
-	my $base = next_log_entry($svn_log) or croak "No base revision!\n";
-	# don't need last_revision from grab_base_rev() because
-	# user could've specified a different revision to skip (they
-	# didn't want to import certain revisions into git for whatever
-	# reason, so trust $base->{revision} instead.
-	my (undef, $last_commit) = svn_grab_base_rev();
-	unless (-d $SVN_WC) {
-		svn_cmd_checkout($SVN_URL,$base->{revision},$SVN_WC);
-		chdir $SVN_WC or croak $!;
-		read_uuid();
-		$last_commit = git_commit($base, @parents);
-		assert_tree($last_commit);
-	} else {
-		chdir $SVN_WC or croak $!;
-		read_uuid();
-		# looks like a user manually cp'd and svn switch'ed
-		unless ($last_commit) {
-			sys(qw/svn revert -R ./);
-			assert_svn_wc_clean($base->{revision});
-			$last_commit = git_commit($base, @parents);
-			assert_tree($last_commit);
-		}
-	}
-	my @svn_up = qw(svn up);
-	push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
-	my $last = $base;
-	while (my $log_msg = next_log_entry($svn_log)) {
-		if ($last->{revision} >= $log_msg->{revision}) {
-			croak "Out of order: last >= current: ",
-				"$last->{revision} >= $log_msg->{revision}\n";
-		}
-		# Revert is needed for cases like:
-		# https://svn.musicpd.org/Jamming/trunk (r166:167), but
-		# I can't seem to reproduce something like that on a test...
-		sys(qw/svn revert -R ./);
-		assert_svn_wc_clean($last->{revision});
-		sys(@svn_up,"-r$log_msg->{revision}");
-		$last_commit = git_commit($log_msg, $last_commit, @parents);
-		$last = $log_msg;
-	}
-	close $svn_log->{fh};
-	$last->{commit} = $last_commit;
-	return $last;
-}
-
 sub fetch_lib {
 	my (@parents) = @_;
 	$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
@@ -498,35 +403,10 @@ sub commit {
 			die "Failed to rev-parse $c\n";
 		}
 	}
-	$_use_lib ? commit_lib(@revs) : commit_cmd(@revs);
+	commit_lib(@revs);
 	print "Done committing ",scalar @revs," revisions to SVN\n";
 }
 
-sub commit_cmd {
-	my (@revs) = @_;
-
-	chdir $SVN_WC or croak "Unable to chdir $SVN_WC: $!\n";
-	my $info = svn_info('.');
-	my $fetched = fetch();
-	if ($info->{Revision} != $fetched->{revision}) {
-		print STDERR "There are new revisions that were fetched ",
-				"and need to be merged (or acknowledged) ",
-				"before committing.\n";
-		exit 1;
-	}
-	$info = svn_info('.');
-	read_uuid($info);
-	my $last = $fetched;
-	foreach my $c (@revs) {
-		my $mods = svn_checkout_tree($last, $c);
-		if (scalar @$mods == 0) {
-			print "Skipping, no changes detected\n";
-			next;
-		}
-		$last = svn_commit_tree($last, $c);
-	}
-}
-
 sub commit_lib {
 	my (@revs) = @_;
 	my ($r_last, $cmt_last) = svn_grab_base_rev();
@@ -649,32 +529,6 @@ sub dcommit {
 
 sub show_ignore {
 	$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
-	$_use_lib ? show_ignore_lib() : show_ignore_cmd();
-}
-
-sub show_ignore_cmd {
-	require File::Find or die $!;
-	if (defined $_revision) {
-		die "-r/--revision option doesn't work unless the Perl SVN ",
-			"libraries are used\n";
-	}
-	chdir $SVN_WC or croak $!;
-	my %ign;
-	File::Find::find({wanted=>sub{if(lstat $_ && -d _ && -d "$_/.svn"){
-		s#^\./##;
-		@{$ign{$_}} = svn_propget_base('svn:ignore', $_);
-		}}, no_chdir=>1},'.');
-
-	print "\n# /\n";
-	foreach (@{$ign{'.'}}) { print '/',$_ if /\S/ }
-	delete $ign{'.'};
-	foreach my $i (sort keys %ign) {
-		print "\n# ",$i,"\n";
-		foreach (@{$ign{$i}}) { print '/',$i,'/',$_ if /\S/ }
-	}
-}
-
-sub show_ignore_lib {
 	my $repo;
 	$SVN ||= libsvn_connect($SVN_URL);
 	my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
@@ -706,11 +560,7 @@ sub graft_branches {
 			}
 		}
 		unless ($_no_graft_copy) {
-			if ($_use_lib) {
-				graft_file_copy_lib($grafts,$l_map,$u);
-			} else {
-				graft_file_copy_cmd($grafts,$l_map,$u);
-			}
+			graft_file_copy_lib($grafts,$l_map,$u);
 		}
 	}
 	graft_tree_joins($grafts);
@@ -838,10 +688,6 @@ sub commit_diff_usage {
 }
 
 sub commit_diff {
-	if (!$_use_lib) {
-		print STDERR "commit-diff must be used with SVN libraries\n";
-		exit 1;
-	}
 	my $ta = shift or commit_diff_usage();
 	my $tb = shift or commit_diff_usage();
 	if (!eval { $SVN_URL = shift || file_to_s("$GIT_SVN_DIR/info/url") }) {
@@ -1042,8 +888,7 @@ sub complete_url_ls_init {
 		}
 		$var = $url . $var;
 	}
-	chomp(my @ls = $_use_lib ? libsvn_ls_fullurl($var)
-				: safe_qx(qw/svn ls --non-interactive/, $var));
+	my @ls = libsvn_ls_fullurl($var);
 	my $old = $GIT_SVN;
 	defined(my $pid = fork) or croak $!;
 	if (!$pid) {
@@ -1156,37 +1001,6 @@ sub graft_tree_joins {
 	});
 }
 
-# this isn't funky-filename safe, but good enough for now...
-sub graft_file_copy_cmd {
-	my ($grafts, $l_map, $u) = @_;
-	my $paths = $l_map->{$u};
-	my $pfx = common_prefix([keys %$paths]);
-	$SVN_URL ||= $u.$pfx;
-	my $pid = open my $fh, '-|';
-	defined $pid or croak $!;
-	unless ($pid) {
-		my @exec = qw/svn log -v/;
-		push @exec, "-r$_revision" if defined $_revision;
-		exec @exec, $u.$pfx or croak $!;
-	}
-	my ($r, $mp) = (undef, undef);
-	while (<$fh>) {
-		chomp;
-		if (/^\-{72}$/) {
-			$mp = $r = undef;
-		} elsif (/^r(\d+) \| /) {
-			$r = $1 unless defined $r;
-		} elsif (/^Changed paths:/) {
-			$mp = 1;
-		} elsif ($mp && m#^   [AR] /(\S.*?) \(from /(\S+?):(\d+)\)$#) {
-			my ($p1, $p0, $r0) = ($1, $2, $3);
-			my $c = find_graft_path_commit($paths, $p1, $r);
-			next unless $c;
-			find_graft_path_parents($grafts, $paths, $c, $p0, $r0);
-		}
-	}
-}
-
 sub graft_file_copy_lib {
 	my ($grafts, $l_map, $u) = @_;
 	my $tree_paths = $l_map->{$u};
@@ -1277,15 +1091,9 @@ sub graft_merge_msg {
 
 sub read_uuid {
 	return if $SVN_UUID;
-	if ($_use_lib) {
-		my $pool = SVN::Pool->new;
-		$SVN_UUID = $SVN->get_uuid($pool);
-		$pool->clear;
-	} else {
-		my $info = shift || svn_info('.');
-		$SVN_UUID = $info->{'Repository UUID'} or
-					croak "Repository UUID unreadable\n";
-	}
+	my $pool = SVN::Pool->new;
+	$SVN_UUID = $SVN->get_uuid($pool);
+	$pool->clear;
 }
 
 sub verify_ref {
@@ -1293,19 +1101,6 @@ sub verify_ref {
 	eval { command_oneline([ 'rev-parse', $ref ], { STDERR => 0 }) };
 }
 
-sub quiet_run {
-	my $pid = fork;
-	defined $pid or croak $!;
-	if (!$pid) {
-		open my $null, '>', '/dev/null' or croak $!;
-		open STDERR, '>&', $null or croak $!;
-		open STDOUT, '>&', $null or croak $!;
-		exec @_ or croak $!;
-	}
-	waitpid $pid, 0;
-	return $?;
-}
-
 sub repo_path_split {
 	my $full_url = shift;
 	$full_url =~ s#/+$##;
@@ -1317,21 +1112,8 @@ sub repo_path_split {
 			return ($u, $full_url);
 		}
 	}
-	if ($_use_lib) {
-		my $tmp = libsvn_connect($full_url);
-		return ($tmp->{repos_root}, $tmp->{svn_path});
-	} else {
-		my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
-		$path =~ s#^/+##;
-		my @paths = split(m#/+#, $path);
-		while (quiet_run(qw/svn ls --non-interactive/, $url)) {
-			my $n = shift @paths || last;
-			$url .= "/$n";
-		}
-		push @repo_path_split_cache, qr/^(\Q$url\E)/;
-		$path = join('/',@paths);
-		return ($url, $path);
-	}
+	my $tmp = libsvn_connect($full_url);
+	return ($tmp->{repos_root}, $tmp->{svn_path});
 }
 
 sub setup_git_svn {
@@ -1347,31 +1129,6 @@ sub setup_git_svn {
 
 }
 
-sub assert_svn_wc_clean {
-	return if $_use_lib;
-	my ($svn_rev) = @_;
-	croak "$svn_rev is not an integer!\n" unless ($svn_rev =~ /^\d+$/);
-	my $lcr = svn_info('.')->{'Last Changed Rev'};
-	if ($svn_rev != $lcr) {
-		print STDERR "Checking for copy-tree ... ";
-		my @diff = grep(/^Index: /,(safe_qx(qw(svn diff),
-						"-r$lcr:$svn_rev")));
-		if (@diff) {
-			croak "Nope!  Expected r$svn_rev, got r$lcr\n";
-		} else {
-			print STDERR "OK!\n";
-		}
-	}
-	my @status = grep(!/^Performing status on external/,(`svn status`));
-	@status = grep(!/^\s*$/,@status);
-	@status = grep(!/^X/,@status) if $_no_ignore_ext;
-	if (scalar @status) {
-		print STDERR "Tree ($SVN_WC) is not clean:\n";
-		print STDERR $_ foreach @status;
-		croak;
-	}
-}
-
 sub get_tree_from_treeish {
 	my ($treeish) = @_;
 	croak "Not a sha1: $treeish\n" unless $treeish =~ /^$sha1$/o;
@@ -1393,28 +1150,8 @@ sub get_tree_from_treeish {
 	return $expected;
 }
 
-sub assert_tree {
-	return if $_use_lib;
-	my ($treeish) = @_;
-	my $expected = get_tree_from_treeish($treeish);
-
-	my $tmpindex = $GIT_SVN_INDEX.'.assert-tmp';
-	if (-e $tmpindex) {
-		unlink $tmpindex or croak $!;
-	}
-	my $old_index = set_index($tmpindex);
-	index_changes(1);
-	my $tree = command_oneline('write-tree');
-	restore_index($old_index);
-	if ($tree ne $expected) {
-		croak "Tree mismatch, Got: $tree, Expected: $expected\n";
-	}
-	unlink $tmpindex;
-}
-
 sub get_diff {
 	my ($from, $treeish) = @_;
-	assert_tree($from);
 	print "diff-tree $from $treeish\n";
 	my @diff_tree = qw(diff-tree -z -r);
 	if ($_cp_similarity) {
@@ -1465,145 +1202,6 @@ sub get_diff {
 	return \@mods;
 }
 
-sub svn_check_prop_executable {
-	my $m = shift;
-	return if -l $m->{file_b};
-	if ($m->{mode_b} =~ /755$/) {
-		chmod((0755 &~ umask),$m->{file_b}) or croak $!;
-		if ($m->{mode_a} !~ /755$/) {
-			sys(qw(svn propset svn:executable 1), $m->{file_b});
-		}
-		-x $m->{file_b} or croak "$m->{file_b} is not executable!\n";
-	} elsif ($m->{mode_b} !~ /755$/ && $m->{mode_a} =~ /755$/) {
-		sys(qw(svn propdel svn:executable), $m->{file_b});
-		chmod((0644 &~ umask),$m->{file_b}) or croak $!;
-		-x $m->{file_b} and croak "$m->{file_b} is executable!\n";
-	}
-}
-
-sub svn_ensure_parent_path {
-	my $dir_b = dirname(shift);
-	svn_ensure_parent_path($dir_b) if ($dir_b ne File::Spec->curdir);
-	mkpath([$dir_b]) unless (-d $dir_b);
-	sys(qw(svn add -N), $dir_b) unless (-d "$dir_b/.svn");
-}
-
-sub precommit_check {
-	my $mods = shift;
-	my (%rm_file, %rmdir_check, %added_check);
-
-	my %o = ( D => 0, R => 1, C => 2, A => 3, M => 3, T => 3 );
-	foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
-		if ($m->{chg} eq 'R') {
-			if (-d $m->{file_b}) {
-				err_dir_to_file("$m->{file_a} => $m->{file_b}");
-			}
-			# dir/$file => dir/file/$file
-			my $dirname = dirname($m->{file_b});
-			while ($dirname ne File::Spec->curdir) {
-				if ($dirname ne $m->{file_a}) {
-					$dirname = dirname($dirname);
-					next;
-				}
-				err_file_to_dir("$m->{file_a} => $m->{file_b}");
-			}
-			# baz/zzz => baz (baz is a file)
-			$dirname = dirname($m->{file_a});
-			while ($dirname ne File::Spec->curdir) {
-				if ($dirname ne $m->{file_b}) {
-					$dirname = dirname($dirname);
-					next;
-				}
-				err_dir_to_file("$m->{file_a} => $m->{file_b}");
-			}
-		}
-		if ($m->{chg} =~ /^(D|R)$/) {
-			my $t = $1 eq 'D' ? 'file_b' : 'file_a';
-			$rm_file{ $m->{$t} } = 1;
-			my $dirname = dirname( $m->{$t} );
-			my $basename = basename( $m->{$t} );
-			$rmdir_check{$dirname}->{$basename} = 1;
-		} elsif ($m->{chg} =~ /^(?:A|C)$/) {
-			if (-d $m->{file_b}) {
-				err_dir_to_file($m->{file_b});
-			}
-			my $dirname = dirname( $m->{file_b} );
-			my $basename = basename( $m->{file_b} );
-			$added_check{$dirname}->{$basename} = 1;
-			while ($dirname ne File::Spec->curdir) {
-				if ($rm_file{$dirname}) {
-					err_file_to_dir($m->{file_b});
-				}
-				$dirname = dirname $dirname;
-			}
-		}
-	}
-	return (\%rmdir_check, \%added_check);
-
-	sub err_dir_to_file {
-		my $file = shift;
-		print STDERR "Node change from directory to file ",
-				"is not supported by Subversion: ",$file,"\n";
-		exit 1;
-	}
-	sub err_file_to_dir {
-		my $file = shift;
-		print STDERR "Node change from file to directory ",
-				"is not supported by Subversion: ",$file,"\n";
-		exit 1;
-	}
-}
-
-
-sub svn_checkout_tree {
-	my ($from, $treeish) = @_;
-	my $mods = get_diff($from->{commit}, $treeish);
-	return $mods unless (scalar @$mods);
-	my ($rm, $add) = precommit_check($mods);
-
-	my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 );
-	foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) {
-		if ($m->{chg} eq 'C') {
-			svn_ensure_parent_path( $m->{file_b} );
-			sys(qw(svn cp),		$m->{file_a}, $m->{file_b});
-			apply_mod_line_blob($m);
-			svn_check_prop_executable($m);
-		} elsif ($m->{chg} eq 'D') {
-			sys(qw(svn rm --force), $m->{file_b});
-		} elsif ($m->{chg} eq 'R') {
-			svn_ensure_parent_path( $m->{file_b} );
-			sys(qw(svn mv --force), $m->{file_a}, $m->{file_b});
-			apply_mod_line_blob($m);
-			svn_check_prop_executable($m);
-		} elsif ($m->{chg} eq 'M') {
-			apply_mod_line_blob($m);
-			svn_check_prop_executable($m);
-		} elsif ($m->{chg} eq 'T') {
-			svn_check_prop_executable($m);
-			apply_mod_line_blob($m);
-			if ($m->{mode_a} =~ /^120/ && $m->{mode_b} !~ /^120/) {
-				sys(qw(svn propdel svn:special), $m->{file_b});
-			} else {
-				sys(qw(svn propset svn:special *),$m->{file_b});
-			}
-		} elsif ($m->{chg} eq 'A') {
-			svn_ensure_parent_path( $m->{file_b} );
-			apply_mod_line_blob($m);
-			sys(qw(svn add), $m->{file_b});
-			svn_check_prop_executable($m);
-		} else {
-			croak "Invalid chg: $m->{chg}\n";
-		}
-	}
-
-	assert_tree($treeish);
-	if ($_rmdir) { # remove empty directories
-		handle_rmdir($rm, $add);
-	}
-	assert_tree($treeish);
-	return $mods;
-}
-
 sub libsvn_checkout_tree {
 	my ($from, $treeish, $ed) = @_;
 	my $mods = get_diff($from, $treeish);
@@ -1621,44 +1219,6 @@ sub libsvn_checkout_tree {
 	return $mods;
 }
 
-# svn ls doesn't work with respect to the current working tree, but what's
-# in the repository.  There's not even an option for it... *sigh*
-# (added files don't show up and removed files remain in the ls listing)
-sub svn_ls_current {
-	my ($dir, $rm, $add) = @_;
-	chomp(my @ls = safe_qx('svn','ls',$dir));
-	my @ret = ();
-	foreach (@ls) {
-		s#/$##; # trailing slashes are evil
-		push @ret, $_ unless $rm->{$dir}->{$_};
-	}
-	if (exists $add->{$dir}) {
-		push @ret, keys %{$add->{$dir}};
-	}
-	return \@ret;
-}
-
-sub handle_rmdir {
-	my ($rm, $add) = @_;
-
-	foreach my $dir (sort {length $b <=> length $a} keys %$rm) {
-		my $ls = svn_ls_current($dir, $rm, $add);
-		next if (scalar @$ls);
-		sys(qw(svn rm --force),$dir);
-
-		my $dn = dirname $dir;
-		$rm->{ $dn }->{ basename $dir } = 1;
-		$ls = svn_ls_current($dn, $rm, $add);
-		while (scalar @$ls == 0 && $dn ne File::Spec->curdir) {
-			sys(qw(svn rm --force),$dn);
-			$dir = basename $dn;
-			$dn = dirname $dn;
-			$rm->{ $dn }->{ $dir } = 1;
-			$ls = svn_ls_current($dn, $rm, $add);
-		}
-	}
-}
-
 sub get_commit_message {
 	my ($commit, $commit_msg) = (@_);
 	my %log_msg = ( msg => '' );
@@ -1704,56 +1264,6 @@ sub set_svn_commit_env {
 	}
 }
 
-sub svn_commit_tree {
-	my ($last, $commit) = @_;
-	my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
-	my $log_msg = get_commit_message($commit, $commit_msg);
-	my ($oneline) = ($log_msg->{msg} =~ /([^\n\r]+)/);
-	print "Committing $commit: $oneline\n";
-
-	set_svn_commit_env();
-	my @ci_output = safe_qx(qw(svn commit -F),$commit_msg);
-	$ENV{LC_ALL} = 'C';
-	unlink $commit_msg;
-	my ($committed) = ($ci_output[$#ci_output] =~ /(\d+)/);
-	if (!defined $committed) {
-		my $out = join("\n",@ci_output);
-		print STDERR "W: Trouble parsing \`svn commit' output:\n\n",
-				$out, "\n\nAssuming English locale...";
-		($committed) = ($out =~ /^Committed revision \d+\./sm);
-		defined $committed or die " FAILED!\n",
-			"Commit output failed to parse committed revision!\n",
-		print STDERR " OK\n";
-	}
-
-	my @svn_up = qw(svn up);
-	push @svn_up, '--ignore-externals' unless $_no_ignore_ext;
-	if ($_optimize_commits && ($committed == ($last->{revision} + 1))) {
-		push @svn_up, "-r$committed";
-		sys(@svn_up);
-		my $info = svn_info('.');
-		my $date = $info->{'Last Changed Date'} or die "Missing date\n";
-		if ($info->{'Last Changed Rev'} != $committed) {
-			croak "$info->{'Last Changed Rev'} != $committed\n"
-		}
-		my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
-					/(\d{4})\-(\d\d)\-(\d\d)\s
-					 (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
-					 or croak "Failed to parse date: $date\n";
-		$log_msg->{date} = "$tz $Y-$m-$d $H:$M:$S";
-		$log_msg->{author} = $info->{'Last Changed Author'};
-		$log_msg->{revision} = $committed;
-		$log_msg->{msg} .= "\n";
-		$log_msg->{parents} = [ $last->{commit} ];
-		$log_msg->{commit} = git_commit($log_msg, $commit);
-		return $log_msg;
-	}
-	# resync immediately
-	push @svn_up, "-r$last->{revision}";
-	sys(@svn_up);
-	return fetch("$committed=$commit");
-}
-
 sub rev_list_raw {
 	my ($fh, $c) = command_output_pipe(qw/rev-list --pretty=raw/, @_);
 	return { fh => $fh, ctx => $c, t => { } };
@@ -1782,177 +1292,6 @@ sub next_rev_list_entry {
 	return ($x != $rl->{t}) ? $x : undef;
 }
 
-# read the entire log into a temporary file (which is removed ASAP)
-# and store the file handle + parser state
-sub svn_log_raw {
-	my (@log_args) = @_;
-	my $log_fh = IO::File->new_tmpfile or croak $!;
-	my $pid = fork;
-	defined $pid or croak $!;
-	if (!$pid) {
-		open STDOUT, '>&', $log_fh or croak $!;
-		exec (qw(svn log), @log_args) or croak $!
-	}
-	waitpid $pid, 0;
-	croak $? if $?;
-	seek $log_fh, 0, 0 or croak $!;
-	return { state => 'sep', fh => $log_fh };
-}
-
-sub next_log_entry {
-	my $log = shift; # retval of svn_log_raw()
-	my $ret = undef;
-	my $fh = $log->{fh};
-
-	while (<$fh>) {
-		chomp;
-		if (/^\-{72}$/) {
-			if ($log->{state} eq 'msg') {
-				if ($ret->{lines}) {
-					$ret->{msg} .= $_."\n";
-					unless(--$ret->{lines}) {
-						$log->{state} = 'sep';
-					}
-				} else {
-					croak "Log parse error at: $_\n",
-						$ret->{revision},
-						"\n";
-				}
-				next;
-			}
-			if ($log->{state} ne 'sep') {
-				croak "Log parse error at: $_\n",
-					"state: $log->{state}\n",
-					$ret->{revision},
-					"\n";
-			}
-			$log->{state} = 'rev';
-
-			# if we have an empty log message, put something there:
-			if ($ret) {
-				$ret->{msg} ||= "\n";
-				delete $ret->{lines};
-				return $ret;
-			}
-			next;
-		}
-		if ($log->{state} eq 'rev' && s/^r(\d+)\s*\|\s*//) {
-			my $rev = $1;
-			my ($author, $date, $lines) = split(/\s*\|\s*/, $_, 3);
-			($lines) = ($lines =~ /(\d+)/);
-			$date = '1970-01-01 00:00:00 +0000'
-				if ($_ignore_nodate && $date eq '(no date)');
-			my ($Y,$m,$d,$H,$M,$S,$tz) = ($date =~
-					/(\d{4})\-(\d\d)\-(\d\d)\s
-					 (\d\d)\:(\d\d)\:(\d\d)\s([\-\+]\d+)/x)
-					 or croak "Failed to parse date: $date\n";
-			$ret = {	revision => $rev,
-					date => "$tz $Y-$m-$d $H:$M:$S",
-					author => $author,
-					lines => $lines,
-					msg => '' };
-			if (defined $_authors && ! defined $users{$author}) {
-				die "Author: $author not defined in ",
-						"$_authors file\n";
-			}
-			$log->{state} = 'msg_start';
-			next;
-		}
-		# skip the first blank line of the message:
-		if ($log->{state} eq 'msg_start' && /^$/) {
-			$log->{state} = 'msg';
-		} elsif ($log->{state} eq 'msg') {
-			if ($ret->{lines}) {
-				$ret->{msg} .= $_."\n";
-				unless (--$ret->{lines}) {
-					$log->{state} = 'sep';
-				}
-			} else {
-				croak "Log parse error at: $_\n",
-					$ret->{revision},"\n";
-			}
-		}
-	}
-	return $ret;
-}
-
-sub svn_info {
-	my $url = shift || $SVN_URL;
-
-	my $pid = open my $info_fh, '-|';
-	defined $pid or croak $!;
-
-	if ($pid == 0) {
-		exec(qw(svn info),$url) or croak $!;
-	}
-
-	my $ret = {};
-	# only single-lines seem to exist in svn info output
-	while (<$info_fh>) {
-		chomp $_;
-		if (m#^([^:]+)\s*:\s*(\S.*)$#) {
-			$ret->{$1} = $2;
-			push @{$ret->{-order}}, $1;
-		}
-	}
-	close $info_fh or croak $?;
-	return $ret;
-}
-
-sub sys { system(@_) == 0 or croak $? }
-
-sub do_update_index {
-	my ($z_cmd, $cmd, $no_text_base) = @_;
-
-	my ($p, $pctx) = command_output_pipe(@$z_cmd);
-
-	my ($ui, $uctx) = command_input_pipe('update-index',
-	                                     "--$cmd",'-z','--stdin');
-	local $/ = "\0";
-	while (my $x = <$p>) {
-		chomp $x;
-		if (!$no_text_base && lstat $x && ! -l _ &&
-				svn_propget_base('svn:keywords', $x)) {
-			my $mode = -x _ ? 0755 : 0644;
-			my ($v,$d,$f) = File::Spec->splitpath($x);
-			my $tb = File::Spec->catfile($d, '.svn', 'tmp',
-						'text-base',"$f.svn-base");
-			$tb =~ s#^/##;
-			unless (-f $tb) {
-				$tb = File::Spec->catfile($d, '.svn',
-						'text-base',"$f.svn-base");
-				$tb =~ s#^/##;
-			}
-			my @s = stat($x);
-			unlink $x or croak $!;
-			copy($tb, $x);
-			chmod(($mode &~ umask), $x) or croak $!;
-			utime $s[8], $s[9], $x;
-		}
-		print $ui $x,"\0";
-	}
-	command_close_pipe($p, $pctx);
-	command_close_pipe($ui, $uctx);
-}
-
-sub index_changes {
-	return if $_use_lib;
-
-	if (!-f "$GIT_SVN_DIR/info/exclude") {
-		open my $fd, '>>', "$GIT_SVN_DIR/info/exclude" or croak $!;
-		print $fd '.svn',"\n";
-		close $fd or croak $!;
-	}
-	my $no_text_base = shift;
-	do_update_index([qw/diff-files --name-only -z/],
-			'remove',
-			$no_text_base);
-	do_update_index([qw/ls-files -z --others/,
-				"--exclude-from=$GIT_SVN_DIR/info/exclude"],
-			'add',
-			$no_text_base);
-}
-
 sub s_to_file {
 	my ($str, $file, $mode) = @_;
 	open my $fd,'>',$file or croak $!;
@@ -1978,18 +1317,6 @@ sub assert_revision_unknown {
 	}
 }
 
-sub trees_eq {
-	my ($x, $y) = @_;
-	my @x = command(qw/cat-file commit/,$x);
-	my @y = command(qw/cat-file commit/,$y);
-	if (($y[0] ne $x[0]) || $x[0] ne "tree $sha1"
-		             || $y[0] ne "tree $sha1") {
-		print STDERR "Trees not equal: $y[0] != $x[0]\n";
-		return 0
-	}
-	return 1;
-}
-
 sub git_commit {
 	my ($log_msg, @parents) = @_;
 	assert_revision_unknown($log_msg->{revision});
@@ -2014,12 +1341,10 @@ sub git_commit {
 	my $tree = $log_msg->{tree};
 	if (!defined $tree) {
 		my $index = set_index($GIT_SVN_INDEX);
-		index_changes();
 		$tree = command_oneline('write-tree');
 		croak $? if $?;
 		restore_index($index);
 	}
-
 	# just in case we clobber the existing ref, we still want that ref
 	# as our parent:
 	if (my $cur = verify_ref("refs/remotes/$GIT_SVN^0")) {
@@ -2104,102 +1429,6 @@ sub set_commit_env {
 	$ENV{GIT_AUTHOR_DATE} = $ENV{GIT_COMMITTER_DATE} = $log_msg->{date};
 }
 
-sub apply_mod_line_blob {
-	my $m = shift;
-	if ($m->{mode_b} =~ /^120/) {
-		blob_to_symlink($m->{sha1_b}, $m->{file_b});
-	} else {
-		blob_to_file($m->{sha1_b}, $m->{file_b});
-	}
-}
-
-sub blob_to_symlink {
-	my ($blob, $link) = @_;
-	defined $link or croak "\$link not defined!\n";
-	croak "Not a sha1: $blob\n" unless $blob =~ /^$sha1$/o;
-	if (-l $link || -f _) {
-		unlink $link or croak $!;
-	}
-
-	my $dest = `git-cat-file blob $blob`; # no newline, so no chomp
-	symlink $dest, $link or croak $!;
-}
-
-sub blob_to_file {
-	my ($blob, $file) = @_;
-	defined $file or croak "\$file not defined!\n";
-	croak "Not a sha1: $blob\n" unless $blob =~ /^$sha1$/o;
-	if (-l $file || -f _) {
-		unlink $file or croak $!;
-	}
-
-	open my $blob_fh, '>', $file or croak "$!: $file\n";
-	my $pid = fork;
-	defined $pid or croak $!;
-
-	if ($pid == 0) {
-		open STDOUT, '>&', $blob_fh or croak $!;
-		exec('git-cat-file','blob',$blob) or croak $!;
-	}
-	waitpid $pid, 0;
-	croak $? if $?;
-
-	close $blob_fh or croak $!;
-}
-
-sub safe_qx {
-	my $pid = open my $child, '-|';
-	defined $pid or croak $!;
-	if ($pid == 0) {
-		exec(@_) or croak $!;
-	}
-	my @ret = (<$child>);
-	close $child or croak $?;
-	die $? if $?; # just in case close didn't error out
-	return wantarray ? @ret : join('',@ret);
-}
-
-sub svn_compat_check {
-	if ($_follow_parent) {
-		print STDERR 'E: --follow-parent functionality is only ',
-				"available when SVN libraries are used\n";
-		exit 1;
-	}
-	my @co_help = safe_qx(qw(svn co -h));
-	unless (grep /ignore-externals/,@co_help) {
-		print STDERR "W: Installed svn version does not support ",
-				"--ignore-externals\n";
-		$_no_ignore_ext = 1;
-	}
-	if (grep /usage: checkout URL\[\@REV\]/,@co_help) {
-		$_svn_co_url_revs = 1;
-	}
-	if (grep /\[TARGET\[\@REV\]\.\.\.\]/, `svn propget -h`) {
-		$_svn_pg_peg_revs = 1;
-	}
-
-	# I really, really hope nobody hits this...
-	unless (grep /stop-on-copy/, (safe_qx(qw(svn log -h)))) {
-		print STDERR <<'';
-W: The installed svn version does not support the --stop-on-copy flag in
-   the log command.
-   Lets hope the directory you're tracking is not a branch or tag
-   and was never moved within the repository...
-
-		$_no_stop_copy = 1;
-	}
-}
-
-# *sigh*, new versions of svn won't honor -r<rev> without URL@<rev>,
-# (and they won't honor URL@<rev> without -r<rev>, too!)
-sub svn_cmd_checkout {
-	my ($url, $rev, $dir) = @_;
-	my @cmd = ('svn','co', "-r$rev");
-	push @cmd, '--ignore-externals' unless $_no_ignore_ext;
-	$url .= "\@$rev" if $_svn_co_url_revs;
-	sys(@cmd, $url, $dir);
-}
-
 sub check_upgrade_needed {
 	if (!-r $REVDB) {
 		-d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]);
@@ -2277,12 +1506,6 @@ sub rload_authors {
 	close $authors or croak $!;
 }
 
-sub svn_propget_base {
-	my ($p, $f) = @_;
-	$f .= '@BASE' if $_svn_pg_peg_revs;
-	return safe_qx(qw/svn propget/, $p, $f);
-}
-
 sub git_svn_each {
 	my $sub = shift;
 	foreach (command(qw/rev-parse --symbolic --all/)) {
@@ -2682,33 +1905,6 @@ sub show_commit_normal {
 	}
 }
 
-sub libsvn_load {
-	return unless $_use_lib;
-	$_use_lib = eval {
-		require SVN::Core;
-		if ($SVN::Core::VERSION lt '1.1.0') {
-			die "Need SVN::Core 1.1.0 or better ",
-					"(got $SVN::Core::VERSION) ",
-					"Falling back to command-line svn\n";
-		}
-		require SVN::Ra;
-		require SVN::Delta;
-		push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
-		push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
-		*SVN::Git::Fetcher::process_rm = *process_rm;
-		my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
-					$SVN::Node::dir.$SVN::Node::unknown.
-					$SVN::Node::none.$SVN::Node::file.
-					$SVN::Node::dir.$SVN::Node::unknown.
-					$SVN::Auth::SSL::CNMISMATCH.
-					$SVN::Auth::SSL::NOTYETVALID.
-					$SVN::Auth::SSL::EXPIRED.
-					$SVN::Auth::SSL::UNKNOWNCA.
-					$SVN::Auth::SSL::OTHER;
-		1;
-	};
-}
-
 sub _simple_prompt {
 	my ($cred, $realm, $default_username, $may_save, $pool) = @_;
 	$may_save = undef if $_no_auth_cache;
@@ -3231,18 +2427,11 @@ sub revisions_eq {
 	my ($path, $r0, $r1) = @_;
 	return 1 if $r0 == $r1;
 	my $nr = 0;
-	if ($_use_lib) {
-		# should be OK to use Pool here (r1 - r0) should be small
-		my $pool = SVN::Pool->new;
-		libsvn_get_log($SVN, [$path], $r0, $r1,
-				0, 0, 1, sub {$nr++}, $pool);
-		$pool->clear;
-	} else {
-		my ($url, undef) = repo_path_split($SVN_URL);
-		my $svn_log = svn_log_raw("$url/$path","-r$r0:$r1");
-		while (next_log_entry($svn_log)) { $nr++ }
-		close $svn_log->{fh};
-	}
+	# should be OK to use Pool here (r1 - r0) should be small
+	my $pool = SVN::Pool->new;
+	libsvn_get_log($SVN, [$path], $r0, $r1,
+			0, 0, 1, sub {$nr++}, $pool);
+	$pool->clear;
 	return 0 if ($nr > 1);
 	return 1;
 }
@@ -3526,6 +2715,19 @@ sub copy_remote_ref {
 				"refs/remotes/$GIT_SVN on $origin\n";
 	}
 }
+
+{
+	my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
+				$SVN::Node::dir.$SVN::Node::unknown.
+				$SVN::Node::none.$SVN::Node::file.
+				$SVN::Node::dir.$SVN::Node::unknown.
+				$SVN::Auth::SSL::CNMISMATCH.
+				$SVN::Auth::SSL::NOTYETVALID.
+				$SVN::Auth::SSL::EXPIRED.
+				$SVN::Auth::SSL::UNKNOWNCA.
+				$SVN::Auth::SSL::OTHER;
+}
+
 package SVN::Git::Fetcher;
 use vars qw/@ISA/;
 use strict;
@@ -3963,13 +3165,7 @@ __END__
 
 Data structures:
 
-$svn_log hashref (as returned by svn_log_raw)
-{
-	fh => file handle of the log file,
-	state => state of the log file parser (sep/msg/rev/msg_start...)
-}
-
-$log_msg hashref as returned by next_log_entry($svn_log)
+$log_msg hashref as returned by libsvn_log_entry()
 {
 	msg => 'whitespace-formatted log entry
 ',						# trailing newline is preserved
@@ -3978,7 +3174,6 @@ $log_msg hashref as returned by next_log_entry($svn_log)
 	author => 'committer name'
 };
 
-
 @mods = array of diff-index line hashes, each element represents one line
 	of diff-index output
 
diff --git a/t/Makefile b/t/Makefile
index c9bd9a4690..250a19019c 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -23,13 +23,9 @@ clean:
 
 # we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
 full-svn-test:
-	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_DELTA_FETCH=1 \
-					GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-							LC_ALL=en_US.UTF-8
-	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-							LC_ALL=en_US.UTF-8
+	$(MAKE) $(TSVN) GIT_SVN_DELTA_FETCH=1 \
+				GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
 .PHONY: $(T) clean
 .NOTPARALLEL:
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 63c670304f..99ada71349 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -7,17 +7,18 @@ then
 	exit
 fi
 
+perl -e 'use SVN::Core; $SVN::Core::VERSION gt "1.1.0" or die' >/dev/null 2>&1
+if test $? -ne 0
+then
+	test_expect_success 'Perl SVN libraries not found, skipping test' :
+	test_done
+	exit
+fi
+
 GIT_DIR=$PWD/.git
 GIT_SVN_DIR=$GIT_DIR/svn/git-svn
 SVN_TREE=$GIT_SVN_DIR/svn-tree
 
-perl -e 'use SVN::Core' >/dev/null 2>&1
-if test $? -ne 0
-then
-   echo 'Perl SVN libraries not found, tests requiring those will be skipped'
-   GIT_SVN_NO_LIB=1
-fi
-
 svnadmin >/dev/null 2>&1
 if test $? -ne 1
 then
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index f9de232366..0fc462e5fe 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -229,9 +229,7 @@ tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
 tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
 EOF
 
-if test -z "$GIT_SVN_NO_LIB" || test "$GIT_SVN_NO_LIB" -eq 0; then
-	echo tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 >> expected
-fi
+echo tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 >> expected
 
 test_expect_success "$name" "diff -u a expected"
 
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 01488ff78a..8d2e2fec39 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -6,13 +6,6 @@
 test_description='git-svn --follow-parent fetching'
 . ./lib-git-svn.sh
 
-if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0
-then
-	echo 'Skipping: --follow-parent needs SVN libraries'
-	test_done
-	exit 0
-fi
-
 test_expect_success 'initialize repo' "
 	mkdir import &&
 	cd import &&
diff --git a/t/t9105-git-svn-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
index 746c8277d0..6323c7e3ac 100755
--- a/t/t9105-git-svn-commit-diff.sh
+++ b/t/t9105-git-svn-commit-diff.sh
@@ -4,13 +4,6 @@
 test_description='git-svn commit-diff'
 . ./lib-git-svn.sh
 
-if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0
-then
-	echo 'Skipping: commit-diff needs SVN libraries'
-	test_done
-	exit 0
-fi
-
 test_expect_success 'initialize repo' "
 	mkdir import &&
 	cd import &&
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index 58698b3f29..59b6425ce4 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -4,13 +4,6 @@
 test_description='git-svn commit-diff clobber'
 . ./lib-git-svn.sh
 
-if test -n "$GIT_SVN_NO_LIB" && test "$GIT_SVN_NO_LIB" -ne 0
-then
-	echo 'Skipping: commit-diff clobber needs SVN libraries'
-	test_done
-	exit 0
-fi
-
 test_expect_success 'initialize repo' "
 	mkdir import &&
 	cd import &&

From 3289e86e1eb4f38b5c8dfd2f44b4486d2755d6d6 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 15 Dec 2006 23:58:08 -0800
Subject: [PATCH 190/330] git-svn: rename 'commit' command to 'set-tree'

'set-tree' probably accurately describes what the command
formerly known as 'commit' does.

I'm not entirely sure that 'dcommit' should be renamed to 'commit'
just yet...  Perhaps 'push' or 'push-changes'?

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-svn.txt     | 20 ++++++++++----------
 git-svn.perl                  | 12 ++++++------
 t/t9100-git-svn-basic.sh      | 22 +++++++++++-----------
 t/t9101-git-svn-props.sh      |  2 +-
 t/t9102-git-svn-deep-rmdir.sh |  2 +-
 5 files changed, 29 insertions(+), 29 deletions(-)

diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index c589a98630..f5f57e8f87 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -64,7 +64,7 @@ manually joining branches on commit.
 	against the latest changes in the SVN repository.
 	An optional command-line argument may be specified as an
 	alternative to HEAD.
-	This is advantageous over 'commit' (below) because it produces
+	This is advantageous over 'set-tree' (below) because it produces
 	cleaner, more linear history.
 
 'log'::
@@ -89,7 +89,7 @@ manually joining branches on commit.
 
 	Any other arguments are passed directly to `git log'
 
-'commit'::
+'set-tree'::
 	You should consider using 'dcommit' instead of this command.
 	Commit specified commit or tree objects to SVN.  This relies on
 	your imported fetch data being up-to-date.  This makes
@@ -172,7 +172,7 @@ This can allow you to make partial mirrors when running fetch.
 -::
 --stdin::
 
-Only used with the 'commit' command.
+Only used with the 'set-tree' command.
 
 Read a list of commits from stdin and commit them in reverse
 order.  Only the leading sha1 is read from each line, so
@@ -180,7 +180,7 @@ git-rev-list --pretty=oneline output can be used.
 
 --rmdir::
 
-Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
+Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
 
 Remove directories from the SVN tree if there are no files left
 behind.  SVN can version empty directories, and they are not
@@ -193,7 +193,7 @@ repo-config key: svn.rmdir
 -e::
 --edit::
 
-Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
+Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
 
 Edit the commit message before committing to SVN.  This is off by
 default for objects that are commits, and forced on when committing
@@ -204,7 +204,7 @@ repo-config key: svn.edit
 -l<num>::
 --find-copies-harder::
 
-Only used with the 'dcommit', 'commit' and 'commit-diff' commands.
+Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
 
 They are both passed directly to git-diff-tree see
 gitlink:git-diff-tree[1] for more information.
@@ -276,7 +276,7 @@ ADVANCED OPTIONS
 
 -b<refname>::
 --branch <refname>::
-Used with 'fetch', 'dcommit' or 'commit'.
+Used with 'fetch', 'dcommit' or 'set-tree'.
 
 This can be used to join arbitrary git branches to remotes/git-svn
 on new commits where the tree object is equivalent.
@@ -392,11 +392,11 @@ REBASE VS. PULL
 ---------------
 
 Originally, git-svn recommended that the remotes/git-svn branch be
-pulled from.  This is because the author favored 'git-svn commit B'
-to commit a single head rather than the 'git-svn commit A..B' notation
+pulled from.  This is because the author favored 'git-svn set-tree B'
+to commit a single head rather than the 'git-svn set-tree A..B' notation
 to commit multiple commits.
 
-If you use 'git-svn commit A..B' to commit several diffs and you do not
+If you use 'git-svn set-tree A..B' to commit several diffs and you do not
 have the latest remotes/git-svn merged into my-branch, you should use
 'git rebase' to update your work branch instead of 'git pull'.  'pull'
 can cause non-linear history to be flattened when committing into SVN,
diff --git a/git-svn.perl b/git-svn.perl
index 077e9200be..07748bc3e3 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -107,7 +107,12 @@ my %cmd = (
 	init => [ \&init, "Initialize a repo for tracking" .
 			  " (requires URL argument)",
 			  \%init_opts ],
-	commit => [ \&commit, "Commit git revisions to SVN",
+	dcommit => [ \&dcommit, 'Commit several diffs to merge with upstream',
+			{ 'merge|m|M' => \$_merge,
+			  'strategy|s=s' => \$_strategy,
+			  'dry-run|n' => \$_dry_run,
+			%cmt_opts } ],
+	'set-tree' => [ \&commit, "Set an SVN repository to a git tree-ish",
 			{	'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
 	'show-ignore' => [ \&show_ignore, "Show svn:ignore listings",
 			{ 'revision|r=i' => \$_revision } ],
@@ -150,11 +155,6 @@ my %cmd = (
 			  'file|F=s' => \$_file,
 			  'revision|r=s' => \$_revision,
 			%cmt_opts } ],
-	dcommit => [ \&dcommit, 'Commit several diffs to merge with upstream',
-			{ 'merge|m|M' => \$_merge,
-			  'strategy|s=s' => \$_strategy,
-			  'dry-run|n' => \$_dry_run,
-			%cmt_opts } ],
 );
 
 my $cmd;
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 0fc462e5fe..0edf19e48d 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -56,7 +56,7 @@ git update-index --add --remove dir/a/b/c/d/e/file dir/file file
 git commit -m "$name"
 
 test_expect_success "$name" \
-    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch &&
+    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch &&
      svn up $SVN_TREE &&
      test -d $SVN_TREE/dir && test ! -d $SVN_TREE/dir/a"
 
@@ -70,7 +70,7 @@ git update-index --add dir/file/file
 git commit -m "$name"
 
 test_expect_failure "$name" \
-    'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch' \
+    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch' \
     || true
 
 
@@ -85,7 +85,7 @@ git update-index --add -- bar
 git commit -m "$name"
 
 test_expect_failure "$name" \
-    'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch2' \
+    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch2' \
     || true
 
 
@@ -100,7 +100,7 @@ git-update-index --add bar/zzz/yyy
 git commit -m "$name"
 
 test_expect_failure "$name" \
-    'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch3' \
+    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch3' \
     || true
 
 
@@ -115,7 +115,7 @@ git update-index --add -- dir
 git commit -m "$name"
 
 test_expect_failure "$name" \
-    'git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch4' \
+    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch4' \
     || true
 
 
@@ -127,7 +127,7 @@ git update-index exec.sh
 git commit -m "$name"
 
 test_expect_success "$name" \
-    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
      svn up $SVN_TREE &&
      test ! -x $SVN_TREE/exec.sh"
 
@@ -138,7 +138,7 @@ git update-index exec.sh
 git commit -m "$name"
 
 test_expect_success "$name" \
-    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
      svn up $SVN_TREE &&
      test -x $SVN_TREE/exec.sh"
 
@@ -153,7 +153,7 @@ then
 	git commit -m "$name"
 
 	test_expect_success "$name" \
-	    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
 	     svn up $SVN_TREE &&
 	     test -L $SVN_TREE/exec.sh"
 
@@ -164,7 +164,7 @@ then
 	git commit -m "$name"
 
 	test_expect_success "$name" \
-	    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
 	     svn up $SVN_TREE &&
 	     test -x $SVN_TREE/bar/zzz &&
 	     test -L $SVN_TREE/exec-2.sh"
@@ -177,7 +177,7 @@ then
 	git commit -m "$name"
 
 	test_expect_success "$name" \
-	    "git-svn commit --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
+	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
 	     svn up $SVN_TREE &&
 	     test -f $SVN_TREE/exec-2.sh &&
 	     test ! -L $SVN_TREE/exec-2.sh &&
@@ -192,7 +192,7 @@ then
 	git update-index exec-2.sh
 	git commit -m 'éï∏'
 	export LC_ALL="$GIT_SVN_LC_ALL"
-	test_expect_success "$name" "git-svn commit HEAD"
+	test_expect_success "$name" "git-svn set-tree HEAD"
 	unset LC_ALL
 else
 	echo "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)"
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index a5a235f100..5543b07f16 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -74,7 +74,7 @@ test_expect_success "$name" \
 	'git checkout -b mybranch remotes/git-svn &&
 	echo Hi again >> kw.c &&
 	git commit -a -m "test keywoards ignoring" &&
-	git-svn commit remotes/git-svn..mybranch &&
+	git-svn set-tree remotes/git-svn..mybranch &&
 	git pull . remotes/git-svn'
 
 expect='/* $Id$ */'
diff --git a/t/t9102-git-svn-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
index d693d183c8..572aaedc06 100755
--- a/t/t9102-git-svn-deep-rmdir.sh
+++ b/t/t9102-git-svn-deep-rmdir.sh
@@ -21,7 +21,7 @@ test_expect_success 'mirror via git-svn' "
 test_expect_success 'Try a commit on rmdir' "
 	git rm -f deeply/nested/directory/number/2/another &&
 	git commit -a -m 'remove another' &&
-	git-svn commit --rmdir HEAD &&
+	git-svn set-tree --rmdir HEAD &&
 	svn ls -R $svnrepo | grep ^deeply/nested/directory/number/1
 	"
 

From 549ab4a30703012ff3a12b5455d319216805a8db Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Fri, 15 Dec 2006 17:53:45 +0100
Subject: [PATCH 191/330] gitweb: Do not show difftree for merges in "commit"
 view

Do not show difftree against first parent for merges (commits with
more than one parent) in "commit" view, because it usually is
misleading.  git-show and git-whatchanged doesn't show diff for merges
either.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 22 +++++++++++++++-------
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 040ee719f0..ebf35a1e2b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3572,14 +3572,19 @@ sub git_commit {
 	my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
 
 	my $parent = $co{'parent'};
+	my $parents = $co{'parents'};
 	if (!defined $parent) {
 		$parent = "--root";
 	}
-	open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
-		@diff_opts, $parent, $hash, "--"
-		or die_error(undef, "Open git-diff-tree failed");
-	my @difftree = map { chomp; $_ } <$fd>;
-	close $fd or die_error(undef, "Reading git-diff-tree failed");
+	my @difftree;
+	if (@$parents <= 1) {
+		# difftree output is not printed for merges
+		open my $fd, "-|", git_cmd(), "diff-tree", '-r', "--no-commit-id",
+			@diff_opts, $parent, $hash, "--"
+				or die_error(undef, "Open git-diff-tree failed");
+		@difftree = map { chomp; $_ } <$fd>;
+		close $fd or die_error(undef, "Reading git-diff-tree failed");
+	}
 
 	# non-textual hash id's can be cached
 	my $expires;
@@ -3641,7 +3646,7 @@ sub git_commit {
 	}
 	print "</td>" .
 	      "</tr>\n";
-	my $parents = $co{'parents'};
+
 	foreach my $par (@$parents) {
 		print "<tr>" .
 		      "<td>parent</td>" .
@@ -3663,7 +3668,10 @@ sub git_commit {
 	git_print_log($co{'comment'});
 	print "</div>\n";
 
-	git_difftree_body(\@difftree, $hash, $parent);
+	if (@$parents <= 1) {
+		# do not output difftree/whatchanged for merges
+		git_difftree_body(\@difftree, $hash, $parent);
+	}
 
 	git_footer_html();
 }

From 5fce278ef313d3b43a5df5bd0494ad23dc313d22 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Fri, 15 Dec 2006 23:49:12 +0100
Subject: [PATCH 192/330] gitweb: Add title attribute to ref marker with full
 ref name

Add title attribute, which will be shown as popup on mouseover in
graphical web browsers, with full name of ref, including part (type)
removed from the name of ref itself. This is useful to see that this
strange ref is StGIT ref, or it is remote branch, or it is lightweigh
tag (with branch-like name).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index ebf35a1e2b..79e518a912 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -855,7 +855,8 @@ sub format_ref_marker {
 				$name = $ref;
 			}
 
-			$markers .= " <span class=\"$type\">" . esc_html($name) . "</span>";
+			$markers .= " <span class=\"$type\" title=\"$ref\">" .
+			            esc_html($name) . "</span>";
 		}
 	}
 

From c9d193dffbeaf0f106e712879c3ce3d2a4846157 Mon Sep 17 00:00:00 2001
From: Jakub Narebski <jnareb@gmail.com>
Date: Fri, 15 Dec 2006 21:57:16 +0100
Subject: [PATCH 193/330] gitweb: Add "next" link to commit view

Add a kind of "next" view in the bottom part of navigation bar for
"commit" view, similar to what was added for "commitdiff" view in
commit 151602df00b8e5c5b4a8193f59a94b85f9b5aebc
  'gitweb: Add "next" link to commitdiff view'

For "commit" view for single parent commit:
  (parent: _commit_)
For "commit" view for merge (multi-parent) commit:
  (merge: _commit_ _commit_ ...)
For "commit" view for root (parentless) commit
  (initial)
where _link_ denotes hyperlink.  SHA1 of commit is shortened
to 7 characters on display.

While at it, remove leftovers from commit cae1862a by Petr Baudis:
  'gitweb: More per-view navigation bar links'
namely the "blame" link if there exist $file_name and commit has a
parent; it was added in git_commit probably by mistake.  The rest
of what mentioned commit added for git_commit was removed in
commit 6e0e92fda893311ff5af91836e5007bf6bbd4a21 by Luben Tuikov:
  'gitweb: Do not print "log" and "shortlog" redundantly in commit view'
(which should have probably removed also this "blame" link removed now).

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 38 +++++++++++++++++++++++++++++---------
 1 file changed, 29 insertions(+), 9 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 79e518a912..4059894e0b 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3572,8 +3572,34 @@ sub git_commit {
 	my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
 	my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
 
-	my $parent = $co{'parent'};
-	my $parents = $co{'parents'};
+	my $parent  = $co{'parent'};
+	my $parents = $co{'parents'}; # listref
+
+	# we need to prepare $formats_nav before any parameter munging
+	my $formats_nav;
+	if (!defined $parent) {
+		# --root commitdiff
+		$formats_nav .= '(initial)';
+	} elsif (@$parents == 1) {
+		# single parent commit
+		$formats_nav .=
+			'(parent: ' .
+			$cgi->a({-href => href(action=>"commit",
+			                       hash=>$parent)},
+			        esc_html(substr($parent, 0, 7))) .
+			')';
+	} else {
+		# merge commit
+		$formats_nav .=
+			'(merge: ' .
+			join(' ', map {
+				$cgi->a({-href => href(action=>"commitdiff",
+				                       hash=>$_)},
+				        esc_html(substr($_, 0, 7)));
+			} @$parents ) .
+			')';
+	}
+
 	if (!defined $parent) {
 		$parent = "--root";
 	}
@@ -3597,16 +3623,10 @@ sub git_commit {
 
 	my $have_snapshot = gitweb_have_snapshot();
 
-	my @views_nav = ();
-	if (defined $file_name && defined $co{'parent'}) {
-		push @views_nav,
-			$cgi->a({-href => href(action=>"blame", hash_parent=>$parent, file_name=>$file_name)},
-			        "blame");
-	}
 	git_header_html(undef, $expires);
 	git_print_page_nav('commit', '',
 	                   $hash, $co{'tree'}, $hash,
-	                   join (' | ', @views_nav));
+	                   $formats_nav);
 
 	if (defined $co{'parent'}) {
 		git_print_header_div('commitdiff', esc_html($co{'title'}) . $ref, $hash);

From b1bfcae438adb485bb66e2f59396373809e346e6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 12:31:45 -0800
Subject: [PATCH 194/330] merge: give a bit prettier merge message to "merge
 branch~$n"

This hacks the input to fmt-merge-msg to make the message for
merging early part of a branch a little easier to read.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index c895a04f6b..4ebfcf65d9 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -91,6 +91,22 @@ finish () {
 	esac
 }
 
+merge_name () {
+	remote="$1"
+	rh=$(git-rev-parse --verify "$remote^0" 2>/dev/null) || return
+	bh=$(git-show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
+	if test "$rh" = "$bh"
+	then
+		echo "$rh		branch '$remote' of ."
+	elif truname=$(expr "$remote" : '\(.*\)~[1-9][0-9]*$') &&
+		git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
+	then
+		echo "$rh		branch '$truname' (early part) of ."
+	else
+		echo "$rh		commit '$remote'"
+	fi
+}
+
 case "$#" in 0) usage ;; esac
 
 rloga= have_message=
@@ -188,15 +204,7 @@ else
 	# in this loop.
 	merge_name=$(for remote
 		do
-			rh=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
-			continue ;# not something we can merge
-			bh=$(git show-ref -s --verify "refs/heads/$remote" 2>/dev/null)
-			if test "$rh" = "$bh"
-			then
-				echo "$rh		branch '$remote' of ."
-			else
-				echo "$rh		commit '$remote'"
-			fi
+			merge_name "$remote"
 		done | git-fmt-merge-msg
 	)
 	merge_msg="${merge_msg:+$merge_msg$LF$LF}$merge_name"

From 61dde8f91672ab362f3cfd3af8d6d09d448d4ffe Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 01:14:39 -0800
Subject: [PATCH 195/330] git-clone: use wildcard specification for tracking
 branches

This stops enumerating the set of branches found on the remote
side when a clone was made in the configuration file.  Instead,
a single entry that maps each remote branch to the local
tracking branch for the remote under the same name is created.

Doing it this way not only shortens the configuration file, but
automatically adjusts to a new branch added on the remote side
after the clone is made.

Unfortunately this cannot be done for the traditional layout,
where we always need to special case the 'master' to 'origin'
mapping within the local branch namespace.  But that is Ok; it
will be going away before v1.5.0.

We could also lose the "primary branch" mapping at the
beginning, but that has to wait until we implement the "forbid
'git pull' when we do not have branch.$current.merge for the
current branch" policy we earlier discussed.  That should also
be in v1.5.0

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-clone.sh | 47 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 30 insertions(+), 17 deletions(-)

diff --git a/git-clone.sh b/git-clone.sh
index 1f5d07a057..422499a537 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -366,41 +366,54 @@ then
 		)
 	)
 
-	# Write out remotes/$origin file, and update our "$head_points_at".
+	# Write out remote.$origin config, and update our "$head_points_at".
 	case "$head_points_at" in
 	?*)
-		mkdir -p "$GIT_DIR/remotes" &&
+		# Local default branch
 		git-symbolic-ref HEAD "refs/heads/$head_points_at" &&
+
+		# Tracking branch for the primary branch at the remote.
 		case "$use_separate_remote" in
 		t)	origin_track="$remote_top/$head_points_at"
 			git-update-ref HEAD "$head_sha1" ;;
 		*)	origin_track="$remote_top/$origin"
 			git-update-ref "refs/heads/$origin" "$head_sha1" ;;
 		esac &&
+
+		# Upstream URL and the primary branch tracking
 		git-repo-config remote."$origin".url "$repo" &&
 		git-repo-config remote."$origin".fetch \
 			"refs/heads/$head_points_at:$origin_track" &&
-		(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
-		while read dotslref
-		do
-			name=`expr "$dotslref" : './\(.*\)'`
-			if test "z$head_points_at" = "z$name"
-			then
-				continue
-			fi
-			if test "$use_separate_remote" = '' &&
-			   test "z$origin" = "z$name"
-			then
-				continue
-			fi
-			git-repo-config remote."$origin".fetch "refs/heads/${name}:$remote_top/${name}" '^$'
-		done &&
+
+		# Set up the mappings to track the remaining branches.
+		case "$use_separate_remote" in
+		t)
+			git-repo-config remote."$origin".fetch \
+				"refs/heads/*:$remote_top/*" '^$'
+			;;
+		*)
+			(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
+			while read dotslref
+			do
+				name=`expr "$dotslref" : './\(.*\)'`
+				if test "z$head_points_at" = "z$name" ||
+					test "z$origin" = "z$name"
+				then
+					continue
+				fi
+				git-repo-config remote."$origin".fetch \
+				"refs/heads/${name}:$remote_top/${name}" '^$'
+			done
+			;;
+		esac &&
+
 		case "$use_separate_remote" in
 		t)
 			rm -f "refs/remotes/$origin/HEAD"
 			git-symbolic-ref "refs/remotes/$origin/HEAD" \
 				"refs/remotes/$origin/$head_points_at"
 		esac &&
+
 		git-repo-config branch."$head_points_at".remote "$origin" &&
 		git-repo-config branch."$head_points_at".merge "refs/heads/$head_points_at"
 	esac

From a71fb0a1412c82405f078fb536797d3f5de68d53 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 01:36:32 -0800
Subject: [PATCH 196/330] git-pull: refuse default merge without branch.*.merge

Everybody hated the pull behaviour of merging the first branch
listed on remotes/* file (or remote.*.fetch config) into the
current branch.  This finally corrects that UI wart by
forbidding "git pull" without an explicit branch name on the
command line or branch.$current.merge for the current branch.

The matching change to git-clone was made to prepare the default
branch.*.merge entry for the primary branch some time ago.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 6ae534bf89..f27c3c231b 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -144,7 +144,8 @@ canon_refs_list_for_fetch () {
 			curr_branch=$(git-symbolic-ref HEAD | \
 			    sed -e 's|^refs/heads/||')
 			merge_branches=$(git-repo-config \
-			    --get-all "branch.${curr_branch}.merge")
+			    --get-all "branch.${curr_branch}.merge") ||
+			merge_branches=.this.would.never.match.any.ref.
 		fi
 		set x $(expand_refs_wildcard "$@")
 		shift

From 3dd3d5b0e20a74a82a7e684d63d9af86a983fbf6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 01:41:51 -0800
Subject: [PATCH 197/330] git-clone: lose the artificial "first" fetch refspec

Now we lost the "first refspec is the one that is merged by default"
rule, there is no reason for clone to list the remote primary branch
in the config file explicitly anymore.

We still need it for the traditional layout for other reasons,
though.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-clone.sh | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/git-clone.sh b/git-clone.sh
index 422499a537..68dc4f200a 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -380,18 +380,18 @@ then
 			git-update-ref "refs/heads/$origin" "$head_sha1" ;;
 		esac &&
 
-		# Upstream URL and the primary branch tracking
+		# Upstream URL
 		git-repo-config remote."$origin".url "$repo" &&
-		git-repo-config remote."$origin".fetch \
-			"refs/heads/$head_points_at:$origin_track" &&
 
-		# Set up the mappings to track the remaining branches.
+		# Set up the mappings to track the remote branches.
 		case "$use_separate_remote" in
 		t)
 			git-repo-config remote."$origin".fetch \
 				"refs/heads/*:$remote_top/*" '^$'
 			;;
 		*)
+			git-repo-config remote."$origin".fetch \
+				"refs/heads/$head_points_at:$origin_track" &&
 			(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
 			while read dotslref
 			do

From 63085fabbdeaad9eb8b29d39d0998c704e6cd232 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 01:53:10 -0800
Subject: [PATCH 198/330] git-clone: lose the traditional 'no-separate-remote'
 layout

Finally.

The separate-remote layout is so much more organized than
traditional and easier to work with especially when you need to
deal with remote repositories with multiple branches and/or you
need to deal with more than one remote repositories, and using
traditional layout for new repositories simply does not make
much sense.

Internally we still have code for 1:1 mappings to create a bare
clone; that is a good thing and will not go away.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-clone.txt | 15 +---------
 git-clone.sh                | 58 ++++++++-----------------------------
 2 files changed, 13 insertions(+), 60 deletions(-)

diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index bfddb21fee..874934a332 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -11,8 +11,7 @@ SYNOPSIS
 [verse]
 'git-clone' [--template=<template_directory>] [-l [-s]] [-q] [-n] [--bare]
 	  [-o <name>] [-u <upload-pack>] [--reference <repository>]
-	  [--use-separate-remote | --no-separate-remote] <repository>
-	  [<directory>]
+	  <repository> [<directory>]
 
 DESCRIPTION
 -----------
@@ -99,18 +98,6 @@ OPTIONS
 	if unset the templates are taken from the installation
 	defined default, typically `/usr/share/git-core/templates`.
 
---use-separate-remote::
-	Save remotes heads under `$GIT_DIR/refs/remotes/origin/` instead
-	of `$GIT_DIR/refs/heads/`.  Only the local master branch is
-	saved in the latter. This is the default.
-
---no-separate-remote::
-	Save remotes heads in the same namespace as the local
-	heads, `$GIT_DIR/refs/heads/'.  In regular repositories,
-	this is a legacy setup git-clone created by default in
-	older Git versions, and will be removed before the next
-	major release.
-
 <repository>::
 	The (possibly remote) repository to clone from.  It can
 	be any URL git-fetch supports.
diff --git a/git-clone.sh b/git-clone.sh
index 68dc4f200a..490f3e48db 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -14,7 +14,7 @@ die() {
 }
 
 usage() {
-	die "Usage: $0 [--template=<template_directory>] [--no-separate-remote] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
+	die "Usage: $0 [--template=<template_directory>] [--reference <reference-repo>] [--bare] [-l [-s]] [-q] [-u <upload-pack>] [--origin <name>] [-n] <repo> [<dir>]"
 }
 
 get_repo_base() {
@@ -137,11 +137,9 @@ while
 	*,--template=*)
 	  template="$1" ;;
 	*,-q|*,--quiet) quiet=-q ;;
-	*,--use-separate-remote)
-		# default
-		use_separate_remote=t ;;
+	*,--use-separate-remote) ;;
 	*,--no-separate-remote)
-		use_separate_remote= ;;
+		die "clones are always made with separate-remote layout" ;;
 	1,--reference) usage ;;
 	*,--reference)
 		shift; reference="$1" ;;
@@ -327,12 +325,8 @@ cd "$D" || exit
 
 if test -z "$bare" && test -f "$GIT_DIR/REMOTE_HEAD"
 then
-	# Figure out which remote branch HEAD points at.
-	case "$use_separate_remote" in
-	'')	remote_top=refs/heads ;;
-	*)	remote_top="refs/remotes/$origin" ;;
-	esac
-
+	# a non-bare repository is always in separate-remote layout
+	remote_top="refs/remotes/$origin"
 	head_sha1=`cat "$GIT_DIR/REMOTE_HEAD"`
 	case "$head_sha1" in
 	'ref: refs/'*)
@@ -373,46 +367,18 @@ then
 		git-symbolic-ref HEAD "refs/heads/$head_points_at" &&
 
 		# Tracking branch for the primary branch at the remote.
-		case "$use_separate_remote" in
-		t)	origin_track="$remote_top/$head_points_at"
-			git-update-ref HEAD "$head_sha1" ;;
-		*)	origin_track="$remote_top/$origin"
-			git-update-ref "refs/heads/$origin" "$head_sha1" ;;
-		esac &&
+		origin_track="$remote_top/$head_points_at" &&
+		git-update-ref HEAD "$head_sha1" &&
 
 		# Upstream URL
 		git-repo-config remote."$origin".url "$repo" &&
 
 		# Set up the mappings to track the remote branches.
-		case "$use_separate_remote" in
-		t)
-			git-repo-config remote."$origin".fetch \
-				"refs/heads/*:$remote_top/*" '^$'
-			;;
-		*)
-			git-repo-config remote."$origin".fetch \
-				"refs/heads/$head_points_at:$origin_track" &&
-			(cd "$GIT_DIR/$remote_top" && find . -type f -print) |
-			while read dotslref
-			do
-				name=`expr "$dotslref" : './\(.*\)'`
-				if test "z$head_points_at" = "z$name" ||
-					test "z$origin" = "z$name"
-				then
-					continue
-				fi
-				git-repo-config remote."$origin".fetch \
-				"refs/heads/${name}:$remote_top/${name}" '^$'
-			done
-			;;
-		esac &&
-
-		case "$use_separate_remote" in
-		t)
-			rm -f "refs/remotes/$origin/HEAD"
-			git-symbolic-ref "refs/remotes/$origin/HEAD" \
-				"refs/remotes/$origin/$head_points_at"
-		esac &&
+		git-repo-config remote."$origin".fetch \
+			"refs/heads/*:$remote_top/*" '^$' &&
+		rm -f "refs/remotes/$origin/HEAD"
+		git-symbolic-ref "refs/remotes/$origin/HEAD" \
+			"refs/remotes/$origin/$head_points_at" &&
 
 		git-repo-config branch."$head_points_at".remote "$origin" &&
 		git-repo-config branch."$head_points_at".merge "refs/heads/$head_points_at"

From 0667fcfb6271e9e1ea032a0e3a7d74c9d6a5fa8a Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sat, 16 Dec 2006 15:14:14 +0100
Subject: [PATCH 199/330] add a function to rename sections in the config

Given a config like this:

	# A config
	[very.interesting.section]
		not

The command

	$ git repo-config --rename-section very.interesting.section bla.1

will lead to this config:

	# A config
	[bla "1"]
		not

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-repo-config.c  | 14 ++++++++-
 cache.h                |  1 +
 config.c               | 64 ++++++++++++++++++++++++++++++++++++++++++
 t/t1300-repo-config.sh | 48 +++++++++++++++++++++++++++++++
 4 files changed, 126 insertions(+), 1 deletion(-)

diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index 64fbdb7b24..a38099a63d 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -3,7 +3,7 @@
 #include <regex.h>
 
 static const char git_config_set_usage[] =
-"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --list";
+"git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
 
 static char *key;
 static regex_t *key_regexp;
@@ -148,6 +148,18 @@ int cmd_repo_config(int argc, const char **argv, const char *prefix)
 			} else {
 				die("$HOME not set");
 			}
+		} else if (!strcmp(argv[1], "--rename-section")) {
+			int ret;
+			if (argc != 4)
+				usage(git_config_set_usage);
+			ret = git_config_rename_section(argv[2], argv[3]);
+			if (ret < 0)
+				return ret;
+			if (ret == 0) {
+				fprintf(stderr, "No such section!\n");
+				return 1;
+			}
+			return 0;
 		} else
 			break;
 		argc--;
diff --git a/cache.h b/cache.h
index 2d3df98dc4..bfab4f9752 100644
--- a/cache.h
+++ b/cache.h
@@ -404,6 +404,7 @@ extern int git_config_int(const char *, const char *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_set(const char *, const char *);
 extern int git_config_set_multivar(const char *, const char *, const char *, int);
+extern int git_config_rename_section(const char *, const char *);
 extern int check_repository_format_version(const char *var, const char *value);
 
 #define MAX_GITNAME (1000)
diff --git a/config.c b/config.c
index 1bdef44a3a..663993fefa 100644
--- a/config.c
+++ b/config.c
@@ -746,4 +746,68 @@ out_free:
 	return ret;
 }
 
+int git_config_rename_section(const char *old_name, const char *new_name)
+{
+	int ret = 0;
+	const char *config_filename;
+	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
+	int out_fd;
+	char buf[1024];
+
+	config_filename = getenv("GIT_CONFIG");
+	if (!config_filename) {
+		config_filename = getenv("GIT_CONFIG_LOCAL");
+		if (!config_filename)
+			config_filename  = git_path("config");
+	}
+	config_filename = xstrdup(config_filename);
+	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
+	if (out_fd < 0)
+		return error("Could not lock config file!");
+
+	if (!(config_file = fopen(config_filename, "rb")))
+		return error("Could not open config file!");
+
+	while (fgets(buf, sizeof(buf), config_file)) {
+		int i;
+		for (i = 0; buf[i] && isspace(buf[i]); i++)
+			; /* do nothing */
+		if (buf[i] == '[') {
+			/* it's a section */
+			int j = 0, dot = 0;
+			for (i++; buf[i] && buf[i] != ']'; i++) {
+				if (!dot && isspace(buf[i])) {
+					dot = 1;
+					if (old_name[j++] != '.')
+						break;
+					for (i++; isspace(buf[i]); i++)
+						; /* do nothing */
+					if (buf[i] != '"')
+						break;
+					continue;
+				}
+				if (buf[i] == '\\' && dot)
+					i++;
+				else if (buf[i] == '"' && dot) {
+					for (i++; isspace(buf[i]); i++)
+						; /* do_nothing */
+					break;
+				}
+				if (buf[i] != old_name[j++])
+					break;
+			}
+			if (buf[i] == ']') {
+				/* old_name matches */
+				ret++;
+				store.baselen = strlen(new_name);
+				store_write_section(out_fd, new_name);
+				continue;
+			}
+		}
+		write(out_fd, buf, strlen(buf));
+	}
+	if (close(out_fd) || commit_lock_file(lock) < 0)
+		return error("Cannot commit config file!");
+	return ret;
+}
 
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 16cd642610..e48a4ecdcf 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -343,5 +343,53 @@ EOF
 
 test_expect_success '--set in alternative GIT_CONFIG' 'cmp other-config expect'
 
+cat > .git/config << EOF
+# Hallo
+	#Bello
+[branch "eins"]
+	x = 1
+[branch.eins]
+	y = 1
+	[branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename section" \
+	"git-repo-config --rename-section branch.eins branch.zwei"
+
+cat > expect << EOF
+# Hallo
+	#Bello
+[branch "zwei"]
+	x = 1
+[branch "zwei"]
+	y = 1
+	[branch "1 234 blabl/a"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
+test_expect_failure "rename non-existing section" \
+	'git-repo-config --rename-section branch."world domination" branch.drei'
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
+test_expect_success "rename another section" \
+	'git-repo-config --rename-section branch."1 234 blabl/a" branch.drei'
+
+cat > expect << EOF
+# Hallo
+	#Bello
+[branch "zwei"]
+	x = 1
+[branch "zwei"]
+	y = 1
+[branch "drei"]
+weird
+EOF
+
+test_expect_success "rename succeeded" "diff -u expect .git/config"
+
 test_done
 

From dc81c58cd6b791a3db23f1d1acb5f7d38d1ff086 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Sat, 16 Dec 2006 15:15:02 +0100
Subject: [PATCH 200/330] git-branch: rename config vars branch.<branch>.*, too

When renaming a branch, the corresponding config section should
be renamed, too.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c            | 10 ++++++++++
 t/t3200-branch.sh |  6 ++++++
 2 files changed, 16 insertions(+)

diff --git a/refs.c b/refs.c
index a02957c399..d911b9e860 100644
--- a/refs.c
+++ b/refs.c
@@ -867,6 +867,16 @@ int rename_ref(const char *oldref, const char *newref, const char *logmsg)
 		goto rollback;
 	}
 
+	if (!strncmp(oldref, "refs/heads/", 11) &&
+			!strncmp(newref, "refs/heads/", 11)) {
+		char oldsection[1024], newsection[1024];
+
+		snprintf(oldsection, 1024, "branch.%s", oldref + 11);
+		snprintf(newsection, 1024, "branch.%s", newref + 11);
+		if (git_config_rename_section(oldsection, newsection) < 0)
+			return 1;
+	}
+
 	return 0;
 
  rollback:
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 5782c30b03..a6ea0f6a19 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -94,6 +94,8 @@ test_expect_failure \
          git-branch r &&
          git-branch -m q r/q'
 
+git-repo-config branch.s/s.dummy Hello
+
 test_expect_success \
     'git branch -m s/s s should work when s/t is deleted' \
        'git-branch -l s/s &&
@@ -104,6 +106,10 @@ test_expect_success \
         git-branch -m s/s s &&
         test -f .git/logs/refs/heads/s'
 
+test_expect_success 'config information was renamed, too' \
+	"test $(git-repo-config branch.s.dummy) = Hello &&
+	 ! git-repo-config branch.s/s/dummy"
+
 test_expect_failure \
     'git-branch -m u v should fail when the reflog for u is a symlink' \
     'git-branch -l u &&

From 790fa0e297b41b9e8c98a77b58004c58408e5f7a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 16:23:02 -0800
Subject: [PATCH 201/330] update-index: make D/F conflict error a bit more
 verbose.

When you remove a directory D that has a tracked file D/F out of the
way to create a file D and try to "git update-index --add D", it used
to say "cannot add" which was not very helpful.  This issues an extra
error message to explain the situation before the final "fatal" message.

Since D/F conflicts are relatively rare event, extra verbosity would
not make things too noisy.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index eae4745d28..a602010a69 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -609,7 +609,7 @@ int add_cache_entry(struct cache_entry *ce, int option)
 	if (!skip_df_check &&
 	    check_file_directory_conflict(ce, pos, ok_to_replace)) {
 		if (!ok_to_replace)
-			return -1;
+			return error("'%s' appears as both a file and as a directory", ce->name);
 		pos = cache_name_pos(ce->name, ntohs(ce->ce_flags));
 		pos = -pos-1;
 	}

From c33ab0dd100d91d417b5ed3378acbf3310c07cec Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 01:09:41 -0800
Subject: [PATCH 202/330] git-add: remove conflicting entry when adding.

When replacing an existing file A with a directory A that has a
file A/B in it in the index, 'git add' did not succeed because
it forgot to pass the allow-replace flag to add_cache_entry().

It might be safer to leave this as an error and require the user
to explicitly remove the existing A first before adding A/B
since it is an unusual case, but doing that automatically is
much easier to use.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index a602010a69..983e68b892 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -358,7 +358,7 @@ int add_file_to_index(const char *path, int verbose)
 
 	if (index_path(ce->sha1, path, &st, 1))
 		die("unable to index file %s", path);
-	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE))
 		die("unable to add %s to index",path);
 	if (verbose)
 		printf("add '%s'\n", path);

From 81a361be3b12f393d402c5da63191c11c18ea6a9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 17:39:06 -0800
Subject: [PATCH 203/330] Fix check_file_directory_conflict().

When replacing an existing file A with a directory A that has a
file A/B in it in the index, 'update-index --replace --add A/B'
did not properly remove the file to make room for the new
directory.

There was a trivial logic error, most likely a cut & paste one,
dating back to quite early days of git.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 read-cache.c     | 2 +-
 t/t0000-basic.sh | 9 +++++++++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/read-cache.c b/read-cache.c
index 983e68b892..b8d83ccd9f 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -517,7 +517,7 @@ static int has_dir_name(const struct cache_entry *ce, int pos, int ok_to_replace
 		pos = cache_name_pos(name, ntohs(create_ce_flags(len, stage)));
 		if (pos >= 0) {
 			retval = -1;
-			if (ok_to_replace)
+			if (!ok_to_replace)
 				break;
 			remove_cache_entry_at(pos);
 			continue;
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 3260d1d7a7..0cd1c41866 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -272,4 +272,13 @@ test_expect_success \
 	 wc -l) &&
      test $numparent = 1'
 
+test_expect_success 'update-index D/F conflict' '
+	mv path0 tmp &&
+	mv path2 path0 &&
+	mv tmp path2 &&
+	git update-index --add --replace path2 path0/file2 &&
+	numpath0=$(git ls-files path0 | wc -l) &&
+	test $numpath0 = 1
+'
+
 test_done

From a7f196a74650e45ce240754e2caa483752651063 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sun, 17 Dec 2006 03:15:14 -0500
Subject: [PATCH 204/330] Default GIT_COMMITTER_NAME to login name in
 recieve-pack.

If GIT_COMMITTER_NAME is not available in receive-pack but reflogs
are enabled we would normally die out with an error message asking
the user to correct their environment settings.

Now that reflogs are enabled by default in (what we guessed to be)
non-bare Git repositories this may cause problems for some users
who don't have their full name in the gecos field and who don't
have access to the remote system to correct the problem.

So rather than die()'ing out in receive-pack when we try to log a
ref change and have no committer name we default to the username,
as obtained from the host's password database.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 cache.h        |  1 +
 ident.c        | 15 +++++++++++++++
 receive-pack.c |  2 ++
 3 files changed, 18 insertions(+)

diff --git a/cache.h b/cache.h
index 2d3df98dc4..b540292d66 100644
--- a/cache.h
+++ b/cache.h
@@ -309,6 +309,7 @@ void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
 
 extern int setup_ident(void);
+extern void ignore_missing_committer_name();
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
 
diff --git a/ident.c b/ident.c
index e415fd3588..d7faba6a70 100644
--- a/ident.c
+++ b/ident.c
@@ -221,3 +221,18 @@ const char *git_committer_info(int error_on_no_name)
 			 getenv("GIT_COMMITTER_DATE"),
 			 error_on_no_name);
 }
+
+void ignore_missing_committer_name()
+{
+	/* If we did not get a name from the user's gecos entry then
+	 * git_default_name is empty; so instead load the username
+	 * into it as a 'good enough for now' approximation of who
+	 * this user is.
+	 */
+	if (!*git_default_name) {
+		struct passwd *pw = getpwuid(getuid());
+		if (!pw)
+			die("You don't exist. Go away!");
+		strlcpy(git_default_name, pw->pw_name, sizeof(git_default_name));
+	}
+}
diff --git a/receive-pack.c b/receive-pack.c
index e76d9aea31..5e5510bc3d 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -420,6 +420,8 @@ int main(int argc, char **argv)
 		die("'%s': unable to chdir or not a git archive", dir);
 
 	setup_ident();
+	/* don't die if gecos is empty */
+	ignore_missing_committer_name();
 	git_config(receive_pack_config);
 
 	write_head_info();

From 38477d9e6055e3572b465ec3f8e716164111683d Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 00:46:35 -0800
Subject: [PATCH 205/330] Fix mis-mark-up in git-merge-file.txt documentation

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-merge-file.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-merge-file.txt b/Documentation/git-merge-file.txt
index 0b41d66a70..29d3faa556 100644
--- a/Documentation/git-merge-file.txt
+++ b/Documentation/git-merge-file.txt
@@ -1,9 +1,9 @@
 git-merge-file(1)
-============
+=================
 
 NAME
 ----
-git-merge-file - threeway file merge
+git-merge-file - three-way file merge
 
 
 SYNOPSIS

From c0e9232e43446d3bfa7844d36e64e087b8709090 Mon Sep 17 00:00:00 2001
From: Quy Tonthat <qtonthat@gmail.com>
Date: Mon, 18 Dec 2006 01:50:12 +1100
Subject: [PATCH 206/330] Documentation: new option -P for git-svnimport

Documentation: new option -P for git-svnimport.

Signed-off-by: Quy Tonthat <qtonthat@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-svnimport.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index b1b87c2fcd..df604e15ec 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -15,6 +15,7 @@ SYNOPSIS
 		[ -b branch_subdir ] [ -T trunk_subdir ] [ -t tag_subdir ]
 		[ -s start_chg ] [ -m ] [ -r ] [ -M regex ]
 		[ -I <ignorefile_name> ] [ -A <author_file> ]
+		[ -P <path_from_trunk> ]
 		<SVN_repository_URL> [ <path> ]
 
 
@@ -107,6 +108,14 @@ repository without -A.
 	Formerly, this option controlled how many revisions to pull,
 	due to SVN memory leaks. (These have been worked around.)
 
+-P <path_from_trunk>::
+	Partial import of the SVN tree.
+
+	By default, the whole tree on the SVN trunk (/trunk) is imported.
+	'-P my/proj' will import starting only from '/trunk/my/proj'.
+	This option is useful when you want to import one project from a
+	svn repo which hosts multiple projects under the same trunk.
+
 -v::
 	Verbosity: let 'svnimport' report what it is doing.
 

From ee6002aa429272871733b8fbc99a9e6df3e150f5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 10:33:24 -0800
Subject: [PATCH 207/330] markup fix in svnimport documentation.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-svnimport.txt | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt
index df604e15ec..2c7c7dad54 100644
--- a/Documentation/git-svnimport.txt
+++ b/Documentation/git-svnimport.txt
@@ -104,17 +104,17 @@ repository without -A.
 
 -l <max_rev>::
 	Specify a maximum revision number to pull.
-
-	Formerly, this option controlled how many revisions to pull,
-	due to SVN memory leaks. (These have been worked around.)
++
+Formerly, this option controlled how many revisions to pull,
+due to SVN memory leaks. (These have been worked around.)
 
 -P <path_from_trunk>::
 	Partial import of the SVN tree.
-
-	By default, the whole tree on the SVN trunk (/trunk) is imported.
-	'-P my/proj' will import starting only from '/trunk/my/proj'.
-	This option is useful when you want to import one project from a
-	svn repo which hosts multiple projects under the same trunk.
++
+By default, the whole tree on the SVN trunk (/trunk) is imported.
+'-P my/proj' will import starting only from '/trunk/my/proj'.
+This option is useful when you want to import one project from a
+svn repo which hosts multiple projects under the same trunk.
 
 -v::
 	Verbosity: let 'svnimport' report what it is doing.

From 577ed5c20b4ca374626c104f90c88550cf415067 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 22 Oct 2006 17:32:47 -0700
Subject: [PATCH 208/330] rev-list --left-right

The output from "symmetric diff", i.e. A...B, does not
distinguish between commits that are reachable from A and the
ones that are reachable from B.  In this picture, such a
symmetric diff includes commits marked with a and b.

         x---b---b  branch B
        / \ /
       /   .
      /   / \
     o---x---a---a  branch A

However, you cannot tell which ones are 'a' and which ones are
'b' from the output.  Sometimes this is frustrating.  This adds
an output option, --left-right, to rev-list.

        rev-list --left-right A...B

would show ones reachable from A prefixed with '<' and the ones
reachable from B prefixed with '>'.

When combined with --boundary, boundary commits (the ones marked
with 'x' in the above picture) are shown with prefix '-', so you
would see list that looks like this:

    git rev-list --left-right --boundary --pretty=oneline A...B

    >bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 3rd on b
    >bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 2nd on b
    <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 3rd on a
    <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 2nd on a
    -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1st on b
    -xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 1st on a

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-rev-list.c | 11 +++++++++++
 commit.c           | 10 +++++-----
 revision.c         |  5 ++++-
 revision.h         |  1 +
 4 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index fb7fc92145..4364035e15 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -45,6 +45,7 @@ static int bisect_list;
 static int show_timestamp;
 static int hdr_termination;
 static const char *header_prefix;
+static int show_left_right;
 
 static void show_commit(struct commit *commit)
 {
@@ -54,6 +55,12 @@ static void show_commit(struct commit *commit)
 		fputs(header_prefix, stdout);
 	if (commit->object.flags & BOUNDARY)
 		putchar('-');
+	else if (show_left_right) {
+		if (commit->object.flags & SYMMETRIC_LEFT)
+			putchar('<');
+		else
+			putchar('>');
+	}
 	if (revs.abbrev_commit && revs.abbrev)
 		fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
 		      stdout);
@@ -240,6 +247,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 			bisect_list = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--left-right")) {
+			show_left_right = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--stdin")) {
 			if (read_from_stdin++)
 				die("--stdin given twice?");
diff --git a/commit.c b/commit.c
index a6d543eee7..289ef65eb1 100644
--- a/commit.c
+++ b/commit.c
@@ -868,11 +868,11 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
 
 /* merge-rebase stuff */
 
-/* bits #0..7 in revision.h */
-#define PARENT1		(1u<< 8)
-#define PARENT2		(1u<< 9)
-#define STALE		(1u<<10)
-#define RESULT		(1u<<11)
+/* bits #0..15 in revision.h */
+#define PARENT1		(1u<<16)
+#define PARENT2		(1u<<17)
+#define STALE		(1u<<18)
+#define RESULT		(1u<<19)
 
 static struct commit *interesting(struct commit_list *list)
 {
diff --git a/revision.c b/revision.c
index 993bb668a2..3d989c3edf 100644
--- a/revision.c
+++ b/revision.c
@@ -344,6 +344,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
 static void add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
 {
 	struct commit_list *parent = commit->parents;
+	unsigned left_flag;
 
 	if (commit->object.flags & ADDED)
 		return;
@@ -388,6 +389,7 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
 	if (revs->no_walk)
 		return;
 
+	left_flag = (commit->object.flags & SYMMETRIC_LEFT);
 	parent = commit->parents;
 	while (parent) {
 		struct commit *p = parent->item;
@@ -395,6 +397,7 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
 		parent = parent->next;
 
 		parse_commit(p);
+		p->object.flags |= left_flag;
 		if (p->object.flags & SEEN)
 			continue;
 		p->object.flags |= SEEN;
@@ -640,7 +643,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
 				add_pending_commit_list(revs, exclude,
 							flags_exclude);
 				free_commit_list(exclude);
-				a->object.flags |= flags;
+				a->object.flags |= flags | SYMMETRIC_LEFT;
 			} else
 				a->object.flags |= flags_exclude;
 			b->object.flags |= flags;
diff --git a/revision.h b/revision.h
index 3adab9590a..f92a4d4375 100644
--- a/revision.h
+++ b/revision.h
@@ -9,6 +9,7 @@
 #define BOUNDARY	(1u<<5)
 #define BOUNDARY_SHOW	(1u<<6)
 #define ADDED		(1u<<7)	/* Parents already parsed and added? */
+#define SYMMETRIC_LEFT	(1u<<8)
 
 struct rev_info;
 struct log_info;

From 74bd9029732db28b2fff50de0a190229c6033e02 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 15:31:25 -0800
Subject: [PATCH 209/330] Teach all of log family --left-right output.

This makes reviewing

     git log --left-right --merge --no-merges -p

a lot more pleasant.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-rev-list.c |  7 +------
 log-tree.c         | 26 ++++++++++++++++++++++----
 revision.c         |  4 ++++
 revision.h         |  1 +
 4 files changed, 28 insertions(+), 10 deletions(-)

diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 4364035e15..1bb3a06680 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -45,7 +45,6 @@ static int bisect_list;
 static int show_timestamp;
 static int hdr_termination;
 static const char *header_prefix;
-static int show_left_right;
 
 static void show_commit(struct commit *commit)
 {
@@ -55,7 +54,7 @@ static void show_commit(struct commit *commit)
 		fputs(header_prefix, stdout);
 	if (commit->object.flags & BOUNDARY)
 		putchar('-');
-	else if (show_left_right) {
+	else if (revs.left_right) {
 		if (commit->object.flags & SYMMETRIC_LEFT)
 			putchar('<');
 		else
@@ -247,10 +246,6 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 			bisect_list = 1;
 			continue;
 		}
-		if (!strcmp(arg, "--left-right")) {
-			show_left_right = 1;
-			continue;
-		}
 		if (!strcmp(arg, "--stdin")) {
 			if (read_from_stdin++)
 				die("--stdin given twice?");
diff --git a/log-tree.c b/log-tree.c
index 8787df5cc6..35be33aaf7 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -114,6 +114,14 @@ void show_log(struct rev_info *opt, const char *sep)
 
 	opt->loginfo = NULL;
 	if (!opt->verbose_header) {
+		if (opt->left_right) {
+			if (commit->object.flags & BOUNDARY)
+				putchar('-');
+			else if (commit->object.flags & SYMMETRIC_LEFT)
+				putchar('<');
+			else
+				putchar('>');
+		}
 		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
 		if (opt->parents)
 			show_parents(commit, abbrev_commit);
@@ -192,10 +200,20 @@ void show_log(struct rev_info *opt, const char *sep)
 			opt->diffopt.stat_sep = buffer;
 		}
 	} else {
-		printf("%s%s%s",
-		       diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
-		       opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
-		       diff_unique_abbrev(commit->object.sha1, abbrev_commit));
+		fputs(diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
+		      stdout);
+		if (opt->commit_format != CMIT_FMT_ONELINE)
+			fputs("commit ", stdout);
+		if (opt->left_right) {
+			if (commit->object.flags & BOUNDARY)
+				putchar('-');
+			else if (commit->object.flags & SYMMETRIC_LEFT)
+				putchar('<');
+			else
+				putchar('>');
+		}
+		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit),
+		      stdout);
 		if (opt->parents)
 			show_parents(commit, abbrev_commit);
 		if (parent)
diff --git a/revision.c b/revision.c
index 3d989c3edf..d84f46e5a6 100644
--- a/revision.c
+++ b/revision.c
@@ -853,6 +853,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->boundary = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--left-right")) {
+				revs->left_right = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--objects")) {
 				revs->tag_objects = 1;
 				revs->tree_objects = 1;
diff --git a/revision.h b/revision.h
index f92a4d4375..4585463a44 100644
--- a/revision.h
+++ b/revision.h
@@ -41,6 +41,7 @@ struct rev_info {
 			limited:1,
 			unpacked:1, /* see also ignore_packed below */
 			boundary:1,
+			left_right:1,
 			parents:1;
 
 	/* Diff flags */

From 5761231975ceffa531d86d9bab0f9a9a370674f6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sat, 16 Dec 2006 16:07:20 -0800
Subject: [PATCH 210/330] Make left-right automatic.

When using symmetric differences, I think the user almost always
would want to know which side of the symmetry each commit came
from.  So this removes --left-right option from the command
line, and turns it on automatically when a symmetric difference
is used ("git log --merge" counts as a symmetric difference
between HEAD and MERGE_HEAD).

Just in case, a new option --no-left-right is provided to defeat
this, but I do not know if it would be useful.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 revision.c | 13 ++++++++++---
 revision.h |  1 +
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/revision.c b/revision.c
index d84f46e5a6..56819f8220 100644
--- a/revision.c
+++ b/revision.c
@@ -853,8 +853,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->boundary = 1;
 				continue;
 			}
-			if (!strcmp(arg, "--left-right")) {
-				revs->left_right = 1;
+			if (!strcmp(arg, "--no-left-right")) {
+				revs->no_left_right = 1;
 				continue;
 			}
 			if (!strcmp(arg, "--objects")) {
@@ -1055,13 +1055,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 void prepare_revision_walk(struct rev_info *revs)
 {
 	int nr = revs->pending.nr;
+	int has_symmetric = 0;
 	struct object_array_entry *list = revs->pending.objects;
 
 	revs->pending.nr = 0;
 	revs->pending.alloc = 0;
 	revs->pending.objects = NULL;
 	while (--nr >= 0) {
-		struct commit *commit = handle_commit(revs, list->item, list->name);
+		struct commit *commit;
+
+		if (list->item->flags & SYMMETRIC_LEFT)
+			has_symmetric = 1;
+		commit = handle_commit(revs, list->item, list->name);
 		if (commit) {
 			if (!(commit->object.flags & SEEN)) {
 				commit->object.flags |= SEEN;
@@ -1073,6 +1078,8 @@ void prepare_revision_walk(struct rev_info *revs)
 
 	if (revs->no_walk)
 		return;
+	if (!revs->no_left_right && has_symmetric)
+		revs->left_right = 1;
 	if (revs->limited)
 		limit_list(revs);
 	if (revs->topo_order)
diff --git a/revision.h b/revision.h
index 4585463a44..b2ab81488c 100644
--- a/revision.h
+++ b/revision.h
@@ -41,6 +41,7 @@ struct rev_info {
 			limited:1,
 			unpacked:1, /* see also ignore_packed below */
 			boundary:1,
+			no_left_right:1,
 			left_right:1,
 			parents:1;
 

From 7e3fe904ef9b1ce9cb9a3ddc03d85508bcc48df2 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 14 Dec 2006 15:58:56 -0800
Subject: [PATCH 211/330] Teach show-branch how to show ref-log data.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-show-branch.c | 35 ++++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index fb1a4000d9..a38ac34efb 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -6,7 +6,7 @@
 #include "builtin.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n] <branch>";
 
 static int default_num;
 static int default_alloc;
@@ -17,6 +17,8 @@ static const char **default_arg;
 #define REV_SHIFT	 2
 #define MAX_REVS	(FLAG_BITS - REV_SHIFT) /* should not exceed bits_per_int - REV_SHIFT */
 
+#define DEFAULT_REFLOG	4
+
 static struct commit *interesting(struct commit_list *list)
 {
 	while (list) {
@@ -570,6 +572,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 	int head_at = -1;
 	int topics = 0;
 	int dense = 1;
+	int reflog = 0;
 
 	git_config(git_show_branch_config);
 
@@ -615,6 +618,15 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			dense = 0;
 		else if (!strcmp(arg, "--date-order"))
 			lifo = 0;
+		else if (!strcmp(arg, "--reflog")) {
+			reflog = DEFAULT_REFLOG;
+		}
+		else if (!strncmp(arg, "--reflog=", 9)) {
+			char *end;
+			reflog = strtoul(arg + 9, &end, 10);
+			if (*end != '\0')
+				die("unrecognized reflog count '%s'", arg + 9);
+		}
 		else
 			usage(show_branch_usage);
 		ac--; av++;
@@ -622,7 +634,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 	ac--; av++;
 
 	/* Only one of these is allowed */
-	if (1 < independent + merge_base + (extra != 0))
+	if (1 < independent + merge_base + (extra != 0) + (!!reflog))
 		usage(show_branch_usage);
 
 	/* If nothing is specified, show all branches by default */
@@ -631,9 +643,22 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 
 	if (all_heads + all_tags)
 		snarf_refs(all_heads, all_tags);
-	while (0 < ac) {
-		append_one_rev(*av);
-		ac--; av++;
+	if (reflog) {
+		int reflen;
+		if (!ac)
+			die("--reflog option needs one branch name");
+		reflen = strlen(*av);
+		for (i = 0; i < reflog; i++) {
+			char *name = xmalloc(reflen + 20);
+			sprintf(name, "%s@{%d}", *av, i);
+			append_one_rev(name);
+		}
+	}
+	else {
+		while (0 < ac) {
+			append_one_rev(*av);
+			ac--; av++;
+		}
 	}
 
 	head_p = resolve_ref("HEAD", head_sha1, 1, NULL);

From ed9f7c954c26ec6d517bdca3d8e4b895278d1b2b Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 17:57:19 -0800
Subject: [PATCH 212/330] git-fetch: Avoid reading packed refs over and over
 again

When checking which tags to fetch, the old code used to call
git-show-ref --verify for each remote tag. Since reading even
packed refs is not a cheap operation when there are a lot of
local refs, the code became quite slow.

This fixes it by teaching git-show-ref to filter out existing
refs using a new mode of operation of git-show-ref.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-show-ref.c | 60 +++++++++++++++++++++++++++++++++++++++++++++-
 git-fetch.sh       | 12 +++-------
 2 files changed, 62 insertions(+), 10 deletions(-)

diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 073979855b..b36f15eeaa 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -2,8 +2,9 @@
 #include "refs.h"
 #include "object.h"
 #include "tag.h"
+#include "path-list.h"
 
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*]";
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] | --filter-invalid < ref-list";
 
 static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
 	found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
@@ -86,6 +87,59 @@ match:
 	return 0;
 }
 
+static int add_existing(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
+{
+	struct path_list *list = (struct path_list *)cbdata;
+	path_list_insert(refname, list);
+	return 0;
+}
+
+/*
+ * read "^(?:<anything>\s)?<refname>(?:\^\{\})?$" from the standard input,
+ * and
+ * (1) strip "^{}" at the end of line if any;
+ * (2) ignore if match is provided and does not head-match refname;
+ * (3) warn if refname is not a well-formed refname and skip;
+ * (4) ignore if refname is a ref that exists in the local repository;
+ * (5) otherwise output the line.
+ */
+static int exclude_existing(const char *match)
+{
+	static struct path_list existing_refs = { NULL, 0, 0, 0 };
+	char buf[1024];
+	int matchlen = match ? strlen(match) : 0;
+
+	for_each_ref(add_existing, &existing_refs);
+	while (fgets(buf, sizeof(buf), stdin)) {
+		int len = strlen(buf);
+		char *ref;
+		if (len > 0 && buf[len - 1] == '\n')
+			buf[--len] = '\0';
+		if (!strcmp(buf + len - 3, "^{}")) {
+			len -= 3;
+			buf[len] = '\0';
+		}
+		for (ref = buf + len; buf < ref; ref--)
+			if (isspace(ref[-1]))
+				break;
+		if (match) {
+			int reflen = buf + len - ref;
+			if (reflen < matchlen)
+				continue;
+			if (strncmp(ref, match, matchlen))
+				continue;
+		}
+		if (check_ref_format(ref)) {
+			fprintf(stderr, "warning: ref '%s' ignored\n", ref);
+			continue;
+		}
+		if (!path_list_has_path(&existing_refs, ref)) {
+			printf("%s\n", buf);
+		}
+	}
+	return 0;
+}
+
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
 	int i;
@@ -153,6 +207,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 			heads_only = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--exclude-existing"))
+			return exclude_existing(NULL);
+		if (!strncmp(arg, "--exclude-existing=", 19))
+			return exclude_existing(arg + 19);
 		usage(show_ref_usage);
 	}
 	if (show_head)
diff --git a/git-fetch.sh b/git-fetch.sh
index fb35815a5f..38101a6ace 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -242,7 +242,7 @@ esac
 reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
-	taglist=`IFS="	" &&
+	taglist=`IFS='	' &&
 		  echo "$ls_remote_result" |
 	          while read sha1 name
 		  do
@@ -438,17 +438,11 @@ case "$no_tags$tags" in
 	*:refs/*)
 		# effective only when we are following remote branch
 		# using local tracking branch.
-		taglist=$(IFS=" " &&
+		taglist=$(IFS='	' &&
 		echo "$ls_remote_result" |
-		sed -n	-e 's|^\('"$_x40"'\)	\(refs/tags/.*\)^{}$|\1 \2|p' \
-			-e 's|^\('"$_x40"'\)	\(refs/tags/.*\)$|\1 \2|p' |
+		git-show-ref --exclude-existing=refs/tags/ |
 		while read sha1 name
 		do
-			git-show-ref --verify --quiet -- "$name" && continue
-			git-check-ref-format "$name" || {
-				echo >&2 "warning: tag ${name} ignored"
-				continue
-			}
 			git-cat-file -t "$sha1" >/dev/null 2>&1 || continue
 			echo >&2 "Auto-following $name"
 			echo ".${name}:${name}"

From 26cdd1e7c7a21b6d71ebc201fc2472a6a68f5ce4 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 18:08:52 -0800
Subject: [PATCH 213/330] avoid accessing _all_ loose refs in git-show-ref
 --verify

If you want to verify a ref, it is overkill to first read all loose refs
into a linked list, and then check if the desired ref is there.

Signed-off-by: Junio C Hamano <junkio@cox.net>
Acked-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
 builtin-show-ref.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index b36f15eeaa..23e0ff8fbf 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -213,6 +213,23 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 			return exclude_existing(arg + 19);
 		usage(show_ref_usage);
 	}
+
+	if (verify) {
+		unsigned char sha1[20];
+
+		while (*pattern) {
+			if (resolve_ref(*pattern, sha1, 1, NULL))
+				printf("%s %s\n", sha1_to_hex(sha1),
+				       *pattern);
+			else if (!quiet)
+				die("'%s' - not a valid ref", *pattern);
+			else
+				return 1;
+			pattern++;
+		}
+		return 0;
+	}
+
 	if (show_head)
 		head_ref(show_ref, NULL);
 	for_each_ref(show_ref, NULL);

From dd9142993c0954e2fd325431235bc7556189a01c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 18:53:24 -0800
Subject: [PATCH 214/330] show-ref: fix --quiet --verify

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-show-ref.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 23e0ff8fbf..0fd606f392 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -218,9 +218,11 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 		unsigned char sha1[20];
 
 		while (*pattern) {
-			if (resolve_ref(*pattern, sha1, 1, NULL))
-				printf("%s %s\n", sha1_to_hex(sha1),
-				       *pattern);
+			if (resolve_ref(*pattern, sha1, 1, NULL)) {
+				if (!quiet)
+					printf("%s %s\n",
+					       sha1_to_hex(sha1), *pattern);
+			}
 			else if (!quiet)
 				die("'%s' - not a valid ref", *pattern);
 			else

From 64fe031a7a78894955af217335b46059a36a582d Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 19:27:49 -0800
Subject: [PATCH 215/330] show-ref: fix --verify --hash=length

An earlier optimization for --verify broke a lot of stuff
because it did not take interaction with other flags into
account.

This also fixes an unrelated argument parsing error; --hash=8
should mean the same as "--hash --abbrev=8".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-show-ref.c | 25 +++++++++++++++----------
 1 file changed, 15 insertions(+), 10 deletions(-)

diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 0fd606f392..296070628c 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -10,6 +10,15 @@ static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
 	found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
 static const char **pattern;
 
+static void show_one(const char *refname, const unsigned char *sha1)
+{
+	const char *hex = find_unique_abbrev(sha1, abbrev);
+	if (hash_only)
+		printf("%s\n", hex);
+	else
+		printf("%s %s\n", hex, refname);
+}
+
 static int show_ref(const char *refname, const unsigned char *sha1, int flag, void *cbdata)
 {
 	struct object *obj;
@@ -58,11 +67,7 @@ match:
 	if (quiet)
 		return 0;
 
-	hex = find_unique_abbrev(sha1, abbrev);
-	if (hash_only)
-		printf("%s\n", hex);
-	else
-		printf("%s %s\n", hex, refname);
+	show_one(refname, sha1);
 
 	if (!deref_tags)
 		return 0;
@@ -175,13 +180,13 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 		if (!strncmp(arg, "--hash=", 7) ||
 		    (!strncmp(arg, "--abbrev", 8) &&
 		     (arg[8] == '=' || arg[8] == '\0'))) {
-			if (arg[3] != 'h' && !arg[8])
+			if (arg[2] != 'h' && !arg[8])
 				/* --abbrev only */
 				abbrev = DEFAULT_ABBREV;
 			else {
 				/* --hash= or --abbrev= */
 				char *end;
-				if (arg[3] == 'h') {
+				if (arg[2] == 'h') {
 					hash_only = 1;
 					arg += 7;
 				}
@@ -218,10 +223,10 @@ int cmd_show_ref(int argc, const char **argv, const char *prefix)
 		unsigned char sha1[20];
 
 		while (*pattern) {
-			if (resolve_ref(*pattern, sha1, 1, NULL)) {
+			if (!strncmp(*pattern, "refs/", 5) &&
+			    resolve_ref(*pattern, sha1, 1, NULL)) {
 				if (!quiet)
-					printf("%s %s\n",
-					       sha1_to_hex(sha1), *pattern);
+					show_one(*pattern, sha1);
 			}
 			else if (!quiet)
 				die("'%s' - not a valid ref", *pattern);

From ff7f22f36e2931db7683121f20ee2e03bd426096 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Mon, 18 Dec 2006 02:26:42 -0500
Subject: [PATCH 216/330] vim syntax: follow recent changes to commit template

This patch changes the syntax highlighting to correctly match the new
text of the commit message introduced by
  82dca84871637ac9812c0dec27f56d07cfba524c

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/vim/syntax/gitcommit.vim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/contrib/vim/syntax/gitcommit.vim b/contrib/vim/syntax/gitcommit.vim
index a9de09fa2f..d911efbb4b 100644
--- a/contrib/vim/syntax/gitcommit.vim
+++ b/contrib/vim/syntax/gitcommit.vim
@@ -1,7 +1,7 @@
 syn region gitLine start=/^#/ end=/$/
-syn region gitCommit start=/^# Updated but not checked in:$/ end=/^#$/ contains=gitHead,gitCommitFile
+syn region gitCommit start=/^# Added but not yet committed:$/ end=/^#$/ contains=gitHead,gitCommitFile
 syn region gitHead contained start=/^#   (.*)/ end=/^#$/
-syn region gitChanged start=/^# Changed but not updated:/ end=/^#$/ contains=gitHead,gitChangedFile
+syn region gitChanged start=/^# Changed but not added:/ end=/^#$/ contains=gitHead,gitChangedFile
 syn region gitUntracked start=/^# Untracked files:/ end=/^#$/ contains=gitHead,gitUntrackedFile
 
 syn match gitCommitFile contained /^#\t.*/hs=s+2

From 0c7a97fafdaf7911183807019dbbb4d8c5079c4e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 12:13:46 -0800
Subject: [PATCH 217/330] parse-remote::expand_refs_wildcard()

Work around dash incompatibility by not using "${name%'^{}'}".

Noticed by Jeff King; dash seems to mistake the closing brace
inside the single quote as the terminating brace for parameter
expansion.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 6ae534bf89..bc881cc5f9 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -111,16 +111,14 @@ expand_refs_wildcard () {
 		local_force=
 		test "z$lref" = "z$ref" || local_force='+'
 		echo "$ls_remote_result" |
+		sed -e '/\^{}$/d' |
 		(
 			IFS='	'
 			while read sha1 name
 			do
+				# ignore the ones that do not start with $from
 				mapped=${name#"$from"}
-				if test "z$name" != "z${name%'^{}'}" ||
-					test "z$name" = "z$mapped"
-				then
-					continue
-				fi
+				test "z$name" = "z$mapped" && continue
 				echo "${local_force}${name}:${to}${mapped}"
 			done
 		)

From d8285af4810f655e28a0ae0e71a28a920bb6871f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 13:33:47 -0800
Subject: [PATCH 218/330] show-ref: fix --exclude-existing

Do not falsely document --filter-invalid which does not even exist.
Also make sure the line is long enough to have ^{} suffix before
checking for it.

Pointed out by Dscho.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-show-ref.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 296070628c..853f13f6ae 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -4,7 +4,7 @@
 #include "tag.h"
 #include "path-list.h"
 
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] | --filter-invalid < ref-list";
+static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] < ref-list";
 
 static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
 	found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
@@ -116,11 +116,12 @@ static int exclude_existing(const char *match)
 
 	for_each_ref(add_existing, &existing_refs);
 	while (fgets(buf, sizeof(buf), stdin)) {
-		int len = strlen(buf);
 		char *ref;
+		int len = strlen(buf);
+
 		if (len > 0 && buf[len - 1] == '\n')
 			buf[--len] = '\0';
-		if (!strcmp(buf + len - 3, "^{}")) {
+		if (3 <= len && !strcmp(buf + len - 3, "^{}")) {
 			len -= 3;
 			buf[len] = '\0';
 		}

From e1bb1d31ea7b5a2a3f20508e5643e8fc9ee5f0fd Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 14:18:54 -0800
Subject: [PATCH 219/330] racy-git: documentation updates.

We've removed the workaround for runtime penalty that did not
exist in practice some time ago, but the technical paper that
proposed that change still said "we probably should do so".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/technical/racy-git.txt | 38 +++++++++++++++-------------
 1 file changed, 20 insertions(+), 18 deletions(-)

diff --git a/Documentation/technical/racy-git.txt b/Documentation/technical/racy-git.txt
index 7597d04142..5030d9f2f8 100644
--- a/Documentation/technical/racy-git.txt
+++ b/Documentation/technical/racy-git.txt
@@ -4,7 +4,7 @@ Use of index and Racy git problem
 Background
 ----------
 
-The index is one of the most important data structure in git.
+The index is one of the most important data structures in git.
 It represents a virtual working tree state by recording list of
 paths and their object names and serves as a staging area to
 write out the next tree object to be committed.  The state is
@@ -16,7 +16,7 @@ virtual working tree state in the index and the files in the
 working tree.  The most obvious case is when the user asks `git
 diff` (or its low level implementation, `git diff-files`) or
 `git-ls-files --modified`.  In addition, git internally checks
-if the files in the working tree is different from what are
+if the files in the working tree are different from what are
 recorded in the index to avoid stomping on local changes in them
 during patch application, switching branches, and merging.
 
@@ -24,9 +24,9 @@ In order to speed up this comparison between the files in the
 working tree and the index entries, the index entries record the
 information obtained from the filesystem via `lstat(2)` system
 call when they were last updated.  When checking if they differ,
-git first runs `lstat(2)` on the files and compare the result
+git first runs `lstat(2)` on the files and compares the result
 with this information (this is what was originally done by the
-`ce_match_stat()` function, which the current code does in
+`ce_match_stat()` function, but the current code does it in
 `ce_match_stat_basic()` function).  If some of these "cached
 stat information" fields do not match, git can tell that the
 files are modified without even looking at their contents.
@@ -53,8 +53,9 @@ Racy git
 There is one slight problem with the optimization based on the
 cached stat information.  Consider this sequence:
 
+  : modify 'foo'
   $ git update-index 'foo'
-  : modify 'foo' in-place without changing its size
+  : modify 'foo' again, in-place, without changing its size
 
 The first `update-index` computes the object name of the
 contents of file `foo` and updates the index entry for `foo`
@@ -62,7 +63,8 @@ along with the `struct stat` information.  If the modification
 that follows it happens very fast so that the file's `st_mtime`
 timestamp does not change, after this sequence, the cached stat
 information the index entry records still exactly match what you
-can obtain from the filesystem, but the file `foo` is modified.
+would see in the filesystem, even though the file `foo` is now
+different.
 This way, git can incorrectly think files in the working tree
 are unmodified even though they actually are.  This is called
 the "racy git" problem (discovered by Pasky), and the entries
@@ -87,7 +89,7 @@ the stat information from updated paths, `st_mtime` timestamp of
 it is usually the same as or newer than any of the paths the
 index contains.  And no matter how quick the modification that
 follows `git update-index foo` finishes, the resulting
-`st_mtime` timestamp on `foo` cannot get the timestamp earlier
+`st_mtime` timestamp on `foo` cannot get a value earlier
 than the index file.  Therefore, index entries that can be
 racily clean are limited to the ones that have the same
 timestamp as the index file itself.
@@ -111,7 +113,7 @@ value, and falsely clean entry `foo` would not be caught by the
 timestamp comparison check done with the former logic anymore.
 The latter makes sure that the cached stat information for `foo`
 would never match with the file in the working tree, so later
-checks by `ce_match_stat_basic()` would report the index entry
+checks by `ce_match_stat_basic()` would report that the index entry
 does not match the file and git does not have to fall back on more
 expensive `ce_modified_check_fs()`.
 
@@ -155,17 +157,16 @@ of the cached stat information.
 Avoiding runtime penalty
 ------------------------
 
-In order to avoid the above runtime penalty, the recent "master"
-branch (post 1.4.2) has a code that makes sure the index file
-gets timestamp newer than the youngest files in the index when
+In order to avoid the above runtime penalty, post 1.4.2 git used
+to have a code that made sure the index file
+got timestamp newer than the youngest files in the index when
 there are many young files with the same timestamp as the
 resulting index file would otherwise would have by waiting
 before finishing writing the index file out.
 
-I suspect that in practice the situation where many paths in the
-index are all racily clean is quite rare.  The only code paths
-that can record recent timestamp for large number of paths I
-know of are:
+I suspected that in practice the situation where many paths in the
+index are all racily clean was quite rare.  The only code paths
+that can record recent timestamp for large number of paths are:
 
 . Initial `git add .` of a large project.
 
@@ -188,6 +189,7 @@ youngest file in the working tree.  This means that in these
 cases there actually will not be any racily clean entry in
 the resulting index.
 
-So in summary I think we should not worry about avoiding the
-runtime penalty and get rid of the "wait before finishing
-writing" code out.
+Based on this discussion, the current code does not use the
+"workaround" to avoid the runtime penalty that does not exist in
+practice anymore.  This was done with commit 0fc82cff on Aug 15,
+2006.

From 57b73150c4f1dbc26474a0031f433fb34db1c13f Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Mon, 18 Dec 2006 16:06:50 -0500
Subject: [PATCH 220/330] make patch_delta() error cases a bit more verbose

It is especially important to distinguish between a malloc() failure
from all the other cases.  An out of memory condition is much less
worrisome than a compatibility/corruption problem.

Also make test-delta compilable again.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile      |  4 ++--
 patch-delta.c | 13 ++++++-------
 2 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 05cfe45b10..8919dabc78 100644
--- a/Makefile
+++ b/Makefile
@@ -796,8 +796,8 @@ test: all
 test-date$X: test-date.c date.o ctype.o
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o
 
-test-delta$X: test-delta.c diff-delta.o patch-delta.o
-	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^
+test-delta$X: test-delta.o diff-delta.o patch-delta.o $(GITLIBS)
+	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
 test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
diff --git a/patch-delta.c b/patch-delta.c
index e3a1d425ee..ed9db81fa8 100644
--- a/patch-delta.c
+++ b/patch-delta.c
@@ -9,8 +9,7 @@
  * published by the Free Software Foundation.
  */
 
-#include <stdlib.h>
-#include <string.h>
+#include "git-compat-util.h"
 #include "delta.h"
 
 void *patch_delta(const void *src_buf, unsigned long src_size,
@@ -34,9 +33,7 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
 
 	/* now the result size */
 	size = get_delta_hdr_size(&data, top);
-	dst_buf = malloc(size + 1);
-	if (!dst_buf)
-		return NULL;
+	dst_buf = xmalloc(size + 1);
 	dst_buf[size] = 0;
 
 	out = dst_buf;
@@ -55,13 +52,13 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
 			if (cp_off + cp_size < cp_size ||
 			    cp_off + cp_size > src_size ||
 			    cp_size > size)
-				goto bad;
+				break;
 			memcpy(out, (char *) src_buf + cp_off, cp_size);
 			out += cp_size;
 			size -= cp_size;
 		} else if (cmd) {
 			if (cmd > size)
-				goto bad;
+				break;
 			memcpy(out, data, cmd);
 			out += cmd;
 			data += cmd;
@@ -72,12 +69,14 @@ void *patch_delta(const void *src_buf, unsigned long src_size,
 			 * extensions. In the mean time we must fail when
 			 * encountering them (might be data corruption).
 			 */
+			error("unexpected delta opcode 0");
 			goto bad;
 		}
 	}
 
 	/* sanity check */
 	if (data != top || size != 0) {
+		error("delta replay has gone wild");
 		bad:
 		free(dst_buf);
 		return NULL;

From b3d9899324af174ff123498b5c7ded3749aac2bc Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Mon, 18 Dec 2006 15:16:58 -0500
Subject: [PATCH 221/330] make git a bit less cryptic on fetch errors

The remote server might not want to tell why it doesn't like us for
security reasons, but let's make the client report such error in a bit
less confusing way.  The remote failure remains a mystery, but the local
message might be a bit less so.

[jc: with a gentle wording updates from Andy Parkins]

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-fetch.sh     | 2 +-
 git-ls-remote.sh | 2 +-
 pkt-line.c       | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-fetch.sh b/git-fetch.sh
index 38101a6ace..ffbd44f0e1 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -96,7 +96,7 @@ fi
 
 # Global that is reused later
 ls_remote_result=$(git ls-remote $upload_pack "$remote") ||
-	die "Cannot find the reflist at $remote"
+	die "Cannot get the repository state from $remote"
 
 append_fetch_head () {
     head_="$1"
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index 0f88953f29..03b624ef33 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -94,7 +94,7 @@ while read sha1 path
 do
 	case "$sha1" in
 	failed)
-		die "Failed to find remote refs"
+		exit 1 ;;
 	esac
 	case "$path" in
 	refs/heads/*)
diff --git a/pkt-line.c b/pkt-line.c
index c1e81f976f..b4cb7e2756 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -72,7 +72,7 @@ static void safe_read(int fd, void *buffer, unsigned size)
 		if (ret < 0)
 			die("read error (%s)", strerror(errno));
 		if (!ret)
-			die("unexpected EOF");
+			die("The remote end hung up unexpectedly");
 		n += ret;
 	}
 }

From 38c594d3300b7640532a3716adf5a0778021542c Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Mon, 18 Dec 2006 10:48:33 -0500
Subject: [PATCH 222/330] Add documentation for show-branch --topics

Add a quick paragraph explaining the --topics option for show-branch.
The explanation is an abbreviated version of the commit message from
d320a5437f8304cf9ea3ee1898e49d643e005738.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-show-branch.txt | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index a2445a48fc..948ff10e6c 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -10,7 +10,7 @@ SYNOPSIS
 [verse]
 'git-show-branch' [--all] [--heads] [--tags] [--topo-order] [--current]
 		[--more=<n> | --list | --independent | --merge-base]
-		[--no-name | --sha1-name] [<rev> | <glob>]...
+		[--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
 
 DESCRIPTION
 -----------
@@ -86,6 +86,14 @@ OPTIONS
 	of "master"), name them with the unique prefix of their
 	object names.
 
+--topics::
+	Shows only commits that are NOT on the first branch given.
+	This helps track topic branches by hiding any commit that
+	is already in the main line of development.  When given
+	"git show-branch --topics master topic1 topic2", this
+	will show the revisions given by "git rev-list {caret}master
+	topic1 topic2"
+
 Note that --more, --list, --independent and --merge-base options
 are mutually exclusive.
 

From 910c00c8ca4d998edb318cc232c03ec855cfa161 Mon Sep 17 00:00:00 2001
From: "Aneesh Kumar K.V" <aneesh.kumar@gmail.com>
Date: Thu, 7 Dec 2006 12:06:55 +0530
Subject: [PATCH 223/330] Add config example with respect to branch

Update config.txt with example with respect to branch
config variable. This give a better idea regarding
how branch names are expected.

Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/config.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/config.txt b/Documentation/config.txt
index ceac54b024..22482d6a94 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -31,6 +31,11 @@ Example
 		external = "/usr/local/bin/gnu-diff -u"
 		renames = true
 
+	[branch "devel"]
+		remote = origin
+		merge = refs/heads/devel
+
+
 Variables
 ~~~~~~~~~
 

From 8336afa563fbeff35e531396273065161181f04c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 16:20:53 -0800
Subject: [PATCH 224/330] rerere: fix breakage of resolving.

commit e2b70087 botched the RCS merge to git-merge-file conversion.
There is no command called "git merge-file" (yes, we are using safer
variant of Perl's system(3)).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-rerere.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-rerere.perl b/git-rerere.perl
index fdd6854896..4f692091e7 100755
--- a/git-rerere.perl
+++ b/git-rerere.perl
@@ -154,7 +154,7 @@ sub find_conflict {
 sub merge {
 	my ($name, $path) = @_;
 	record_preimage($path, "$rr_dir/$name/thisimage");
-	unless (system('git merge-file', map { "$rr_dir/$name/${_}image" }
+	unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" }
 		       qw(this pre post))) {
 		my $in;
 		open $in, "<$rr_dir/$name/thisimage" or

From 74d20040cafdced657efbf49795183d209a3a07b Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 15:27:43 -0800
Subject: [PATCH 225/330] fix testsuite: make sure they use templates freshly
 built from the source

The initial t/trash repository for testing was created properly
but over time we gained many tests that create secondary test
repositories with init-db or clone and they were not careful
enough.

This fixes it.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t4116-apply-reverse.sh      |  4 ++--
 t/t5300-pack-object.sh        |  6 +++---
 t/t5400-send-pack.sh          |  2 +-
 t/t5500-fetch-pack.sh         |  2 +-
 t/t5510-fetch.sh              |  8 ++++----
 t/t5520-pull.sh               |  2 +-
 t/t5600-clone-fail-cleanup.sh |  6 +++---
 t/t5700-clone-reference.sh    |  4 ++--
 t/t5710-info-alternate.sh     | 14 +++++++-------
 t/t7001-mv.sh                 |  4 ++--
 t/test-lib.sh                 | 10 ++++++++++
 11 files changed, 36 insertions(+), 26 deletions(-)

diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index 74f5c2a575..a79c77af39 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -50,12 +50,12 @@ test_expect_success 'setup separate repository lacking postimage' '
 
 	git tar-tree initial initial | tar xf - &&
 	(
-		cd initial && git init-db && git add .
+		cd initial && git_init_db && git add .
 	) &&
 
 	git tar-tree second second | tar xf - &&
 	(
-		cd second && git init-db && git add .
+		cd second && git_init_db && git add .
 	)
 
 '
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index de45ac4e0f..8a8152b257 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -44,7 +44,7 @@ test_expect_success \
     'unpack without delta' \
     "GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git_init_db &&
      git-unpack-objects -n <test-1-${packname_1}.pack &&
      git-unpack-objects <test-1-${packname_1}.pack"
 
@@ -75,7 +75,7 @@ test_expect_success \
     'unpack with delta' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git_init_db &&
      git-unpack-objects -n <test-2-${packname_2}.pack &&
      git-unpack-objects <test-2-${packname_2}.pack'
 
@@ -100,7 +100,7 @@ test_expect_success \
     'use packed objects' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git-init-db &&
+     git_init_db &&
      cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
 	 git-diff-tree --root -p $commit &&
 	 while read object
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 28744b35e1..901da8c4a9 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -24,7 +24,7 @@ test_expect_success setup '
 	    parent=$commit || return 1
 	done &&
 	git-update-ref HEAD "$commit" &&
-	git-clone -l ./. victim &&
+	git_clone -l ./. victim &&
 	cd victim &&
 	git-log &&
 	cd .. &&
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index f7625a6f46..61e2a55311 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -97,7 +97,7 @@ pull_to_client () {
 (
 	mkdir client &&
 	cd client &&
-	git-init-db 2>> log2.txt
+	git_init_db 2>> log2.txt
 )
 
 add A1
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index a11ab0ad41..e2e8c894f9 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -15,12 +15,12 @@ test_expect_success setup '
 	git commit -a -m original'
 
 test_expect_success "clone and setup child repos" '
-	git clone . one &&
+	git_clone . one &&
 	cd one &&
 	echo >file updated by one &&
 	git commit -a -m "updated by one" &&
 	cd .. &&
-	git clone . two &&
+	git_clone . two &&
 	cd two &&
 	git repo-config branch.master.remote one &&
 	{
@@ -28,7 +28,7 @@ test_expect_success "clone and setup child repos" '
 		echo "Pull: refs/heads/master:refs/heads/one"
 	} >.git/remotes/one
 	cd .. &&
-	git clone . three &&
+	git_clone . three &&
 	cd three &&
 	git repo-config branch.master.remote two &&
 	git repo-config branch.master.merge refs/heads/one &&
@@ -74,7 +74,7 @@ test_expect_success 'fetch following tags' '
 
 	mkdir four &&
 	cd four &&
-	git init-db &&
+	git_init_db &&
 
 	git fetch .. :track &&
 	git show-ref --verify refs/tags/anno &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index f841574573..66ef92f41f 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -17,7 +17,7 @@ test_expect_success setup '
 test_expect_success 'pulling into void' '
 	mkdir cloned &&
 	cd cloned &&
-	git init-db &&
+	git_init_db &&
 	git pull ..
 '
 
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 041be04f5c..1913a12d71 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,7 +13,7 @@ remove the directory before attempting a clone again.'
 
 test_expect_failure \
     'clone of non-existent source should fail' \
-    'git-clone foo bar'
+    'git_clone foo bar'
 
 test_expect_failure \
     'failed clone should not leave a directory' \
@@ -29,11 +29,11 @@ test_create_repo foo
 # current path not to the target dir
 test_expect_failure \
     'clone of non-existent (relative to $PWD) source should fail' \
-    'git-clone ../foo baz'
+    'git_clone ../foo baz'
 
 test_expect_success \
     'clone should work now that source exists' \
-    'git-clone foo bar'
+    'git_clone foo bar'
 
 test_expect_success \
     'successfull clone must leave the directory' \
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index dd9caad1c2..52dab2d484 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -17,7 +17,7 @@ git commit -m initial'
 cd "$base_dir"
 
 test_expect_success 'preparing second repository' \
-'git clone A B && cd B &&
+'git_clone A B && cd B &&
 echo second > file2 &&
 git add file2 &&
 git commit -m addition &&
@@ -27,7 +27,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'cloning with reference' \
-'git clone -l -s --reference B A C'
+'git_clone -l -s --reference B A C'
 
 cd "$base_dir"
 
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index b9f6d96363..3c43554c69 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -34,7 +34,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'preparing second repository' \
-'git clone -l -s A B && cd B &&
+'git_clone -l -s A B && cd B &&
 echo "foo bar" > file2 &&
 git add file2 &&
 git commit -m "next commit" file2 &&
@@ -44,7 +44,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'preparing third repository' \
-'git clone -l -s B C && cd C &&
+'git_clone -l -s B C && cd C &&
 echo "Goodbye, cruel world" > file3 &&
 git add file3 &&
 git commit -m "one more" file3 &&
@@ -54,11 +54,11 @@ git prune'
 cd "$base_dir"
 
 test_expect_failure 'creating too deep nesting' \
-'git clone -l -s C D &&
-git clone -l -s D E &&
-git clone -l -s E F &&
-git clone -l -s F G &&
-git clone -l -s G H &&
+'git_clone -l -s C D &&
+git_clone -l -s D E &&
+git_clone -l -s E F &&
+git_clone -l -s F G &&
+git_clone -l -s G H &&
 cd H &&
 test_valid_repo'
 
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 2f4ff82e14..ae597e80dd 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -88,7 +88,7 @@ test_expect_success \
 
 test_expect_success "Michael Cassar's test case" '
 	rm -fr .git papers partA &&
-	git init-db &&
+	git_init_db &&
 	mkdir -p papers/unsorted papers/all-papers partA &&
 	echo a > papers/unsorted/Thesis.pdf &&
 	echo b > partA/outline.txt &&
@@ -109,7 +109,7 @@ rm -fr papers partA path?
 
 test_expect_success "Sergey Vlasov's test case" '
 	rm -fr .git &&
-	git init-db &&
+	git_init_db &&
 	mkdir ab &&
 	date >ab.c &&
 	date >ab/d &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index ac7be769b4..7e9149751e 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -182,6 +182,16 @@ test_create_repo () {
 	cd "$owd"
 }
 	
+# Many tests do init-db and clone but they must be told about the freshly
+# built templates.
+git_init_db () {
+	git init-db --template="$GIT_EXEC_PATH/templates/blt/" "$@"
+}
+
+git_clone () {
+	git clone --template="$GIT_EXEC_PATH/templates/blt/" "$@"
+}
+
 test_done () {
 	trap - exit
 	case "$test_failure" in

From f3d985c38090b77e46dcc00d42574b9ba49bdc22 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 17 Dec 2006 23:58:16 -0800
Subject: [PATCH 226/330] Teach git-branch to delete tracking branches with -r
 -d

Because -r already means "remote" when listing, you can say:

	$ git branch -d -r origin/todo origin/html origin/man

I just twisted it not to check fast-forwardness with the current
branch when you are removing a tracking branch.  Most likely,
removal of a tracking branch is not because you are "done with"
it (for a local branch, it usually means "you merged it up"),
but because you are not even interested in it.  In other words,
remote tracking branches are more like tags than branches.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c | 41 ++++++++++++++++++++++++++++-------------
 1 file changed, 28 insertions(+), 13 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 560309cb15..7fb93e7796 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -12,8 +12,12 @@
 #include "builtin.h"
 
 static const char builtin_branch_usage[] =
-  "git-branch (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [-r | -a] [-v [--abbrev=<length>]]";
+  "git-branch [-r] (-d | -D) <branchname> | [-l] [-f] <branchname> [<start-point>] | (-m | -M) [<oldbranch>] <newbranch> | [-r | -a] [-v [--abbrev=<length>]]";
 
+#define REF_UNKNOWN_TYPE    0x00
+#define REF_LOCAL_BRANCH    0x01
+#define REF_REMOTE_BRANCH   0x02
+#define REF_TAG             0x04
 
 static const char *head;
 static unsigned char head_sha1[20];
@@ -89,25 +93,40 @@ static int in_merge_bases(const unsigned char *sha1,
 	return ret;
 }
 
-static void delete_branches(int argc, const char **argv, int force)
+static void delete_branches(int argc, const char **argv, int force, int kinds)
 {
 	struct commit *rev, *head_rev = head_rev;
 	unsigned char sha1[20];
 	char *name;
+	const char *fmt, *remote;
 	int i;
 
+	switch (kinds) {
+	case REF_REMOTE_BRANCH:
+		fmt = "refs/remotes/%s";
+		remote = "remote ";
+		force = 1;
+		break;
+	case REF_LOCAL_BRANCH:
+		fmt = "refs/heads/%s";
+		remote = "";
+		break;
+	default:
+		die("cannot use -a with -d");
+	}
+
 	if (!force) {
 		head_rev = lookup_commit_reference(head_sha1);
 		if (!head_rev)
 			die("Couldn't look up commit object for HEAD");
 	}
 	for (i = 0; i < argc; i++) {
-		if (!strcmp(head, argv[i]))
+		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i]))
 			die("Cannot delete the branch you are currently on.");
 
-		name = xstrdup(mkpath("refs/heads/%s", argv[i]));
+		name = xstrdup(mkpath(fmt, argv[i]));
 		if (!resolve_ref(name, sha1, 1, NULL))
-			die("Branch '%s' not found.", argv[i]);
+			die("%sbranch '%s' not found.", remote, argv[i]);
 
 		rev = lookup_commit_reference(sha1);
 		if (!rev)
@@ -128,19 +147,15 @@ static void delete_branches(int argc, const char **argv, int force)
 		}
 
 		if (delete_ref(name, sha1))
-			printf("Error deleting branch '%s'\n", argv[i]);
+			printf("Error deleting %sbranch '%s'\n", remote,
+			       argv[i]);
 		else
-			printf("Deleted branch %s.\n", argv[i]);
+			printf("Deleted %sbranch %s.\n", remote, argv[i]);
 
 		free(name);
 	}
 }
 
-#define REF_UNKNOWN_TYPE    0x00
-#define REF_LOCAL_BRANCH    0x01
-#define REF_REMOTE_BRANCH   0x02
-#define REF_TAG             0x04
-
 struct ref_item {
 	char *name;
 	unsigned int kind;
@@ -435,7 +450,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	head += 11;
 
 	if (delete)
-		delete_branches(argc - i, argv + i, force_delete);
+		delete_branches(argc - i, argv + i, force_delete, kinds);
 	else if (i == argc)
 		print_ref_list(kinds, verbose, abbrev);
 	else if (rename && (i == argc - 1))

From b8e9a00d40839a76928c54366a1c46ad54d7fd5d Mon Sep 17 00:00:00 2001
From: Quy Tonthat <qtonthat@gmail.com>
Date: Tue, 19 Dec 2006 09:42:16 +1100
Subject: [PATCH 227/330] git-branch -d: do not stop at the first failure.

If there are more than one branches to be deleted, failure on
one will no longer stop git-branch to process the next ones.
The command still reports failures by exitting non-zero status.

Signed-off-by: Quy Tonthat <qtonthat@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-branch.c | 57 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 39 insertions(+), 18 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 7fb93e7796..011ef3a265 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -93,13 +93,14 @@ static int in_merge_bases(const unsigned char *sha1,
 	return ret;
 }
 
-static void delete_branches(int argc, const char **argv, int force, int kinds)
+static int delete_branches(int argc, const char **argv, int force, int kinds)
 {
 	struct commit *rev, *head_rev = head_rev;
 	unsigned char sha1[20];
-	char *name;
+	char *name = NULL;
 	const char *fmt, *remote;
 	int i;
+	int ret = 0;
 
 	switch (kinds) {
 	case REF_REMOTE_BRANCH:
@@ -121,16 +122,30 @@ static void delete_branches(int argc, const char **argv, int force, int kinds)
 			die("Couldn't look up commit object for HEAD");
 	}
 	for (i = 0; i < argc; i++) {
-		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i]))
-			die("Cannot delete the branch you are currently on.");
+		if (kinds == REF_LOCAL_BRANCH && !strcmp(head, argv[i])) {
+			error("Cannot delete the branch '%s' "
+				"which you are currently on.", argv[i]);
+			ret = 1;
+			continue;
+		}
+
+		if (name)
+			free(name);
 
 		name = xstrdup(mkpath(fmt, argv[i]));
-		if (!resolve_ref(name, sha1, 1, NULL))
-			die("%sbranch '%s' not found.", remote, argv[i]);
+		if (!resolve_ref(name, sha1, 1, NULL)) {
+			error("%sbranch '%s' not found.",
+					remote, argv[i]);
+			ret = 1;
+			continue;
+		}
 
 		rev = lookup_commit_reference(sha1);
-		if (!rev)
-			die("Couldn't look up commit object for '%s'", name);
+		if (!rev) {
+			error("Couldn't look up commit object for '%s'", name);
+			ret = 1;
+			continue;
+		}
 
 		/* This checks whether the merge bases of branch and
 		 * HEAD contains branch -- which means that the HEAD
@@ -139,21 +154,27 @@ static void delete_branches(int argc, const char **argv, int force, int kinds)
 
 		if (!force &&
 		    !in_merge_bases(sha1, rev, head_rev)) {
-			fprintf(stderr,
-				"The branch '%s' is not a strict subset of your current HEAD.\n"
-				"If you are sure you want to delete it, run 'git branch -D %s'.\n",
-				argv[i], argv[i]);
-			exit(1);
+			error("The branch '%s' is not a strict subset of "
+				"your current HEAD.\n"
+				"If you are sure you want to delete it, "
+				"run 'git branch -D %s'.", argv[i], argv[i]);
+			ret = 1;
+			continue;
 		}
 
-		if (delete_ref(name, sha1))
-			printf("Error deleting %sbranch '%s'\n", remote,
+		if (delete_ref(name, sha1)) {
+			error("Error deleting %sbranch '%s'", remote,
 			       argv[i]);
-		else
+			ret = 1;
+		} else
 			printf("Deleted %sbranch %s.\n", remote, argv[i]);
 
-		free(name);
 	}
+
+	if (name)
+		free(name);
+
+	return(ret);
 }
 
 struct ref_item {
@@ -450,7 +471,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 	head += 11;
 
 	if (delete)
-		delete_branches(argc - i, argv + i, force_delete, kinds);
+		return delete_branches(argc - i, argv + i, force_delete, kinds);
 	else if (i == argc)
 		print_ref_list(kinds, verbose, abbrev);
 	else if (rename && (i == argc - 1))

From 4c10a5caa7d5e481a2acefbae71b694ab7e57108 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 14:04:38 -0800
Subject: [PATCH 228/330] blame: -b (blame.blankboundary) and --root
 (blame.showroot)

When blame.blankboundary is set (or -b option is given), commit
object names are blanked out in the "human readable" output
format for boundary commits.

When blame.showroot is not set (or --root is not given), the
root commits are treated as boundary commits.  The code still
attributes the lines to them, but with -b their object names are
not shown.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-blame.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/builtin-blame.c b/builtin-blame.c
index a250724463..211bdb3a75 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -22,7 +22,9 @@
 static char blame_usage[] =
 "git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\n"
 "  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+"  -b                  Show blank SHA-1 for boundary commits (Default: off)\n"
 "  -l, --long          Show long commit SHA1 (Default: off)\n"
+"  --root              Do not treat root commits as boundaries (Default: off)\n"
 "  -t, --time          Show raw timestamp (Default: off)\n"
 "  -f, --show-name     Show original filename (Default: auto)\n"
 "  -n, --show-number   Show original linenumber (Default: off)\n"
@@ -36,6 +38,8 @@ static int longest_author;
 static int max_orig_digits;
 static int max_digits;
 static int max_score_digits;
+static int show_root;
+static int blank_boundary;
 
 #ifndef DEBUG
 #define DEBUG 0
@@ -1095,6 +1099,9 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
 			if (commit->object.parsed)
 				mark_parents_uninteresting(commit);
 		}
+		/* treat root commit as boundary */
+		if (!commit->parents && !show_root)
+			commit->object.flags |= UNINTERESTING;
 
 		/* Take responsibility for the remaining entries */
 		for (ent = sb->ent; ent; ent = ent->next)
@@ -1318,8 +1325,12 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
 		int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? 40 : 8;
 
 		if (suspect->commit->object.flags & UNINTERESTING) {
-			length--;
-			putchar('^');
+			if (!blank_boundary) {
+				length--;
+				putchar('^');
+			}
+			else
+				memset(hex, ' ', length);
 		}
 
 		printf("%.*s", length, hex);
@@ -1639,6 +1650,19 @@ static void prepare_blame_range(struct scoreboard *sb,
 		usage(blame_usage);
 }
 
+static int git_blame_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "blame.showroot")) {
+		show_root = git_config_bool(var, value);
+		return 0;
+	}
+	if (!strcmp(var, "blame.blankboundary")) {
+		blank_boundary = git_config_bool(var, value);
+		return 0;
+	}
+	return git_default_config(var, value);
+}
+
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info revs;
@@ -1654,6 +1678,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	char type[10];
 	const char *bottomtop = NULL;
 
+	git_config(git_blame_config);
 	save_commit_buffer = 0;
 
 	opt = 0;
@@ -1662,6 +1687,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		const char *arg = argv[i];
 		if (*arg != '-')
 			break;
+		else if (!strcmp("-b", arg))
+			blank_boundary = 1;
+		else if (!strcmp("--root", arg))
+			show_root = 1;
 		else if (!strcmp("-c", arg))
 			output_option |= OUTPUT_ANNOTATE_COMPAT;
 		else if (!strcmp("-t", arg))

From 5cde71d64aff03d305099b4d239552679ecfaab6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 10 Dec 2006 20:55:50 -0800
Subject: [PATCH 229/330] git-add --interactive

A script to be driven when the user says "git add --interactive"
is introduced.

When it is run, first it runs its internal 'status' command to
show the current status, and then goes into its internactive
command loop.

The command loop shows the list of subcommands available, and
gives a prompt "What now> ".  In general, when the prompt ends
with a single '>', you can pick only one of the choices given
and type return, like this:

    *** Commands ***
      1: status       2: update       3: revert       4: add untracked
      5: patch        6: diff         7: quit         8: help
    What now> 1

You also could say "s" or "sta" or "status" above as long as the
choice is unique.

The main command loop has 6 subcommands (plus help and quit).

 * 'status' shows the change between HEAD and index (i.e. what
   will be committed if you say "git commit"), and between index
   and working tree files (i.e. what you could stage further
   before "git commit" using "git-add") for each path.  A sample
   output looks like this:

              staged     unstaged path
     1:       binary      nothing foo.png
     2:     +403/-35        +1/-1 git-add--interactive.perl

   It shows that foo.png has differences from HEAD (but that is
   binary so line count cannot be shown) and there is no
   difference between indexed copy and the working tree
   version (if the working tree version were also different,
   'binary' would have been shown in place of 'nothing').  The
   other file, git-add--interactive.perl, has 403 lines added
   and 35 lines deleted if you commit what is in the index, but
   working tree file has further modifications (one addition and
   one deletion).

 * 'update' shows the status information and gives prompt
   "Update>>".  When the prompt ends with double '>>', you can
   make more than one selection, concatenated with whitespace or
   comma.  Also you can say ranges.  E.g. "2-5 7,9" to choose
   2,3,4,5,7,9 from the list.  You can say '*' to choose
   everything.

   What you chose are then highlighted with '*', like this:

              staged     unstaged path
     1:       binary      nothing foo.png
   * 2:     +403/-35        +1/-1 git-add--interactive.perl

   To remove selection, prefix the input with - like this:

        Update>> -2

   After making the selection, answer with an empty line to
   stage the contents of working tree files for selected paths
   in the index.

 * 'revert' has a very similar UI to 'update', and the staged
   information for selected paths are reverted to that of the
   HEAD version.  Reverting new paths makes them untracked.

 * 'add untracked' has a very similar UI to 'update' and
   'revert', and lets you add untracked paths to the index.

 * 'patch' lets you choose one path out of 'status' like
   selection.  After choosing the path, it presents diff between
   the index and the working tree file and asks you if you want
   to stage the change of each hunk.  You can say:

        y - add the change from that hunk to index
        n - do not add the change from that hunk to index
        a - add the change from that hunk and all the rest to index
        d - do not the change from that hunk nor any of the rest to index
        j - do not decide on this hunk now, and view the next
            undecided hunk
        J - do not decide on this hunk now, and view the next hunk
        k - do not decide on this hunk now, and view the previous
            undecided hunk
        K - do not decide on this hunk now, and view the previous hunk

   After deciding the fate for all hunks, if there is any hunk
   that was chosen, the index is updated with the selected hunks.

 * 'diff' lets you review what will be committed (i.e. between
   HEAD and index).

This is still rough, but does everything except a few things I
think are needed.

 * 'patch' should be able to allow splitting a hunk into
   multiple hunks.

 * 'patch' does not adjust the line offsets @@ -k,l +m,n @@
   in the hunk header.  This does not have major problem in
   practice, but it _should_ do the adjustment.

 * It does not have any explicit support for a merge in
   progress; it may not work at all.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore                |   1 +
 Makefile                  |   1 +
 builtin-add.c             |  15 +
 git-add--interactive.perl | 570 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 587 insertions(+)
 create mode 100755 git-add--interactive.perl

diff --git a/.gitignore b/.gitignore
index d706dd92c6..91e69665f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@ GIT-CFLAGS
 GIT-VERSION-FILE
 git
 git-add
+git-add--interactive
 git-am
 git-annotate
 git-apply
diff --git a/Makefile b/Makefile
index 8919dabc78..354f66b233 100644
--- a/Makefile
+++ b/Makefile
@@ -177,6 +177,7 @@ SCRIPT_SH = \
 	git-lost-found.sh git-quiltimport.sh
 
 SCRIPT_PERL = \
+	git-add--interactive.perl \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
 	git-rerere.perl \
 	git-cvsserver.perl \
diff --git a/builtin-add.c b/builtin-add.c
index b3f920676a..aa2f0f32af 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -8,6 +8,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "dir.h"
+#include "exec_cmd.h"
 #include "cache-tree.h"
 
 static const char builtin_add_usage[] =
@@ -89,6 +90,20 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	int verbose = 0, show_only = 0;
 	const char **pathspec;
 	struct dir_struct dir;
+	int add_interactive = 0;
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp("--interactive", argv[i]))
+			add_interactive++;
+	}
+	if (add_interactive) {
+		const char *args[] = { "add--interactive", NULL };
+
+		if (add_interactive != 1 || argc != 2)
+			die("add --interactive does not take any parameters");
+		execv_git_cmd(args);
+		exit(1);
+	}
 
 	git_config(git_default_config);
 
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
new file mode 100755
index 0000000000..83d017d23c
--- /dev/null
+++ b/git-add--interactive.perl
@@ -0,0 +1,570 @@
+#!/usr/bin/perl -w
+
+use strict;
+
+sub run_cmd_pipe {
+	my $fh = undef;
+	open($fh, '-|', @_) or die;
+	return <$fh>;
+}
+
+my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));
+
+if (!defined $GIT_DIR) {
+	exit(1); # rev-parse would have already said "not a git repo"
+}
+chomp($GIT_DIR);
+
+sub refresh {
+	my $fh;
+	open $fh, '-|', qw(git update-index --refresh)
+	    or die;
+	while (<$fh>) {
+		;# ignore 'needs update'
+	}
+	close $fh;
+}
+
+sub list_untracked {
+	map {
+		chomp $_;
+		$_;
+	}
+	run_cmd_pipe(qw(git ls-files --others
+			--exclude-per-directory=.gitignore),
+		     "--exclude-from=$GIT_DIR/info/exclude",
+		     '--', @_);
+}
+
+my $status_fmt = '%12s %12s %s';
+my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');
+
+# Returns list of hashes, contents of each of which are:
+# PRINT:	print message
+# VALUE:	pathname
+# BINARY:	is a binary path
+# INDEX:	is index different from HEAD?
+# FILE:		is file different from index?
+# INDEX_ADDDEL:	is it add/delete between HEAD and index?
+# FILE_ADDDEL:	is it add/delete between index and file?
+
+sub list_modified {
+	my ($only) = @_;
+	my (%data, @return);
+	my ($add, $del, $adddel, $file);
+
+	for (run_cmd_pipe(qw(git diff-index --cached
+			     --numstat --summary HEAD))) {
+		if (($add, $del, $file) =
+		    /^([-\d]+)	([-\d]+)	(.*)/) {
+			my ($change, $bin);
+			if ($add eq '-' && $del eq '-') {
+				$change = 'binary';
+				$bin = 1;
+			}
+			else {
+				$change = "+$add/-$del";
+			}
+			$data{$file} = {
+				INDEX => $change,
+				BINARY => $bin,
+				FILE => 'nothing',
+			}
+		}
+		elsif (($adddel, $file) =
+		       /^ (create|delete) mode [0-7]+ (.*)$/) {
+			$data{$file}{INDEX_ADDDEL} = $adddel;
+		}
+	}
+
+	for (run_cmd_pipe(qw(git diff-files --numstat --summary))) {
+		if (($add, $del, $file) =
+		    /^([-\d]+)	([-\d]+)	(.*)/) {
+			if (!exists $data{$file}) {
+				$data{$file} = +{
+					INDEX => 'unchanged',
+					BINARY => 0,
+				};
+			}
+			my ($change, $bin);
+			if ($add eq '-' && $del eq '-') {
+				$change = 'binary';
+				$bin = 1;
+			}
+			else {
+				$change = "+$add/-$del";
+			}
+			$data{$file}{FILE} = $change;
+			if ($bin) {
+				$data{$file}{BINARY} = 1;
+			}
+		}
+		elsif (($adddel, $file) =
+		       /^ (create|delete) mode [0-7]+ (.*)$/) {
+			$data{$file}{FILE_ADDDEL} = $adddel;
+		}
+	}
+
+	for (sort keys %data) {
+		my $it = $data{$_};
+
+		if ($only) {
+			if ($only eq 'index-only') {
+				next if ($it->{INDEX} eq 'unchanged');
+			}
+			if ($only eq 'file-only') {
+				next if ($it->{FILE} eq 'nothing');
+			}
+		}
+		push @return, +{
+			VALUE => $_,
+			PRINT => (sprintf $status_fmt,
+				  $it->{INDEX}, $it->{FILE}, $_),
+			%$it,
+		};
+	}
+	return @return;
+}
+
+sub find_unique {
+	my ($string, @stuff) = @_;
+	my $found = undef;
+	for (my $i = 0; $i < @stuff; $i++) {
+		my $it = $stuff[$i];
+		my $hit = undef;
+		if (ref $it) {
+			if ((ref $it) eq 'ARRAY') {
+				$it = $it->[0];
+			}
+			else {
+				$it = $it->{VALUE};
+			}
+		}
+		eval {
+			if ($it =~ /^$string/) {
+				$hit = 1;
+			};
+		};
+		if (defined $hit && defined $found) {
+			return undef;
+		}
+		if ($hit) {
+			$found = $i + 1;
+		}
+	}
+	return $found;
+}
+
+sub list_and_choose {
+	my ($opts, @stuff) = @_;
+	my (@chosen, @return);
+	my $i;
+
+      TOPLOOP:
+	while (1) {
+		my $last_lf = 0;
+
+		if ($opts->{HEADER}) {
+			if (!$opts->{LIST_FLAT}) {
+				print "     ";
+			}
+			print "$opts->{HEADER}\n";
+		}
+		for ($i = 0; $i < @stuff; $i++) {
+			my $chosen = $chosen[$i] ? '*' : ' ';
+			my $print = $stuff[$i];
+			if (ref $print) {
+				if ((ref $print) eq 'ARRAY') {
+					$print = $print->[0];
+				}
+				else {
+					$print = $print->{PRINT};
+				}
+			}
+			printf("%s%2d: %s", $chosen, $i+1, $print);
+			if (($opts->{LIST_FLAT}) &&
+			    (($i + 1) % ($opts->{LIST_FLAT}))) {
+				print "\t";
+				$last_lf = 0;
+			}
+			else {
+				print "\n";
+				$last_lf = 1;
+			}
+		}
+		if (!$last_lf) {
+			print "\n";
+		}
+
+		return if ($opts->{LIST_ONLY});
+
+		print $opts->{PROMPT};
+		if ($opts->{SINGLETON}) {
+			print "> ";
+		}
+		else {
+			print ">> ";
+		}
+		my $line = <STDIN>;
+		last if (!$line);
+		chomp $line;
+		my $donesomething = 0;
+		for my $choice (split(/[\s,]+/, $line)) {
+			my $choose = 1;
+			my ($bottom, $top);
+
+			# Input that begins with '-'; unchoose
+			if ($choice =~ s/^-//) {
+				$choose = 0;
+			}
+			# A range can be specified like 5-7
+			if ($choice =~ /^(\d+)-(\d+)$/) {
+				($bottom, $top) = ($1, $2);
+			}
+			elsif ($choice =~ /^\d+$/) {
+				$bottom = $top = $choice;
+			}
+			elsif ($choice eq '*') {
+				$bottom = 1;
+				$top = 1 + @stuff;
+			}
+			else {
+				$bottom = $top = find_unique($choice, @stuff);
+				if (!defined $bottom) {
+					print "Huh ($choice)?\n";
+					next TOPLOOP;
+				}
+			}
+			if ($opts->{SINGLETON} && $bottom != $top) {
+				print "Huh ($choice)?\n";
+				next TOPLOOP;
+			}
+			for ($i = $bottom-1; $i <= $top-1; $i++) {
+				next if (@stuff <= $i);
+				$chosen[$i] = $choose;
+				$donesomething++;
+			}
+		}
+		last if (!$donesomething || $opts->{IMMEDIATE});
+	}
+	for ($i = 0; $i < @stuff; $i++) {
+		if ($chosen[$i]) {
+			push @return, $stuff[$i];
+		}
+	}
+	return @return;
+}
+
+sub status_cmd {
+	list_and_choose({ LIST_ONLY => 1, HEADER => $status_head },
+			list_modified());
+	print "\n";
+}
+
+sub say_n_paths {
+	my $did = shift @_;
+	my $cnt = scalar @_;
+	print "$did ";
+	if (1 < $cnt) {
+		print "$cnt paths\n";
+	}
+	else {
+		print "one path\n";
+	}
+}
+
+sub update_cmd {
+	my @mods = list_modified('file-only');
+	return if (!@mods);
+
+	my @update = list_and_choose({ PROMPT => 'Update',
+				       HEADER => $status_head, },
+				     @mods);
+	if (@update) {
+		system(qw(git update-index --add --),
+		       map { $_->{VALUE} } @update);
+		say_n_paths('updated', @update);
+	}
+	print "\n";
+}
+
+sub revert_cmd {
+	my @update = list_and_choose({ PROMPT => 'Revert',
+				       HEADER => $status_head, },
+				     list_modified());
+	if (@update) {
+		my @lines = run_cmd_pipe(qw(git ls-tree HEAD --),
+					 map { $_->{VALUE} } @update);
+		my $fh;
+		open $fh, '|-', qw(git update-index --index-info)
+		    or die;
+		for (@lines) {
+			print $fh $_;
+		}
+		close($fh);
+		for (@update) {
+			if ($_->{INDEX_ADDDEL} &&
+			    $_->{INDEX_ADDDEL} eq 'create') {
+				system(qw(git update-index --force-remove --),
+				       $_->{VALUE});
+				print "note: $_->{VALUE} is untracked now.\n";
+			}
+		}
+		refresh();
+		say_n_paths('reverted', @update);
+	}
+	print "\n";
+}
+
+sub add_untracked_cmd {
+	my @add = list_and_choose({ PROMPT => 'Add untracked' },
+				  list_untracked());
+	if (@add) {
+		system(qw(git update-index --add --), @add);
+		say_n_paths('added', @add);
+	}
+	print "\n";
+}
+
+sub parse_diff {
+	my ($path) = @_;
+	my @diff = run_cmd_pipe(qw(git diff-files -p --), $path);
+	my (@hunk) = { TEXT => [] };
+
+	for (@diff) {
+		if (/^@@ /) {
+			push @hunk, { TEXT => [] };
+		}
+		push @{$hunk[-1]{TEXT}}, $_;
+	}
+	return @hunk;
+}
+
+sub help_patch_cmd {
+	print <<\EOF ;
+y - stage this hunk
+n - do not stage this hunk
+a - stage this and all the remaining hunks
+d - do not stage this hunk nor any of the remaining hunks
+j - leave this hunk undecided, see next undecided hunk
+J - leave this hunk undecided, see next hunk
+k - leave this hunk undecided, see previous undecided hunk
+K - leave this hunk undecided, see previous hunk
+EOF
+}
+
+sub patch_update_cmd {
+	my @mods = list_modified('file-only');
+	@mods = grep { !($_->{BINARY}) } @mods;
+	return if (!@mods);
+
+	my ($it) = list_and_choose({ PROMPT => 'Patch update',
+				     SINGLETON => 1,
+				     IMMEDIATE => 1,
+				     HEADER => $status_head, },
+				   @mods);
+	return if (!$it);
+
+	my ($ix, $num);
+	my $path = $it->{VALUE};
+	my ($head, @hunk) = parse_diff($path);
+	for (@{$head->{TEXT}}) {
+		print;
+	}
+	$num = scalar @hunk;
+	$ix = 0;
+
+	while (1) {
+		my ($prev, $next, $other, $undecided);
+		$other = '';
+
+		if ($num <= $ix) {
+			$ix = 0;
+		}
+		for (my $i = 0; $i < $ix; $i++) {
+			if (!defined $hunk[$i]{USE}) {
+				$prev = 1;
+				$other .= '/k';
+				last;
+			}
+		}
+		if ($ix) {
+			$other .= '/K';
+		}
+		for (my $i = $ix + 1; $i < $num; $i++) {
+			if (!defined $hunk[$i]{USE}) {
+				$next = 1;
+				$other .= '/j';
+				last;
+			}
+		}
+		if ($ix < $num - 1) {
+			$other .= '/J';
+		}
+		for (my $i = 0; $i < $num; $i++) {
+			if (!defined $hunk[$i]{USE}) {
+				$undecided = 1;
+				last;
+			}
+		}
+		last if (!$undecided);
+
+		for (@{$hunk[$ix]{TEXT}}) {
+			print;
+		}
+		print "Stage this hunk [y/n/a/d$other/?]? ";
+		my $line = <STDIN>;
+		if ($line) {
+			if ($line =~ /^y/i) {
+				$hunk[$ix]{USE} = 1;
+			}
+			elsif ($line =~ /^n/i) {
+				$hunk[$ix]{USE} = 0;
+			}
+			elsif ($line =~ /^a/i) {
+				while ($ix < $num) {
+					if (!defined $hunk[$ix]{USE}) {
+						$hunk[$ix]{USE} = 1;
+					}
+					$ix++;
+				}
+				next;
+			}
+			elsif ($line =~ /^d/i) {
+				while ($ix < $num) {
+					if (!defined $hunk[$ix]{USE}) {
+						$hunk[$ix]{USE} = 0;
+					}
+					$ix++;
+				}
+				next;
+			}
+			elsif ($other =~ /K/ && $line =~ /^K/) {
+				$ix--;
+				next;
+			}
+			elsif ($other =~ /J/ && $line =~ /^J/) {
+				$ix++;
+				next;
+			}
+			elsif ($other =~ /k/ && $line =~ /^k/) {
+				while (1) {
+					$ix--;
+					last if (!$ix ||
+						 !defined $hunk[$ix]{USE});
+				}
+				next;
+			}
+			elsif ($other =~ /j/ && $line =~ /^j/) {
+				while (1) {
+					$ix++;
+					last if ($ix >= $num ||
+						 !defined $hunk[$ix]{USE});
+				}
+				next;
+			}
+			else {
+				help_patch_cmd($other);
+				next;
+			}
+			# soft increment
+			while (1) {
+				$ix++;
+				last if ($ix >= $num ||
+					 !defined $hunk[$ix]{USE});
+			}
+		}
+	}
+
+	my ($o_lno, $n_lno);
+	my @result = ();
+	for (@hunk) {
+		my $text = $_->{TEXT};
+		my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
+		    $text->[0] =~ /^@@ -(\d+)(?:,(\d+)) \+(\d+)(?:,(\d+)) @@/;
+		if (!$_->{USE}) {
+			# Adjust offset here.
+			next;
+		}
+		else {
+			for (@$text) {
+				push @result, $_;
+			}
+		}
+	}
+
+	if (@result) {
+		my $fh;
+
+		open $fh, '|-', qw(git apply --cached);
+		for (@{$head->{TEXT}}, @result) {
+			print $fh $_;
+		}
+		close $fh;
+		refresh();
+	}
+
+	print "\n";
+}
+
+sub diff_cmd {
+	my @mods = list_modified('index-only');
+	@mods = grep { !($_->{BINARY}) } @mods;
+	return if (!@mods);
+	my (@them) = list_and_choose({ PROMPT => 'Review diff',
+				     IMMEDIATE => 1,
+				     HEADER => $status_head, },
+				   @mods);
+	return if (!@them);
+	system(qw(git diff-index -p --cached HEAD --),
+	       map { $_->{VALUE} } @them);
+}
+
+sub quit_cmd {
+	print "Bye.\n";
+	exit(0);
+}
+
+sub help_cmd {
+	print <<\EOF ;
+status        - show paths with changes
+update        - add working tree state to the staged set of changes
+revert        - revert staged set of changes back to the HEAD version
+patch         - pick hunks and update selectively
+diff	      - view diff between HEAD and index
+add untracked - add contents of untracked files to the staged set of changes
+EOF
+}
+
+sub main_loop {
+	my @cmd = ([ 'status', \&status_cmd, ],
+		   [ 'update', \&update_cmd, ],
+		   [ 'revert', \&revert_cmd, ],
+		   [ 'add untracked', \&add_untracked_cmd, ],
+		   [ 'patch', \&patch_update_cmd, ],
+		   [ 'diff', \&diff_cmd, ],
+		   [ 'quit', \&quit_cmd, ],
+		   [ 'help', \&help_cmd, ],
+	);
+	while (1) {
+		my ($it) = list_and_choose({ PROMPT => 'What now',
+					     SINGLETON => 1,
+					     LIST_FLAT => 4,
+					     HEADER => '*** Commands ***',
+					     IMMEDIATE => 1 }, @cmd);
+		if ($it) {
+			eval {
+				$it->[1]->();
+			};
+			if ($@) {
+				print "$@";
+			}
+		}
+	}
+}
+
+my @z;
+
+refresh();
+status_cmd();
+main_loop();

From 835b2aeba74d64e21bba50f31a7c2a16acf24af7 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 11 Dec 2006 17:09:26 -0800
Subject: [PATCH 230/330] git-add --interactive: hunk splitting

This adds hunk splitting and recounting the patch lines.  The
'patch' subcommand now allows you to split a large hunk at
context lines in the middle.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-add--interactive.perl | 250 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 242 insertions(+), 8 deletions(-)

diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 83d017d23c..0057f86588 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -1,5 +1,6 @@
 #!/usr/bin/perl -w
 
+
 use strict;
 
 sub run_cmd_pipe {
@@ -340,6 +341,199 @@ sub parse_diff {
 	return @hunk;
 }
 
+sub hunk_splittable {
+	my ($text) = @_;
+
+	my @s = split_hunk($text);
+	return (1 < @s);
+}
+
+sub parse_hunk_header {
+	my ($line) = @_;
+	my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
+	    $line =~ /^@@ -(\d+)(?:,(\d+)) \+(\d+)(?:,(\d+)) @@/;
+	return ($o_ofs, $o_cnt, $n_ofs, $n_cnt);
+}
+
+sub split_hunk {
+	my ($text) = @_;
+	my @split = ();
+
+	# If there are context lines in the middle of a hunk,
+	# it can be split, but we would need to take care of
+	# overlaps later.
+
+	my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = parse_hunk_header($text->[0]);
+	my $hunk_start = 1;
+	my $next_hunk_start;
+
+      OUTER:
+	while (1) {
+		my $next_hunk_start = undef;
+		my $i = $hunk_start - 1;
+		my $this = +{
+			TEXT => [],
+			OLD => $o_ofs,
+			NEW => $n_ofs,
+			OCNT => 0,
+			NCNT => 0,
+			ADDDEL => 0,
+			POSTCTX => 0,
+		};
+
+		while (++$i < @$text) {
+			my $line = $text->[$i];
+			if ($line =~ /^ /) {
+				if ($this->{ADDDEL} &&
+				    !defined $next_hunk_start) {
+					# We have seen leading context and
+					# adds/dels and then here is another
+					# context, which is trailing for this
+					# split hunk and leading for the next
+					# one.
+					$next_hunk_start = $i;
+				}
+				push @{$this->{TEXT}}, $line;
+				$this->{OCNT}++;
+				$this->{NCNT}++;
+				if (defined $next_hunk_start) {
+					$this->{POSTCTX}++;
+				}
+				next;
+			}
+
+			# add/del
+			if (defined $next_hunk_start) {
+				# We are done with the current hunk and
+				# this is the first real change for the
+				# next split one.
+				$hunk_start = $next_hunk_start;
+				$o_ofs = $this->{OLD} + $this->{OCNT};
+				$n_ofs = $this->{NEW} + $this->{NCNT};
+				$o_ofs -= $this->{POSTCTX};
+				$n_ofs -= $this->{POSTCTX};
+				push @split, $this;
+				redo OUTER;
+			}
+			push @{$this->{TEXT}}, $line;
+			$this->{ADDDEL}++;
+			if ($line =~ /^-/) {
+				$this->{OCNT}++;
+			}
+			else {
+				$this->{NCNT}++;
+			}
+		}
+
+		push @split, $this;
+		last;
+	}
+
+	for my $hunk (@split) {
+		$o_ofs = $hunk->{OLD};
+		$n_ofs = $hunk->{NEW};
+		$o_cnt = $hunk->{OCNT};
+		$n_cnt = $hunk->{NCNT};
+
+		my $head = ("@@ -$o_ofs" .
+			    (($o_cnt != 1) ? ",$o_cnt" : '') .
+			    " +$n_ofs" .
+			    (($n_cnt != 1) ? ",$n_cnt" : '') .
+			    " @@\n");
+		unshift @{$hunk->{TEXT}}, $head;
+	}
+	return map { $_->{TEXT} } @split;
+}
+
+sub find_last_o_ctx {
+	my ($it) = @_;
+	my $text = $it->{TEXT};
+	my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) = parse_hunk_header($text->[0]);
+	my $i = @{$text};
+	my $last_o_ctx = $o_ofs + $o_cnt;
+	while (0 < --$i) {
+		my $line = $text->[$i];
+		if ($line =~ /^ /) {
+			$last_o_ctx--;
+			next;
+		}
+		last;
+	}
+	return $last_o_ctx;
+}
+
+sub merge_hunk {
+	my ($prev, $this) = @_;
+	my ($o0_ofs, $o0_cnt, $n0_ofs, $n0_cnt) =
+	    parse_hunk_header($prev->{TEXT}[0]);
+	my ($o1_ofs, $o1_cnt, $n1_ofs, $n1_cnt) =
+	    parse_hunk_header($this->{TEXT}[0]);
+
+	my (@line, $i, $ofs, $o_cnt, $n_cnt);
+	$ofs = $o0_ofs;
+	$o_cnt = $n_cnt = 0;
+	for ($i = 1; $i < @{$prev->{TEXT}}; $i++) {
+		my $line = $prev->{TEXT}[$i];
+		if ($line =~ /^\+/) {
+			$n_cnt++;
+			push @line, $line;
+			next;
+		}
+
+		last if ($o1_ofs <= $ofs);
+
+		$o_cnt++;
+		$ofs++;
+		if ($line =~ /^ /) {
+			$n_cnt++;
+		}
+		push @line, $line;
+	}
+
+	for ($i = 1; $i < @{$this->{TEXT}}; $i++) {
+		my $line = $this->{TEXT}[$i];
+		if ($line =~ /^\+/) {
+			$n_cnt++;
+			push @line, $line;
+			next;
+		}
+		$ofs++;
+		$o_cnt++;
+		if ($line =~ /^ /) {
+			$n_cnt++;
+		}
+		push @line, $line;
+	}
+	my $head = ("@@ -$o0_ofs" .
+		    (($o_cnt != 1) ? ",$o_cnt" : '') .
+		    " +$n0_ofs" .
+		    (($n_cnt != 1) ? ",$n_cnt" : '') .
+		    " @@\n");
+	@{$prev->{TEXT}} = ($head, @line);
+}
+
+sub coalesce_overlapping_hunks {
+	my (@in) = @_;
+	my @out = ();
+
+	my ($last_o_ctx);
+
+	for (grep { $_->{USE} } @in) {
+		my $text = $_->{TEXT};
+		my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
+		    parse_hunk_header($text->[0]);
+		if (defined $last_o_ctx &&
+		    $o_ofs <= $last_o_ctx) {
+			merge_hunk($out[-1], $_);
+		}
+		else {
+			push @out, $_;
+		}
+		$last_o_ctx = find_last_o_ctx($out[-1]);
+	}
+	return @out;
+}
+
 sub help_patch_cmd {
 	print <<\EOF ;
 y - stage this hunk
@@ -350,6 +544,7 @@ j - leave this hunk undecided, see next undecided hunk
 J - leave this hunk undecided, see next hunk
 k - leave this hunk undecided, see previous undecided hunk
 K - leave this hunk undecided, see previous hunk
+s - split the current hunk into smaller hunks
 EOF
 }
 
@@ -375,13 +570,13 @@ sub patch_update_cmd {
 	$ix = 0;
 
 	while (1) {
-		my ($prev, $next, $other, $undecided);
+		my ($prev, $next, $other, $undecided, $i);
 		$other = '';
 
 		if ($num <= $ix) {
 			$ix = 0;
 		}
-		for (my $i = 0; $i < $ix; $i++) {
+		for ($i = 0; $i < $ix; $i++) {
 			if (!defined $hunk[$i]{USE}) {
 				$prev = 1;
 				$other .= '/k';
@@ -391,7 +586,7 @@ sub patch_update_cmd {
 		if ($ix) {
 			$other .= '/K';
 		}
-		for (my $i = $ix + 1; $i < $num; $i++) {
+		for ($i = $ix + 1; $i < $num; $i++) {
 			if (!defined $hunk[$i]{USE}) {
 				$next = 1;
 				$other .= '/j';
@@ -401,7 +596,7 @@ sub patch_update_cmd {
 		if ($ix < $num - 1) {
 			$other .= '/J';
 		}
-		for (my $i = 0; $i < $num; $i++) {
+		for ($i = 0; $i < $num; $i++) {
 			if (!defined $hunk[$i]{USE}) {
 				$undecided = 1;
 				last;
@@ -409,6 +604,9 @@ sub patch_update_cmd {
 		}
 		last if (!$undecided);
 
+		if (hunk_splittable($hunk[$ix]{TEXT})) {
+			$other .= '/s';
+		}
 		for (@{$hunk[$ix]{TEXT}}) {
 			print;
 		}
@@ -463,6 +661,18 @@ sub patch_update_cmd {
 				}
 				next;
 			}
+			elsif ($other =~ /s/ && $line =~ /^s/) {
+				my @split = split_hunk($hunk[$ix]{TEXT});
+				if (1 < @split) {
+					print "Split into ",
+					scalar(@split), " hunks.\n";
+				}
+				splice(@hunk, $ix, 1,
+				       map { +{ TEXT => $_, USE => undef } }
+				       @split);
+				$num = scalar @hunk;
+				next;
+			}
 			else {
 				help_patch_cmd($other);
 				next;
@@ -476,17 +686,37 @@ sub patch_update_cmd {
 		}
 	}
 
-	my ($o_lno, $n_lno);
+	@hunk = coalesce_overlapping_hunks(@hunk);
+
+	my ($o_lofs, $n_lofs) = (0, 0);
 	my @result = ();
 	for (@hunk) {
 		my $text = $_->{TEXT};
 		my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =
-		    $text->[0] =~ /^@@ -(\d+)(?:,(\d+)) \+(\d+)(?:,(\d+)) @@/;
+		    parse_hunk_header($text->[0]);
+
 		if (!$_->{USE}) {
-			# Adjust offset here.
+			if (!defined $o_cnt) { $o_cnt = 1; }
+			if (!defined $n_cnt) { $n_cnt = 1; }
+
+			# We would have added ($n_cnt - $o_cnt) lines
+			# to the postimage if we were to use this hunk,
+			# but we didn't.  So the line number that the next
+			# hunk starts at would be shifted by that much.
+			$n_lofs -= ($n_cnt - $o_cnt);
 			next;
 		}
 		else {
+			if ($n_lofs) {
+				$n_ofs += $n_lofs;
+				$text->[0] = ("@@ -$o_ofs" .
+					      ((defined $o_cnt)
+					       ? ",$o_cnt" : '') .
+					      " +$n_ofs" .
+					      ((defined $n_cnt)
+					       ? ",$n_cnt" : '') .
+					      " @@\n");
+			}
 			for (@$text) {
 				push @result, $_;
 			}
@@ -500,7 +730,11 @@ sub patch_update_cmd {
 		for (@{$head->{TEXT}}, @result) {
 			print $fh $_;
 		}
-		close $fh;
+		if (!close $fh) {
+			for (@{$head->{TEXT}}, @result) {
+				print STDERR $_;
+			}
+		}
 		refresh();
 	}
 

From 171e800b374c98dd703e906bd9955b2b0410cabe Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 01:14:35 -0800
Subject: [PATCH 231/330] Revert "fix testsuite: make sure they use templates
 freshly built from the source"

This reverts commit 74d20040cafdced657efbf49795183d209a3a07b.
Version from Johannes to introduce GIT_TEMPLATE_DIR is simpler,
although I unconsciously stayed away from introducing yet another
environment variable.
---
 t/t4116-apply-reverse.sh      |  4 ++--
 t/t5300-pack-object.sh        |  6 +++---
 t/t5400-send-pack.sh          |  2 +-
 t/t5500-fetch-pack.sh         |  2 +-
 t/t5510-fetch.sh              |  8 ++++----
 t/t5520-pull.sh               |  2 +-
 t/t5600-clone-fail-cleanup.sh |  6 +++---
 t/t5700-clone-reference.sh    |  4 ++--
 t/t5710-info-alternate.sh     | 14 +++++++-------
 t/t7001-mv.sh                 |  4 ++--
 t/test-lib.sh                 | 10 ----------
 11 files changed, 26 insertions(+), 36 deletions(-)

diff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh
index a79c77af39..74f5c2a575 100755
--- a/t/t4116-apply-reverse.sh
+++ b/t/t4116-apply-reverse.sh
@@ -50,12 +50,12 @@ test_expect_success 'setup separate repository lacking postimage' '
 
 	git tar-tree initial initial | tar xf - &&
 	(
-		cd initial && git_init_db && git add .
+		cd initial && git init-db && git add .
 	) &&
 
 	git tar-tree second second | tar xf - &&
 	(
-		cd second && git_init_db && git add .
+		cd second && git init-db && git add .
 	)
 
 '
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 8a8152b257..de45ac4e0f 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -44,7 +44,7 @@ test_expect_success \
     'unpack without delta' \
     "GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git_init_db &&
+     git-init-db &&
      git-unpack-objects -n <test-1-${packname_1}.pack &&
      git-unpack-objects <test-1-${packname_1}.pack"
 
@@ -75,7 +75,7 @@ test_expect_success \
     'unpack with delta' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git_init_db &&
+     git-init-db &&
      git-unpack-objects -n <test-2-${packname_2}.pack &&
      git-unpack-objects <test-2-${packname_2}.pack'
 
@@ -100,7 +100,7 @@ test_expect_success \
     'use packed objects' \
     'GIT_OBJECT_DIRECTORY=.git2/objects &&
      export GIT_OBJECT_DIRECTORY &&
-     git_init_db &&
+     git-init-db &&
      cp test-1-${packname_1}.pack test-1-${packname_1}.idx .git2/objects/pack && {
 	 git-diff-tree --root -p $commit &&
 	 while read object
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 901da8c4a9..28744b35e1 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -24,7 +24,7 @@ test_expect_success setup '
 	    parent=$commit || return 1
 	done &&
 	git-update-ref HEAD "$commit" &&
-	git_clone -l ./. victim &&
+	git-clone -l ./. victim &&
 	cd victim &&
 	git-log &&
 	cd .. &&
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 61e2a55311..f7625a6f46 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -97,7 +97,7 @@ pull_to_client () {
 (
 	mkdir client &&
 	cd client &&
-	git_init_db 2>> log2.txt
+	git-init-db 2>> log2.txt
 )
 
 add A1
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e2e8c894f9..a11ab0ad41 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -15,12 +15,12 @@ test_expect_success setup '
 	git commit -a -m original'
 
 test_expect_success "clone and setup child repos" '
-	git_clone . one &&
+	git clone . one &&
 	cd one &&
 	echo >file updated by one &&
 	git commit -a -m "updated by one" &&
 	cd .. &&
-	git_clone . two &&
+	git clone . two &&
 	cd two &&
 	git repo-config branch.master.remote one &&
 	{
@@ -28,7 +28,7 @@ test_expect_success "clone and setup child repos" '
 		echo "Pull: refs/heads/master:refs/heads/one"
 	} >.git/remotes/one
 	cd .. &&
-	git_clone . three &&
+	git clone . three &&
 	cd three &&
 	git repo-config branch.master.remote two &&
 	git repo-config branch.master.merge refs/heads/one &&
@@ -74,7 +74,7 @@ test_expect_success 'fetch following tags' '
 
 	mkdir four &&
 	cd four &&
-	git_init_db &&
+	git init-db &&
 
 	git fetch .. :track &&
 	git show-ref --verify refs/tags/anno &&
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 66ef92f41f..f841574573 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -17,7 +17,7 @@ test_expect_success setup '
 test_expect_success 'pulling into void' '
 	mkdir cloned &&
 	cd cloned &&
-	git_init_db &&
+	git init-db &&
 	git pull ..
 '
 
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 1913a12d71..041be04f5c 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,7 +13,7 @@ remove the directory before attempting a clone again.'
 
 test_expect_failure \
     'clone of non-existent source should fail' \
-    'git_clone foo bar'
+    'git-clone foo bar'
 
 test_expect_failure \
     'failed clone should not leave a directory' \
@@ -29,11 +29,11 @@ test_create_repo foo
 # current path not to the target dir
 test_expect_failure \
     'clone of non-existent (relative to $PWD) source should fail' \
-    'git_clone ../foo baz'
+    'git-clone ../foo baz'
 
 test_expect_success \
     'clone should work now that source exists' \
-    'git_clone foo bar'
+    'git-clone foo bar'
 
 test_expect_success \
     'successfull clone must leave the directory' \
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 52dab2d484..dd9caad1c2 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -17,7 +17,7 @@ git commit -m initial'
 cd "$base_dir"
 
 test_expect_success 'preparing second repository' \
-'git_clone A B && cd B &&
+'git clone A B && cd B &&
 echo second > file2 &&
 git add file2 &&
 git commit -m addition &&
@@ -27,7 +27,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'cloning with reference' \
-'git_clone -l -s --reference B A C'
+'git clone -l -s --reference B A C'
 
 cd "$base_dir"
 
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 3c43554c69..b9f6d96363 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -34,7 +34,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'preparing second repository' \
-'git_clone -l -s A B && cd B &&
+'git clone -l -s A B && cd B &&
 echo "foo bar" > file2 &&
 git add file2 &&
 git commit -m "next commit" file2 &&
@@ -44,7 +44,7 @@ git prune'
 cd "$base_dir"
 
 test_expect_success 'preparing third repository' \
-'git_clone -l -s B C && cd C &&
+'git clone -l -s B C && cd C &&
 echo "Goodbye, cruel world" > file3 &&
 git add file3 &&
 git commit -m "one more" file3 &&
@@ -54,11 +54,11 @@ git prune'
 cd "$base_dir"
 
 test_expect_failure 'creating too deep nesting' \
-'git_clone -l -s C D &&
-git_clone -l -s D E &&
-git_clone -l -s E F &&
-git_clone -l -s F G &&
-git_clone -l -s G H &&
+'git clone -l -s C D &&
+git clone -l -s D E &&
+git clone -l -s E F &&
+git clone -l -s F G &&
+git clone -l -s G H &&
 cd H &&
 test_valid_repo'
 
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index ae597e80dd..2f4ff82e14 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -88,7 +88,7 @@ test_expect_success \
 
 test_expect_success "Michael Cassar's test case" '
 	rm -fr .git papers partA &&
-	git_init_db &&
+	git init-db &&
 	mkdir -p papers/unsorted papers/all-papers partA &&
 	echo a > papers/unsorted/Thesis.pdf &&
 	echo b > partA/outline.txt &&
@@ -109,7 +109,7 @@ rm -fr papers partA path?
 
 test_expect_success "Sergey Vlasov's test case" '
 	rm -fr .git &&
-	git_init_db &&
+	git init-db &&
 	mkdir ab &&
 	date >ab.c &&
 	date >ab/d &&
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 7e9149751e..ac7be769b4 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -182,16 +182,6 @@ test_create_repo () {
 	cd "$owd"
 }
 	
-# Many tests do init-db and clone but they must be told about the freshly
-# built templates.
-git_init_db () {
-	git init-db --template="$GIT_EXEC_PATH/templates/blt/" "$@"
-}
-
-git_clone () {
-	git clone --template="$GIT_EXEC_PATH/templates/blt/" "$@"
-}
-
 test_done () {
 	trap - exit
 	case "$test_failure" in

From 8683a45d66967b0969516a2b72cdbf136c2064a2 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Tue, 19 Dec 2006 09:18:09 +0100
Subject: [PATCH 232/330] Introduce GIT_TEMPLATE_DIR

Instead of passing --template explicitely to init-db and clone, you can
just set the environment variable GIT_TEMPLATE_DIR.

Also make use of it in the tests, to make sure that the templates are
copied.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-init-db.c | 7 +++++--
 t/test-lib.sh     | 3 ++-
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/builtin-init-db.c b/builtin-init-db.c
index 1d7d15e8d5..c8ed5c2a0b 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -124,8 +124,11 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
 	int template_len;
 	DIR *dir;
 
-	if (!template_dir)
-		template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+	if (!template_dir) {
+		template_dir = getenv("GIT_TEMPLATE_DIR");
+		if (!template_dir)
+			template_dir = DEFAULT_GIT_TEMPLATE_DIR;
+	}
 	strcpy(template_path, template_dir);
 	template_len = strlen(template_path);
 	if (template_path[template_len-1] != '/') {
diff --git a/t/test-lib.sh b/t/test-lib.sh
index ac7be769b4..f0f9cd6be0 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -208,8 +208,9 @@ test_done () {
 # t/ subdirectory and are run in trash subdirectory.
 PATH=$(pwd)/..:$PATH
 GIT_EXEC_PATH=$(pwd)/..
+GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
 HOME=$(pwd)/trash
-export PATH GIT_EXEC_PATH HOME
+export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR HOME
 
 GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git
 export GITPERLLIB

From 75c384efb52d0e3eb1e8c2f53668b4066fe6a8d6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 01:50:37 -0800
Subject: [PATCH 233/330] Do not create $GIT_DIR/remotes/ directory anymore.

Because we do not use --no-separate-remote anymore, there is no
reason to create that directory from the template.

t5510 test is updated to test both $GIT_DIR/remotes/ based
configuration and $GIT_DIR/config variable (credits to
Johannes).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t5510-fetch.sh    | 7 +++----
 templates/remotes-- | 1 -
 2 files changed, 3 insertions(+), 5 deletions(-)
 delete mode 100644 templates/remotes--

diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index a11ab0ad41..90eeeba2a3 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -23,15 +23,14 @@ test_expect_success "clone and setup child repos" '
 	git clone . two &&
 	cd two &&
 	git repo-config branch.master.remote one &&
-	{
-		echo "URL: ../one/.git/"
-		echo "Pull: refs/heads/master:refs/heads/one"
-	} >.git/remotes/one
+	git repo-config remote.one.url ../one/.git/ &&
+	git repo-config remote.one.fetch refs/heads/master:refs/heads/one &&
 	cd .. &&
 	git clone . three &&
 	cd three &&
 	git repo-config branch.master.remote two &&
 	git repo-config branch.master.merge refs/heads/one &&
+	mkdir -p .git/remotes &&
 	{
 		echo "URL: ../two/.git/"
 		echo "Pull: refs/heads/master:refs/heads/two"
diff --git a/templates/remotes-- b/templates/remotes--
deleted file mode 100644
index fae88709a6..0000000000
--- a/templates/remotes--
+++ /dev/null
@@ -1 +0,0 @@
-: this is just to ensure the directory exists.

From d4ebc36c5ee964592303c59260417b758d024c31 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 01:28:15 -0800
Subject: [PATCH 234/330] Use preprocessor constants for environment variable
 names.

We broke the discipline Linus set up to allow compiler help us
avoid typos in environment names in the early days of git over
time.  This defines a handful preprocessor constants for
environment variable names used in relatively core parts of the
system.

I've left out variable names specific to subsystems such as HTTP
and SSL as I do not think they are big problems.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-init-db.c     |  2 +-
 builtin-repo-config.c |  4 ++--
 cache.h               |  4 ++++
 config.c              | 12 ++++++------
 exec_cmd.c            |  4 ++--
 merge-recursive.c     |  2 +-
 6 files changed, 16 insertions(+), 12 deletions(-)

diff --git a/builtin-init-db.c b/builtin-init-db.c
index c8ed5c2a0b..01f366ad0b 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -125,7 +125,7 @@ static void copy_templates(const char *git_dir, int len, const char *template_di
 	DIR *dir;
 
 	if (!template_dir) {
-		template_dir = getenv("GIT_TEMPLATE_DIR");
+		template_dir = getenv(TEMPLATE_DIR_ENVIRONMENT);
 		if (!template_dir)
 			template_dir = DEFAULT_GIT_TEMPLATE_DIR;
 	}
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index a38099a63d..4885930ca4 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -67,10 +67,10 @@ static int get_value(const char* key_, const char* regex_)
 	char *global = NULL, *repo_config = NULL;
 	const char *local;
 
-	local = getenv("GIT_CONFIG");
+	local = getenv(CONFIG_ENVIRONMENT);
 	if (!local) {
 		const char *home = getenv("HOME");
-		local = getenv("GIT_CONFIG_LOCAL");
+		local = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!local)
 			local = repo_config = xstrdup(git_path("config"));
 		if (home)
diff --git a/cache.h b/cache.h
index 8ad5920d2b..4943056c19 100644
--- a/cache.h
+++ b/cache.h
@@ -122,6 +122,10 @@ extern int cache_errno;
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
 #define GRAFT_ENVIRONMENT "GIT_GRAFT_FILE"
+#define TEMPLATE_DIR_ENVIRONMENT "GIT_TEMPLATE_DIR"
+#define CONFIG_ENVIRONMENT "GIT_CONFIG"
+#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
+#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
 
 extern int is_bare_git_dir(const char *dir);
 extern const char *get_git_dir(void);
diff --git a/config.c b/config.c
index 663993fefa..6a4224e98c 100644
--- a/config.c
+++ b/config.c
@@ -350,10 +350,10 @@ int git_config(config_fn_t fn)
 	 * $GIT_CONFIG_LOCAL will make it process it in addition to the
 	 * global config file, the same way it would the per-repository
 	 * config file otherwise. */
-	filename = getenv("GIT_CONFIG");
+	filename = getenv(CONFIG_ENVIRONMENT);
 	if (!filename) {
 		home = getenv("HOME");
-		filename = getenv("GIT_CONFIG_LOCAL");
+		filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!filename)
 			filename = repo_config = xstrdup(git_path("config"));
 	}
@@ -544,9 +544,9 @@ int git_config_set_multivar(const char* key, const char* value,
 	char* lock_file;
 	const char* last_dot = strrchr(key, '.');
 
-	config_filename = getenv("GIT_CONFIG");
+	config_filename = getenv(CONFIG_ENVIRONMENT);
 	if (!config_filename) {
-		config_filename = getenv("GIT_CONFIG_LOCAL");
+		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!config_filename)
 			config_filename  = git_path("config");
 	}
@@ -754,9 +754,9 @@ int git_config_rename_section(const char *old_name, const char *new_name)
 	int out_fd;
 	char buf[1024];
 
-	config_filename = getenv("GIT_CONFIG");
+	config_filename = getenv(CONFIG_ENVIRONMENT);
 	if (!config_filename) {
-		config_filename = getenv("GIT_CONFIG_LOCAL");
+		config_filename = getenv(CONFIG_LOCAL_ENVIRONMENT);
 		if (!config_filename)
 			config_filename  = git_path("config");
 	}
diff --git a/exec_cmd.c b/exec_cmd.c
index 5d6a1247b4..3996bce33f 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -21,7 +21,7 @@ const char *git_exec_path(void)
 	if (current_exec_path)
 		return current_exec_path;
 
-	env = getenv("GIT_EXEC_PATH");
+	env = getenv(EXEC_PATH_ENVIRONMENT);
 	if (env && *env) {
 		return env;
 	}
@@ -35,7 +35,7 @@ int execv_git_cmd(const char **argv)
 	char git_command[PATH_MAX + 1];
 	int i;
 	const char *paths[] = { current_exec_path,
-				getenv("GIT_EXEC_PATH"),
+				getenv(EXEC_PATH_ENVIRONMENT),
 				builtin_exec_path };
 
 	for (i = 0; i < ARRAY_SIZE(paths); ++i) {
diff --git a/merge-recursive.c b/merge-recursive.c
index 6dd6e2e5af..ae4dcfbe5b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1279,7 +1279,7 @@ int main(int argc, char *argv[])
 	struct commit *result, *h1, *h2;
 
 	git_config(git_default_config); /* core.filemode */
-	original_index_file = getenv("GIT_INDEX_FILE");
+	original_index_file = getenv(INDEX_ENVIRONMENT);
 
 	if (!original_index_file)
 		original_index_file = xstrdup(git_path("index"));

From 4363dfbe3d2f3fe3a4bd0fa7e9b22a14532c6cdb Mon Sep 17 00:00:00 2001
From: Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
Date: Tue, 19 Dec 2006 01:39:07 +0100
Subject: [PATCH 235/330] Move "no merge candidate" warning into git-pull

The warning triggered even when running "git fetch" only
when resulting .git/FETCH_HEAD only contained
branches marked as 'not-for-merge'.

Signed-off-by: Josef Weidendorfer <weidendo@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 10 ----------
 git-pull.sh         |  4 ++++
 2 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 11c4aba244..ea7511e8a0 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -132,7 +132,6 @@ canon_refs_list_for_fetch () {
 	# or the first one otherwise; add prefix . to the rest
 	# to prevent the secondary branches to be merged by default.
 	merge_branches=
-	found_mergeref=
 	curr_branch=
 	if test "$1" = "-d"
 	then
@@ -172,10 +171,6 @@ canon_refs_list_for_fetch () {
 			    dot_prefix= && break
 			done
 		fi
-		if test -z $dot_prefix
-		then
-			found_mergeref=true
-		fi
 		case "$remote" in
 		'') remote=HEAD ;;
 		refs/heads/* | refs/tags/* | refs/remotes/*) ;;
@@ -196,11 +191,6 @@ canon_refs_list_for_fetch () {
 		fi
 		echo "${dot_prefix}${force}${remote}:${local}"
 	done
-	if test -z "$found_mergeref" -a "$curr_branch"
-	then
-		echo >&2 "Warning: No merge candidate found because value of config option
-         \"branch.${curr_branch}.merge\" does not match any remote branch fetched."
-	fi
 }
 
 # Returns list of src: (no store), or src:dst (store)
diff --git a/git-pull.sh b/git-pull.sh
index e23beb685d..1703091bbb 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -76,6 +76,10 @@ merge_head=$(sed -e '/	not-for-merge	/d' \
 
 case "$merge_head" in
 '')
+	curr_branch=$(git-symbolic-ref HEAD | \
+		sed -e 's|^refs/heads/||')
+	echo >&2 "Warning: No merge candidate found because value of config option
+         \"branch.${curr_branch}.merge\" does not match any remote branch fetched."
 	echo >&2 "No changes."
 	exit 0
 	;;

From 8dce82356221df894a1b0dd5a4189ad3169a5069 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 02:28:16 -0800
Subject: [PATCH 236/330] Revert "Make left-right automatic."

This reverts commit 5761231975ceffa531d86d9bab0f9a9a370674f6.

Feeding symmetric difference to gitk is so useful, and it is the
same for other graphical Porcelains.  Rather than forcing them
to pass --no-left-right, making it optional.

Noticed and reported by Jeff King.
---
 revision.c | 13 +++----------
 revision.h |  1 -
 2 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/revision.c b/revision.c
index 56819f8220..d84f46e5a6 100644
--- a/revision.c
+++ b/revision.c
@@ -853,8 +853,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->boundary = 1;
 				continue;
 			}
-			if (!strcmp(arg, "--no-left-right")) {
-				revs->no_left_right = 1;
+			if (!strcmp(arg, "--left-right")) {
+				revs->left_right = 1;
 				continue;
 			}
 			if (!strcmp(arg, "--objects")) {
@@ -1055,18 +1055,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 void prepare_revision_walk(struct rev_info *revs)
 {
 	int nr = revs->pending.nr;
-	int has_symmetric = 0;
 	struct object_array_entry *list = revs->pending.objects;
 
 	revs->pending.nr = 0;
 	revs->pending.alloc = 0;
 	revs->pending.objects = NULL;
 	while (--nr >= 0) {
-		struct commit *commit;
-
-		if (list->item->flags & SYMMETRIC_LEFT)
-			has_symmetric = 1;
-		commit = handle_commit(revs, list->item, list->name);
+		struct commit *commit = handle_commit(revs, list->item, list->name);
 		if (commit) {
 			if (!(commit->object.flags & SEEN)) {
 				commit->object.flags |= SEEN;
@@ -1078,8 +1073,6 @@ void prepare_revision_walk(struct rev_info *revs)
 
 	if (revs->no_walk)
 		return;
-	if (!revs->no_left_right && has_symmetric)
-		revs->left_right = 1;
 	if (revs->limited)
 		limit_list(revs);
 	if (revs->topo_order)
diff --git a/revision.h b/revision.h
index b2ab81488c..4585463a44 100644
--- a/revision.h
+++ b/revision.h
@@ -41,7 +41,6 @@ struct rev_info {
 			limited:1,
 			unpacked:1, /* see also ignore_packed below */
 			boundary:1,
-			no_left_right:1,
 			left_right:1,
 			parents:1;
 

From c6b4fa96cbb65036b36e19b4dd61c7030e457327 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 19 Dec 2006 14:58:20 -0800
Subject: [PATCH 237/330] git-cvsserver: fix breakage when calling git
 merge-file

In the same vein as 8336afa563fbeff35e531396273065161181f04c,
this fixes the the RCS merge to git-merge-file conversion in
commit e2b70087.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-cvsserver.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 2a8447e253..df395126b8 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -946,7 +946,7 @@ sub req_update
 
             $log->debug("Temporary directory for merge is $dir");
 
-            my $return = system("git merge-file", $file_local, $file_old, $file_new);
+            my $return = system("git", "merge-file", $file_local, $file_old, $file_new);
             $return >>= 8;
 
             if ( $return == 0 )

From 5caf92322303a0942e0cee5a93cb24f2d8d44022 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 18:26:48 -0800
Subject: [PATCH 238/330] fix populate-filespec

I hand munged the original patch when committing 1510fea78, and
screwed up the conversion.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/diff.c b/diff.c
index 99744354a7..6e6f2a765c 100644
--- a/diff.c
+++ b/diff.c
@@ -1223,7 +1223,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
 	 * objects however would tend to be slower as they need
 	 * to be individually opened and inflated.
 	 */
-	if (FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL))
+	if (!FAST_WORKING_DIRECTORY && !want_file && has_sha1_pack(sha1, NULL))
 		return 0;
 
 	len = strlen(name);

From c17f9f12a4a8d224e5cff6bcfa6191450f90fa0b Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Tue, 19 Dec 2006 22:25:13 -0500
Subject: [PATCH 239/330] Remove COLLISION_CHECK from Makefile since it's not
 used.

It's rather misleading to have configuration options that don't do
anything.  If someone adds collision checking they might also want to
restore this option.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile     | 4 ----
 configure.ac | 4 ----
 2 files changed, 8 deletions(-)

diff --git a/Makefile b/Makefile
index 8919dabc78..76511045a8 100644
--- a/Makefile
+++ b/Makefile
@@ -82,10 +82,6 @@ all:
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 #
-# Define COLLISION_CHECK below if you believe that SHA1's
-# 1461501637330902918203684832716283019655932542976 hashes do not give you
-# sufficient guarantee that no collisions between objects will ever happen.
-#
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
diff --git a/configure.ac b/configure.ac
index 34e34789bd..e153d53823 100644
--- a/configure.ac
+++ b/configure.ac
@@ -316,10 +316,6 @@ GIT_PARSE_WITH(iconv))
 
 ## --enable-FEATURE[=ARG] and --disable-FEATURE
 #
-# Define COLLISION_CHECK below if you believe that SHA1's
-# 1461501637330902918203684832716283019655932542976 hashes do not give you
-# sufficient guarantee that no collisions between objects will ever happen.
-#
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely

From fc1905bb9340304fb5980841fca24638028c1c5e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 21:55:27 -0800
Subject: [PATCH 240/330] config_rename_section: fix FILE* leak

Noticed by SungHyun Nam.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 config.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/config.c b/config.c
index 663993fefa..8cbdd178bd 100644
--- a/config.c
+++ b/config.c
@@ -749,7 +749,7 @@ out_free:
 int git_config_rename_section(const char *old_name, const char *new_name)
 {
 	int ret = 0;
-	const char *config_filename;
+	char *config_filename;
 	struct lock_file *lock = xcalloc(sizeof(struct lock_file), 1);
 	int out_fd;
 	char buf[1024];
@@ -762,11 +762,15 @@ int git_config_rename_section(const char *old_name, const char *new_name)
 	}
 	config_filename = xstrdup(config_filename);
 	out_fd = hold_lock_file_for_update(lock, config_filename, 0);
-	if (out_fd < 0)
-		return error("Could not lock config file!");
+	if (out_fd < 0) {
+		ret = error("Could not lock config file!");
+		goto out;
+	}
 
-	if (!(config_file = fopen(config_filename, "rb")))
-		return error("Could not open config file!");
+	if (!(config_file = fopen(config_filename, "rb"))) {
+		ret = error("Could not open config file!");
+		goto out;
+	}
 
 	while (fgets(buf, sizeof(buf), config_file)) {
 		int i;
@@ -806,8 +810,11 @@ int git_config_rename_section(const char *old_name, const char *new_name)
 		}
 		write(out_fd, buf, strlen(buf));
 	}
+	fclose(config_file);
 	if (close(out_fd) || commit_lock_file(lock) < 0)
-		return error("Cannot commit config file!");
+		ret = error("Cannot commit config file!");
+ out:
+	free(config_filename);
 	return ret;
 }
 

From 313ce8cee665447e4476d7e8985b270346a8e5a1 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Tue, 19 Dec 2006 12:08:54 +0000
Subject: [PATCH 241/330] gitweb: Show '...' links in "summary" view only if
 there are more items

Show "..." links in "summary" view to shortlog, heads (if there are
any), and tags (if there are any) only if there are more items to show
than shown already.

This means that "..." link is shown below shortened shortlog if there
are more than 16 commits, "..." link below shortened heads list if
there are more than 16 heads refs (16 branches), "..." link below
shortened tags list if there are more than 16 tags.

Modified patch from Jakub to to apply cleanly to master, also preform
the same "..." link logic to the forks list.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 4059894e0b..ebbc397ee8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2915,8 +2915,10 @@ sub git_summary {
 	my $owner = git_get_project_owner($project);
 
 	my $refs = git_get_references();
-	my @taglist  = git_get_tags_list(15);
-	my @headlist = git_get_heads_list(15);
+	# These get_*_list functions return one more to allow us to see if
+	# there are more ...
+	my @taglist  = git_get_tags_list(16);
+	my @headlist = git_get_heads_list(16);
 	my @forklist;
 	my ($check_forks) = gitweb_check_feature('forks');
 
@@ -2952,6 +2954,8 @@ sub git_summary {
 		}
 	}
 
+	# we need to request one more than 16 (0..15) to check if
+	# those 16 are all
 	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
 		git_get_head_hash($project), "--"
 		or die_error(undef, "Open git-rev-list failed");
@@ -2959,17 +2963,20 @@ sub git_summary {
 	close $fd;
 	git_print_header_div('shortlog');
 	git_shortlog_body(\@revlist, 0, 15, $refs,
+	                  $#revlist <=  15 ? undef :
 	                  $cgi->a({-href => href(action=>"shortlog")}, "..."));
 
 	if (@taglist) {
 		git_print_header_div('tags');
 		git_tags_body(\@taglist, 0, 15,
+		              $#taglist <=  15 ? undef :
 		              $cgi->a({-href => href(action=>"tags")}, "..."));
 	}
 
 	if (@headlist) {
 		git_print_header_div('heads');
 		git_heads_body(\@headlist, $head, 0, 15,
+		               $#headlist <= 15 ? undef :
 		               $cgi->a({-href => href(action=>"heads")}, "..."));
 	}
 

From 6d2fa7f1b489c65e677c18eda5c144dbc5d614ab Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Tue, 19 Dec 2006 10:53:08 -0500
Subject: [PATCH 242/330] index-pack usage of mmap() is unacceptably slower on
 many OSes other than Linux

It was reported by Randal L. Schwartz <merlyn@stonehenge.com> that
indexing the Linux repository ~150MB pack takes about an hour on OS x
while it's a minute on Linux.  It seems that the OS X mmap()
implementation is more than 2 orders of magnitude slower than the Linux
one.

Linus proposed a patch replacing mmap() with pread() bringing index-pack
performance on OS X in line with the Linux one.  The performances on
Linux also improved by a small margin.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 index-pack.c | 27 ++++++++++++++-------------
 1 file changed, 14 insertions(+), 13 deletions(-)

diff --git a/index-pack.c b/index-pack.c
index 6d6c92bf14..c05feae161 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -1,3 +1,8 @@
+#define _XOPEN_SOURCE 600
+#include <unistd.h>
+#include <sys/time.h>
+#include <signal.h>
+
 #include "cache.h"
 #include "delta.h"
 #include "pack.h"
@@ -6,8 +11,6 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree.h"
-#include <sys/time.h>
-#include <signal.h>
 
 static const char index_pack_usage[] =
 "git-index-pack [-v] [-o <index-file>] [{ ---keep | --keep=<msg> }] { <pack-file> | --stdin [--fix-thin] [<pack-file>] }";
@@ -87,7 +90,7 @@ static unsigned display_progress(unsigned n, unsigned total, unsigned last_pc)
 static unsigned char input_buffer[4096];
 static unsigned long input_offset, input_len, consumed_bytes;
 static SHA_CTX input_ctx;
-static int input_fd, output_fd, mmap_fd;
+static int input_fd, output_fd, pack_fd;
 
 /* Discard current buffer used content. */
 static void flush(void)
@@ -148,14 +151,14 @@ static const char *open_pack_file(const char *pack_name)
 			output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
 		if (output_fd < 0)
 			die("unable to create %s: %s\n", pack_name, strerror(errno));
-		mmap_fd = output_fd;
+		pack_fd = output_fd;
 	} else {
 		input_fd = open(pack_name, O_RDONLY);
 		if (input_fd < 0)
 			die("cannot open packfile '%s': %s",
 			    pack_name, strerror(errno));
 		output_fd = -1;
-		mmap_fd = input_fd;
+		pack_fd = input_fd;
 	}
 	SHA1_Init(&input_ctx);
 	return pack_name;
@@ -279,27 +282,25 @@ static void *get_data_from_pack(struct object_entry *obj)
 {
 	unsigned long from = obj[0].offset + obj[0].hdr_size;
 	unsigned long len = obj[1].offset - from;
-	unsigned pg_offset = from % getpagesize();
-	unsigned char *map, *data;
+	unsigned char *src, *data;
 	z_stream stream;
 	int st;
 
-	map = mmap(NULL, len + pg_offset, PROT_READ, MAP_PRIVATE,
-		   mmap_fd, from - pg_offset);
-	if (map == MAP_FAILED)
-		die("cannot mmap pack file: %s", strerror(errno));
+	src = xmalloc(len);
+	if (pread(pack_fd, src, len, from) != len)
+		die("cannot pread pack file: %s", strerror(errno));
 	data = xmalloc(obj->size);
 	memset(&stream, 0, sizeof(stream));
 	stream.next_out = data;
 	stream.avail_out = obj->size;
-	stream.next_in = map + pg_offset;
+	stream.next_in = src;
 	stream.avail_in = len;
 	inflateInit(&stream);
 	while ((st = inflate(&stream, Z_FINISH)) == Z_OK);
 	inflateEnd(&stream);
 	if (st != Z_STREAM_END || stream.total_out != obj->size)
 		die("serious inflate inconsistency");
-	munmap(map, len + pg_offset);
+	free(src);
 	return data;
 }
 

From 85023577a8f4b540aa64aa37f6f44578c0c305a3 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 14:34:12 -0800
Subject: [PATCH 243/330] simplify inclusion of system header files.

This is a mechanical clean-up of the way *.c files include
system header files.

 (1) sources under compat/, platform sha-1 implementations, and
     xdelta code are exempt from the following rules;

 (2) the first #include must be "git-compat-util.h" or one of
     our own header file that includes it first (e.g. config.h,
     builtin.h, pkt-line.h);

 (3) system headers that are included in "git-compat-util.h"
     need not be included in individual C source files.

 (4) "git-compat-util.h" does not have to include subsystem
     specific header files (e.g. expat.h).

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 archive-tar.c            |  1 -
 archive-zip.c            |  1 -
 blob.c                   |  1 -
 builtin-add.c            |  2 --
 builtin-apply.c          |  1 -
 builtin-archive.c        |  1 -
 builtin-blame.c          |  4 ----
 builtin-branch.c         |  2 +-
 builtin-for-each-ref.c   |  1 -
 builtin-grep.c           |  3 ---
 builtin-log.c            |  2 --
 builtin-ls-files.c       |  2 --
 builtin-mailinfo.c       |  9 ---------
 builtin-mailsplit.c      |  7 -------
 builtin-mv.c             |  2 --
 builtin-name-rev.c       |  1 -
 builtin-pack-objects.c   |  2 --
 builtin-repo-config.c    |  1 -
 builtin-runstatus.c      |  2 +-
 builtin-shortlog.c       |  1 -
 builtin-show-branch.c    |  2 --
 builtin-stripspace.c     |  3 ---
 builtin-tar-tree.c       |  1 -
 builtin-unpack-objects.c |  2 --
 builtin-upload-archive.c |  3 ---
 color.c                  |  5 +----
 compat/mmap.c            |  4 ----
 compat/setenv.c          |  3 +--
 compat/strlcpy.c         |  2 +-
 compat/unsetenv.c        |  3 +--
 config.c                 |  1 -
 connect.c                |  6 ------
 convert-objects.c        |  4 ----
 daemon.c                 | 16 +++-------------
 date.c                   |  3 ---
 diff-delta.c             |  5 +----
 diff.c                   |  3 ---
 diffcore-order.c         |  1 -
 diffcore-pickaxe.c       |  2 --
 dir.c                    |  3 ---
 entry.c                  |  2 --
 fetch-pack.c             |  1 -
 fetch.c                  |  3 +--
 fsck-objects.c           |  3 ---
 git-compat-util.h        | 25 ++++++++++++++++++++++++-
 git.c                    | 14 +-------------
 grep.c                   |  1 -
 help.c                   |  3 +--
 ident.c                  |  3 ---
 imap-send.c              |  7 -------
 index-pack.c             |  5 -----
 interpolate.c            |  2 --
 lockfile.c               |  1 -
 merge-base.c             |  1 -
 merge-index.c            |  4 ----
 merge-recursive.c        |  7 -------
 path-list.c              |  1 -
 path.c                   |  1 -
 receive-pack.c           |  1 -
 refs.c                   |  4 +---
 revision.c               |  1 -
 rsh.c                    |  6 +-----
 run-command.c            |  1 -
 ssh-upload.c             |  2 --
 strbuf.c                 |  4 +---
 test-date.c              |  3 ---
 test-delta.c             |  8 +-------
 tree.c                   |  1 -
 unpack-trees.c           |  2 --
 upload-pack.c            |  3 ---
 var.c                    |  3 ---
 wt-status.c              |  2 +-
 72 files changed, 42 insertions(+), 201 deletions(-)

diff --git a/archive-tar.c b/archive-tar.c
index ff0f6e2929..af47fdc955 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -1,7 +1,6 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include <time.h>
 #include "cache.h"
 #include "commit.h"
 #include "strbuf.h"
diff --git a/archive-zip.c b/archive-zip.c
index 36e922a1f2..f31b8ed823 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -1,7 +1,6 @@
 /*
  * Copyright (c) 2006 Rene Scharfe
  */
-#include <time.h>
 #include "cache.h"
 #include "commit.h"
 #include "blob.h"
diff --git a/blob.c b/blob.c
index d1af2e62f1..9776beac58 100644
--- a/blob.c
+++ b/blob.c
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "blob.h"
-#include <stdlib.h>
 
 const char *blob_type = "blob";
 
diff --git a/builtin-add.c b/builtin-add.c
index b3f920676a..c8a114fefb 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -3,8 +3,6 @@
  *
  * Copyright (C) 2006 Linus Torvalds
  */
-#include <fnmatch.h>
-
 #include "cache.h"
 #include "builtin.h"
 #include "dir.h"
diff --git a/builtin-apply.c b/builtin-apply.c
index 436d9e1880..1c35837068 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -6,7 +6,6 @@
  * This applies patches on top of some (arbitrary) version of the SCM.
  *
  */
-#include <fnmatch.h>
 #include "cache.h"
 #include "cache-tree.h"
 #include "quote.h"
diff --git a/builtin-archive.c b/builtin-archive.c
index a8a1f079bf..391cf43911 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -2,7 +2,6 @@
  * Copyright (c) 2006 Franck Bui-Huu
  * Copyright (c) 2006 Rene Scharfe
  */
-#include <time.h>
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
diff --git a/builtin-blame.c b/builtin-blame.c
index a250724463..9bf6ec951f 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -15,10 +15,6 @@
 #include "revision.h"
 #include "xdiff-interface.h"
 
-#include <time.h>
-#include <sys/time.h>
-#include <regex.h>
-
 static char blame_usage[] =
 "git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-L n,m] [-S <revs-file>] [-M] [-C] [-C] [commit] [--] file\n"
 "  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
diff --git a/builtin-branch.c b/builtin-branch.c
index 560309cb15..515330c155 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -5,8 +5,8 @@
  * Based on git-branch.sh by Junio C Hamano.
  */
 
-#include "color.h"
 #include "cache.h"
+#include "color.h"
 #include "refs.h"
 #include "commit.h"
 #include "builtin.h"
diff --git a/builtin-for-each-ref.c b/builtin-for-each-ref.c
index 227aa3cd7f..af72a12a57 100644
--- a/builtin-for-each-ref.c
+++ b/builtin-for-each-ref.c
@@ -6,7 +6,6 @@
 #include "tree.h"
 #include "blob.h"
 #include "quote.h"
-#include <fnmatch.h>
 
 /* Quoting styles */
 #define QUOTE_NONE 0
diff --git a/builtin-grep.c b/builtin-grep.c
index 9873e3d1db..3b1b1cbbfa 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -10,10 +10,7 @@
 #include "tag.h"
 #include "tree-walk.h"
 #include "builtin.h"
-#include <regex.h>
 #include "grep.h"
-#include <fnmatch.h>
-#include <sys/wait.h>
 
 /*
  * git grep pathspecs are somewhat different from diff-tree pathspecs;
diff --git a/builtin-log.c b/builtin-log.c
index 17014f70a2..8df3c1394a 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -11,8 +11,6 @@
 #include "log-tree.h"
 #include "builtin.h"
 #include "tag.h"
-#include <time.h>
-#include <sys/time.h>
 
 static int default_show_root = 1;
 
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index bc79ce40fc..21c2a6e2d9 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -5,8 +5,6 @@
  *
  * Copyright (C) Linus Torvalds, 2005
  */
-#include <fnmatch.h>
-
 #include "cache.h"
 #include "quote.h"
 #include "dir.h"
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index b8d7dbc0b7..e6472293d4 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -2,15 +2,6 @@
  * Another stupid program, this one parsing the headers of an
  * email to figure out authorship and subject
  */
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#ifndef NO_ICONV
-#include <iconv.h>
-#endif
-#include "git-compat-util.h"
 #include "cache.h"
 #include "builtin.h"
 
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index 91a699d34d..3bca855aae 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -4,13 +4,6 @@
  * It just splits a mbox into a list of files: "0001" "0002" ..
  * so you can process them further from there.
  */
-#include <unistd.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <stdio.h>
 #include "cache.h"
 #include "builtin.h"
 
diff --git a/builtin-mv.c b/builtin-mv.c
index d14a4a7f5c..737af350b8 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -3,8 +3,6 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#include <fnmatch.h>
-
 #include "cache.h"
 #include "builtin.h"
 #include "dir.h"
diff --git a/builtin-name-rev.c b/builtin-name-rev.c
index 618aa314d2..b4f15cc38a 100644
--- a/builtin-name-rev.c
+++ b/builtin-name-rev.c
@@ -1,4 +1,3 @@
-#include <stdlib.h>
 #include "builtin.h"
 #include "cache.h"
 #include "commit.h"
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index a2dc7d1d9d..807be8c3f8 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -12,8 +12,6 @@
 #include "diff.h"
 #include "revision.h"
 #include "list-objects.h"
-#include <sys/time.h>
-#include <signal.h>
 
 static const char pack_usage[] = "\
 git-pack-objects [{ -q | --progress | --all-progress }] \n\
diff --git a/builtin-repo-config.c b/builtin-repo-config.c
index a38099a63d..a7ab4cee58 100644
--- a/builtin-repo-config.c
+++ b/builtin-repo-config.c
@@ -1,6 +1,5 @@
 #include "builtin.h"
 #include "cache.h"
-#include <regex.h>
 
 static const char git_config_set_usage[] =
 "git-repo-config [ --global ] [ --bool | --int ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --list";
diff --git a/builtin-runstatus.c b/builtin-runstatus.c
index 0b63037dd0..4b489b1214 100644
--- a/builtin-runstatus.c
+++ b/builtin-runstatus.c
@@ -1,5 +1,5 @@
-#include "wt-status.h"
 #include "cache.h"
+#include "wt-status.h"
 
 extern int wt_status_use_color;
 
diff --git a/builtin-shortlog.c b/builtin-shortlog.c
index 3fc43dd7dd..edb40429ec 100644
--- a/builtin-shortlog.c
+++ b/builtin-shortlog.c
@@ -4,7 +4,6 @@
 #include "diff.h"
 #include "path-list.h"
 #include "revision.h"
-#include <string.h>
 
 static const char shortlog_usage[] =
 "git-shortlog [-n] [-s] [<commit-id>... ]";
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index a38ac34efb..b9d9781d4d 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -1,5 +1,3 @@
-#include <stdlib.h>
-#include <fnmatch.h>
 #include "cache.h"
 #include "commit.h"
 #include "refs.h"
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 09cc9108cd..f0d4d9e2d1 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -1,6 +1,3 @@
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
 #include "builtin.h"
 
 /*
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index 4d4cfec878..11e62fc141 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -1,7 +1,6 @@
 /*
  * Copyright (c) 2005, 2006 Rene Scharfe
  */
-#include <time.h>
 #include "cache.h"
 #include "commit.h"
 #include "tar.h"
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index e6d7574844..d351e02649 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -8,8 +8,6 @@
 #include "tag.h"
 #include "tree.h"
 
-#include <sys/time.h>
-
 static int dry_run, quiet, recover, has_errors;
 static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
 
diff --git a/builtin-upload-archive.c b/builtin-upload-archive.c
index 45c92e163c..e4156f8f48 100644
--- a/builtin-upload-archive.c
+++ b/builtin-upload-archive.c
@@ -1,9 +1,6 @@
 /*
  * Copyright (c) 2006 Franck Bui-Huu
  */
-#include <time.h>
-#include <sys/wait.h>
-#include <sys/poll.h>
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
diff --git a/color.c b/color.c
index d8c8399d59..09d82eec3d 100644
--- a/color.c
+++ b/color.c
@@ -1,8 +1,5 @@
-#include "color.h"
 #include "cache.h"
-#include "git-compat-util.h"
-
-#include <stdarg.h>
+#include "color.h"
 
 #define COLOR_RESET "\033[m"
 
diff --git a/compat/mmap.c b/compat/mmap.c
index a4d2e507f7..0fd46e793d 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -1,7 +1,3 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
 #include "../git-compat-util.h"
 
 void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
diff --git a/compat/setenv.c b/compat/setenv.c
index b7d7678598..3a22ea7b75 100644
--- a/compat/setenv.c
+++ b/compat/setenv.c
@@ -1,5 +1,4 @@
-#include <stdlib.h>
-#include <string.h>
+#include "../git-compat-util.h"
 
 int gitsetenv(const char *name, const char *value, int replace)
 {
diff --git a/compat/strlcpy.c b/compat/strlcpy.c
index b66856a3a5..4024c36030 100644
--- a/compat/strlcpy.c
+++ b/compat/strlcpy.c
@@ -1,4 +1,4 @@
-#include <string.h>
+#include "../git-compat-util.h"
 
 size_t gitstrlcpy(char *dest, const char *src, size_t size)
 {
diff --git a/compat/unsetenv.c b/compat/unsetenv.c
index 3a5e4ec04a..eb29f5e084 100644
--- a/compat/unsetenv.c
+++ b/compat/unsetenv.c
@@ -1,5 +1,4 @@
-#include <stdlib.h>
-#include <string.h>
+#include "../git-compat-util.h"
 
 void gitunsetenv (const char *name)
 {
diff --git a/config.c b/config.c
index 8cbdd178bd..e86b2328ab 100644
--- a/config.c
+++ b/config.c
@@ -6,7 +6,6 @@
  *
  */
 #include "cache.h"
-#include <regex.h>
 
 #define MAXNAME (256)
 
diff --git a/connect.c b/connect.c
index f7edba82c4..66daa11a57 100644
--- a/connect.c
+++ b/connect.c
@@ -3,12 +3,6 @@
 #include "pkt-line.h"
 #include "quote.h"
 #include "refs.h"
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <signal.h>
 
 static char *server_capabilities;
 
diff --git a/convert-objects.c b/convert-objects.c
index 881258311a..a630132985 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -1,7 +1,3 @@
-#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
-#define _GNU_SOURCE
-#include <time.h>
 #include "cache.h"
 #include "blob.h"
 #include "commit.h"
diff --git a/daemon.c b/daemon.c
index e66bb802da..b129b83e40 100644
--- a/daemon.c
+++ b/daemon.c
@@ -1,20 +1,10 @@
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <sys/poll.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <grp.h>
-#include <limits.h>
-#include "pkt-line.h"
 #include "cache.h"
+#include "pkt-line.h"
 #include "exec_cmd.h"
 #include "interpolate.h"
 
+#include <syslog.h>
+
 #ifndef HOST_NAME_MAX
 #define HOST_NAME_MAX 256
 #endif
diff --git a/date.c b/date.c
index 1825922732..7acb8cbd91 100644
--- a/date.c
+++ b/date.c
@@ -4,9 +4,6 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 
-#include <time.h>
-#include <sys/time.h>
-
 #include "cache.h"
 
 static time_t my_mktime(struct tm *tm)
diff --git a/diff-delta.c b/diff-delta.c
index fa16d06c8d..9f998d0a73 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -18,11 +18,8 @@
  *  licensing gets turned into GPLv2 within this project.
  */
 
-#include <stdlib.h>
-#include <string.h>
-#include "delta.h"
-
 #include "git-compat-util.h"
+#include "delta.h"
 
 /* maximum hash entry list for the same hash bucket */
 #define HASH_LIMIT 64
diff --git a/diff.c b/diff.c
index 6e6f2a765c..91f956b23b 100644
--- a/diff.c
+++ b/diff.c
@@ -1,9 +1,6 @@
 /*
  * Copyright (C) 2005 Junio C Hamano
  */
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
 #include "cache.h"
 #include "quote.h"
 #include "diff.h"
diff --git a/diffcore-order.c b/diffcore-order.c
index aef6da6044..7ad0946185 100644
--- a/diffcore-order.c
+++ b/diffcore-order.c
@@ -4,7 +4,6 @@
 #include "cache.h"
 #include "diff.h"
 #include "diffcore.h"
-#include <fnmatch.h>
 
 static char **order;
 static int order_cnt;
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index cfcce315ba..de44adabf0 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -5,8 +5,6 @@
 #include "diff.h"
 #include "diffcore.h"
 
-#include <regex.h>
-
 static unsigned int contains(struct diff_filespec *one,
 			     const char *needle, unsigned long len,
 			     regex_t *regexp)
diff --git a/dir.c b/dir.c
index e6a61ee913..16401d8017 100644
--- a/dir.c
+++ b/dir.c
@@ -5,9 +5,6 @@
  * Copyright (C) Linus Torvalds, 2005-2006
  *		 Junio Hamano, 2005-2006
  */
-#include <dirent.h>
-#include <fnmatch.h>
-
 #include "cache.h"
 #include "dir.h"
 
diff --git a/entry.c b/entry.c
index b2ea0efa82..88df713947 100644
--- a/entry.c
+++ b/entry.c
@@ -1,5 +1,3 @@
-#include <sys/types.h>
-#include <dirent.h>
 #include "cache.h"
 #include "blob.h"
 
diff --git a/fetch-pack.c b/fetch-pack.c
index 743eab7efa..92322cf4da 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -5,7 +5,6 @@
 #include "tag.h"
 #include "exec_cmd.h"
 #include "sideband.h"
-#include <sys/wait.h>
 
 static int keep_pack;
 static int quiet;
diff --git a/fetch.c b/fetch.c
index 663b4b2f42..f69be82f10 100644
--- a/fetch.c
+++ b/fetch.c
@@ -1,6 +1,5 @@
-#include "fetch.h"
-
 #include "cache.h"
+#include "fetch.h"
 #include "commit.h"
 #include "tree.h"
 #include "tree-walk.h"
diff --git a/fsck-objects.c b/fsck-objects.c
index 46b628cb94..409aea02b4 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -1,6 +1,3 @@
-#include <sys/types.h>
-#include <dirent.h>
-
 #include "cache.h"
 #include "commit.h"
 #include "tree.h"
diff --git a/git-compat-util.h b/git-compat-util.h
index 0272d043d0..bc296b3a45 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -11,6 +11,11 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
+#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
+#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
+#define _GNU_SOURCE
+#define _BSD_SOURCE
+
 #include <unistd.h>
 #include <stdio.h>
 #include <sys/stat.h>
@@ -22,9 +27,27 @@
 #include <errno.h>
 #include <limits.h>
 #include <sys/param.h>
-#include <netinet/in.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <fnmatch.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <regex.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <grp.h>
+
+#ifndef NO_ICONV
+#include <iconv.h>
+#endif
 
 /* On most systems <limits.h> would have given us this, but
  * not on some systems (e.g. GNU/Hurd).
diff --git a/git.c b/git.c
index 016ee8adb7..73cf4d4e01 100644
--- a/git.c
+++ b/git.c
@@ -1,20 +1,8 @@
-#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <limits.h>
-#include <stdarg.h>
-#include "git-compat-util.h"
+#include "builtin.h"
 #include "exec_cmd.h"
 #include "cache.h"
 #include "quote.h"
 
-#include "builtin.h"
-
 const char git_usage_string[] =
 	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate] [--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]";
 
diff --git a/grep.c b/grep.c
index 0fc078ec0a..fcc6762302 100644
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,4 @@
 #include "cache.h"
-#include <regex.h>
 #include "grep.h"
 
 void append_grep_pattern(struct grep_opt *opt, const char *pat,
diff --git a/help.c b/help.c
index 0824c25226..341b9e370e 100644
--- a/help.c
+++ b/help.c
@@ -3,12 +3,11 @@
  *
  * Builtin help-related commands (help, usage, version)
  */
-#include <sys/ioctl.h>
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "common-cmds.h"
-
+#include <sys/ioctl.h>
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
diff --git a/ident.c b/ident.c
index d7faba6a70..6ad8fedd60 100644
--- a/ident.c
+++ b/ident.c
@@ -7,9 +7,6 @@
  */
 #include "cache.h"
 
-#include <pwd.h>
-#include <netdb.h>
-
 static char git_default_date[50];
 
 static void copy_gecos(struct passwd *w, char *name, int sz)
diff --git a/imap-send.c b/imap-send.c
index a6a65680ee..894cbbdf53 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -24,13 +24,6 @@
 
 #include "cache.h"
 
-#include <assert.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <sys/socket.h>
-#include <netdb.h>
-
 typedef struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
diff --git a/index-pack.c b/index-pack.c
index c05feae161..43e007f823 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -1,8 +1,3 @@
-#define _XOPEN_SOURCE 600
-#include <unistd.h>
-#include <sys/time.h>
-#include <signal.h>
-
 #include "cache.h"
 #include "delta.h"
 #include "pack.h"
diff --git a/interpolate.c b/interpolate.c
index 5d9d1889f0..f992ef7753 100644
--- a/interpolate.c
+++ b/interpolate.c
@@ -2,8 +2,6 @@
  * Copyright 2006 Jon Loeliger
  */
 
-#include <string.h>
-
 #include "git-compat-util.h"
 #include "interpolate.h"
 
diff --git a/lockfile.c b/lockfile.c
index 2a2fea3cb6..261baff049 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -1,7 +1,6 @@
 /*
  * Copyright (c) 2005, Junio C Hamano
  */
-#include <signal.h>
 #include "cache.h"
 
 static struct lock_file *lock_file_list;
diff --git a/merge-base.c b/merge-base.c
index 009caf804b..385f4ba386 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -1,4 +1,3 @@
-#include <stdlib.h>
 #include "cache.h"
 #include "commit.h"
 
diff --git a/merge-index.c b/merge-index.c
index 646d090c58..a9983dd78a 100644
--- a/merge-index.c
+++ b/merge-index.c
@@ -1,7 +1,3 @@
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <signal.h>
-
 #include "cache.h"
 
 static const char *pgm;
diff --git a/merge-recursive.c b/merge-recursive.c
index 6dd6e2e5af..1de273ea1e 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3,13 +3,6 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
-#include <stdarg.h>
-#include <string.h>
-#include <assert.h>
-#include <sys/wait.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <time.h>
 #include "cache.h"
 #include "cache-tree.h"
 #include "commit.h"
diff --git a/path-list.c b/path-list.c
index f8800f8e66..caaa5cc57b 100644
--- a/path-list.c
+++ b/path-list.c
@@ -1,4 +1,3 @@
-#include <stdio.h>
 #include "cache.h"
 #include "path-list.h"
 
diff --git a/path.c b/path.c
index d2c076d7cb..066f621955 100644
--- a/path.c
+++ b/path.c
@@ -11,7 +11,6 @@
  * which is what it's designed for.
  */
 #include "cache.h"
-#include <pwd.h>
 
 static char bad_path[] = "/bad-path/";
 
diff --git a/receive-pack.c b/receive-pack.c
index 5e5510bc3d..59b682c03a 100644
--- a/receive-pack.c
+++ b/receive-pack.c
@@ -6,7 +6,6 @@
 #include "exec_cmd.h"
 #include "commit.h"
 #include "object.h"
-#include <sys/wait.h>
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
diff --git a/refs.c b/refs.c
index d911b9e860..a101ff3bf8 100644
--- a/refs.c
+++ b/refs.c
@@ -1,10 +1,8 @@
-#include "refs.h"
 #include "cache.h"
+#include "refs.h"
 #include "object.h"
 #include "tag.h"
 
-#include <errno.h>
-
 /* ISSYMREF=01 and ISPACKED=02 are public interfaces */
 #define REF_KNOWS_PEELED 04
 
diff --git a/revision.c b/revision.c
index 993bb668a2..7b6f91f83e 100644
--- a/revision.c
+++ b/revision.c
@@ -6,7 +6,6 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
-#include <regex.h>
 #include "grep.h"
 
 static char *path_name(struct name_path *path, const char *name)
diff --git a/rsh.c b/rsh.c
index f34409e1bc..5754a230e2 100644
--- a/rsh.c
+++ b/rsh.c
@@ -1,10 +1,6 @@
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-
+#include "cache.h"
 #include "rsh.h"
 #include "quote.h"
-#include "cache.h"
 
 #define COMMAND_SIZE 4096
 
diff --git a/run-command.c b/run-command.c
index 61908682b9..492ad3e64c 100644
--- a/run-command.c
+++ b/run-command.c
@@ -1,6 +1,5 @@
 #include "cache.h"
 #include "run-command.h"
-#include <sys/wait.h>
 #include "exec_cmd.h"
 
 int run_command_v_opt(int argc, const char **argv, int flags)
diff --git a/ssh-upload.c b/ssh-upload.c
index 20b15eab57..0b52ae15cb 100644
--- a/ssh-upload.c
+++ b/ssh-upload.c
@@ -12,8 +12,6 @@
 #include "rsh.h"
 #include "refs.h"
 
-#include <string.h>
-
 static unsigned char local_version = 1;
 static unsigned char remote_version;
 
diff --git a/strbuf.c b/strbuf.c
index 9d9d8bed91..7f14b0fb59 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -1,7 +1,5 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include "strbuf.h"
 #include "cache.h"
+#include "strbuf.h"
 
 void strbuf_init(struct strbuf *sb) {
 	sb->buf = NULL;
diff --git a/test-date.c b/test-date.c
index 93e802759f..62e8f2387a 100644
--- a/test-date.c
+++ b/test-date.c
@@ -1,6 +1,3 @@
-#include <stdio.h>
-#include <time.h>
-
 #include "cache.h"
 
 int main(int argc, char **argv)
diff --git a/test-delta.c b/test-delta.c
index 1be8ee0c72..795aa08377 100644
--- a/test-delta.c
+++ b/test-delta.c
@@ -8,13 +8,7 @@
  * published by the Free Software Foundation.
  */
 
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
+#include "git-compat-util.h"
 #include "delta.h"
 
 static const char usage[] =
diff --git a/tree.c b/tree.c
index ea386e5066..b6f02fecc4 100644
--- a/tree.c
+++ b/tree.c
@@ -4,7 +4,6 @@
 #include "commit.h"
 #include "tag.h"
 #include "tree-walk.h"
-#include <stdlib.h>
 
 const char *tree_type = "tree";
 
diff --git a/unpack-trees.c b/unpack-trees.c
index b8689ebc86..2e2232cbb0 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -1,5 +1,3 @@
-#include <signal.h>
-#include <sys/time.h>
 #include "cache.h"
 #include "dir.h"
 #include "tree.h"
diff --git a/upload-pack.c b/upload-pack.c
index 4572fff07c..32b06b2e66 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -1,6 +1,3 @@
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/poll.h>
 #include "cache.h"
 #include "refs.h"
 #include "pkt-line.h"
diff --git a/var.c b/var.c
index a57a33b81a..39977b949a 100644
--- a/var.c
+++ b/var.c
@@ -4,9 +4,6 @@
  * Copyright (C) Eric Biederman, 2005
  */
 #include "cache.h"
-#include <stdio.h>
-#include <errno.h>
-#include <string.h>
 
 static const char var_usage[] = "git-var [-l | <variable>]";
 
diff --git a/wt-status.c b/wt-status.c
index cface6ca88..db427384ff 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1,6 +1,6 @@
+#include "cache.h"
 #include "wt-status.h"
 #include "color.h"
-#include "cache.h"
 #include "object.h"
 #include "dir.h"
 #include "commit.h"

From 08a19d873c16aa6127bf4c40cf99a9dd19558a0c Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Wed, 20 Dec 2006 13:34:05 -0500
Subject: [PATCH 244/330] clarify some error messages wrt unknown object types

If ever new object types are added for future extensions then better
have current git version report them as "unknown" instead of
"corrupted".

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 index-pack.c | 2 +-
 sha1_file.c  | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/index-pack.c b/index-pack.c
index 43e007f823..5f6d128a83 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -266,7 +266,7 @@ static void *unpack_raw_entry(struct object_entry *obj, union delta_base *delta_
 	case OBJ_TAG:
 		break;
 	default:
-		bad_object(obj->offset, "bad object type %d", obj->type);
+		bad_object(obj->offset, "unknown object type %d", obj->type);
 	}
 	obj->hdr_size = consumed_bytes - obj->offset;
 
diff --git a/sha1_file.c b/sha1_file.c
index 63f416bb5a..1c4df5b73e 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1013,7 +1013,7 @@ void packed_object_info_detail(struct packed_git *p,
 	for (;;) {
 		switch (kind) {
 		default:
-			die("corrupted pack file %s containing object of kind %d",
+			die("pack %s contains unknown object type %d",
 			    p->pack_name, kind);
 		case OBJ_COMMIT:
 		case OBJ_TREE:
@@ -1063,7 +1063,7 @@ static int packed_object_info(struct packed_git *p, unsigned long offset,
 		strcpy(type, type_names[kind]);
 		break;
 	default:
-		die("corrupted pack file %s containing object of kind %d",
+		die("pack %s contains unknown object type %d",
 		    p->pack_name, kind);
 	}
 	if (sizep)

From 851a911024481f6759bce337b8dc50241070db81 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 20 Dec 2006 11:23:22 -0800
Subject: [PATCH 245/330] GIT 1.4.4.3

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 GIT-VERSION-GEN | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 4eac314f3a..bedc69271e 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.4.1.GIT
+DEF_VER=v1.4.4.3.GIT
 
 LF='
 '

From d5db6c9ee79cfa1794d75847b8376430f08b203f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 18:25:32 -0800
Subject: [PATCH 246/330] revision: --skip=<n>

This adds --skip=<n> option to revision traversal machinery.
Documentation and test were added by Robert Fitzsimons.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-rev-list.txt |  5 ++++
 revision.c                     | 46 ++++++++++++++++++++----------
 revision.h                     |  1 +
 t/t6005-rev-list-count.sh      | 51 ++++++++++++++++++++++++++++++++++
 4 files changed, 89 insertions(+), 14 deletions(-)
 create mode 100755 t/t6005-rev-list-count.sh

diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index ec43c0b3a8..9e0dcf8d3f 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git-rev-list' [ \--max-count=number ]
+	     [ \--skip=number ]
 	     [ \--max-age=timestamp ]
 	     [ \--min-age=timestamp ]
 	     [ \--sparse ]
@@ -139,6 +140,10 @@ limiting may be applied.
 
 	Limit the number of commits output.
 
+--skip='number'::
+
+	Skip 'number' commits before starting to show the commit output.
+
 --since='date', --after='date'::
 
 	Show commits more recent than a specific date.
diff --git a/revision.c b/revision.c
index 7b6f91f83e..5dc1e70260 100644
--- a/revision.c
+++ b/revision.c
@@ -523,6 +523,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
 	revs->prefix = prefix;
 	revs->max_age = -1;
 	revs->min_age = -1;
+	revs->skip_count = -1;
 	revs->max_count = -1;
 
 	revs->prune_fn = NULL;
@@ -759,6 +760,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				revs->max_count = atoi(arg + 12);
 				continue;
 			}
+			if (!strncmp(arg, "--skip=", 7)) {
+				revs->skip_count = atoi(arg + 7);
+				continue;
+			}
 			/* accept -<digit>, like traditional "head" */
 			if ((*arg == '-') && isdigit(arg[1])) {
 				revs->max_count = atoi(arg + 1);
@@ -1122,23 +1127,11 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
 			   commit->buffer, strlen(commit->buffer));
 }
 
-struct commit *get_revision(struct rev_info *revs)
+static struct commit *get_revision_1(struct rev_info *revs)
 {
-	struct commit_list *list = revs->commits;
-
-	if (!list)
+	if (!revs->commits)
 		return NULL;
 
-	/* Check the max_count ... */
-	switch (revs->max_count) {
-	case -1:
-		break;
-	case 0:
-		return NULL;
-	default:
-		revs->max_count--;
-	}
-
 	do {
 		struct commit_list *entry = revs->commits;
 		struct commit *commit = entry->item;
@@ -1205,3 +1198,28 @@ struct commit *get_revision(struct rev_info *revs)
 	} while (revs->commits);
 	return NULL;
 }
+
+struct commit *get_revision(struct rev_info *revs)
+{
+	struct commit *c = NULL;
+
+	if (0 < revs->skip_count) {
+		while ((c = get_revision_1(revs)) != NULL) {
+			if (revs->skip_count-- <= 0)
+				break;
+		}
+	}
+
+	/* Check the max_count ... */
+	switch (revs->max_count) {
+	case -1:
+		break;
+	case 0:
+		return NULL;
+	default:
+		revs->max_count--;
+	}
+	if (c)
+		return c;
+	return get_revision_1(revs);
+}
diff --git a/revision.h b/revision.h
index 3adab9590a..81f522c35d 100644
--- a/revision.h
+++ b/revision.h
@@ -75,6 +75,7 @@ struct rev_info {
 	struct grep_opt	*grep_filter;
 
 	/* special limits */
+	int skip_count;
 	int max_count;
 	unsigned long max_age;
 	unsigned long min_age;
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
new file mode 100755
index 0000000000..334fccf58c
--- /dev/null
+++ b/t/t6005-rev-list-count.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='git-rev-list --max-count and --skip test'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+    for n in 1 2 3 4 5 ; do \
+        echo $n > a ; \
+        git add a ; \
+        git commit -m "$n" ; \
+    done
+'
+
+test_expect_success 'no options' '
+    test $(git-rev-list HEAD | wc -l) = 5
+'
+
+test_expect_success '--max-count' '
+    test $(git-rev-list HEAD --max-count=0 | wc -l) = 0 &&
+    test $(git-rev-list HEAD --max-count=3 | wc -l) = 3 &&
+    test $(git-rev-list HEAD --max-count=5 | wc -l) = 5 &&
+    test $(git-rev-list HEAD --max-count=10 | wc -l) = 5
+'
+
+test_expect_success '--max-count all forms' '
+    test $(git-rev-list HEAD --max-count=1 | wc -l) = 1 &&
+    test $(git-rev-list HEAD -1 | wc -l) = 1 &&
+    test $(git-rev-list HEAD -n1 | wc -l) = 1 &&
+    test $(git-rev-list HEAD -n 1 | wc -l) = 1
+'
+
+test_expect_success '--skip' '
+    test $(git-rev-list HEAD --skip=0 | wc -l) = 5 &&
+    test $(git-rev-list HEAD --skip=3 | wc -l) = 2 &&
+    test $(git-rev-list HEAD --skip=5 | wc -l) = 0 &&
+    test $(git-rev-list HEAD --skip=10 | wc -l) = 0
+'
+
+test_expect_success '--skip --max-count' '
+    test $(git-rev-list HEAD --skip=0 --max-count=0 | wc -l) = 0 &&
+    test $(git-rev-list HEAD --skip=0 --max-count=10 | wc -l) = 5 &&
+    test $(git-rev-list HEAD --skip=3 --max-count=0 | wc -l) = 0 &&
+    test $(git-rev-list HEAD --skip=3 --max-count=1 | wc -l) = 1 &&
+    test $(git-rev-list HEAD --skip=3 --max-count=2 | wc -l) = 2 &&
+    test $(git-rev-list HEAD --skip=3 --max-count=10 | wc -l) = 2 &&
+    test $(git-rev-list HEAD --skip=5 --max-count=10 | wc -l) = 0 &&
+    test $(git-rev-list HEAD --skip=10 --max-count=10 | wc -l) = 0
+'
+
+test_done

From 93b0d86aafdd3e00de2c3c6ebb3c109b4571e1c4 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 20 Dec 2006 13:06:46 -0800
Subject: [PATCH 247/330] git-add: error out when given no arguments.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-add.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/builtin-add.c b/builtin-add.c
index c8a114fefb..f306f82b16 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -111,6 +111,11 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		}
 		usage(builtin_add_usage);
 	}
+	if (argc <= i) {
+		fprintf(stderr, "Nothing specified, nothing added.\n");
+		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
+		return 0;
+	}
 	pathspec = get_pathspec(prefix, argv + i);
 
 	fill_directory(&dir, pathspec);

From ec722203ee43beed8e4feac84af4307eb68f1ba3 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 13 Dec 2006 00:58:28 -0800
Subject: [PATCH 248/330] spurious .sp in manpages

This cherry-picks 7ef04350 that has been applied to the 'master'
to fix ".sp" in generated manpages.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/callouts.xsl | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/Documentation/callouts.xsl b/Documentation/callouts.xsl
index ad03755d8f..6a361a2136 100644
--- a/Documentation/callouts.xsl
+++ b/Documentation/callouts.xsl
@@ -13,4 +13,18 @@
 	<xsl:apply-templates/>
 	<xsl:text>.br&#10;</xsl:text>
 </xsl:template>
+
+<!-- sorry, this is not about callouts, but attempts to work around
+ spurious .sp at the tail of the line docbook stylesheets seem to add -->
+<xsl:template match="simpara">
+  <xsl:variable name="content">
+    <xsl:apply-templates/>
+  </xsl:variable>
+  <xsl:value-of select="normalize-space($content)"/>
+  <xsl:if test="not(ancestor::authorblurb) and
+                not(ancestor::personblurb)">
+    <xsl:text>&#10;&#10;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
 </xsl:stylesheet>

From 1d182bd5f4ec17f2f44ac0ee0d1bf6a25e98761e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 20 Dec 2006 13:45:55 -0800
Subject: [PATCH 249/330] compat/inet_ntop: do not use u_int

It is pointless.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 compat/inet_ntop.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/compat/inet_ntop.c b/compat/inet_ntop.c
index ec8c1bff53..4d7ab9d975 100644
--- a/compat/inet_ntop.c
+++ b/compat/inet_ntop.c
@@ -93,7 +93,7 @@ inet_ntop6(src, dst, size)
 	 */
 	char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
 	struct { int base, len; } best, cur;
-	u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
+	unsigned int words[NS_IN6ADDRSZ / NS_INT16SZ];
 	int i;
 
 	/*

From ce0545455a09113a8ece8f80c87ec61110569940 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 20 Dec 2006 14:41:54 -0800
Subject: [PATCH 250/330] diff documentation: mostly talk about <commit>

This corrects minor remaining bits that still talked about <tree-ish>;
the Porcelain users (as opposed to plumbers) are mostly interested in
commits so use <commit> consistently and keep a sentence that mentions
that <tree-ish> can be used in place of them.
---
 Documentation/git-diff.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 10fdf88dce..8977877b21 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -8,7 +8,7 @@ git-diff - Show changes between commits, commit and working tree, etc
 
 SYNOPSIS
 --------
-'git-diff' [ --diff-options ] <tree-ish>{0,2} [--] [<path>...]
+'git-diff' [ --diff-options ] <commit>{0,2} [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -26,7 +26,7 @@ tree and the index file, or the index file and the working tree.
 'git-diff' [--options] --cached [<commit>] [--] [<path>...]::
 
 	This form is to view the changes you staged for the next
-	commit relative to the named <tree-ish>.  Typically you
+	commit relative to the named <commit>.  Typically you
 	would want comparison with the latest commit, so if you
 	do not give <commit>, it defaults to HEAD.
 

From 2ff81662c2fb2679f841c09a28bd80c0a63011ac Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 01:18:16 -0800
Subject: [PATCH 251/330] add for_each_reflog_ent() iterator

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c | 25 +++++++++++++++++++++++++
 refs.h |  4 ++++
 2 files changed, 29 insertions(+)

diff --git a/refs.c b/refs.c
index a101ff3bf8..b67b1f011f 100644
--- a/refs.c
+++ b/refs.c
@@ -1091,3 +1091,28 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
 		logfile, show_rfc2822_date(date, tz));
 	return 0;
 }
+
+void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
+{
+	const char *logfile;
+	FILE *logfp;
+	char buf[1024];
+
+	logfile = git_path("logs/%s", ref);
+	logfp = fopen(logfile, "r");
+	if (!logfp)
+		return;
+	while (fgets(buf, sizeof(buf), logfp)) {
+		unsigned char osha1[20], nsha1[20];
+		int len;
+
+		/* old SP new SP name <email> SP time TAB msg LF */
+		len = strlen(buf);
+		if (len < 83 || buf[len-1] != '\n' ||
+		    get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
+		    get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ')
+			continue; /* corrupt? */
+		fn(osha1, nsha1, buf+82, cb_data);
+	}
+	fclose(logfp);
+}
diff --git a/refs.h b/refs.h
index 51aab1e6bf..de43cc768a 100644
--- a/refs.h
+++ b/refs.h
@@ -44,6 +44,10 @@ extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, cons
 /** Reads log for the value of ref during at_time. **/
 extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
 
+/* iterate over reflog entries */
+typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, char *, void *);
+void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
+
 /** Returns 0 if target has the right format for a ref. **/
 extern int check_ref_format(const char *target);
 

From 55dd55263b6d27aa8daa77bd69f5ea990b66c7a1 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 01:36:16 -0800
Subject: [PATCH 252/330] Protect commits recorded in reflog from pruning.

This teaches fsck-objects and prune to protect objects referred
to by reflog entries.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-prune.c | 16 ++++++++++++++++
 fsck-objects.c  | 22 ++++++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/builtin-prune.c b/builtin-prune.c
index 8591d28b8e..00a53b3647 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -181,12 +181,28 @@ static void walk_commit_list(struct rev_info *revs)
 	}
 }
 
+static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
+{
+	struct object *object;
+
+	object = parse_object(osha1);
+	if (object)
+		add_pending_object(&revs, object, "");
+	object = parse_object(nsha1);
+	if (object)
+		add_pending_object(&revs, object, "");
+	return 0;
+}
+
 static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *object = parse_object(sha1);
 	if (!object)
 		die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
 	add_pending_object(&revs, object, "");
+
+	for_each_reflog_ent(path, add_one_reflog_ent, NULL);
+
 	return 0;
 }
 
diff --git a/fsck-objects.c b/fsck-objects.c
index 409aea02b4..1cc3b399bc 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -399,6 +399,25 @@ static void fsck_dir(int i, char *path)
 
 static int default_refs;
 
+static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
+{
+	struct object *obj;
+
+	if (!is_null_sha1(osha1)) {
+		obj = lookup_object(osha1);
+		if (obj) {
+			obj->used = 1;
+			mark_reachable(obj, REACHABLE);
+		}
+	}
+	obj = lookup_object(nsha1);
+	if (obj) {
+		obj->used = 1;
+		mark_reachable(obj, REACHABLE);
+	}
+	return 0;
+}
+
 static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct object *obj;
@@ -416,6 +435,9 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
 	default_refs++;
 	obj->used = 1;
 	mark_reachable(obj, REACHABLE);
+
+	for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL);
+
 	return 0;
 }
 

From 63049292e083faf80e033eba4fa43efdbac3acad Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 17:25:28 -0800
Subject: [PATCH 253/330] Teach git-repack to preserve objects referred to by
 reflog entries.

This adds a new option --reflog to pack-objects and revision
machinery; do not bother documenting it for now, since this is
only useful for local repacking.

When the option is passed, objects reachable from reflog entries
are marked as interesting while computing the set of objects to
pack.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-pack-objects.c |  3 ++-
 git-repack.sh          |  2 +-
 revision.c             | 56 ++++++++++++++++++++++++++++++++++++------
 3 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 807be8c3f8..9e15beb3ba 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -17,7 +17,7 @@ static const char pack_usage[] = "\
 git-pack-objects [{ -q | --progress | --all-progress }] \n\
 	[--local] [--incremental] [--window=N] [--depth=N] \n\
 	[--no-reuse-delta] [--delta-base-offset] [--non-empty] \n\
-	[--revs [--unpacked | --all]*] [--stdout | base-name] \n\
+	[--revs [--unpacked | --all]*] [--reflog] [--stdout | base-name] \n\
 	[<ref-list | <object-list]";
 
 struct object_entry {
@@ -1575,6 +1575,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp("--unpacked", arg) ||
 		    !strncmp("--unpacked=", arg, 11) ||
+		    !strcmp("--reflog", arg) ||
 		    !strcmp("--all", arg)) {
 			use_internal_rev_list = 1;
 			if (ARRAY_SIZE(rp_av) - 1 <= rp_ac)
diff --git a/git-repack.sh b/git-repack.sh
index 067898f120..375434b1dc 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -62,7 +62,7 @@ case ",$all_into_one," in
 esac
 
 args="$args $local $quiet $no_reuse_delta$extra"
-name=$(git-pack-objects --non-empty --all $args </dev/null "$PACKTMP") ||
+name=$(git-pack-objects --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
 	exit 1
 if [ -z "$name" ]; then
 	echo Nothing new to pack.
diff --git a/revision.c b/revision.c
index 4dae5342eb..bda9d4dfa5 100644
--- a/revision.c
+++ b/revision.c
@@ -464,21 +464,59 @@ static void limit_list(struct rev_info *revs)
 	revs->commits = newlist;
 }
 
-static int all_flags;
-static struct rev_info *all_revs;
+struct all_refs_cb {
+	int all_flags;
+	struct rev_info *all_revs;
+	const char *name_for_errormsg;
+};
 
 static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-	struct object *object = get_reference(all_revs, path, sha1, all_flags);
-	add_pending_object(all_revs, object, "");
+	struct all_refs_cb *cb = cb_data;
+	struct object *object = get_reference(cb->all_revs, path, sha1,
+					      cb->all_flags);
+	add_pending_object(cb->all_revs, object, "");
 	return 0;
 }
 
 static void handle_all(struct rev_info *revs, unsigned flags)
 {
-	all_revs = revs;
-	all_flags = flags;
-	for_each_ref(handle_one_ref, NULL);
+	struct all_refs_cb cb;
+	cb.all_revs = revs;
+	cb.all_flags = flags;
+	for_each_ref(handle_one_ref, &cb);
+}
+
+static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *detail, void *cb_data)
+{
+	struct all_refs_cb *cb = cb_data;
+	struct object *object;
+
+	if (!is_null_sha1(osha1)) {
+		object = get_reference(cb->all_revs, cb->name_for_errormsg,
+				       osha1, cb->all_flags);
+		add_pending_object(cb->all_revs, object, "");
+	}
+	object = get_reference(cb->all_revs, cb->name_for_errormsg,
+			       nsha1, cb->all_flags);
+	add_pending_object(cb->all_revs, object, "");
+	return 0;
+}
+
+static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+	struct all_refs_cb *cb = cb_data;
+	cb->name_for_errormsg = path;
+	for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+	return 0;
+}
+
+static void handle_reflog(struct rev_info *revs, unsigned flags)
+{
+	struct all_refs_cb cb;
+	cb.all_revs = revs;
+	cb.all_flags = flags;
+	for_each_ref(handle_one_reflog, &cb);
 }
 
 static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -805,6 +843,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 				handle_all(revs, flags);
 				continue;
 			}
+			if (!strcmp(arg, "--reflog")) {
+				handle_reflog(revs, flags);
+				continue;
+			}
 			if (!strcmp(arg, "--not")) {
 				flags ^= UNINTERESTING;
 				continue;

From e29cb53a8b6aa1256221207b14a1c8ef72f69d9f Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 18 Dec 2006 22:07:45 -0800
Subject: [PATCH 254/330] reflog: fix warning message.

When ref@{N} is specified on a ref that has only M entries (M < N),
instead of saying the initial timestamp the reflog has, warn that
there is only M entries.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 refs.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/refs.c b/refs.c
index b67b1f011f..8b2a3c1378 100644
--- a/refs.c
+++ b/refs.c
@@ -1013,7 +1013,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
 {
 	const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec;
 	char *tz_c;
-	int logfd, tz;
+	int logfd, tz, reccnt = 0;
 	struct stat st;
 	unsigned long date;
 	unsigned char logged_sha1[20];
@@ -1031,6 +1031,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
 	lastrec = NULL;
 	rec = logend = logdata + st.st_size;
 	while (logdata < rec) {
+		reccnt++;
 		if (logdata < rec && *(rec-1) == '\n')
 			rec--;
 		lastgt = NULL;
@@ -1087,8 +1088,12 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
 	if (get_sha1_hex(logdata, sha1))
 		die("Log %s is corrupt.", logfile);
 	munmap((void*)logdata, st.st_size);
-	fprintf(stderr, "warning: Log %s only goes back to %s.\n",
-		logfile, show_rfc2822_date(date, tz));
+	if (at_time)
+		fprintf(stderr, "warning: Log %s only goes back to %s.\n",
+			logfile, show_rfc2822_date(date, tz));
+	else
+		fprintf(stderr, "warning: Log %s only has %d entries.\n",
+			logfile, reccnt);
 	return 0;
 }
 
@@ -1116,3 +1121,4 @@ void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
 	}
 	fclose(logfp);
 }
+

From 2ecd2bbcbe5335c1d9209b6ce28513e4e9d3491b Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 00:14:04 -0800
Subject: [PATCH 255/330] Move in_merge_bases() to commit.c

This reasonably useful function was hidden inside builtin-branch.c
---
 builtin-branch.c | 21 +--------------------
 commit.c         | 17 +++++++++++++++++
 commit.h         |  1 +
 3 files changed, 19 insertions(+), 20 deletions(-)

diff --git a/builtin-branch.c b/builtin-branch.c
index 903d5cf056..745ee04d6e 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -74,25 +74,6 @@ const char *branch_get_color(enum color_branch ix)
 	return "";
 }
 
-static int in_merge_bases(const unsigned char *sha1,
-			  struct commit *rev1,
-			  struct commit *rev2)
-{
-	struct commit_list *bases, *b;
-	int ret = 0;
-
-	bases = get_merge_bases(rev1, rev2, 1);
-	for (b = bases; b; b = b->next) {
-		if (!hashcmp(sha1, b->item->object.sha1)) {
-			ret = 1;
-			break;
-		}
-	}
-
-	free_commit_list(bases);
-	return ret;
-}
-
 static int delete_branches(int argc, const char **argv, int force, int kinds)
 {
 	struct commit *rev, *head_rev = head_rev;
@@ -153,7 +134,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
 		 */
 
 		if (!force &&
-		    !in_merge_bases(sha1, rev, head_rev)) {
+		    !in_merge_bases(rev, head_rev)) {
 			error("The branch '%s' is not a strict subset of "
 				"your current HEAD.\n"
 				"If you are sure you want to delete it, "
diff --git a/commit.c b/commit.c
index 289ef65eb1..3167ce62ac 100644
--- a/commit.c
+++ b/commit.c
@@ -1009,3 +1009,20 @@ struct commit_list *get_merge_bases(struct commit *one,
 	free(rslt);
 	return result;
 }
+
+int in_merge_bases(struct commit *rev1, struct commit *rev2)
+{
+	struct commit_list *bases, *b;
+	int ret = 0;
+
+	bases = get_merge_bases(rev1, rev2, 1);
+	for (b = bases; b; b = b->next) {
+		if (!hashcmp(rev1->object.sha1, b->item->object.sha1)) {
+			ret = 1;
+			break;
+		}
+	}
+
+	free_commit_list(bases);
+	return ret;
+}
diff --git a/commit.h b/commit.h
index fc13de9780..10eea9f26f 100644
--- a/commit.h
+++ b/commit.h
@@ -107,4 +107,5 @@ int read_graft_file(const char *graft_file);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
 
+int in_merge_bases(struct commit *rev1, struct commit *rev2);
 #endif /* COMMIT_H */

From 4264dc15e198bf9e9a2bb4ee897dd8e3eaabca47 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 19 Dec 2006 00:23:12 -0800
Subject: [PATCH 256/330] git reflog expire

This prepares a place to collect reflog management subcommands,
and implements "expire" action.

	$ git reflog expire --dry-run \
		--expire=4.weeks \
		--expire-unreachable=1.week \
		refs/heads/master

The expiration uses two timestamps: --expire and --expire-unreachable.
Entries older than expire time (defaults to 90 days), and entries older
than expire-unreachable time (defaults to 30 days) and records a commit
that has been rewound and made unreachable from the current tip of the
ref are removed from the reflog.

The parameter handling is still rough, but I think the
core logic for expiration is already sound.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile         |   1 +
 builtin-reflog.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++
 builtin.h        |   1 +
 git.c            |   1 +
 4 files changed, 178 insertions(+)
 create mode 100644 builtin-reflog.c

diff --git a/Makefile b/Makefile
index 76511045a8..d4d8590b6e 100644
--- a/Makefile
+++ b/Makefile
@@ -288,6 +288,7 @@ BUILTIN_OBJS = \
 	builtin-prune-packed.o \
 	builtin-push.o \
 	builtin-read-tree.o \
+	builtin-reflog.o \
 	builtin-repo-config.o \
 	builtin-rev-list.o \
 	builtin-rev-parse.o \
diff --git a/builtin-reflog.c b/builtin-reflog.c
new file mode 100644
index 0000000000..d4f73535c4
--- /dev/null
+++ b/builtin-reflog.c
@@ -0,0 +1,175 @@
+#include "cache.h"
+#include "builtin.h"
+#include "commit.h"
+#include "refs.h"
+#include "dir.h"
+#include <time.h>
+
+struct expire_reflog_cb {
+	FILE *newlog;
+	const char *ref;
+	struct commit *ref_commit;
+	unsigned long expire_total;
+	unsigned long expire_unreachable;
+};
+
+static int keep_entry(struct commit **it, unsigned char *sha1)
+{
+	*it = NULL;
+	if (is_null_sha1(sha1))
+		return 1;
+	*it = lookup_commit_reference_gently(sha1, 1);
+	return (*it != NULL);
+}
+
+static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+			     char *data, void *cb_data)
+{
+	struct expire_reflog_cb *cb = cb_data;
+	unsigned long timestamp;
+	char *cp, *ep;
+	struct commit *old, *new;
+
+	cp = strchr(data, '>');
+	if (!cp || *++cp != ' ')
+		goto prune;
+	timestamp = strtoul(cp, &ep, 10);
+	if (*ep != ' ')
+		goto prune;
+	if (timestamp < cb->expire_total)
+		goto prune;
+
+	if (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1))
+		goto prune;
+
+	if ((timestamp < cb->expire_unreachable) &&
+	    ((old && !in_merge_bases(old, cb->ref_commit)) ||
+	     (new && !in_merge_bases(new, cb->ref_commit))))
+		goto prune;
+
+	if (cb->newlog)
+		fprintf(cb->newlog, "%s %s %s",
+			sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
+	return 0;
+ prune:
+	if (!cb->newlog)
+		fprintf(stderr, "would prune %s", data);
+	return 0;
+}
+
+struct cmd_reflog_expire_cb {
+	int dry_run;
+	unsigned long expire_total;
+	unsigned long expire_unreachable;
+};
+
+static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
+{
+	struct cmd_reflog_expire_cb *cmd = cb_data;
+	struct expire_reflog_cb cb;
+	struct ref_lock *lock;
+	char *newlog_path = NULL;
+	int status = 0;
+
+	if (strncmp(ref, "refs/", 5))
+		return error("not a ref '%s'", ref);
+
+	memset(&cb, 0, sizeof(cb));
+	/* we take the lock for the ref itself to prevent it from
+	 * getting updated.
+	 */
+	lock = lock_ref_sha1(ref + 5, sha1);
+	if (!lock)
+		return error("cannot lock ref '%s'", ref);
+	if (!file_exists(lock->log_file))
+		goto finish;
+	if (!cmd->dry_run) {
+		newlog_path = xstrdup(git_path("logs/%s.lock", ref));
+		cb.newlog = fopen(newlog_path, "w");
+	}
+
+	cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
+	if (!cb.ref_commit) {
+		status = error("ref '%s' does not point at a commit", ref);
+		goto finish;
+	}
+	cb.ref = ref;
+	cb.expire_total = cmd->expire_total;
+	cb.expire_unreachable = cmd->expire_unreachable;
+	for_each_reflog_ent(ref, expire_reflog_ent, &cb);
+ finish:
+	if (cb.newlog) {
+		if (fclose(cb.newlog))
+			status |= error("%s: %s", strerror(errno),
+					newlog_path);
+		if (rename(newlog_path, lock->log_file)) {
+			status |= error("cannot rename %s to %s",
+					newlog_path, lock->log_file);
+			unlink(newlog_path);
+		}
+	}
+	free(newlog_path);
+	unlock_ref(lock);
+	return status;
+}
+
+static const char reflog_expire_usage[] =
+"git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
+
+static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
+{
+	struct cmd_reflog_expire_cb cb;
+	unsigned long now = time(NULL);
+	int i, status, do_all;
+
+	save_commit_buffer = 0;
+	do_all = status = 0;
+	memset(&cb, 0, sizeof(cb));
+	cb.expire_total = now - 90 * 24 * 3600;
+	cb.expire_unreachable = now - 30 * 24 * 3600;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
+			cb.dry_run = 1;
+		else if (!strncmp(arg, "--expire=", 9))
+			cb.expire_total = approxidate(arg + 9);
+		else if (!strncmp(arg, "--expire-unreachable=", 21))
+			cb.expire_unreachable = approxidate(arg + 21);
+		else if (!strcmp(arg, "--all"))
+			do_all = 1;
+		else if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		else if (arg[0] == '-')
+			usage(reflog_expire_usage);
+		else
+			break;
+	}
+	if (do_all)
+		status |= for_each_ref(expire_reflog, &cb);
+	while (i < argc) {
+		const char *ref = argv[i++];
+		unsigned char sha1[20];
+		if (!resolve_ref(ref, sha1, 1, NULL)) {
+			status |= error("%s points nowhere!", ref);
+			continue;
+		}
+		status |= expire_reflog(ref, sha1, 0, &cb);
+	}
+	return status;
+}
+
+static const char reflog_usage[] =
+"git-reflog (expire | ...)";
+
+int cmd_reflog(int argc, const char **argv, const char *prefix)
+{
+	if (argc < 2)
+		usage(reflog_usage);
+	else if (!strcmp(argv[1], "expire"))
+		return cmd_reflog_expire(argc - 1, argv + 1, prefix);
+	else
+		usage(reflog_usage);
+}
diff --git a/builtin.h b/builtin.h
index 08519e7c82..fdc0907eca 100644
--- a/builtin.h
+++ b/builtin.h
@@ -51,6 +51,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);
 extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index 73cf4d4e01..5822296e6e 100644
--- a/git.c
+++ b/git.c
@@ -244,6 +244,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "prune-packed", cmd_prune_packed, RUN_SETUP },
 		{ "push", cmd_push, RUN_SETUP },
 		{ "read-tree", cmd_read_tree, RUN_SETUP },
+		{ "reflog", cmd_reflog, RUN_SETUP },
 		{ "repo-config", cmd_repo_config },
 		{ "rev-list", cmd_rev_list, RUN_SETUP },
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },

From c902c9a608c1b727160d6fbb04fa363d9418cd00 Mon Sep 17 00:00:00 2001
From: Terje Sten Bjerkseth <terje@bjerkseth.org>
Date: Wed, 20 Dec 2006 17:32:21 -0800
Subject: [PATCH 257/330] Fix system header problems on Mac OS X

For Mac OS X 10.4, _XOPEN_SOURCE defines _POSIX_C_SOURCE which
hides many symbols from the program.

Breakage noticed and initial analysis provided by Randal
L. Schwartz.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-compat-util.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index bc296b3a45..41fa7f67bc 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -11,8 +11,10 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
+#ifndef __APPLE_CC__
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
+#endif
 #define _GNU_SOURCE
 #define _BSD_SOURCE
 

From 7dda22e3174dd6e4b8b2f60fb0ad1e8f1be912ad Mon Sep 17 00:00:00 2001
From: Quy Tonthat <qtonthat@gmail.com>
Date: Thu, 21 Dec 2006 15:18:07 +1100
Subject: [PATCH 258/330] Documentation/git-branch: new -r to delete
 remote-tracking branches.

Signed-off-by: Quy Tonthat <qtonthat@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-branch.txt | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 71417feba8..c464bd2fda 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -11,7 +11,7 @@ SYNOPSIS
 'git-branch' [-r | -a] [-v [--abbrev=<length>]]
 'git-branch' [-l] [-f] <branchname> [<start-point>]
 'git-branch' (-m | -M) [<oldbranch>] <newbranch>
-'git-branch' (-d | -D) <branchname>...
+'git-branch' (-d | -D) [-r] <branchname>...
 
 DESCRIPTION
 -----------
@@ -33,7 +33,8 @@ to happen.
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
-has a ref log then the ref log will also be deleted.
+has a ref log then the ref log will also be deleted. Use -r together with -d
+to delete remote-tracking branches.
 
 
 OPTIONS
@@ -60,7 +61,7 @@ OPTIONS
 	Move/rename a branch even if the new branchname already exists.
 
 -r::
-	List the remote-tracking branches.
+	List or delete (if used with -d) the remote-tracking branches.
 
 -a::
 	List both remote-tracking branches and local branches.
@@ -111,10 +112,12 @@ Delete unneeded branch::
 ------------
 $ git clone git://git.kernel.org/.../git.git my.git
 $ cd my.git
-$ git branch -D todo    <1>
+$ git branch -d -r todo html man   <1>
+$ git branch -D test               <2>
 ------------
 +
-<1> delete todo branch even if the "master" branch does not have all
+<1> delete remote-tracking branches "todo", "html", "man"
+<2> delete "test" branch even if the "master" branch does not have all
 commits from todo branch.
 
 

From e6d40d65df07059fc655fabe62fa5b575ead7815 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Fri, 22 Dec 2006 03:20:11 +0100
Subject: [PATCH 259/330] diff --check: fix off by one error

When parsing the diff line starting with '@@', the line number of the
'+' file is parsed. For the subsequent line parses, the line number
should therefore be incremented after the parse, not before it.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 diff.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/diff.c b/diff.c
index 33153787b8..b003c00016 100644
--- a/diff.c
+++ b/diff.c
@@ -825,8 +825,6 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
 	if (line[0] == '+') {
 		int i, spaces = 0;
 
-		data->lineno++;
-
 		/* check space before tab */
 		for (i = 1; i < len && (line[i] == ' ' || line[i] == '\t'); i++)
 			if (line[i] == ' ')
@@ -841,6 +839,8 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
 		if (isspace(line[len - 1]))
 			printf("%s:%d: white space at end: %.*s\n",
 				data->filename, data->lineno, (int)len, line);
+
+		data->lineno++;
 	} else if (line[0] == ' ')
 		data->lineno++;
 	else if (line[0] == '@') {

From caba139d432c420f853c352987cf29cefafc2b84 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Fri, 22 Dec 2006 03:20:55 +0100
Subject: [PATCH 260/330] Use git-merge-file in git-merge-one-file, too

Would you believe? I edited git-merge-one-file (note the missing ".sh"!)
when I submitted the patch which became commit e2b7008752...

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge-one-file.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index c49e4c65af..7d62d7902c 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -104,7 +104,7 @@ case "${1:-.}${2:-.}${3:-.}" in
 	# Be careful for funny filename such as "-L" in "$4", which
 	# would confuse "merge" greatly.
 	src1=`git-unpack-file $2`
-	merge "$src1" "$orig" "$src2"
+	git-merge-file "$src1" "$orig" "$src2"
 	ret=$?
 
 	# Create the working tree file, using "our tree" version from the

From 64646d11773774193093df96020d3fcb0df4a270 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 21 Dec 2006 21:30:17 -0500
Subject: [PATCH 261/330] Suggest 'add' in am/revert/cherry-pick.

Now that we have decided to make 'add' behave like 'update-index'
(and therefore fully classify update-index as strictly plumbing)
the am/revert/cherry-pick family of commands should not steer the
user towards update-index.  Instead send them to the command they
probably already know, 'add'.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-am.sh     | 4 ++--
 git-revert.sh | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-am.sh b/git-am.sh
index 5df6787a3f..0126a77b92 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -401,14 +401,14 @@ do
 		changed="$(git-diff-index --cached --name-only HEAD)"
 		if test '' = "$changed"
 		then
-			echo "No changes - did you forget update-index?"
+			echo "No changes - did you forget to use 'git add'?"
 			stop_here_user_resolve $this
 		fi
 		unmerged=$(git-ls-files -u)
 		if test -n "$unmerged"
 		then
 			echo "You still have unmerged paths in your index"
-			echo "did you forget update-index?"
+			echo "did you forget to use 'git add'?"
 			stop_here_user_resolve $this
 		fi
 		apply_status=0
diff --git a/git-revert.sh b/git-revert.sh
index 6eab3c72df..50cc47b063 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -155,7 +155,7 @@ Conflicts:
 		uniq
 	    } >>"$GIT_DIR/MERGE_MSG"
 	    echo >&2 "Automatic $me failed.  After resolving the conflicts,"
-	    echo >&2 "mark the corrected paths with 'git-update-index <paths>'"
+	    echo >&2 "mark the corrected paths with 'git-add <paths>'"
 	    echo >&2 "and commit the result."
 	    case "$me" in
 	    cherry-pick)

From 9e11554917d391c18e043bc0b38ee0377e69568e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 21 Dec 2006 22:10:56 -0800
Subject: [PATCH 262/330] Revert "git-pull: refuse default merge without
 branch.*.merge"

This reverts commit a71fb0a1412c82405f078fb536797d3f5de68d53.

The logic to decide when to refuse to use the default "first set of
refs fetched" for merge was utterly bogus.

In a repository that happily worked correctly without any of the
per-branch configuration crap did not have (and did not have to
have) any branch.<current>.merge.  With that broken commit, pulling
from origin no longer would work.
---
 git-parse-remote.sh | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index ea7511e8a0..871c08f097 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -141,8 +141,7 @@ canon_refs_list_for_fetch () {
 			curr_branch=$(git-symbolic-ref HEAD | \
 			    sed -e 's|^refs/heads/||')
 			merge_branches=$(git-repo-config \
-			    --get-all "branch.${curr_branch}.merge") ||
-			merge_branches=.this.would.never.match.any.ref.
+			    --get-all "branch.${curr_branch}.merge")
 		fi
 		set x $(expand_refs_wildcard "$@")
 		shift

From f79c73ce9ce5d8bd1b4abaf3fc384aa8daa169ef Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 21 Dec 2006 15:13:02 +0100
Subject: [PATCH 263/330] git-tag: support -F <file> option

This imitates the behaviour of git-commit.

Noticed by Han-Wen Nienhuys.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-tag.txt |  6 +++++-
 git-tag.sh                | 11 +++++++++++
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 45476c2e41..48b82b86f8 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -9,7 +9,8 @@ git-tag - Create a tag object signed with GPG
 SYNOPSIS
 --------
 [verse]
-'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
+'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg> | -F <file>]
+	 <name> [<head>]
 'git-tag' -l [<pattern>]
 
 DESCRIPTION
@@ -60,6 +61,9 @@ OPTIONS
 -m <msg>::
 	Use the given tag message (instead of prompting)
 
+-F <file>::
+	Take the tag message from the given file.  Use '-' to
+	read the message from the standard input.
 
 Author
 ------
diff --git a/git-tag.sh b/git-tag.sh
index d53f94cd9c..36cd6aa256 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -45,6 +45,17 @@ do
 	    message_given=1
 	fi
 	;;
+    -F)
+	annotate=1
+	shift
+	if test "$#" = "0"; then
+	    die "error: option -F needs an argument"
+	    exit 2
+	else
+	    message="$(cat "$1")"
+	    message_given=1
+	fi
+	;;
     -u)
 	annotate=1
 	signed=1

From 95f2fb7d9f84779bf036bb748e245c91bc572840 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 21 Dec 2006 15:26:35 +0100
Subject: [PATCH 264/330] git-reset --hard: tell the user what the HEAD was
 reset to

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-reset.sh | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/git-reset.sh b/git-reset.sh
index 8d95e3748d..2379db082f 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -86,7 +86,12 @@ update_ref_status=$?
 
 case "$reset_type" in
 --hard )
-	;; # Nothing else to do
+	test $update_ref_status = 0 && {
+		echo -n "HEAD is now at "
+		GIT_PAGER= git log --max-count=1 --pretty=oneline \
+			--abbrev-commit HEAD
+	}
+	;;
 --soft )
 	;; # Nothing else to do
 --mixed )

From d41cb273d313a21979b05dba57d0c6b565b28ba6 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 21 Dec 2006 22:39:09 -0800
Subject: [PATCH 265/330] parse-remote: mark all refs not for merge only when
 fetching more than one

An earlier commit a71fb0a1 implemented much requested safety
valve to refuse "git pull" or "git pull origin" without explicit
refspecs from using the first set of remote refs obtained by
reading .git/remotes/origin file or branch.*.fetch configuration
variables to create a merge.  The argument was that while on a
branch different from the default branch, it is often wrong to
merge the default remote ref suitable for merging into the master.

That is fine as a theory.  But many repositories already in use
by people in the real world do not have any of the per branch
configuration crap.  They did not need it, and they do not need
it now.  Merging with the first remote ref listed was just fine,
because they had only one ref (e.g. 'master' from linux-2.6.git)
anyway.

So this changes the safety valve to be a lot looser.  When "git
fetch" gets only one remote branch, the irritating warning would
not trigger anymore.

I think we could also make the warning trigger when branch.*.merge
is not specified for the current branch, but is for some other
branch.  That is for another commit.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index 871c08f097..f163821d03 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -136,6 +136,8 @@ canon_refs_list_for_fetch () {
 	if test "$1" = "-d"
 	then
 		shift ; remote="$1" ; shift
+		set x $(expand_refs_wildcard "$@")
+		shift
 		if test "$remote" = "$(get_default_remote)"
 		then
 			curr_branch=$(git-symbolic-ref HEAD | \
@@ -143,8 +145,13 @@ canon_refs_list_for_fetch () {
 			merge_branches=$(git-repo-config \
 			    --get-all "branch.${curr_branch}.merge")
 		fi
-		set x $(expand_refs_wildcard "$@")
-		shift
+		# If we are fetching only one branch, then first branch
+		# is the only thing that makes sense to merge anyway,
+		# so there is no point refusing that traditional rule.
+		if test $# != 1 && test "z$merge_branches" = z
+		then
+			merge_branches=..this..would..never..match..
+		fi
 	fi
 	for ref
 	do

From a01c9c28a576fbf3cd7caa4423dae4858a5ae6f5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 21 Dec 2006 22:51:42 -0800
Subject: [PATCH 266/330] _XOPEN_SOURCE problem also exists on FreeBSD

Suggested by Rocco Rutte, Marco Roeland and others.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-compat-util.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index 41fa7f67bc..c66e0a4247 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -11,7 +11,7 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
-#ifndef __APPLE_CC__
+#if !defined(__APPLE__) && !defined(__FreeBSD)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif

From 51dcaa9686d9ff591e9d051e6090d8529bc263a5 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 21 Dec 2006 19:48:12 -0500
Subject: [PATCH 267/330] Rename imap-send's internal info/warn functions.

Because I am about to introduce a global warn() function (much
like the global error function) this global declaration would
conflict with the one supplied by imap-send.  Further since
imap-send's warn function output depends on its Quiet setting
we cannot simply remove its internal definition and use the
forthcoming global one.

So refactor warn() -> imap_warn() and info() -> imap_info()
(the latter was done just to be consistent in naming).

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 imap-send.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index 894cbbdf53..ad91858bc4 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -96,8 +96,8 @@ typedef struct {
 
 static int Verbose, Quiet;
 
-static void info( const char *, ... );
-static void warn( const char *, ... );
+static void imap_info( const char *, ... );
+static void imap_warn( const char *, ... );
 
 static char *next_arg( char ** );
 
@@ -297,7 +297,7 @@ buffer_gets( buffer_t * b, char **s )
 }
 
 static void
-info( const char *msg, ... )
+imap_info( const char *msg, ... )
 {
 	va_list va;
 
@@ -310,7 +310,7 @@ info( const char *msg, ... )
 }
 
 static void
-warn( const char *msg, ... )
+imap_warn( const char *msg, ... )
 {
 	va_list va;
 
@@ -903,7 +903,7 @@ imap_open_store( imap_server_conf_t *srvc )
 	/* open connection to IMAP server */
 
 	if (srvc->tunnel) {
-		info( "Starting tunnel '%s'... ", srvc->tunnel );
+		imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
 
 		if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
 			perror( "socketpair" );
@@ -926,31 +926,31 @@ imap_open_store( imap_server_conf_t *srvc )
 
 		imap->buf.sock.fd = a[1];
 
-		info( "ok\n" );
+		imap_info( "ok\n" );
 	} else {
 		memset( &addr, 0, sizeof(addr) );
 		addr.sin_port = htons( srvc->port );
 		addr.sin_family = AF_INET;
 
-		info( "Resolving %s... ", srvc->host );
+		imap_info( "Resolving %s... ", srvc->host );
 		he = gethostbyname( srvc->host );
 		if (!he) {
 			perror( "gethostbyname" );
 			goto bail;
 		}
-		info( "ok\n" );
+		imap_info( "ok\n" );
 
 		addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
 
 		s = socket( PF_INET, SOCK_STREAM, 0 );
 
-		info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
+		imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
 		if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
 			close( s );
 			perror( "connect" );
 			goto bail;
 		}
-		info( "ok\n" );
+		imap_info( "ok\n" );
 
 		imap->buf.sock.fd = s;
 
@@ -979,7 +979,7 @@ imap_open_store( imap_server_conf_t *srvc )
 
 	if (!preauth) {
 
-		info ("Logging in...\n");
+		imap_info ("Logging in...\n");
 		if (!srvc->user) {
 			fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
 			goto bail;
@@ -1006,7 +1006,7 @@ imap_open_store( imap_server_conf_t *srvc )
 			fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
 			goto bail;
 		}
-		warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
+		imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
 		if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
 			fprintf( stderr, "IMAP error: LOGIN failed\n" );
 			goto bail;

From fa39b6b5b11f9b580c515a7b4a8e4eb8eaa79b75 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 21 Dec 2006 19:48:32 -0500
Subject: [PATCH 268/330] Introduce a global level warn() function.

Like the existing error() function the new warn() function can be
used to describe a situation that probably should not be occuring,
but which the user (and Git) can continue to work around without
running into too many problems.

An example situation is a bad commit SHA1 found in a reflog.
Attempting to read this record out of the reflog isn't really an
error as we have skipped over it in the past.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-compat-util.h |  2 ++
 usage.c           | 19 +++++++++++++++++++
 2 files changed, 21 insertions(+)

diff --git a/git-compat-util.h b/git-compat-util.h
index c66e0a4247..a55b923894 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -71,10 +71,12 @@
 extern void usage(const char *err) NORETURN;
 extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
+extern void warn(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
 extern void set_usage_routine(void (*routine)(const char *err) NORETURN);
 extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);
 extern void set_error_routine(void (*routine)(const char *err, va_list params));
+extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
 
 #ifdef NO_MMAP
 
diff --git a/usage.c b/usage.c
index 52c2e96056..4dc5c77633 100644
--- a/usage.c
+++ b/usage.c
@@ -29,12 +29,17 @@ static void error_builtin(const char *err, va_list params)
 	report("error: ", err, params);
 }
 
+static void warn_builtin(const char *warn, va_list params)
+{
+	report("warning: ", warn, params);
+}
 
 /* If we are in a dlopen()ed .so write to a global variable would segfault
  * (ugh), so keep things static. */
 static void (*usage_routine)(const char *err) NORETURN = usage_builtin;
 static void (*die_routine)(const char *err, va_list params) NORETURN = die_builtin;
 static void (*error_routine)(const char *err, va_list params) = error_builtin;
+static void (*warn_routine)(const char *err, va_list params) = warn_builtin;
 
 void set_usage_routine(void (*routine)(const char *err) NORETURN)
 {
@@ -51,6 +56,11 @@ void set_error_routine(void (*routine)(const char *err, va_list params))
 	error_routine = routine;
 }
 
+void set_warn_routine(void (*routine)(const char *warn, va_list params))
+{
+	warn_routine = routine;
+}
+
 
 void usage(const char *err)
 {
@@ -75,3 +85,12 @@ int error(const char *err, ...)
 	va_end(params);
 	return -1;
 }
+
+void warn(const char *warn, ...)
+{
+	va_list params;
+
+	va_start(params, warn);
+	warn_routine(warn, params);
+	va_end(params);
+}

From 71b03b42c637d884295b48d4f5d830a5f5f3578b Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Thu, 21 Dec 2006 19:49:06 -0500
Subject: [PATCH 269/330] Don't crash during repack of a reflog with pruned
 commits.

If the user has been using reflog for a long time (e.g. since its
introduction) then it is very likely that an existing branch's
reflog may still mention commits which have long since been pruned
out of the repository.

Rather than aborting with a very useless error message during
git-repack, pack as many valid commits as we can get from the
reflog and let the user know that the branch's reflog contains
already pruned commits.  A future 'git reflog expire' (or whatever
it finally winds up being called) can then be performed to expunge
those reflog entries.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 revision.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/revision.c b/revision.c
index bda9d4dfa5..4f6de2dfdd 100644
--- a/revision.c
+++ b/revision.c
@@ -466,6 +466,7 @@ static void limit_list(struct rev_info *revs)
 
 struct all_refs_cb {
 	int all_flags;
+	int warned_bad_reflog;
 	struct rev_info *all_revs;
 	const char *name_for_errormsg;
 };
@@ -487,25 +488,34 @@ static void handle_all(struct rev_info *revs, unsigned flags)
 	for_each_ref(handle_one_ref, &cb);
 }
 
-static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *detail, void *cb_data)
+static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
 {
 	struct all_refs_cb *cb = cb_data;
-	struct object *object;
-
-	if (!is_null_sha1(osha1)) {
-		object = get_reference(cb->all_revs, cb->name_for_errormsg,
-				       osha1, cb->all_flags);
-		add_pending_object(cb->all_revs, object, "");
+	if (!is_null_sha1(sha1)) {
+		struct object *o = parse_object(sha1);
+		if (o) {
+			o->flags |= cb->all_flags;
+			add_pending_object(cb->all_revs, o, "");
+		}
+		else if (!cb->warned_bad_reflog) {
+			warn("reflog of '%s' references pruned commits",
+				cb->name_for_errormsg);
+			cb->warned_bad_reflog = 1;
+		}
 	}
-	object = get_reference(cb->all_revs, cb->name_for_errormsg,
-			       nsha1, cb->all_flags);
-	add_pending_object(cb->all_revs, object, "");
+}
+
+static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *detail, void *cb_data)
+{
+	handle_one_reflog_commit(osha1, cb_data);
+	handle_one_reflog_commit(nsha1, cb_data);
 	return 0;
 }
 
 static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
 	struct all_refs_cb *cb = cb_data;
+	cb->warned_bad_reflog = 0;
 	cb->name_for_errormsg = path;
 	for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
 	return 0;

From 7cab5883ffd643916d0322a1d3360a79ccf6f653 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 20 Dec 2006 17:37:07 +0100
Subject: [PATCH 270/330] move read_mmfile() into xdiff-interface

read_file() was a useful function if you want to work with the xdiff stuff,
so it was renamed and put into a more central place.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-merge-file.c | 20 ++------------------
 xdiff-interface.c    | 19 +++++++++++++++++++
 xdiff-interface.h    |  1 +
 3 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/builtin-merge-file.c b/builtin-merge-file.c
index 6c4c3a3513..9135773908 100644
--- a/builtin-merge-file.c
+++ b/builtin-merge-file.c
@@ -1,26 +1,10 @@
 #include "cache.h"
 #include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
 
 static const char merge_file_usage[] =
 "git merge-file [-p | --stdout] [-q | --quiet] [-L name1 [-L orig [-L name2]]] file1 orig_file file2";
 
-static int read_file(mmfile_t *ptr, const char *filename)
-{
-	struct stat st;
-	FILE *f;
-
-	if (stat(filename, &st))
-		return error("Could not stat %s", filename);
-	if ((f = fopen(filename, "rb")) == NULL)
-		return error("Could not open %s", filename);
-	ptr->ptr = xmalloc(st.st_size);
-	if (fread(ptr->ptr, st.st_size, 1, f) != 1)
-		return error("Could not read %s", filename);
-	fclose(f);
-	ptr->size = st.st_size;
-	return 0;
-}
-
 int cmd_merge_file(int argc, char **argv, char **envp)
 {
 	char *names[3];
@@ -53,7 +37,7 @@ int cmd_merge_file(int argc, char **argv, char **envp)
 		names[i] = argv[i + 1];
 
 	for (i = 0; i < 3; i++)
-		if (read_file(mmfs + i, argv[i + 1]))
+		if (read_mmfile(mmfs + i, argv[i + 1]))
 			return -1;
 
 	ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2],
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 08602f5221..6c1f99b149 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -102,3 +102,22 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
 	}
 	return 0;
 }
+
+int read_mmfile(mmfile_t *ptr, const char *filename)
+{
+	struct stat st;
+	FILE *f;
+
+	if (stat(filename, &st))
+		return error("Could not stat %s", filename);
+	if ((f = fopen(filename, "rb")) == NULL)
+		return error("Could not open %s", filename);
+	ptr->ptr = xmalloc(st.st_size);
+	if (fread(ptr->ptr, st.st_size, 1, f) != 1)
+		return error("Could not read %s", filename);
+	fclose(f);
+	ptr->size = st.st_size;
+	return 0;
+}
+
+
diff --git a/xdiff-interface.h b/xdiff-interface.h
index 1346908bea..1918808081 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -17,5 +17,6 @@ int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
 int parse_hunk_header(char *line, int len,
 		      int *ob, int *on,
 		      int *nb, int *nn);
+int read_mmfile(mmfile_t *ptr, const char *filename);
 
 #endif

From ad8e72cba8e8dee8fccf11d98f197a44b5e3c10a Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 21 Dec 2006 10:21:28 +0100
Subject: [PATCH 271/330] Add a test for git-rerere

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t4200-rerere.sh | 154 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)
 create mode 100755 t/t4200-rerere.sh

diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh
new file mode 100755
index 0000000000..5ee5b23095
--- /dev/null
+++ b/t/t4200-rerere.sh
@@ -0,0 +1,154 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Johannes E. Schindelin
+#
+
+test_description='git-rerere
+'
+
+. ./test-lib.sh
+
+cat > a1 << EOF
+Whether 'tis nobler in the mind to suffer
+The slings and arrows of outrageous fortune,
+Or to take arms against a sea of troubles,
+And by opposing end them? To die: to sleep;
+No more; and by a sleep to say we end
+The heart-ache and the thousand natural shocks
+That flesh is heir to, 'tis a consummation
+Devoutly to be wish'd.
+EOF
+
+git add a1
+git commit -q -a -m initial
+
+git checkout -b first
+cat >> a1 << EOF
+To die, to sleep;
+To sleep: perchance to dream: ay, there's the rub;
+For in that sleep of death what dreams may come
+When we have shuffled off this mortal coil,
+Must give us pause: there's the respect
+That makes calamity of so long life;
+EOF
+git commit -q -a -m first
+
+git checkout -b second master
+git show first:a1 | sed 's/To die, t/To die! T/' > a1
+git commit -q -a -m second
+
+# activate rerere
+mkdir .git/rr-cache
+
+test_expect_failure 'conflicting merge' 'git pull . first'
+
+sha1=4f58849a60b4f969a2848966b6d02893b783e8fb
+rr=.git/rr-cache/$sha1
+test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
+
+test_expect_success 'no postimage or thisimage yet' \
+	"test ! -f $rr/postimage -a ! -f $rr/thisimage"
+
+git show first:a1 > a1
+
+cat > expect << EOF
+--- a/a1
++++ b/a1
+@@ -6,11 +6,7 @@
+ The heart-ache and the thousand natural shocks
+ That flesh is heir to, 'tis a consummation
+ Devoutly to be wish'd.
+-<<<<<<<
+-To die! To sleep;
+-=======
+ To die, to sleep;
+->>>>>>>
+ To sleep: perchance to dream: ay, there's the rub;
+ For in that sleep of death what dreams may come
+ When we have shuffled off this mortal coil,
+EOF
+
+git rerere diff > out
+
+test_expect_success 'rerere diff' 'diff -u expect out'
+
+cat > expect << EOF
+a1
+EOF
+
+git rerere status > out
+
+test_expect_success 'rerere status' 'diff -u expect out'
+
+test_expect_success 'commit succeeds' \
+	"git commit -q -a -m 'prefer first over second'"
+
+test_expect_success 'recorded postimage' "test -f $rr/postimage"
+
+git checkout -b third master
+git show second^:a1 | sed 's/To die: t/To die! T/' > a1
+git commit -q -a -m third
+
+test_expect_failure 'another conflicting merge' 'git pull . first'
+
+git show first:a1 | sed 's/To die: t/To die! T/' > expect
+test_expect_success 'rerere kicked in' "! grep ======= a1"
+
+test_expect_success 'rerere prefers first change' 'diff -u a1 expect'
+
+rm $rr/postimage
+echo "$sha1	a1" | tr '\012' '\0' > .git/rr-cache/MERGE_RR
+
+test_expect_success 'rerere clear' 'git rerere clear'
+
+test_expect_success 'clear removed the directory' "test ! -d $rr"
+
+mkdir $rr
+echo Hello > $rr/preimage
+echo World > $rr/postimage
+
+sha2=4000000000000000000000000000000000000000
+rr2=.git/rr-cache/$sha2
+mkdir $rr2
+echo Hello > $rr2/preimage
+
+case "$(date -d @11111111 +%s 2>/dev/null)" in
+[1-9]*)
+	# it is a recent GNU date. good.
+	now=$(date +%s)
+	almost_15_days_ago=$(($now+60-15*86400))
+	just_over_15_days_ago=$(($now-1-15*86400))
+	almost_60_days_ago=$(($now+60-60*86400))
+	just_over_60_days_ago=$(($now-1-60*86400))
+	predate1="$(date -d "@$almost_60_days_ago" +%c)"
+	predate2="$(date -d "@$almost_15_days_ago" +%c)"
+	postdate1="$(date -d "@$just_over_60_days_ago" +%c)"
+	postdate2="$(date -d "@$just_over_15_days_ago" +%c)"
+	;;
+*)
+	# it is not GNU date. oh, well.
+	predate1="$(date)"
+	predate2="$(date)"
+	postdate1='1 Oct 2006 00:00:00'
+	postdate2='1 Dec 2006 00:00:00'
+esac
+
+touch -m -d "$predate1" $rr/preimage
+touch -m -d "$predate2" $rr2/preimage
+
+test_expect_success 'garbage collection (part1)' 'git rerere gc'
+
+test_expect_success 'young records still live' \
+	"test -f $rr/preimage -a -f $rr2/preimage"
+
+touch -m -d "$postdate1" $rr/preimage
+touch -m -d "$postdate2" $rr2/preimage
+
+test_expect_success 'garbage collection (part2)' 'git rerere gc'
+
+test_expect_success 'old records rest in peace' \
+	"test ! -f $rr/preimage -a ! -f $rr2/preimage"
+
+test_done
+
+

From 658f3650273abd6888c964b9fb1291c603306df8 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Wed, 20 Dec 2006 17:39:41 +0100
Subject: [PATCH 272/330] Make git-rerere a builtin

The perl version used modules which are non-standard in some setups.
This patch brings the full power of rerere to a wider audience.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile         |   2 +-
 builtin-rerere.c | 406 +++++++++++++++++++++++++++++++++++++++++++++++
 builtin.h        |   1 +
 git.c            |   1 +
 4 files changed, 409 insertions(+), 1 deletion(-)
 create mode 100644 builtin-rerere.c

diff --git a/Makefile b/Makefile
index 76511045a8..154bf4cf52 100644
--- a/Makefile
+++ b/Makefile
@@ -174,7 +174,6 @@ SCRIPT_SH = \
 
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-rerere.perl \
 	git-cvsserver.perl \
 	git-svnimport.perl git-cvsexportcommit.perl \
 	git-send-email.perl git-svn.perl
@@ -289,6 +288,7 @@ BUILTIN_OBJS = \
 	builtin-push.o \
 	builtin-read-tree.o \
 	builtin-repo-config.o \
+	builtin-rerere.o \
 	builtin-rev-list.o \
 	builtin-rev-parse.o \
 	builtin-rm.o \
diff --git a/builtin-rerere.c b/builtin-rerere.c
new file mode 100644
index 0000000000..d064bd8bf0
--- /dev/null
+++ b/builtin-rerere.c
@@ -0,0 +1,406 @@
+#include "cache.h"
+#include "path-list.h"
+#include "xdiff/xdiff.h"
+#include "xdiff-interface.h"
+
+#include <time.h>
+
+static const char git_rerere_usage[] =
+"git-rerere [clear | status | diff | gc]";
+
+/* these values are days */
+static int cutoff_noresolve = 15;
+static int cutoff_resolve = 60;
+
+static char *merge_rr_path;
+
+static const char *rr_path(const char *name, const char *file)
+{
+	return git_path("rr-cache/%s/%s", name, file);
+}
+
+static void read_rr(struct path_list *rr)
+{
+	unsigned char sha1[20];
+	char buf[PATH_MAX];
+	FILE *in = fopen(merge_rr_path, "r");
+	if (!in)
+		return;
+	while (fread(buf, 40, 1, in) == 1) {
+		int i;
+		char *name;
+		if (get_sha1_hex(buf, sha1))
+			die("corrupt MERGE_RR");
+		buf[40] = '\0';
+		name = xstrdup(buf);
+		if (fgetc(in) != '\t')
+			die("corrupt MERGE_RR");
+		for (i = 0; i < sizeof(buf) && (buf[i] = fgetc(in)); i++)
+			; /* do nothing */
+		if (i == sizeof(buf))
+			die("filename too long");
+		path_list_insert(buf, rr)->util = xstrdup(name);
+	}
+	fclose(in);
+}
+
+static struct lock_file write_lock;
+
+static int write_rr(struct path_list *rr, int out_fd)
+{
+	int i;
+	for (i = 0; i < rr->nr; i++) {
+		const char *path = rr->items[i].path;
+		write(out_fd, rr->items[i].util, 40);
+		write(out_fd, "\t", 1);
+		write(out_fd, path, strlen(path) + 1);
+	}
+	close(out_fd);
+	return commit_lock_file(&write_lock);
+}
+
+struct buffer {
+	char *ptr;
+	int nr, alloc;
+};
+
+static void append_line(struct buffer *buffer, const char *line)
+{
+	int len = strlen(line);
+
+	if (buffer->nr + len > buffer->alloc) {
+		buffer->alloc = alloc_nr(buffer->nr + len);
+		buffer->ptr = xrealloc(buffer->ptr, buffer->alloc);
+	}
+	memcpy(buffer->ptr + buffer->nr, line, len);
+	buffer->nr += len;
+}
+
+static int handle_file(const char *path,
+	 unsigned char *sha1, const char *output)
+{
+	SHA_CTX ctx;
+	char buf[1024];
+	int hunk = 0, hunk_no = 0;
+	struct buffer minus = { NULL, 0, 0 }, plus = { NULL, 0, 0 };
+	struct buffer *one = &minus, *two = &plus;
+	FILE *f = fopen(path, "r");
+	FILE *out;
+
+	if (!f)
+		return error("Could not open %s", path);
+
+	if (output) {
+		out = fopen(output, "w");
+		if (!out) {
+			fclose(f);
+			return error("Could not write %s", output);
+		}
+	} else
+		out = NULL;
+
+	if (sha1)
+		SHA1_Init(&ctx);
+
+	while (fgets(buf, sizeof(buf), f)) {
+		if (!strncmp("<<<<<<< ", buf, 8))
+			hunk = 1;
+		else if (!strncmp("=======", buf, 7))
+			hunk = 2;
+		else if (!strncmp(">>>>>>> ", buf, 8)) {
+			hunk_no++;
+			hunk = 0;
+			if (memcmp(one->ptr, two->ptr, one->nr < two->nr ?
+						one->nr : two->nr) > 0) {
+				struct buffer *swap = one;
+				one = two;
+				two = swap;
+			}
+			if (out) {
+				fputs("<<<<<<<\n", out);
+				fwrite(one->ptr, one->nr, 1, out);
+				fputs("=======\n", out);
+				fwrite(two->ptr, two->nr, 1, out);
+				fputs(">>>>>>>\n", out);
+			}
+			if (sha1) {
+				SHA1_Update(&ctx, one->ptr, one->nr);
+				SHA1_Update(&ctx, "\0", 1);
+				SHA1_Update(&ctx, two->ptr, two->nr);
+				SHA1_Update(&ctx, "\0", 1);
+			}
+		} else if (hunk == 1)
+			append_line(one, buf);
+		else if (hunk == 2)
+			append_line(two, buf);
+		else if (out)
+			fputs(buf, out);
+	}
+
+	fclose(f);
+	if (out)
+		fclose(out);
+	if (sha1)
+		SHA1_Final(sha1, &ctx);
+	return hunk_no;
+}
+
+static int find_conflict(struct path_list *conflict)
+{
+	int i;
+	if (read_cache() < 0)
+		return error("Could not read index");
+	for (i = 0; i + 2 < active_nr; i++) {
+		struct cache_entry *e1 = active_cache[i];
+		struct cache_entry *e2 = active_cache[i + 1];
+		struct cache_entry *e3 = active_cache[i + 2];
+		if (ce_stage(e1) == 1 && ce_stage(e2) == 2 &&
+				ce_stage(e3) == 3 && ce_same_name(e1, e2) &&
+				ce_same_name(e1, e3)) {
+			path_list_insert((const char *)e1->name, conflict);
+			i += 3;
+		}
+	}
+	return 0;
+}
+
+static int merge(const char *name, const char *path)
+{
+	int ret;
+	mmfile_t cur, base, other;
+	mmbuffer_t result = {NULL, 0};
+	xpparam_t xpp = {XDF_NEED_MINIMAL};
+
+	if (handle_file(path, NULL, rr_path(name, "thisimage")) < 0)
+		return 1;
+
+	if (read_mmfile(&cur, rr_path(name, "thisimage")) ||
+			read_mmfile(&base, rr_path(name, "preimage")) ||
+			read_mmfile(&other, rr_path(name, "postimage")))
+		return 1;
+	ret = xdl_merge(&base, &cur, "", &other, "",
+			&xpp, XDL_MERGE_ZEALOUS, &result);
+	if (!ret) {
+		FILE *f = fopen(path, "w");
+		if (!f)
+			return error("Could not write to %s", path);
+		fwrite(result.ptr, result.size, 1, f);
+		fclose(f);
+	}
+
+	free(cur.ptr);
+	free(base.ptr);
+	free(other.ptr);
+	free(result.ptr);
+
+	return ret;
+}
+
+static void unlink_rr_item(const char *name)
+{
+	unlink(rr_path(name, "thisimage"));
+	unlink(rr_path(name, "preimage"));
+	unlink(rr_path(name, "postimage"));
+	rmdir(git_path("rr-cache/%s", name));
+}
+
+static void garbage_collect(struct path_list *rr)
+{
+	struct path_list to_remove = { NULL, 0, 0, 1 };
+	char buf[1024];
+	DIR *dir;
+	struct dirent *e;
+	int len, i, cutoff;
+	time_t now = time(NULL), then;
+
+	strlcpy(buf, git_path("rr-cache"), sizeof(buf));
+	len = strlen(buf);
+	dir = opendir(buf);
+	strcpy(buf + len++, "/");
+	while ((e = readdir(dir))) {
+		const char *name = e->d_name;
+		struct stat st;
+		if (name[0] == '.' && (name[1] == '\0' ||
+					(name[1] == '.' && name[2] == '\0')))
+			continue;
+		i = snprintf(buf + len, sizeof(buf) - len, "%s", name);
+		strlcpy(buf + len + i, "/preimage", sizeof(buf) - len - i);
+		if (stat(buf, &st))
+			continue;
+		then = st.st_mtime;
+		strlcpy(buf + len + i, "/postimage", sizeof(buf) - len - i);
+		cutoff = stat(buf, &st) ? cutoff_noresolve : cutoff_resolve;
+		if (then < now - cutoff * 86400) {
+			buf[len + i] = '\0';
+			path_list_insert(xstrdup(name), &to_remove);
+		}
+	}
+	for (i = 0; i < to_remove.nr; i++)
+		unlink_rr_item(to_remove.items[i].path);
+	path_list_clear(&to_remove, 0);
+}
+
+static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		write(1, ptr[i].ptr, ptr[i].size);
+	return 0;
+}
+
+static int diff_two(const char *file1, const char *label1,
+		const char *file2, const char *label2)
+{
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+	mmfile_t minus, plus;
+
+	if (read_mmfile(&minus, file1) || read_mmfile(&plus, file2))
+		return 1;
+
+	printf("--- a/%s\n+++ b/%s\n", label1, label2);
+	fflush(stdout);
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 3;
+	xecfg.flags = 0;
+	ecb.outf = outf;
+	xdl_diff(&minus, &plus, &xpp, &xecfg, &ecb);
+
+	free(minus.ptr);
+	free(plus.ptr);
+	return 0;
+}
+
+static int copy_file(const char *src, const char *dest)
+{
+	FILE *in, *out;
+	char buffer[32768];
+	int count;
+
+	if (!(in = fopen(src, "r")))
+		return error("Could not open %s", src);
+	if (!(out = fopen(dest, "w")))
+		return error("Could not open %s", dest);
+	while ((count = fread(buffer, 1, sizeof(buffer), in)))
+		fwrite(buffer, 1, count, out);
+	fclose(in);
+	fclose(out);
+	return 0;
+}
+
+static int do_plain_rerere(struct path_list *rr, int fd)
+{
+	struct path_list conflict = { NULL, 0, 0, 1 };
+	int i;
+
+	find_conflict(&conflict);
+
+	/*
+	 * MERGE_RR records paths with conflicts immediately after merge
+	 * failed.  Some of the conflicted paths might have been hand resolved
+	 * in the working tree since then, but the initial run would catch all
+	 * and register their preimages.
+	 */
+
+	for (i = 0; i < conflict.nr; i++) {
+		const char *path = conflict.items[i].path;
+		if (!path_list_has_path(rr, path)) {
+			unsigned char sha1[20];
+			char *hex;
+			int ret;
+			ret = handle_file(path, sha1, NULL);
+			if (ret < 1)
+				continue;
+			hex = xstrdup(sha1_to_hex(sha1));
+			path_list_insert(path, rr)->util = hex;
+			if (mkdir(git_path("rr-cache/%s", hex), 0755))
+				continue;;
+			handle_file(path, NULL, rr_path(hex, "preimage"));
+			fprintf(stderr, "Recorded preimage for '%s'\n", path);
+		}
+	}
+
+	/*
+	 * Now some of the paths that had conflicts earlier might have been
+	 * hand resolved.  Others may be similar to a conflict already that
+	 * was resolved before.
+	 */
+
+	for (i = 0; i < rr->nr; i++) {
+		struct stat st;
+		int ret;
+		const char *path = rr->items[i].path;
+		const char *name = (const char *)rr->items[i].util;
+
+		if (!stat(rr_path(name, "preimage"), &st) &&
+				!stat(rr_path(name, "postimage"), &st)) {
+			if (!merge(name, path)) {
+				fprintf(stderr, "Resolved '%s' using "
+						"previous resolution.\n", path);
+				goto tail_optimization;
+			}
+		}
+
+		/* Let's see if we have resolved it. */
+		ret = handle_file(path, NULL, NULL);
+		if (ret)
+			continue;
+
+		fprintf(stderr, "Recorded resolution for '%s'.\n", path);
+		copy_file(path, rr_path(name, "postimage"));
+tail_optimization:
+		if (i < rr->nr - 1) {
+			memmove(rr->items + i,
+					rr->items + i + 1,
+					rr->nr - i - 1);
+		}
+		rr->nr--;
+		i--;
+	}
+
+	return write_rr(rr, fd);
+}
+
+int cmd_rerere(int argc, const char **argv, const char *prefix)
+{
+	struct path_list merge_rr = { NULL, 0, 0, 1 };
+	int i, fd = -1;
+	struct stat st;
+
+	if (stat(git_path("rr-cache"), &st) || !S_ISDIR(st.st_mode))
+		return 0;
+
+	merge_rr_path = xstrdup(git_path("rr-cache/MERGE_RR"));
+	fd = hold_lock_file_for_update(&write_lock, merge_rr_path, 1);
+	read_rr(&merge_rr);
+
+	if (argc < 2)
+		return do_plain_rerere(&merge_rr, fd);
+	else if (!strcmp(argv[1], "clear")) {
+		for (i = 0; i < merge_rr.nr; i++) {
+			const char *name = (const char *)merge_rr.items[i].util;
+			if (!stat(git_path("rr-cache/%s", name), &st) &&
+					S_ISDIR(st.st_mode) &&
+					stat(rr_path(name, "postimage"), &st))
+				unlink_rr_item(name);
+		}
+		unlink(merge_rr_path);
+	} else if (!strcmp(argv[1], "gc"))
+		garbage_collect(&merge_rr);
+	else if (!strcmp(argv[1], "status"))
+		for (i = 0; i < merge_rr.nr; i++)
+			printf("%s\n", merge_rr.items[i].path);
+	else if (!strcmp(argv[1], "diff"))
+		for (i = 0; i < merge_rr.nr; i++) {
+			const char *path = merge_rr.items[i].path;
+			const char *name = (const char *)merge_rr.items[i].util;
+			diff_two(rr_path(name, "preimage"), path, path, path);
+		}
+	else
+		usage(git_rerere_usage);
+
+	path_list_clear(&merge_rr, 1);
+	return 0;
+}
+
diff --git a/builtin.h b/builtin.h
index 08519e7c82..8ffd8b2653 100644
--- a/builtin.h
+++ b/builtin.h
@@ -52,6 +52,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
 extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
+extern int cmd_rerere(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
 extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
 extern int cmd_rm(int argc, const char **argv, const char *prefix);
diff --git a/git.c b/git.c
index 73cf4d4e01..7a01e02f2b 100644
--- a/git.c
+++ b/git.c
@@ -245,6 +245,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
 		{ "push", cmd_push, RUN_SETUP },
 		{ "read-tree", cmd_read_tree, RUN_SETUP },
 		{ "repo-config", cmd_repo_config },
+		{ "rerere", cmd_rerere, RUN_SETUP },
 		{ "rev-list", cmd_rev_list, RUN_SETUP },
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "rm", cmd_rm, RUN_SETUP },

From 2295e8d0c4cec41fbe257ddb957516a7c1a139df Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Thu, 21 Dec 2006 23:49:13 -0800
Subject: [PATCH 273/330] commit-tree: do not overflow MAXPARENT

We have a static allocation of MAXPARENT, but that limit was not
enforced, other than by a lucky invocation of the program segfaulting.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-commit-tree.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index 856f3cd841..bc28770664 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -101,6 +101,9 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 		a = argv[i]; b = argv[i+1];
 		if (!b || strcmp(a, "-p"))
 			usage(commit_tree_usage);
+
+		if (parents >= MAXPARENT)
+			die("Too many parents (%d max)", MAXPARENT);
 		if (get_sha1(b, parent_sha1[parents]))
 			die("Not a valid object name %s", b);
 		check_valid(parent_sha1[parents], commit_type);

From 8d8b9f6252271899708b32fbce4cd2541af9545a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 00:46:33 -0800
Subject: [PATCH 274/330] reflog expire: prune commits that are not incomplete

Older fsck-objects and prune did not protect commits in reflog
entries, and it is quite possible that a commit still exists in
the repository (because it was in a pack, or something) while
some of its trees and blobs are long gone.  Make sure the commit
and its associated tree is complete and expire incomplete ones.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-reflog.c | 43 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 40 insertions(+), 3 deletions(-)

diff --git a/builtin-reflog.c b/builtin-reflog.c
index d4f73535c4..4097c328cc 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -3,7 +3,7 @@
 #include "commit.h"
 #include "refs.h"
 #include "dir.h"
-#include <time.h>
+#include "tree-walk.h"
 
 struct expire_reflog_cb {
 	FILE *newlog;
@@ -13,13 +13,50 @@ struct expire_reflog_cb {
 	unsigned long expire_unreachable;
 };
 
+static int tree_is_complete(const unsigned char *sha1)
+{
+	struct tree_desc desc;
+	void *buf;
+	char type[20];
+
+	buf = read_sha1_file(sha1, type, &desc.size);
+	if (!buf)
+		return 0;
+	desc.buf = buf;
+	while (desc.size) {
+		const unsigned char *elem;
+		const char *name;
+		unsigned mode;
+
+		elem = tree_entry_extract(&desc, &name, &mode);
+		if (!has_sha1_file(elem) ||
+		    (S_ISDIR(mode) && !tree_is_complete(elem))) {
+			free(buf);
+			return 0;
+		}
+		update_tree_entry(&desc);
+	}
+	free(buf);
+	return 1;
+}
+
 static int keep_entry(struct commit **it, unsigned char *sha1)
 {
+	struct commit *commit;
+
 	*it = NULL;
 	if (is_null_sha1(sha1))
 		return 1;
-	*it = lookup_commit_reference_gently(sha1, 1);
-	return (*it != NULL);
+	commit = lookup_commit_reference_gently(sha1, 1);
+	if (!commit)
+		return 0;
+
+	/* Make sure everything in this commit exists. */
+	parse_object(commit->object.sha1);
+	if (!tree_is_complete(commit->tree->object.sha1))
+		return 0;
+	*it = commit;
+	return 1;
 }
 
 static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,

From 228e2eb67e9f15983519f472cc1566a3dd857f9c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 15:21:55 -0800
Subject: [PATCH 275/330] merge and reset: adjust for "reset --hard" messages

An earlier commit made "reset --hard" chattier but leaking its
message from "git rebase" (which calls it when first rewinding
the current branch to prepare replaying our own changes) without
explanation was confusing, so add an extra message to mention
it.  Inside restorestate in merge (which is rarely exercised
codepath, where more than one strategies are attempted),
resetting to the original state uses "reset --hard" -- this can
be squelched entirely.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh  | 2 +-
 git-rebase.sh | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/git-merge.sh b/git-merge.sh
index 4ebfcf65d9..aec215e725 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -32,7 +32,7 @@ savestate() {
 restorestate() {
         if test -f "$GIT_DIR/MERGE_SAVE"
 	then
-		git reset --hard $head
+		git reset --hard $head >/dev/null
 		cpio -iuv <"$GIT_DIR/MERGE_SAVE"
 		git-update-index --refresh >/dev/null
 	fi
diff --git a/git-rebase.sh b/git-rebase.sh
index 2b4f3477fa..ece31425d0 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -292,6 +292,7 @@ then
 fi
 
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
+echo "First, rewinding head to replay your work on top of it..."
 git-reset --hard "$onto"
 
 # If the $onto is a proper descendant of the tip of the branch, then

From fb8696d9d82e78fe829fdd95ff9ff10fdfa52ef9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 15:25:21 -0800
Subject: [PATCH 276/330] default pull: forget about "newbie protection" for
 now.

This will not be backward compatible no matter how you cut it.
Shelve it for now until somebody comes up with a better way to
determine when we can safely refuse to use the first set of
branchse for merging without upsetting valid workflows.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 7 -------
 1 file changed, 7 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index f163821d03..b163d22ddb 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -145,13 +145,6 @@ canon_refs_list_for_fetch () {
 			merge_branches=$(git-repo-config \
 			    --get-all "branch.${curr_branch}.merge")
 		fi
-		# If we are fetching only one branch, then first branch
-		# is the only thing that makes sense to merge anyway,
-		# so there is no point refusing that traditional rule.
-		if test $# != 1 && test "z$merge_branches" = z
-		then
-			merge_branches=..this..would..never..match..
-		fi
 	fi
 	for ref
 	do

From ea560e6d64374ec1f6c163c276319a3da21a1345 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 15:37:48 -0800
Subject: [PATCH 277/330] Do not support "partial URL shorthand" anymore.

We used to support specifying the top part of remote URL in
remotes and use that as a short-hand for the URL.

	$ cat .git/remotes/jgarzik
	URL: git://git.kernel.org/pub/scm/linux/kernel/git/jgarzik/
	$ git pull jgarzik/misc-2.6

This is confusing when somebody attempts to do this:

	$ git pull origin/foo

which is not syntactically correct (unless you have origin/foo.git
repository) and should fail, but it resulted in a mysterious
access to the 'foo' subdirectory of the origin repository.

Which was what it was designed to do, but because this is an
oddball "feature" I suspect nobody uses, let's remove it.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 34 +++++++---------------------------
 1 file changed, 7 insertions(+), 27 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index b163d22ddb..aaef861ada 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -7,18 +7,7 @@ GIT_DIR=$(git-rev-parse --git-dir 2>/dev/null) || :;
 get_data_source () {
 	case "$1" in
 	*/*)
-		# Not so fast.	This could be the partial URL shorthand...
-		token=$(expr "z$1" : 'z\([^/]*\)/')
-		remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
-		if test "$(git-repo-config --get "remote.$token.url")"
-		then
-			echo config-partial
-		elif test -f "$GIT_DIR/branches/$token"
-		then
-			echo branches-partial
-		else
-			echo ''
-		fi
+		echo ''
 		;;
 	*)
 		if test "$(git-repo-config --get "remote.$1.url")"
@@ -40,12 +29,7 @@ get_remote_url () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
 	'')
-		echo "$1" ;;
-	config-partial)
-		token=$(expr "z$1" : 'z\([^/]*\)/')
-		remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
-		url=$(git-repo-config --get "remote.$token.url")
-		echo "$url/$remainder"
+		echo "$1"
 		;;
 	config)
 		git-repo-config --get "remote.$1.url"
@@ -54,14 +38,10 @@ get_remote_url () {
 		sed -ne '/^URL: */{
 			s///p
 			q
-		}' "$GIT_DIR/remotes/$1" ;;
+		}' "$GIT_DIR/remotes/$1"
+		;;
 	branches)
-		sed -e 's/#.*//' "$GIT_DIR/branches/$1" ;;
-	branches-partial)
-		token=$(expr "z$1" : 'z\([^/]*\)/')
-		remainder=$(expr "z$1" : 'z[^/]*/\(.*\)')
-		url=$(sed -e 's/#.*//' "$GIT_DIR/branches/$token")
-		echo "$url/$remainder"
+		sed -e 's/#.*//' "$GIT_DIR/branches/$1"
 		;;
 	*)
 		die "internal error: get-remote-url $1" ;;
@@ -77,7 +57,7 @@ get_default_remote () {
 get_remote_default_refs_for_push () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
-	'' | config-partial | branches | branches-partial)
+	'' | branches)
 		;; # no default push mapping, just send matching refs.
 	config)
 		git-repo-config --get-all "remote.$1.push" ;;
@@ -196,7 +176,7 @@ canon_refs_list_for_fetch () {
 get_remote_default_refs_for_fetch () {
 	data_source=$(get_data_source "$1")
 	case "$data_source" in
-	'' | config-partial | branches-partial)
+	'')
 		echo "HEAD:" ;;
 	config)
 		canon_refs_list_for_fetch -d "$1" \

From 95ca1c6cb76ae01528f70c5ed437a79887a60775 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 22:46:11 -0800
Subject: [PATCH 278/330] Really fix headers for __FreeBSD__

The symbol to detect FreeBSD is __FreeBSD__, not __FreeBSD.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-compat-util.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-compat-util.h b/git-compat-util.h
index a55b923894..f79365b362 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -11,7 +11,7 @@
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
 
-#if !defined(__APPLE__) && !defined(__FreeBSD)
+#if !defined(__APPLE__) && !defined(__FreeBSD__)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
 #endif

From a6782bc572530be6f7d799f18d3e79dd6a406294 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 22:48:46 -0800
Subject: [PATCH 279/330] git-tag: lose exit after die

We are not running under /bin/resurrection shell ;-)

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-tag.sh | 2 --
 1 file changed, 2 deletions(-)

diff --git a/git-tag.sh b/git-tag.sh
index 36cd6aa256..e1bfa82f1e 100755
--- a/git-tag.sh
+++ b/git-tag.sh
@@ -40,7 +40,6 @@ do
 	message="$1"
 	if test "$#" = "0"; then
 	    die "error: option -m needs an argument"
-	    exit 2
 	else
 	    message_given=1
 	fi
@@ -50,7 +49,6 @@ do
 	shift
 	if test "$#" = "0"; then
 	    die "error: option -F needs an argument"
-	    exit 2
 	else
 	    message="$(cat "$1")"
 	    message_given=1

From 40f656cd0214355df4c29aea315bfc2e9d983516 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 22 Dec 2006 22:18:03 -0800
Subject: [PATCH 280/330] Remove NO_ACCURATE_DIFF options from build systems

The code no longer uses it, as we have --inaccurate-eof in
git-apply.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile     | 6 ------
 configure.ac | 3 ---
 2 files changed, 9 deletions(-)

diff --git a/Makefile b/Makefile
index 76511045a8..4362297e00 100644
--- a/Makefile
+++ b/Makefile
@@ -79,9 +79,6 @@ all:
 #
 # Define NO_ICONV if your libc does not properly support iconv.
 #
-# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
-# a missing newline at the end of the file.
-#
 # Define USE_NSEC below if you want git to care about sub-second file mtimes
 # and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 # it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
@@ -549,9 +546,6 @@ else
 endif
 endif
 endif
-ifdef NO_ACCURATE_DIFF
-	BASIC_CFLAGS += -DNO_ACCURATE_DIFF
-endif
 ifdef NO_PERL_MAKEMAKER
 	export NO_PERL_MAKEMAKER
 endif
diff --git a/configure.ac b/configure.ac
index e153d53823..7cfb3a0666 100644
--- a/configure.ac
+++ b/configure.ac
@@ -235,9 +235,6 @@ AC_SUBST(NO_SETENV)
 #
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
-#
-# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
-# a missing newline at the end of the file.
 
 ## Site configuration (override autodetection)
 ## --with-PACKAGE[=ARG] and --without-PACKAGE

From 4de0f9f9b6ed1731de00b67952504e5a783f54a4 Mon Sep 17 00:00:00 2001
From: Alexandre Julliard <julliard@winehq.org>
Date: Fri, 22 Dec 2006 14:56:15 +0100
Subject: [PATCH 281/330] vc-git: Ignore errors caused by a non-existent
 directory in vc-git-registered.

Signed-off-by: Alexandre Julliard <julliard@winehq.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 contrib/emacs/vc-git.el | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 8b6361922f..3eb4bd19e9 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -58,8 +58,9 @@
   (with-temp-buffer
     (let* ((dir (file-name-directory file))
            (name (file-relative-name file dir)))
-      (when dir (cd dir))
-      (and (ignore-errors (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
+      (and (ignore-errors
+             (when dir (cd dir))
+             (eq 0 (call-process "git" nil '(t nil) nil "ls-files" "-c" "-z" "--" name)))
            (let ((str (buffer-string)))
              (and (> (length str) (length name))
                   (string= (substring str 0 (1+ (length name))) (concat name "\0"))))))))

From 4b1552238ec3b8b711f60b2b65265a751a40f5ff Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Fri, 22 Dec 2006 21:59:24 -0800
Subject: [PATCH 282/330] git-svn: enable common fetch/commit options for
 dcommit

dcommit does commits and fetches, so all options used for those
should work, too, including --authors-file.

Reported missing by Nicolas Vilz.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/git-svn.perl b/git-svn.perl
index 07748bc3e3..4288a05c16 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -111,7 +111,7 @@ my %cmd = (
 			{ 'merge|m|M' => \$_merge,
 			  'strategy|s=s' => \$_strategy,
 			  'dry-run|n' => \$_dry_run,
-			%cmt_opts } ],
+			%cmt_opts, %fc_opts } ],
 	'set-tree' => [ \&commit, "Set an SVN repository to a git tree-ish",
 			{	'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
 	'show-ignore' => [ \&show_ignore, "Show svn:ignore listings",

From c321f00d09f56dcf2f149757a2a337f3732f3097 Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Fri, 22 Dec 2006 08:56:25 -0500
Subject: [PATCH 283/330] Keep "git --git-dir" from causing a bus error.

The option checking code for --git-dir had an off by 1 error that
would cause it to access uninitialized memory if it was the last
argument.  This causes it to display an error and display the usage
string instead.

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/git.c b/git.c
index 73cf4d4e01..7bb61d8375 100644
--- a/git.c
+++ b/git.c
@@ -59,8 +59,10 @@ static int handle_options(const char*** argv, int* argc)
 		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
 			setup_pager();
 		} else if (!strcmp(cmd, "--git-dir")) {
-			if (*argc < 1)
-				return -1;
+			if (*argc < 2) {
+				fprintf(stderr, "No directory given for --git-dir.\n" );
+				usage(git_usage_string);
+			}
 			setenv("GIT_DIR", (*argv)[1], 1);
 			(*argv)++;
 			(*argc)--;

From f49006b0c79bb74bc500a9a3a883e929fa0f803e Mon Sep 17 00:00:00 2001
From: Brian Gernhardt <benji@silverinsanity.com>
Date: Fri, 22 Dec 2006 08:58:39 -0500
Subject: [PATCH 284/330] Make git-show-branch options similar to git-branch.

Branch has "-r" for remote branches and "-a" for local and remote.
It seems logical to mirror that in show-branch.  Also removes the
dubiously useful "--tags" option (as part of changing the meaning
for "--all").

Signed-off-by: Brian Gernhardt <benji@silverinsanity.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-show-branch.txt | 10 ++++----
 builtin-show-branch.c             | 40 ++++++++++++++++++++-----------
 2 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 948ff10e6c..dafacd4308 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -8,7 +8,7 @@ git-show-branch - Show branches and their commits
 SYNOPSIS
 --------
 [verse]
-'git-show-branch' [--all] [--heads] [--tags] [--topo-order] [--current]
+'git-show-branch' [--all] [--remotes] [--topo-order] [--current]
 		[--more=<n> | --list | --independent | --merge-base]
 		[--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
 
@@ -37,9 +37,11 @@ OPTIONS
 	branches under $GIT_DIR/refs/heads/topic, giving
 	`topic/*` would show all of them.
 
---all --heads --tags::
-	Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads,
-	and $GIT_DIR/refs/tags, respectively.
+-r|--remotes::
+	Show the remote-tracking branches.
+
+-a|--all::
+	Show both remote-tracking branches and local branches.
 
 --current::
 	With this option, the command includes the current
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index b9d9781d4d..c67f2fa2fe 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -4,7 +4,7 @@
 #include "builtin.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n] <branch>";
+"git-show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...] | --reflog[=n] <branch>";
 
 static int default_num;
 static int default_alloc;
@@ -383,6 +383,20 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, int f
 	return append_ref(refname + ofs, sha1, flag, cb_data);
 }
 
+static int append_remote_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
+{
+	unsigned char tmp[20];
+	int ofs = 13;
+	if (strncmp(refname, "refs/remotes/", ofs))
+		return 0;
+	/* If both heads/foo and tags/foo exists, get_sha1 would
+	 * get confused.
+	 */
+	if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))
+		ofs = 5;
+	return append_ref(refname + ofs, sha1, flag, cb_data);
+}
+
 static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
 	if (strncmp(refname, "refs/tags/", 10))
@@ -423,16 +437,16 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, i
 	return append_ref(refname, sha1, flag, cb_data);
 }
 
-static void snarf_refs(int head, int tag)
+static void snarf_refs(int head, int remotes)
 {
 	if (head) {
 		int orig_cnt = ref_name_cnt;
 		for_each_ref(append_head_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
-	if (tag) {
+	if (remotes) {
 		int orig_cnt = ref_name_cnt;
-		for_each_ref(append_tag_ref, NULL);
+		for_each_ref(append_remote_ref, NULL);
 		sort_ref_range(orig_cnt, ref_name_cnt);
 	}
 }
@@ -554,7 +568,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 	struct commit_list *list = NULL, *seen = NULL;
 	unsigned int rev_mask[MAX_REVS];
 	int num_rev, i, extra = 0;
-	int all_heads = 0, all_tags = 0;
+	int all_heads = 0, all_remotes = 0;
 	int all_mask, all_revs;
 	int lifo = 1;
 	char head[128];
@@ -586,12 +600,10 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 			ac--; av++;
 			break;
 		}
-		else if (!strcmp(arg, "--all"))
-			all_heads = all_tags = 1;
-		else if (!strcmp(arg, "--heads"))
-			all_heads = 1;
-		else if (!strcmp(arg, "--tags"))
-			all_tags = 1;
+		else if (!strcmp(arg, "--all") || !strcmp(arg, "-a"))
+			all_heads = all_remotes = 1;
+		else if (!strcmp(arg, "--remotes") || !strcmp(arg, "-r"))
+			all_remotes = 1;
 		else if (!strcmp(arg, "--more"))
 			extra = 1;
 		else if (!strcmp(arg, "--list"))
@@ -636,11 +648,11 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
 		usage(show_branch_usage);
 
 	/* If nothing is specified, show all branches by default */
-	if (ac + all_heads + all_tags == 0)
+	if (ac + all_heads + all_remotes == 0)
 		all_heads = 1;
 
-	if (all_heads + all_tags)
-		snarf_refs(all_heads, all_tags);
+	if (all_heads + all_remotes)
+		snarf_refs(all_heads, all_remotes);
 	if (reflog) {
 		int reflen;
 		if (!ac)

From aaca9675a494fb42e7caa182ecb89c1951cafefa Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Fri, 22 Dec 2006 19:38:12 +0000
Subject: [PATCH 285/330] gitweb: Add missing show '...' links change.

Part of the patch for "gitweb: Show '...' links in "summary" view only
if there are more items" (313ce8cee665447e4476d7e8985b270346a8e5a1) is
missing.  Add it back in.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 1 +
 1 file changed, 1 insertion(+)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index ebbc397ee8..80c04b88da 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2983,6 +2983,7 @@ sub git_summary {
 	if (@forklist) {
 		git_print_header_div('forks');
 		git_project_list_body(\@forklist, undef, 0, 15,
+		                      $#forklist <= 15 ? undef :
 		                      $cgi->a({-href => href(action=>"forks")}, "..."),
 				      'noheader');
 	}

From 0ff5ec70c7d4fb5f176da5e05897c316fda82584 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Fri, 22 Dec 2006 19:38:13 +0000
Subject: [PATCH 286/330] gitweb: optimize git_get_last_activity.

Only return one line of output and we don't need the refname value.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 80c04b88da..01e3a8adaa 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1139,8 +1139,9 @@ sub git_get_last_activity {
 
 	$git_dir = "$projectroot/$path";
 	open($fd, "-|", git_cmd(), 'for-each-ref',
-	     '--format=%(refname) %(committer)',
+	     '--format=%(committer)',
 	     '--sort=-committerdate',
+	     '--count=1',
 	     'refs/heads') or return;
 	my $most_recent = <$fd>;
 	close $fd or return;

From 3fcf06be5d02de15992d11482cfae82dc058261b Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Fri, 22 Dec 2006 19:38:14 +0000
Subject: [PATCH 287/330] gitweb: optimize git_shortlog_body.

Don't call gitweb_have_snapshot from within the loop.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 01e3a8adaa..d2ddac8bf3 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2636,6 +2636,8 @@ sub git_shortlog_body {
 	# uses global variable $project
 	my ($revlist, $from, $to, $refs, $extra) = @_;
 
+	my $have_snapshot = gitweb_have_snapshot();
+
 	$from = 0 unless defined $from;
 	$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
 
@@ -2663,7 +2665,7 @@ sub git_shortlog_body {
 		      $cgi->a({-href => href(action=>"commit", hash=>$commit)}, "commit") . " | " .
 		      $cgi->a({-href => href(action=>"commitdiff", hash=>$commit)}, "commitdiff") . " | " .
 		      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree");
-		if (gitweb_have_snapshot()) {
+		if ($have_snapshot) {
 			print " | " . $cgi->a({-href => href(action=>"snapshot", hash=>$commit)}, "snapshot");
 		}
 		print "</td>\n" .

From a979d1289be6a3999d7e89bf0359ebf28075fc6b Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Fri, 22 Dec 2006 19:38:15 +0000
Subject: [PATCH 288/330] gitweb: optimize git_summary.

We don't need to call git_get_head_hash at all just pass in "HEAD" and
use the return id field.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d2ddac8bf3..b0e6fdfb9d 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2911,9 +2911,9 @@ sub git_project_index {
 
 sub git_summary {
 	my $descr = git_get_project_description($project) || "none";
-	my $head = git_get_head_hash($project);
-	my %co = parse_commit($head);
+	my %co = parse_commit("HEAD");
 	my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
+	my $head = $co{'id'};
 
 	my $owner = git_get_project_owner($project);
 
@@ -2960,7 +2960,7 @@ sub git_summary {
 	# we need to request one more than 16 (0..15) to check if
 	# those 16 are all
 	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
-		git_get_head_hash($project), "--"
+		$head, "--"
 		or die_error(undef, "Open git-rev-list failed");
 	my @revlist = map { chomp; $_ } <$fd>;
 	close $fd;

From 0a5761746ca046d546da6afc2b0079706a6629e0 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Fri, 22 Dec 2006 15:06:54 -0500
Subject: [PATCH 289/330] checkout: make the message about the need for a new
 branch a bit clearer

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-checkout.sh | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/git-checkout.sh b/git-checkout.sh
index 4192a99fec..92ec069a3a 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -146,8 +146,11 @@ fi
 
 [ -z "$branch$newbranch" ] &&
 	[ "$new" != "$old" ] &&
-	die "git checkout: to checkout the requested commit you need to specify 
-              a name for a new branch which is created and switched to"
+	die "git checkout: provided reference cannot be checked out directly
+
+  You need -b to associate a new branch with the wanted checkout. Example:
+  git checkout -b <new_branch_name> $arg
+"
 
 if [ "X$old" = X ]
 then

From 8e574fb542eaf7f41d5416b2cf65b5c2e93829d6 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sat, 23 Dec 2006 03:35:14 +0000
Subject: [PATCH 290/330] gitweb: Use rev-list pattern search options.

Use rev-list pattern search options instead of hand coded perl.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index b0e6fdfb9d..c1e8bbc3cb 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4176,20 +4176,20 @@ sub git_search {
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 1;
 	if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
+		my $greptype;
+		if ($searchtype eq 'commit') {
+			$greptype = "--grep=";
+		} elsif ($searchtype eq 'author') {
+			$greptype = "--author=";
+		} elsif ($searchtype eq 'committer') {
+			$greptype = "--committer=";
+		}
 		$/ = "\0";
 		open my $fd, "-|", git_cmd(), "rev-list",
-			"--header", "--parents", $hash, "--"
+			"--header", "--parents", ($greptype . $searchtext),
+			 $hash, "--"
 			or next;
 		while (my $commit_text = <$fd>) {
-			if (!grep m/$searchtext/i, $commit_text) {
-				next;
-			}
-			if ($searchtype eq 'author' && !grep m/\nauthor .*$searchtext/i, $commit_text) {
-				next;
-			}
-			if ($searchtype eq 'committer' && !grep m/\ncommitter .*$searchtext/i, $commit_text) {
-				next;
-			}
 			my @commit_lines = split "\n", $commit_text;
 			my %co = parse_commit(undef, \@commit_lines);
 			if (!%co) {

From 9d032c725034e7c072f8c1a3e11855594c2fc0af Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sat, 23 Dec 2006 03:35:15 +0000
Subject: [PATCH 291/330] gitweb: Require a minimum of two character for the
 search text.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c1e8bbc3cb..9061c4a75e 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -351,6 +351,9 @@ if (defined $searchtext) {
 	if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
 		die_error(undef, "Invalid search parameter");
 	}
+	if (length($searchtext) < 2) {
+		die_error(undef, "At least two characters are required for search parameter");
+	}
 	$searchtext = quotemeta $searchtext;
 }
 

From 6be935115b4a3bfa9062875a569d4a018ac372e2 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sat, 23 Dec 2006 03:35:16 +0000
Subject: [PATCH 292/330] gitweb: Allow search to be disabled from the config
 file.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 9061c4a75e..9a4f3b4841 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -128,6 +128,12 @@ our %feature = (
 		#         => [content-encoding, suffix, program]
 		'default' => ['x-gzip', 'gz', 'gzip']},
 
+	# Enable text search, which will list the commits which match author,
+	# committer or commit text to a given string.  Enabled by default.
+	'search' => {
+		'override' => 0,
+		'default' => [1]},
+
 	# Enable the pickaxe search, which will list the commits that modified
 	# a given string in a file. This can be practical and quite faster
 	# alternative to 'blame', but still potentially CPU-intensive.
@@ -1730,6 +1736,9 @@ EOF
 			print " / $action";
 		}
 		print "\n";
+	}
+	my ($have_search) = gitweb_check_feature('search');
+	if ((defined $project) && ($have_search)) {
 		if (!defined $searchtext) {
 			$searchtext = "";
 		}
@@ -4151,6 +4160,10 @@ sub git_history {
 }
 
 sub git_search {
+	my ($have_search) = gitweb_check_feature('search');
+	if (!$have_search) {
+		die_error('403 Permission denied', "Permission denied");
+	}
 	if (!defined $searchtext) {
 		die_error(undef, "Text field empty");
 	}

From 58748293f6fa450c6db89a78bd20aae7bf708d3b Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Fri, 22 Dec 2006 23:42:30 -0800
Subject: [PATCH 293/330] reflog expire: do not punt on tags that point at non
 commits.

It is unusual for a tag to point at a non-commit, and it is also
unusual for a tag to have reflog, but that is not an error and
we should still prune its reflog entries just as other refs.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-reflog.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/builtin-reflog.c b/builtin-reflog.c
index 4097c328cc..de31967b99 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -80,7 +80,8 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 		goto prune;
 
 	if ((timestamp < cb->expire_unreachable) &&
-	    ((old && !in_merge_bases(old, cb->ref_commit)) ||
+	    (!cb->ref_commit ||
+	     (old && !in_merge_bases(old, cb->ref_commit)) ||
 	     (new && !in_merge_bases(new, cb->ref_commit))))
 		goto prune;
 
@@ -126,10 +127,9 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
 	}
 
 	cb.ref_commit = lookup_commit_reference_gently(sha1, 1);
-	if (!cb.ref_commit) {
-		status = error("ref '%s' does not point at a commit", ref);
-		goto finish;
-	}
+	if (!cb.ref_commit)
+		fprintf(stderr,
+			"warning: ref '%s' does not point at a commit\n", ref);
 	cb.ref = ref;
 	cb.expire_total = cmd->expire_total;
 	cb.expire_unreachable = cmd->expire_unreachable;

From d9606e85cdf26b738786b5e5289bf8335656e95e Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sat, 23 Dec 2006 03:44:00 -0500
Subject: [PATCH 294/330] Use extended SHA1 syntax in merge-recursive
 conflicts.

When we get a line-level conflict in merge-recursive and print out
the two sides in the conflict hunk header and footer we should use
the standard extended SHA1 syntax to specify the specific blob,
as this allows the user to copy and paste the line right into
'git show' to view the complete version.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 merge-recursive.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/merge-recursive.c b/merge-recursive.c
index ae7ae4cd2a..abebb950ab 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -649,8 +649,8 @@ static struct merge_file_info merge_file(struct diff_filespec *o,
 			char *name1, *name2;
 			int merge_status;
 
-			name1 = xstrdup(mkpath("%s/%s", branch1, a->path));
-			name2 = xstrdup(mkpath("%s/%s", branch2, b->path));
+			name1 = xstrdup(mkpath("%s:%s", branch1, a->path));
+			name2 = xstrdup(mkpath("%s:%s", branch2, b->path));
 
 			fill_mm(o->sha1, &orig);
 			fill_mm(a->sha1, &src1);

From e0ec18192db8a5f80705a81dfaa1caa3e6c48c1a Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sat, 23 Dec 2006 03:44:47 -0500
Subject: [PATCH 295/330] Display 'theirs' branch name when possible in merge.

Displaying the SHA1 of 'their' branch (the branch being merged into
the current branch) is not nearly as friendly as just displaying
the name of that branch, especially if that branch is already local
to this repository.

git-merge now sets the environment variable 'GITHEAD_%(sha1)=%(name)'
for each argument it gets passed, making the actual input name that
resolved to the commit '%(sha1)' easily available to the invoked
merge strategy.

git-merge-recursive makes use of these environment variables when
they are available by using '%(name)' whenever it outputs the commit
identification rather than '%(sha1)'.  This is most obvious in the
conflict hunks created by xdl_merge:

  $ git mege sideb~1
  <<<<<<< HEAD:INSTALL
     Good!
  =======
     Oops.
  >>>>>>> sideb~1:INSTALL

[jc: adjusted a test script and a minor constness glitch.]

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-merge.sh               |  2 ++
 merge-recursive.c          | 17 ++++++++++++++++-
 t/t6024-recursive-merge.sh |  6 +++---
 3 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/git-merge.sh b/git-merge.sh
index aec215e725..7dd0a11236 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -221,6 +221,8 @@ do
 	remotehead=$(git-rev-parse --verify "$remote"^0 2>/dev/null) ||
 	    die "$remote - not something we can merge"
 	remoteheads="${remoteheads}$remotehead "
+	eval GITHEAD_$remotehead='"$remote"'
+	export GITHEAD_$remotehead
 done
 set x $remoteheads ; shift
 
diff --git a/merge-recursive.c b/merge-recursive.c
index abebb950ab..ca4f19e34d 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1263,6 +1263,18 @@ static struct commit *get_ref(const char *ref)
 	return (struct commit *)object;
 }
 
+static const char *better_branch_name(const char *branch)
+{
+	static char githead_env[8 + 40 + 1];
+	char *name;
+
+	if (strlen(branch) != 40)
+		return branch;
+	sprintf(githead_env, "GITHEAD_%s", branch);
+	name = getenv(githead_env);
+	return name ? name : branch;
+}
+
 int main(int argc, char *argv[])
 {
 	static const char *bases[2];
@@ -1293,11 +1305,14 @@ int main(int argc, char *argv[])
 
 	branch1 = argv[++i];
 	branch2 = argv[++i];
-	printf("Merging %s with %s\n", branch1, branch2);
 
 	h1 = get_ref(branch1);
 	h2 = get_ref(branch2);
 
+	branch1 = better_branch_name(branch1);
+	branch2 = better_branch_name(branch2);
+	printf("Merging %s with %s\n", branch1, branch2);
+
 	if (bases_count == 1) {
 		struct commit *ancestor = get_ref(bases[0]);
 		clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 964010e764..69b18f7d81 100644
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -59,18 +59,18 @@ GIT_AUTHOR_DATE="2006-12-12 23:00:08" git commit -m F
 test_expect_failure "combined merge conflicts" "git merge -m final G"
 
 cat > expect << EOF
-<<<<<<< HEAD/a1
+<<<<<<< HEAD:a1
 F
 =======
 G
->>>>>>> 26f86b677eb03d4d956dbe108b29cb77061c1e73/a1
+>>>>>>> G:a1
 EOF
 
 test_expect_success "result contains a conflict" "diff -u expect a1"
 
 git ls-files --stage > out
 cat > expect << EOF
-100644 f16f906ab60483c100d1241dfc39868de9ec9fcb 1	a1
+100644 da056ce14a2241509897fa68bb2b3b6e6194ef9e 1	a1
 100644 cf84443e49e1b366fac938711ddf4be2d4d1d9e9 2	a1
 100644 fd7923529855d0b274795ae3349c5e0438333979 3	a1
 EOF

From 6538d1ef31fc2989e4dd0422a31a170a7d560b02 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Sat, 23 Dec 2006 08:26:09 -0800
Subject: [PATCH 296/330] Makefile: add quick-install-doc for installing
 pre-built manpages

This adds and uses the install-doc-quick.sh file to
Documentation/, which is usable for people who track either the
'html' or 'man' heads in Junio's repository (prefixed with
'origin/' if cloned locally).  You may override this by
specifying DOC_REF in the make environment or in config.mak.

GZ may also be set in the environment (or config.mak) if you
wish to gzip the documentation after installing it.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/Makefile             |  4 ++++
 Documentation/install-doc-quick.sh | 31 ++++++++++++++++++++++++++++++
 Makefile                           |  2 ++
 3 files changed, 37 insertions(+)
 create mode 100755 Documentation/install-doc-quick.sh

diff --git a/Documentation/Makefile b/Documentation/Makefile
index d68bc4a788..93c7024b48 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -32,6 +32,7 @@ man7dir=$(mandir)/man7
 # DESTDIR=
 
 INSTALL?=install
+DOC_REF = origin/man
 
 -include ../config.mak.autogen
 
@@ -112,3 +113,6 @@ $(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
 
 install-webdoc : html
 	sh ./install-webdoc.sh $(WEBDOC_DEST)
+
+quick-install:
+	sh ./install-doc-quick.sh $(DOC_REF) $(mandir)
diff --git a/Documentation/install-doc-quick.sh b/Documentation/install-doc-quick.sh
new file mode 100755
index 0000000000..a64054948a
--- /dev/null
+++ b/Documentation/install-doc-quick.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+# This requires a branch named in $head
+# (usually 'man' or 'html', provided by the git.git repository)
+set -e
+head="$1"
+mandir="$2"
+SUBDIRECTORY_OK=t
+USAGE='<refname> <target directory>'
+. git-sh-setup
+export GIT_DIR
+
+test -z "$mandir" && usage
+if ! git-rev-parse --verify "$head^0" >/dev/null; then
+	echo >&2 "head: $head does not exist in the current repository"
+	usage
+fi
+
+GIT_INDEX_FILE=`pwd`/.quick-doc.index
+export GIT_INDEX_FILE
+rm -f "$GIT_INDEX_FILE"
+git-read-tree $head
+git-checkout-index -a -f --prefix="$mandir"/
+
+if test -n "$GZ"; then
+	cd "$mandir"
+	for i in `git-ls-tree -r --name-only $head`
+	do
+		gzip < $i > $i.gz && rm $i
+	done
+fi
+rm -f "$GIT_INDEX_FILE"
diff --git a/Makefile b/Makefile
index 4362297e00..ebc1a17e70 100644
--- a/Makefile
+++ b/Makefile
@@ -824,6 +824,8 @@ install: all
 install-doc:
 	$(MAKE) -C Documentation install
 
+quick-install-doc:
+	$(MAKE) -C Documentation quick-install
 
 
 

From 8dbc0fcef4912f7814b7822324b3c1943e55865b Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sat, 23 Dec 2006 14:57:12 +0000
Subject: [PATCH 297/330] gitweb: Paginate commit/author/committer search
 output

Paginate commit/author/committer search output to only show 100 commits
at a time, added appropriate nav links.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 148 +++++++++++++++++++++++++++++++--------------
 1 file changed, 103 insertions(+), 45 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 9a4f3b4841..d01d689348 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2852,6 +2852,58 @@ sub git_heads_body {
 	print "</table>\n";
 }
 
+sub git_search_grep_body {
+	my ($greplist, $from, $to, $extra) = @_;
+	$from = 0 unless defined $from;
+	$to = $#{$greplist} if (!defined $to || $#{$greplist} < $to);
+
+	print "<table class=\"grep\" cellspacing=\"0\">\n";
+	my $alternate = 1;
+	for (my $i = $from; $i <= $to; $i++) {
+		my $commit = $greplist->[$i];
+		my %co = parse_commit($commit);
+		if (!%co) {
+			next;
+		}
+		if ($alternate) {
+			print "<tr class=\"dark\">\n";
+		} else {
+			print "<tr class=\"light\">\n";
+		}
+		$alternate ^= 1;
+		print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
+		      "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
+		      "<td>" .
+		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
+			       esc_html(chop_str($co{'title'}, 50)) . "<br/>");
+		my $comment = $co{'comment'};
+		foreach my $line (@$comment) {
+			if ($line =~ m/^(.*)($searchtext)(.*)$/i) {
+				my $lead = esc_html($1) || "";
+				$lead = chop_str($lead, 30, 10);
+				my $match = esc_html($2) || "";
+				my $trail = esc_html($3) || "";
+				$trail = chop_str($trail, 30, 10);
+				my $text = "$lead<span class=\"match\">$match</span>$trail";
+				print chop_str($text, 80, 5) . "<br/>\n";
+			}
+		}
+		print "</td>\n" .
+		      "<td class=\"link\">" .
+		      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
+		      " | " .
+		      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
+		print "</td>\n" .
+		      "</tr>\n";
+	}
+	if (defined $extra) {
+		print "<tr>\n" .
+		      "<td colspan=\"3\">$extra</td>\n" .
+		      "</tr>\n";
+	}
+	print "</table>\n";
+}
+
 ## ======================================================================
 ## ======================================================================
 ## actions
@@ -4174,6 +4226,9 @@ sub git_search {
 	if (!%co) {
 		die_error(undef, "Unknown commit object");
 	}
+	if (!defined $page) {
+		$page = 0;
+	}
 
 	$searchtype ||= 'commit';
 	if ($searchtype eq 'pickaxe') {
@@ -4186,11 +4241,7 @@ sub git_search {
 	}
 
 	git_header_html();
-	git_print_page_nav('','', $hash,$co{'tree'},$hash);
-	git_print_header_div('commit', esc_html($co{'title'}), $hash);
 
-	print "<table cellspacing=\"0\">\n";
-	my $alternate = 1;
 	if ($searchtype eq 'commit' or $searchtype eq 'author' or $searchtype eq 'committer') {
 		my $greptype;
 		if ($searchtype eq 'commit') {
@@ -4200,52 +4251,58 @@ sub git_search {
 		} elsif ($searchtype eq 'committer') {
 			$greptype = "--committer=";
 		}
-		$/ = "\0";
 		open my $fd, "-|", git_cmd(), "rev-list",
-			"--header", "--parents", ($greptype . $searchtext),
-			 $hash, "--"
+			("--max-count=" . (100 * ($page+1))),
+			($greptype . $searchtext),
+			$hash, "--"
 			or next;
-		while (my $commit_text = <$fd>) {
-			my @commit_lines = split "\n", $commit_text;
-			my %co = parse_commit(undef, \@commit_lines);
-			if (!%co) {
-				next;
-			}
-			if ($alternate) {
-				print "<tr class=\"dark\">\n";
-			} else {
-				print "<tr class=\"light\">\n";
-			}
-			$alternate ^= 1;
-			print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-			      "<td><i>" . esc_html(chop_str($co{'author_name'}, 15, 5)) . "</i></td>\n" .
-			      "<td>" .
-			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}), -class => "list subject"},
-			               esc_html(chop_str($co{'title'}, 50)) . "<br/>");
-			my $comment = $co{'comment'};
-			foreach my $line (@$comment) {
-				if ($line =~ m/^(.*)($searchtext)(.*)$/i) {
-					my $lead = esc_html($1) || "";
-					$lead = chop_str($lead, 30, 10);
-					my $match = esc_html($2) || "";
-					my $trail = esc_html($3) || "";
-					$trail = chop_str($trail, 30, 10);
-					my $text = "$lead<span class=\"match\">$match</span>$trail";
-					print chop_str($text, 80, 5) . "<br/>\n";
-				}
-			}
-			print "</td>\n" .
-			      "<td class=\"link\">" .
-			      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'})}, "commit") .
-			      " | " .
-			      $cgi->a({-href => href(action=>"tree", hash=>$co{'tree'}, hash_base=>$co{'id'})}, "tree");
-			print "</td>\n" .
-			      "</tr>\n";
-		}
+		my @revlist = map { chomp; $_ } <$fd>;
 		close $fd;
+
+		my $paging_nav = '';
+		if ($page > 0) {
+			$paging_nav .=
+				$cgi->a({-href => href(action=>"search", hash=>$hash,
+						       searchtext=>$searchtext, searchtype=>$searchtype)},
+					"first");
+			$paging_nav .= " &sdot; " .
+				$cgi->a({-href => href(action=>"search", hash=>$hash,
+						       searchtext=>$searchtext, searchtype=>$searchtype,
+						       page=>$page-1),
+					 -accesskey => "p", -title => "Alt-p"}, "prev");
+		} else {
+			$paging_nav .= "first";
+			$paging_nav .= " &sdot; prev";
+		}
+		if ($#revlist >= (100 * ($page+1)-1)) {
+			$paging_nav .= " &sdot; " .
+				$cgi->a({-href => href(action=>"search", hash=>$hash,
+						       searchtext=>$searchtext, searchtype=>$searchtype,
+						       page=>$page+1),
+					 -accesskey => "n", -title => "Alt-n"}, "next");
+		} else {
+			$paging_nav .= " &sdot; next";
+		}
+		my $next_link = '';
+		if ($#revlist >= (100 * ($page+1)-1)) {
+			$next_link =
+				$cgi->a({-href => href(action=>"search", hash=>$hash,
+						       searchtext=>$searchtext, searchtype=>$searchtype,
+						       page=>$page+1),
+					 -accesskey => "n", -title => "Alt-n"}, "next");
+		}
+
+		git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
+		git_print_header_div('commit', esc_html($co{'title'}), $hash);
+		git_search_grep_body(\@revlist, ($page * 100), $#revlist, $next_link);
 	}
 
 	if ($searchtype eq 'pickaxe') {
+		git_print_page_nav('','', $hash,$co{'tree'},$hash);
+		git_print_header_div('commit', esc_html($co{'title'}), $hash);
+
+		print "<table cellspacing=\"0\">\n";
+		my $alternate = 1;
 		$/ = "\n";
 		my $git_command = git_cmd_str();
 		open my $fd, "-|", "$git_command rev-list $hash | " .
@@ -4300,8 +4357,9 @@ sub git_search {
 			}
 		}
 		close $fd;
+
+		print "</table>\n";
 	}
-	print "</table>\n";
 	git_footer_html();
 }
 

From d6779124b90ef5c00697815b04c9f75fd3756586 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sun, 24 Dec 2006 00:45:37 -0500
Subject: [PATCH 298/330] Rename gitfakemmap to git_mmap.

This minor cleanup was suggested by Johannes Schindelin.

The mmap is still fake in the sense that we don't support PROT_WRITE
or MAP_SHARED with external modification at all, but that hasn't
stopped us from using mmap() thoughout the Git code.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 compat/mmap.c     | 6 +++---
 git-compat-util.h | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/compat/mmap.c b/compat/mmap.c
index 0fd46e793d..bb34c7e95f 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -1,12 +1,12 @@
 #include "../git-compat-util.h"
 
-void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
+void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
 {
 	int n = 0;
 	off_t current_offset = lseek(fd, 0, SEEK_CUR);
 
 	if (start != NULL || !(flags & MAP_PRIVATE))
-		die("Invalid usage of gitfakemmap.");
+		die("Invalid usage of mmap when built with NO_MMAP");
 
 	if (lseek(fd, offset, SEEK_SET) < 0) {
 		errno = EINVAL;
@@ -44,7 +44,7 @@ void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_
 	return start;
 }
 
-int gitfakemunmap(void *start, size_t length)
+int git_munmap(void *start, size_t length)
 {
 	free(start);
 	return 0;
diff --git a/git-compat-util.h b/git-compat-util.h
index f79365b362..5d9eb2615b 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -87,10 +87,10 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
 #define MAP_FAILED ((void*)-1)
 #endif
 
-#define mmap gitfakemmap
-#define munmap gitfakemunmap
-extern void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
-extern int gitfakemunmap(void *start, size_t length);
+#define mmap git_mmap
+#define munmap git_munmap
+extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
+extern int git_munmap(void *start, size_t length);
 
 #else /* NO_MMAP */
 

From 8e554429e8a9dbb5dba6b8aeca66fc27c0bb5538 Mon Sep 17 00:00:00 2001
From: "Shawn O. Pearce" <spearce@spearce.org>
Date: Sun, 24 Dec 2006 00:45:47 -0500
Subject: [PATCH 299/330] Switch git_mmap to use pread.

Now that Git depends on pread in index-pack its safe to say we can
also depend on it within the git_mmap emulation we activate when
NO_MMAP is set.  On most systems pread should be slightly faster
than an lseek/read/lseek sequence as its one system call vs. three
system calls.

We also now honor EAGAIN and EINTR error codes from pread and
restart the prior read.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 compat/mmap.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/compat/mmap.c b/compat/mmap.c
index bb34c7e95f..4cfaee3136 100644
--- a/compat/mmap.c
+++ b/compat/mmap.c
@@ -2,17 +2,11 @@
 
 void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
 {
-	int n = 0;
-	off_t current_offset = lseek(fd, 0, SEEK_CUR);
+	size_t n = 0;
 
 	if (start != NULL || !(flags & MAP_PRIVATE))
 		die("Invalid usage of mmap when built with NO_MMAP");
 
-	if (lseek(fd, offset, SEEK_SET) < 0) {
-		errno = EINVAL;
-		return MAP_FAILED;
-	}
-
 	start = xmalloc(length);
 	if (start == NULL) {
 		errno = ENOMEM;
@@ -20,14 +14,16 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
 	}
 
 	while (n < length) {
-		int count = read(fd, start+n, length-n);
+		ssize_t count = pread(fd, (char *)start + n, length - n, offset + n);
 
 		if (count == 0) {
-			memset(start+n, 0, length-n);
+			memset((char *)start+n, 0, length-n);
 			break;
 		}
 
 		if (count < 0) {
+			if (errno == EAGAIN || errno == EINTR)
+				continue;
 			free(start);
 			errno = EACCES;
 			return MAP_FAILED;
@@ -36,11 +32,6 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of
 		n += count;
 	}
 
-	if (current_offset != lseek(fd, current_offset, SEEK_SET)) {
-		errno = EINVAL;
-		return MAP_FAILED;
-	}
-
 	return start;
 }
 

From 9e83266525aad5c6210b9a21be9b1c6996d5544f Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Fri, 22 Dec 2006 22:06:08 +0100
Subject: [PATCH 300/330] commit-tree: encourage UTF-8 commit messages.

Introduce is_utf() to check if a text looks like it is encoded
in UTF-8, utf8_width() to count display width, and implements
print_wrapped_text() using them.

git-commit-tree warns if the commit message does not minimally
conform to the UTF-8 encoding when i18n.commitencoding is either
unset, or set to "utf-8".

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Makefile              |   6 +-
 builtin-commit-tree.c |  15 ++-
 utf8.c                | 278 ++++++++++++++++++++++++++++++++++++++++++
 utf8.h                |   8 ++
 4 files changed, 303 insertions(+), 4 deletions(-)
 create mode 100644 utf8.c
 create mode 100644 utf8.h

diff --git a/Makefile b/Makefile
index ebc1a17e70..d56970c569 100644
--- a/Makefile
+++ b/Makefile
@@ -230,7 +230,8 @@ LIB_H = \
 	archive.h blob.h cache.h commit.h csum-file.h delta.h grep.h \
 	diff.h object.h pack.h pkt-line.h quote.h refs.h list-objects.h sideband.h \
 	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-	tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h
+	tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
+	utf8.h
 
 DIFF_OBJS = \
 	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -249,7 +250,8 @@ LIB_OBJS = \
 	revision.o pager.o tree-walk.o xdiff-interface.o \
 	write_or_die.o trace.o list-objects.o grep.o \
 	alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
-	color.o wt-status.o archive-zip.o archive-tar.o
+	color.o wt-status.o archive-zip.o archive-tar.o \
+	utf8.o
 
 BUILTIN_OBJS = \
 	builtin-add.o \
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index bc28770664..f641787988 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -7,6 +7,7 @@
 #include "commit.h"
 #include "tree.h"
 #include "builtin.h"
+#include "utf8.h"
 
 #define BLOCKING (1ul << 14)
 
@@ -32,7 +33,7 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
 	len = vsnprintf(one_line, sizeof(one_line), fmt, args);
 	va_end(args);
 	size = *sizep;
-	newsize = size + len;
+	newsize = size + len + 1;
 	alloc = (size + 32767) & ~32767;
 	buf = *bufp;
 	if (newsize > alloc) {
@@ -40,7 +41,7 @@ static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
 		buf = xrealloc(buf, alloc);
 		*bufp = buf;
 	}
-	*sizep = newsize;
+	*sizep = newsize - 1;
 	memcpy(buf + size, one_line, len);
 }
 
@@ -77,6 +78,11 @@ static int new_parent(int idx)
 	return 1;
 }
 
+static const char commit_utf8_warn[] =
+"Warning: commit message does not conform to UTF-8.\n"
+"You may want to amend it after fixing the message, or set the config\n"
+"variable i18n.commitencoding to the encoding your project uses.\n";
+
 int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 {
 	int i;
@@ -130,6 +136,11 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 	while (fgets(comment, sizeof(comment), stdin) != NULL)
 		add_buffer(&buffer, &size, "%s", comment);
 
+	/* And check the encoding */
+	buffer[size] = '\0';
+	if (!strcmp(git_commit_encoding, "utf-8") && !is_utf8(buffer))
+		fprintf(stderr, commit_utf8_warn);
+
 	if (!write_sha1_file(buffer, size, commit_type, commit_sha1)) {
 		printf("%s\n", sha1_to_hex(commit_sha1));
 		return 0;
diff --git a/utf8.c b/utf8.c
new file mode 100644
index 0000000000..8fa62571aa
--- /dev/null
+++ b/utf8.c
@@ -0,0 +1,278 @@
+#include "git-compat-util.h"
+#include "utf8.h"
+
+/* This code is originally from http://www.cl.cam.ac.uk/~mgk25/ucs/ */
+
+struct interval {
+  int first;
+  int last;
+};
+
+/* auxiliary function for binary search in interval table */
+static int bisearch(wchar_t ucs, const struct interval *table, int max) {
+	int min = 0;
+	int mid;
+
+	if (ucs < table[0].first || ucs > table[max].last)
+		return 0;
+	while (max >= min) {
+		mid = (min + max) / 2;
+		if (ucs > table[mid].last)
+			min = mid + 1;
+		else if (ucs < table[mid].first)
+			max = mid - 1;
+		else
+			return 1;
+	}
+
+	return 0;
+}
+
+/* The following two functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ *    - The null character (U+0000) has a column width of 0.
+ *
+ *    - Other C0/C1 control characters and DEL will lead to a return
+ *      value of -1.
+ *
+ *    - Non-spacing and enclosing combining characters (general
+ *      category code Mn or Me in the Unicode database) have a
+ *      column width of 0.
+ *
+ *    - SOFT HYPHEN (U+00AD) has a column width of 1.
+ *
+ *    - Other format characters (general category code Cf in the Unicode
+ *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ *      have a column width of 0.
+ *
+ *    - Spacing characters in the East Asian Wide (W) or East Asian
+ *      Full-width (F) category as defined in Unicode Technical
+ *      Report #11 have a column width of 2.
+ *
+ *    - All remaining characters (including all printable
+ *      ISO 8859-1 and WGL4 characters, Unicode control characters,
+ *      etc.) have a column width of 1.
+ *
+ * This implementation assumes that wchar_t characters are encoded
+ * in ISO 10646.
+ */
+
+static int wcwidth(wchar_t ch)
+{
+	/*
+	 * Sorted list of non-overlapping intervals of non-spacing characters,
+	 * generated by
+	 *   "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c".
+	 */
+	static const struct interval combining[] = {
+		{ 0x0300, 0x0357 }, { 0x035D, 0x036F }, { 0x0483, 0x0486 },
+		{ 0x0488, 0x0489 }, { 0x0591, 0x05A1 }, { 0x05A3, 0x05B9 },
+		{ 0x05BB, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+		{ 0x05C4, 0x05C4 }, { 0x0600, 0x0603 }, { 0x0610, 0x0615 },
+		{ 0x064B, 0x0658 }, { 0x0670, 0x0670 }, { 0x06D6, 0x06E4 },
+		{ 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, { 0x070F, 0x070F },
+		{ 0x0711, 0x0711 }, { 0x0730, 0x074A }, { 0x07A6, 0x07B0 },
+		{ 0x0901, 0x0902 }, { 0x093C, 0x093C }, { 0x0941, 0x0948 },
+		{ 0x094D, 0x094D }, { 0x0951, 0x0954 }, { 0x0962, 0x0963 },
+		{ 0x0981, 0x0981 }, { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 },
+		{ 0x09CD, 0x09CD }, { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 },
+		{ 0x0A3C, 0x0A3C }, { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 },
+		{ 0x0A4B, 0x0A4D }, { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 },
+		{ 0x0ABC, 0x0ABC }, { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 },
+		{ 0x0ACD, 0x0ACD }, { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 },
+		{ 0x0B3C, 0x0B3C }, { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 },
+		{ 0x0B4D, 0x0B4D }, { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 },
+		{ 0x0BC0, 0x0BC0 }, { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 },
+		{ 0x0C46, 0x0C48 }, { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 },
+		{ 0x0CBC, 0x0CBC }, { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 },
+		{ 0x0CCC, 0x0CCD }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
+		{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
+		{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
+		{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
+		{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
+		{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
+		{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
+		{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
+		{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
+		{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x1712, 0x1714 },
+		{ 0x1732, 0x1734 }, { 0x1752, 0x1753 }, { 0x1772, 0x1773 },
+		{ 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, { 0x17C6, 0x17C6 },
+		{ 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, { 0x180B, 0x180D },
+		{ 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, { 0x1927, 0x1928 },
+		{ 0x1932, 0x1932 }, { 0x1939, 0x193B }, { 0x200B, 0x200F },
+		{ 0x202A, 0x202E }, { 0x2060, 0x2063 }, { 0x206A, 0x206F },
+		{ 0x20D0, 0x20EA }, { 0x302A, 0x302F }, { 0x3099, 0x309A },
+		{ 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE20, 0xFE23 },
+		{ 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, { 0x1D167, 0x1D169 },
+		{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B },
+		{ 0x1D1AA, 0x1D1AD }, { 0xE0001, 0xE0001 },
+		{ 0xE0020, 0xE007F }, { 0xE0100, 0xE01EF }
+	};
+
+	/* test for 8-bit control characters */
+	if (ch == 0)
+		return 0;
+	if (ch < 32 || (ch >= 0x7f && ch < 0xa0))
+		return -1;
+
+	/* binary search in table of non-spacing characters */
+	if (bisearch(ch, combining, sizeof(combining)
+				/ sizeof(struct interval) - 1))
+		return 0;
+
+	/*
+	 * If we arrive here, ch is neither a combining nor a C0/C1
+	 * control character.
+	 */
+
+	return 1 +
+		(ch >= 0x1100 &&
+                    /* Hangul Jamo init. consonants */
+		 (ch <= 0x115f ||
+		  ch == 0x2329 || ch == 0x232a ||
+                  /* CJK ... Yi */
+		  (ch >= 0x2e80 && ch <= 0xa4cf &&
+		   ch != 0x303f) ||
+		  /* Hangul Syllables */
+		  (ch >= 0xac00 && ch <= 0xd7a3) ||
+		  /* CJK Compatibility Ideographs */
+		  (ch >= 0xf900 && ch <= 0xfaff) ||
+		  /* CJK Compatibility Forms */
+		  (ch >= 0xfe30 && ch <= 0xfe6f) ||
+		  /* Fullwidth Forms */
+		  (ch >= 0xff00 && ch <= 0xff60) ||
+		  (ch >= 0xffe0 && ch <= 0xffe6) ||
+		  (ch >= 0x20000 && ch <= 0x2fffd) ||
+		  (ch >= 0x30000 && ch <= 0x3fffd)));
+}
+
+/*
+ * This function returns the number of columns occupied by the character
+ * pointed to by the variable start. The pointer is updated to point at
+ * the next character. If it was not valid UTF-8, the pointer is set to NULL.
+ */
+int utf8_width(const char **start)
+{
+	unsigned char *s = (unsigned char *)*start;
+	wchar_t ch;
+
+	if (*s < 0x80) {
+		/* 0xxxxxxx */
+		ch = *s;
+		*start += 1;
+	} else if ((s[0] & 0xe0) == 0xc0) {
+		/* 110XXXXx 10xxxxxx */
+		if ((s[1] & 0xc0) != 0x80 ||
+				/* overlong? */
+				(s[0] & 0xfe) == 0xc0)
+			goto invalid;
+		ch = ((s[0] & 0x1f) << 6) | (s[1] & 0x3f);
+		*start += 2;
+	} else if ((s[0] & 0xf0) == 0xe0) {
+		/* 1110XXXX 10Xxxxxx 10xxxxxx */
+		if ((s[1] & 0xc0) != 0x80 ||
+				(s[2] & 0xc0) != 0x80 ||
+				/* overlong? */
+				(s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) ||
+				/* surrogate? */
+				(s[0] == 0xed && (s[1] & 0xe0) == 0xa0) ||
+				/* U+FFFE or U+FFFF? */
+				(s[0] == 0xef && s[1] == 0xbf &&
+				 (s[2] & 0xfe) == 0xbe))
+			goto invalid;
+		ch = ((s[0] & 0x0f) << 12) |
+			((s[1] & 0x3f) << 6) | (s[2] & 0x3f);
+		*start += 3;
+	} else if ((s[0] & 0xf8) == 0xf0) {
+		/* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */
+		if ((s[1] & 0xc0) != 0x80 ||
+				(s[2] & 0xc0) != 0x80 ||
+				(s[3] & 0xc0) != 0x80 ||
+				/* overlong? */
+				(s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) ||
+				/* > U+10FFFF? */
+				(s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4)
+			goto invalid;
+		ch = ((s[0] & 0x07) << 18) | ((s[1] & 0x3f) << 12) |
+			((s[2] & 0x3f) << 6) | (s[3] & 0x3f);
+		*start += 4;
+	} else {
+invalid:
+		*start = NULL;
+		return 0;
+	}
+
+	return wcwidth(ch);
+}
+
+int is_utf8(const char *text)
+{
+	while (*text) {
+		if (*text == '\n' || *text == '\t' || *text == '\r') {
+			text++;
+			continue;
+		}
+		utf8_width(&text);
+		if (!text)
+			return 0;
+	}
+	return 1;
+}
+
+static void print_spaces(int count)
+{
+	static const char s[] = "                    ";
+	while (count >= sizeof(s)) {
+		fwrite(s, sizeof(s) - 1, 1, stdout);
+		count -= sizeof(s) - 1;
+	}
+	fwrite(s, count, 1, stdout);
+}
+
+/*
+ * Wrap the text, if necessary. The variable indent is the indent for the
+ * first line, indent2 is the indent for all other lines.
+ */
+void print_wrapped_text(const char *text, int indent, int indent2, int width)
+{
+	int w = indent, assume_utf8 = is_utf8(text);
+	const char *bol = text, *space = NULL;
+
+	for (;;) {
+		char c = *text;
+		if (!c || isspace(c)) {
+			if (w < width || !space) {
+				const char *start = bol;
+				if (space)
+					start = space;
+				else
+					print_spaces(indent);
+				fwrite(start, text - start, 1, stdout);
+				if (!c) {
+					putchar('\n');
+					return;
+				} else if (c == '\t')
+					w |= 0x07;
+				space = text;
+				w++;
+				text++;
+			}
+			else {
+				putchar('\n');
+				text = bol = space + 1;
+				space = NULL;
+				w = indent = indent2;
+			}
+			continue;
+		}
+		if (assume_utf8)
+			w += utf8_width(&text);
+		else {
+			w++;
+			text++;
+		}
+	}
+}
diff --git a/utf8.h b/utf8.h
new file mode 100644
index 0000000000..a0d7f591ad
--- /dev/null
+++ b/utf8.h
@@ -0,0 +1,8 @@
+#ifndef GIT_UTF8_H
+#define GIT_UTF8_H
+
+int utf8_width(const char **start);
+int is_utf8(const char *text);
+void print_wrapped_text(const char *text, int indent, int indent2, int len);
+
+#endif

From 80c797764a6b6a373f0f1f47d7f56b0d950418a9 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Sun, 24 Dec 2006 01:59:53 -0800
Subject: [PATCH 301/330] Allow branch.*.merge to talk about remote tracking
 branches.

People often get confused if the value of branch.*.merge should
be the remote branch name they are fetching from, or the
tracking branch they locally have.  So this allows either.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-parse-remote.sh | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/git-parse-remote.sh b/git-parse-remote.sh
index aaef861ada..144f170155 100755
--- a/git-parse-remote.sh
+++ b/git-parse-remote.sh
@@ -146,8 +146,12 @@ canon_refs_list_for_fetch () {
 		else
 			for merge_branch in $merge_branches
 			do
-			    [ "$remote" = "$merge_branch" ] &&
-			    dot_prefix= && break
+			    if	test "$remote" = "$merge_branch" ||
+				test "$local" = "$merge_branch"
+			    then
+				    dot_prefix=
+				    break
+			    fi
 			done
 		fi
 		case "$remote" in

From 6a5ad23de6aa95b35d90d631062e5a353e59d3f2 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 01:30:55 -0800
Subject: [PATCH 302/330] git-add --interactive: add documentation

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-add.txt | 119 +++++++++++++++++++++++++++++++++++++-
 builtin-add.c             |   2 +-
 2 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index d86c0e7f19..8710b3a75e 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -7,7 +7,7 @@ git-add - Add file contents to the changeset to be committed next
 
 SYNOPSIS
 --------
-'git-add' [-n] [-v] [--] <file>...
+'git-add' [-n] [-v] [--interactive] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -43,6 +43,10 @@ OPTIONS
 -v::
         Be verbose.
 
+\--interactive::
+	Add modified contents in the working tree interactively to
+	the index.
+
 \--::
 	This option can be used to separate command-line options from
 	the list of files, (useful when filenames might be mistaken
@@ -67,6 +71,119 @@ git-add git-*.sh::
 	(i.e. you are listing the files explicitly), it does not
 	consider `subdir/git-foo.sh`.
 
+Interactive mode
+----------------
+When the command enters the interactive mode, it shows the
+output of the 'status' subcommand, and then goes into ints
+interactive command loop.
+
+The command loop shows the list of subcommands available, and
+gives a prompt "What now> ".  In general, when the prompt ends
+with a single '>', you can pick only one of the choices given
+and type return, like this:
+
+------------
+    *** Commands ***
+      1: status       2: update       3: revert       4: add untracked
+      5: patch        6: diff         7: quit         8: help
+    What now> 1
+------------
+
+You also could say "s" or "sta" or "status" above as long as the
+choice is unique.
+
+The main command loop has 6 subcommands (plus help and quit).
+
+status::
+
+   This shows the change between HEAD and index (i.e. what will be
+   committed if you say "git commit"), and between index and
+   working tree files (i.e. what you could stage further before
+   "git commit" using "git-add") for each path.  A sample output
+   looks like this:
++
+------------
+              staged     unstaged path
+     1:       binary      nothing foo.png
+     2:     +403/-35        +1/-1 git-add--interactive.perl
+------------
++
+It shows that foo.png has differences from HEAD (but that is
+binary so line count cannot be shown) and there is no
+difference between indexed copy and the working tree
+version (if the working tree version were also different,
+'binary' would have been shown in place of 'nothing').  The
+other file, git-add--interactive.perl, has 403 lines added
+and 35 lines deleted if you commit what is in the index, but
+working tree file has further modifications (one addition and
+one deletion).
+
+update::
+
+   This shows the status information and gives prompt
+   "Update>>".  When the prompt ends with double '>>', you can
+   make more than one selection, concatenated with whitespace or
+   comma.  Also you can say ranges.  E.g. "2-5 7,9" to choose
+   2,3,4,5,7,9 from the list.  You can say '*' to choose
+   everything.
++
+What you chose are then highlighted with '*',
+like this:
++
+------------
+           staged     unstaged path
+  1:       binary      nothing foo.png
+* 2:     +403/-35        +1/-1 git-add--interactive.perl
+------------
++
+To remove selection, prefix the input with `-`
+like this:
++
+------------
+Update>> -2
+------------
++
+After making the selection, answer with an empty line to stage the
+contents of working tree files for selected paths in the index.
+
+revert::
+
+  This has a very similar UI to 'update', and the staged
+  information for selected paths are reverted to that of the
+  HEAD version.  Reverting new paths makes them untracked.
+
+add untracked::
+
+  This has a very similar UI to 'update' and
+  'revert', and lets you add untracked paths to the index.
+
+patch::
+
+  This lets you choose one path out of 'status' like selection.
+  After choosing the path, it presents diff between the index
+  and the working tree file and asks you if you want to stage
+  the change of each hunk.  You can say:
+
+       y - add the change from that hunk to index
+       n - do not add the change from that hunk to index
+       a - add the change from that hunk and all the rest to index
+       d - do not the change from that hunk nor any of the rest to index
+       j - do not decide on this hunk now, and view the next
+           undecided hunk
+       J - do not decide on this hunk now, and view the next hunk
+       k - do not decide on this hunk now, and view the previous
+           undecided hunk
+       K - do not decide on this hunk now, and view the previous hunk
++
+After deciding the fate for all hunks, if there is any hunk
+that was chosen, the index is updated with the selected hunks.
+
+diff::
+
+  This lets you review what will be committed (i.e. between
+  HEAD and index).
+
+
 See Also
 --------
 gitlink:git-status[1]
diff --git a/builtin-add.c b/builtin-add.c
index aa2f0f32af..9443bae535 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -12,7 +12,7 @@
 #include "cache-tree.h"
 
 static const char builtin_add_usage[] =
-"git-add [-n] [-v] <filepattern>...";
+"git-add [-n] [-v] [--interactive] [--] <filepattern>...";
 
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {

From e813d50e35653bdb0ce3329f99d1be7fc1c36de5 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:09:52 -0800
Subject: [PATCH 303/330] match_pathspec() -- return how well the spec matched

This updates the return value from match_pathspec() so that the
caller can tell cases between exact match, leading pathname
match (i.e. file "foo/bar" matches a pathspec "foo"), or
filename glob match.  This can be used to prevent "rm dir" from
removing "dir/file" without explicitly asking for recursive
behaviour with -r flag, for example.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 dir.c | 51 +++++++++++++++++++++++++++++++++++----------------
 dir.h |  4 ++++
 2 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/dir.c b/dir.c
index 16401d8017..8477472c03 100644
--- a/dir.c
+++ b/dir.c
@@ -40,6 +40,18 @@ int common_prefix(const char **pathspec)
 	return prefix;
 }
 
+/*
+ * Does 'match' matches the given name?
+ * A match is found if
+ *
+ * (1) the 'match' string is leading directory of 'name', or
+ * (2) the 'match' string is a wildcard and matches 'name', or
+ * (3) the 'match' string is exactly the same as 'name'.
+ *
+ * and the return value tells which case it was.
+ *
+ * It returns 0 when there is no match.
+ */
 static int match_one(const char *match, const char *name, int namelen)
 {
 	int matchlen;
@@ -47,27 +59,30 @@ static int match_one(const char *match, const char *name, int namelen)
 	/* If the match was just the prefix, we matched */
 	matchlen = strlen(match);
 	if (!matchlen)
-		return 1;
+		return MATCHED_RECURSIVELY;
 
 	/*
 	 * If we don't match the matchstring exactly,
 	 * we need to match by fnmatch
 	 */
 	if (strncmp(match, name, matchlen))
-		return !fnmatch(match, name, 0);
+		return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
 
-	/*
-	 * If we did match the string exactly, we still
-	 * need to make sure that it happened on a path
-	 * component boundary (ie either the last character
-	 * of the match was '/', or the next character of
-	 * the name was '/' or the terminating NUL.
-	 */
-	return	match[matchlen-1] == '/' ||
-		name[matchlen] == '/' ||
-		!name[matchlen];
+	if (!name[matchlen])
+		return MATCHED_EXACTLY;
+	if (match[matchlen-1] == '/' || name[matchlen] == '/')
+		return MATCHED_RECURSIVELY;
+	return 0;
 }
 
+/*
+ * Given a name and a list of pathspecs, see if the name matches
+ * any of the pathspecs.  The caller is also interested in seeing
+ * all pathspec matches some names it calls this function with
+ * (otherwise the user could have mistyped the unmatched pathspec),
+ * and a mark is left in seen[] array for pathspec element that
+ * actually matched anything.
+ */
 int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
 {
 	int retval;
@@ -77,12 +92,16 @@ int match_pathspec(const char **pathspec, const char *name, int namelen, int pre
 	namelen -= prefix;
 
 	for (retval = 0; (match = *pathspec++) != NULL; seen++) {
-		if (retval & *seen)
+		int how;
+		if (retval && *seen == MATCHED_EXACTLY)
 			continue;
 		match += prefix;
-		if (match_one(match, name, namelen)) {
-			retval = 1;
-			*seen = 1;
+		how = match_one(match, name, namelen);
+		if (how) {
+			if (retval < how)
+				retval = how;
+			if (*seen < how)
+				*seen = how;
 		}
 	}
 	return retval;
diff --git a/dir.h b/dir.h
index 550551ab25..c919727949 100644
--- a/dir.h
+++ b/dir.h
@@ -40,6 +40,10 @@ struct dir_struct {
 };
 
 extern int common_prefix(const char **pathspec);
+
+#define MATCHED_RECURSIVELY 1
+#define MATCHED_FNMATCH 2
+#define MATCHED_EXACTLY 3
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
 extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen);

From 9f95069beb507f496c8d3005defbaa27318f5347 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:11:00 -0800
Subject: [PATCH 304/330] git-rm: update to saner semantics

This updates the "git rm" command with saner semantics suggested
on the list earlier with:

	Message-ID: <Pine.LNX.4.64.0612020919400.3476@woody.osdl.org>
	Message-ID: <Pine.LNX.4.64.0612040737120.3476@woody.osdl.org>

The command still validates that the given paths all talk about
sensible paths to avoid mistakes (e.g. "git rm fiel" when file
"fiel" does not exist would error out -- user meant to remove
"file"), and it has further safety checks described next.  The
biggest difference is that the paths are removed from both index
and from the working tree (if you have an exotic need to remove
paths only from the index, you can use the --cached option).

The command refuses to remove if the copy on the working tree
does not match the index, or if the index and the HEAD does not
match.  You can defeat this check with -f option.

This safety check has two exceptions: if the working tree file
does not exist to begin with, that technically does not match
the index but it is allowed.  This is to allow this CVS style
command sequence:

	rm <path> && git rm <path>

Also if the index is unmerged at the <path>, you can use "git rm
<path>" to declare that the result of the merge loses that path,
and the above safety check does not trigger; requiring the file
to match the index in this case forces the user to do "git
update-index file && git rm file", which is just crazy.

To recursively remove all contents from a directory, you need to
pass -r option, not just the directory name as the <path>.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-rm.c | 123 ++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 103 insertions(+), 20 deletions(-)

diff --git a/builtin-rm.c b/builtin-rm.c
index 33d04bd015..5b078c4194 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -7,9 +7,10 @@
 #include "builtin.h"
 #include "dir.h"
 #include "cache-tree.h"
+#include "tree-walk.h"
 
 static const char builtin_rm_usage[] =
-"git-rm [-n] [-v] [-f] <filepattern>...";
+"git-rm [-n] [-f] [--cached] <filepattern>...";
 
 static struct {
 	int nr, alloc;
@@ -41,12 +42,75 @@ static int remove_file(const char *name)
 	return ret;
 }
 
+static int check_local_mod(unsigned char *head)
+{
+	/* items in list are already sorted in the cache order,
+	 * so we could do this a lot more efficiently by using
+	 * tree_desc based traversal if we wanted to, but I am
+	 * lazy, and who cares if removal of files is a tad
+	 * slower than the theoretical maximum speed?
+	 */
+	int i, no_head;
+	int errs = 0;
+
+	no_head = is_null_sha1(head);
+	for (i = 0; i < list.nr; i++) {
+		struct stat st;
+		int pos;
+		struct cache_entry *ce;
+		const char *name = list.name[i];
+		unsigned char sha1[20];
+		unsigned mode;
+
+		pos = cache_name_pos(name, strlen(name));
+		if (pos < 0)
+			continue; /* removing unmerged entry */
+		ce = active_cache[pos];
+
+		if (lstat(ce->name, &st) < 0) {
+			if (errno != ENOENT)
+				fprintf(stderr, "warning: '%s': %s",
+					ce->name, strerror(errno));
+			/* It already vanished from the working tree */
+			continue;
+		}
+		else if (S_ISDIR(st.st_mode)) {
+			/* if a file was removed and it is now a
+			 * directory, that is the same as ENOENT as
+			 * far as git is concerned; we do not track
+			 * directories.
+			 */
+			continue;
+		}
+		if (ce_match_stat(ce, &st, 0))
+			errs = error("'%s' has local modifications "
+				     "(hint: try -f)", ce->name);
+		if (no_head)
+			continue;
+		/*
+		 * It is Ok to remove a newly added path, as long as
+		 * it is cache-clean.
+		 */
+		if (get_tree_entry(head, name, sha1, &mode))
+			continue;
+		/*
+		 * Otherwise make sure the version from the HEAD
+		 * matches the index.
+		 */
+		if (ce->ce_mode != create_ce_mode(mode) ||
+		    hashcmp(ce->sha1, sha1))
+			errs = error("'%s' has changes staged in the index "
+				     "(hint: try -f)", name);
+	}
+	return errs;
+}
+
 static struct lock_file lock_file;
 
 int cmd_rm(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
-	int verbose = 0, show_only = 0, force = 0;
+	int show_only = 0, force = 0, index_only = 0, recursive = 0;
 	const char **pathspec;
 	char *seen;
 
@@ -62,23 +126,20 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
 		if (*arg != '-')
 			break;
-		if (!strcmp(arg, "--")) {
+		else if (!strcmp(arg, "--")) {
 			i++;
 			break;
 		}
-		if (!strcmp(arg, "-n")) {
+		else if (!strcmp(arg, "-n"))
 			show_only = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-v")) {
-			verbose = 1;
-			continue;
-		}
-		if (!strcmp(arg, "-f")) {
+		else if (!strcmp(arg, "--cached"))
+			index_only = 1;
+		else if (!strcmp(arg, "-f"))
 			force = 1;
-			continue;
-		}
-		usage(builtin_rm_usage);
+		else if (!strcmp(arg, "-r"))
+			recursive = 1;
+		else
+			usage(builtin_rm_usage);
 	}
 	if (argc <= i)
 		usage(builtin_rm_usage);
@@ -99,14 +160,36 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (pathspec) {
 		const char *match;
 		for (i = 0; (match = pathspec[i]) != NULL ; i++) {
-			if (*match && !seen[i])
-				die("pathspec '%s' did not match any files", match);
+			if (!seen[i])
+				die("pathspec '%s' did not match any files",
+				    match);
+			if (!recursive && seen[i] == MATCHED_RECURSIVELY)
+				die("not removing '%s' recursively without -r",
+				    *match ? match : ".");
 		}
 	}
 
+	/*
+	 * If not forced, the file, the index and the HEAD (if exists)
+	 * must match; but the file can already been removed, since
+	 * this sequence is a natural "novice" way:
+	 *
+	 *	rm F; git fm F
+	 *
+	 * Further, if HEAD commit exists, "diff-index --cached" must
+	 * report no changes unless forced.
+	 */
+	if (!force) {
+		unsigned char sha1[20];
+		if (get_sha1("HEAD", sha1))
+			hashclr(sha1);
+		if (check_local_mod(sha1))
+			exit(1);
+	}
+
 	/*
 	 * First remove the names from the index: we won't commit
-	 * the index unless all of them succeed
+	 * the index unless all of them succeed.
 	 */
 	for (i = 0; i < list.nr; i++) {
 		const char *path = list.name[i];
@@ -121,14 +204,14 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 		return 0;
 
 	/*
-	 * Then, if we used "-f", remove the filenames from the
-	 * workspace. If we fail to remove the first one, we
+	 * Then, unless we used "--cache", remove the filenames from
+	 * the workspace. If we fail to remove the first one, we
 	 * abort the "git rm" (but once we've successfully removed
 	 * any file at all, we'll go ahead and commit to it all:
 	 * by then we've already committed ourselves and can't fail
 	 * in the middle)
 	 */
-	if (force) {
+	if (!index_only) {
 		int removed = 0;
 		for (i = 0; i < list.nr; i++) {
 			const char *path = list.name[i];

From 467e1b5383c214e9562b2dc575ac027c54aa4fba Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:11:17 -0800
Subject: [PATCH 305/330] t3600: update the test for updated git rm

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t3600-rm.sh | 78 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 74 insertions(+), 4 deletions(-)

diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 201d1642da..e31cf93a00 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -43,19 +43,19 @@ test_expect_success \
 
 test_expect_success \
     'Test that git-rm foo succeeds' \
-    'git-rm foo'
+    'git-rm --cached foo'
 
 test_expect_success \
     'Post-check that foo exists but is not in index after git-rm foo' \
     '[ -f foo ] && ! git-ls-files --error-unmatch foo'
 
 test_expect_success \
-    'Pre-check that bar exists and is in index before "git-rm -f bar"' \
+    'Pre-check that bar exists and is in index before "git-rm bar"' \
     '[ -f bar ] && git-ls-files --error-unmatch bar'
 
 test_expect_success \
-    'Test that "git-rm -f bar" succeeds' \
-    'git-rm -f bar'
+    'Test that "git-rm bar" succeeds' \
+    'git-rm bar'
 
 test_expect_success \
     'Post-check that bar does not exist and is not in index after "git-rm -f bar"' \
@@ -84,4 +84,74 @@ test_expect_success \
     'When the rm in "git-rm -f" fails, it should not remove the file from the index' \
     'git-ls-files --error-unmatch baz'
 
+# Now, failure cases.
+test_expect_success 'Re-add foo and baz' '
+	git add foo baz &&
+	git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'Modify foo -- rm should refuse' '
+	echo >>foo &&
+	! git rm foo baz &&
+	test -f foo &&
+	test -f baz &&
+	git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'Modified foo -- rm -f should work' '
+	git rm -f foo baz &&
+	test ! -f foo &&
+	test ! -f baz &&
+	! git ls-files --error-unmatch foo &&
+	! git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Re-add foo and baz for HEAD tests' '
+	echo frotz >foo &&
+	git checkout HEAD -- baz &&
+	git add foo baz &&
+	git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'foo is different in index from HEAD -- rm should refuse' '
+	! git rm foo baz &&
+	test -f foo &&
+	test -f baz &&
+	git ls-files --error-unmatch foo baz
+'
+
+test_expect_success 'but with -f it should work.' '
+	git rm -f foo baz &&
+	test ! -f foo &&
+	test ! -f baz &&
+	! git ls-files --error-unmatch foo
+	! git ls-files --error-unmatch baz
+'
+
+test_expect_success 'Recursive test setup' '
+	mkdir -p frotz &&
+	echo qfwfq >frotz/nitfol &&
+	git add frotz &&
+	git commit -m "subdir test"
+'
+
+test_expect_success 'Recursive without -r fails' '
+	! git rm frotz &&
+	test -d frotz &&
+	test -f frotz/nitfol
+'
+
+test_expect_success 'Recursive with -r but dirty' '
+	echo qfwfq >>frotz/nitfol
+	! git rm -r frotz &&
+	test -d frotz &&
+	test -f frotz/nitfol
+'
+
+test_expect_success 'Recursive with -r -f' '
+	git rm -f -r frotz &&
+	! test -f frotz/nitfol &&
+	! test -d frotz
+'
+
 test_done

From 08d22488a65fde7d570bd4ea57626f5352b9b064 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:23:45 -0800
Subject: [PATCH 306/330] git-rm: Documentation

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-rm.txt | 51 +++++++++++++++++++++-------------------
 1 file changed, 27 insertions(+), 24 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index 66fc478f57..3a8f279e1a 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -7,51 +7,54 @@ git-rm - Remove files from the working tree and from the index
 
 SYNOPSIS
 --------
-'git-rm' [-f] [-n] [-v] [--] <file>...
+'git-rm' [-f] [-n] [-r] [--cached] [--] <file>...
 
 DESCRIPTION
 -----------
-A convenience wrapper for git-update-index --remove. For those coming
-from cvs, git-rm provides an operation similar to "cvs rm" or "cvs
-remove".
+Remove files from the working tree and from the index.  The
+files have to be identical to the tip of the branch, and no
+updates to its contents must have been placed in the staging
+area (aka index).
 
 
 OPTIONS
 -------
 <file>...::
-	Files to remove from the index and optionally, from the
-	working tree as well.
+	Files to remove.  Fileglobs (e.g. `*.c`) can be given to
+	remove all matching files.  Also a leading directory name
+	(e.g. `dir` to add `dir/file1` and `dir/file2`) can be
+	given to remove all files in the directory, recursively,
+	but this requires `-r` option to be given for safety.
 
 -f::
-	Remove files from the working tree as well as from the index.
+	Override the up-to-date check.
 
 -n::
         Don't actually remove the file(s), just show if they exist in
         the index.
 
--v::
-        Be verbose.
+-r::
+        Allow recursive removal when a leading directory name is
+        given.
 
 \--::
 	This option can be used to separate command-line options from
 	the list of files, (useful when filenames might be mistaken
 	for command-line options).
 
+\--cached::
+	This option can be used to tell the command to remove
+	the paths only from the index, leaving working tree
+	files.
+
 
 DISCUSSION
 ----------
 
-The list of <file> given to the command is fed to `git-ls-files`
-command to list files that are registered in the index and
-are not ignored/excluded by `$GIT_DIR/info/exclude` file or
-`.gitignore` file in each directory.  This means two things:
-
-. You can put the name of a directory on the command line, and the
-  command will remove all files in it and its subdirectories (the
-  directories themselves are never removed from the working tree);
-
-. Giving the name of a file that is not in the index does not
-  remove that file.
+The list of <file> given to the command can be exact pathnames,
+file glob patterns, or leading directory name.  The command
+removes only the paths that is known to git.  Giving the name of
+a file that you have not told git about does not remove that file.
 
 
 EXAMPLES
@@ -69,10 +72,10 @@ subdirectories of `Documentation/` directory.
 git-rm -f git-*.sh::
 
 	Remove all git-*.sh scripts that are in the index. The files
-	are removed from the index, and (because of the -f option),
-	from the working tree as well. Because this example lets the
-	shell expand the asterisk (i.e. you are listing the files
-	explicitly), it does not remove `subdir/git-foo.sh`.
+	are removed from the index, and from the working
+	tree. Because this example lets the shell expand the
+	asterisk (i.e. you are listing the files explicitly), it
+	does not remove `subdir/git-foo.sh`.
 
 See Also
 --------

From 4888c534099012d71d24051deb5b14319747bd1a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:11:34 -0800
Subject: [PATCH 307/330] read_directory: show_both option.

This teaches the internal read_directory() routine to return
both interesting and ignored pathnames.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 dir.c | 19 ++++++++++++-------
 dir.h |  6 ++++--
 2 files changed, 16 insertions(+), 9 deletions(-)

diff --git a/dir.c b/dir.c
index 8477472c03..dd188a8c56 100644
--- a/dir.c
+++ b/dir.c
@@ -260,7 +260,8 @@ int excluded(struct dir_struct *dir, const char *pathname)
 	return 0;
 }
 
-static void add_name(struct dir_struct *dir, const char *pathname, int len)
+static void add_name(struct dir_struct *dir, const char *pathname, int len,
+		     int ignored_entry)
 {
 	struct dir_entry *ent;
 
@@ -273,6 +274,7 @@ static void add_name(struct dir_struct *dir, const char *pathname, int len)
 		dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
 	}
 	ent = xmalloc(sizeof(*ent) + len + 1);
+	ent->ignored_entry = ignored_entry;
 	ent->len = len;
 	memcpy(ent->name, pathname, len);
 	ent->name[len] = 0;
@@ -314,6 +316,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 
 		while ((de = readdir(fdir)) != NULL) {
 			int len;
+			int ignored_entry;
 
 			if ((de->d_name[0] == '.') &&
 			    (de->d_name[1] == 0 ||
@@ -322,11 +325,12 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 				continue;
 			len = strlen(de->d_name);
 			memcpy(fullname + baselen, de->d_name, len+1);
-			if (excluded(dir, fullname) != dir->show_ignored) {
-				if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
-					continue;
-				}
-			}
+			ignored_entry = excluded(dir, fullname);
+
+			if (!dir->show_both &&
+			    (ignored_entry != dir->show_ignored) &&
+			    (!dir->show_ignored || DTYPE(de) != DT_DIR))
+				continue;
 
 			switch (DTYPE(de)) {
 			struct stat st;
@@ -364,7 +368,8 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
 			if (check_only)
 				goto exit_early;
 			else
-				add_name(dir, fullname, baselen + len);
+				add_name(dir, fullname, baselen + len,
+					 ignored_entry);
 		}
 exit_early:
 		closedir(fdir);
diff --git a/dir.h b/dir.h
index c919727949..08c6345472 100644
--- a/dir.h
+++ b/dir.h
@@ -13,7 +13,8 @@
 
 
 struct dir_entry {
-	int len;
+	unsigned ignored_entry : 1;
+	unsigned int len : 15;
 	char name[FLEX_ARRAY]; /* more */
 };
 
@@ -29,7 +30,8 @@ struct exclude_list {
 
 struct dir_struct {
 	int nr, alloc;
-	unsigned int show_ignored:1,
+	unsigned int show_both: 1,
+		     show_ignored:1,
 		     show_other_directories:1,
 		     hide_empty_directories:1;
 	struct dir_entry **entries;

From e23ca9e1f95a756bfe598568be9d03059db1dad2 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 03:13:45 -0800
Subject: [PATCH 308/330] git-add: add ignored files when asked explicitly.

One thing many people found confusing about git-add was that a
file whose name matches an ignored pattern could not be added to
the index.  With this, such a file can be added by explicitly
spelling its name to git-add.

Fileglobs and recursive behaviour do not add ignored files to
the index.  That is, if a pattern '*.o' is in .gitignore, and
two files foo.o, bar/baz.o are in the working tree:

    $ git add foo.o
    $ git add '*.o'
    $ git add bar

Only the first form adds foo.o to the index.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-add.txt | 11 ++++++++---
 builtin-add.c             | 11 ++++++++++-
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 8710b3a75e..2fef0681bf 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -25,8 +25,9 @@ the commit.
 The 'git status' command can be used to obtain a summary of what is included
 for the next commit.
 
-This command only adds non-ignored files, to add ignored files use
-"git update-index --add".
+This command can be used to add ignored files, but they have to be
+explicitly and exactly specified from the command line.  File globbing
+and recursive behaviour do not add ignored files.
 
 Please see gitlink:git-commit[1] for alternative ways to add content to a
 commit.
@@ -35,7 +36,11 @@ commit.
 OPTIONS
 -------
 <file>...::
-	Files to add content from.
+	Files to add content from.  Fileglobs (e.g. `*.c`) can
+	be given to add all matching files.  Also a
+	leading directory name (e.g. `dir` to add `dir/file1`
+	and `dir/file2`) can be given to add all files in the
+	directory, recursively.
 
 -n::
         Don't actually add the file(s), just show if they exist.
diff --git a/builtin-add.c b/builtin-add.c
index 17641b433d..822075ac22 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -26,7 +26,14 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
 	i = dir->nr;
 	while (--i >= 0) {
 		struct dir_entry *entry = *src++;
-		if (!match_pathspec(pathspec, entry->name, entry->len, prefix, seen)) {
+		int how = match_pathspec(pathspec, entry->name, entry->len,
+					 prefix, seen);
+		/*
+		 * ignored entries can be added with exact match,
+		 * but not with glob nor recursive.
+		 */
+		if (!how ||
+		    (entry->ignored_entry && how != MATCHED_EXACTLY)) {
 			free(entry);
 			continue;
 		}
@@ -55,6 +62,8 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
 
 	/* Set up the default git porcelain excludes */
 	memset(dir, 0, sizeof(*dir));
+	if (pathspec)
+		dir->show_both = 1;
 	dir->exclude_per_dir = ".gitignore";
 	path = git_path("info/exclude");
 	if (!access(path, R_OK))

From 1e423f56558e74a3a94c653108f64f0305199b9a Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 10:38:35 -0800
Subject: [PATCH 309/330] git-add: warn when adding an ignored file with an
 explicit request.

We allow otherwise ignored paths to be added to the index by
spelling its path out on the command line, but we would warn the
user about them when we do so.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-add.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/builtin-add.c b/builtin-add.c
index 822075ac22..c54c694532 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -37,6 +37,9 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
 			free(entry);
 			continue;
 		}
+		if (entry->ignored_entry)
+			fprintf(stderr, "warning: '%s' is an ignored path.\n",
+				entry->name);
 		*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;

From 756bbf548dbef5b738c50ce6517f5c63ab929373 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:42 +0000
Subject: [PATCH 310/330] gitweb: Add parse_commits, used to bulk load commit
 objects.

Add a new method parse_commits which is able to parse multiple commit
objects at once.  Reworked parse_commit to share the commit object
parsing logic.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 91 +++++++++++++++++++++++++++++++++++++---------
 1 file changed, 74 insertions(+), 17 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index d01d689348..6bd57a4e32 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1270,25 +1270,13 @@ sub parse_tag {
 	return %tag
 }
 
-sub parse_commit {
-	my $commit_id = shift;
-	my $commit_text = shift;
-
-	my @commit_lines;
+sub parse_commit_text {
+	my ($commit_text) = @_;
+	my @commit_lines = split '\n', $commit_text;
 	my %co;
 
-	if (defined $commit_text) {
-		@commit_lines = @$commit_text;
-	} else {
-		local $/ = "\0";
-		open my $fd, "-|", git_cmd(), "rev-list",
-			"--header", "--parents", "--max-count=1",
-			$commit_id, "--"
-			or return;
-		@commit_lines = split '\n', <$fd>;
-		close $fd or return;
-		pop @commit_lines;
-	}
+	pop @commit_lines; # Remove '\0'
+
 	my $header = shift @commit_lines;
 	if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
 		return;
@@ -1375,6 +1363,75 @@ sub parse_commit {
 	return %co;
 }
 
+sub parse_commit {
+	my ($commit_id) = @_;
+	my %co;
+
+	local $/ = "\0";
+
+	open my $fd, "-|", git_cmd(), "rev-list",
+		"--header",
+		"--parents",
+		"--max-count=1",
+		$commit_id,
+		"--",
+		or die_error(undef, "Open git-rev-list failed");
+	%co = parse_commit_text(<$fd>);
+	close $fd;
+
+	return %co;
+}
+
+sub parse_commits {
+	my ($commit_id, $maxcount, $skip, $arg, $filename) = @_;
+	my @cos;
+
+	$maxcount ||= 1;
+	$skip ||= 0;
+
+	# Delete once rev-list supports the --skip option
+	if ($skip > 0) {
+		open my $fd, "-|", git_cmd(), "rev-list",
+			($arg ? ($arg) : ()),
+			("--max-count=" . ($maxcount + $skip)),
+			$commit_id,
+			"--",
+			($filename ? ($filename) : ())
+			or die_error(undef, "Open git-rev-list failed");
+		while (my $line = <$fd>) {
+			if ($skip-- <= 0) {
+				chomp $line;
+				my %co = parse_commit($line);
+				push @cos, \%co;
+			}
+		}
+		close $fd;
+
+		return wantarray ? @cos : \@cos;
+	}
+
+	local $/ = "\0";
+
+	open my $fd, "-|", git_cmd(), "rev-list",
+		"--header",
+		"--parents",
+		($arg ? ($arg) : ()),
+		("--max-count=" . $maxcount),
+		# Add once rev-list supports the --skip option
+		# ("--skip=" . $skip),
+		$commit_id,
+		"--",
+		($filename ? ($filename) : ())
+		or die_error(undef, "Open git-rev-list failed");
+	while (my $line = <$fd>) {
+		my %co = parse_commit_text($line);
+		push @cos, \%co;
+	}
+	close $fd;
+
+	return wantarray ? @cos : \@cos;
+}
+
 # parse ref from ref_file, given by ref_id, with given type
 sub parse_ref {
 	my $ref_file = shift;

From 208b2dff95bb48682c351099023a1cbb0e1edf26 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:43 +0000
Subject: [PATCH 311/330] gitweb: We do longer need the --parents flag in
 rev-list.

We only want to know the direct parents of a given commit object,
these parents are available in the --header output of rev-list.  If
--parents is supplied with --full-history the output includes merge
commits that aren't relevant.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 6bd57a4e32..c645686a74 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1281,13 +1281,14 @@ sub parse_commit_text {
 	if (!($header =~ m/^[0-9a-fA-F]{40}/)) {
 		return;
 	}
-	($co{'id'}, my @parents) = split ' ', $header;
-	$co{'parents'} = \@parents;
-	$co{'parent'} = $parents[0];
+	$co{'id'} = $header;
+	my @parents;
 	while (my $line = shift @commit_lines) {
 		last if $line eq "\n";
 		if ($line =~ m/^tree ([0-9a-fA-F]{40})$/) {
 			$co{'tree'} = $1;
+		} elsif ($line =~ m/^parent ([0-9a-fA-F]{40})$/) {
+			push @parents, $1;
 		} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
 			$co{'author'} = $1;
 			$co{'author_epoch'} = $2;
@@ -1314,6 +1315,8 @@ sub parse_commit_text {
 	if (!defined $co{'tree'}) {
 		return;
 	};
+	$co{'parents'} = \@parents;
+	$co{'parent'} = $parents[0];
 
 	foreach my $title (@commit_lines) {
 		$title =~ s/^    //;
@@ -1371,7 +1374,6 @@ sub parse_commit {
 
 	open my $fd, "-|", git_cmd(), "rev-list",
 		"--header",
-		"--parents",
 		"--max-count=1",
 		$commit_id,
 		"--",
@@ -1414,7 +1416,6 @@ sub parse_commits {
 
 	open my $fd, "-|", git_cmd(), "rev-list",
 		"--header",
-		"--parents",
 		($arg ? ($arg) : ()),
 		("--max-count=" . $maxcount),
 		# Add once rev-list supports the --skip option

From 190d7fdcf325bb444fa806f09ebbb403a4ae4ee6 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:44 +0000
Subject: [PATCH 312/330] gitweb: Change summary, shortlog actions to use
 parse_commits.

Also added missing accesskey.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 34 ++++++++++++----------------------
 1 file changed, 12 insertions(+), 22 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c645686a74..5f1ace98aa 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2704,20 +2704,19 @@ sub git_project_list_body {
 
 sub git_shortlog_body {
 	# uses global variable $project
-	my ($revlist, $from, $to, $refs, $extra) = @_;
+	my ($commitlist, $from, $to, $refs, $extra) = @_;
 
 	my $have_snapshot = gitweb_have_snapshot();
 
 	$from = 0 unless defined $from;
-	$to = $#{$revlist} if (!defined $to || $#{$revlist} < $to);
+	$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
 	print "<table class=\"shortlog\" cellspacing=\"0\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
-		my $commit = $revlist->[$i];
-		#my $ref = defined $refs ? format_ref_marker($refs, $commit) : '';
+		my %co = %{$commitlist->[$i]};
+		my $commit = $co{'id'};
 		my $ref = format_ref_marker($refs, $commit);
-		my %co = parse_commit($commit);
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
@@ -3081,14 +3080,10 @@ sub git_summary {
 
 	# we need to request one more than 16 (0..15) to check if
 	# those 16 are all
-	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=17",
-		$head, "--"
-		or die_error(undef, "Open git-rev-list failed");
-	my @revlist = map { chomp; $_ } <$fd>;
-	close $fd;
+	my @commitlist = parse_commits($head, 17);
 	git_print_header_div('shortlog');
-	git_shortlog_body(\@revlist, 0, 15, $refs,
-	                  $#revlist <=  15 ? undef :
+	git_shortlog_body(\@commitlist, 0, 15, $refs,
+	                  $#commitlist <=  15 ? undef :
 	                  $cgi->a({-href => href(action=>"shortlog")}, "..."));
 
 	if (@taglist) {
@@ -4456,26 +4451,21 @@ sub git_shortlog {
 	}
 	my $refs = git_get_references();
 
-	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
-		or die_error(undef, "Open git-rev-list failed");
-	my @revlist = map { chomp; $_ } <$fd>;
-	close $fd;
+	my @commitlist = parse_commits($head, 101, (100 * $page));
 
-	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, $#revlist);
+	my $paging_nav = format_paging_nav('shortlog', $hash, $head, $page, (100 * ($page+1)));
 	my $next_link = '';
-	if ($#revlist >= (100 * ($page+1)-1)) {
+	if ($#commitlist >= 100) {
 		$next_link =
 			$cgi->a({-href => href(action=>"shortlog", hash=>$hash, page=>$page+1),
-			         -title => "Alt-n"}, "next");
+			         -accesskey => "n", -title => "Alt-n"}, "next");
 	}
 
-
 	git_header_html();
 	git_print_page_nav('shortlog','', $hash,$hash,$hash, $paging_nav);
 	git_print_header_div('summary', $project);
 
-	git_shortlog_body(\@revlist, ($page * 100), $#revlist, $refs, $next_link);
+	git_shortlog_body(\@commitlist, 0, 99, $refs, $next_link);
 
 	git_footer_html();
 }

From 719dad28c599dc2e2917c3cb0e44152fd92bf133 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:45 +0000
Subject: [PATCH 313/330] gitweb: Change log action to use parse_commits.

Also add missing next link to bottom of page.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 5f1ace98aa..42b7449251 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3645,28 +3645,25 @@ sub git_log {
 	}
 	my $refs = git_get_references();
 
-	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", git_cmd(), "rev-list", $limit, $hash, "--"
-		or die_error(undef, "Open git-rev-list failed");
-	my @revlist = map { chomp; $_ } <$fd>;
-	close $fd;
+	my @commitlist = parse_commits($hash, 101, (100 * $page));
 
-	my $paging_nav = format_paging_nav('log', $hash, $head, $page, $#revlist);
+	my $paging_nav = format_paging_nav('log', $hash, $head, $page, (100 * ($page+1)));
 
 	git_header_html();
 	git_print_page_nav('log','', $hash,undef,undef, $paging_nav);
 
-	if (!@revlist) {
+	if (!@commitlist) {
 		my %co = parse_commit($hash);
 
 		git_print_header_div('summary', $project);
 		print "<div class=\"page_body\"> Last change $co{'age_string'}.<br/><br/></div>\n";
 	}
-	for (my $i = ($page * 100); $i <= $#revlist; $i++) {
-		my $commit = $revlist[$i];
-		my $ref = format_ref_marker($refs, $commit);
-		my %co = parse_commit($commit);
+	my $to = ($#commitlist >= 99) ? (99) : ($#commitlist);
+	for (my $i = 0; $i <= $to; $i++) {
+		my %co = %{$commitlist[$i]};
 		next if !%co;
+		my $commit = $co{'id'};
+		my $ref = format_ref_marker($refs, $commit);
 		my %ad = parse_date($co{'author_epoch'});
 		git_print_header_div('commit',
 		               "<span class=\"age\">$co{'age_string'}</span>" .
@@ -3688,6 +3685,12 @@ sub git_log {
 		git_print_log($co{'comment'}, -final_empty_line=> 1);
 		print "</div>\n";
 	}
+	if ($#commitlist >= 100) {
+		print "<div class=\"page_nav\">\n";
+		print $cgi->a({-href => href(action=>"log", hash=>$hash, page=>$page+1),
+			       -accesskey => "n", -title => "Alt-n"}, "next");
+		print "</div>\n";
+	}
 	git_footer_html();
 }
 

From 5ad66088d1166bf91cf2890682f4582049756539 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:46 +0000
Subject: [PATCH 314/330] gitweb: Change header search action to use
 parse_commits.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 23 +++++++++--------------
 1 file changed, 9 insertions(+), 14 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 42b7449251..53dd2251db 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2910,18 +2910,18 @@ sub git_heads_body {
 }
 
 sub git_search_grep_body {
-	my ($greplist, $from, $to, $extra) = @_;
+	my ($commitlist, $from, $to, $extra) = @_;
 	$from = 0 unless defined $from;
-	$to = $#{$greplist} if (!defined $to || $#{$greplist} < $to);
+	$to = $#{$commitlist} if (!defined $to || $#{$commitlist} < $to);
 
 	print "<table class=\"grep\" cellspacing=\"0\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
-		my $commit = $greplist->[$i];
-		my %co = parse_commit($commit);
+		my %co = %{$commitlist->[$i]};
 		if (!%co) {
 			next;
 		}
+		my $commit = $co{'id'};
 		if ($alternate) {
 			print "<tr class=\"dark\">\n";
 		} else {
@@ -4307,13 +4307,8 @@ sub git_search {
 		} elsif ($searchtype eq 'committer') {
 			$greptype = "--committer=";
 		}
-		open my $fd, "-|", git_cmd(), "rev-list",
-			("--max-count=" . (100 * ($page+1))),
-			($greptype . $searchtext),
-			$hash, "--"
-			or next;
-		my @revlist = map { chomp; $_ } <$fd>;
-		close $fd;
+		$greptype .= $searchtext;
+		my @commitlist = parse_commits($hash, 101, (100 * $page), $greptype);
 
 		my $paging_nav = '';
 		if ($page > 0) {
@@ -4330,7 +4325,7 @@ sub git_search {
 			$paging_nav .= "first";
 			$paging_nav .= " &sdot; prev";
 		}
-		if ($#revlist >= (100 * ($page+1)-1)) {
+		if ($#commitlist >= 100) {
 			$paging_nav .= " &sdot; " .
 				$cgi->a({-href => href(action=>"search", hash=>$hash,
 						       searchtext=>$searchtext, searchtype=>$searchtype,
@@ -4340,7 +4335,7 @@ sub git_search {
 			$paging_nav .= " &sdot; next";
 		}
 		my $next_link = '';
-		if ($#revlist >= (100 * ($page+1)-1)) {
+		if ($#commitlist >= 100) {
 			$next_link =
 				$cgi->a({-href => href(action=>"search", hash=>$hash,
 						       searchtext=>$searchtext, searchtype=>$searchtype,
@@ -4350,7 +4345,7 @@ sub git_search {
 
 		git_print_page_nav('','', $hash,$co{'tree'},$hash, $paging_nav);
 		git_print_header_div('commit', esc_html($co{'title'}), $hash);
-		git_search_grep_body(\@revlist, ($page * 100), $#revlist, $next_link);
+		git_search_grep_body(\@commitlist, 0, 99, $next_link);
 	}
 
 	if ($searchtype eq 'pickaxe') {

From b6093a5c02f6dd16dadcc0e9caa1e0e76216f929 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:47 +0000
Subject: [PATCH 315/330] gitweb: Change atom, rss actions to use
 parse_commits.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 53dd2251db..f752a6f605 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -4483,11 +4483,7 @@ sub git_feed {
 
 	# log/feed of current (HEAD) branch, log of given branch, history of file/directory
 	my $head = $hash || 'HEAD';
-	open my $fd, "-|", git_cmd(), "rev-list", "--max-count=150",
-		$head, "--", (defined $file_name ? $file_name : ())
-		or die_error(undef, "Open git-rev-list failed");
-	my @revlist = map { chomp; $_ } <$fd>;
-	close $fd or die_error(undef, "Reading git-rev-list failed");
+	my @commitlist = parse_commits($head, 150);
 
 	my %latest_commit;
 	my %latest_date;
@@ -4497,8 +4493,8 @@ sub git_feed {
 		# browser (feed reader) prefers text/xml
 		$content_type = 'text/xml';
 	}
-	if (defined($revlist[0])) {
-		%latest_commit = parse_commit($revlist[0]);
+	if (defined($commitlist[0])) {
+		%latest_commit = %{$commitlist[0]};
 		%latest_date   = parse_date($latest_commit{'author_epoch'});
 		print $cgi->header(
 			-type => $content_type,
@@ -4588,9 +4584,9 @@ XML
 	}
 
 	# contents
-	for (my $i = 0; $i <= $#revlist; $i++) {
-		my $commit = $revlist[$i];
-		my %co = parse_commit($commit);
+	for (my $i = 0; $i <= $#commitlist; $i++) {
+		my %co = %{$commitlist[$i]};
+		my $commit = $co{'id'};
 		# we read 150, we always show 30 and the ones more recent than 48 hours
 		if (($i >= 20) && ((time - $co{'author_epoch'}) > 48*60*60)) {
 			last;
@@ -4598,7 +4594,7 @@ XML
 		my %cd = parse_date($co{'author_epoch'});
 
 		# get list of changed files
-		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+		open my $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
 			$co{'parent'}, $co{'id'}, "--", (defined $file_name ? $file_name : ())
 			or next;
 		my @difftree = map { chomp; $_ } <$fd>;

From a8b983bfea9daa255fd32dfc6b39e809733e4c5a Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:48 +0000
Subject: [PATCH 316/330] gitweb: Change history action to use parse_commits.

Also added missing accesskey.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 27 +++++++++------------------
 1 file changed, 9 insertions(+), 18 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index f752a6f605..f9994d9417 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -2750,23 +2750,19 @@ sub git_shortlog_body {
 
 sub git_history_body {
 	# Warning: assumes constant type (blob or tree) during history
-	my ($revlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_;
+	my ($commitlist, $from, $to, $refs, $hash_base, $ftype, $extra) = @_;
 
 	$from = 0 unless defined $from;
-	$to = $#{$revlist} unless (defined $to && $to <= $#{$revlist});
+	$to = $#{$commitlist} unless (defined $to && $to <= $#{$commitlist});
 
 	print "<table class=\"history\" cellspacing=\"0\">\n";
 	my $alternate = 1;
 	for (my $i = $from; $i <= $to; $i++) {
-		if ($revlist->[$i] !~ m/^([0-9a-fA-F]{40})/) {
-			next;
-		}
-
-		my $commit = $1;
-		my %co = parse_commit($commit);
+		my %co = %{$commitlist->[$i]};
 		if (!%co) {
 			next;
 		}
+		my $commit = $co{'id'};
 
 		my $ref = format_ref_marker($refs, $commit);
 
@@ -4219,12 +4215,7 @@ sub git_history {
 		$ftype = git_get_type($hash);
 	}
 
-	open my $fd, "-|",
-		git_cmd(), "rev-list", $limit, "--full-history", $hash_base, "--", $file_name
-			or die_error(undef, "Open git-rev-list-failed");
-	my @revlist = map { chomp; $_ } <$fd>;
-	close $fd
-		or die_error(undef, "Reading git-rev-list failed");
+	my @commitlist = parse_commits($hash_base, 101, (100 * $page), "--full-history", $file_name);
 
 	my $paging_nav = '';
 	if ($page > 0) {
@@ -4240,7 +4231,7 @@ sub git_history {
 		$paging_nav .= "first";
 		$paging_nav .= " &sdot; prev";
 	}
-	if ($#revlist >= (100 * ($page+1)-1)) {
+	if ($#commitlist >= 100) {
 		$paging_nav .= " &sdot; " .
 			$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
 			                       file_name=>$file_name, page=>$page+1),
@@ -4249,11 +4240,11 @@ sub git_history {
 		$paging_nav .= " &sdot; next";
 	}
 	my $next_link = '';
-	if ($#revlist >= (100 * ($page+1)-1)) {
+	if ($#commitlist >= 100) {
 		$next_link =
 			$cgi->a({-href => href(action=>"history", hash=>$hash, hash_base=>$hash_base,
 			                       file_name=>$file_name, page=>$page+1),
-			         -title => "Alt-n"}, "next");
+			         -accesskey => "n", -title => "Alt-n"}, "next");
 	}
 
 	git_header_html();
@@ -4261,7 +4252,7 @@ sub git_history {
 	git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
 	git_print_page_path($file_name, $ftype, $hash_base);
 
-	git_history_body(\@revlist, ($page * 100), $#revlist,
+	git_history_body(\@commitlist, 0, 99,
 	                 $refs, $hash_base, $ftype, $next_link);
 
 	git_footer_html();

From f47efbb7ab735fc49d8d93f9ed1c087c5da09c23 Mon Sep 17 00:00:00 2001
From: Robert Fitzsimons <robfitz@273k.net>
Date: Sun, 24 Dec 2006 14:31:49 +0000
Subject: [PATCH 317/330] gitweb: Use rev-list --skip option.

Signed-off-by: Robert Fitzsimons <robfitz@273k.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 gitweb/gitweb.perl | 24 +-----------------------
 1 file changed, 1 insertion(+), 23 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index f9994d9417..65fcdb0f28 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -1391,35 +1391,13 @@ sub parse_commits {
 	$maxcount ||= 1;
 	$skip ||= 0;
 
-	# Delete once rev-list supports the --skip option
-	if ($skip > 0) {
-		open my $fd, "-|", git_cmd(), "rev-list",
-			($arg ? ($arg) : ()),
-			("--max-count=" . ($maxcount + $skip)),
-			$commit_id,
-			"--",
-			($filename ? ($filename) : ())
-			or die_error(undef, "Open git-rev-list failed");
-		while (my $line = <$fd>) {
-			if ($skip-- <= 0) {
-				chomp $line;
-				my %co = parse_commit($line);
-				push @cos, \%co;
-			}
-		}
-		close $fd;
-
-		return wantarray ? @cos : \@cos;
-	}
-
 	local $/ = "\0";
 
 	open my $fd, "-|", git_cmd(), "rev-list",
 		"--header",
 		($arg ? ($arg) : ()),
 		("--max-count=" . $maxcount),
-		# Add once rev-list supports the --skip option
-		# ("--skip=" . $skip),
+		("--skip=" . $skip),
 		$commit_id,
 		"--",
 		($filename ? ($filename) : ())

From 6a1ad32519898a8fa4400e081c2d61fd3af42e2c Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 17:46:38 -0800
Subject: [PATCH 318/330] git-add -f: allow adding otherwise ignored files.

Instead of just warning, refuse to add otherwise ignored files
by default, and allow it with an -f option.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-add.txt |  8 ++++++--
 builtin-add.c             | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 2fef0681bf..95bea66374 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -7,7 +7,7 @@ git-add - Add file contents to the changeset to be committed next
 
 SYNOPSIS
 --------
-'git-add' [-n] [-v] [--interactive] [--] <file>...
+'git-add' [-n] [-v] [-f] [--interactive] [--] <file>...
 
 DESCRIPTION
 -----------
@@ -25,7 +25,8 @@ the commit.
 The 'git status' command can be used to obtain a summary of what is included
 for the next commit.
 
-This command can be used to add ignored files, but they have to be
+This command can be used to add ignored files with `-f` (force)
+option, but they have to be
 explicitly and exactly specified from the command line.  File globbing
 and recursive behaviour do not add ignored files.
 
@@ -48,6 +49,9 @@ OPTIONS
 -v::
         Be verbose.
 
+-f::
+	Allow adding otherwise ignored files.
+
 \--interactive::
 	Add modified contents in the working tree interactively to
 	the index.
diff --git a/builtin-add.c b/builtin-add.c
index c54c694532..8ed4a6a9f3 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -10,7 +10,7 @@
 #include "cache-tree.h"
 
 static const char builtin_add_usage[] =
-"git-add [-n] [-v] [--interactive] [--] <filepattern>...";
+"git-add [-n] [-v] [-f] [--interactive] [--] <filepattern>...";
 
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
@@ -37,9 +37,6 @@ static void prune_directory(struct dir_struct *dir, const char **pathspec, int p
 			free(entry);
 			continue;
 		}
-		if (entry->ignored_entry)
-			fprintf(stderr, "warning: '%s' is an ignored path.\n",
-				entry->name);
 		*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
@@ -94,10 +91,13 @@ static void fill_directory(struct dir_struct *dir, const char **pathspec)
 
 static struct lock_file lock_file;
 
+static const char ignore_warning[] =
+"The following paths are ignored by one of your .gitignore files:\n";
+
 int cmd_add(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
-	int verbose = 0, show_only = 0;
+	int verbose = 0, show_only = 0, ignored_too = 0;
 	const char **pathspec;
 	struct dir_struct dir;
 	int add_interactive = 0;
@@ -132,6 +132,10 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 			show_only = 1;
 			continue;
 		}
+		if (!strcmp(arg, "-f")) {
+			ignored_too = 1;
+			continue;
+		}
 		if (!strcmp(arg, "-v")) {
 			verbose = 1;
 			continue;
@@ -150,6 +154,8 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (show_only) {
 		const char *sep = "", *eof = "";
 		for (i = 0; i < dir.nr; i++) {
+			if (!ignored_too && dir.entries[i]->ignored_entry)
+				continue;
 			printf("%s%s", sep, dir.entries[i]->name);
 			sep = " ";
 			eof = "\n";
@@ -161,6 +167,24 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (read_cache() < 0)
 		die("index file corrupt");
 
+	if (!ignored_too) {
+		int has_ignored = -1;
+		for (i = 0; has_ignored < 0 && i < dir.nr; i++)
+			if (dir.entries[i]->ignored_entry)
+				has_ignored = i;
+		if (0 <= has_ignored) {
+			fprintf(stderr, ignore_warning);
+			for (i = has_ignored; i < dir.nr; i++) {
+				if (!dir.entries[i]->ignored_entry)
+					continue;
+				fprintf(stderr, "%s\n", dir.entries[i]->name);
+			}
+			fprintf(stderr,
+				"Use -f if you really want to add them.\n");
+			exit(1);
+		}
+	}
+
 	for (i = 0; i < dir.nr; i++)
 		add_file_to_index(dir.entries[i]->name, verbose);
 

From d5e86da89155b462c30a59bc724037fb111d3894 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Mon, 25 Dec 2006 18:14:12 -0800
Subject: [PATCH 319/330] GIT v1.5.0 preview

This is not yet -rc1 where all new topics closes, but I think it
is getting pretty closer.  I'd still want to merge updates to
fsck/prune to honor reflog entries before -rc1.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 GIT-VERSION-GEN | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index eca1ff2175..21ff949ea2 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.4.GIT
+DEF_VER=v1.4.5-rc0.GIT
 
 LF='
 '

From 2cf0223ba48ae51af03de486e119fd7a36e1b0ee Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Mon, 25 Dec 2006 23:25:44 -0500
Subject: [PATCH 320/330] add .mailmap for git-shortlog output with the git
 repository

The git repository itself was messed up in a couple cases.

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .mailmap | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)
 create mode 100644 .mailmap

diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..2c658f42f5
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,37 @@
+#
+# This list is used by git-shortlog to fix a few botched name translations
+# in the git archive, either because the author's full name was messed up
+# and/or not always written the same way, making contributions from the
+# same person appearing not to be so.
+#
+
+Aneesh Kumar K.V <aneesh.kumar@gmail.com>
+Chris Shoemaker <c.shoemaker@cox.net>
+Daniel Barkalow <barkalow@iabervon.org>
+David Kågedal <davidk@lysator.liu.se>
+Fredrik Kuivinen <freku045@student.liu.se>
+H. Peter Anvin <hpa@bonde.sc.orionmulti.com>
+H. Peter Anvin <hpa@tazenda.sc.orionmulti.com>
+H. Peter Anvin <hpa@trantor.hos.anvin.org>
+Horst H. von Brand <vonbrand@inf.utfsm.cl>
+Joachim Berdal Haga <cjhaga@fys.uio.no>
+Jon Loeliger <jdl@freescale.com>
+Jon Seymour <jon@blackcubes.dyndns.org>
+Karl Hasselström <kha@treskal.com>
+Kent Engstrom <kent@lysator.liu.se>
+Lars Doelle <lars.doelle@on-line.de>
+Lars Doelle <lars.doelle@on-line ! de>
+Lukas Sandström <lukass@etek.chalmers.se>
+Martin Langhoff <martin@catalyst.net.nz>
+Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
+Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk>
+René Scharfe <rene.scharfe@lsrfire.ath.cx>
+Robert Fitzsimons <robfitz@273k.net>
+Santi Béjar <sbejar@gmail.com>
+Sean Estabrooks <seanlkml@sympatico.ca>
+Shawn O. Pearce <spearce@spearce.org>
+Tony Luck <tony.luck@intel.com>
+Ville Skyttä <scop@xemacs.org>
+YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+anonymous <linux@horizon.com>
+anonymous <linux@horizon.net>

From abc8ab19ae72e811335c847166228362035b22c0 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 26 Dec 2006 00:11:50 -0800
Subject: [PATCH 321/330] show-branch --reflog: add documentation.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-show-branch.txt | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index dafacd4308..912e15bcba 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -11,6 +11,7 @@ SYNOPSIS
 'git-show-branch' [--all] [--remotes] [--topo-order] [--current]
 		[--more=<n> | --list | --independent | --merge-base]
 		[--no-name | --sha1-name] [--topics] [<rev> | <glob>]...
+'git-show-branch' --reflog[=<n>] <ref>
 
 DESCRIPTION
 -----------
@@ -96,6 +97,10 @@ OPTIONS
 	will show the revisions given by "git rev-list {caret}master
 	topic1 topic2"
 
+--reflog[=<n>] <ref>::
+	Shows <n> most recent ref-log entries for the given ref.
+
+
 Note that --more, --list, --independent and --merge-base options
 are mutually exclusive.
 

From 2f89543eaf98400d72498078d762c10e240c0657 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 26 Dec 2006 00:15:26 -0800
Subject: [PATCH 322/330] Document --numstat in git-apply and git-diff

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/diff-options.txt | 4 +++-
 Documentation/git-apply.txt    | 5 +++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index f12082e134..da1cc60e97 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -19,7 +19,9 @@
 --numstat::
 	Similar to \--stat, but shows number of added and
 	deleted lines in decimal notation and pathname without
-	abbreviation, to make it more machine friendly.
+	abbreviation, to make it more machine friendly.  For
+	binary files, outputs two `-` instead of saying
+	`0 0`.
 
 --shortstat::
 	Output only the last line of the --stat format containing total
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 2cc32d1c5e..33b93db508 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -33,8 +33,9 @@ OPTIONS
 --numstat::
 	Similar to \--stat, but shows number of added and
 	deleted lines in decimal notation and pathname without
-	abbreviation, to make it more machine friendly.  Turns
-	off "apply".
+	abbreviation, to make it more machine friendly.  For
+	binary files, outputs two `-` instead of saying
+	`0 0`.  Turns off "apply".
 
 --summary::
 	Instead of applying the patch, output a condensed

From 6934dec89538e054823aadcce08af040bc8dcf79 Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 26 Dec 2006 00:21:01 -0800
Subject: [PATCH 323/330] Document git-reset <commit> -- <paths>...

---
 Documentation/git-reset.txt | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 4a4ceb6201..4f424782eb 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -7,7 +7,9 @@ git-reset - Reset current HEAD to the specified state
 
 SYNOPSIS
 --------
-'git-reset' [--mixed | --soft | --hard] [<commit-ish>]
+[verse]
+'git-reset' [--mixed | --soft | --hard] [<commit>]
+'git-reset' [--mixed] <commit> [--] <paths>...
 
 DESCRIPTION
 -----------
@@ -21,6 +23,10 @@ the undo in the history.
 If you want to undo a commit other than the latest on a branch,
 gitlink:git-revert[1] is your friend.
 
+The second form with 'paths' is used to revert selected paths in
+the index from a given commit, without moving HEAD.
+
+
 OPTIONS
 -------
 --mixed::
@@ -37,9 +43,9 @@ OPTIONS
 --hard::
 	Matches the working tree and index to that of the tree being
 	switched to. Any changes to tracked files in the working tree
-	since <commit-ish> are lost.
+	since <commit> are lost.
 
-<commit-ish>::
+<commit>::
 	Commit to make the current HEAD.
 
 Examples

From 7dc269230761e32e663d9cd0b6f4c316466a490d Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Tue, 26 Dec 2006 14:08:08 -0500
Subject: [PATCH 324/330] Add git-reflog to .gitignore

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index d706dd92c6..255789a8b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,6 +87,7 @@ git-quiltimport
 git-read-tree
 git-rebase
 git-receive-pack
+git-reflog
 git-relink
 git-repack
 git-repo-config

From 7948d7744dfc19d25b6cde1091460fa68733c3d7 Mon Sep 17 00:00:00 2001
From: Nicolas Pitre <nico@cam.org>
Date: Tue, 26 Dec 2006 12:14:18 -0800
Subject: [PATCH 325/330] move git-blame to its place in .gitignore

Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 .gitignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 91e69665f4..98e513de53 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ git-applypatch
 git-archimport
 git-archive
 git-bisect
+git-blame
 git-branch
 git-cat-file
 git-check-ref-format
@@ -153,4 +154,3 @@ config.status
 config.mak.autogen
 config.mak.append
 configure
-git-blame

From 59f3e254801648322b2d5cd073ca0cc5009aa2de Mon Sep 17 00:00:00 2001
From: Andy Parkins <andyparkins@gmail.com>
Date: Thu, 21 Dec 2006 09:24:17 +0000
Subject: [PATCH 326/330] hooks/commit-msg: add example to add Signed-off-by
 line to message

After checking to see if the commit message already has the target
signed-off-by (for example in --amend commits), this patch generates a
signed off by line from the repository owner and adds it to the commit
message.

Based on Johannes Schindelin's earlier patch to perform the same
function.

Originally, this was done in the pre-commit hook but Junio pointed out
that the commit-msg hook allows the message to be edited.  This has the
aditional advantage that the commit-msg hook gets passed the name of the
message file as a parameter, so it doesn't have to figure out GIT_DIR for
itself.

Signed-off-by: Andy Parkins <andyparkins@gmail.com>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 templates/hooks--commit-msg | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/templates/hooks--commit-msg b/templates/hooks--commit-msg
index 0b906caa98..9b04f2d69c 100644
--- a/templates/hooks--commit-msg
+++ b/templates/hooks--commit-msg
@@ -8,6 +8,10 @@
 #
 # To enable this hook, make this file executable.
 
+# Uncomment the below to add a Signed-off-by line to the message.
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
 # This example catches duplicate Signed-off-by lines.
 
 test "" = "$(grep '^Signed-off-by: ' "$1" |

From 39ed7c181b7a8f5e28cffcaca9369e6ebc74a055 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 26 Dec 2006 16:27:37 -0800
Subject: [PATCH 327/330] git-svn: quiet down tests and fix some unportable
 shell constructs

The latest changes to git-commit have made it more verbose; and
I was running the setup of the tests outside of the test_expect_*,
so errors in those were not caught.  Now we move them to where
they can be eval'ed and have their output trapped.

export var=value has been removed

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 t/t9100-git-svn-basic.sh | 258 +++++++++++++++++++--------------------
 1 file changed, 127 insertions(+), 131 deletions(-)

diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 0edf19e48d..c22fe47213 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -19,180 +19,176 @@ esac
 
 echo 'define NO_SVN_TESTS to skip git-svn tests'
 
-mkdir import
-cd import
-
-echo foo > foo
-if test -z "$NO_SYMLINK"
-then
-	ln -s foo foo.link
-fi
-mkdir -p dir/a/b/c/d/e
-echo 'deep dir' > dir/a/b/c/d/e/file
-mkdir -p bar
-echo 'zzz' > bar/zzz
-echo '#!/bin/sh' > exec.sh
-chmod +x exec.sh
-svn import -m 'import for git-svn' . "$svnrepo" >/dev/null
-
-cd ..
-rm -rf import
-
 test_expect_success \
-    'initialize git-svn' \
-    "git-svn init $svnrepo"
+    'initialize git-svn' "
+	mkdir import &&
+	cd import &&
+	echo foo > foo &&
+	if test -z '$NO_SYMLINK'
+	then
+		ln -s foo foo.link
+	fi
+	mkdir -p dir/a/b/c/d/e &&
+	echo 'deep dir' > dir/a/b/c/d/e/file &&
+	mkdir bar &&
+	echo 'zzz' > bar/zzz &&
+	echo '#!/bin/sh' > exec.sh &&
+	chmod +x exec.sh &&
+	svn import -m 'import for git-svn' . $svnrepo >/dev/null &&
+	cd .. &&
+	rm -rf import &&
+	git-svn init $svnrepo"
 
 test_expect_success \
     'import an SVN revision into git' \
     'git-svn fetch'
 
-test_expect_success "checkout from svn" "svn co $svnrepo $SVN_TREE"
+test_expect_success "checkout from svn" "svn co $svnrepo '$SVN_TREE'"
 
 name='try a deep --rmdir with a commit'
-git checkout -f -b mybranch remotes/git-svn
-mv dir/a/b/c/d/e/file dir/file
-cp dir/file file
-git update-index --add --remove dir/a/b/c/d/e/file dir/file file
-git commit -m "$name"
-
-test_expect_success "$name" \
-    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch &&
-     svn up $SVN_TREE &&
-     test -d $SVN_TREE/dir && test ! -d $SVN_TREE/dir/a"
+test_expect_success "$name" "
+	git checkout -f -b mybranch remotes/git-svn &&
+	mv dir/a/b/c/d/e/file dir/file &&
+	cp dir/file file &&
+	git update-index --add --remove dir/a/b/c/d/e/file dir/file file &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch &&
+	svn up '$SVN_TREE' &&
+	test -d '$SVN_TREE'/dir && test ! -d '$SVN_TREE'/dir/a"
 
 
 name='detect node change from file to directory #1'
-mkdir dir/new_file
-mv dir/file dir/new_file/file
-mv dir/new_file dir/file
-git update-index --remove dir/file
-git update-index --add dir/file/file
-git commit -m "$name"
-
-test_expect_failure "$name" \
-    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch' \
-    || true
+test_expect_failure "$name" "
+	mkdir dir/new_file &&
+	mv dir/file dir/new_file/file &&
+	mv dir/new_file dir/file &&
+	git update-index --remove dir/file &&
+	git update-index --add dir/file/file &&
+	git commit -m '$name'  &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch" || true
 
 
 name='detect node change from directory to file #1'
-rm -rf dir $GIT_DIR/index
-git checkout -f -b mybranch2 remotes/git-svn
-mv bar/zzz zzz
-rm -rf bar
-mv zzz bar
-git update-index --remove -- bar/zzz
-git update-index --add -- bar
-git commit -m "$name"
-
-test_expect_failure "$name" \
-    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch2' \
-    || true
+test_expect_failure "$name" "
+	rm -rf dir '$GIT_DIR'/index &&
+	git checkout -f -b mybranch2 remotes/git-svn &&
+	mv bar/zzz zzz &&
+	rm -rf bar &&
+	mv zzz bar &&
+	git update-index --remove -- bar/zzz &&
+	git update-index --add -- bar &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch2" || true
 
 
 name='detect node change from file to directory #2'
-rm -f $GIT_DIR/index
-git checkout -f -b mybranch3 remotes/git-svn
-rm bar/zzz
-git-update-index --remove bar/zzz
-mkdir bar/zzz
-echo yyy > bar/zzz/yyy
-git-update-index --add bar/zzz/yyy
-git commit -m "$name"
-
-test_expect_failure "$name" \
-    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch3' \
-    || true
+test_expect_failure "$name" "
+	rm -f '$GIT_DIR'/index &&
+	git checkout -f -b mybranch3 remotes/git-svn &&
+	rm bar/zzz &&
+	git-update-index --remove bar/zzz &&
+	mkdir bar/zzz &&
+	echo yyy > bar/zzz/yyy &&
+	git-update-index --add bar/zzz/yyy &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch3" || true
 
 
 name='detect node change from directory to file #2'
-rm -f $GIT_DIR/index
-git checkout -f -b mybranch4 remotes/git-svn
-rm -rf dir
-git update-index --remove -- dir/file
-touch dir
-echo asdf > dir
-git update-index --add -- dir
-git commit -m "$name"
-
-test_expect_failure "$name" \
-    'git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch4' \
-    || true
+test_expect_failure "$name" "
+	rm -f '$GIT_DIR'/index &&
+	git checkout -f -b mybranch4 remotes/git-svn &&
+	rm -rf dir &&
+	git update-index --remove -- dir/file &&
+	touch dir &&
+	echo asdf > dir &&
+	git update-index --add -- dir &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch4" || true
 
 
 name='remove executable bit from a file'
-rm -f $GIT_DIR/index
-git checkout -f -b mybranch5 remotes/git-svn
-chmod -x exec.sh
-git update-index exec.sh
-git commit -m "$name"
-
-test_expect_success "$name" \
-    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
-     svn up $SVN_TREE &&
-     test ! -x $SVN_TREE/exec.sh"
+test_expect_success "$name" "
+	rm -f '$GIT_DIR'/index &&
+	git checkout -f -b mybranch5 remotes/git-svn &&
+	chmod -x exec.sh &&
+	git update-index exec.sh &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch5 &&
+	svn up '$SVN_TREE' &&
+	test ! -x '$SVN_TREE'/exec.sh"
 
 
 name='add executable bit back file'
-chmod +x exec.sh
-git update-index exec.sh
-git commit -m "$name"
-
-test_expect_success "$name" \
-    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
-     svn up $SVN_TREE &&
-     test -x $SVN_TREE/exec.sh"
-
+test_expect_success "$name" "
+	chmod +x exec.sh &&
+	git update-index exec.sh &&
+	git commit -m '$name' &&
+	git-svn set-tree --find-copies-harder --rmdir \
+		remotes/git-svn..mybranch5 &&
+	svn up '$SVN_TREE' &&
+	test -x '$SVN_TREE'/exec.sh"
 
 
 if test -z "$NO_SYMLINK"
 then
 	name='executable file becomes a symlink to bar/zzz (file)'
-	rm exec.sh
-	ln -s bar/zzz exec.sh
-	git update-index exec.sh
-	git commit -m "$name"
 
-	test_expect_success "$name" \
-	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
-	     svn up $SVN_TREE &&
-	     test -L $SVN_TREE/exec.sh"
+	test_expect_success "$name" "
+		rm exec.sh &&
+		ln -s bar/zzz exec.sh &&
+		git update-index exec.sh &&
+		git commit -m '$name' &&
+		git-svn set-tree --find-copies-harder --rmdir \
+			remotes/git-svn..mybranch5 &&
+		svn up '$SVN_TREE' &&
+		test -L '$SVN_TREE'/exec.sh"
 
 	name='new symlink is added to a file that was also just made executable'
-	chmod +x bar/zzz
-	ln -s bar/zzz exec-2.sh
-	git update-index --add bar/zzz exec-2.sh
-	git commit -m "$name"
 
-	test_expect_success "$name" \
-	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
-	     svn up $SVN_TREE &&
-	     test -x $SVN_TREE/bar/zzz &&
-	     test -L $SVN_TREE/exec-2.sh"
+	test_expect_success "$name" "
+		chmod +x bar/zzz &&
+		ln -s bar/zzz exec-2.sh &&
+		git update-index --add bar/zzz exec-2.sh &&
+		git commit -m '$name' &&
+		git-svn set-tree --find-copies-harder --rmdir \
+			remotes/git-svn..mybranch5 &&
+		svn up '$SVN_TREE' &&
+		test -x '$SVN_TREE'/bar/zzz &&
+		test -L '$SVN_TREE'/exec-2.sh"
 
 	name='modify a symlink to become a file'
-	echo git help > help || true
-	rm exec-2.sh
-	cp help exec-2.sh
-	git update-index exec-2.sh
-	git commit -m "$name"
-
-	test_expect_success "$name" \
-	    "git-svn set-tree --find-copies-harder --rmdir remotes/git-svn..mybranch5 &&
-	     svn up $SVN_TREE &&
-	     test -f $SVN_TREE/exec-2.sh &&
-	     test ! -L $SVN_TREE/exec-2.sh &&
-	     diff -u help $SVN_TREE/exec-2.sh"
+	test_expect_success "$name" "
+		echo git help > help || true &&
+		rm exec-2.sh &&
+		cp help exec-2.sh &&
+		git update-index exec-2.sh &&
+		git commit -m '$name' &&
+		git-svn set-tree --find-copies-harder --rmdir \
+			remotes/git-svn..mybranch5 &&
+		svn up '$SVN_TREE' &&
+		test -f '$SVN_TREE'/exec-2.sh &&
+		test ! -L '$SVN_TREE'/exec-2.sh &&
+		diff -u help $SVN_TREE/exec-2.sh"
 fi
 
 
 if test "$have_utf8" = t
 then
 	name="commit with UTF-8 message: locale: $GIT_SVN_LC_ALL"
-	echo '# hello' >> exec-2.sh
-	git update-index exec-2.sh
-	git commit -m 'éï∏'
-	export LC_ALL="$GIT_SVN_LC_ALL"
-	test_expect_success "$name" "git-svn set-tree HEAD"
+	LC_ALL="$GIT_SVN_LC_ALL"
+	export LC_ALL
+	test_expect_success "$name" "
+		echo '# hello' >> exec-2.sh &&
+		git update-index exec-2.sh &&
+		git commit -m 'éï∏' &&
+		git-svn set-tree HEAD"
 	unset LC_ALL
 else
 	echo "UTF-8 locale not set, test skipped ($GIT_SVN_LC_ALL)"

From c3a41037ed5fa113c8eb0f67a1cc17654f7ce5b1 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Tue, 26 Dec 2006 16:27:38 -0800
Subject: [PATCH 328/330] git-svn: dcommit should diff against the current HEAD
 after committing

This is a followup to dd31da2fdc199132c9fd42023aea5b33672d73cc.
Regardless of whether we commit an alternate head, we always
diff-tree based on the current HEAD, and rebase against our
remote reference as necessary.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 git-svn.perl | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/git-svn.perl b/git-svn.perl
index 4288a05c16..c2cdceb1d1 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -512,15 +512,15 @@ sub dcommit {
 	}
 	return if $_dry_run;
 	fetch();
-	my @diff = command('diff-tree', $head, $gs, '--');
+	my @diff = command('diff-tree', 'HEAD', $gs, '--');
 	my @finish;
 	if (@diff) {
 		@finish = qw/rebase/;
 		push @finish, qw/--merge/ if $_merge;
 		push @finish, "--strategy=$_strategy" if $_strategy;
-		print STDERR "W: $head and $gs differ, using @finish:\n", @diff;
+		print STDERR "W: HEAD and $gs differ, using @finish:\n", @diff;
 	} else {
-		print "No changes between current $head and $gs\n",
+		print "No changes between current HEAD and $gs\n",
 		      "Resetting to the latest $gs\n";
 		@finish = qw/reset --mixed/;
 	}

From 268b827d9883e77f395a63e4afa10ebbac10bfcf Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Tue, 26 Dec 2006 22:42:33 -0800
Subject: [PATCH 329/330] everyday: update for v1.5.0

Fix minor mark-up mistakes and adjust to v1.5.0 BCP, namely:

 - use "git add" instead of "git update-index";
 - use "git merge" instead of "git pull .";
 - use separate remote layout;
 - use config instead of remotes/origin file;

Also updates "My typical git day" example since now I have
'next' branch these days.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/everyday.txt | 89 ++++++++++++++++++++------------------
 1 file changed, 48 insertions(+), 41 deletions(-)

diff --git a/Documentation/everyday.txt b/Documentation/everyday.txt
index 9677671892..5d17ace721 100644
--- a/Documentation/everyday.txt
+++ b/Documentation/everyday.txt
@@ -47,11 +47,11 @@ $ git repack <3>
 $ git prune <4>
 ------------
 +
-<1> running without "--full" is usually cheap and assures the
+<1> running without `\--full` is usually cheap and assures the
 repository health reasonably well.
 <2> check how many loose objects there are and how much
 disk space is wasted by not repacking.
-<3> without "-a" repacks incrementally.  repacking every 4-5MB
+<3> without `-a` repacks incrementally.  repacking every 4-5MB
 of loose objects accumulation may be a good rule of thumb.
 <4> after repack, prune removes the duplicate loose objects.
 
@@ -80,8 +80,7 @@ following commands.
   * gitlink:git-checkout[1] and gitlink:git-branch[1] to switch
     branches.
 
-  * gitlink:git-add[1] and gitlink:git-update-index[1] to manage
-    the index file.
+  * gitlink:git-add[1] to manage the index file.
 
   * gitlink:git-diff[1] and gitlink:git-status[1] to see what
     you are in the middle of doing.
@@ -91,8 +90,7 @@ following commands.
   * gitlink:git-reset[1] and gitlink:git-checkout[1] (with
     pathname parameters) to undo changes.
 
-  * gitlink:git-pull[1] with "." as the remote to merge between
-    local branches.
+  * gitlink:git-merge[1] to merge between local branches.
 
   * gitlink:git-rebase[1] to maintain topic branches.
 
@@ -101,7 +99,7 @@ following commands.
 Examples
 ~~~~~~~~
 
-Use a tarball as a starting point for a new repository:
+Use a tarball as a starting point for a new repository.::
 +
 ------------
 $ tar zxf frotz.tar.gz
@@ -123,7 +121,7 @@ $ edit/compile/test
 $ git checkout -- curses/ux_audio_oss.c <2>
 $ git add curses/ux_audio_alsa.c <3>
 $ edit/compile/test
-$ git diff <4>
+$ git diff HEAD <4>
 $ git commit -a -s <5>
 $ edit/compile/test
 $ git reset --soft HEAD^ <6>
@@ -131,15 +129,15 @@ $ edit/compile/test
 $ git diff ORIG_HEAD <7>
 $ git commit -a -c ORIG_HEAD <8>
 $ git checkout master <9>
-$ git pull . alsa-audio <10>
+$ git merge alsa-audio <10>
 $ git log --since='3 days ago' <11>
 $ git log v2.43.. curses/ <12>
 ------------
 +
 <1> create a new topic branch.
-<2> revert your botched changes in "curses/ux_audio_oss.c".
+<2> revert your botched changes in `curses/ux_audio_oss.c`.
 <3> you need to tell git if you added a new file; removal and
-modification will be caught if you do "commit -a" later.
+modification will be caught if you do `git commit -a` later.
 <4> to see what changes you are committing.
 <5> commit everything as you have tested, with your sign-off.
 <6> take the last commit back, keeping what is in the working tree.
@@ -147,11 +145,13 @@ modification will be caught if you do "commit -a" later.
 <8> redo the commit undone in the previous step, using the message
 you originally wrote.
 <9> switch to the master branch.
-<10> merge a topic branch into your master branch
+<10> merge a topic branch into your master branch.  You can also use
+`git pull . alsa-audio`, i.e. pull from the local repository.
 <11> review commit logs; other forms to limit output can be
-combined and include --max-count=10 (show 10 commits), --until='2005-12-10'.
-<12> view only the changes that touch what's in curses/
-directory, since v2.43 tag.
+combined and include `\--max-count=10` (show 10 commits),
+`\--until=2005-12-10`, etc.
+<12> view only the changes that touch what's in `curses/`
+directory, since `v2.43` tag.
 
 
 Individual Developer (Participant)[[Individual Developer (Participant)]]
@@ -193,7 +193,7 @@ $ git fetch --tags <8>
 +
 <1> repeat as needed.
 <2> extract patches from your branch for e-mail submission.
-<3> "pull" fetches from "origin" by default and merges into the
+<3> `git pull` fetches from `origin` by default and merges into the
 current branch.
 <4> immediately after pulling, look at the changes done upstream
 since last time we checked, only in the
@@ -201,37 +201,41 @@ area we are interested in.
 <5> fetch from a specific branch from a specific repository and merge.
 <6> revert the pull.
 <7> garbage collect leftover objects from reverted pull.
-<8> from time to time, obtain official tags from the "origin"
-and store them under .git/refs/tags/.
+<8> from time to time, obtain official tags from the `origin`
+and store them under `.git/refs/tags/`.
 
 
 Push into another repository.::
 +
 ------------
-satellite$ git clone mothership:frotz/.git frotz <1>
+satellite$ git clone mothership:frotz frotz <1>
 satellite$ cd frotz
-satellite$ cat .git/remotes/origin <2>
-URL: mothership:frotz/.git
-Pull: master:origin
-satellite$ echo 'Push: master:satellite' >>.git/remotes/origin <3>
+satellite$ git repo-config --get-regexp '^(remote|branch)\.' <2>
+remote.origin.url mothership:frotz
+remote.origin.fetch refs/heads/*:refs/remotes/origin/*
+branch.master.remote origin
+branch.master.merge refs/heads/master
+satellite$ git repo-config remote.origin.push \
+           master:refs/remotes/satellite/master <3>
 satellite$ edit/compile/test/commit
 satellite$ git push origin <4>
 
 mothership$ cd frotz
 mothership$ git checkout master
-mothership$ git pull . satellite <5>
+mothership$ git merge satellite/master <5>
 ------------
 +
 <1> mothership machine has a frotz repository under your home
 directory; clone from it to start a repository on the satellite
 machine.
-<2> clone creates this file by default.  It arranges "git pull"
-to fetch and store the master branch head of mothership machine
-to local "origin" branch.
-<3> arrange "git push" to push local "master" branch to
-"satellite" branch of the mothership machine.
-<4> push will stash our work away on "satellite" branch on the
-mothership machine.  You could use this as a back-up method.
+<2> clone sets these configuration variables by default.
+It arranges `git pull` to fetch and store the branches of mothership
+machine to local `remotes/origin/*` tracking branches.
+<3> arrange `git push` to push local `master` branch to
+`remotes/satellite/master` branch of the mothership machine.
+<4> push will stash our work away on `remotes/satellite/master`
+tracking branch on the mothership machine.  You could use this as
+a back-up method.
 <5> on mothership machine, merge the work done on the satellite
 machine into the master branch.
 
@@ -247,7 +251,7 @@ $ git format-patch -k -m --stdout v2.6.14..private2.6.14 |
 +
 <1> create a private branch based on a well known (but somewhat behind)
 tag.
-<2> forward port all changes in private2.6.14 branch to master branch
+<2> forward port all changes in `private2.6.14` branch to `master` branch
 without a formal "merging".
 
 
@@ -284,13 +288,13 @@ $ mailx <3>
 & s 2 3 4 5 ./+to-apply
 & s 7 8 ./+hold-linus
 & q
-$ git checkout master
+$ git checkout -b topic/one master
 $ git am -3 -i -s -u ./+to-apply <4>
 $ compile/test
 $ git checkout -b hold/linus && git am -3 -i -s -u ./+hold-linus <5>
 $ git checkout topic/one && git rebase master <6>
-$ git checkout pu && git reset --hard master <7>
-$ git pull . topic/one topic/two && git pull . hold/linus <8>
+$ git checkout pu && git reset --hard next <7>
+$ git merge topic/one topic/two && git merge hold/linus <8>
 $ git checkout maint
 $ git cherry-pick master~4 <9>
 $ compile/test
@@ -307,29 +311,32 @@ they are.
 that are not quite ready.
 <4> apply them, interactively, with my sign-offs.
 <5> create topic branch as needed and apply, again with my
-sign-offs. 
+sign-offs.
 <6> rebase internal topic branch that has not been merged to the
 master, nor exposed as a part of a stable branch.
-<7> restart "pu" every time from the master.
+<7> restart `pu` every time from the next.
 <8> and bundle topic branches still cooking.
 <9> backport a critical fix.
 <10> create a signed tag.
 <11> make sure I did not accidentally rewind master beyond what I
-already pushed out.  "ko" shorthand points at the repository I have
+already pushed out.  `ko` shorthand points at the repository I have
 at kernel.org, and looks like this:
 +
 ------------
 $ cat .git/remotes/ko
 URL: kernel.org:/pub/scm/git/git.git
 Pull: master:refs/tags/ko-master
+Pull: next:refs/tags/ko-next
 Pull: maint:refs/tags/ko-maint
 Push: master
+Push: next
 Push: +pu
 Push: maint
 ------------
 +
-In the output from "git show-branch", "master" should have
-everything "ko-master" has.
+In the output from `git show-branch`, `master` should have
+everything `ko-master` has, and `next` should have
+everything `ko-next` has.
 
 <12> push out the bleeding edge.
 <13> push the tag out, too.
@@ -406,7 +413,7 @@ $ grep git /etc/shells <2>
 ------------
 +
 <1> log-in shell is set to /usr/bin/git-shell, which does not
-allow anything but "git push" and "git pull".  The users should
+allow anything but `git push` and `git pull`.  The users should
 get an ssh access to the machine.
 <2> in many distributions /etc/shells needs to list what is used
 as the login shell.

From ae72f685418b79bbd67e1017c5b1ac7d731c042e Mon Sep 17 00:00:00 2001
From: Junio C Hamano <junkio@cox.net>
Date: Wed, 27 Dec 2006 01:04:03 -0800
Subject: [PATCH 330/330] count-objects -v: show number of packs as well.

Recent "git push" keeps transferred objects packed much more aggressively
than before.  Monitoring output from git-count-objects -v for number of
loose objects is not enough to decide when to repack -- having too many
small packs is also a good cue for repacking.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-count-objects.txt | 4 ++--
 builtin-count-objects.c             | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 198ce77a8a..c59df6438c 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -20,8 +20,8 @@ OPTIONS
 -v::
 	In addition to the number of loose objects and disk
 	space consumed, it reports the number of in-pack
-	objects, and number of objects that can be removed by
-	running `git-prune-packed`.
+	objects, number of packs, and number of objects that can be
+	removed by running `git-prune-packed`.
 
 
 Author
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index 73c5982423..f5b22bb80e 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -105,16 +105,19 @@ int cmd_count_objects(int ac, const char **av, const char *prefix)
 	}
 	if (verbose) {
 		struct packed_git *p;
+		unsigned long num_pack = 0;
 		if (!packed_git)
 			prepare_packed_git();
 		for (p = packed_git; p; p = p->next) {
 			if (!p->pack_local)
 				continue;
 			packed += num_packed_objects(p);
+			num_pack++;
 		}
 		printf("count: %lu\n", loose);
 		printf("size: %lu\n", loose_size / 2);
 		printf("in-pack: %lu\n", packed);
+		printf("packs: %lu\n", num_pack);
 		printf("prune-packable: %lu\n", packed_loose);
 		printf("garbage: %lu\n", garbage);
 	}