Merge branch 'maint'

* maint:
  Fixed update-hook example allow-users format.
  Documentation/git-svn: updated design philosophy notes
  t/t4014: test "am -3" with mode-only change.
  Fix lapsus in builtin-apply.c
  git-push: documentation and tests for pushing only branches
  git-svnimport: Use separate arguments in the pipe for git-rev-parse
diff --git a/Documentation/RelNotes-1.5.4.txt b/Documentation/RelNotes-1.5.4.txt
new file mode 100644
index 0000000..1df66af
--- /dev/null
+++ b/Documentation/RelNotes-1.5.4.txt
@@ -0,0 +1,14 @@
+GIT v1.5.4 Release Notes
+========================
+
+Updates since v1.5.3
+--------------------
+
+
+
+Fixes since v1.5.3
+------------------
+
+All of the fixes in v1.5.3 maintenance series are included in
+this release, unless otherwise noted.
+
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 866e053..015910f 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -630,9 +630,17 @@
 	A value of 0 means no limit. Defaults to 0.
 
 pack.deltaCacheLimit::
-	The maxium size of a delta, that is cached in
+	The maximum size of a delta, that is cached in
 	gitlink:git-pack-objects[1]. Defaults to 1000.
 
+pack.threads::
+	Specifies the number of threads to spawn when searching for best
+	delta matches.  This requires that gitlink:git-pack-objects[1]
+	be compiled with pthreads otherwise this option is ignored with a
+	warning. This is meant to reduce packing time on multiprocessor
+	machines. The required amount of memory for the delta search window
+	is however multiplied by the number of threads.
+
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
 	at once.
diff --git a/Documentation/git-pack-objects.txt b/Documentation/git-pack-objects.txt
index f8a0be3..628f296 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -169,6 +169,14 @@
 	length, this option typically shrinks the resulting
 	packfile by 3-5 per-cent.
 
+--threads=<n>::
+	Specifies the number of threads to spawn when searching for best
+	delta matches.  This requires that pack-objects be compiled with
+	pthreads otherwise this option is ignored with a warning.
+	This is meant to reduce packing time on multiprocessor machines.
+	The required amount of memory for the delta search window is
+	however multiplied by the number of threads.
+
 --index-version=<version>[,<offset>]::
 	This is intended to be used by the test suite only. It allows
 	to force the version for the generated pack index, and to force
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 61b1810..0858fa8 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -8,8 +8,9 @@
 SYNOPSIS
 --------
 [verse]
-'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge] [-C<n>]
-	[-p | --preserve-merges] [--onto <newbase>] <upstream> [<branch>]
+'git-rebase' [-i | --interactive] [-v | --verbose] [-m | --merge]
+	[-C<n>] [ --whitespace=<option>] [-p | --preserve-merges]
+	[--onto <newbase>] <upstream> [<branch>]
 'git-rebase' --continue | --skip | --abort
 
 DESCRIPTION
@@ -209,6 +210,10 @@
 	context exist they all must match.  By default no context is
 	ever ignored.
 
+--whitespace=<nowarn|warn|error|error-all|strip>::
+	This flag is passed to the `git-apply` program
+	(see gitlink:git-apply[1]) that applies the patch.
+
 -i, \--interactive::
 	Make a list of the commits which are about to be rebased.  Let the
 	user edit that list before rebasing.  This mode can also be used to
diff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt
index 61a6022..94b9f17 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -10,7 +10,7 @@
 --------
 [verse]
 'git-remote'
-'git-remote' add [-t <branch>] [-m <branch>] [-f] <name> <url>
+'git-remote' add [-t <branch>] [-m <branch>] [-f] [--mirror] <name> <url>
 'git-remote' show <name>
 'git-remote' prune <name>
 'git-remote' update [group]
@@ -45,6 +45,10 @@
 With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set
 up to point at remote's `<master>` branch instead of whatever
 branch the `HEAD` at the remote repository actually points at.
++
+In mirror mode, enabled with `--mirror`, the refs will not be stored
+in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option
+only makes sense in bare repositories.
 
 'show'::
 
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 16bfd7b..1ec61af 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -75,6 +75,12 @@
 	Make git-send-email less verbose.  One line per email should be
 	all that is output.
 
+--identity::
+	A configuration identity. When given, causes values in the
+	'sendemail.<identity>' subsection to take precedence over
+	values in the 'sendemail' section. The default identity is
+	the value of 'sendemail.identity'.
+
 --smtp-server::
 	If set, specifies the outgoing SMTP server to use (e.g.
 	`smtp.example.com` or a raw IP address).  Alternatively it can
@@ -85,6 +91,17 @@
 	`/usr/lib/sendmail` if such program is available, or
 	`localhost` otherwise.
 
+--smtp-user, --smtp-pass::
+	Username and password for SMTP-AUTH. Defaults are the values of
+	the configuration values 'sendemail.smtpuser' and
+	'sendemail.smtppass', but see also 'sendemail.identity'.
+	If not set, authentication is not attempted.
+
+--smtp-ssl::
+	If set, connects to the SMTP server using SSL.
+	Default is the value of the 'sendemail.smtpssl' configuration value;
+	if that is unspecified, does not use SSL.
+
 --subject::
 	Specify the initial subject of the email thread.
 	Only necessary if --compose is also set.  If --compose
@@ -122,6 +139,13 @@
 
 CONFIGURATION
 -------------
+sendemail.identity::
+	The default configuration identity. When specified,
+	'sendemail.<identity>.<item>' will have higher precedence than
+	'sendemail.<item>'. This is useful to declare multiple SMTP
+	identities and to hoist sensitive authentication information
+	out of the repository and into the global configuation file.
+
 sendemail.aliasesfile::
 	To avoid typing long email addresses, point this to one or more
 	email aliases files.  You must also supply 'sendemail.aliasfiletype'.
@@ -141,7 +165,16 @@
 	parameter.
 
 sendemail.smtpserver::
-	Default smtp server to use.
+	Default SMTP server to use.
+
+sendemail.smtpuser::
+	Default SMTP-AUTH username.
+
+sendemail.smtppass::
+	Default SMTP-AUTH password.
+
+sendemail.smtpssl::
+	Boolean value specifying the default to the '--smtp-ssl' parameter.
 
 Author
 ------
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 46f9d59..d0e951e 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -421,6 +421,23 @@
 ----------------------------------------------------------------
 
 
+Creating an archive
+~~~~~~~~~~~~~~~~~~~
+
+`export-subst`
+^^^^^^^^^^^^^^
+
+If the attribute `export-subst` is set for a file then git will expand
+several placeholders when adding this file to an archive.  The
+expansion depends on the availability of a commit ID, i.e. if
+gitlink:git-archive[1] has been given a tree instead of a commit or a
+tag then no replacement will be done.  The placeholders are the same
+as those for the option `--pretty=format:` of gitlink:git-log[1],
+except that they need to be wrapped like this: `$Format:PLACEHOLDERS$`
+in the file.  E.g. the string `$Format:%H$` will be replaced by the
+commit hash.
+
+
 GIT
 ---
 Part of the gitlink:git[7] suite
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 3835fb3..3c0032c 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.5.3.1.GIT
+DEF_VER=v1.5.3.GIT
 
 LF='
 '
diff --git a/Makefile b/Makefile
index dace211..0055eef 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,8 @@
 #
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
+# Define NO_MEMMEM if you don't have memmem.
+#
 # Define NO_STRLCPY if you don't have strlcpy.
 #
 # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
@@ -122,6 +124,9 @@
 # If not set it defaults to the bare 'wish'. If it is set to the empty
 # string then NO_TCLTK will be forced (this is used by configure script).
 #
+# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
+# parallel delta searching when packing objects.
+#
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
 	@$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -206,7 +211,7 @@
 	git-ls-remote.sh \
 	git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
 	git-pull.sh git-rebase.sh git-rebase--interactive.sh \
-	git-repack.sh git-request-pull.sh git-reset.sh \
+	git-repack.sh git-request-pull.sh \
 	git-sh-setup.sh \
 	git-am.sh \
 	git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
@@ -353,6 +358,7 @@
 	builtin-reflog.o \
 	builtin-config.o \
 	builtin-rerere.o \
+	builtin-reset.o \
 	builtin-rev-list.o \
 	builtin-rev-parse.o \
 	builtin-revert.o \
@@ -396,12 +402,14 @@
 	NEEDS_LIBICONV = YesPlease
 	OLD_ICONV = UnfortunatelyYes
 	NO_STRLCPY = YesPlease
+	NO_MEMMEM = YesPlease
 endif
 ifeq ($(uname_S),SunOS)
 	NEEDS_SOCKET = YesPlease
 	NEEDS_NSL = YesPlease
 	SHELL_PATH = /bin/bash
 	NO_STRCASESTR = YesPlease
+	NO_MEMMEM = YesPlease
 	NO_HSTRERROR = YesPlease
 	ifeq ($(uname_R),5.8)
 		NEEDS_LIBICONV = YesPlease
@@ -424,6 +432,7 @@
 	NO_D_TYPE_IN_DIRENT = YesPlease
 	NO_D_INO_IN_DIRENT = YesPlease
 	NO_STRCASESTR = YesPlease
+	NO_MEMMEM = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
@@ -437,11 +446,13 @@
 endif
 ifeq ($(uname_S),FreeBSD)
 	NEEDS_LIBICONV = YesPlease
+	NO_MEMMEM = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
 endif
 ifeq ($(uname_S),OpenBSD)
 	NO_STRCASESTR = YesPlease
+	NO_MEMMEM = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
@@ -456,6 +467,7 @@
 endif
 ifeq ($(uname_S),AIX)
 	NO_STRCASESTR=YesPlease
+	NO_MEMMEM = YesPlease
 	NO_STRLCPY = YesPlease
 	NEEDS_LIBICONV=YesPlease
 endif
@@ -467,6 +479,7 @@
 	NO_IPV6=YesPlease
 	NO_SETENV=YesPlease
 	NO_STRCASESTR=YesPlease
+	NO_MEMMEM = YesPlease
 	NO_STRLCPY = YesPlease
 	NO_SOCKADDR_STORAGE=YesPlease
 	SHELL_PATH=/usr/gnu/bin/bash
@@ -661,6 +674,15 @@
 	COMPAT_CFLAGS += -DNO_HSTRERROR
 	COMPAT_OBJS += compat/hstrerror.o
 endif
+ifdef NO_MEMMEM
+	COMPAT_CFLAGS += -DNO_MEMMEM
+	COMPAT_OBJS += compat/memmem.o
+endif
+
+ifdef THREADED_DELTA_SEARCH
+	BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
+	EXTLIBS += -lpthread
+endif
 
 ifeq ($(TCLTK_PATH),)
 NO_TCLTK=NoThanks
diff --git a/RelNotes b/RelNotes
index ea8f800..46308ce 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.5.3.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.5.4.txt
\ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 66fe3e3..c0d95da 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -17,6 +17,7 @@
 static time_t archive_time;
 static int tar_umask = 002;
 static int verbose;
+static const struct commit *commit;
 
 /* writes out the whole block, but only if it is full */
 static void write_if_needed(void)
@@ -285,7 +286,8 @@
 		buffer = NULL;
 		size = 0;
 	} else {
-		buffer = convert_sha1_file(path.buf, sha1, mode, &type, &size);
+		buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
+		                              &size, commit);
 		if (!buffer)
 			die("cannot read %s", sha1_to_hex(sha1));
 	}
@@ -304,6 +306,7 @@
 
 	archive_time = args->time;
 	verbose = args->verbose;
+	commit = args->commit;
 
 	if (args->commit_sha1)
 		write_global_extended_header(args->commit_sha1);
diff --git a/archive-zip.c b/archive-zip.c
index 444e162..74e30f6 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -12,6 +12,7 @@
 static int verbose;
 static int zip_date;
 static int zip_time;
+static const struct commit *commit;
 
 static unsigned char *zip_dir;
 static unsigned int zip_dir_size;
@@ -191,11 +192,13 @@
 		compressed_size = 0;
 	} else if (S_ISREG(mode) || S_ISLNK(mode)) {
 		method = 0;
-		attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) : 0;
+		attr2 = S_ISLNK(mode) ? ((mode | 0777) << 16) :
+			(mode & 0111) ? ((mode) << 16) : 0;
 		if (S_ISREG(mode) && zlib_compression_level != 0)
 			method = 8;
 		result = 0;
-		buffer = convert_sha1_file(path, sha1, mode, &type, &size);
+		buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
+		                              commit);
 		if (!buffer)
 			die("cannot read %s", sha1_to_hex(sha1));
 		crc = crc32(crc, buffer, size);
@@ -229,7 +232,8 @@
 	}
 
 	copy_le32(dirent.magic, 0x02014b50);
-	copy_le16(dirent.creator_version, S_ISLNK(mode) ? 0x0317 : 0);
+	copy_le16(dirent.creator_version,
+		S_ISLNK(mode) || (S_ISREG(mode) && (mode & 0111)) ? 0x0317 : 0);
 	copy_le16(dirent.version, 10);
 	copy_le16(dirent.flags, 0);
 	copy_le16(dirent.compression_method, method);
@@ -316,6 +320,7 @@
 	zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
 	zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
 	verbose = args->verbose;
+	commit = args->commit;
 
 	if (args->base && plen > 0 && args->base[plen - 1] == '/') {
 		char *base = xstrdup(args->base);
diff --git a/archive.h b/archive.h
index 6838dc7..5791e65 100644
--- a/archive.h
+++ b/archive.h
@@ -8,6 +8,7 @@
 	const char *base;
 	struct tree *tree;
 	const unsigned char *commit_sha1;
+	const struct commit *commit;
 	time_t time;
 	const char **pathspec;
 	unsigned int verbose : 1;
@@ -42,4 +43,6 @@
 extern int write_zip_archive(struct archiver_args *);
 extern void *parse_extra_zip_args(int argc, const char **argv);
 
+extern void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size, const struct commit *commit);
+
 #endif	/* ARCHIVE_H */
diff --git a/builtin-add.c b/builtin-add.c
index 3d8b8b4..0d7d0ce 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -103,7 +103,6 @@
 			break;
 		case DIFF_STATUS_DELETED:
 			remove_file_from_cache(path);
-			cache_tree_invalidate_path(active_cache_tree, path);
 			if (verbose)
 				printf("remove '%s'\n", path);
 			break;
diff --git a/builtin-apply.c b/builtin-apply.c
index bd96977..86d89a4 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -2423,7 +2423,6 @@
 	if (update_index) {
 		if (remove_file_from_cache(patch->old_name) < 0)
 			die("unable to remove %s from index", patch->old_name);
-		cache_tree_invalidate_path(active_cache_tree, patch->old_name);
 	}
 	if (!cached) {
 		if (S_ISGITLINK(patch->old_mode)) {
@@ -2578,7 +2577,6 @@
 		mode = S_IFREG | 0644;
 	create_one_file(path, mode, buf, size);
 	add_index_file(path, mode, buf, size);
-	cache_tree_invalidate_path(active_cache_tree, path);
 }
 
 /* phase zero is to remove, phase one is to create */
diff --git a/builtin-archive.c b/builtin-archive.c
index 187491b..a90c65c 100644
--- a/builtin-archive.c
+++ b/builtin-archive.c
@@ -10,6 +10,7 @@
 #include "exec_cmd.h"
 #include "pkt-line.h"
 #include "sideband.h"
+#include "attr.h"
 
 static const char archive_usage[] = \
 "git-archive --format=<fmt> [--prefix=<prefix>/] [--verbose] [<extra>] <tree-ish> [path...]";
@@ -80,6 +81,100 @@
 	return !!rv;
 }
 
+static void *format_subst(const struct commit *commit, const char *format,
+                          unsigned long *sizep)
+{
+	unsigned long len = *sizep, result_len = 0;
+	const char *a = format;
+	char *result = NULL;
+
+	for (;;) {
+		const char *b, *c;
+		char *fmt, *formatted = NULL;
+		unsigned long a_len, fmt_len, formatted_len, allocated = 0;
+
+		b = memmem(a, len, "$Format:", 8);
+		if (!b || a + len < b + 9)
+			break;
+		c = memchr(b + 8, '$', len - 8);
+		if (!c)
+			break;
+
+		a_len = b - a;
+		fmt_len = c - b - 8;
+		fmt = xmalloc(fmt_len + 1);
+		memcpy(fmt, b + 8, fmt_len);
+		fmt[fmt_len] = '\0';
+
+		formatted_len = format_commit_message(commit, fmt, &formatted,
+		                                      &allocated);
+		free(fmt);
+		result = xrealloc(result, result_len + a_len + formatted_len);
+		memcpy(result + result_len, a, a_len);
+		memcpy(result + result_len + a_len, formatted, formatted_len);
+		result_len += a_len + formatted_len;
+		len -= c + 1 - a;
+		a = c + 1;
+	}
+
+	if (result && len) {
+		result = xrealloc(result, result_len + len);
+		memcpy(result + result_len, a, len);
+		result_len += len;
+	}
+
+	*sizep = result_len;
+
+	return result;
+}
+
+static void *convert_to_archive(const char *path,
+                                const void *src, unsigned long *sizep,
+                                const struct commit *commit)
+{
+	static struct git_attr *attr_export_subst;
+	struct git_attr_check check[1];
+
+	if (!commit)
+		return NULL;
+
+        if (!attr_export_subst)
+                attr_export_subst = git_attr("export-subst", 12);
+
+	check[0].attr = attr_export_subst;
+	if (git_checkattr(path, ARRAY_SIZE(check), check))
+		return NULL;
+	if (!ATTR_TRUE(check[0].value))
+		return NULL;
+
+	return format_subst(commit, src, sizep);
+}
+
+void *sha1_file_to_archive(const char *path, const unsigned char *sha1,
+                           unsigned int mode, enum object_type *type,
+                           unsigned long *size,
+                           const struct commit *commit)
+{
+	void *buffer, *converted;
+
+	buffer = read_sha1_file(sha1, type, size);
+	if (buffer && S_ISREG(mode)) {
+		converted = convert_to_working_tree(path, buffer, size);
+		if (converted) {
+			free(buffer);
+			buffer = converted;
+		}
+
+		converted = convert_to_archive(path, buffer, size, commit);
+		if (converted) {
+			free(buffer);
+			buffer = converted;
+		}
+	}
+
+	return buffer;
+}
+
 static int init_archiver(const char *name, struct archiver *ar)
 {
 	int rv = -1, i;
@@ -109,7 +204,7 @@
 	const unsigned char *commit_sha1;
 	time_t archive_time;
 	struct tree *tree;
-	struct commit *commit;
+	const struct commit *commit;
 	unsigned char sha1[20];
 
 	if (get_sha1(name, sha1))
@@ -142,6 +237,7 @@
 	}
 	ar_args->tree = tree;
 	ar_args->commit_sha1 = commit_sha1;
+	ar_args->commit = commit;
 	ar_args->time = archive_time;
 }
 
diff --git a/builtin-fetch--tool.c b/builtin-fetch--tool.c
index e2f8ede..24c7e6f 100644
--- a/builtin-fetch--tool.c
+++ b/builtin-fetch--tool.c
@@ -31,24 +31,19 @@
 		find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
 }
 
-static int update_ref(const char *action,
+static int update_ref_env(const char *action,
 		      const char *refname,
 		      unsigned char *sha1,
 		      unsigned char *oldval)
 {
 	char msg[1024];
 	char *rla = getenv("GIT_REFLOG_ACTION");
-	static struct ref_lock *lock;
 
 	if (!rla)
 		rla = "(reflog update)";
-	snprintf(msg, sizeof(msg), "%s: %s", rla, action);
-	lock = lock_any_ref_for_update(refname, oldval, 0);
-	if (!lock)
-		return 1;
-	if (write_ref_sha1(lock, sha1, msg) < 0)
-		return 1;
-	return 0;
+	if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+		warning("reflog message too long: %.*s...", 50, msg);
+	return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
 }
 
 static int update_local_ref(const char *name,
@@ -88,7 +83,7 @@
 		fprintf(stderr, "* %s: storing %s\n",
 			name, note);
 		show_new(type, sha1_new);
-		return update_ref(msg, name, sha1_new, NULL);
+		return update_ref_env(msg, name, sha1_new, NULL);
 	}
 
 	if (!hashcmp(sha1_old, sha1_new)) {
@@ -102,7 +97,7 @@
 	if (!strncmp(name, "refs/tags/", 10)) {
 		fprintf(stderr, "* %s: updating with %s\n", name, note);
 		show_new(type, sha1_new);
-		return update_ref("updating tag", name, sha1_new, NULL);
+		return update_ref_env("updating tag", name, sha1_new, NULL);
 	}
 
 	current = lookup_commit_reference(sha1_old);
@@ -117,7 +112,7 @@
 		fprintf(stderr, "* %s: fast forward to %s\n",
 			name, note);
 		fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
-		return update_ref("fast forward", name, sha1_new, sha1_old);
+		return update_ref_env("fast forward", name, sha1_new, sha1_old);
 	}
 	if (!force) {
 		fprintf(stderr,
@@ -131,7 +126,7 @@
 		"* %s: forcing update to non-fast forward %s\n",
 		name, note);
 	fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
-	return update_ref("forced-update", name, sha1_new, sha1_old);
+	return update_ref_env("forced-update", name, sha1_new, sha1_old);
 }
 
 static int append_fetch_head(FILE *fp,
diff --git a/builtin-mv.c b/builtin-mv.c
index 3563216..b95b7d2 100644
--- a/builtin-mv.c
+++ b/builtin-mv.c
@@ -276,11 +276,8 @@
 			add_file_to_cache(path, verbose);
 		}
 
-		for (i = 0; i < deleted.nr; i++) {
-			const char *path = deleted.items[i].path;
-			remove_file_from_cache(path);
-			cache_tree_invalidate_path(active_cache_tree, path);
-		}
+		for (i = 0; i < deleted.nr; i++)
+			remove_file_from_cache(deleted.items[i].path);
 
 		if (active_cache_changed) {
 			if (write_cache(newfd, active_cache, active_nr) ||
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 12509fa..a15906b 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -15,12 +15,16 @@
 #include "list-objects.h"
 #include "progress.h"
 
+#ifdef THREADED_DELTA_SEARCH
+#include <pthread.h>
+#endif
+
 static const char pack_usage[] = "\
 git-pack-objects [{ -q | --progress | --all-progress }] \n\
 	[--max-pack-size=N] [--local] [--incremental] \n\
 	[--window=N] [--window-memory=N] [--depth=N] \n\
 	[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
-	[--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
+	[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
 	[--stdout | base-name] [<ref-list | <object-list]";
 
 struct object_entry {
@@ -68,6 +72,7 @@
 static int window = 10;
 static uint32_t pack_size_limit;
 static int depth = 50;
+static int delta_search_threads = 1;
 static int pack_to_stdout;
 static int num_preferred_base;
 static struct progress progress_state;
@@ -78,7 +83,6 @@
 static unsigned long max_delta_cache_size = 0;
 static unsigned long cache_max_small_delta_size = 1000;
 
-static unsigned long window_memory_usage = 0;
 static unsigned long window_memory_limit = 0;
 
 /*
@@ -1291,6 +1295,31 @@
 	return 0;
 }
 
+#ifdef THREADED_DELTA_SEARCH
+
+static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define read_lock()		pthread_mutex_lock(&read_mutex)
+#define read_unlock()		pthread_mutex_unlock(&read_mutex)
+
+static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define cache_lock()		pthread_mutex_lock(&cache_mutex)
+#define cache_unlock()		pthread_mutex_unlock(&cache_mutex)
+
+static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
+#define progress_lock()		pthread_mutex_lock(&progress_mutex)
+#define progress_unlock()	pthread_mutex_unlock(&progress_mutex)
+
+#else
+
+#define read_lock()		(void)0
+#define read_unlock()		(void)0
+#define cache_lock()		(void)0
+#define cache_unlock()		(void)0
+#define progress_lock()		(void)0
+#define progress_unlock()	(void)0
+
+#endif
+
 /*
  * We search for deltas _backwards_ in a list sorted by type and
  * by size, so that we see progressively smaller and smaller files.
@@ -1300,7 +1329,7 @@
  * one.
  */
 static int try_delta(struct unpacked *trg, struct unpacked *src,
-		     unsigned max_depth)
+		     unsigned max_depth, unsigned long *mem_usage)
 {
 	struct object_entry *trg_entry = trg->entry;
 	struct object_entry *src_entry = src->entry;
@@ -1313,12 +1342,6 @@
 	if (trg_entry->type != src_entry->type)
 		return -1;
 
-	/* We do not compute delta to *create* objects we are not
-	 * going to pack.
-	 */
-	if (trg_entry->preferred_base)
-		return -1;
-
 	/*
 	 * We do not bother to try a delta that we discarded
 	 * on an earlier try, but only when reusing delta data.
@@ -1355,24 +1378,28 @@
 
 	/* Load data if not already done */
 	if (!trg->data) {
+		read_lock();
 		trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
+		read_unlock();
 		if (!trg->data)
 			die("object %s cannot be read",
 			    sha1_to_hex(trg_entry->idx.sha1));
 		if (sz != trg_size)
 			die("object %s inconsistent object length (%lu vs %lu)",
 			    sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
-		window_memory_usage += sz;
+		*mem_usage += sz;
 	}
 	if (!src->data) {
+		read_lock();
 		src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
+		read_unlock();
 		if (!src->data)
 			die("object %s cannot be read",
 			    sha1_to_hex(src_entry->idx.sha1));
 		if (sz != src_size)
 			die("object %s inconsistent object length (%lu vs %lu)",
 			    sha1_to_hex(src_entry->idx.sha1), sz, src_size);
-		window_memory_usage += sz;
+		*mem_usage += sz;
 	}
 	if (!src->index) {
 		src->index = create_delta_index(src->data, src_size);
@@ -1382,7 +1409,7 @@
 				warning("suboptimal pack - out of memory");
 			return 0;
 		}
-		window_memory_usage += sizeof_delta_index(src->index);
+		*mem_usage += sizeof_delta_index(src->index);
 	}
 
 	delta_buf = create_delta(src->index, trg->data, trg_size, &delta_size, max_size);
@@ -1402,17 +1429,27 @@
 	trg_entry->delta_size = delta_size;
 	trg->depth = src->depth + 1;
 
+	/*
+	 * Handle memory allocation outside of the cache
+	 * accounting lock.  Compiler will optimize the strangeness
+	 * away when THREADED_DELTA_SEARCH is not defined.
+	 */
+	if (trg_entry->delta_data)
+		free(trg_entry->delta_data);
+	cache_lock();
 	if (trg_entry->delta_data) {
 		delta_cache_size -= trg_entry->delta_size;
-		free(trg_entry->delta_data);
 		trg_entry->delta_data = NULL;
 	}
-
 	if (delta_cacheable(src_size, trg_size, delta_size)) {
-		trg_entry->delta_data = xrealloc(delta_buf, delta_size);
 		delta_cache_size += trg_entry->delta_size;
-	} else
+		cache_unlock();
+		trg_entry->delta_data = xrealloc(delta_buf, delta_size);
+	} else {
+		cache_unlock();
 		free(delta_buf);
+	}
+
 	return 1;
 }
 
@@ -1429,68 +1466,60 @@
 	return m;
 }
 
-static void free_unpacked(struct unpacked *n)
+static unsigned long free_unpacked(struct unpacked *n)
 {
-	window_memory_usage -= sizeof_delta_index(n->index);
+	unsigned long freed_mem = sizeof_delta_index(n->index);
 	free_delta_index(n->index);
 	n->index = NULL;
 	if (n->data) {
+		freed_mem += n->entry->size;
 		free(n->data);
 		n->data = NULL;
-		window_memory_usage -= n->entry->size;
 	}
 	n->entry = NULL;
 	n->depth = 0;
+	return freed_mem;
 }
 
-static void find_deltas(struct object_entry **list, int window, int depth)
+static void find_deltas(struct object_entry **list, unsigned list_size,
+			int window, int depth, unsigned *processed)
 {
-	uint32_t i = nr_objects, idx = 0, count = 0, processed = 0;
+	uint32_t i = list_size, idx = 0, count = 0;
 	unsigned int array_size = window * sizeof(struct unpacked);
 	struct unpacked *array;
-	int max_depth;
+	unsigned long mem_usage = 0;
 
-	if (!nr_objects)
-		return;
 	array = xmalloc(array_size);
 	memset(array, 0, array_size);
-	if (progress)
-		start_progress(&progress_state, "Deltifying %u objects...", "", nr_result);
 
 	do {
 		struct object_entry *entry = list[--i];
 		struct unpacked *n = array + idx;
-		int j;
+		int j, max_depth, best_base = -1;
 
-		if (!entry->preferred_base)
-			processed++;
-
-		if (progress)
-			display_progress(&progress_state, processed);
-
-		if (entry->delta)
-			/* This happens if we decided to reuse existing
-			 * delta from a pack.  "!no_reuse_delta &&" is implied.
-			 */
-			continue;
-
-		if (entry->size < 50)
-			continue;
-
-		if (entry->no_try_delta)
-			continue;
-
-		free_unpacked(n);
+		mem_usage -= free_unpacked(n);
 		n->entry = entry;
 
 		while (window_memory_limit &&
-		       window_memory_usage > window_memory_limit &&
+		       mem_usage > window_memory_limit &&
 		       count > 1) {
 			uint32_t tail = (idx + window - count) % window;
-			free_unpacked(array + tail);
+			mem_usage -= free_unpacked(array + tail);
 			count--;
 		}
 
+		/* We do not compute delta to *create* objects we are not
+		 * going to pack.
+		 */
+		if (entry->preferred_base)
+			goto next;
+
+		progress_lock();
+		(*processed)++;
+		if (progress)
+			display_progress(&progress_state, *processed);
+		progress_unlock();
+
 		/*
 		 * If the current object is at pack edge, take the depth the
 		 * objects that depend on the current object into account
@@ -1505,6 +1534,7 @@
 
 		j = window;
 		while (--j > 0) {
+			int ret;
 			uint32_t other_idx = idx + j;
 			struct unpacked *m;
 			if (other_idx >= window)
@@ -1512,8 +1542,11 @@
 			m = array + other_idx;
 			if (!m->entry)
 				break;
-			if (try_delta(n, m, max_depth) < 0)
+			ret = try_delta(n, m, max_depth, &mem_usage);
+			if (ret < 0)
 				break;
+			else if (ret > 0)
+				best_base = other_idx;
 		}
 
 		/* if we made n a delta, and if n is already at max
@@ -1523,6 +1556,23 @@
 		if (entry->delta && depth <= n->depth)
 			continue;
 
+		/*
+		 * Move the best delta base up in the window, after the
+		 * currently deltified object, to keep it longer.  It will
+		 * be the first base object to be attempted next.
+		 */
+		if (entry->delta) {
+			struct unpacked swap = array[best_base];
+			int dist = (window + idx - best_base) % window;
+			int dst = best_base;
+			while (dist--) {
+				int src = (dst + 1) % window;
+				array[dst] = array[src];
+				dst = src;
+			}
+			array[dst] = swap;
+		}
+
 		next:
 		idx++;
 		if (count + 1 < window)
@@ -1531,9 +1581,6 @@
 			idx = 0;
 	} while (i > 0);
 
-	if (progress)
-		stop_progress(&progress_state);
-
 	for (i = 0; i < window; ++i) {
 		free_delta_index(array[i].index);
 		free(array[i].data);
@@ -1541,21 +1588,145 @@
 	free(array);
 }
 
+#ifdef THREADED_DELTA_SEARCH
+
+struct thread_params {
+	pthread_t thread;
+	struct object_entry **list;
+	unsigned list_size;
+	int window;
+	int depth;
+	unsigned *processed;
+};
+
+static pthread_mutex_t data_request  = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t data_ready    = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t data_provider = PTHREAD_MUTEX_INITIALIZER;
+static struct thread_params *data_requester;
+
+static void *threaded_find_deltas(void *arg)
+{
+	struct thread_params *me = arg;
+
+	for (;;) {
+		pthread_mutex_lock(&data_request);
+		data_requester = me;
+		pthread_mutex_unlock(&data_provider);
+		pthread_mutex_lock(&data_ready);
+		pthread_mutex_unlock(&data_request);
+
+		if (!me->list_size)
+			return NULL;
+
+		find_deltas(me->list, me->list_size,
+			    me->window, me->depth, me->processed);
+	}
+}
+
+static void ll_find_deltas(struct object_entry **list, unsigned list_size,
+			   int window, int depth, unsigned *processed)
+{
+	struct thread_params *target, p[delta_search_threads];
+	int i, ret;
+	unsigned chunk_size;
+
+	if (delta_search_threads <= 1) {
+		find_deltas(list, list_size, window, depth, processed);
+		return;
+	}
+
+	pthread_mutex_lock(&data_provider);
+	pthread_mutex_lock(&data_ready);
+
+	for (i = 0; i < delta_search_threads; i++) {
+		p[i].window = window;
+		p[i].depth = depth;
+		p[i].processed = processed;
+		ret = pthread_create(&p[i].thread, NULL,
+				     threaded_find_deltas, &p[i]);
+		if (ret)
+			die("unable to create thread: %s", strerror(ret));
+	}
+
+	/* this should be auto-tuned somehow */
+	chunk_size = window * 1000;
+
+	do {
+		unsigned sublist_size = chunk_size;
+		if (sublist_size > list_size)
+			sublist_size = list_size;
+
+		/* try to split chunks on "path" boundaries */
+		while (sublist_size < list_size && list[sublist_size]->hash &&
+		       list[sublist_size]->hash == list[sublist_size-1]->hash)
+			sublist_size++;
+
+		pthread_mutex_lock(&data_provider);
+		target = data_requester;
+		target->list = list;
+		target->list_size = sublist_size;
+		pthread_mutex_unlock(&data_ready);
+
+		list += sublist_size;
+		list_size -= sublist_size;
+		if (!sublist_size) {
+			pthread_join(target->thread, NULL);
+			i--;
+		}
+	} while (i);
+}
+
+#else
+#define ll_find_deltas find_deltas
+#endif
+
 static void prepare_pack(int window, int depth)
 {
 	struct object_entry **delta_list;
-	uint32_t i;
+	uint32_t i, n, nr_deltas;
 
 	get_object_details();
 
-	if (!window || !depth)
+	if (!nr_objects || !window || !depth)
 		return;
 
 	delta_list = xmalloc(nr_objects * sizeof(*delta_list));
-	for (i = 0; i < nr_objects; i++)
-		delta_list[i] = objects + i;
-	qsort(delta_list, nr_objects, sizeof(*delta_list), type_size_sort);
-	find_deltas(delta_list, window+1, depth);
+	nr_deltas = n = 0;
+
+	for (i = 0; i < nr_objects; i++) {
+		struct object_entry *entry = objects + i;
+
+		if (entry->delta)
+			/* This happens if we decided to reuse existing
+			 * delta from a pack.  "!no_reuse_delta &&" is implied.
+			 */
+			continue;
+
+		if (entry->size < 50)
+			continue;
+
+		if (entry->no_try_delta)
+			continue;
+
+		if (!entry->preferred_base)
+			nr_deltas++;
+
+		delta_list[n++] = entry;
+	}
+
+	if (nr_deltas) {
+		unsigned nr_done = 0;
+		if (progress)
+			start_progress(&progress_state,
+				       "Deltifying %u objects...", "",
+				       nr_deltas);
+		qsort(delta_list, n, sizeof(*delta_list), type_size_sort);
+		ll_find_deltas(delta_list, n, window+1, depth, &nr_done);
+		if (progress)
+			stop_progress(&progress_state);
+		if (nr_done != nr_deltas)
+			die("inconsistency with delta count");
+	}
 	free(delta_list);
 }
 
@@ -1591,6 +1762,17 @@
 		cache_max_small_delta_size = git_config_int(k, v);
 		return 0;
 	}
+	if (!strcmp(k, "pack.threads")) {
+		delta_search_threads = git_config_int(k, v);
+		if (delta_search_threads < 1)
+			die("invalid number of threads specified (%d)",
+			    delta_search_threads);
+#ifndef THREADED_DELTA_SEARCH
+		if (delta_search_threads > 1)
+			warning("no threads support, ignoring %s", k);
+#endif
+		return 0;
+	}
 	return git_default_config(k, v);
 }
 
@@ -1750,6 +1932,18 @@
 				usage(pack_usage);
 			continue;
 		}
+		if (!prefixcmp(arg, "--threads=")) {
+			char *end;
+			delta_search_threads = strtoul(arg+10, &end, 0);
+			if (!arg[10] || *end || delta_search_threads < 1)
+				usage(pack_usage);
+#ifndef THREADED_DELTA_SEARCH
+			if (delta_search_threads > 1)
+				warning("no threads support, "
+					"ignoring %s", arg);
+#endif
+			continue;
+		}
 		if (!prefixcmp(arg, "--depth=")) {
 			char *end;
 			depth = strtoul(arg+8, &end, 0);
diff --git a/builtin-reset.c b/builtin-reset.c
new file mode 100644
index 0000000..99d5c08
--- /dev/null
+++ b/builtin-reset.c
@@ -0,0 +1,279 @@
+/*
+ * "git reset" builtin command
+ *
+ * Copyright (c) 2007 Carlos Rica
+ *
+ * Based on git-reset.sh, which is
+ *
+ * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
+ */
+#include "cache.h"
+#include "tag.h"
+#include "object.h"
+#include "commit.h"
+#include "run-command.h"
+#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tree.h"
+
+static const char builtin_reset_usage[] =
+"git-reset [--mixed | --soft | --hard]  [<commit-ish>] [ [--] <paths>...]";
+
+static char *args_to_str(const char **argv)
+{
+	char *buf = NULL;
+	unsigned long len, space = 0, nr = 0;
+
+	for (; *argv; argv++) {
+		len = strlen(*argv);
+		ALLOC_GROW(buf, nr + 1 + len, space);
+		if (nr)
+			buf[nr++] = ' ';
+		memcpy(buf + nr, *argv, len);
+		nr += len;
+	}
+	ALLOC_GROW(buf, nr + 1, space);
+	buf[nr] = '\0';
+
+	return buf;
+}
+
+static inline int is_merge(void)
+{
+	return !access(git_path("MERGE_HEAD"), F_OK);
+}
+
+static int unmerged_files(void)
+{
+	char b;
+	ssize_t len;
+	struct child_process cmd;
+	const char *argv_ls_files[] = {"ls-files", "--unmerged", NULL};
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.argv = argv_ls_files;
+	cmd.git_cmd = 1;
+	cmd.out = -1;
+
+	if (start_command(&cmd))
+		die("Could not run sub-command: git ls-files");
+
+	len = xread(cmd.out, &b, 1);
+	if (len < 0)
+		die("Could not read output from git ls-files: %s",
+						strerror(errno));
+	finish_command(&cmd);
+
+	return len;
+}
+
+static int reset_index_file(const unsigned char *sha1, int is_hard_reset)
+{
+	int i = 0;
+	const char *args[6];
+
+	args[i++] = "read-tree";
+	args[i++] = "-v";
+	args[i++] = "--reset";
+	if (is_hard_reset)
+		args[i++] = "-u";
+	args[i++] = sha1_to_hex(sha1);
+	args[i] = NULL;
+
+	return run_command_v_opt(args, RUN_GIT_CMD);
+}
+
+static void print_new_head_line(struct commit *commit)
+{
+	const char *hex, *dots = "...", *body;
+
+	hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+	if (!hex) {
+		hex = sha1_to_hex(commit->object.sha1);
+		dots = "";
+	}
+	printf("HEAD is now at %s%s", hex, dots);
+	body = strstr(commit->buffer, "\n\n");
+	if (body) {
+		const char *eol;
+		size_t len;
+		body += 2;
+		eol = strchr(body, '\n');
+		len = eol ? eol - body : strlen(body);
+		printf(" %.*s\n", (int) len, body);
+	}
+	else
+		printf("\n");
+}
+
+static int update_index_refresh(void)
+{
+	const char *argv_update_index[] = {"update-index", "--refresh", NULL};
+	return run_command_v_opt(argv_update_index, RUN_GIT_CMD);
+}
+
+static void update_index_from_diff(struct diff_queue_struct *q,
+		struct diff_options *opt, void *data)
+{
+	int i;
+
+	/* do_diff_cache() mangled the index */
+	discard_cache();
+	read_cache();
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filespec *one = q->queue[i]->one;
+		if (one->mode) {
+			struct cache_entry *ce;
+			ce = make_cache_entry(one->mode, one->sha1, one->path,
+				0, 0);
+			add_cache_entry(ce, ADD_CACHE_OK_TO_ADD |
+				ADD_CACHE_OK_TO_REPLACE);
+		} else
+			remove_file_from_cache(one->path);
+	}
+}
+
+static int read_from_tree(const char *prefix, const char **argv,
+		unsigned char *tree_sha1)
+{
+        struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+	int index_fd;
+	struct diff_options opt;
+
+	memset(&opt, 0, sizeof(opt));
+	diff_tree_setup_paths(get_pathspec(prefix, (const char **)argv), &opt);
+	opt.output_format = DIFF_FORMAT_CALLBACK;
+	opt.format_callback = update_index_from_diff;
+
+	index_fd = hold_locked_index(lock, 1);
+	read_cache();
+	if (do_diff_cache(tree_sha1, &opt))
+		return 1;
+	diffcore_std(&opt);
+	diff_flush(&opt);
+	return write_cache(index_fd, active_cache, active_nr) ||
+		close(index_fd) ||
+		commit_locked_index(lock);
+}
+
+static void prepend_reflog_action(const char *action, char *buf, size_t size)
+{
+	const char *sep = ": ";
+	const char *rla = getenv("GIT_REFLOG_ACTION");
+	if (!rla)
+		rla = sep = "";
+	if (snprintf(buf, size, "%s%s%s", rla, sep, action) >= size)
+		warning("Reflog action message too long: %.*s...", 50, buf);
+}
+
+enum reset_type { MIXED, SOFT, HARD, NONE };
+static char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
+
+int cmd_reset(int argc, const char **argv, const char *prefix)
+{
+	int i = 1, reset_type = NONE, update_ref_status = 0;
+	const char *rev = "HEAD";
+	unsigned char sha1[20], *orig = NULL, sha1_orig[20],
+				*old_orig = NULL, sha1_old_orig[20];
+	struct commit *commit;
+	char *reflog_action, msg[1024];
+
+	git_config(git_default_config);
+
+	reflog_action = args_to_str(argv);
+	setenv("GIT_REFLOG_ACTION", reflog_action, 0);
+
+	if (i < argc) {
+		if (!strcmp(argv[i], "--mixed")) {
+			reset_type = MIXED;
+			i++;
+		}
+		else if (!strcmp(argv[i], "--soft")) {
+			reset_type = SOFT;
+			i++;
+		}
+		else if (!strcmp(argv[i], "--hard")) {
+			reset_type = HARD;
+			i++;
+		}
+	}
+
+	if (i < argc && argv[i][0] != '-')
+		rev = argv[i++];
+
+	if (get_sha1(rev, sha1))
+		die("Failed to resolve '%s' as a valid ref.", rev);
+
+	commit = lookup_commit_reference(sha1);
+	if (!commit)
+		die("Could not parse object '%s'.", rev);
+	hashcpy(sha1, commit->object.sha1);
+
+	if (i < argc && !strcmp(argv[i], "--"))
+		i++;
+	else if (i < argc && argv[i][0] == '-')
+		usage(builtin_reset_usage);
+
+	/* git reset tree [--] paths... can be used to
+	 * load chosen paths from the tree into the index without
+	 * affecting the working tree nor HEAD. */
+	if (i < argc) {
+		if (reset_type == MIXED)
+			warning("--mixed option is deprecated with paths.");
+		else if (reset_type != NONE)
+			die("Cannot do %s reset with paths.",
+					reset_type_names[reset_type]);
+		if (read_from_tree(prefix, argv + i, sha1))
+			return 1;
+		return update_index_refresh() ? 1 : 0;
+	}
+	if (reset_type == NONE)
+		reset_type = MIXED; /* by default */
+
+	/* Soft reset does not touch the index file nor the working tree
+	 * at all, but requires them in a good order.  Other resets reset
+	 * the index file to the tree object we are switching to. */
+	if (reset_type == SOFT) {
+		if (is_merge() || unmerged_files())
+			die("Cannot do a soft reset in the middle of a merge.");
+	}
+	else if (reset_index_file(sha1, (reset_type == HARD)))
+		die("Could not reset index file to revision '%s'.", rev);
+
+	/* Any resets update HEAD to the head being switched to,
+	 * saving the previous head in ORIG_HEAD before. */
+	if (!get_sha1("ORIG_HEAD", sha1_old_orig))
+		old_orig = sha1_old_orig;
+	if (!get_sha1("HEAD", sha1_orig)) {
+		orig = sha1_orig;
+		prepend_reflog_action("updating ORIG_HEAD", msg, sizeof(msg));
+		update_ref(msg, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+	}
+	else if (old_orig)
+		delete_ref("ORIG_HEAD", old_orig);
+	prepend_reflog_action("updating HEAD", msg, sizeof(msg));
+	update_ref_status = update_ref(msg, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+
+	switch (reset_type) {
+	case HARD:
+		if (!update_ref_status)
+			print_new_head_line(commit);
+		break;
+	case SOFT: /* Nothing else to do. */
+		break;
+	case MIXED: /* Report what has not been updated. */
+		update_index_refresh();
+		break;
+	}
+
+	unlink(git_path("MERGE_HEAD"));
+	unlink(git_path("rr-cache/MERGE_RR"));
+	unlink(git_path("MERGE_MSG"));
+	unlink(git_path("SQUASH_MSG"));
+
+	free(reflog_action);
+
+	return update_ref_status;
+}
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index ac551d5..899a31d 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -189,7 +189,7 @@
 	return count;
 }
 
-static inline int halfway(struct commit_list *p, int distance, int nr)
+static inline int halfway(struct commit_list *p, int nr)
 {
 	/*
 	 * Don't short-cut something we are not going to return!
@@ -202,8 +202,7 @@
 	 * 2 and 3 are halfway of 5.
 	 * 3 is halfway of 6 but 2 and 4 are not.
 	 */
-	distance *= 2;
-	switch (distance - nr) {
+	switch (2 * weight(p) - nr) {
 	case -1: case 0: case 1:
 		return 1;
 	default:
@@ -255,6 +254,30 @@
 }
 #endif /* DEBUG_BISECT */
 
+static struct commit_list *best_bisection(struct commit_list *list, int nr)
+{
+	struct commit_list *p, *best;
+	int best_distance = -1;
+
+	best = list;
+	for (p = list; p; p = p->next) {
+		int distance;
+		unsigned flags = p->item->object.flags;
+
+		if (revs.prune_fn && !(flags & TREECHANGE))
+			continue;
+		distance = weight(p);
+		if (nr - distance < distance)
+			distance = nr - distance;
+		if (distance > best_distance) {
+			best = p;
+			best_distance = distance;
+		}
+	}
+
+	return best;
+}
+
 /*
  * zero or positive weight is the number of interesting commits it can
  * reach, including itself.  Especially, weight = 0 means it does not
@@ -268,39 +291,12 @@
  * unknown.  After running count_distance() first, they will get zero
  * or positive distance.
  */
-
-static struct commit_list *find_bisection(struct commit_list *list,
-					  int *reaches, int *all)
+static struct commit_list *do_find_bisection(struct commit_list *list,
+					     int nr, int *weights)
 {
-	int n, nr, on_list, counted, distance;
-	struct commit_list *p, *best, *next, *last;
-	int *weights;
+	int n, counted;
+	struct commit_list *p;
 
-	show_list("bisection 2 entry", 0, 0, list);
-
-	/*
-	 * Count the number of total and tree-changing items on the
-	 * list, while reversing the list.
-	 */
-	for (nr = on_list = 0, last = NULL, p = list;
-	     p;
-	     p = next) {
-		unsigned flags = p->item->object.flags;
-
-		next = p->next;
-		if (flags & UNINTERESTING)
-			continue;
-		p->next = last;
-		last = p;
-		if (!revs.prune_fn || (flags & TREECHANGE))
-			nr++;
-		on_list++;
-	}
-	list = last;
-	show_list("bisection 2 sorted", 0, nr, list);
-
-	*all = nr;
-	weights = xcalloc(on_list, sizeof(*weights));
 	counted = 0;
 
 	for (n = 0, p = list; p; p = p->next) {
@@ -349,20 +345,14 @@
 	for (p = list; p; p = p->next) {
 		if (p->item->object.flags & UNINTERESTING)
 			continue;
-		n = weight(p);
-		if (n != -2)
+		if (weight(p) != -2)
 			continue;
-		distance = count_distance(p);
+		weight_set(p, count_distance(p));
 		clear_distance(list);
-		weight_set(p, distance);
 
 		/* Does it happen to be at exactly half-way? */
-		if (halfway(p, distance, nr)) {
-			p->next = NULL;
-			*reaches = distance;
-			free(weights);
+		if (halfway(p, nr))
 			return p;
-		}
 		counted++;
 	}
 
@@ -399,38 +389,59 @@
 				weight_set(p, weight(q));
 
 			/* Does it happen to be at exactly half-way? */
-			distance = weight(p);
-			if (halfway(p, distance, nr)) {
-				p->next = NULL;
-				*reaches = distance;
-				free(weights);
+			if (halfway(p, nr))
 				return p;
-			}
 		}
 	}
 
 	show_list("bisection 2 counted all", counted, nr, list);
 
 	/* Then find the best one */
-	counted = -1;
-	best = list;
-	for (p = list; p; p = p->next) {
+	return best_bisection(list, nr);
+}
+
+static struct commit_list *find_bisection(struct commit_list *list,
+					  int *reaches, int *all)
+{
+	int nr, on_list;
+	struct commit_list *p, *best, *next, *last;
+	int *weights;
+
+	show_list("bisection 2 entry", 0, 0, list);
+
+	/*
+	 * Count the number of total and tree-changing items on the
+	 * list, while reversing the list.
+	 */
+	for (nr = on_list = 0, last = NULL, p = list;
+	     p;
+	     p = next) {
 		unsigned flags = p->item->object.flags;
 
-		if (revs.prune_fn && !(flags & TREECHANGE))
+		next = p->next;
+		if (flags & UNINTERESTING)
 			continue;
-		distance = weight(p);
-		if (nr - distance < distance)
-			distance = nr - distance;
-		if (distance > counted) {
-			best = p;
-			counted = distance;
-			*reaches = weight(p);
-		}
+		p->next = last;
+		last = p;
+		if (!revs.prune_fn || (flags & TREECHANGE))
+			nr++;
+		on_list++;
 	}
+	list = last;
+	show_list("bisection 2 sorted", 0, nr, list);
+
+	*all = nr;
+	weights = xcalloc(on_list, sizeof(*weights));
+
+	/* Do the real work of finding bisection commit. */
+	best = do_find_bisection(list, nr, weights);
+
 	if (best)
 		best->next = NULL;
+
+	*reaches = weight(best);
 	free(weights);
+
 	return best;
 }
 
diff --git a/builtin-rm.c b/builtin-rm.c
index 9a808c1..3b0677e 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -227,7 +227,6 @@
 
 		if (remove_file_from_cache(path))
 			die("git-rm: unable to remove %s", path);
-		cache_tree_invalidate_path(active_cache_tree, path);
 	}
 
 	if (show_only)
diff --git a/builtin-update-index.c b/builtin-update-index.c
index a7a4574..55fb679 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -195,11 +195,6 @@
 	int len;
 	struct stat st;
 
-	/* We probably want to do this in remove_file_from_cache() and
-	 * add_cache_entry() instead...
-	 */
-	cache_tree_invalidate_path(active_cache_tree, path);
-
 	/*
 	 * First things first: get the stat information, to decide
 	 * what to do about the pathname!
@@ -239,7 +234,6 @@
 		return error("%s: cannot add to the index - missing --add option?",
 			     path);
 	report("add '%s'", path);
-	cache_tree_invalidate_path(active_cache_tree, path);
 	return 0;
 }
 
@@ -284,7 +278,6 @@
 			die("Unable to mark file %s", path);
 		goto free_return;
 	}
-	cache_tree_invalidate_path(active_cache_tree, path);
 
 	if (force_remove) {
 		if (remove_file_from_cache(p))
@@ -367,7 +360,6 @@
 				free(path_name);
 			continue;
 		}
-		cache_tree_invalidate_path(active_cache_tree, path_name);
 
 		if (!mode) {
 			/* mode == 0 means there is no such path -- remove */
@@ -474,7 +466,6 @@
 		goto free_return;
 	}
 
-	cache_tree_invalidate_path(active_cache_tree, path);
 	remove_file_from_cache(path);
 	if (add_cache_entry(ce_2, ADD_CACHE_OK_TO_ADD)) {
 		error("%s: cannot add our version to the index.", path);
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 8339cf1..fe1f74c 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -8,7 +8,6 @@
 int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
 	const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
-	struct ref_lock *lock;
 	unsigned char sha1[20], oldsha1[20];
 	int i, delete, ref_flags;
 
@@ -62,10 +61,6 @@
 	if (oldval && *oldval && get_sha1(oldval, oldsha1))
 		die("%s: not a valid old SHA1", oldval);
 
-	lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, ref_flags);
-	if (!lock)
-		die("%s: cannot lock the ref", refname);
-	if (write_ref_sha1(lock, sha1, msg) < 0)
-		die("%s: cannot update the ref", refname);
-	return 0;
+	return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
+			  ref_flags, DIE_ON_ERR);
 }
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
index dfcfcd0..cc4c55d 100644
--- a/builtin-verify-tag.c
+++ b/builtin-verify-tag.c
@@ -35,7 +35,7 @@
 
 	/* find the length without signature */
 	len = 0;
-	while (len < size && prefixcmp(buf + len, PGP_SIGNATURE "\n")) {
+	while (len < size && prefixcmp(buf + len, PGP_SIGNATURE)) {
 		eol = memchr(buf + len, '\n', size - len);
 		len += eol ? eol - (buf + len) + 1 : size - len;
 	}
diff --git a/builtin.h b/builtin.h
index bb72000..03ee7bf 100644
--- a/builtin.h
+++ b/builtin.h
@@ -60,6 +60,7 @@
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_config(int argc, const char **argv, const char *prefix);
 extern int cmd_rerere(int argc, const char **argv, const char *prefix);
+extern int cmd_reset(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_revert(int argc, const char **argv, const char *prefix);
diff --git a/cache.h b/cache.h
index 70abbd5..8246500 100644
--- a/cache.h
+++ b/cache.h
@@ -264,6 +264,7 @@
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern int remove_file_from_index(struct index_state *, const char *path);
 extern int add_file_to_index(struct index_state *, const char *path, int verbose);
+extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 extern int ie_match_stat(struct index_state *, struct cache_entry *, struct stat *, int);
 extern int ie_modified(struct index_state *, struct cache_entry *, struct stat *, int);
@@ -592,7 +593,6 @@
 /* convert.c */
 extern char *convert_to_git(const char *path, const char *src, unsigned long *sizep);
 extern char *convert_to_working_tree(const char *path, const char *src, unsigned long *sizep);
-extern void *convert_sha1_file(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *size);
 
 /* diff.c */
 extern int diff_auto_refresh_index;
diff --git a/commit.c b/commit.c
index dc5a064..99f65ce 100644
--- a/commit.c
+++ b/commit.c
@@ -787,8 +787,8 @@
 	interp_set_entry(table, 6, show_date(date, tz, DATE_ISO8601));
 }
 
-static long format_commit_message(const struct commit *commit,
-		const char *msg, char **buf_p, unsigned long *space_p)
+long format_commit_message(const struct commit *commit, const void *format,
+                           char **buf_p, unsigned long *space_p)
 {
 	struct interp table[] = {
 		{ "%H" },	/* commit hash */
@@ -843,6 +843,7 @@
 	char parents[1024];
 	int i;
 	enum { HEADER, SUBJECT, BODY } state;
+	const char *msg = commit->buffer;
 
 	if (ILEFT_RIGHT + 1 != ARRAY_SIZE(table))
 		die("invalid interp table!");
@@ -924,7 +925,7 @@
 		char *buf = *buf_p;
 		unsigned long space = *space_p;
 
-		space = interpolate(buf, space, user_format,
+		space = interpolate(buf, space, format,
 				    table, ARRAY_SIZE(table));
 		if (!space)
 			break;
@@ -1165,7 +1166,7 @@
 	char *buf;
 
 	if (fmt == CMIT_FMT_USERFORMAT)
-		return format_commit_message(commit, msg, buf_p, space_p);
+		return format_commit_message(commit, user_format, buf_p, space_p);
 
 	encoding = (git_log_output_encoding
 		    ? git_log_output_encoding
diff --git a/commit.h b/commit.h
index 467872e..a8d7661 100644
--- a/commit.h
+++ b/commit.h
@@ -61,6 +61,7 @@
 };
 
 extern enum cmit_fmt get_commit_format(const char *arg);
+extern long format_commit_message(const struct commit *commit, const void *template, char **buf_p, unsigned long *space_p);
 extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char **buf_p, unsigned long *space_p, int abbrev, const char *subject, const char *after_subject, enum date_mode dmode);
 
 /** Removes the first commit from a list sorted by date, and adds all
diff --git a/compat/memmem.c b/compat/memmem.c
new file mode 100644
index 0000000..cd0d877
--- /dev/null
+++ b/compat/memmem.c
@@ -0,0 +1,29 @@
+#include "../git-compat-util.h"
+
+void *gitmemmem(const void *haystack, size_t haystack_len,
+                const void *needle, size_t needle_len)
+{
+	const char *begin = haystack;
+	const char *last_possible = begin + haystack_len - needle_len;
+
+	/*
+	 * The first occurrence of the empty string is deemed to occur at
+	 * the beginning of the string.
+	 */
+	if (needle_len == 0)
+		return (void *)begin;
+
+	/*
+	 * Sanity check, otherwise the loop might search through the whole
+	 * memory.
+	 */
+	if (haystack_len < needle_len)
+		return NULL;
+
+	for (; begin <= last_possible; begin++) {
+		if (!memcmp(begin, needle, needle_len))
+			return (void *)begin;
+	}
+
+	return NULL;
+}
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index 280557e..2d77fd4 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -97,6 +97,21 @@
   :group 'git
   :type 'string)
 
+(defcustom git-show-uptodate nil
+  "Whether to display up-to-date files."
+  :group 'git
+  :type 'boolean)
+
+(defcustom git-show-ignored nil
+  "Whether to display ignored files."
+  :group 'git
+  :type 'boolean)
+
+(defcustom git-show-unknown t
+  "Whether to display unknown files."
+  :group 'git
+  :type 'boolean)
+
 
 (defface git-status-face
   '((((class color) (background light)) (:foreground "purple"))
@@ -479,6 +494,27 @@
       (setf (git-fileinfo->orig-name info) nil)
       (setf (git-fileinfo->needs-refresh info) t))))
 
+(defun git-set-filenames-state (status files state)
+  "Set the state of a list of named files."
+  (when files
+    (setq files (sort files #'string-lessp))
+    (let ((file (pop files))
+          (node (ewoc-nth status 0)))
+      (while (and file node)
+        (let ((info (ewoc-data node)))
+          (cond ((string-lessp (git-fileinfo->name info) file)
+                 (setq node (ewoc-next status node)))
+                ((string-equal (git-fileinfo->name info) file)
+                 (unless (eq (git-fileinfo->state info) state)
+                   (setf (git-fileinfo->state info) state)
+                   (setf (git-fileinfo->rename-state info) nil)
+                   (setf (git-fileinfo->orig-name info) nil)
+                   (setf (git-fileinfo->needs-refresh info) t))
+                 (setq file (pop files)))
+                (t (setq file (pop files)))))))
+    (unless state  ;; delete files whose state has been set to nil
+      (ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
+
 (defun git-state-code (code)
   "Convert from a string to a added/deleted/modified state."
   (case (string-to-char code)
@@ -532,19 +568,36 @@
                   "  " (git-escape-file-name (git-fileinfo->name info))
                   (git-rename-as-string info))))
 
-(defun git-insert-fileinfo (status info &optional refresh)
-  "Insert INFO in the status buffer, optionally refreshing an existing one."
-  (let ((node (and refresh
-                   (git-find-status-file status (git-fileinfo->name info)))))
-    (setf (git-fileinfo->needs-refresh info) t)
-    (when node   ;preserve the marked flag
-      (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node))))
-    (if node (setf (ewoc-data node) info) (ewoc-enter-last status info))))
+(defun git-insert-info-list (status infolist)
+  "Insert a list of file infos in the status buffer, replacing existing ones if any."
+  (setq infolist (sort infolist
+                       (lambda (info1 info2)
+                         (string-lessp (git-fileinfo->name info1)
+                                       (git-fileinfo->name info2)))))
+  (let ((info (pop infolist))
+        (node (ewoc-nth status 0)))
+    (while info
+      (setf (git-fileinfo->needs-refresh info) t)
+      (cond ((not node)
+             (ewoc-enter-last status info)
+             (setq info (pop infolist)))
+            ((string-lessp (git-fileinfo->name (ewoc-data node))
+                           (git-fileinfo->name info))
+             (setq node (ewoc-next status node)))
+            ((string-equal (git-fileinfo->name (ewoc-data node))
+                           (git-fileinfo->name info))
+              ;; preserve the marked flag
+              (setf (git-fileinfo->marked info) (git-fileinfo->marked (ewoc-data node)))
+              (setf (ewoc-data node) info)
+              (setq info (pop infolist)))
+            (t
+             (ewoc-enter-before status node info)
+             (setq info (pop infolist)))))))
 
 (defun git-run-diff-index (status files)
   "Run git-diff-index on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((refresh files))
+  (let (infolist)
     (with-temp-buffer
       (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
       (goto-char (point-min))
@@ -558,13 +611,14 @@
               (new-name (match-string 8)))
           (if new-name  ; copy or rename
               (if (eq ?C (string-to-char state))
-                  (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) refresh)
-                (git-insert-fileinfo status (git-create-fileinfo 'deleted name 0 0 'rename new-name) refresh)
-                (git-insert-fileinfo status (git-create-fileinfo 'added new-name old-perm new-perm 'rename name)) refresh)
-            (git-insert-fileinfo status (git-create-fileinfo (git-state-code state) name old-perm new-perm) refresh))
+                  (push (git-create-fileinfo 'added new-name old-perm new-perm 'copy name) infolist)
+                (push (git-create-fileinfo 'deleted name 0 0 'rename new-name) infolist)
+                (push (git-create-fileinfo 'added new-name old-perm new-perm 'rename name) infolist))
+            (push (git-create-fileinfo (git-state-code state) name old-perm new-perm) infolist))
           (setq files (delete name files))
-          (when new-name (setq files (delete new-name files)))))))
-  files)
+          (when new-name (setq files (delete new-name files))))))
+    (git-insert-info-list status infolist)
+    files))
 
 (defun git-find-status-file (status file)
   "Find a given file in the status ewoc and return its node."
@@ -576,16 +630,16 @@
 (defun git-run-ls-files (status files default-state &rest options)
   "Run git-ls-files on FILES and parse the results into STATUS.
 Return the list of files that haven't been handled."
-  (let ((refresh files))
+  (let (infolist)
     (with-temp-buffer
-      (apply #'git-run-command t nil "ls-files" "-z" "-t" (append options (list "--") files))
+      (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
-      (while (re-search-forward "\\([HMRCK?]\\) \\([^\0]*\\)\0" nil t 1)
-        (let ((state (match-string 1))
-              (name (match-string 2)))
-          (git-insert-fileinfo status (git-create-fileinfo (or (git-state-code state) default-state) name) refresh)
-          (setq files (delete name files))))))
-  files)
+      (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
+        (let ((name (match-string 1)))
+          (push (git-create-fileinfo default-state name) infolist)
+          (setq files (delete name files)))))
+    (git-insert-info-list status infolist)
+    files))
 
 (defun git-run-ls-unmerged (status files)
   "Run git-ls-files -u on FILES and parse the results into STATUS."
@@ -594,9 +648,8 @@
     (goto-char (point-min))
     (let (unmerged-files)
       (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
-        (let ((node (git-find-status-file status (match-string 1))))
-          (when node (push (ewoc-data node) unmerged-files))))
-      (git-set-files-state unmerged-files 'unmerged))))
+        (push (match-string 1) unmerged-files))
+      (git-set-filenames-state status unmerged-files 'unmerged))))
 
 (defun git-get-exclude-files ()
   "Get the list of exclude files to pass to git-ls-files."
@@ -608,34 +661,30 @@
       (push config files))
     files))
 
+(defun git-run-ls-files-with-excludes (status files default-state &rest options)
+  "Run git-ls-files on FILES with appropriate --exclude-from options."
+  (let ((exclude-files (git-get-exclude-files)))
+    (apply #'git-run-ls-files status files default-state
+           (concat "--exclude-per-directory=" git-per-dir-ignore-file)
+           (append options (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
+
 (defun git-update-status-files (files &optional default-state)
   "Update the status of FILES from the index."
   (unless git-status (error "Not in git-status buffer."))
-  (let* ((status git-status)
-         (remaining-files
+  (unless files
+    (when git-show-uptodate (git-run-ls-files git-status nil 'uptodate "-c")))
+  (let* ((remaining-files
           (if (git-empty-db-p) ; we need some special handling for an empty db
-              (git-run-ls-files status files 'added "-c")
-            (git-run-diff-index status files))))
-    (git-run-ls-unmerged status files)
-    (when (or (not files) remaining-files)
-      (let ((exclude-files (git-get-exclude-files)))
-        (setq remaining-files (apply #'git-run-ls-files status remaining-files 'unknown "-o"
-                                     (concat "--exclude-per-directory=" git-per-dir-ignore-file)
-                                     (mapcar (lambda (f) (concat "--exclude-from=" f)) exclude-files)))))
-    ; mark remaining files with the default state (or remove them if nil)
-    (when remaining-files
-      (if default-state
-          (ewoc-map (lambda (info)
-                      (when (member (git-fileinfo->name info) remaining-files)
-                        (git-set-files-state (list info) default-state))
-                      nil)
-                    status)
-        (ewoc-filter status
-                     (lambda (info files)
-                       (not (member (git-fileinfo->name info) files)))
-                     remaining-files)))
+              (git-run-ls-files git-status files 'added "-c")
+            (git-run-diff-index git-status files))))
+    (git-run-ls-unmerged git-status files)
+    (when (or remaining-files (and git-show-unknown (not files)))
+      (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'unknown "-o")))
+    (when (or remaining-files (and git-show-ignored (not files)))
+      (setq remaining-files (git-run-ls-files-with-excludes git-status remaining-files 'ignored "-o" "-i")))
+    (git-set-filenames-state git-status remaining-files default-state)
     (git-refresh-files)
-    (git-refresh-ewoc-hf status)))
+    (git-refresh-ewoc-hf git-status)))
 
 (defun git-marked-files ()
   "Return a list of all marked files, or if none a list containing just the file at cursor position."
@@ -853,7 +902,7 @@
 (defun git-add-file ()
   "Add marked file(s) to the index cache."
   (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'unknown))))
+  (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
     (apply #'git-run-command nil nil "update-index" "--add" "--" files)
@@ -871,7 +920,7 @@
 (defun git-remove-file ()
   "Remove the marked file(s)."
   (interactive)
-  (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate))))
+  (let ((files (git-get-filenames (git-marked-files-state 'added 'modified 'unknown 'uptodate 'ignored))))
     (unless files
       (push (file-relative-name (read-file-name "File to remove: " nil nil t)) files))
     (if (yes-or-no-p
@@ -916,11 +965,41 @@
   (interactive)
   (ewoc-filter git-status
                (lambda (info)
-                 (not (or (eq (git-fileinfo->state info) 'ignored)
-                          (eq (git-fileinfo->state info) 'uptodate)))))
+                 (case (git-fileinfo->state info)
+                   ('ignored git-show-ignored)
+                   ('uptodate git-show-uptodate)
+                   ('unknown git-show-unknown)
+                   (t t))))
   (unless (ewoc-nth git-status 0)  ; refresh header if list is empty
     (git-refresh-ewoc-hf git-status)))
 
+(defun git-toggle-show-uptodate ()
+  "Toogle the option for showing up-to-date files."
+  (interactive)
+  (if (setq git-show-uptodate (not git-show-uptodate))
+      (git-refresh-status)
+    (git-remove-handled)))
+
+(defun git-toggle-show-ignored ()
+  "Toogle the option for showing ignored files."
+  (interactive)
+  (if (setq git-show-ignored (not git-show-ignored))
+      (progn
+        (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i")
+        (git-refresh-files)
+        (git-refresh-ewoc-hf git-status))
+    (git-remove-handled)))
+
+(defun git-toggle-show-unknown ()
+  "Toogle the option for showing unknown files."
+  (interactive)
+  (if (setq git-show-unknown (not git-show-unknown))
+      (progn
+        (git-run-ls-files-with-excludes git-status nil 'unknown "-o")
+        (git-refresh-files)
+        (git-refresh-ewoc-hf git-status))
+    (git-remove-handled)))
+
 (defun git-setup-diff-buffer (buffer)
   "Setup a buffer for displaying a diff."
   (let ((dir default-directory))
@@ -1146,7 +1225,8 @@
 
 (unless git-status-mode-map
   (let ((map (make-keymap))
-        (diff-map (make-sparse-keymap)))
+        (diff-map (make-sparse-keymap))
+        (toggle-map (make-sparse-keymap)))
     (suppress-keymap map)
     (define-key map "?"   'git-help)
     (define-key map "h"   'git-help)
@@ -1170,6 +1250,7 @@
     (define-key map "q"   'git-status-quit)
     (define-key map "r"   'git-remove-file)
     (define-key map "R"   'git-resolve-file)
+    (define-key map "t"    toggle-map)
     (define-key map "T"   'git-toggle-all-marks)
     (define-key map "u"   'git-unmark-file)
     (define-key map "U"   'git-revert-file)
@@ -1186,6 +1267,11 @@
     (define-key diff-map "h" 'git-diff-file-merge-head)
     (define-key diff-map "m" 'git-diff-file-mine)
     (define-key diff-map "o" 'git-diff-file-other)
+    ; the toggle submap
+    (define-key toggle-map "u" 'git-toggle-show-uptodate)
+    (define-key toggle-map "i" 'git-toggle-show-ignored)
+    (define-key toggle-map "k" 'git-toggle-show-unknown)
+    (define-key toggle-map "m" 'git-toggle-all-marks)
     (setq git-status-mode-map map)))
 
 ;; git mode should only run in the *git status* buffer
@@ -1207,6 +1293,9 @@
   (let ((status (ewoc-create 'git-fileinfo-prettyprint "" "")))
     (set (make-local-variable 'git-status) status))
   (set (make-local-variable 'list-buffers-directory) default-directory)
+  (make-local-variable 'git-show-uptodate)
+  (make-local-variable 'git-show-ignored)
+  (make-local-variable 'git-show-unknown)
   (run-hooks 'git-status-mode-hook)))
 
 (defun git-find-status-buffer (dir)
diff --git a/git-reset.sh b/contrib/examples/git-reset.sh
similarity index 100%
rename from git-reset.sh
rename to contrib/examples/git-reset.sh
diff --git a/contrib/fast-import/git-import.perl b/contrib/fast-import/git-import.perl
new file mode 100755
index 0000000..f9fef6d
--- /dev/null
+++ b/contrib/fast-import/git-import.perl
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+#
+# Performs an initial import of a directory. This is the equivalent
+# of doing 'git init; git add .; git commit'. It's a little slower,
+# but is meant to be a simple fast-import example.
+
+use strict;
+use File::Find;
+
+my $USAGE = 'Usage: git-import branch import-message';
+my $branch = shift or die "$USAGE\n";
+my $message = shift or die "$USAGE\n";
+
+chomp(my $username = `git config user.name`);
+chomp(my $email = `git config user.email`);
+die 'You need to set user name and email'
+  unless $username && $email;
+
+system('git init');
+open(my $fi, '|-', qw(git fast-import --date-format=now))
+  or die "unable to spawn fast-import: $!";
+
+print $fi <<EOF;
+commit refs/heads/$branch
+committer $username <$email> now
+data <<MSGEOF
+$message
+MSGEOF
+
+EOF
+
+find(
+  sub {
+    if($File::Find::name eq './.git') {
+      $File::Find::prune = 1;
+      return;
+    }
+    return unless -f $_;
+
+    my $fn = $File::Find::name;
+    $fn =~ s#^.\/##;
+
+    open(my $in, '<', $_)
+      or die "unable to open $fn: $!";
+    my @st = stat($in)
+      or die "unable to stat $fn: $!";
+    my $len = $st[7];
+
+    print $fi "M 644 inline $fn\n";
+    print $fi "data $len\n";
+    while($len > 0) {
+      my $r = read($in, my $buf, $len < 4096 ? $len : 4096);
+      defined($r) or die "read error from $fn: $!";
+      $r > 0 or die "premature EOF from $fn: $!";
+      print $fi $buf;
+      $len -= $r;
+    }
+    print $fi "\n";
+
+  }, '.'
+);
+
+close($fi);
+exit $?;
diff --git a/contrib/fast-import/git-import.sh b/contrib/fast-import/git-import.sh
new file mode 100755
index 0000000..0ca7718
--- /dev/null
+++ b/contrib/fast-import/git-import.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Performs an initial import of a directory. This is the equivalent
+# of doing 'git init; git add .; git commit'. It's a lot slower,
+# but is meant to be a simple fast-import example.
+
+if [ -z "$1" -o -z "$2" ]; then
+	echo "Usage: git-import branch import-message"
+	exit 1
+fi
+
+USERNAME="$(git config user.name)"
+EMAIL="$(git config user.email)"
+
+if [ -z "$USERNAME" -o -z "$EMAIL" ]; then
+	echo "You need to set user name and email"
+	exit 1
+fi
+
+git init
+
+(
+	cat <<EOF
+commit refs/heads/$1
+committer $USERNAME <$EMAIL> now
+data <<MSGEOF
+$2
+MSGEOF
+
+EOF
+	find * -type f|while read i;do
+		echo "M 100644 inline $i"
+		echo data $(stat -c '%s' "$i")
+		cat "$i"
+		echo
+	done
+	echo
+) | git fast-import --date-format=now
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 55778c5..adaaae6 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -281,6 +281,19 @@
 def originP4BranchesExist():
         return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
 
+def p4ChangesForPaths(depotPaths, changeRange):
+    assert depotPaths
+    output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, changeRange)
+                                                        for p in depotPaths]))
+
+    changes = []
+    for line in output:
+        changeNum = line.split(" ")[1]
+        changes.append(int(changeNum))
+
+    changes.sort()
+    return changes
+
 class Command:
     def __init__(self):
         self.usage = "usage: %prog [options]"
@@ -664,9 +677,8 @@
             f.close();
 
         os.chdir(self.clientPath)
-        response = raw_input("Do you want to sync %s with p4 sync? [y]es/[n]o " % self.clientPath)
-        if response == "y" or response == "yes":
-            system("p4 sync ...")
+        print "Syncronizing p4 checkout..."
+        system("p4 sync ...")
 
         if self.reset:
             self.firstTime = True
@@ -705,10 +717,14 @@
             else:
                 print "All changes applied!"
                 os.chdir(self.oldWorkingDirectory)
-                response = raw_input("Do you want to sync from Perforce now using git-p4 rebase? [y]es/[n]o ")
+
+                sync = P4Sync()
+                sync.run([])
+
+                response = raw_input("Do you want to rebase current HEAD from Perforce now using git-p4 rebase? [y]es/[n]o ")
                 if response == "y" or response == "yes":
                     rebase = P4Rebase()
-                    rebase.run([])
+                    rebase.rebase()
             os.remove(self.configFile)
 
         return True
@@ -1102,6 +1118,186 @@
         self.keepRepoPath = (d.has_key('options')
                              and ('keepRepoPath' in d['options']))
 
+    def gitRefForBranch(self, branch):
+        if branch == "main":
+            return self.refPrefix + "master"
+
+        if len(branch) <= 0:
+            return branch
+
+        return self.refPrefix + self.projectName + branch
+
+    def gitCommitByP4Change(self, ref, change):
+        if self.verbose:
+            print "looking in ref " + ref + " for change %s using bisect..." % change
+
+        earliestCommit = ""
+        latestCommit = parseRevision(ref)
+
+        while True:
+            if self.verbose:
+                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+            next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
+            if len(next) == 0:
+                if self.verbose:
+                    print "argh"
+                return ""
+            log = extractLogMessageFromGitCommit(next)
+            settings = extractSettingsGitLog(log)
+            currentChange = int(settings['change'])
+            if self.verbose:
+                print "current change %s" % currentChange
+
+            if currentChange == change:
+                if self.verbose:
+                    print "found %s" % next
+                return next
+
+            if currentChange < change:
+                earliestCommit = "^%s" % next
+            else:
+                latestCommit = "%s" % next
+
+        return ""
+
+    def importNewBranch(self, branch, maxChange):
+        # make fast-import flush all changes to disk and update the refs using the checkpoint
+        # command so that we can try to find the branch parent in the git history
+        self.gitStream.write("checkpoint\n\n");
+        self.gitStream.flush();
+        branchPrefix = self.depotPaths[0] + branch + "/"
+        range = "@1,%s" % maxChange
+        #print "prefix" + branchPrefix
+        changes = p4ChangesForPaths([branchPrefix], range)
+        if len(changes) <= 0:
+            return False
+        firstChange = changes[0]
+        #print "first change in branch: %s" % firstChange
+        sourceBranch = self.knownBranches[branch]
+        sourceDepotPath = self.depotPaths[0] + sourceBranch
+        sourceRef = self.gitRefForBranch(sourceBranch)
+        #print "source " + sourceBranch
+
+        branchParentChange = int(p4Cmd("changes -m 1 %s...@1,%s" % (sourceDepotPath, firstChange))["change"])
+        #print "branch parent: %s" % branchParentChange
+        gitParent = self.gitCommitByP4Change(sourceRef, branchParentChange)
+        if len(gitParent) > 0:
+            self.initialParents[self.gitRefForBranch(branch)] = gitParent
+            #print "parent git commit: %s" % gitParent
+
+        self.importChanges(changes)
+        return True
+
+    def importChanges(self, changes):
+        cnt = 1
+        for change in changes:
+            description = p4Cmd("describe %s" % change)
+            self.updateOptionDict(description)
+
+            if not self.silent:
+                sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
+                sys.stdout.flush()
+            cnt = cnt + 1
+
+            try:
+                if self.detectBranches:
+                    branches = self.splitFilesIntoBranches(description)
+                    for branch in branches.keys():
+                        ## HACK  --hwn
+                        branchPrefix = self.depotPaths[0] + branch + "/"
+
+                        parent = ""
+
+                        filesForCommit = branches[branch]
+
+                        if self.verbose:
+                            print "branch is %s" % branch
+
+                        self.updatedBranches.add(branch)
+
+                        if branch not in self.createdBranches:
+                            self.createdBranches.add(branch)
+                            parent = self.knownBranches[branch]
+                            if parent == branch:
+                                parent = ""
+                            else:
+                                fullBranch = self.projectName + branch
+                                if fullBranch not in self.p4BranchesInGit:
+                                    if not self.silent:
+                                        print("\n    Importing new branch %s" % fullBranch);
+                                    if self.importNewBranch(branch, change - 1):
+                                        parent = ""
+                                        self.p4BranchesInGit.append(fullBranch)
+                                    if not self.silent:
+                                        print("\n    Resuming with change %s" % change);
+
+                                if self.verbose:
+                                    print "parent determined through known branches: %s" % parent
+
+                        branch = self.gitRefForBranch(branch)
+                        parent = self.gitRefForBranch(parent)
+
+                        if self.verbose:
+                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+
+                        if len(parent) == 0 and branch in self.initialParents:
+                            parent = self.initialParents[branch]
+                            del self.initialParents[branch]
+
+                        self.commit(description, filesForCommit, branch, [branchPrefix], parent)
+                else:
+                    files = self.extractFilesFromCommit(description)
+                    self.commit(description, files, self.branch, self.depotPaths,
+                                self.initialParent)
+                    self.initialParent = ""
+            except IOError:
+                print self.gitError.read()
+                sys.exit(1)
+
+    def importHeadRevision(self, revision):
+        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+
+        details = { "user" : "git perforce import user", "time" : int(time.time()) }
+        details["desc"] = ("Initial import of %s from the state at revision %s"
+                           % (' '.join(self.depotPaths), revision))
+        details["change"] = revision
+        newestRevision = 0
+
+        fileCnt = 0
+        for info in p4CmdList("files "
+                              +  ' '.join(["%s...%s"
+                                           % (p, revision)
+                                           for p in self.depotPaths])):
+
+            if info['code'] == 'error':
+                sys.stderr.write("p4 returned an error: %s\n"
+                                 % info['data'])
+                sys.exit(1)
+
+
+            change = int(info["change"])
+            if change > newestRevision:
+                newestRevision = change
+
+            if info["action"] == "delete":
+                # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
+                #fileCnt = fileCnt + 1
+                continue
+
+            for prop in ["depotFile", "rev", "action", "type" ]:
+                details["%s%s" % (prop, fileCnt)] = info[prop]
+
+            fileCnt = fileCnt + 1
+
+        details["change"] = newestRevision
+        self.updateOptionDict(details)
+        try:
+            self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
+        except IOError:
+            print "IO error with git fast-import. Is your git version recent enough?"
+            print self.gitError.read()
+
+
     def run(self, args):
         self.depotPaths = []
         self.changeRange = ""
@@ -1199,7 +1395,7 @@
 
             self.depotPaths = sorted(args)
 
-        self.revision = ""
+        revision = ""
         self.users = {}
 
         newPaths = []
@@ -1210,15 +1406,15 @@
                 if self.changeRange == "@all":
                     self.changeRange = ""
                 elif ',' not in self.changeRange:
-                    self.revision = self.changeRange
+                    revision = self.changeRange
                     self.changeRange = ""
                 p = p[:atIdx]
             elif p.find("#") != -1:
                 hashIdx = p.index("#")
-                self.revision = p[hashIdx:]
+                revision = p[hashIdx:]
                 p = p[:hashIdx]
             elif self.previousDepotPaths == []:
-                self.revision = "#head"
+                revision = "#head"
 
             p = re.sub ("\.\.\.$", "", p)
             if not p.endswith("/"):
@@ -1259,49 +1455,8 @@
         self.gitStream = importProcess.stdin
         self.gitError = importProcess.stderr
 
-        if self.revision:
-            print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), self.revision, self.branch)
-
-            details = { "user" : "git perforce import user", "time" : int(time.time()) }
-            details["desc"] = ("Initial import of %s from the state at revision %s"
-                               % (' '.join(self.depotPaths), self.revision))
-            details["change"] = self.revision
-            newestRevision = 0
-
-            fileCnt = 0
-            for info in p4CmdList("files "
-                                  +  ' '.join(["%s...%s"
-                                               % (p, self.revision)
-                                               for p in self.depotPaths])):
-
-                if info['code'] == 'error':
-                    sys.stderr.write("p4 returned an error: %s\n"
-                                     % info['data'])
-                    sys.exit(1)
-
-
-                change = int(info["change"])
-                if change > newestRevision:
-                    newestRevision = change
-
-                if info["action"] == "delete":
-                    # don't increase the file cnt, otherwise details["depotFile123"] will have gaps!
-                    #fileCnt = fileCnt + 1
-                    continue
-
-                for prop in ["depotFile", "rev", "action", "type" ]:
-                    details["%s%s" % (prop, fileCnt)] = info[prop]
-
-                fileCnt = fileCnt + 1
-
-            details["change"] = newestRevision
-            self.updateOptionDict(details)
-            try:
-                self.commit(details, self.extractFilesFromCommit(details), self.branch, self.depotPaths)
-            except IOError:
-                print "IO error with git fast-import. Is your git version recent enough?"
-                print self.gitError.read()
-
+        if revision:
+            self.importHeadRevision(revision)
         else:
             changes = []
 
@@ -1319,15 +1474,7 @@
                 if self.verbose:
                     print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
                                                               self.changeRange)
-                assert self.depotPaths
-                output = read_pipe_lines("p4 changes " + ' '.join (["%s...%s" % (p, self.changeRange)
-                                                                    for p in self.depotPaths]))
-
-                for line in output:
-                    changeNum = line.split(" ")[1]
-                    changes.append(int(changeNum))
-
-                changes.sort()
+                changes = p4ChangesForPaths(self.depotPaths, self.changeRange)
 
                 if len(self.maxChanges) > 0:
                     changes = changes[:min(int(self.maxChanges), len(changes))]
@@ -1342,74 +1489,7 @@
 
             self.updatedBranches = set()
 
-            cnt = 1
-            for change in changes:
-                description = p4Cmd("describe %s" % change)
-                self.updateOptionDict(description)
-
-                if not self.silent:
-                    sys.stdout.write("\rImporting revision %s (%s%%)" % (change, cnt * 100 / len(changes)))
-                    sys.stdout.flush()
-                cnt = cnt + 1
-
-                try:
-                    if self.detectBranches:
-                        branches = self.splitFilesIntoBranches(description)
-                        for branch in branches.keys():
-                            ## HACK  --hwn
-                            branchPrefix = self.depotPaths[0] + branch + "/"
-
-                            parent = ""
-
-                            filesForCommit = branches[branch]
-
-                            if self.verbose:
-                                print "branch is %s" % branch
-
-                            self.updatedBranches.add(branch)
-
-                            if branch not in self.createdBranches:
-                                self.createdBranches.add(branch)
-                                parent = self.knownBranches[branch]
-                                if parent == branch:
-                                    parent = ""
-                                elif self.verbose:
-                                    print "parent determined through known branches: %s" % parent
-
-                            # main branch? use master
-                            if branch == "main":
-                                branch = "master"
-                            else:
-
-                                ## FIXME
-                                branch = self.projectName + branch
-
-                            if parent == "main":
-                                parent = "master"
-                            elif len(parent) > 0:
-                                ## FIXME
-                                parent = self.projectName + parent
-
-                            branch = self.refPrefix + branch
-                            if len(parent) > 0:
-                                parent = self.refPrefix + parent
-
-                            if self.verbose:
-                                print "looking for initial parent for %s; current parent is %s" % (branch, parent)
-
-                            if len(parent) == 0 and branch in self.initialParents:
-                                parent = self.initialParents[branch]
-                                del self.initialParents[branch]
-
-                            self.commit(description, filesForCommit, branch, [branchPrefix], parent)
-                    else:
-                        files = self.extractFilesFromCommit(description)
-                        self.commit(description, files, self.branch, self.depotPaths,
-                                    self.initialParent)
-                        self.initialParent = ""
-                except IOError:
-                    print self.gitError.read()
-                    sys.exit(1)
+            self.importChanges(changes)
 
             if not self.silent:
                 print ""
@@ -1419,7 +1499,6 @@
                         sys.stdout.write("%s " % b)
                     sys.stdout.write("\n")
 
-
         self.gitStream.close()
         if importProcess.wait() != 0:
             die("fast-import failed: %s" % self.gitError.read())
@@ -1440,6 +1519,9 @@
         sync = P4Sync()
         sync.run([])
 
+        return self.rebase()
+
+    def rebase(self):
         [upstream, settings] = findUpstreamBranchPoint()
         if len(upstream) == 0:
             die("Cannot find upstream branchpoint for rebase")
diff --git a/convert.c b/convert.c
index 21908b1..d77c8eb 100644
--- a/convert.c
+++ b/convert.c
@@ -687,18 +687,3 @@
 
 	return buf;
 }
-
-void *convert_sha1_file(const char *path, const unsigned char *sha1,
-                        unsigned int mode, enum object_type *type,
-                        unsigned long *size)
-{
-	void *buffer = read_sha1_file(sha1, type, size);
-	if (S_ISREG(mode) && buffer) {
-		void *converted = convert_to_working_tree(path, buffer, size);
-		if (converted) {
-			free(buffer);
-			buffer = converted;
-		}
-	}
-	return buffer;
-}
diff --git a/diff-delta.c b/diff-delta.c
index 0dde2f2..9e440a9 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -115,7 +115,11 @@
 struct index_entry {
 	const unsigned char *ptr;
 	unsigned int val;
-	struct index_entry *next;
+};
+
+struct unpacked_index_entry {
+	struct index_entry entry;
+	struct unpacked_index_entry *next;
 };
 
 struct delta_index {
@@ -131,7 +135,8 @@
 	unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
 	const unsigned char *data, *buffer = buf;
 	struct delta_index *index;
-	struct index_entry *entry, **hash;
+	struct unpacked_index_entry *entry, **hash;
+	struct index_entry *packed_entry, **packed_hash;
 	void *mem;
 	unsigned long memsize;
 
@@ -148,28 +153,21 @@
 	hmask = hsize - 1;
 
 	/* allocate lookup index */
-	memsize = sizeof(*index) +
-		  sizeof(*hash) * hsize +
+	memsize = sizeof(*hash) * hsize +
 		  sizeof(*entry) * entries;
 	mem = malloc(memsize);
 	if (!mem)
 		return NULL;
-	index = mem;
-	mem = index + 1;
 	hash = mem;
 	mem = hash + hsize;
 	entry = mem;
 
-	index->memsize = memsize;
-	index->src_buf = buf;
-	index->src_size = bufsize;
-	index->hash_mask = hmask;
 	memset(hash, 0, hsize * sizeof(*hash));
 
 	/* allocate an array to count hash entries */
 	hash_count = calloc(hsize, sizeof(*hash_count));
 	if (!hash_count) {
-		free(index);
+		free(hash);
 		return NULL;
 	}
 
@@ -183,12 +181,13 @@
 			val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
 		if (val == prev_val) {
 			/* keep the lowest of consecutive identical blocks */
-			entry[-1].ptr = data + RABIN_WINDOW;
+			entry[-1].entry.ptr = data + RABIN_WINDOW;
+			--entries;
 		} else {
 			prev_val = val;
 			i = val & hmask;
-			entry->ptr = data + RABIN_WINDOW;
-			entry->val = val;
+			entry->entry.ptr = data + RABIN_WINDOW;
+			entry->entry.val = val;
 			entry->next = hash[i];
 			hash[i] = entry++;
 			hash_count[i]++;
@@ -208,20 +207,84 @@
 	 * the reference buffer.
 	 */
 	for (i = 0; i < hsize; i++) {
-		if (hash_count[i] < HASH_LIMIT)
+		int acc;
+
+		if (hash_count[i] <= HASH_LIMIT)
 			continue;
+
+		entries -= hash_count[i] - HASH_LIMIT;
+		/* We leave exactly HASH_LIMIT entries in the bucket */
+
 		entry = hash[i];
+		acc = 0;
 		do {
-			struct index_entry *keep = entry;
-			int skip = hash_count[i] / HASH_LIMIT;
-			do {
-				entry = entry->next;
-			} while(--skip && entry);
-			keep->next = entry;
-		} while(entry);
+			acc += hash_count[i] - HASH_LIMIT;
+			if (acc > 0) {
+				struct unpacked_index_entry *keep = entry;
+				do {
+					entry = entry->next;
+					acc -= HASH_LIMIT;
+				} while (acc > 0);
+				keep->next = entry->next;
+			}
+			entry = entry->next;
+		} while (entry);
+
+		/* Assume that this loop is gone through exactly
+		 * HASH_LIMIT times and is entered and left with
+		 * acc==0.  So the first statement in the loop
+		 * contributes (hash_count[i]-HASH_LIMIT)*HASH_LIMIT
+		 * to the accumulator, and the inner loop consequently
+		 * is run (hash_count[i]-HASH_LIMIT) times, removing
+		 * one element from the list each time.  Since acc
+		 * balances out to 0 at the final run, the inner loop
+		 * body can't be left with entry==NULL.  So we indeed
+		 * encounter entry==NULL in the outer loop only.
+		 */
 	}
 	free(hash_count);
 
+	/* Now create the packed index in array form rather than
+	 * linked lists */
+
+	memsize = sizeof(*index)
+		+ sizeof(*packed_hash) * (hsize+1)
+		+ sizeof(*packed_entry) * entries;
+
+	mem = malloc(memsize);
+
+	if (!mem) {
+		free(hash);
+		return NULL;
+	}
+
+	index = mem;
+	index->memsize = memsize;
+	index->src_buf = buf;
+	index->src_size = bufsize;
+	index->hash_mask = hmask;
+
+	mem = index + 1;
+	packed_hash = mem;
+	mem = packed_hash + (hsize+1);
+	packed_entry = mem;
+
+	/* Coalesce all entries belonging to one linked list into
+	 * consecutive array entries */
+
+	for (i = 0; i < hsize; i++) {
+		packed_hash[i] = packed_entry;
+		for (entry = hash[i]; entry; entry = entry->next)
+			*packed_entry++ = entry->entry;
+	}
+
+	/* Sentinel value to indicate the length of the last hash
+	 * bucket */
+
+	packed_hash[hsize] = packed_entry;
+	assert(packed_entry - (struct index_entry *)mem == entries);
+	free(hash);
+
 	return index;
 }
 
@@ -302,7 +365,7 @@
 			val ^= U[data[-RABIN_WINDOW]];
 			val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
 			i = val & index->hash_mask;
-			for (entry = index->hash[i]; entry; entry = entry->next) {
+			for (entry = index->hash[i]; entry < index->hash[i+1]; entry++) {
 				const unsigned char *ref = entry->ptr;
 				const unsigned char *src = data;
 				unsigned int ref_size = ref_top - ref;
diff --git a/git-commit.sh b/git-commit.sh
index bb113e8..3e46dbb 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -98,101 +98,71 @@
 		no_edit=t
 		log_given=t$log_given
 		logfile="$1"
-		shift
 		;;
 	-F*|-f*)
 		no_edit=t
 		log_given=t$log_given
-		logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
-		shift
+		logfile="${1#-[Ff]}"
 		;;
 	--F=*|--f=*|--fi=*|--fil=*|--file=*)
 		no_edit=t
 		log_given=t$log_given
-		logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		shift
+		logfile="${1#*=}"
 		;;
 	-a|--a|--al|--all)
 		all=t
-		shift
 		;;
 	--au=*|--aut=*|--auth=*|--autho=*|--author=*)
-		force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		shift
+		force_author="${1#*=}"
 		;;
 	--au|--aut|--auth|--autho|--author)
 		case "$#" in 1) usage ;; esac
 		shift
 		force_author="$1"
-		shift
 		;;
 	-e|--e|--ed|--edi|--edit)
 		edit_flag=t
-		shift
 		;;
 	-i|--i|--in|--inc|--incl|--inclu|--includ|--include)
 		also=t
-		shift
 		;;
 	--int|--inte|--inter|--intera|--interac|--interact|--interacti|\
 	--interactiv|--interactive)
 		interactive=t
-		shift
 		;;
 	-o|--o|--on|--onl|--only)
 		only=t
-		shift
 		;;
 	-m|--m|--me|--mes|--mess|--messa|--messag|--message)
 		case "$#" in 1) usage ;; esac
 		shift
 		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message="$1"
-		else
-		    log_message="$log_message
+		log_message="${log_message:+${log_message}
 
-$1"
-		fi
+}$1"
 		no_edit=t
-		shift
 		;;
 	-m*)
 		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message=`expr "z$1" : 'z-m\(.*\)'`
-		else
-		    log_message="$log_message
+		log_message="${log_message:+${log_message}
 
-`expr "z$1" : 'z-m\(.*\)'`"
-		fi
+}${1#-m}"
 		no_edit=t
-		shift
 		;;
 	--m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
 		log_given=m$log_given
-		if test "$log_message" = ''
-		then
-		    log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
-		else
-		    log_message="$log_message
+		log_message="${log_message:+${log_message}
 
-`expr "z$1" : 'zq-[^=]*=\(.*\)'`"
-		fi
+}${1#*=}"
 		no_edit=t
-		shift
 		;;
 	-n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
 	--no-verify)
 		verify=
-		shift
 		;;
 	--a|--am|--ame|--amen|--amend)
 		amend=t
 		use_commit=HEAD
-		shift
 		;;
 	-c)
 		case "$#" in 1) usage ;; esac
@@ -200,15 +170,13 @@
 		log_given=t$log_given
 		use_commit="$1"
 		no_edit=
-		shift
 		;;
 	--ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
 	--reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
 	--reedit-messag=*|--reedit-message=*)
 		log_given=t$log_given
-		use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		use_commit="${1#*=}"
 		no_edit=
-		shift
 		;;
 	--ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
 	--reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
@@ -218,7 +186,6 @@
 		log_given=t$log_given
 		use_commit="$1"
 		no_edit=
-		shift
 		;;
 	-C)
 		case "$#" in 1) usage ;; esac
@@ -226,15 +193,13 @@
 		log_given=t$log_given
 		use_commit="$1"
 		no_edit=t
-		shift
 		;;
 	--reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
 	--reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
 	--reuse-message=*)
 		log_given=t$log_given
-		use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		use_commit="${1#*=}"
 		no_edit=t
-		shift
 		;;
 	--reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
 	--reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
@@ -243,32 +208,26 @@
 		log_given=t$log_given
 		use_commit="$1"
 		no_edit=t
-		shift
 		;;
 	-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
 		signoff=t
-		shift
 		;;
 	-t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
 		case "$#" in 1) usage ;; esac
 		shift
 		templatefile="$1"
 		no_edit=
-		shift
 		;;
 	-q|--q|--qu|--qui|--quie|--quiet)
 		quiet=t
-		shift
 		;;
 	-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 		verbose=t
-		shift
 		;;
 	-u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
 	--untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
 	--untracked-file|--untracked-files)
 		untracked_files=t
-		shift
 		;;
 	--)
 		shift
@@ -281,6 +240,7 @@
 		break
 		;;
 	esac
+	shift
 done
 case "$edit_flag" in t) no_edit= ;; esac
 
@@ -441,12 +401,8 @@
 
 if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
 then
-	if test "$TMP_INDEX"
-	then
-		GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
-	else
-		GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit
-	fi || exit
+    GIT_INDEX_FILE="${TMP_INDEX:-${USE_INDEX}}" "$GIT_DIR"/hooks/pre-commit \
+    || exit
 fi
 
 if test "$log_message" != ''
diff --git a/git-compat-util.h b/git-compat-util.h
index ca0a597..1bfbdeb 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -172,6 +172,12 @@
 extern const char *githstrerror(int herror);
 #endif
 
+#ifdef NO_MEMMEM
+#define memmem gitmemmem
+void *gitmemmem(const void *haystack, size_t haystacklen,
+                const void *needle, size_t needlelen);
+#endif
+
 extern void release_pack_memory(size_t, int);
 
 static inline char* xstrdup(const char *str)
diff --git a/git-rebase.sh b/git-rebase.sh
index 3bd66b0..c9942f2 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -216,9 +216,11 @@
 	-v|--verbose)
 		verbose=t
 		;;
+	--whitespace=*)
+		git_am_opt="$git_am_opt $1"
+		;;
 	-C*)
-		git_am_opt=$1
-		shift
+		git_am_opt="$git_am_opt $1"
 		;;
 	-*)
 		usage
diff --git a/git-remote.perl b/git-remote.perl
index 01cf480..f6f283e 100755
--- a/git-remote.perl
+++ b/git-remote.perl
@@ -278,7 +278,9 @@
 
 	for (@$track) {
 		$git->command('config', '--add', "remote.$name.fetch",
-			      "+refs/heads/$_:refs/remotes/$name/$_");
+				$opts->{'mirror'} ?
+				"+refs/$_:refs/$_" :
+				"+refs/heads/$_:refs/remotes/$name/$_");
 	}
 	if ($opts->{'fetch'}) {
 		$git->command('fetch', $name);
@@ -409,6 +411,10 @@
 			shift @ARGV;
 			next;
 		}
+		if ($opt eq '--mirror') {
+			$opts{'mirror'} = 1;
+			next;
+		}
 		add_usage();
 	}
 	if (@ARGV != 3) {
diff --git a/git-send-email.perl b/git-send-email.perl
index 9547cc3..4031e86 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -73,9 +73,18 @@
    --signed-off-cc Automatically add email addresses that appear in
                  Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
 
+   --identity     The configuration identity, a subsection to prioritise over
+                  the default section.
+
    --smtp-server  If set, specifies the outgoing SMTP server to use.
                   Defaults to localhost.
 
+   --smtp-user    The username for SMTP-AUTH.
+
+   --smtp-pass    The password for SMTP-AUTH.
+
+   --smtp-ssl     If set, connects to the SMTP server using SSL.
+
    --suppress-from Suppress sending emails to yourself if your address
                   appears in a From: line. Defaults to off.
 
@@ -145,7 +154,6 @@
 my (@to,@cc,@initial_cc,@bcclist,@xh,
 	$initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
 
-my $smtp_server;
 my $envelope_sender;
 
 # Example reply to:
@@ -164,24 +172,26 @@
 
 # Variables with corresponding config settings
 my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
+my ($smtp_server, $smtp_authuser, $smtp_authpass, $smtp_ssl);
+my ($identity, $aliasfiletype, @alias_files);
 
-my %config_settings = (
+my %config_bool_settings = (
     "thread" => [\$thread, 1],
     "chainreplyto" => [\$chain_reply_to, 1],
     "suppressfrom" => [\$suppress_from, 0],
     "signedoffcc" => [\$signed_off_cc, 1],
-    "cccmd" => [\$cc_cmd, ""],
+    "smtpssl" => [\$smtp_ssl, 0],
 );
 
-foreach my $setting (keys %config_settings) {
-    my $config = $repo->config_bool("sendemail.$setting");
-    ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1];
-}
-
-@bcclist = $repo->config('sendemail.bcc');
-if (!@bcclist or !$bcclist[0]) {
-    @bcclist = ();
-}
+my %config_settings = (
+    "smtpserver" => \$smtp_server,
+    "smtpuser" => \$smtp_authuser,
+    "smtppass" => \$smtp_authpass,
+    "cccmd" => \$cc_cmd,
+    "aliasfiletype" => \$aliasfiletype,
+    "bcc" => \@bcclist,
+    "aliasesfile" => \@alias_files,
+);
 
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
@@ -194,6 +204,10 @@
 		    "bcc=s" => \@bcclist,
 		    "chain-reply-to!" => \$chain_reply_to,
 		    "smtp-server=s" => \$smtp_server,
+		    "smtp-user=s" => \$smtp_authuser,
+		    "smtp-pass=s" => \$smtp_authpass,
+		    "smtp-ssl!" => \$smtp_ssl,
+		    "identity=s" => \$identity,
 		    "compose" => \$compose,
 		    "quiet" => \$quiet,
 		    "cc-cmd=s" => \$cc_cmd,
@@ -208,6 +222,43 @@
     usage();
 }
 
+# Now, let's fill any that aren't set in with defaults:
+
+sub read_config {
+	my ($prefix) = @_;
+
+	foreach my $setting (keys %config_bool_settings) {
+		my $target = $config_bool_settings{$setting}->[0];
+		$$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
+	}
+
+	foreach my $setting (keys %config_settings) {
+		my $target = $config_settings{$setting};
+		if (ref($target) eq "ARRAY") {
+			unless (@$target) {
+				my @values = $repo->config("$prefix.$setting");
+				@$target = @values if (@values && defined $values[0]);
+			}
+		}
+		else {
+			$$target = $repo->config("$prefix.$setting") unless (defined $$target);
+		}
+	}
+}
+
+# read configuration from [sendemail "$identity"], fall back on [sendemail]
+$identity = $repo->config("sendemail.identity") unless (defined $identity);
+read_config("sendemail.$identity") if (defined $identity);
+read_config("sendemail");
+
+# fall back on builtin bool defaults
+foreach my $setting (values %config_bool_settings) {
+	${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
+}
+
+my ($repoauthor) = $repo->ident_person('author');
+my ($repocommitter) = $repo->ident_person('committer');
+
 # Verify the user input
 
 foreach my $entry (@to) {
@@ -222,14 +273,7 @@
 	die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
 }
 
-# Now, let's fill any that aren't set in with defaults:
-
-my ($repoauthor) = $repo->ident_person('author');
-my ($repocommitter) = $repo->ident_person('committer');
-
 my %aliases;
-my @alias_files = $repo->config('sendemail.aliasesfile');
-my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
 my %parse_alias = (
 	# multiline formats can be supported in the future
 	mutt => sub { my $fh = shift; while (<$fh>) {
@@ -321,10 +365,7 @@
 	$initial_reply_to =~ s/>?\s+$/>/;
 }
 
-if (!$smtp_server) {
-	$smtp_server = $repo->config('sendemail.smtpserver');
-}
-if (!$smtp_server) {
+if (!defined $smtp_server) {
 	foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
 		if (-x $_) {
 			$smtp_server = $_;
@@ -561,8 +602,16 @@
 		print $sm "$header\n$message";
 		close $sm or die $?;
 	} else {
-		require Net::SMTP;
-		$smtp ||= Net::SMTP->new( $smtp_server );
+		if ($smtp_ssl) {
+			require Net::SMTP::SSL;
+			$smtp ||= Net::SMTP::SSL->new( $smtp_server, Port => 465 );
+		}
+		else {
+			require Net::SMTP;
+			$smtp ||= Net::SMTP->new( $smtp_server );
+		}
+		$smtp->auth( $smtp_authuser, $smtp_authpass )
+			or die $smtp->message if (defined $smtp_authuser);
 		$smtp->mail( $raw_from ) or die $smtp->message;
 		$smtp->to( @recipients ) or die $smtp->message;
 		$smtp->data or die $smtp->message;
@@ -669,7 +718,7 @@
 	}
 	close F;
 
-	if ($cc_cmd ne "") {
+	if (defined $cc_cmd) {
 		open(F, "$cc_cmd $t |")
 			or die "(cc-cmd) Could not execute '$cc_cmd'";
 		while(<F>) {
diff --git a/git-svn.perl b/git-svn.perl
index f818160..288d32c 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -124,7 +124,8 @@
 	                "Set an SVN repository to a git tree-ish",
 			{ 'stdin|' => \$_stdin, %cmt_opts, %fc_opts, } ],
 	'show-ignore' => [ \&cmd_show_ignore, "Show svn:ignore listings",
-			{ 'revision|r=i' => \$_revision } ],
+			{ 'revision|r=i' => \$_revision
+			} ],
 	'multi-fetch' => [ \&cmd_multi_fetch,
 	                   "Deprecated alias for $0 fetch --all",
 			   { 'revision|r=s' => \$_revision, %fc_opts } ],
@@ -144,10 +145,10 @@
 			  'non-recursive' => \$Git::SVN::Log::non_recursive,
 			  'authors-file|A=s' => \$_authors,
 			  'color' => \$Git::SVN::Log::color,
-			  'pager=s' => \$Git::SVN::Log::pager,
+			  'pager=s' => \$Git::SVN::Log::pager
 			} ],
 	'find-rev' => [ \&cmd_find_rev, "Translate between SVN revision numbers and tree-ish",
-			{ } ],
+			{} ],
 	'rebase' => [ \&cmd_rebase, "Fetch and rebase your working directory",
 			{ 'merge|m|M' => \$_merge,
 			  'verbose|v' => \$_verbose,
@@ -811,7 +812,8 @@
 
 sub working_head_info {
 	my ($head, $refs) = @_;
-	my ($fh, $ctx) = command_output_pipe('log', '--no-color', $head);
+	my @args = ('log', '--no-color', '--first-parent');
+	my ($fh, $ctx) = command_output_pipe(@args, $head);
 	my $hash;
 	my %max;
 	while (<$fh>) {
diff --git a/git.c b/git.c
index fd3d83c..56ae8cc 100644
--- a/git.c
+++ b/git.c
@@ -364,6 +364,7 @@
 		{ "reflog", cmd_reflog, RUN_SETUP },
 		{ "repo-config", cmd_config },
 		{ "rerere", cmd_rerere, RUN_SETUP },
+		{ "reset", cmd_reset, RUN_SETUP },
 		{ "rev-list", cmd_rev_list, RUN_SETUP },
 		{ "rev-parse", cmd_rev_parse, RUN_SETUP },
 		{ "revert", cmd_revert, RUN_SETUP | NEED_WORK_TREE },
diff --git a/merge-recursive.c b/merge-recursive.c
index 16f6a0f..19d5f3b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -171,30 +171,6 @@
 	}
 }
 
-static struct cache_entry *make_cache_entry(unsigned int mode,
-		const unsigned char *sha1, const char *path, int stage, int refresh)
-{
-	int size, len;
-	struct cache_entry *ce;
-
-	if (!verify_path(path))
-		return NULL;
-
-	len = strlen(path);
-	size = cache_entry_size(len);
-	ce = xcalloc(1, size);
-
-	hashcpy(ce->sha1, sha1);
-	memcpy(ce->name, path, len);
-	ce->ce_flags = create_ce_flags(len, stage);
-	ce->ce_mode = create_ce_mode(mode);
-
-	if (refresh)
-		return refresh_cache_entry(ce, 0);
-
-	return ce;
-}
-
 static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
 		const char *path, int stage, int refresh, int options)
 {
diff --git a/read-cache.c b/read-cache.c
index 8b1c94e..2e40a34 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -346,6 +346,7 @@
 	int pos = index_name_pos(istate, path, strlen(path));
 	if (pos < 0)
 		pos = -pos-1;
+	cache_tree_invalidate_path(istate->cache_tree, path);
 	while (pos < istate->cache_nr && !strcmp(istate->cache[pos]->name, path))
 		remove_index_entry_at(istate, pos);
 	return 0;
@@ -430,10 +431,34 @@
 		die("unable to add %s to index",path);
 	if (verbose)
 		printf("add '%s'\n", path);
-	cache_tree_invalidate_path(istate->cache_tree, path);
 	return 0;
 }
 
+struct cache_entry *make_cache_entry(unsigned int mode,
+		const unsigned char *sha1, const char *path, int stage,
+		int refresh)
+{
+	int size, len;
+	struct cache_entry *ce;
+
+	if (!verify_path(path))
+		return NULL;
+
+	len = strlen(path);
+	size = cache_entry_size(len);
+	ce = xcalloc(1, size);
+
+	hashcpy(ce->sha1, sha1);
+	memcpy(ce->name, path, len);
+	ce->ce_flags = create_ce_flags(len, stage);
+	ce->ce_mode = create_ce_mode(mode);
+
+	if (refresh)
+		return refresh_cache_entry(ce, 0);
+
+	return ce;
+}
+
 int ce_same_name(struct cache_entry *a, struct cache_entry *b)
 {
 	int len = ce_namelen(a);
@@ -673,6 +698,7 @@
 	int ok_to_replace = option & ADD_CACHE_OK_TO_REPLACE;
 	int skip_df_check = option & ADD_CACHE_SKIP_DFCHECK;
 
+	cache_tree_invalidate_path(istate->cache_tree, ce->name);
 	pos = index_name_pos(istate, ce->name, ntohs(ce->ce_flags));
 
 	/* existing match? Just replace it. */
diff --git a/refs.c b/refs.c
index 09a2c87..7fb3350 100644
--- a/refs.c
+++ b/refs.c
@@ -1455,3 +1455,30 @@
 {
 	return do_for_each_reflog("", fn, cb_data);
 }
+
+int update_ref(const char *action, const char *refname,
+		const unsigned char *sha1, const unsigned char *oldval,
+		int flags, enum action_on_err onerr)
+{
+	static struct ref_lock *lock;
+	lock = lock_any_ref_for_update(refname, oldval, flags);
+	if (!lock) {
+		const char *str = "Cannot lock the ref '%s'.";
+		switch (onerr) {
+		case MSG_ON_ERR: error(str, refname); break;
+		case DIE_ON_ERR: die(str, refname); break;
+		case QUIET_ON_ERR: break;
+		}
+		return 1;
+	}
+	if (write_ref_sha1(lock, sha1, action) < 0) {
+		const char *str = "Cannot update the ref '%s'.";
+		switch (onerr) {
+		case MSG_ON_ERR: error(str, refname); break;
+		case DIE_ON_ERR: die(str, refname); break;
+		case QUIET_ON_ERR: break;
+		}
+		return 1;
+	}
+	return 0;
+}
diff --git a/refs.h b/refs.h
index f234eb7..6eb98a4 100644
--- a/refs.h
+++ b/refs.h
@@ -64,4 +64,10 @@
 /** resolve ref in nested "gitlink" repository */
 extern int resolve_gitlink_ref(const char *name, const char *refname, unsigned char *result);
 
+/** lock a ref and then write its file */
+enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
+int update_ref(const char *action, const char *refname,
+		const unsigned char *sha1, const unsigned char *oldval,
+		int flags, enum action_on_err onerr);
+
 #endif /* REFS_H */
diff --git a/send-pack.c b/send-pack.c
index 9fc8a81..f74e66a 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -307,20 +307,14 @@
 			rs.src = ref->name;
 			rs.dst = NULL;
 			if (!remote_find_tracking(remote, &rs)) {
-				struct ref_lock *lock;
 				fprintf(stderr, " Also local %s\n", rs.dst);
 				if (will_delete_ref) {
 					if (delete_ref(rs.dst, NULL)) {
 						error("Failed to delete");
 					}
-				} else {
-					lock = lock_any_ref_for_update(rs.dst, NULL, 0);
-					if (!lock)
-						error("Failed to lock");
-					else
-						write_ref_sha1(lock, ref->new_sha1,
-							       "update by push");
-				}
+				} else
+					update_ref("update by push", rs.dst,
+						ref->new_sha1, NULL, 0, 0);
 				free(rs.dst);
 			}
 		}
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 1a4c53a..42e28ab 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -28,12 +28,15 @@
 TAR=${TAR:-tar}
 UNZIP=${UNZIP:-unzip}
 
+SUBSTFORMAT=%H%n
+
 test_expect_success \
     'populate workdir' \
     'mkdir a b c &&
      echo simple textfile >a/a &&
      mkdir a/bin &&
      cp /bin/sh a/bin &&
+     printf "A\$Format:%s\$O" "$SUBSTFORMAT" >a/substfile &&
      ln -s a a/l1 &&
      (p=long_path_to_a_file && cd a &&
       for depth in 1 2 3 4 5; do mkdir $p && cd $p; done &&
@@ -105,6 +108,22 @@
     'diff -r a c/prefix/a'
 
 test_expect_success \
+    'create an archive with a substfile' \
+    'echo substfile export-subst >a/.gitattributes &&
+     git archive HEAD >f.tar &&
+     rm a/.gitattributes'
+
+test_expect_success \
+    'extract substfile' \
+    '(mkdir f && cd f && $TAR xf -) <f.tar'
+
+test_expect_success \
+     'validate substfile contents' \
+     'git log --max-count=1 "--pretty=format:A${SUBSTFORMAT}O" HEAD \
+      >f/a/substfile.expected &&
+      diff f/a/substfile.expected f/a/substfile'
+
+test_expect_success \
     'git archive --format=zip' \
     'git archive --format=zip HEAD >d.zip'
 
diff --git a/t/t7102-reset.sh b/t/t7102-reset.sh
new file mode 100755
index 0000000..f64b1cb
--- /dev/null
+++ b/t/t7102-reset.sh
@@ -0,0 +1,405 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carlos Rica
+#
+
+test_description='git-reset
+
+Documented tests for git-reset'
+
+. ./test-lib.sh
+
+test_expect_success 'creating initial files and commits' '
+	test_tick &&
+	echo "1st file" >first &&
+	git add first &&
+	git commit -m "create 1st file" &&
+
+	echo "2nd file" >second &&
+	git add second &&
+	git commit -m "create 2nd file" &&
+
+	echo "2nd line 1st file" >>first &&
+	git commit -a -m "modify 1st file" &&
+
+	git rm first &&
+	git mv second secondfile &&
+	git commit -a -m "remove 1st and rename 2nd" &&
+
+	echo "1st line 2nd file" >secondfile &&
+	echo "2nd line 2nd file" >>secondfile &&
+	git commit -a -m "modify 2nd file"
+'
+# git log --pretty=oneline # to see those SHA1 involved
+
+check_changes () {
+	test "$(git rev-parse HEAD)" = "$1" &&
+	git diff | git diff .diff_expect - &&
+	git diff --cached | git diff .cached_expect - &&
+	for FILE in *
+	do
+		echo $FILE':'
+		cat $FILE || return
+	done | git diff .cat_expect -
+}
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+
+test_expect_success 'giving a non existing revision should fail' '
+	! git reset aaaaaa &&
+	! git reset --mixed aaaaaa &&
+	! git reset --soft aaaaaa &&
+	! git reset --hard aaaaaa &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+	'giving paths with options different than --mixed should fail' '
+	! git reset --soft -- first &&
+	! git reset --hard -- first &&
+	! git reset --soft HEAD^ -- first &&
+	! git reset --hard HEAD^ -- first &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success 'giving unrecognized options should fail' '
+	! git reset --other &&
+	! git reset -o &&
+	! git reset --mixed --other &&
+	! git reset --mixed -o &&
+	! git reset --soft --other &&
+	! git reset --soft -o &&
+	! git reset --hard --other &&
+	! git reset --hard -o &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+	'trying to do reset --soft with pending merge should fail' '
+	git branch branch1 &&
+	git branch branch2 &&
+
+	git checkout branch1 &&
+	echo "3rd line in branch1" >>secondfile &&
+	git commit -a -m "change in branch1" &&
+
+	git checkout branch2 &&
+	echo "3rd line in branch2" >>secondfile &&
+	git commit -a -m "change in branch2" &&
+
+	! git merge branch1 &&
+	! git reset --soft &&
+
+	printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+	git commit -a -m "the change in branch2" &&
+
+	git checkout master &&
+	git branch -D branch1 branch2 &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+	'trying to do reset --soft with pending checkout merge should fail' '
+	git branch branch3 &&
+	git branch branch4 &&
+
+	git checkout branch3 &&
+	echo "3rd line in branch3" >>secondfile &&
+	git commit -a -m "line in branch3" &&
+
+	git checkout branch4 &&
+	echo "3rd line in branch4" >>secondfile &&
+
+	git checkout -m branch3 &&
+	! git reset --soft &&
+
+	printf "1st line 2nd file\n2nd line 2nd file\n3rd line" >secondfile &&
+	git commit -a -m "the line in branch3" &&
+
+	git checkout master &&
+	git branch -D branch3 branch4 &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+test_expect_success \
+	'resetting to HEAD with no changes should succeed and do nothing' '
+	git reset --hard &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset --hard HEAD &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset --soft &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset --soft HEAD &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset --mixed &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset --mixed HEAD &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+	git reset HEAD &&
+		check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/secondfile b/secondfile
+index 1bbba79..44c5b58 100644
+--- a/secondfile
++++ b/secondfile
+@@ -1 +1,2 @@
+-2nd file
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--soft reset only should show changes in diff --cached' '
+	git reset --soft HEAD^ &&
+	check_changes d1a4bc3abce4829628ae2dcb0d60ef3d1a78b1c4 &&
+	test "$(git rev-parse ORIG_HEAD)" = \
+			3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line 2nd file
+EOF
+test_expect_success \
+	'changing files and redo the last commit should succeed' '
+	echo "3rd line 2nd file" >>secondfile &&
+	git commit -a -C ORIG_HEAD &&
+	check_changes 3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d &&
+	test "$(git rev-parse ORIG_HEAD)" = \
+			3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+first:
+1st file
+2nd line 1st file
+second:
+2nd file
+EOF
+test_expect_success \
+	'--hard reset should change the files and undo commits permanently' '
+	git reset --hard HEAD~2 &&
+	check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+	test "$(git rev-parse ORIG_HEAD)" = \
+			3d3b7be011a58ca0c179ae45d94e6c83c0b0cd0d
+'
+
+>.diff_expect
+cat >.cached_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+diff --git a/secondfile b/secondfile
+new file mode 100644
+index 0000000..44c5b58
+--- /dev/null
++++ b/secondfile
+@@ -0,0 +1,2 @@
++1st line 2nd file
++2nd line 2nd file
+EOF
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+	'redoing changes adding them without commit them should succeed' '
+	git rm first &&
+	git mv second secondfile &&
+
+	echo "1st line 2nd file" >secondfile &&
+	echo "2nd line 2nd file" >>secondfile &&
+	git add secondfile &&
+	check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+cat >.diff_expect <<EOF
+diff --git a/first b/first
+deleted file mode 100644
+index 8206c22..0000000
+--- a/first
++++ /dev/null
+@@ -1,2 +0,0 @@
+-1st file
+-2nd line 1st file
+diff --git a/second b/second
+deleted file mode 100644
+index 1bbba79..0000000
+--- a/second
++++ /dev/null
+@@ -1 +0,0 @@
+-2nd file
+EOF
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success '--mixed reset to HEAD should unadd the files' '
+	git reset &&
+	check_changes ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+	test "$(git rev-parse ORIG_HEAD)" = \
+			ddaefe00f1da16864591c61fdc7adb5d7cd6b74e
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success 'redoing the last two commits should succeed' '
+	git add secondfile &&
+	git reset --hard ddaefe00f1da16864591c61fdc7adb5d7cd6b74e &&
+
+	git rm first &&
+	git mv second secondfile &&
+	git commit -a -m "remove 1st and rename 2nd" &&
+
+	echo "1st line 2nd file" >secondfile &&
+	echo "2nd line 2nd file" >>secondfile &&
+	git commit -a -m "modify 2nd file" &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+3rd line in branch2
+EOF
+test_expect_success '--hard reset to HEAD should clear a failed merge' '
+	git branch branch1 &&
+	git branch branch2 &&
+
+	git checkout branch1 &&
+	echo "3rd line in branch1" >>secondfile &&
+	git commit -a -m "change in branch1" &&
+
+	git checkout branch2 &&
+	echo "3rd line in branch2" >>secondfile &&
+	git commit -a -m "change in branch2" &&
+
+	! git pull . branch1 &&
+	git reset --hard &&
+	check_changes 77abb337073fb4369a7ad69ff6f5ec0e4d6b54bb
+'
+
+>.diff_expect
+>.cached_expect
+cat >.cat_expect <<EOF
+secondfile:
+1st line 2nd file
+2nd line 2nd file
+EOF
+test_expect_success \
+	'--hard reset to ORIG_HEAD should clear a fast-forward merge' '
+	git reset --hard HEAD^ &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+	git pull . branch1 &&
+	git reset --hard ORIG_HEAD &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
+
+	git checkout master &&
+	git branch -D branch1 branch2 &&
+	check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+'
+
+cat > expect << EOF
+diff --git a/file1 b/file1
+index d00491f..7ed6ff8 100644
+--- a/file1
++++ b/file1
+@@ -1 +1 @@
+-1
++5
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 0cfbf08..0000000
+--- a/file2
++++ /dev/null
+@@ -1 +0,0 @@
+-2
+EOF
+cat > cached_expect << EOF
+diff --git a/file4 b/file4
+new file mode 100644
+index 0000000..b8626c4
+--- /dev/null
++++ b/file4
+@@ -0,0 +1 @@
++4
+EOF
+test_expect_success 'test --mixed <paths>' '
+	echo 1 > file1 &&
+	echo 2 > file2 &&
+	git add file1 file2 &&
+	test_tick &&
+	git commit -m files &&
+	git rm file2 &&
+	echo 3 > file3 &&
+	echo 4 > file4 &&
+	echo 5 > file1 &&
+	git add file1 file3 file4 &&
+	! git reset HEAD -- file1 file2 file3 &&
+	git diff > output &&
+	git diff output expect &&
+	git diff --cached > output &&
+	git diff output cached_expect
+'
+
+test_expect_success 'test resetting the index at give paths' '
+
+	mkdir sub &&
+	>sub/file1 &&
+	>sub/file2 &&
+	git update-index --add sub/file1 sub/file2 &&
+	T=$(git write-tree) &&
+	! git reset HEAD sub/file2 &&
+	U=$(git write-tree) &&
+	echo "$T" &&
+	echo "$U" &&
+	! git diff-index --cached --exit-code "$T" &&
+	test "$T" != "$U"
+
+'
+
+test_done