Merge branch 'maint'

* maint:
  gitattributes: -crlf is not binary
  git-apply: Loosen "match_beginning" logic
  Fix example in git-name-rev documentation
  shell: do not play duplicated definition games to shrink the executable
  Fix use of hardlinks in "make install"
  pack-objects: Allow missing base objects when creating thin packs
diff --git a/.gitignore b/.gitignore
index a213e8e..bbaf9de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,6 +51,7 @@
 git-get-tar-commit-id
 git-grep
 git-hash-object
+git-help
 git-http-fetch
 git-http-push
 git-imap-send
diff --git a/Documentation/RelNotes-1.6.1.txt b/Documentation/RelNotes-1.6.1.txt
new file mode 100644
index 0000000..d37da03
--- /dev/null
+++ b/Documentation/RelNotes-1.6.1.txt
@@ -0,0 +1,58 @@
+GIT v1.6.1 Release Notes
+========================
+
+Updates since v1.6.0
+--------------------
+
+When some commands (e.g. "git log", "git diff") spawn pager internally, we
+used to make the pager the parent process of the git command that produces
+output.  This meant that the exit status of the whole thing comes from the
+pager, not the underlying git command.  We swapped the order of the
+processes around and you will see the exit code from the command from now
+on.
+
+(subsystems)
+
+* ...
+
+(portability)
+
+* ...
+
+(documentation)
+
+* ...
+
+(performance)
+
+* The underlying diff machinery to produce textual output has been
+  optimized, which would result in faster "git blame" processing.
+
+(usability, bells and whistles)
+
+* "git checkout --track origin/hack" used to be a syntax error.  It now
+  DWIMs to create a corresponding local branch "hack", i.e. acts as if you
+  said "git checkout --track -b hack origin/hack".
+
+* "git diff" learned to mimick --suppress-blank-empty from GNU diff via a
+  configuration option.
+
+* "git imap-send" can optionally talk SSL.
+
+(internal)
+
+* "git hash-object" learned to lie about the path being hashed, so that
+  correct gitattributes processing can be done while hashing contents
+  stored in a temporary file.
+
+Fixes since v1.6.0
+------------------
+
+All of the fixes in v1.6.0.X maintenance series are included in this
+release, unless otherwise noted.
+
+--
+exec >/var/tmp/1
+O=v1.6.0-48-ge28a867
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 841bead..a1e9100 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -71,7 +71,7 @@
 
 (1a) Try to be nice to older C compilers
 
-We try to support wide range of C compilers to compile
+We try to support a wide range of C compilers to compile
 git with. That means that you should not use C99 initializers, even
 if a lot of compilers grok it.
 
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 81f9815..af57d94 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -572,6 +572,10 @@
 	affects only 'git-diff' Porcelain, and not lower level
 	'diff' commands, such as 'git-diff-files'.
 
+diff.suppress-blank-empty::
+	A boolean to inhibit the standard behavior of printing a space
+	before each empty output line. Defaults to false.
+
 diff.external::
 	If this config variable is set, diff generation is not
 	performed using the internal diff machinery, but using the
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index cba90fd..1759386 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -107,9 +107,9 @@
 	--exit-code.
 
 --full-index::
-	Instead of the first handful characters, show full
-	object name of pre- and post-image blob on the "index"
-	line when generating a patch format output.
+	Instead of the first handful of characters, show the full
+	pre- and post-image blob object names on the "index"
+	line when generating patch format output.
 
 --binary::
 	In addition to --full-index, output "binary diff" that
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 5aa69c0..be54a02 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -8,7 +8,7 @@
 SYNOPSIS
 --------
 [verse]
-'git checkout' [-q] [-f] [[--track | --no-track] -b <new_branch> [-l]] [-m] [<branch>]
+'git checkout' [-q] [-f] [--track | --no-track] [-b <new_branch> [-l]] [-m] [<branch>]
 'git checkout' [<tree-ish>] [--] <paths>...
 
 DESCRIPTION
@@ -21,6 +21,10 @@
 be created; in this case you can use the --track or --no-track
 options, which will be passed to `git branch`.
 
+As a convenience, --track will default to create a branch whose
+name is constructed from the specified branch name by stripping
+the first namespace level.
+
 When <paths> are given, this command does *not* switch
 branches.  It updates the named paths in the working tree from
 the index file (i.e. it runs `git checkout-index -f -u`), or
@@ -59,6 +63,17 @@
 	'git-checkout' and 'git-branch' to always behave as if '--no-track' were
 	given. Set it to `always` if you want this behavior when the
 	start-point is either a local or remote branch.
++
+If no '-b' option was given, the name of the new branch will be
+derived from the remote branch, by attempting to guess the name
+of the branch on remote system.  If "remotes/" or "refs/remotes/"
+are prefixed, it is stripped away, and then the part up to the
+next slash (which would be the nickname of the remote) is removed.
+This would tell us to use "hack" as the local branch when branching
+off of "origin/hack" (or "remotes/origin/hack", or even
+"refs/remotes/origin/hack").  If the given name has no slash, or the above
+guessing results in an empty name, the guessing is aborted.  You can
+exlicitly give a name with '-b' in such a case.
 
 --no-track::
 	Ignore the branch.autosetupmerge configuration variable.
diff --git a/Documentation/git-count-objects.txt b/Documentation/git-count-objects.txt
index 75a8da1..6bc1c21 100644
--- a/Documentation/git-count-objects.txt
+++ b/Documentation/git-count-objects.txt
@@ -21,8 +21,9 @@
 --verbose::
 	In addition to the number of loose objects and disk
 	space consumed, it reports the number of in-pack
-	objects, number of packs, and number of objects that can be
-	removed by running `git prune-packed`.
+	objects, number of packs, disk space consumed by those packs,
+	and number of objects that can be removed by running
+	`git prune-packed`.
 
 
 Author
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 4ba4b75..b08a08c 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -9,8 +9,9 @@
 --------
 [verse]
 'git daemon' [--verbose] [--syslog] [--export-all]
-	     [--timeout=n] [--init-timeout=n] [--strict-paths]
-	     [--base-path=path] [--user-path | --user-path=path]
+	     [--timeout=n] [--init-timeout=n] [--max-connections=n]
+	     [--strict-paths] [--base-path=path] [--base-path-relaxed]
+	     [--user-path | --user-path=path]
 	     [--interpolated-path=pathtemplate]
 	     [--reuseaddr] [--detach] [--pid-file=file]
 	     [--enable=service] [--disable=service]
@@ -99,6 +100,10 @@
 	it takes for the server to process the sub-request and time spent
 	waiting for next client's request.
 
+--max-connections::
+	Maximum number of concurrent clients, defaults to 32.  Set it to
+	zero for no limit.
+
 --syslog::
 	Log to syslog instead of stderr. Note that this option does not imply
 	--verbose, thus by default only error conditions will be logged.
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 1fdf20d..5d48664 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -49,13 +49,22 @@
 --stdin::
 	When '--stdin' is specified, the command does not take
 	<tree-ish> arguments from the command line.  Instead, it
-	reads either one <commit> or a list of <commit>
-	separated with a single space from its standard input.
+	reads lines containing either two <tree>, one <commit>, or a
+	list of <commit> from its standard input.  (Use a single space
+	as separator.)
 +
-When a single commit is given on one line of such input, it compares
-the commit with its parents.  The following flags further affects its
-behavior.  The remaining commits, when given, are used as if they are
+When two trees are given, it compares the first tree with the second.
+When a single commit is given, it compares the commit with its
+parents.  The remaining commits, when given, are used as if they are
 parents of the first commit.
++
+When comparing two trees, the ID of both trees (separated by a space
+and terminated by a newline) is printed before the difference.  When
+comparing commits, the ID of the first (or only) commit, followed by a
+newline, is printed.
++
+The following flags further affects the behavior when comparing
+commits (but not trees).
 
 -m::
 	By default, 'git-diff-tree --stdin' does not show
diff --git a/Documentation/git-hash-object.txt b/Documentation/git-hash-object.txt
index ac928e1..0af40cf 100644
--- a/Documentation/git-hash-object.txt
+++ b/Documentation/git-hash-object.txt
@@ -8,7 +8,9 @@
 
 SYNOPSIS
 --------
-'git hash-object' [-t <type>] [-w] [--stdin | --stdin-paths] [--] <file>...
+[verse]
+'git hash-object' [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...
+'git hash-object' [-t <type>] [-w] --stdin-paths < <list-of-paths>
 
 DESCRIPTION
 -----------
@@ -35,6 +37,22 @@
 --stdin-paths::
 	Read file names from stdin instead of from the command-line.
 
+--path::
+	Hash object as it were located at the given path. The location of
+	file does not directly influence on the hash value, but path is
+	used to determine what git filters should be applied to the object
+	before it can be placed to the object database, and, as result of
+	applying filters, the actual blob put into the object database may
+	differ from the given file. This option is mainly useful for hashing
+	temporary files located outside of the working directory or files
+	read from stdin.
+
+--no-filters::
+	Hash the contents as is, ignoring any input filter that would
+	have been chosen by the attributes mechanism, including crlf
+	conversion. If the file is read from standard input then this
+	is always implied, unless the --path option is given.
+
 Author
 ------
 Written by Junio C Hamano <gitster@pobox.com>
diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index b3d8da3..bd49a0a 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -3,7 +3,7 @@
 
 NAME
 ----
-git-imap-send - Dump a mailbox from stdin into an imap folder
+git-imap-send - Send a collection of patches from stdin to an IMAP folder
 
 
 SYNOPSIS
@@ -13,9 +13,9 @@
 
 DESCRIPTION
 -----------
-This command uploads a mailbox generated with git-format-patch
-into an imap drafts folder.  This allows patches to be sent as
-other email is sent with mail clients that cannot read mailbox
+This command uploads a mailbox generated with 'git-format-patch'
+into an IMAP drafts folder.  This allows patches to be sent as
+other email is when using mail clients that cannot read mailbox
 files directly.
 
 Typical usage is something like:
@@ -26,21 +26,75 @@
 CONFIGURATION
 -------------
 
-'git-imap-send' requires the following values in the repository
-configuration file (shown with examples):
+To use the tool, imap.folder and either imap.tunnel or imap.host must be set
+to appropriate values.
+
+Variables
+~~~~~~~~~
+
+imap.folder::
+	The folder to drop the mails into, which is typically the Drafts
+	folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
+	"[Gmail]/Drafts". Required to use imap-send.
+
+imap.tunnel::
+	Command used to setup a tunnel to the IMAP server through which
+	commands will be piped instead of using a direct network connection
+	to the server. Required when imap.host is not set to use imap-send.
+
+imap.host::
+	A URL identifying the server. Use a `imap://` prefix for non-secure
+	connections and a `imaps://` prefix for secure connections.
+	Ignored when imap.tunnel is set, but required to use imap-send
+	otherwise.
+
+imap.user::
+	The username to use when logging in to the server.
+
+imap.password::
+	The password to use when logging in to the server.
+
+imap.port::
+	An integer port number to connect to on the server.
+	Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
+	Ignored when imap.tunnel is set.
+
+imap.sslverify::
+	A boolean to enable/disable verification of the server certificate
+	used by the SSL/TLS connection. Default is `true`. Ignored when
+	imap.tunnel is set.
+
+Examples
+~~~~~~~~
+
+Using tunnel mode:
 
 ..........................
 [imap]
-    Folder = "INBOX.Drafts"
+    folder = "INBOX.Drafts"
+    tunnel = "ssh -q -C user@example.com /usr/bin/imapd ./Maildir 2> /dev/null"
+..........................
 
-[imap]
-    Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
+Using direct mode:
 
+.........................
 [imap]
-    Host = imap.server.com
-    User = bob
-    Pass = pwd
-    Port = 143
+    folder = "INBOX.Drafts"
+    host = imap://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+..........................
+
+Using direct mode with SSL:
+
+.........................
+[imap]
+    folder = "INBOX.Drafts"
+    host = imaps://imap.example.com
+    user = bob
+    pass = p4ssw0rd
+    port = 123
+    sslverify = false
 ..........................
 
 
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 1a7ecbf..2f0c525 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -8,26 +8,81 @@
 
 SYNOPSIS
 --------
-'git merge-base' [--all] <commit> <commit>
+'git merge-base' [--all] <commit> <commit>...
 
 DESCRIPTION
 -----------
 
-'git-merge-base' finds as good a common ancestor as possible between
-the two commits. That is, given two commits A and B, `git merge-base A
-B` will output a commit which is reachable from both A and B through
-the parent relationship.
+'git-merge-base' finds best common ancestor(s) between two commits to use
+in a three-way merge.  One common ancestor is 'better' than another common
+ancestor if the latter is an ancestor of the former.  A common ancestor
+that does not have any better common ancestor than it is a 'best common
+ancestor', i.e. a 'merge base'.  Note that there can be more than one
+merge bases between two commits.
 
-Given a selection of equally good common ancestors it should not be
-relied on to decide in any particular way.
-
-The 'git-merge-base' algorithm is still in flux - use the source...
+Among the two commits to compute their merge bases, one is specified by
+the first commit argument on the command line; the other commit is a
+(possibly hypothetical) commit that is a merge across all the remaining
+commits on the command line.  As the most common special case, giving only
+two commits from the command line means computing the merge base between
+the given two commits.
 
 OPTIONS
 -------
 --all::
-	Output all common ancestors for the two commits instead of
-	just one.
+	Output all merge bases for the commits, instead of just one.
+
+DISCUSSION
+----------
+
+Given two commits 'A' and 'B', `git merge-base A B` will output a commit
+which is reachable from both 'A' and 'B' through the parent relationship.
+
+For example, with this topology:
+
+		 o---o---o---B
+		/
+	---o---1---o---o---o---A
+
+the merge base between 'A' and 'B' is '1'.
+
+Given three commits 'A', 'B' and 'C', `git merge-base A B C` will compute the
+merge base between 'A' and an hypothetical commit 'M', which is a merge
+between 'B' and 'C'.  For example, with this topology:
+
+	       o---o---o---o---C
+	      /
+	     /   o---o---o---B
+	    /   /
+	---2---1---o---o---o---A
+
+the result of `git merge-base A B C` is '1'.  This is because the
+equivalent topology with a merge commit 'M' between 'B' and 'C' is:
+
+
+	       o---o---o---o---o
+	      /                 \
+	     /   o---o---o---o---M
+	    /   /
+	---2---1---o---o---o---A
+
+and the result of `git merge-base A M` is '1'.  Commit '2' is also a
+common ancestor between 'A' and 'M', but '1' is a better common ancestor,
+because '2' is an ancestor of '1'.  Hence, '2' is not a merge base.
+
+When the history involves criss-cross merges, there can be more than one
+'best' common ancestors between two commits.  For example, with this
+topology:
+
+       ---1---o---A
+	   \ /
+	    X
+	   / \
+       ---2---o---o---B
+
+both '1' and '2' are merge-base of A and B.  Neither one is better than
+the other (both are 'best' merge base).  When `--all` option is not given,
+it is unspecified which best one is output.
 
 Author
 ------
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 17a15ac..685e1fe 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -126,13 +126,25 @@
    up working tree changes made by 2. and 3.; 'git-reset --hard' can
    be used for this.
 
- * Resolve the conflicts.  `git diff` would report only the
-   conflicting paths because of the above 2. and 3.
-   Edit the working tree files into a desirable shape
-   ('git mergetool' can ease this task), 'git-add' or 'git-rm'
-   them, to make the index file contain what the merge result
-   should be, and run 'git-commit' to commit the result.
+ * Resolve the conflicts.  Git will mark the conflicts in
+   the working tree.  Edit the files into shape and
+   'git-add' to the index.  'git-commit' to seal the deal.
 
+You can work through the conflict with a number of tools:
+
+ * Use a mergetool.  'git mergetool' to launch a graphical
+   mergetool which will work you through the merge.
+
+ * Look at the diffs.  'git diff' will show a three-way diff,
+   highlighting changes from both the HEAD and remote versions.
+
+ * Look at the diffs on their own. 'git log --merge -p <path>'
+   will show diffs first for the HEAD version and then the
+   remote version.
+
+ * Look at the originals.  'git show :1:filename' shows the
+   common ancestor, 'git show :2:filename' shows the HEAD
+   version and 'git show :3:filename' shows the remote version.
 
 SEE ALSO
 --------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index bf33b0c..babaa9b 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -14,6 +14,8 @@
 'git submodule' [--quiet] init [--] [<path>...]
 'git submodule' [--quiet] update [--init] [--] [<path>...]
 'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach <command>
+'git submodule' [--quiet] sync [--] [<path>...]
 
 
 DESCRIPTION
@@ -123,6 +125,30 @@
 	in the submodule between the given super project commit and the
 	index or working tree (switched by --cached) are shown.
 
+foreach::
+	Evaluates an arbitrary shell command in each checked out submodule.
+	The command has access to the variables $path and $sha1:
+	$path is the name of the submodule directory relative to the
+	superproject, and $sha1 is the commit as recorded in the superproject.
+	Any submodules defined in the superproject but not checked out are
+	ignored by this command. Unless given --quiet, foreach prints the name
+	of each submodule before evaluating the command.
+	A non-zero return from the command in any submodule causes
+	the processing to terminate. This can be overridden by adding '|| :'
+	to the end of the command.
++
+As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
+show the path and currently checked out commit for each submodule.
+
+sync::
+	Synchronizes submodules' remote URL configuration setting
+	to the value specified in .gitmodules.  This is useful when
+	submodule URLs change upstream and you need to update your local
+	repositories accordingly.
++
+"git submodule sync" synchronizes all submodules while
+"git submodule sync -- A" synchronizes submodule "A" only.
+
 OPTIONS
 -------
 -q::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 363a785..e178fb5 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.6.0/git.html[documentation for release 1.6.0]
+* link:v1.6.1/git.html[documentation for release 1.6.1]
 
 * release notes for
+  link:RelNotes-1.6.1.txt[1.6.1],
   link:RelNotes-1.6.0.txt[1.6.0].
 
 * link:v1.5.6.5/git.html[documentation for release 1.5.6.5]
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 1993887..1f23d27 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -315,10 +315,14 @@
 
 - `pascal` suitable for source code in the Pascal/Delphi language.
 
+- `python` suitable for source code in the Python language.
+
 - `ruby` suitable for source code in the Ruby language.
 
 - `tex` suitable for source code for LaTeX documents.
 
+- `html` suitable for HTML/XHTML documents.
+
 
 Performing a three-way merge
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Makefile b/Makefile
index 00991b7..20f028f 100644
--- a/Makefile
+++ b/Makefile
@@ -124,6 +124,9 @@
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-index perspective.
 #
+# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks
+# field that counts the on-disk footprint in 512-byte blocks.
+#
 # Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8
 #
 # Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.
@@ -354,6 +357,7 @@
 LIB_H += graph.h
 LIB_H += grep.h
 LIB_H += hash.h
+LIB_H += help.h
 LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
@@ -517,6 +521,7 @@
 BUILTIN_OBJS += builtin-fsck.o
 BUILTIN_OBJS += builtin-gc.o
 BUILTIN_OBJS += builtin-grep.o
+BUILTIN_OBJS += builtin-help.o
 BUILTIN_OBJS += builtin-init-db.o
 BUILTIN_OBJS += builtin-log.o
 BUILTIN_OBJS += builtin-ls-files.o
@@ -574,9 +579,11 @@
 
 ifeq ($(uname_S),Linux)
 	NO_STRLCPY = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),GNU/kFreeBSD)
 	NO_STRLCPY = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),UnixWare)
 	CC = cc
@@ -674,6 +681,7 @@
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
 	DIR_HAS_BSD_GROUP_SEMANTICS = YesPlease
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),OpenBSD)
 	NO_STRCASESTR = YesPlease
@@ -681,6 +689,7 @@
 	NEEDS_LIBICONV = YesPlease
 	BASIC_CFLAGS += -I/usr/local/include
 	BASIC_LDFLAGS += -L/usr/local/lib
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),NetBSD)
 	ifeq ($(shell expr "$(uname_R)" : '[01]\.'),2)
@@ -689,6 +698,7 @@
 	BASIC_CFLAGS += -I/usr/pkg/include
 	BASIC_LDFLAGS += -L/usr/pkg/lib
 	ALL_LDFLAGS += -Wl,-rpath,/usr/pkg/lib
+	THREADED_DELTA_SEARCH = YesPlease
 endif
 ifeq ($(uname_S),AIX)
 	NO_STRCASESTR=YesPlease
@@ -749,6 +759,7 @@
 	NO_SVN_TESTS = YesPlease
 	NO_PERL_MAKEMAKER = YesPlease
 	NO_POSIX_ONLY_PROGRAMS = YesPlease
+	NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
 	COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat
 	COMPAT_CFLAGS += -DSNPRINTF_SIZE_CORR=1
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
@@ -863,6 +874,9 @@
 ifdef NO_D_INO_IN_DIRENT
 	BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT
 endif
+ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+	BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT
+endif
 ifdef NO_C99_FORMAT
 	BASIC_CFLAGS += -DNO_C99_FORMAT
 endif
@@ -1091,7 +1105,7 @@
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
 		$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-help.o: help.c common-cmds.h GIT-CFLAGS
+builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
 	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
 		'-DGIT_HTML_PATH="$(htmldir_SQ)"' \
 		'-DGIT_MAN_PATH="$(mandir_SQ)"' \
@@ -1218,7 +1232,9 @@
 git-%$X: %.o $(GITLIBS)
 	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
-git-imap-send$X: imap-send.o $(LIB_FILE)
+git-imap-send$X: imap-send.o $(GITLIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
+		$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
 
 http.o http-walker.o http-push.o transport.o: http.h
 
@@ -1348,7 +1364,7 @@
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-	$(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X '$(DESTDIR_SQ)$(bindir_SQ)'
+	$(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X '$(DESTDIR_SQ)$(bindir_SQ)'
 	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 	$(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_TCLTK
diff --git a/RelNotes b/RelNotes
index 8f32997..3d42084 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.0.1.txt
\ No newline at end of file
+Documentation/RelNotes-1.6.1.txt
\ No newline at end of file
diff --git a/builtin-add.c b/builtin-add.c
index fc3f96e..7c874e3 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -8,10 +8,6 @@
 #include "dir.h"
 #include "exec_cmd.h"
 #include "cache-tree.h"
-#include "diff.h"
-#include "diffcore.h"
-#include "commit.h"
-#include "revision.h"
 #include "run-command.h"
 #include "parse-options.h"
 
@@ -22,6 +18,27 @@
 static int patch_interactive = 0, add_interactive = 0;
 static int take_worktree_changes;
 
+static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
+{
+	int num_unmatched = 0, i;
+
+	/*
+	 * Since we are walking the index as if we are warlking the directory,
+	 * we have to mark the matched pathspec as seen; otherwise we will
+	 * mistakenly think that the user gave a pathspec that did not match
+	 * anything.
+	 */
+	for (i = 0; i < specs; i++)
+		if (!seen[i])
+			num_unmatched++;
+	if (!num_unmatched)
+		return;
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
+	}
+}
+
 static void prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
 {
 	char *seen;
@@ -41,6 +58,7 @@
 			*dst++ = entry;
 	}
 	dir->nr = dst - dir->entries;
+	fill_pathspec_matches(pathspec, seen, specs);
 
 	for (i = 0; i < specs; i++) {
 		if (!seen[i] && !file_exists(pathspec[i]))
@@ -79,59 +97,6 @@
 		prune_directory(dir, pathspec, baselen);
 }
 
-struct update_callback_data
-{
-	int flags;
-	int add_errors;
-};
-
-static void update_callback(struct diff_queue_struct *q,
-			    struct diff_options *opt, void *cbdata)
-{
-	int i;
-	struct update_callback_data *data = cbdata;
-
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		const char *path = p->one->path;
-		switch (p->status) {
-		default:
-			die("unexpected diff status %c", p->status);
-		case DIFF_STATUS_UNMERGED:
-		case DIFF_STATUS_MODIFIED:
-		case DIFF_STATUS_TYPE_CHANGED:
-			if (add_file_to_cache(path, data->flags)) {
-				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
-					die("updating files failed");
-				data->add_errors++;
-			}
-			break;
-		case DIFF_STATUS_DELETED:
-			if (!(data->flags & ADD_CACHE_PRETEND))
-				remove_file_from_cache(path);
-			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
-				printf("remove '%s'\n", path);
-			break;
-		}
-	}
-}
-
-int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
-{
-	struct update_callback_data data;
-	struct rev_info rev;
-	init_revisions(&rev, prefix);
-	setup_revisions(0, NULL, &rev, NULL);
-	rev.prune_data = pathspec;
-	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
-	rev.diffopt.format_callback = update_callback;
-	data.flags = flags;
-	data.add_errors = 0;
-	rev.diffopt.format_callback_data = &data;
-	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	return !!data.add_errors;
-}
-
 static void refresh(int verbose, const char **pathspec)
 {
 	char *seen;
@@ -153,6 +118,16 @@
 {
 	const char **pathspec = get_pathspec(prefix, argv);
 
+	if (pathspec) {
+		const char **p;
+		for (p = pathspec; *p; p++) {
+			if (has_symlink_leading_path(strlen(*p), *p)) {
+				int len = prefix ? strlen(prefix) : 0;
+				die("'%s' is beyond a symbolic link", *p + len);
+			}
+		}
+	}
+
 	return pathspec;
 }
 
@@ -258,7 +233,7 @@
 
 	if (addremove && take_worktree_changes)
 		die("-A and -u are mutually incompatible");
-	if (addremove && !argc) {
+	if ((addremove || take_worktree_changes) && !argc) {
 		static const char *here[2] = { ".", NULL };
 		argc = 1;
 		argv = here;
@@ -271,33 +246,30 @@
 
 	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
 		 (show_only ? ADD_CACHE_PRETEND : 0) |
-		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0));
+		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+		 (!(addremove || take_worktree_changes)
+		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
 
 	if (require_pathspec && argc == 0) {
 		fprintf(stderr, "Nothing specified, nothing added.\n");
 		fprintf(stderr, "Maybe you wanted to say 'git add .'?\n");
 		return 0;
 	}
-	pathspec = get_pathspec(prefix, argv);
-
-	/*
-	 * If we are adding new files, we need to scan the working
-	 * tree to find the ones that match pathspecs; this needs
-	 * to be done before we read the index.
-	 */
-	if (add_new_files)
-		fill_directory(&dir, pathspec, ignored_too);
+	pathspec = validate_pathspec(argc, argv, prefix);
 
 	if (read_cache() < 0)
 		die("index file corrupt");
 
+	if (add_new_files)
+		/* This picks up the paths that are not tracked */
+		fill_directory(&dir, pathspec, ignored_too);
+
 	if (refresh_only) {
 		refresh(verbose, pathspec);
 		goto finish;
 	}
 
-	if (take_worktree_changes || addremove)
-		exit_status |= add_files_to_cache(prefix, pathspec, flags);
+	exit_status |= add_files_to_cache(prefix, pathspec, flags);
 
 	if (add_new_files)
 		exit_status |= add_files(&dir, flags);
diff --git a/builtin-blame.c b/builtin-blame.c
index 4ea3431..e4d12de 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -465,7 +465,6 @@
 };
 
 struct blame_diff_state {
-	struct xdiff_emit_state xm;
 	struct patch *ret;
 	unsigned hunk_post_context;
 	unsigned hunk_in_pre_context : 1;
@@ -528,15 +527,12 @@
 	xpp.flags = xdl_opts;
 	memset(&xecfg, 0, sizeof(xecfg));
 	xecfg.ctxlen = context;
-	ecb.outf = xdiff_outf;
-	ecb.priv = &state;
 	memset(&state, 0, sizeof(state));
-	state.xm.consume = process_u_diff;
 	state.ret = xmalloc(sizeof(struct patch));
 	state.ret->chunks = NULL;
 	state.ret->num = 0;
 
-	xdi_diff(file_p, file_o, &xpp, &xecfg, &ecb);
+	xdi_diff_outf(file_p, file_o, process_u_diff, &state, &xpp, &xecfg, &ecb);
 
 	if (state.ret->num) {
 		struct chunk *chunk;
diff --git a/builtin-checkout.c b/builtin-checkout.c
index 411cc51..b380ad6 100644
--- a/builtin-checkout.c
+++ b/builtin-checkout.c
@@ -157,7 +157,7 @@
 	int force;
 	int writeout_error;
 
-	char *new_branch;
+	const char *new_branch;
 	int new_branch_log;
 	enum branch_track track;
 };
@@ -437,13 +437,28 @@
 
 	git_config(git_default_config, NULL);
 
-	opts.track = git_branch_track;
+	opts.track = BRANCH_TRACK_UNSPECIFIED;
 
 	argc = parse_options(argc, argv, options, checkout_usage,
 			     PARSE_OPT_KEEP_DASHDASH);
 
-	if (!opts.new_branch && (opts.track != git_branch_track))
-		die("git checkout: --track and --no-track require -b");
+	/* --track without -b should DWIM */
+	if (0 < opts.track && !opts.new_branch) {
+		const char *argv0 = argv[0];
+		if (!argc || !strcmp(argv0, "--"))
+			die ("--track needs a branch name");
+		if (!prefixcmp(argv0, "refs/"))
+			argv0 += 5;
+		if (!prefixcmp(argv0, "remotes/"))
+			argv0 += 8;
+		argv0 = strchr(argv0, '/');
+		if (!argv0 || !argv0[1])
+			die ("Missing branch name; try -b");
+		opts.new_branch = argv0 + 1;
+	}
+
+	if (opts.track == BRANCH_TRACK_UNSPECIFIED)
+		opts.track = git_branch_track;
 
 	if (opts.force && opts.merge)
 		die("git checkout: -f and -m are incompatible");
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index 7a9a309..f773db5 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -48,6 +48,7 @@
 int commit_tree(const char *msg, unsigned char *tree,
 		struct commit_list *parents, unsigned char *ret)
 {
+	int result;
 	int encoding_is_utf8;
 	struct strbuf buffer;
 
@@ -86,7 +87,9 @@
 	if (encoding_is_utf8 && !is_utf8(buffer.buf))
 		fprintf(stderr, commit_utf8_warn);
 
-	return write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+	result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
+	strbuf_release(&buffer);
+	return result;
 }
 
 int cmd_commit_tree(int argc, const char **argv, const char *prefix)
diff --git a/builtin-count-objects.c b/builtin-count-objects.c
index 91b5487..6e80fe7 100644
--- a/builtin-count-objects.c
+++ b/builtin-count-objects.c
@@ -43,7 +43,7 @@
 			if (lstat(path, &st) || !S_ISREG(st.st_mode))
 				bad = 1;
 			else
-				(*loose_size) += xsize_t(st.st_blocks);
+				(*loose_size) += xsize_t(on_disk_bytes(st));
 		}
 		if (bad) {
 			if (verbose) {
@@ -104,6 +104,7 @@
 	if (verbose) {
 		struct packed_git *p;
 		unsigned long num_pack = 0;
+		unsigned long size_pack = 0;
 		if (!packed_git)
 			prepare_packed_git();
 		for (p = packed_git; p; p = p->next) {
@@ -112,12 +113,14 @@
 			if (open_pack_index(p))
 				continue;
 			packed += p->num_objects;
+			size_pack += p->pack_size + p->index_size;
 			num_pack++;
 		}
 		printf("count: %lu\n", loose);
-		printf("size: %lu\n", loose_size / 2);
+		printf("size: %lu\n", loose_size / 1024);
 		printf("in-pack: %lu\n", packed);
 		printf("packs: %lu\n", num_pack);
+		printf("size-pack: %lu\n", size_pack / 1024);
 		printf("prune-packable: %lu\n", packed_loose);
 		printf("garbage: %lu\n", garbage);
 	}
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index 415cb16..1138c2d 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -14,20 +14,10 @@
 	return log_tree_commit(&log_tree_opt, commit);
 }
 
-static int diff_tree_stdin(char *line)
+/* Diff one or more commits. */
+static int stdin_diff_commit(struct commit *commit, char *line, int len)
 {
-	int len = strlen(line);
 	unsigned char sha1[20];
-	struct commit *commit;
-
-	if (!len || line[len-1] != '\n')
-		return -1;
-	line[len-1] = 0;
-	if (get_sha1_hex(line, sha1))
-		return -1;
-	commit = lookup_commit(sha1);
-	if (!commit || parse_commit(commit))
-		return -1;
 	if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
 		/* Graft the fake parents locally to the commit */
 		int pos = 41;
@@ -52,6 +42,48 @@
 	return log_tree_commit(&log_tree_opt, commit);
 }
 
+/* Diff two trees. */
+static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+{
+	unsigned char sha1[20];
+	struct tree *tree2;
+	if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+		return error("Need exactly two trees, separated by a space");
+	tree2 = lookup_tree(sha1);
+	if (!tree2 || parse_tree(tree2))
+		return -1;
+	printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
+			  sha1_to_hex(tree2->object.sha1));
+	diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+		       "", &log_tree_opt.diffopt);
+	log_tree_diff_flush(&log_tree_opt);
+	return 0;
+}
+
+static int diff_tree_stdin(char *line)
+{
+	int len = strlen(line);
+	unsigned char sha1[20];
+	struct object *obj;
+
+	if (!len || line[len-1] != '\n')
+		return -1;
+	line[len-1] = 0;
+	if (get_sha1_hex(line, sha1))
+		return -1;
+	obj = lookup_object(sha1);
+	obj = obj ? obj : parse_object(sha1);
+	if (!obj)
+		return -1;
+	if (obj->type == OBJ_COMMIT)
+		return stdin_diff_commit((struct commit *)obj, line, len);
+	if (obj->type == OBJ_TREE)
+		return stdin_diff_trees((struct tree *)obj, line, len);
+	error("Object %s is a %s, not a commit or tree",
+	      sha1_to_hex(sha1), typename(obj->type));
+	return -1;
+}
+
 static const char diff_tree_usage[] =
 "git diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
diff --git a/builtin-help.c b/builtin-help.c
new file mode 100644
index 0000000..721038e
--- /dev/null
+++ b/builtin-help.c
@@ -0,0 +1,462 @@
+/*
+ * builtin-help.c
+ *
+ * Builtin help command
+ */
+#include "cache.h"
+#include "builtin.h"
+#include "exec_cmd.h"
+#include "common-cmds.h"
+#include "parse-options.h"
+#include "run-command.h"
+#include "help.h"
+
+static struct man_viewer_list {
+	struct man_viewer_list *next;
+	char name[FLEX_ARRAY];
+} *man_viewer_list;
+
+static struct man_viewer_info_list {
+	struct man_viewer_info_list *next;
+	const char *info;
+	char name[FLEX_ARRAY];
+} *man_viewer_info_list;
+
+enum help_format {
+	HELP_FORMAT_MAN,
+	HELP_FORMAT_INFO,
+	HELP_FORMAT_WEB,
+};
+
+static int show_all = 0;
+static enum help_format help_format = HELP_FORMAT_MAN;
+static struct option builtin_help_options[] = {
+	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
+	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
+	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
+			HELP_FORMAT_WEB),
+	OPT_SET_INT('i', "info", &help_format, "show info page",
+			HELP_FORMAT_INFO),
+	OPT_END(),
+};
+
+static const char * const builtin_help_usage[] = {
+	"git help [--all] [--man|--web|--info] [command]",
+	NULL
+};
+
+static enum help_format parse_help_format(const char *format)
+{
+	if (!strcmp(format, "man"))
+		return HELP_FORMAT_MAN;
+	if (!strcmp(format, "info"))
+		return HELP_FORMAT_INFO;
+	if (!strcmp(format, "web") || !strcmp(format, "html"))
+		return HELP_FORMAT_WEB;
+	die("unrecognized help format '%s'", format);
+}
+
+static const char *get_man_viewer_info(const char *name)
+{
+	struct man_viewer_info_list *viewer;
+
+	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
+	{
+		if (!strcasecmp(name, viewer->name))
+			return viewer->info;
+	}
+	return NULL;
+}
+
+static int check_emacsclient_version(void)
+{
+	struct strbuf buffer = STRBUF_INIT;
+	struct child_process ec_process;
+	const char *argv_ec[] = { "emacsclient", "--version", NULL };
+	int version;
+
+	/* emacsclient prints its version number on stderr */
+	memset(&ec_process, 0, sizeof(ec_process));
+	ec_process.argv = argv_ec;
+	ec_process.err = -1;
+	ec_process.stdout_to_stderr = 1;
+	if (start_command(&ec_process)) {
+		fprintf(stderr, "Failed to start emacsclient.\n");
+		return -1;
+	}
+	strbuf_read(&buffer, ec_process.err, 20);
+	close(ec_process.err);
+
+	/*
+	 * Don't bother checking return value, because "emacsclient --version"
+	 * seems to always exits with code 1.
+	 */
+	finish_command(&ec_process);
+
+	if (prefixcmp(buffer.buf, "emacsclient")) {
+		fprintf(stderr, "Failed to parse emacsclient version.\n");
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_remove(&buffer, 0, strlen("emacsclient"));
+	version = atoi(buffer.buf);
+
+	if (version < 22) {
+		fprintf(stderr,
+			"emacsclient version '%d' too old (< 22).\n",
+			version);
+		strbuf_release(&buffer);
+		return -1;
+	}
+
+	strbuf_release(&buffer);
+	return 0;
+}
+
+static void exec_woman_emacs(const char* path, const char *page)
+{
+	if (!check_emacsclient_version()) {
+		/* This works only with emacsclient version >= 22. */
+		struct strbuf man_page = STRBUF_INIT;
+
+		if (!path)
+			path = "emacsclient";
+		strbuf_addf(&man_page, "(woman \"%s\")", page);
+		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
+		warning("failed to exec '%s': %s", path, strerror(errno));
+	}
+}
+
+static void exec_man_konqueror(const char* path, const char *page)
+{
+	const char *display = getenv("DISPLAY");
+	if (display && *display) {
+		struct strbuf man_page = STRBUF_INIT;
+		const char *filename = "kfmclient";
+
+		/* It's simpler to launch konqueror using kfmclient. */
+		if (path) {
+			const char *file = strrchr(path, '/');
+			if (file && !strcmp(file + 1, "konqueror")) {
+				char *new = xstrdup(path);
+				char *dest = strrchr(new, '/');
+
+				/* strlen("konqueror") == strlen("kfmclient") */
+				strcpy(dest + 1, "kfmclient");
+				path = new;
+			}
+			if (file)
+				filename = file;
+		} else
+			path = "kfmclient";
+		strbuf_addf(&man_page, "man:%s(1)", page);
+		execlp(path, filename, "newTab", man_page.buf, NULL);
+		warning("failed to exec '%s': %s", path, strerror(errno));
+	}
+}
+
+static void exec_man_man(const char* path, const char *page)
+{
+	if (!path)
+		path = "man";
+	execlp(path, "man", page, NULL);
+	warning("failed to exec '%s': %s", path, strerror(errno));
+}
+
+static void exec_man_cmd(const char *cmd, const char *page)
+{
+	struct strbuf shell_cmd = STRBUF_INIT;
+	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
+	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
+	warning("failed to exec '%s': %s", cmd, strerror(errno));
+}
+
+static void add_man_viewer(const char *name)
+{
+	struct man_viewer_list **p = &man_viewer_list;
+	size_t len = strlen(name);
+
+	while (*p)
+		p = &((*p)->next);
+	*p = xcalloc(1, (sizeof(**p) + len + 1));
+	strncpy((*p)->name, name, len);
+}
+
+static int supported_man_viewer(const char *name, size_t len)
+{
+	return (!strncasecmp("man", name, len) ||
+		!strncasecmp("woman", name, len) ||
+		!strncasecmp("konqueror", name, len));
+}
+
+static void do_add_man_viewer_info(const char *name,
+				   size_t len,
+				   const char *value)
+{
+	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
+
+	strncpy(new->name, name, len);
+	new->info = xstrdup(value);
+	new->next = man_viewer_info_list;
+	man_viewer_info_list = new;
+}
+
+static int add_man_viewer_path(const char *name,
+			       size_t len,
+			       const char *value)
+{
+	if (supported_man_viewer(name, len))
+		do_add_man_viewer_info(name, len, value);
+	else
+		warning("'%s': path for unsupported man viewer.\n"
+			"Please consider using 'man.<tool>.cmd' instead.",
+			name);
+
+	return 0;
+}
+
+static int add_man_viewer_cmd(const char *name,
+			      size_t len,
+			      const char *value)
+{
+	if (supported_man_viewer(name, len))
+		warning("'%s': cmd for supported man viewer.\n"
+			"Please consider using 'man.<tool>.path' instead.",
+			name);
+	else
+		do_add_man_viewer_info(name, len, value);
+
+	return 0;
+}
+
+static int add_man_viewer_info(const char *var, const char *value)
+{
+	const char *name = var + 4;
+	const char *subkey = strrchr(name, '.');
+
+	if (!subkey)
+		return error("Config with no key for man viewer: %s", name);
+
+	if (!strcmp(subkey, ".path")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_path(name, subkey - name, value);
+	}
+	if (!strcmp(subkey, ".cmd")) {
+		if (!value)
+			return config_error_nonbool(var);
+		return add_man_viewer_cmd(name, subkey - name, value);
+	}
+
+	warning("'%s': unsupported man viewer sub key.", subkey);
+	return 0;
+}
+
+static int git_help_config(const char *var, const char *value, void *cb)
+{
+	if (!strcmp(var, "help.format")) {
+		if (!value)
+			return config_error_nonbool(var);
+		help_format = parse_help_format(value);
+		return 0;
+	}
+	if (!strcmp(var, "man.viewer")) {
+		if (!value)
+			return config_error_nonbool(var);
+		add_man_viewer(value);
+		return 0;
+	}
+	if (!prefixcmp(var, "man."))
+		return add_man_viewer_info(var, value);
+
+	return git_default_config(var, value, cb);
+}
+
+static struct cmdnames main_cmds, other_cmds;
+
+void list_common_cmds_help(void)
+{
+	int i, longest = 0;
+
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		if (longest < strlen(common_cmds[i].name))
+			longest = strlen(common_cmds[i].name);
+	}
+
+	puts("The most commonly used git commands are:");
+	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
+		printf("   %s   ", common_cmds[i].name);
+		mput_char(' ', longest - strlen(common_cmds[i].name));
+		puts(common_cmds[i].help);
+	}
+}
+
+static int is_git_command(const char *s)
+{
+	return is_in_cmdlist(&main_cmds, s) ||
+		is_in_cmdlist(&other_cmds, s);
+}
+
+static const char *prepend(const char *prefix, const char *cmd)
+{
+	size_t pre_len = strlen(prefix);
+	size_t cmd_len = strlen(cmd);
+	char *p = xmalloc(pre_len + cmd_len + 1);
+	memcpy(p, prefix, pre_len);
+	strcpy(p + pre_len, cmd);
+	return p;
+}
+
+static const char *cmd_to_page(const char *git_cmd)
+{
+	if (!git_cmd)
+		return "git";
+	else if (!prefixcmp(git_cmd, "git"))
+		return git_cmd;
+	else if (is_git_command(git_cmd))
+		return prepend("git-", git_cmd);
+	else
+		return prepend("git", git_cmd);
+}
+
+static void setup_man_path(void)
+{
+	struct strbuf new_path;
+	const char *old_path = getenv("MANPATH");
+
+	strbuf_init(&new_path, 0);
+
+	/* We should always put ':' after our path. If there is no
+	 * old_path, the ':' at the end will let 'man' to try
+	 * system-wide paths after ours to find the manual page. If
+	 * there is old_path, we need ':' as delimiter. */
+	strbuf_addstr(&new_path, GIT_MAN_PATH);
+	strbuf_addch(&new_path, ':');
+	if (old_path)
+		strbuf_addstr(&new_path, old_path);
+
+	setenv("MANPATH", new_path.buf, 1);
+
+	strbuf_release(&new_path);
+}
+
+static void exec_viewer(const char *name, const char *page)
+{
+	const char *info = get_man_viewer_info(name);
+
+	if (!strcasecmp(name, "man"))
+		exec_man_man(info, page);
+	else if (!strcasecmp(name, "woman"))
+		exec_woman_emacs(info, page);
+	else if (!strcasecmp(name, "konqueror"))
+		exec_man_konqueror(info, page);
+	else if (info)
+		exec_man_cmd(info, page);
+	else
+		warning("'%s': unknown man viewer.", name);
+}
+
+static void show_man_page(const char *git_cmd)
+{
+	struct man_viewer_list *viewer;
+	const char *page = cmd_to_page(git_cmd);
+
+	setup_man_path();
+	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
+	{
+		exec_viewer(viewer->name, page); /* will return when unable */
+	}
+	exec_viewer("man", page);
+	die("no man viewer handled the request");
+}
+
+static void show_info_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	setenv("INFOPATH", GIT_INFO_PATH, 1);
+	execlp("info", "info", "gitman", page, NULL);
+}
+
+static void get_html_page_path(struct strbuf *page_path, const char *page)
+{
+	struct stat st;
+	const char *html_path = system_path(GIT_HTML_PATH);
+
+	/* Check that we have a git documentation directory. */
+	if (stat(mkpath("%s/git.html", html_path), &st)
+	    || !S_ISREG(st.st_mode))
+		die("'%s': not a documentation directory.", html_path);
+
+	strbuf_init(page_path, 0);
+	strbuf_addf(page_path, "%s/%s.html", html_path, page);
+}
+
+/*
+ * If open_html is not defined in a platform-specific way (see for
+ * example compat/mingw.h), we use the script web--browse to display
+ * HTML.
+ */
+#ifndef open_html
+void open_html(const char *path)
+{
+	execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
+}
+#endif
+
+static void show_html_page(const char *git_cmd)
+{
+	const char *page = cmd_to_page(git_cmd);
+	struct strbuf page_path; /* it leaks but we exec bellow */
+
+	get_html_page_path(&page_path, page);
+
+	open_html(page_path.buf);
+}
+
+int cmd_help(int argc, const char **argv, const char *prefix)
+{
+	int nongit;
+	const char *alias;
+	load_command_list("git-", &main_cmds, &other_cmds);
+
+	setup_git_directory_gently(&nongit);
+	git_config(git_help_config, NULL);
+
+	argc = parse_options(argc, argv, builtin_help_options,
+			builtin_help_usage, 0);
+
+	if (show_all) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_commands("git commands", &main_cmds, &other_cmds);
+		printf("%s\n", git_more_info_string);
+		return 0;
+	}
+
+	if (!argv[0]) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_common_cmds_help();
+		printf("\n%s\n", git_more_info_string);
+		return 0;
+	}
+
+	alias = alias_lookup(argv[0]);
+	if (alias && !is_git_command(argv[0])) {
+		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
+		return 0;
+	}
+
+	switch (help_format) {
+	case HELP_FORMAT_MAN:
+		show_man_page(argv[0]);
+		break;
+	case HELP_FORMAT_INFO:
+		show_info_page(argv[0]);
+		break;
+	case HELP_FORMAT_WEB:
+		show_html_page(argv[0]);
+		break;
+	}
+
+	return 0;
+}
diff --git a/builtin-log.c b/builtin-log.c
index 911fd65..1d3c5cb 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -217,6 +217,11 @@
 	if (rev->early_output)
 		finish_early_output(rev);
 
+	/*
+	 * For --check and --exit-code, the exit code is based on CHECK_FAILED
+	 * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
+	 * retain that state information if replacing rev->diffopt in this loop
+	 */
 	while ((commit = get_revision(rev)) != NULL) {
 		log_tree_commit(rev, commit);
 		if (!rev->reflog_info) {
@@ -227,7 +232,11 @@
 		free_commit_list(commit->parents);
 		commit->parents = NULL;
 	}
-	return 0;
+	if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
+	    DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
+		return 02;
+	}
+	return diff_result_code(&rev->diffopt, 0);
 }
 
 static int git_log_config(const char *var, const char *value, void *cb)
@@ -923,7 +932,8 @@
 	if (argc > 1)
 		die ("unrecognized argument: %s", argv[1]);
 
-	if (!rev.diffopt.output_format)
+	if (!rev.diffopt.output_format
+		|| rev.diffopt.output_format == DIFF_FORMAT_PATCH)
 		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
 
 	if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
index 3382b13..b08da51 100644
--- a/builtin-merge-base.c
+++ b/builtin-merge-base.c
@@ -2,9 +2,11 @@
 #include "cache.h"
 #include "commit.h"
 
-static int show_merge_base(struct commit *rev1, struct commit *rev2, int show_all)
+static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
 {
-	struct commit_list *result = get_merge_bases(rev1, rev2, 0);
+	struct commit_list *result;
+
+	result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1, 0);
 
 	if (!result)
 		return 1;
@@ -20,7 +22,7 @@
 }
 
 static const char merge_base_usage[] =
-"git merge-base [--all] <commit-id> <commit-id>";
+"git merge-base [--all] <commit-id> <commit-id>...";
 
 static struct commit *get_commit_reference(const char *arg)
 {
@@ -38,7 +40,8 @@
 
 int cmd_merge_base(int argc, const char **argv, const char *prefix)
 {
-	struct commit *rev1, *rev2;
+	struct commit **rev;
+	int rev_nr = 0;
 	int show_all = 0;
 
 	git_config(git_default_config, NULL);
@@ -51,10 +54,15 @@
 			usage(merge_base_usage);
 		argc--; argv++;
 	}
-	if (argc != 3)
+	if (argc < 3)
 		usage(merge_base_usage);
-	rev1 = get_commit_reference(argv[1]);
-	rev2 = get_commit_reference(argv[2]);
 
-	return show_merge_base(rev1, rev2, show_all);
+	rev = xmalloc((argc - 1) * sizeof(*rev));
+
+	do {
+		rev[rev_nr++] = get_commit_reference(argv[1]);
+		argc--; argv++;
+	} while (argc > 1);
+
+	return show_merge_base(rev, rev_nr, show_all);
 }
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 43e55bf..dfb363e 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -729,13 +729,13 @@
 	const char *dst_name2 = ren2_dst;
 	if (string_list_has_string(&current_directory_set, ren1_dst)) {
 		dst_name1 = del[delp++] = unique_path(ren1_dst, branch1);
-		output(1, "%s is a directory in %s added as %s instead",
+		output(1, "%s is a directory in %s adding as %s instead",
 		       ren1_dst, branch2, dst_name1);
 		remove_file(0, ren1_dst, 0);
 	}
 	if (string_list_has_string(&current_directory_set, ren2_dst)) {
 		dst_name2 = del[delp++] = unique_path(ren2_dst, branch2);
-		output(1, "%s is a directory in %s added as %s instead",
+		output(1, "%s is a directory in %s adding as %s instead",
 		       ren2_dst, branch1, dst_name2);
 		remove_file(0, ren2_dst, 0);
 	}
@@ -760,7 +760,7 @@
 				const char *branch1)
 {
 	char *new_path = unique_path(ren1->pair->two->path, branch1);
-	output(1, "Renamed %s to %s instead", ren1->pair->one->path, new_path);
+	output(1, "Renaming %s to %s instead", ren1->pair->one->path, new_path);
 	remove_file(0, ren1->pair->two->path, 0);
 	update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, new_path);
 	free(new_path);
@@ -773,7 +773,7 @@
 {
 	char *new_path1 = unique_path(ren1->pair->two->path, branch1);
 	char *new_path2 = unique_path(ren2->pair->two->path, branch2);
-	output(1, "Renamed %s to %s and %s to %s instead",
+	output(1, "Renaming %s to %s and %s to %s instead",
 	       ren1->pair->one->path, new_path1,
 	       ren2->pair->one->path, new_path2);
 	remove_file(0, ren1->pair->two->path, 0);
@@ -887,10 +887,10 @@
 						 branch1,
 						 branch2);
 				if (mfi.merge || !mfi.clean)
-					output(1, "Renamed %s->%s", src, ren1_dst);
+					output(1, "Renaming %s->%s", src, ren1_dst);
 
 				if (mfi.merge)
-					output(2, "Auto-merged %s", ren1_dst);
+					output(2, "Auto-merging %s", ren1_dst);
 
 				if (!mfi.clean) {
 					output(1, "CONFLICT (content): merge conflict in %s",
@@ -924,14 +924,14 @@
 
 			if (string_list_has_string(&current_directory_set, ren1_dst)) {
 				clean_merge = 0;
-				output(1, "CONFLICT (rename/directory): Renamed %s->%s in %s "
+				output(1, "CONFLICT (rename/directory): Rename %s->%s in %s "
 				       " directory %s added in %s",
 				       ren1_src, ren1_dst, branch1,
 				       ren1_dst, branch2);
 				conflict_rename_dir(ren1, branch1);
 			} else if (sha_eq(src_other.sha1, null_sha1)) {
 				clean_merge = 0;
-				output(1, "CONFLICT (rename/delete): Renamed %s->%s in %s "
+				output(1, "CONFLICT (rename/delete): Rename %s->%s in %s "
 				       "and deleted in %s",
 				       ren1_src, ren1_dst, branch1,
 				       branch2);
@@ -940,19 +940,19 @@
 				const char *new_path;
 				clean_merge = 0;
 				try_merge = 1;
-				output(1, "CONFLICT (rename/add): Renamed %s->%s in %s. "
+				output(1, "CONFLICT (rename/add): Rename %s->%s in %s. "
 				       "%s added in %s",
 				       ren1_src, ren1_dst, branch1,
 				       ren1_dst, branch2);
 				new_path = unique_path(ren1_dst, branch2);
-				output(1, "Added as %s instead", new_path);
+				output(1, "Adding as %s instead", new_path);
 				update_file(0, dst_other.sha1, dst_other.mode, new_path);
 			} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
 				ren2 = item->util;
 				clean_merge = 0;
 				ren2->processed = 1;
-				output(1, "CONFLICT (rename/rename): Renamed %s->%s in %s. "
-				       "Renamed %s->%s in %s",
+				output(1, "CONFLICT (rename/rename): Rename %s->%s in %s. "
+				       "Rename %s->%s in %s",
 				       ren1_src, ren1_dst, branch1,
 				       ren2->pair->one->path, ren2->pair->two->path, branch2);
 				conflict_rename_rename_2(ren1, branch1, ren2, branch2);
@@ -986,9 +986,9 @@
 					output(3, "Skipped %s (merged same as existing)", ren1_dst);
 				else {
 					if (mfi.merge || !mfi.clean)
-						output(1, "Renamed %s => %s", ren1_src, ren1_dst);
+						output(1, "Renaming %s => %s", ren1_src, ren1_dst);
 					if (mfi.merge)
-						output(2, "Auto-merged %s", ren1_dst);
+						output(2, "Auto-merging %s", ren1_dst);
 					if (!mfi.clean) {
 						output(1, "CONFLICT (rename/modify): Merge conflict in %s",
 						       ren1_dst);
@@ -1039,7 +1039,7 @@
 			/* Deleted in both or deleted in one and
 			 * unchanged in the other */
 			if (a_sha)
-				output(2, "Removed %s", path);
+				output(2, "Removing %s", path);
 			/* do not touch working file if it did not exist */
 			remove_file(1, path, !a_sha);
 		} else {
@@ -1086,12 +1086,12 @@
 			const char *new_path = unique_path(path, add_branch);
 			clean_merge = 0;
 			output(1, "CONFLICT (%s): There is a directory with name %s in %s. "
-			       "Added %s as %s",
+			       "Adding %s as %s",
 			       conf, path, other_branch, path, new_path);
 			remove_file(0, path, 0);
 			update_file(0, sha, mode, new_path);
 		} else {
-			output(2, "Added %s", path);
+			output(2, "Adding %s", path);
 			update_file(1, sha, mode, path);
 		}
 	} else if (a_sha && b_sha) {
@@ -1105,7 +1105,7 @@
 			reason = "add/add";
 			o_sha = (unsigned char *)null_sha1;
 		}
-		output(2, "Auto-merged %s", path);
+		output(2, "Auto-merging %s", path);
 		o.path = a.path = b.path = (char *)path;
 		hashcpy(o.sha1, o_sha);
 		o.mode = o_mode;
diff --git a/builtin-merge.c b/builtin-merge.c
index b280444..9ad9791 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -22,6 +22,7 @@
 #include "log-tree.h"
 #include "color.h"
 #include "rerere.h"
+#include "help.h"
 
 #define DEFAULT_TWOHEAD (1<<0)
 #define DEFAULT_OCTOPUS (1<<1)
@@ -77,7 +78,9 @@
 static struct strategy *get_strategy(const char *name)
 {
 	int i;
-	struct strbuf err;
+	struct strategy *ret;
+	static struct cmdnames main_cmds, other_cmds;
+	static int loaded;
 
 	if (!name)
 		return NULL;
@@ -86,12 +89,43 @@
 		if (!strcmp(name, all_strategy[i].name))
 			return &all_strategy[i];
 
-	strbuf_init(&err, 0);
-	for (i = 0; i < ARRAY_SIZE(all_strategy); i++)
-		strbuf_addf(&err, " %s", all_strategy[i].name);
-	fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
-	fprintf(stderr, "Available strategies are:%s.\n", err.buf);
-	exit(1);
+	if (!loaded) {
+		struct cmdnames not_strategies;
+		loaded = 1;
+
+		memset(&not_strategies, 0, sizeof(struct cmdnames));
+		load_command_list("git-merge-", &main_cmds, &other_cmds);
+		for (i = 0; i < main_cmds.cnt; i++) {
+			int j, found = 0;
+			struct cmdname *ent = main_cmds.names[i];
+			for (j = 0; j < ARRAY_SIZE(all_strategy); j++)
+				if (!strncmp(ent->name, all_strategy[j].name, ent->len)
+						&& !all_strategy[j].name[ent->len])
+					found = 1;
+			if (!found)
+				add_cmdname(&not_strategies, ent->name, ent->len);
+			exclude_cmds(&main_cmds, &not_strategies);
+		}
+	}
+	if (!is_in_cmdlist(&main_cmds, name) && !is_in_cmdlist(&other_cmds, name)) {
+		fprintf(stderr, "Could not find merge strategy '%s'.\n", name);
+		fprintf(stderr, "Available strategies are:");
+		for (i = 0; i < main_cmds.cnt; i++)
+			fprintf(stderr, " %s", main_cmds.names[i]->name);
+		fprintf(stderr, ".\n");
+		if (other_cmds.cnt) {
+			fprintf(stderr, "Available custom strategies are:");
+			for (i = 0; i < other_cmds.cnt; i++)
+				fprintf(stderr, " %s", other_cmds.names[i]->name);
+			fprintf(stderr, ".\n");
+		}
+		exit(1);
+	}
+
+	ret = xmalloc(sizeof(struct strategy));
+	memset(ret, 0, sizeof(struct strategy));
+	ret->name = xstrdup(name);
+	return ret;
 }
 
 static void append_strategy(struct strategy *s)
@@ -834,6 +868,11 @@
 		if (argc != 1)
 			die("Can merge only exactly one commit into "
 				"empty head");
+		if (squash)
+			die("Squash commit into empty head not supported yet");
+		if (!allow_fast_forward)
+			die("Non-fast-forward commit does not make sense into "
+			    "an empty head");
 		remote_head = peel_to_type(argv[0], 0, NULL, OBJ_COMMIT);
 		if (!remote_head)
 			die("%s - not something we can merge", argv[0]);
diff --git a/builtin-push.c b/builtin-push.c
index c1ed68d..cc6666f 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -59,8 +59,17 @@
 	if (remote->mirror)
 		flags |= (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE);
 
-	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) && refspec)
-		return -1;
+	if ((flags & TRANSPORT_PUSH_ALL) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error("--all and --tags are incompatible");
+		return error("--all can't be combined with refspecs");
+	}
+
+	if ((flags & TRANSPORT_PUSH_MIRROR) && refspec) {
+		if (!strcmp(*refspec, "refs/tags/*"))
+			return error("--mirror and --tags are incompatible");
+		return error("--mirror can't be combined with refspecs");
+	}
 
 	if ((flags & (TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) ==
 				(TRANSPORT_PUSH_ALL|TRANSPORT_PUSH_MIRROR)) {
diff --git a/builtin-reflog.c b/builtin-reflog.c
index 196fa03..6b3667e 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -540,11 +540,11 @@
 		free(collected.e);
 	}
 
-	while (i < argc) {
-		const char *ref = argv[i++];
+	for (; i < argc; i++) {
+		char *ref;
 		unsigned char sha1[20];
-		if (!resolve_ref(ref, sha1, 1, NULL)) {
-			status |= error("%s points nowhere!", ref);
+		if (!dwim_log(argv[i], strlen(argv[i]), sha1, &ref)) {
+			status |= error("%s points nowhere!", argv[i]);
 			continue;
 		}
 		set_reflog_expiry_param(&cb, explicit_expiry, ref);
diff --git a/builtin-revert.c b/builtin-revert.c
index 27881e9..3667705 100644
--- a/builtin-revert.c
+++ b/builtin-revert.c
@@ -11,6 +11,7 @@
 #include "cache-tree.h"
 #include "diff.h"
 #include "revision.h"
+#include "rerere.h"
 
 /*
  * This implements the builtins revert and cherry-pick.
@@ -395,6 +396,7 @@
 			die ("Error wrapping up %s", defmsg);
 		fprintf(stderr, "Automatic %s failed.%s\n",
 			me, help_msg(commit->object.sha1));
+		rerere();
 		exit(1);
 	}
 	if (commit_lock_file(&msg_file) < 0)
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 38eb53c..434cb8e 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -194,6 +194,10 @@
 	int len;
 	struct stat st;
 
+	len = strlen(path);
+	if (has_symlink_leading_path(len, path))
+		return error("'%s' is beyond a symbolic link", path);
+
 	/*
 	 * First things first: get the stat information, to decide
 	 * what to do about the pathname!
@@ -201,7 +205,6 @@
 	if (lstat(path, &st) < 0)
 		return process_lstat_error(path, errno);
 
-	len = strlen(path);
 	if (S_ISDIR(st.st_mode))
 		return process_directory(path, len, &st);
 
diff --git a/cache.h b/cache.h
index 884fae8..de8c2b6 100644
--- a/cache.h
+++ b/cache.h
@@ -126,6 +126,7 @@
 
 #define CE_NAMEMASK  (0x0fff)
 #define CE_STAGEMASK (0x3000)
+#define CE_EXTENDED  (0x4000)
 #define CE_VALID     (0x8000)
 #define CE_STAGESHIFT 12
 
@@ -378,6 +379,7 @@
 #define ADD_CACHE_VERBOSE 1
 #define ADD_CACHE_PRETEND 2
 #define ADD_CACHE_IGNORE_ERRORS	4
+#define ADD_CACHE_IGNORE_REMOVAL 8
 extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
 extern int add_file_to_index(struct index_state *, const char *path, int flags);
 extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
@@ -392,7 +394,6 @@
 
 extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
 extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, enum object_type type, const char *path);
-extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
@@ -452,6 +453,7 @@
 extern enum safe_crlf safe_crlf;
 
 enum branch_track {
+	BRANCH_TRACK_UNSPECIFIED = -1,
 	BRANCH_TRACK_NEVER = 0,
 	BRANCH_TRACK_REMOTE,
 	BRANCH_TRACK_ALWAYS,
diff --git a/combine-diff.c b/combine-diff.c
index 4dfc330..0cf2a83 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -143,8 +143,6 @@
 }
 
 struct combine_diff_state {
-	struct xdiff_emit_state xm;
-
 	unsigned int lno;
 	int ob, on, nb, nn;
 	unsigned long nmask;
@@ -217,17 +215,15 @@
 	parent_file.size = sz;
 	xpp.flags = XDF_NEED_MINIMAL;
 	memset(&xecfg, 0, sizeof(xecfg));
-	ecb.outf = xdiff_outf;
-	ecb.priv = &state;
 	memset(&state, 0, sizeof(state));
-	state.xm.consume = consume_line;
 	state.nmask = nmask;
 	state.sline = sline;
 	state.lno = 1;
 	state.num_parent = num_parent;
 	state.n = n;
 
-	xdi_diff(&parent_file, result_file, &xpp, &xecfg, &ecb);
+	xdi_diff_outf(&parent_file, result_file, consume_line, &state,
+		      &xpp, &xecfg, &ecb);
 	free(parent_file.ptr);
 
 	/* Assign line numbers for this parent.
diff --git a/commit.h b/commit.h
index ecdd573..de15f4d 100644
--- a/commit.h
+++ b/commit.h
@@ -122,6 +122,7 @@
 struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
 
 extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
 extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
 
 extern int register_shallow(const unsigned char *sha1);
diff --git a/compat/mingw.c b/compat/mingw.c
index 772cad5..ccfa2a0 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -31,11 +31,6 @@
 	return (time_t)winTime;
 }
 
-static inline size_t size_to_blocks(size_t s)
-{
-	return (s+511)/512;
-}
-
 extern int _getdrive( void );
 /* We keep the do_lstat code in a separate function to avoid recursion.
  * When a path ends with a slash, the stat will fail with ENOENT. In
@@ -57,10 +52,10 @@
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
+		buf->st_nlink = 1;
 		buf->st_mode = fMode;
 		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = _getdrive() - 1;
+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
 		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
@@ -94,7 +89,7 @@
  * complete. Note that Git stat()s are redirected to mingw_lstat()
  * too, since Windows doesn't really handle symlinks that well.
  */
-int mingw_lstat(const char *file_name, struct mingw_stat *buf)
+int mingw_lstat(const char *file_name, struct stat *buf)
 {
 	int namelen;
 	static char alt_name[PATH_MAX];
@@ -122,8 +117,7 @@
 }
 
 #undef fstat
-#undef stat
-int mingw_fstat(int fd, struct mingw_stat *buf)
+int mingw_fstat(int fd, struct stat *buf)
 {
 	HANDLE fh = (HANDLE)_get_osfhandle(fd);
 	BY_HANDLE_FILE_INFORMATION fdata;
@@ -133,22 +127,8 @@
 		return -1;
 	}
 	/* direct non-file handles to MS's fstat() */
-	if (GetFileType(fh) != FILE_TYPE_DISK) {
-		struct stat st;
-		if (fstat(fd, &st))
-			return -1;
-		buf->st_ino = st.st_ino;
-		buf->st_gid = st.st_gid;
-		buf->st_uid = st.st_uid;
-		buf->st_mode = st.st_mode;
-		buf->st_size = st.st_size;
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = st.st_dev;
-		buf->st_atime = st.st_atime;
-		buf->st_mtime = st.st_mtime;
-		buf->st_ctime = st.st_ctime;
-		return 0;
-	}
+	if (GetFileType(fh) != FILE_TYPE_DISK)
+		return fstat(fd, buf);
 
 	if (GetFileInformationByHandle(fh, &fdata)) {
 		int fMode = S_IREAD;
@@ -162,10 +142,10 @@
 		buf->st_ino = 0;
 		buf->st_gid = 0;
 		buf->st_uid = 0;
+		buf->st_nlink = 1;
 		buf->st_mode = fMode;
 		buf->st_size = fdata.nFileSizeLow; /* Can't use nFileSizeHigh, since it's not a stat64 */
-		buf->st_blocks = size_to_blocks(buf->st_size);
-		buf->st_dev = _getdrive() - 1;
+		buf->st_dev = buf->st_rdev = (_getdrive() - 1);
 		buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
 		buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
 		buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
diff --git a/compat/mingw.h b/compat/mingw.h
index a52e657..4f275cb 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -162,22 +162,12 @@
 
 /* Use mingw_lstat() instead of lstat()/stat() and
  * mingw_fstat() instead of fstat() on Windows.
- * struct stat is redefined because it lacks the st_blocks member.
  */
-struct mingw_stat {
-	unsigned st_mode;
-	time_t st_mtime, st_atime, st_ctime;
-	unsigned st_dev, st_ino, st_uid, st_gid;
-	size_t st_size;
-	size_t st_blocks;
-};
-int mingw_lstat(const char *file_name, struct mingw_stat *buf);
-int mingw_fstat(int fd, struct mingw_stat *buf);
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
-#define stat mingw_stat
-static inline int mingw_stat(const char *file_name, struct mingw_stat *buf)
-{ return mingw_lstat(file_name, buf); }
+#define stat(x,y) mingw_lstat(x,y)
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
diff --git a/config.mak.in b/config.mak.in
index b776149..f67d673 100644
--- a/config.mak.in
+++ b/config.mak.in
@@ -3,6 +3,7 @@
 
 CC = @CC@
 CFLAGS = @CFLAGS@
+LDFLAGS = @LDFLAGS@
 AR = @AR@
 TAR = @TAR@
 #INSTALL = @INSTALL@		# needs install-sh or install.sh in sources
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 89858c2..4f64f8a 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1483,7 +1483,7 @@
 {
 	__git_has_doubledash && return
 
-	local subcommands="add status init update"
+	local subcommands="add status init update summary foreach sync"
 	if [ -z "$(__git_find_subcommand "$subcommands")" ]; then
 		local cur="${COMP_WORDS[COMP_CWORD]}"
 		case "$cur" in
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 46136d4..2216cac 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -708,6 +708,7 @@
                 newdiff = newdiff.replace("\n", "\r\n")
             tmpFile.write(submitTemplate + separatorLine + diff + newdiff)
             tmpFile.close()
+            mtime = os.stat(fileName).st_mtime
             defaultEditor = "vi"
             if platform.system() == "Windows":
                 defaultEditor = "notepad"
@@ -716,15 +717,29 @@
             else:
                 editor = os.environ.get("EDITOR", defaultEditor);
             system(editor + " " + fileName)
-            tmpFile = open(fileName, "rb")
-            message = tmpFile.read()
-            tmpFile.close()
-            os.remove(fileName)
-            submitTemplate = message[:message.index(separatorLine)]
-            if self.isWindows:
-                submitTemplate = submitTemplate.replace("\r\n", "\n")
 
-            p4_write_pipe("submit -i", submitTemplate)
+            response = "y"
+            if os.stat(fileName).st_mtime <= mtime:
+                response = "x"
+                while response != "y" and response != "n":
+                    response = raw_input("Submit template unchanged. Submit anyway? [y]es, [n]o (skip this patch) ")
+
+            if response == "y":
+                tmpFile = open(fileName, "rb")
+                message = tmpFile.read()
+                tmpFile.close()
+                submitTemplate = message[:message.index(separatorLine)]
+                if self.isWindows:
+                    submitTemplate = submitTemplate.replace("\r\n", "\n")
+                p4_write_pipe("submit -i", submitTemplate)
+            else:
+                for f in editedFiles:
+                    p4_system("revert \"%s\"" % f);
+                for f in filesToAdd:
+                    p4_system("revert \"%s\"" % f);
+                    system("rm %s" %f)
+
+            os.remove(fileName)
         else:
             fileName = "submit.txt"
             file = open(fileName, "w+")
@@ -1733,8 +1748,12 @@
         if not P4Sync.run(self, depotPaths):
             return False
         if self.branch != "master":
-            if gitBranchExists("refs/remotes/p4/master"):
-                system("git branch master refs/remotes/p4/master")
+            if self.importIntoRemotes:
+                masterbranch = "refs/remotes/p4/master"
+            else:
+                masterbranch = "refs/heads/p4/master"
+            if gitBranchExists(masterbranch):
+                system("git branch master %s" % masterbranch)
                 system("git checkout -f")
             else:
                 print "Could not detect main branch. No checkout/master branch created."
diff --git a/ctype.c b/ctype.c
index d2bd38e..9208d67 100644
--- a/ctype.c
+++ b/ctype.c
@@ -9,18 +9,20 @@
 #undef SS
 #undef AA
 #undef DD
+#undef GS
 
 #define SS GIT_SPACE
 #define AA GIT_ALPHA
 #define DD GIT_DIGIT
+#define GS GIT_SPECIAL  /* \0, *, ?, [, \\ */
 
 unsigned char sane_ctype[256] = {
-	 0,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,		/* 0-15 */
+	GS,  0,  0,  0,  0,  0,  0,  0,  0, SS, SS,  0,  0, SS,  0,  0,		/* 0-15 */
 	 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 16-15 */
-	SS,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,		/* 32-15 */
-	DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0,  0,		/* 48-15 */
+	SS,  0,  0,  0,  0,  0,  0,  0,  0,  0, GS,  0,  0,  0,  0,  0,		/* 32-15 */
+	DD, DD, DD, DD, DD, DD, DD, DD, DD, DD,  0,  0,  0,  0,  0, GS,		/* 48-15 */
 	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 64-15 */
-	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,		/* 80-15 */
+	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, GS, GS,  0,  0,  0,		/* 80-15 */
 	 0, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,		/* 96-15 */
 	AA, AA, AA, AA, AA, AA, AA, AA, AA, AA, AA,  0,  0,  0,  0,  0,		/* 112-15 */
 	/* Nothing in the 128.. range */
diff --git a/daemon.c b/daemon.c
index 8dcde73..23278e2 100644
--- a/daemon.c
+++ b/daemon.c
@@ -16,12 +16,11 @@
 static int log_syslog;
 static int verbose;
 static int reuseaddr;
-static int child_handler_pipe[2];
 
 static const char daemon_usage[] =
 "git daemon [--verbose] [--syslog] [--export-all]\n"
-"           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
-"           [--base-path=path] [--base-path-relaxed]\n"
+"           [--timeout=n] [--init-timeout=n] [--max-connections=n]\n"
+"           [--strict-paths] [--base-path=path] [--base-path-relaxed]\n"
 "           [--user-path | --user-path=path]\n"
 "           [--interpolated-path=path]\n"
 "           [--reuseaddr] [--detach] [--pid-file=file]\n"
@@ -78,38 +77,19 @@
 
 static void logreport(int priority, const char *err, va_list params)
 {
-	/* We should do a single write so that it is atomic and output
-	 * of several processes do not get intermingled. */
-	char buf[1024];
-	int buflen;
-	int maxlen, msglen;
-
-	/* sizeof(buf) should be big enough for "[pid] \n" */
-	buflen = snprintf(buf, sizeof(buf), "[%ld] ", (long) getpid());
-
-	maxlen = sizeof(buf) - buflen - 1; /* -1 for our own LF */
-	msglen = vsnprintf(buf + buflen, maxlen, err, params);
-
 	if (log_syslog) {
+		char buf[1024];
+		vsnprintf(buf, sizeof(buf), err, params);
 		syslog(priority, "%s", buf);
-		return;
+	} else {
+		/*
+		 * Since stderr is set to linebuffered mode, the
+		 * logging of different processes will not overlap
+		 */
+		fprintf(stderr, "[%d] ", (int)getpid());
+		vfprintf(stderr, err, params);
+		fputc('\n', stderr);
 	}
-
-	/* maxlen counted our own LF but also counts space given to
-	 * vsnprintf for the terminating NUL.  We want to make sure that
-	 * we have space for our own LF and NUL after the "meat" of the
-	 * message, so truncate it at maxlen - 1.
-	 */
-	if (msglen > maxlen - 1)
-		msglen = maxlen - 1;
-	else if (msglen < 0)
-		msglen = 0; /* Protect against weird return values. */
-	buflen += msglen;
-
-	buf[buflen++] = '\n';
-	buf[buflen] = '\0';
-
-	write_in_full(2, buf, buflen);
 }
 
 static void logerror(const char *err, ...)
@@ -604,169 +584,107 @@
 	return -1;
 }
 
+static int max_connections = 32;
 
-/*
- * We count spawned/reaped separately, just to avoid any
- * races when updating them from signals. The SIGCHLD handler
- * will only update children_reaped, and the fork logic will
- * only update children_spawned.
- *
- * MAX_CHILDREN should be a power-of-two to make the modulus
- * operation cheap. It should also be at least twice
- * the maximum number of connections we will ever allow.
- */
-#define MAX_CHILDREN 128
-
-static int max_connections = 25;
-
-/* These are updated by the signal handler */
-static volatile unsigned int children_reaped;
-static pid_t dead_child[MAX_CHILDREN];
-
-/* These are updated by the main loop */
-static unsigned int children_spawned;
-static unsigned int children_deleted;
+static unsigned int live_children;
 
 static struct child {
+	struct child *next;
 	pid_t pid;
-	int addrlen;
 	struct sockaddr_storage address;
-} live_child[MAX_CHILDREN];
+} *firstborn;
 
-static void add_child(int idx, pid_t pid, struct sockaddr *addr, int addrlen)
+static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
 {
-	live_child[idx].pid = pid;
-	live_child[idx].addrlen = addrlen;
-	memcpy(&live_child[idx].address, addr, addrlen);
+	struct child *newborn, **cradle;
+
+	/*
+	 * This must be xcalloc() -- we'll compare the whole sockaddr_storage
+	 * but individual address may be shorter.
+	 */
+	newborn = xcalloc(1, sizeof(*newborn));
+	live_children++;
+	newborn->pid = pid;
+	memcpy(&newborn->address, addr, addrlen);
+	for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
+		if (!memcmp(&(*cradle)->address, &newborn->address,
+			    sizeof(newborn->address)))
+			break;
+	newborn->next = *cradle;
+	*cradle = newborn;
 }
 
-/*
- * Walk from "deleted" to "spawned", and remove child "pid".
- *
- * We move everything up by one, since the new "deleted" will
- * be one higher.
- */
-static void remove_child(pid_t pid, unsigned deleted, unsigned spawned)
+static void remove_child(pid_t pid)
 {
-	struct child n;
+	struct child **cradle, *blanket;
 
-	deleted %= MAX_CHILDREN;
-	spawned %= MAX_CHILDREN;
-	if (live_child[deleted].pid == pid) {
-		live_child[deleted].pid = -1;
-		return;
-	}
-	n = live_child[deleted];
-	for (;;) {
-		struct child m;
-		deleted = (deleted + 1) % MAX_CHILDREN;
-		if (deleted == spawned)
-			die("could not find dead child %d\n", pid);
-		m = live_child[deleted];
-		live_child[deleted] = n;
-		if (m.pid == pid)
-			return;
-		n = m;
-	}
+	for (cradle = &firstborn; (blanket = *cradle); cradle = &blanket->next)
+		if (blanket->pid == pid) {
+			*cradle = blanket->next;
+			live_children--;
+			free(blanket);
+			break;
+		}
 }
 
 /*
  * This gets called if the number of connections grows
  * past "max_connections".
  *
- * We _should_ start off by searching for connections
- * from the same IP, and if there is some address wth
- * multiple connections, we should kill that first.
- *
- * As it is, we just "randomly" kill 25% of the connections,
- * and our pseudo-random generator sucks too. I have no
- * shame.
- *
- * Really, this is just a place-holder for a _real_ algorithm.
+ * We kill the newest connection from a duplicate IP.
  */
-static void kill_some_children(int signo, unsigned start, unsigned stop)
+static void kill_some_child(void)
 {
-	start %= MAX_CHILDREN;
-	stop %= MAX_CHILDREN;
-	while (start != stop) {
-		if (!(start & 3))
-			kill(live_child[start].pid, signo);
-		start = (start + 1) % MAX_CHILDREN;
-	}
+	const struct child *blanket, *next;
+
+	if (!(blanket = firstborn))
+		return;
+
+	for (; (next = blanket->next); blanket = next)
+		if (!memcmp(&blanket->address, &next->address,
+			    sizeof(next->address))) {
+			kill(blanket->pid, SIGTERM);
+			break;
+		}
 }
 
 static void check_dead_children(void)
 {
-	unsigned spawned, reaped, deleted;
+	int status;
+	pid_t pid;
 
-	spawned = children_spawned;
-	reaped = children_reaped;
-	deleted = children_deleted;
-
-	while (deleted < reaped) {
-		pid_t pid = dead_child[deleted % MAX_CHILDREN];
-		const char *dead = pid < 0 ? " (with error)" : "";
-
-		if (pid < 0)
-			pid = -pid;
-
-		/* XXX: Custom logging, since we don't wanna getpid() */
-		if (verbose) {
-			if (log_syslog)
-				syslog(LOG_INFO, "[%d] Disconnected%s",
-						pid, dead);
-			else
-				fprintf(stderr, "[%d] Disconnected%s\n",
-						pid, dead);
-		}
-		remove_child(pid, deleted, spawned);
-		deleted++;
-	}
-	children_deleted = deleted;
-}
-
-static void check_max_connections(void)
-{
-	for (;;) {
-		int active;
-		unsigned spawned, deleted;
-
-		check_dead_children();
-
-		spawned = children_spawned;
-		deleted = children_deleted;
-
-		active = spawned - deleted;
-		if (active <= max_connections)
-			break;
-
-		/* Kill some unstarted connections with SIGTERM */
-		kill_some_children(SIGTERM, deleted, spawned);
-		if (active <= max_connections << 1)
-			break;
-
-		/* If the SIGTERM thing isn't helping use SIGKILL */
-		kill_some_children(SIGKILL, deleted, spawned);
-		sleep(1);
+	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
+		const char *dead = "";
+		remove_child(pid);
+		if (!WIFEXITED(status) || (WEXITSTATUS(status) > 0))
+			dead = " (with error)";
+		loginfo("[%d] Disconnected%s", (int)pid, dead);
 	}
 }
 
 static void handle(int incoming, struct sockaddr *addr, int addrlen)
 {
-	pid_t pid = fork();
+	pid_t pid;
 
-	if (pid) {
-		unsigned idx;
-
-		close(incoming);
-		if (pid < 0)
+	if (max_connections && live_children >= max_connections) {
+		kill_some_child();
+		sleep(1);  /* give it some time to die */
+		check_dead_children();
+		if (live_children >= max_connections) {
+			close(incoming);
+			logerror("Too many children, dropping connection");
 			return;
+		}
+	}
 
-		idx = children_spawned % MAX_CHILDREN;
-		children_spawned++;
-		add_child(idx, pid, addr, addrlen);
+	if ((pid = fork())) {
+		close(incoming);
+		if (pid < 0) {
+			logerror("Couldn't fork %s", strerror(errno));
+			return;
+		}
 
-		check_max_connections();
+		add_child(pid, addr, addrlen);
 		return;
 	}
 
@@ -779,21 +697,11 @@
 
 static void child_handler(int signo)
 {
-	for (;;) {
-		int status;
-		pid_t pid = waitpid(-1, &status, WNOHANG);
-
-		if (pid > 0) {
-			unsigned reaped = children_reaped;
-			if (!WIFEXITED(status) || WEXITSTATUS(status) > 0)
-				pid = -pid;
-			dead_child[reaped % MAX_CHILDREN] = pid;
-			children_reaped = reaped + 1;
-			write(child_handler_pipe[1], &status, 1);
-			continue;
-		}
-		break;
-	}
+	/*
+	 * Otherwise empty handler because systemcalls will get interrupted
+	 * upon signal receipt
+	 * SysV needs the handler to be rearmed
+	 */
 	signal(SIGCHLD, child_handler);
 }
 
@@ -836,7 +744,7 @@
 		if (sockfd < 0)
 			continue;
 		if (sockfd >= FD_SETSIZE) {
-			error("too large socket descriptor.");
+			logerror("Socket descriptor too large");
 			close(sockfd);
 			continue;
 		}
@@ -936,35 +844,28 @@
 	struct pollfd *pfd;
 	int i;
 
-	if (pipe(child_handler_pipe) < 0)
-		die ("Could not set up pipe for child handler");
-
-	pfd = xcalloc(socknum + 1, sizeof(struct pollfd));
+	pfd = xcalloc(socknum, sizeof(struct pollfd));
 
 	for (i = 0; i < socknum; i++) {
 		pfd[i].fd = socklist[i];
 		pfd[i].events = POLLIN;
 	}
-	pfd[socknum].fd = child_handler_pipe[0];
-	pfd[socknum].events = POLLIN;
 
 	signal(SIGCHLD, child_handler);
 
 	for (;;) {
 		int i;
 
-		if (poll(pfd, socknum + 1, -1) < 0) {
+		check_dead_children();
+
+		if (poll(pfd, socknum, -1) < 0) {
 			if (errno != EINTR) {
-				error("poll failed, resuming: %s",
+				logerror("Poll failed, resuming: %s",
 				      strerror(errno));
 				sleep(1);
 			}
 			continue;
 		}
-		if (pfd[socknum].revents & POLLIN) {
-			read(child_handler_pipe[0], &i, 1);
-			check_dead_children();
-		}
 
 		for (i = 0; i < socknum; i++) {
 			if (pfd[i].revents & POLLIN) {
@@ -1055,11 +956,6 @@
 	gid_t gid = 0;
 	int i;
 
-	/* Without this we cannot rely on waitpid() to tell
-	 * what happened to our children.
-	 */
-	signal(SIGCHLD, SIG_DFL);
-
 	for (i = 1; i < argc; i++) {
 		char *arg = argv[i];
 
@@ -1105,6 +1001,12 @@
 			init_timeout = atoi(arg+15);
 			continue;
 		}
+		if (!prefixcmp(arg, "--max-connections=")) {
+			max_connections = atoi(arg+18);
+			if (max_connections < 0)
+				max_connections = 0;	        /* unlimited */
+			continue;
+		}
 		if (!strcmp(arg, "--strict-paths")) {
 			strict_paths = 1;
 			continue;
@@ -1178,9 +1080,10 @@
 	}
 
 	if (log_syslog) {
-		openlog("git-daemon", 0, LOG_DAEMON);
+		openlog("git-daemon", LOG_PID, LOG_DAEMON);
 		set_die_routine(daemon_die);
-	}
+	} else
+		setlinebuf(stderr); /* avoid splitting a message in the middle */
 
 	if (inetd_mode && (group_name || user_name))
 		die("--user and --group are incompatible with --inetd");
@@ -1233,8 +1136,10 @@
 		return execute(peer);
 	}
 
-	if (detach)
+	if (detach) {
 		daemonize();
+		loginfo("Ready to rumble");
+	}
 	else
 		sanitize_stdfds();
 
diff --git a/diff.c b/diff.c
index 7b4300a..135dec4 100644
--- a/diff.c
+++ b/diff.c
@@ -20,6 +20,7 @@
 
 static int diff_detect_rename_default;
 static int diff_rename_limit_default = 200;
+static int diff_suppress_blank_empty;
 int diff_use_color_default = -1;
 static const char *external_diff_cmd_cfg;
 int diff_auto_refresh_index = 1;
@@ -176,6 +177,12 @@
 		return 0;
 	}
 
+	/* like GNU diff's --suppress-blank-empty option  */
+	if (!strcmp(var, "diff.suppress-blank-empty")) {
+		diff_suppress_blank_empty = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (!prefixcmp(var, "diff.")) {
 		const char *ep = strrchr(var, '.');
 		if (ep != var + 4) {
@@ -369,7 +376,6 @@
 }
 
 struct diff_words_data {
-	struct xdiff_emit_state xm;
 	struct diff_words_buffer minus, plus;
 	FILE *file;
 };
@@ -459,11 +465,8 @@
 
 	xpp.flags = XDF_NEED_MINIMAL;
 	xecfg.ctxlen = diff_words->minus.alloc + diff_words->plus.alloc;
-	ecb.outf = xdiff_outf;
-	ecb.priv = diff_words;
-	diff_words->xm.consume = fn_out_diff_words_aux;
-	xdi_diff(&minus, &plus, &xpp, &xecfg, &ecb);
-
+	xdi_diff_outf(&minus, &plus, fn_out_diff_words_aux, diff_words,
+		      &xpp, &xecfg, &ecb);
 	free(minus.ptr);
 	free(plus.ptr);
 	diff_words->minus.text.size = diff_words->plus.text.size = 0;
@@ -477,7 +480,6 @@
 typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
 
 struct emit_callback {
-	struct xdiff_emit_state xm;
 	int nparents, color_diff;
 	unsigned ws_rule;
 	sane_truncate_fn truncate;
@@ -580,6 +582,12 @@
 		ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
 	}
 
+	if (diff_suppress_blank_empty
+	    && len == 2 && line[0] == ' ' && line[1] == '\n') {
+		line[0] = '\n';
+		len = 1;
+	}
+
 	/* This is not really necessary for now because
 	 * this codepath only deals with two-way diffs.
 	 */
@@ -708,8 +716,6 @@
 }
 
 struct diffstat_t {
-	struct xdiff_emit_state xm;
-
 	int nr;
 	int alloc;
 	struct diffstat_file {
@@ -1139,7 +1145,6 @@
 }
 
 struct checkdiff_t {
-	struct xdiff_emit_state xm;
 	const char *filename;
 	int lineno;
 	struct diff_options *o;
@@ -1384,6 +1389,8 @@
 	const char *name;
 	const char *pattern;
 } builtin_funcname_pattern[] = {
+	{ "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
+	{ "html", "^\\s*\\(<[Hh][1-6]\\s.*>.*\\)$" },
 	{ "java", "!^[ 	]*\\(catch\\|do\\|for\\|if\\|instanceof\\|"
 			"new\\|return\\|switch\\|throw\\|while\\)\n"
 			"^[ 	]*\\(\\([ 	]*"
@@ -1395,9 +1402,9 @@
 			"\\|"
 			"^\\(.*=[ \t]*\\(class\\|record\\).*\\)$"
 			},
-	{ "bibtex", "\\(@[a-zA-Z]\\{1,\\}[ \t]*{\\{0,1\\}[ \t]*[^ \t\"@',\\#}{~%]*\\).*$" },
-	{ "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
+	{ "python", "^\\s*\\(\\(class\\|def\\)\\s.*\\)$" },
 	{ "ruby", "^\\s*\\(\\(class\\|module\\|def\\)\\s.*\\)$" },
+	{ "tex", "^\\(\\\\\\(\\(sub\\)*section\\|chapter\\|part\\)\\*\\{0,1\\}{.*\\)$" },
 };
 
 static const char *diff_funcname_pattern(struct diff_filespec *one)
@@ -1529,15 +1536,13 @@
 			xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
 		else if (!prefixcmp(diffopts, "-u"))
 			xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
-		ecb.outf = xdiff_outf;
-		ecb.priv = &ecbdata;
-		ecbdata.xm.consume = fn_out_consume;
 		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS)) {
 			ecbdata.diff_words =
 				xcalloc(1, sizeof(struct diff_words_data));
 			ecbdata.diff_words->file = o->file;
 		}
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, fn_out_consume, &ecbdata,
+			      &xpp, &xecfg, &ecb);
 		if (DIFF_OPT_TST(o, COLOR_DIFF_WORDS))
 			free_diff_words_data(&ecbdata);
 	}
@@ -1588,9 +1593,8 @@
 
 		memset(&xecfg, 0, sizeof(xecfg));
 		xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
-		ecb.outf = xdiff_outf;
-		ecb.priv = diffstat;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, diffstat_consume, diffstat,
+			      &xpp, &xecfg, &ecb);
 	}
 
  free_and_return:
@@ -1611,7 +1615,6 @@
 		return;
 
 	memset(&data, 0, sizeof(data));
-	data.xm.consume = checkdiff_consume;
 	data.filename = name_b ? name_b : name_a;
 	data.lineno = 0;
 	data.o = o;
@@ -1637,9 +1640,8 @@
 		memset(&xecfg, 0, sizeof(xecfg));
 		xecfg.ctxlen = 1; /* at least one context line */
 		xpp.flags = XDF_NEED_MINIMAL;
-		ecb.outf = xdiff_outf;
-		ecb.priv = &data;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
+			      &xpp, &xecfg, &ecb);
 
 		if ((data.ws_rule & WS_TRAILING_SPACE) &&
 		    data.trailing_blanks_start) {
@@ -3027,7 +3029,6 @@
 }
 
 struct patch_id_t {
-	struct xdiff_emit_state xm;
 	SHA_CTX *ctx;
 	int patchlen;
 };
@@ -3072,7 +3073,6 @@
 	SHA1_Init(&ctx);
 	memset(&data, 0, sizeof(struct patch_id_t));
 	data.ctx = &ctx;
-	data.xm.consume = patch_id_consume;
 
 	for (i = 0; i < q->nr; i++) {
 		xpparam_t xpp;
@@ -3137,9 +3137,8 @@
 		xpp.flags = XDF_NEED_MINIMAL;
 		xecfg.ctxlen = 3;
 		xecfg.flags = XDL_EMIT_FUNCNAMES;
-		ecb.outf = xdiff_outf;
-		ecb.priv = &data;
-		xdi_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+		xdi_diff_outf(&mf1, &mf2, patch_id_consume, &data,
+			      &xpp, &xecfg, &ecb);
 	}
 
 	SHA1_Final(sha1, &ctx);
@@ -3216,7 +3215,6 @@
 		struct diffstat_t diffstat;
 
 		memset(&diffstat, 0, sizeof(struct diffstat_t));
-		diffstat.xm.consume = diffstat_consume;
 		for (i = 0; i < q->nr; i++) {
 			struct diff_filepair *p = q->queue[i];
 			if (check_pair_status(p))
diff --git a/dir.c b/dir.c
index 109e05b..acf1001 100644
--- a/dir.c
+++ b/dir.c
@@ -52,11 +52,6 @@
 	return prefix;
 }
 
-static inline int special_char(unsigned char c1)
-{
-	return !c1 || c1 == '*' || c1 == '[' || c1 == '?' || c1 == '\\';
-}
-
 /*
  * Does 'match' matches the given name?
  * A match is found if
@@ -80,7 +75,7 @@
 	for (;;) {
 		unsigned char c1 = *match;
 		unsigned char c2 = *name;
-		if (special_char(c1))
+		if (isspecial(c1))
 			break;
 		if (c1 != c2)
 			return 0;
@@ -680,17 +675,12 @@
  */
 static int simple_length(const char *match)
 {
-	const char special[256] = {
-		[0] = 1, ['?'] = 1,
-		['\\'] = 1, ['*'] = 1,
-		['['] = 1
-	};
 	int len = -1;
 
 	for (;;) {
 		unsigned char c = *match++;
 		len++;
-		if (special[c])
+		if (isspecial(c))
 			return len;
 	}
 }
@@ -727,8 +717,12 @@
 
 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
 {
-	struct path_simplify *simplify = create_simplify(pathspec);
+	struct path_simplify *simplify;
 
+	if (has_symlink_leading_path(strlen(path), path))
+		return dir->nr;
+
+	simplify = create_simplify(pathspec);
 	read_directory_recursive(dir, path, base, baselen, 0, simplify);
 	free_simplify(simplify);
 	qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
diff --git a/git-compat-util.h b/git-compat-util.h
index cf89cdf..db2836f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -99,6 +99,11 @@
 #include <iconv.h>
 #endif
 
+#ifndef NO_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#endif
+
 /* On most systems <limits.h> would have given us this, but
  * not on some systems (e.g. GNU/Hurd).
  */
@@ -192,6 +197,12 @@
 
 #endif /* NO_MMAP */
 
+#ifdef NO_ST_BLOCKS_IN_STRUCT_STAT
+#define on_disk_bytes(st) ((st).st_size)
+#else
+#define on_disk_bytes(st) ((st).st_blocks * 512)
+#endif
+
 #define DEFAULT_PACKED_GIT_LIMIT \
 	((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
 
@@ -318,11 +329,13 @@
 #define GIT_SPACE 0x01
 #define GIT_DIGIT 0x02
 #define GIT_ALPHA 0x04
+#define GIT_SPECIAL 0x08
 #define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
 #define isspace(x) sane_istest(x,GIT_SPACE)
 #define isdigit(x) sane_istest(x,GIT_DIGIT)
 #define isalpha(x) sane_istest(x,GIT_ALPHA)
 #define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
+#define isspecial(x) sane_istest(x,GIT_SPECIAL)
 #define tolower(x) sane_case((unsigned char)(x), 0x20)
 #define toupper(x) sane_case((unsigned char)(x), 0)
 
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index a324cf0..2871a59 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -416,15 +416,17 @@
 		echo "$ref -> $new_ref ($sha1 -> $new_sha1)"
 
 		if [ "$type" = "tag" ]; then
-			new_sha1=$(git cat-file tag "$ref" |
+			new_sha1=$( ( printf 'object %s\ntype commit\ntag %s\n' \
+						"$new_sha1" "$new_ref"
+				git cat-file tag "$ref" |
 				sed -n \
 				    -e "1,/^$/{
-					  s/^object .*/object $new_sha1/
-					  s/^type .*/type commit/
-					  s/^tag .*/tag $new_ref/
+					  /^object /d
+					  /^type /d
+					  /^tag /d
 					}" \
 				    -e '/^-----BEGIN PGP SIGNATURE-----/q' \
-				    -e 'p' |
+				    -e 'p' ) |
 				git mktag) ||
 				die "Could not create new tag object for $ref"
 			if git cat-file tag "$ref" | \
diff --git a/git-merge-octopus.sh b/git-merge-octopus.sh
index 645e114..1dadbb4 100755
--- a/git-merge-octopus.sh
+++ b/git-merge-octopus.sh
@@ -61,7 +61,7 @@
 		exit 2
 	esac
 
-	common=$(git merge-base --all $MRC $SHA1) ||
+	common=$(git merge-base --all $SHA1 $MRC) ||
 		die "Unable to find common commit with $SHA1"
 
 	case "$LF$common$LF" in
@@ -100,14 +100,7 @@
 		next=$(git write-tree 2>/dev/null)
 	fi
 
-	# We have merged the other branch successfully.  Ideally
-	# we could implement OR'ed heads in merge-base, and keep
-	# a list of commits we have merged so far in MRC to feed
-	# them to merge-base, but we approximate it by keep using
-	# the current MRC.  We used to update it to $common, which
-	# was incorrectly doing AND'ed merge-base here, which was
-	# unneeded.
-
+	MRC="$MRC $SHA1"
 	MRT=$next
 done
 
diff --git a/git-submodule.sh b/git-submodule.sh
index b40f876..1c39b59 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -6,9 +6,10 @@
 
 USAGE="[--quiet] [--cached] \
 [add <repo> [-b branch] <path>]|[status|init|update [-i|--init]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]"
+[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
 OPTIONS_SPEC=
 . git-sh-setup
+. git-parse-remote
 require_work_tree
 
 command=
@@ -30,12 +31,11 @@
 # Resolve relative url by appending to parent's url
 resolve_relative_url ()
 {
-	branch="$(git symbolic-ref HEAD 2>/dev/null)"
-	remote="$(git config branch.${branch#refs/heads/}.remote)"
-	remote="${remote:-origin}"
+	remote=$(get_default_remote)
 	remoteurl=$(git config "remote.$remote.url") ||
 		die "remote ($remote) does not have a url defined in .git/config"
 	url="$1"
+	remoteurl=${remoteurl%/}
 	while test -n "$url"
 	do
 		case "$url" in
@@ -50,7 +50,16 @@
 			break;;
 		esac
 	done
-	echo "$remoteurl/$url"
+	echo "$remoteurl/${url%/}"
+}
+
+#
+# Get submodule info for registered submodules
+# $@ = path to limit submodule list
+#
+module_list()
+{
+	git ls-files --stage -- "$@" | grep '^160000 '
 }
 
 #
@@ -199,6 +208,26 @@
 }
 
 #
+# Execute an arbitrary command sequence in each checked out
+# submodule
+#
+# $@ = command to execute
+#
+cmd_foreach()
+{
+	module_list |
+	while read mode sha1 stage path
+	do
+		if test -e "$path"/.git
+		then
+			say "Entering '$path'"
+			(cd "$path" && eval "$@") ||
+			die "Stopping at '$path'; script returned non-zero status."
+		fi
+	done
+}
+
+#
 # Register submodules in .git/config
 #
 # $@ = requested paths (default to all)
@@ -226,7 +255,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		# Skip already registered paths
@@ -284,7 +313,7 @@
 		esac
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -549,7 +578,7 @@
 		shift
 	done
 
-	git ls-files --stage -- "$@" | grep '^160000 ' |
+	module_list "$@" |
 	while read mode sha1 stage path
 	do
 		name=$(module_name "$path") || exit
@@ -573,6 +602,50 @@
 		fi
 	done
 }
+#
+# Sync remote urls for submodules
+# This makes the value for remote.$remote.url match the value
+# specified in .gitmodules.
+#
+cmd_sync()
+{
+	while test $# -ne 0
+	do
+		case "$1" in
+		-q|--quiet)
+			quiet=1
+			shift
+			;;
+		--)
+			shift
+			break
+			;;
+		-*)
+			usage
+			;;
+		*)
+			break
+			;;
+		esac
+	done
+	cd_to_toplevel
+	module_list "$@" |
+	while read mode sha1 stage path
+	do
+		name=$(module_name "$path")
+		url=$(git config -f .gitmodules --get submodule."$name".url)
+		if test -e "$path"/.git
+		then
+		(
+			unset GIT_DIR
+			cd "$path"
+			remote=$(get_default_remote)
+			say "Synchronizing submodule url for '$name'"
+			git config remote."$remote".url "$url"
+		)
+		fi
+	done
+}
 
 # This loop parses the command line arguments to find the
 # subcommand name to dispatch.  Parsing of the subcommand specific
@@ -583,7 +656,7 @@
 while test $# != 0 && test -z "$command"
 do
 	case "$1" in
-	add | init | update | status | summary)
+	add | foreach | init | update | status | summary | sync)
 		command=$1
 		;;
 	-q|--quiet)
diff --git a/hash-object.c b/hash-object.c
index 46c06a9..a4d127c 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -7,16 +7,14 @@
 #include "cache.h"
 #include "blob.h"
 #include "quote.h"
+#include "parse-options.h"
 
-static void hash_object(const char *path, enum object_type type, int write_object)
+static void hash_fd(int fd, const char *type, int write_object, const char *path)
 {
-	int fd;
 	struct stat st;
 	unsigned char sha1[20];
-	fd = open(path, O_RDONLY);
-	if (fd < 0 ||
-	    fstat(fd, &st) < 0 ||
-	    index_fd(sha1, fd, &st, write_object, type, path))
+	if (fstat(fd, &st) < 0 ||
+	    index_fd(sha1, fd, &st, write_object, type_from_string(type), path))
 		die(write_object
 		    ? "Unable to add %s to database"
 		    : "Unable to hash %s", path);
@@ -24,12 +22,14 @@
 	maybe_flush_or_die(stdout, "hash to stdout");
 }
 
-static void hash_stdin(const char *type, int write_object)
+static void hash_object(const char *path, const char *type, int write_object,
+			const char *vpath)
 {
-	unsigned char sha1[20];
-	if (index_pipe(sha1, 0, type, write_object))
-		die("Unable to add stdin to database");
-	printf("%s\n", sha1_to_hex(sha1));
+	int fd;
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		die("Cannot open %s", path);
+	hash_fd(fd, type, write_object, vpath);
 }
 
 static void hash_stdin_paths(const char *type, int write_objects)
@@ -45,92 +45,91 @@
 				die("line is badly quoted");
 			strbuf_swap(&buf, &nbuf);
 		}
-		hash_object(buf.buf, type_from_string(type), write_objects);
+		hash_object(buf.buf, type, write_objects, buf.buf);
 	}
 	strbuf_release(&buf);
 	strbuf_release(&nbuf);
 }
 
-static const char hash_object_usage[] =
-"git hash-object [ [-t <type>] [-w] [--stdin] <file>... | --stdin-paths < <list-of-paths> ]";
+static const char * const hash_object_usage[] = {
+	"git hash-object [-t <type>] [-w] [--path=<file>|--no-filters] [--stdin] [--] <file>...",
+	"git hash-object  --stdin-paths < <list-of-paths>",
+	NULL
+};
 
-int main(int argc, char **argv)
+static const char *type;
+static int write_object;
+static int hashstdin;
+static int stdin_paths;
+static int no_filters;
+static const char *vpath;
+
+static const struct option hash_object_options[] = {
+	OPT_STRING('t', NULL, &type, "type", "object type"),
+	OPT_BOOLEAN('w', NULL, &write_object, "write the object into the object database"),
+	OPT_BOOLEAN( 0 , "stdin", &hashstdin, "read the object from stdin"),
+	OPT_BOOLEAN( 0 , "stdin-paths", &stdin_paths, "read file names from stdin"),
+	OPT_BOOLEAN( 0 , "no-filters", &no_filters, "store file as is without filters"),
+	OPT_STRING( 0 , "path", &vpath, "file", "process file as it were from this path"),
+	OPT_END()
+};
+
+int main(int argc, const char **argv)
 {
 	int i;
-	const char *type = blob_type;
-	int write_object = 0;
 	const char *prefix = NULL;
 	int prefix_length = -1;
-	int no_more_flags = 0;
-	int hashstdin = 0;
-	int stdin_paths = 0;
+	const char *errstr = NULL;
+
+	type = blob_type;
 
 	git_config(git_default_config, NULL);
 
-	for (i = 1 ; i < argc; i++) {
-		if (!no_more_flags && argv[i][0] == '-') {
-			if (!strcmp(argv[i], "-t")) {
-				if (argc <= ++i)
-					usage(hash_object_usage);
-				type = argv[i];
-			}
-			else if (!strcmp(argv[i], "-w")) {
-				if (prefix_length < 0) {
-					prefix = setup_git_directory();
-					prefix_length =
-						prefix ? strlen(prefix) : 0;
-				}
-				write_object = 1;
-			}
-			else if (!strcmp(argv[i], "--")) {
-				no_more_flags = 1;
-			}
-			else if (!strcmp(argv[i], "--help"))
-				usage(hash_object_usage);
-			else if (!strcmp(argv[i], "--stdin-paths")) {
-				if (hashstdin) {
-					error("Can't use --stdin-paths with --stdin");
-					usage(hash_object_usage);
-				}
-				stdin_paths = 1;
+	argc = parse_options(argc, argv, hash_object_options, hash_object_usage, 0);
 
-			}
-			else if (!strcmp(argv[i], "--stdin")) {
-				if (stdin_paths) {
-					error("Can't use %s with --stdin-paths", argv[i]);
-					usage(hash_object_usage);
-				}
-				if (hashstdin)
-					die("Multiple --stdin arguments are not supported");
-				hashstdin = 1;
-			}
-			else
-				usage(hash_object_usage);
-		}
-		else {
-			const char *arg = argv[i];
+	if (write_object) {
+		prefix = setup_git_directory();
+		prefix_length = prefix ? strlen(prefix) : 0;
+		if (vpath && prefix)
+			vpath = prefix_filename(prefix, prefix_length, vpath);
+	}
 
-			if (stdin_paths) {
-				error("Can't specify files (such as \"%s\") with --stdin-paths", arg);
-				usage(hash_object_usage);
-			}
+	if (stdin_paths) {
+		if (hashstdin)
+			errstr = "Can't use --stdin-paths with --stdin";
+		else if (argc)
+			errstr = "Can't specify files with --stdin-paths";
+		else if (vpath)
+			errstr = "Can't use --stdin-paths with --path";
+		else if (no_filters)
+			errstr = "Can't use --stdin-paths with --no-filters";
+	}
+	else {
+		if (hashstdin > 1)
+			errstr = "Multiple --stdin arguments are not supported";
+		if (vpath && no_filters)
+			errstr = "Can't use --path with --no-filters";
+	}
 
-			if (hashstdin) {
-				hash_stdin(type, write_object);
-				hashstdin = 0;
-			}
-			if (0 <= prefix_length)
-				arg = prefix_filename(prefix, prefix_length,
-						      arg);
-			hash_object(arg, type_from_string(type), write_object);
-			no_more_flags = 1;
-		}
+	if (errstr) {
+		error (errstr);
+		usage_with_options(hash_object_usage, hash_object_options);
+	}
+
+	if (hashstdin)
+		hash_fd(0, type, write_object, vpath);
+
+	for (i = 0 ; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (0 <= prefix_length)
+			arg = prefix_filename(prefix, prefix_length, arg);
+		hash_object(arg, type, write_object,
+			    no_filters ? NULL : vpath ? vpath : arg);
 	}
 
 	if (stdin_paths)
 		hash_stdin_paths(type, write_object);
 
-	if (hashstdin)
-		hash_stdin(type, write_object);
 	return 0;
 }
diff --git a/help.c b/help.c
index dc0786d..b278257 100644
--- a/help.c
+++ b/help.c
@@ -1,276 +1,7 @@
-/*
- * builtin-help.c
- *
- * Builtin help-related commands (help, usage, version)
- */
 #include "cache.h"
 #include "builtin.h"
 #include "exec_cmd.h"
-#include "common-cmds.h"
-#include "parse-options.h"
-#include "run-command.h"
-
-static struct man_viewer_list {
-	struct man_viewer_list *next;
-	char name[FLEX_ARRAY];
-} *man_viewer_list;
-
-static struct man_viewer_info_list {
-	struct man_viewer_info_list *next;
-	const char *info;
-	char name[FLEX_ARRAY];
-} *man_viewer_info_list;
-
-enum help_format {
-	HELP_FORMAT_MAN,
-	HELP_FORMAT_INFO,
-	HELP_FORMAT_WEB,
-};
-
-static int show_all = 0;
-static enum help_format help_format = HELP_FORMAT_MAN;
-static struct option builtin_help_options[] = {
-	OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
-	OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
-	OPT_SET_INT('w', "web", &help_format, "show manual in web browser",
-			HELP_FORMAT_WEB),
-	OPT_SET_INT('i', "info", &help_format, "show info page",
-			HELP_FORMAT_INFO),
-	OPT_END(),
-};
-
-static const char * const builtin_help_usage[] = {
-	"git help [--all] [--man|--web|--info] [command]",
-	NULL
-};
-
-static enum help_format parse_help_format(const char *format)
-{
-	if (!strcmp(format, "man"))
-		return HELP_FORMAT_MAN;
-	if (!strcmp(format, "info"))
-		return HELP_FORMAT_INFO;
-	if (!strcmp(format, "web") || !strcmp(format, "html"))
-		return HELP_FORMAT_WEB;
-	die("unrecognized help format '%s'", format);
-}
-
-static const char *get_man_viewer_info(const char *name)
-{
-	struct man_viewer_info_list *viewer;
-
-	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)
-	{
-		if (!strcasecmp(name, viewer->name))
-			return viewer->info;
-	}
-	return NULL;
-}
-
-static int check_emacsclient_version(void)
-{
-	struct strbuf buffer = STRBUF_INIT;
-	struct child_process ec_process;
-	const char *argv_ec[] = { "emacsclient", "--version", NULL };
-	int version;
-
-	/* emacsclient prints its version number on stderr */
-	memset(&ec_process, 0, sizeof(ec_process));
-	ec_process.argv = argv_ec;
-	ec_process.err = -1;
-	ec_process.stdout_to_stderr = 1;
-	if (start_command(&ec_process)) {
-		fprintf(stderr, "Failed to start emacsclient.\n");
-		return -1;
-	}
-	strbuf_read(&buffer, ec_process.err, 20);
-	close(ec_process.err);
-
-	/*
-	 * Don't bother checking return value, because "emacsclient --version"
-	 * seems to always exits with code 1.
-	 */
-	finish_command(&ec_process);
-
-	if (prefixcmp(buffer.buf, "emacsclient")) {
-		fprintf(stderr, "Failed to parse emacsclient version.\n");
-		strbuf_release(&buffer);
-		return -1;
-	}
-
-	strbuf_remove(&buffer, 0, strlen("emacsclient"));
-	version = atoi(buffer.buf);
-
-	if (version < 22) {
-		fprintf(stderr,
-			"emacsclient version '%d' too old (< 22).\n",
-			version);
-		strbuf_release(&buffer);
-		return -1;
-	}
-
-	strbuf_release(&buffer);
-	return 0;
-}
-
-static void exec_woman_emacs(const char* path, const char *page)
-{
-	if (!check_emacsclient_version()) {
-		/* This works only with emacsclient version >= 22. */
-		struct strbuf man_page = STRBUF_INIT;
-
-		if (!path)
-			path = "emacsclient";
-		strbuf_addf(&man_page, "(woman \"%s\")", page);
-		execlp(path, "emacsclient", "-e", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_konqueror(const char* path, const char *page)
-{
-	const char *display = getenv("DISPLAY");
-	if (display && *display) {
-		struct strbuf man_page = STRBUF_INIT;
-		const char *filename = "kfmclient";
-
-		/* It's simpler to launch konqueror using kfmclient. */
-		if (path) {
-			const char *file = strrchr(path, '/');
-			if (file && !strcmp(file + 1, "konqueror")) {
-				char *new = xstrdup(path);
-				char *dest = strrchr(new, '/');
-
-				/* strlen("konqueror") == strlen("kfmclient") */
-				strcpy(dest + 1, "kfmclient");
-				path = new;
-			}
-			if (file)
-				filename = file;
-		} else
-			path = "kfmclient";
-		strbuf_addf(&man_page, "man:%s(1)", page);
-		execlp(path, filename, "newTab", man_page.buf, NULL);
-		warning("failed to exec '%s': %s", path, strerror(errno));
-	}
-}
-
-static void exec_man_man(const char* path, const char *page)
-{
-	if (!path)
-		path = "man";
-	execlp(path, "man", page, NULL);
-	warning("failed to exec '%s': %s", path, strerror(errno));
-}
-
-static void exec_man_cmd(const char *cmd, const char *page)
-{
-	struct strbuf shell_cmd = STRBUF_INIT;
-	strbuf_addf(&shell_cmd, "%s %s", cmd, page);
-	execl("/bin/sh", "sh", "-c", shell_cmd.buf, NULL);
-	warning("failed to exec '%s': %s", cmd, strerror(errno));
-}
-
-static void add_man_viewer(const char *name)
-{
-	struct man_viewer_list **p = &man_viewer_list;
-	size_t len = strlen(name);
-
-	while (*p)
-		p = &((*p)->next);
-	*p = xcalloc(1, (sizeof(**p) + len + 1));
-	strncpy((*p)->name, name, len);
-}
-
-static int supported_man_viewer(const char *name, size_t len)
-{
-	return (!strncasecmp("man", name, len) ||
-		!strncasecmp("woman", name, len) ||
-		!strncasecmp("konqueror", name, len));
-}
-
-static void do_add_man_viewer_info(const char *name,
-				   size_t len,
-				   const char *value)
-{
-	struct man_viewer_info_list *new = xcalloc(1, sizeof(*new) + len + 1);
-
-	strncpy(new->name, name, len);
-	new->info = xstrdup(value);
-	new->next = man_viewer_info_list;
-	man_viewer_info_list = new;
-}
-
-static int add_man_viewer_path(const char *name,
-			       size_t len,
-			       const char *value)
-{
-	if (supported_man_viewer(name, len))
-		do_add_man_viewer_info(name, len, value);
-	else
-		warning("'%s': path for unsupported man viewer.\n"
-			"Please consider using 'man.<tool>.cmd' instead.",
-			name);
-
-	return 0;
-}
-
-static int add_man_viewer_cmd(const char *name,
-			      size_t len,
-			      const char *value)
-{
-	if (supported_man_viewer(name, len))
-		warning("'%s': cmd for supported man viewer.\n"
-			"Please consider using 'man.<tool>.path' instead.",
-			name);
-	else
-		do_add_man_viewer_info(name, len, value);
-
-	return 0;
-}
-
-static int add_man_viewer_info(const char *var, const char *value)
-{
-	const char *name = var + 4;
-	const char *subkey = strrchr(name, '.');
-
-	if (!subkey)
-		return error("Config with no key for man viewer: %s", name);
-
-	if (!strcmp(subkey, ".path")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_path(name, subkey - name, value);
-	}
-	if (!strcmp(subkey, ".cmd")) {
-		if (!value)
-			return config_error_nonbool(var);
-		return add_man_viewer_cmd(name, subkey - name, value);
-	}
-
-	warning("'%s': unsupported man viewer sub key.", subkey);
-	return 0;
-}
-
-static int git_help_config(const char *var, const char *value, void *cb)
-{
-	if (!strcmp(var, "help.format")) {
-		if (!value)
-			return config_error_nonbool(var);
-		help_format = parse_help_format(value);
-		return 0;
-	}
-	if (!strcmp(var, "man.viewer")) {
-		if (!value)
-			return config_error_nonbool(var);
-		add_man_viewer(value);
-		return 0;
-	}
-	if (!prefixcmp(var, "man."))
-		return add_man_viewer_info(var, value);
-
-	return git_default_config(var, value, cb);
-}
+#include "help.h"
 
 /* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
@@ -294,24 +25,9 @@
 	return 80;
 }
 
-static inline void mput_char(char c, unsigned int num)
+void add_cmdname(struct cmdnames *cmds, const char *name, int len)
 {
-	while(num--)
-		putchar(c);
-}
-
-static struct cmdnames {
-	int alloc;
-	int cnt;
-	struct cmdname {
-		size_t len;
-		char name[1];
-	} **names;
-} main_cmds, other_cmds;
-
-static void add_cmdname(struct cmdnames *cmds, const char *name, int len)
-{
-	struct cmdname *ent = xmalloc(sizeof(*ent) + len);
+	struct cmdname *ent = xmalloc(sizeof(*ent) + len + 1);
 
 	ent->len = len;
 	memcpy(ent->name, name, len);
@@ -342,7 +58,7 @@
 	cmds->cnt = j;
 }
 
-static void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes)
 {
 	int ci, cj, ei;
 	int cmp;
@@ -417,19 +133,21 @@
 	return st.st_mode & S_IXUSR;
 }
 
-static unsigned int list_commands_in_dir(struct cmdnames *cmds,
-					 const char *path)
+static void list_commands_in_dir(struct cmdnames *cmds,
+					 const char *path,
+					 const char *prefix)
 {
-	unsigned int longest = 0;
-	const char *prefix = "git-";
-	int prefix_len = strlen(prefix);
+	int prefix_len;
 	DIR *dir = opendir(path);
 	struct dirent *de;
 	struct strbuf buf = STRBUF_INIT;
 	int len;
 
 	if (!dir)
-		return 0;
+		return;
+	if (!prefix)
+		prefix = "git-";
+	prefix_len = strlen(prefix);
 
 	strbuf_addf(&buf, "%s/", path);
 	len = buf.len;
@@ -449,100 +167,81 @@
 		if (has_extension(de->d_name, ".exe"))
 			entlen -= 4;
 
-		if (longest < entlen)
-			longest = entlen;
-
 		add_cmdname(cmds, de->d_name + prefix_len, entlen);
 	}
 	closedir(dir);
 	strbuf_release(&buf);
-
-	return longest;
 }
 
-static unsigned int load_command_list(void)
+void load_command_list(const char *prefix,
+		struct cmdnames *main_cmds,
+		struct cmdnames *other_cmds)
 {
-	unsigned int longest = 0;
-	unsigned int len;
 	const char *env_path = getenv("PATH");
-	char *paths, *path, *colon;
 	const char *exec_path = git_exec_path();
 
-	if (exec_path)
-		longest = list_commands_in_dir(&main_cmds, exec_path);
-
-	if (!env_path) {
-		fprintf(stderr, "PATH not set\n");
-		exit(1);
+	if (exec_path) {
+		list_commands_in_dir(main_cmds, exec_path, prefix);
+		qsort(main_cmds->names, main_cmds->cnt,
+		      sizeof(*main_cmds->names), cmdname_compare);
+		uniq(main_cmds);
 	}
 
-	path = paths = xstrdup(env_path);
-	while (1) {
-		if ((colon = strchr(path, PATH_SEP)))
-			*colon = 0;
+	if (env_path) {
+		char *paths, *path, *colon;
+		path = paths = xstrdup(env_path);
+		while (1) {
+			if ((colon = strchr(path, PATH_SEP)))
+				*colon = 0;
 
-		len = list_commands_in_dir(&other_cmds, path);
-		if (len > longest)
-			longest = len;
+			list_commands_in_dir(other_cmds, path, prefix);
 
-		if (!colon)
-			break;
-		path = colon + 1;
+			if (!colon)
+				break;
+			path = colon + 1;
+		}
+		free(paths);
+
+		qsort(other_cmds->names, other_cmds->cnt,
+		      sizeof(*other_cmds->names), cmdname_compare);
+		uniq(other_cmds);
 	}
-	free(paths);
-
-	qsort(main_cmds.names, main_cmds.cnt,
-	      sizeof(*main_cmds.names), cmdname_compare);
-	uniq(&main_cmds);
-
-	qsort(other_cmds.names, other_cmds.cnt,
-	      sizeof(*other_cmds.names), cmdname_compare);
-	uniq(&other_cmds);
-	exclude_cmds(&other_cmds, &main_cmds);
-
-	return longest;
+	exclude_cmds(other_cmds, main_cmds);
 }
 
-static void list_commands(void)
-{
-	unsigned int longest = load_command_list();
-	const char *exec_path = git_exec_path();
-
-	if (main_cmds.cnt) {
-		printf("available git commands in '%s'\n", exec_path);
-		printf("----------------------------");
-		mput_char('-', strlen(exec_path));
-		putchar('\n');
-		pretty_print_string_list(&main_cmds, longest);
-		putchar('\n');
-	}
-
-	if (other_cmds.cnt) {
-		printf("git commands available from elsewhere on your $PATH\n");
-		printf("---------------------------------------------------\n");
-		pretty_print_string_list(&other_cmds, longest);
-		putchar('\n');
-	}
-}
-
-void list_common_cmds_help(void)
+void list_commands(const char *title, struct cmdnames *main_cmds,
+		   struct cmdnames *other_cmds)
 {
 	int i, longest = 0;
 
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		if (longest < strlen(common_cmds[i].name))
-			longest = strlen(common_cmds[i].name);
+	for (i = 0; i < main_cmds->cnt; i++)
+		if (longest < main_cmds->names[i]->len)
+			longest = main_cmds->names[i]->len;
+	for (i = 0; i < other_cmds->cnt; i++)
+		if (longest < other_cmds->names[i]->len)
+			longest = other_cmds->names[i]->len;
+
+	if (main_cmds->cnt) {
+		const char *exec_path = git_exec_path();
+		printf("available %s in '%s'\n", title, exec_path);
+		printf("----------------");
+		mput_char('-', strlen(title) + strlen(exec_path));
+		putchar('\n');
+		pretty_print_string_list(main_cmds, longest);
+		putchar('\n');
 	}
 
-	puts("The most commonly used git commands are:");
-	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {
-		printf("   %s   ", common_cmds[i].name);
-		mput_char(' ', longest - strlen(common_cmds[i].name));
-		puts(common_cmds[i].help);
+	if (other_cmds->cnt) {
+		printf("%s available from elsewhere on your $PATH\n", title);
+		printf("---------------------------------------");
+		mput_char('-', strlen(title));
+		putchar('\n');
+		pretty_print_string_list(other_cmds, longest);
+		putchar('\n');
 	}
 }
 
-static int is_in_cmdlist(struct cmdnames *c, const char *s)
+int is_in_cmdlist(struct cmdnames *c, const char *s)
 {
 	int i;
 	for (i = 0; i < c->cnt; i++)
@@ -551,130 +250,6 @@
 	return 0;
 }
 
-static int is_git_command(const char *s)
-{
-	load_command_list();
-	return is_in_cmdlist(&main_cmds, s) ||
-		is_in_cmdlist(&other_cmds, s) ||
-		!strcmp(s, "help");
-}
-
-static const char *prepend(const char *prefix, const char *cmd)
-{
-	size_t pre_len = strlen(prefix);
-	size_t cmd_len = strlen(cmd);
-	char *p = xmalloc(pre_len + cmd_len + 1);
-	memcpy(p, prefix, pre_len);
-	strcpy(p + pre_len, cmd);
-	return p;
-}
-
-static const char *cmd_to_page(const char *git_cmd)
-{
-	if (!git_cmd)
-		return "git";
-	else if (!prefixcmp(git_cmd, "git"))
-		return git_cmd;
-	else if (is_git_command(git_cmd))
-		return prepend("git-", git_cmd);
-	else
-		return prepend("git", git_cmd);
-}
-
-static void setup_man_path(void)
-{
-	struct strbuf new_path;
-	const char *old_path = getenv("MANPATH");
-
-	strbuf_init(&new_path, 0);
-
-	/* We should always put ':' after our path. If there is no
-	 * old_path, the ':' at the end will let 'man' to try
-	 * system-wide paths after ours to find the manual page. If
-	 * there is old_path, we need ':' as delimiter. */
-	strbuf_addstr(&new_path, GIT_MAN_PATH);
-	strbuf_addch(&new_path, ':');
-	if (old_path)
-		strbuf_addstr(&new_path, old_path);
-
-	setenv("MANPATH", new_path.buf, 1);
-
-	strbuf_release(&new_path);
-}
-
-static void exec_viewer(const char *name, const char *page)
-{
-	const char *info = get_man_viewer_info(name);
-
-	if (!strcasecmp(name, "man"))
-		exec_man_man(info, page);
-	else if (!strcasecmp(name, "woman"))
-		exec_woman_emacs(info, page);
-	else if (!strcasecmp(name, "konqueror"))
-		exec_man_konqueror(info, page);
-	else if (info)
-		exec_man_cmd(info, page);
-	else
-		warning("'%s': unknown man viewer.", name);
-}
-
-static void show_man_page(const char *git_cmd)
-{
-	struct man_viewer_list *viewer;
-	const char *page = cmd_to_page(git_cmd);
-
-	setup_man_path();
-	for (viewer = man_viewer_list; viewer; viewer = viewer->next)
-	{
-		exec_viewer(viewer->name, page); /* will return when unable */
-	}
-	exec_viewer("man", page);
-	die("no man viewer handled the request");
-}
-
-static void show_info_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	setenv("INFOPATH", GIT_INFO_PATH, 1);
-	execlp("info", "info", "gitman", page, NULL);
-}
-
-static void get_html_page_path(struct strbuf *page_path, const char *page)
-{
-	struct stat st;
-	const char *html_path = system_path(GIT_HTML_PATH);
-
-	/* Check that we have a git documentation directory. */
-	if (stat(mkpath("%s/git.html", html_path), &st)
-	    || !S_ISREG(st.st_mode))
-		die("'%s': not a documentation directory.", html_path);
-
-	strbuf_init(page_path, 0);
-	strbuf_addf(page_path, "%s/%s.html", html_path, page);
-}
-
-/*
- * If open_html is not defined in a platform-specific way (see for
- * example compat/mingw.h), we use the script web--browse to display
- * HTML.
- */
-#ifndef open_html
-void open_html(const char *path)
-{
-	execl_git_cmd("web--browse", "-c", "help.browser", path, NULL);
-}
-#endif
-
-static void show_html_page(const char *git_cmd)
-{
-	const char *page = cmd_to_page(git_cmd);
-	struct strbuf page_path; /* it leaks but we exec bellow */
-
-	get_html_page_path(&page_path, page);
-
-	open_html(page_path.buf);
-}
-
 void help_unknown_cmd(const char *cmd)
 {
 	fprintf(stderr, "git: '%s' is not a git-command. See 'git --help'.\n", cmd);
@@ -686,49 +261,3 @@
 	printf("git version %s\n", git_version_string);
 	return 0;
 }
-
-int cmd_help(int argc, const char **argv, const char *prefix)
-{
-	int nongit;
-	const char *alias;
-
-	setup_git_directory_gently(&nongit);
-	git_config(git_help_config, NULL);
-
-	argc = parse_options(argc, argv, builtin_help_options,
-			builtin_help_usage, 0);
-
-	if (show_all) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_commands();
-		printf("%s\n", git_more_info_string);
-		return 0;
-	}
-
-	if (!argv[0]) {
-		printf("usage: %s\n\n", git_usage_string);
-		list_common_cmds_help();
-		printf("\n%s\n", git_more_info_string);
-		return 0;
-	}
-
-	alias = alias_lookup(argv[0]);
-	if (alias && !is_git_command(argv[0])) {
-		printf("`git %s' is aliased to `%s'\n", argv[0], alias);
-		return 0;
-	}
-
-	switch (help_format) {
-	case HELP_FORMAT_MAN:
-		show_man_page(argv[0]);
-		break;
-	case HELP_FORMAT_INFO:
-		show_info_page(argv[0]);
-		break;
-	case HELP_FORMAT_WEB:
-		show_html_page(argv[0]);
-		break;
-	}
-
-	return 0;
-}
diff --git a/help.h b/help.h
new file mode 100644
index 0000000..2733433
--- /dev/null
+++ b/help.h
@@ -0,0 +1,29 @@
+#ifndef HELP_H
+#define HELP_H
+
+struct cmdnames {
+	int alloc;
+	int cnt;
+	struct cmdname {
+		size_t len;
+		char name[FLEX_ARRAY];
+	} **names;
+};
+
+static inline void mput_char(char c, unsigned int num)
+{
+	while(num--)
+		putchar(c);
+}
+
+void load_command_list(const char *prefix,
+		struct cmdnames *main_cmds,
+		struct cmdnames *other_cmds);
+void add_cmdname(struct cmdnames *cmds, const char *name, int len);
+/* Here we require that excludes is a sorted list. */
+void exclude_cmds(struct cmdnames *cmds, struct cmdnames *excludes);
+int is_in_cmdlist(struct cmdnames *c, const char *s);
+void list_commands(const char *title, struct cmdnames *main_cmds,
+		   struct cmdnames *other_cmds);
+
+#endif /* HELP_H */
diff --git a/imap-send.c b/imap-send.c
index 1ec1310..af7e08c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -23,71 +23,74 @@
  */
 
 #include "cache.h"
+#ifdef NO_OPENSSL
+typedef void *SSL;
+#endif
 
-typedef struct store_conf {
+struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
 	char *map_inbox;
 	char *trash;
 	unsigned max_size; /* off_t is overkill */
 	unsigned trash_remote_new:1, trash_only_new:1;
-} store_conf_t;
+};
 
-typedef struct string_list {
+struct string_list {
 	struct string_list *next;
 	char string[1];
-} string_list_t;
+};
 
-typedef struct channel_conf {
+struct channel_conf {
 	struct channel_conf *next;
 	char *name;
-	store_conf_t *master, *slave;
+	struct store_conf *master, *slave;
 	char *master_name, *slave_name;
 	char *sync_state;
-	string_list_t *patterns;
+	struct string_list *patterns;
 	int mops, sops;
 	unsigned max_messages; /* for slave only */
-} channel_conf_t;
+};
 
-typedef struct group_conf {
+struct group_conf {
 	struct group_conf *next;
 	char *name;
-	string_list_t *channels;
-} group_conf_t;
+	struct string_list *channels;
+};
 
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
 #define M_FLAGS        (1<<2) /* flags fetched */
 
-typedef struct message {
+struct message {
 	struct message *next;
-	/* string_list_t *keywords; */
+	/* struct string_list *keywords; */
 	size_t size; /* zero implies "not fetched" */
 	int uid;
 	unsigned char flags, status;
-} message_t;
+};
 
-typedef struct store {
-	store_conf_t *conf; /* foreign */
+struct store {
+	struct store_conf *conf; /* foreign */
 
 	/* currently open mailbox */
 	const char *name; /* foreign! maybe preset? */
 	char *path; /* own */
-	message_t *msgs; /* own */
+	struct message *msgs; /* own */
 	int uidvalidity;
 	unsigned char opts; /* maybe preset? */
 	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
 	int count; /* # of messages */
 	int recent; /* # of recent messages - don't trust this beyond the initial read */
-} store_t;
+};
 
-typedef struct {
+struct msg_data {
 	char *data;
 	int len;
 	unsigned char flags;
 	unsigned int crlf:1;
-} msg_data_t;
+};
 
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
@@ -96,14 +99,14 @@
 
 static int Verbose, Quiet;
 
-static void imap_info( const char *, ... );
-static void imap_warn( const char *, ... );
+static void imap_info(const char *, ...);
+static void imap_warn(const char *, ...);
 
-static char *next_arg( char ** );
+static char *next_arg(char **);
 
-static void free_generic_messages( message_t * );
+static void free_generic_messages(struct message *);
 
-static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 {
@@ -119,67 +122,70 @@
 	return len;
 }
 
-static void arc4_init( void );
-static unsigned char arc4_getbyte( void );
+static void arc4_init(void);
+static unsigned char arc4_getbyte(void);
 
-typedef struct imap_server_conf {
+struct imap_server_conf {
 	char *name;
 	char *tunnel;
 	char *host;
 	int port;
 	char *user;
 	char *pass;
-} imap_server_conf_t;
+	int use_ssl;
+	int ssl_verify;
+};
 
-typedef struct imap_store_conf {
-	store_conf_t gen;
-	imap_server_conf_t *server;
+struct imap_store_conf {
+	struct store_conf gen;
+	struct imap_server_conf *server;
 	unsigned use_namespace:1;
-} imap_store_conf_t;
+};
 
-#define NIL	(void*)0x1
-#define LIST	(void*)0x2
+#define NIL	(void *)0x1
+#define LIST	(void *)0x2
 
-typedef struct _list {
-	struct _list *next, *child;
+struct imap_list {
+	struct imap_list *next, *child;
 	char *val;
 	int len;
-} list_t;
+};
 
-typedef struct {
+struct imap_socket {
 	int fd;
-} Socket_t;
+	SSL *ssl;
+};
 
-typedef struct {
-	Socket_t sock;
+struct imap_buffer {
+	struct imap_socket sock;
 	int bytes;
 	int offset;
 	char buf[1024];
-} buffer_t;
+};
 
 struct imap_cmd;
 
-typedef struct imap {
+struct imap {
 	int uidnext; /* from SELECT responses */
-	list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
+	struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
 	unsigned caps, rcaps; /* CAPABILITY results */
 	/* command queue */
 	int nexttag, num_in_progress, literal_pending;
 	struct imap_cmd *in_progress, **in_progress_append;
-	buffer_t buf; /* this is BIG, so put it last */
-} imap_t;
+	struct imap_buffer buf; /* this is BIG, so put it last */
+};
 
-typedef struct imap_store {
-	store_t gen;
+struct imap_store {
+	struct store gen;
 	int uidvalidity;
-	imap_t *imap;
+	struct imap *imap;
 	const char *prefix;
 	unsigned /*currentnc:1,*/ trashnc:1;
-} imap_store_t;
+};
 
 struct imap_cmd_cb {
-	int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );
-	void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);
+	int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
+	void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
 	void *ctx;
 	char *data;
 	int dlen;
@@ -201,6 +207,7 @@
 	UIDPLUS,
 	LITERALPLUS,
 	NAMESPACE,
+	STARTTLS,
 };
 
 static const char *cap_list[] = {
@@ -208,13 +215,14 @@
 	"UIDPLUS",
 	"LITERAL+",
 	"NAMESPACE",
+	"STARTTLS",
 };
 
 #define RESP_OK    0
 #define RESP_NO    1
 #define RESP_BAD   2
 
-static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
 
 
 static const char *Flags[] = {
@@ -225,42 +233,137 @@
 	"Deleted",
 };
 
-static void
-socket_perror( const char *func, Socket_t *sock, int ret )
+#ifndef NO_OPENSSL
+static void ssl_socket_perror(const char *func)
 {
-	if (ret < 0)
-		perror( func );
+	fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
+}
+#endif
+
+static void socket_perror(const char *func, struct imap_socket *sock, int ret)
+{
+#ifndef NO_OPENSSL
+	if (sock->ssl) {
+		int sslerr = SSL_get_error(sock->ssl, ret);
+		switch (sslerr) {
+		case SSL_ERROR_NONE:
+			break;
+		case SSL_ERROR_SYSCALL:
+			perror("SSL_connect");
+			break;
+		default:
+			ssl_socket_perror("SSL_connect");
+			break;
+		}
+	} else
+#endif
+	{
+		if (ret < 0)
+			perror(func);
+		else
+			fprintf(stderr, "%s: unexpected EOF\n", func);
+	}
+}
+
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
+{
+#ifdef NO_OPENSSL
+	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+	return -1;
+#else
+	SSL_METHOD *meth;
+	SSL_CTX *ctx;
+	int ret;
+
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	if (use_tls_only)
+		meth = TLSv1_method();
 	else
-		fprintf( stderr, "%s: unexpected EOF\n", func );
+		meth = SSLv23_method();
+
+	if (!meth) {
+		ssl_socket_perror("SSLv23_method");
+		return -1;
+	}
+
+	ctx = SSL_CTX_new(meth);
+
+	if (verify)
+		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
+
+	if (!SSL_CTX_set_default_verify_paths(ctx)) {
+		ssl_socket_perror("SSL_CTX_set_default_verify_paths");
+		return -1;
+	}
+	sock->ssl = SSL_new(ctx);
+	if (!sock->ssl) {
+		ssl_socket_perror("SSL_new");
+		return -1;
+	}
+	if (!SSL_set_fd(sock->ssl, sock->fd)) {
+		ssl_socket_perror("SSL_set_fd");
+		return -1;
+	}
+
+	ret = SSL_connect(sock->ssl);
+	if (ret <= 0) {
+		socket_perror("SSL_connect", sock, ret);
+		return -1;
+	}
+
+	return 0;
+#endif
 }
 
-static int
-socket_read( Socket_t *sock, char *buf, int len )
+static int socket_read(struct imap_socket *sock, char *buf, int len)
 {
-	ssize_t n = xread( sock->fd, buf, len );
+	ssize_t n;
+#ifndef NO_OPENSSL
+	if (sock->ssl)
+		n = SSL_read(sock->ssl, buf, len);
+	else
+#endif
+		n = xread(sock->fd, buf, len);
 	if (n <= 0) {
-		socket_perror( "read", sock, n );
-		close( sock->fd );
+		socket_perror("read", sock, n);
+		close(sock->fd);
 		sock->fd = -1;
 	}
 	return n;
 }
 
-static int
-socket_write( Socket_t *sock, const char *buf, int len )
+static int socket_write(struct imap_socket *sock, const char *buf, int len)
 {
-	int n = write_in_full( sock->fd, buf, len );
+	int n;
+#ifndef NO_OPENSSL
+	if (sock->ssl)
+		n = SSL_write(sock->ssl, buf, len);
+	else
+#endif
+		n = write_in_full(sock->fd, buf, len);
 	if (n != len) {
-		socket_perror( "write", sock, n );
-		close( sock->fd );
+		socket_perror("write", sock, n);
+		close(sock->fd);
 		sock->fd = -1;
 	}
 	return n;
 }
 
+static void socket_shutdown(struct imap_socket *sock)
+{
+#ifndef NO_OPENSSL
+	if (sock->ssl) {
+		SSL_shutdown(sock->ssl);
+		SSL_free(sock->ssl);
+	}
+#endif
+	close(sock->fd);
+}
+
 /* simple line buffering */
-static int
-buffer_gets( buffer_t * b, char **s )
+static int buffer_gets(struct imap_buffer *b, char **s)
 {
 	int n;
 	int start = b->offset;
@@ -274,7 +377,7 @@
 				/* shift down used bytes */
 				*s = b->buf;
 
-				assert( start <= b->bytes );
+				assert(start <= b->bytes);
 				n = b->bytes - start;
 
 				if (n)
@@ -284,8 +387,8 @@
 				start = 0;
 			}
 
-			n = socket_read( &b->sock, b->buf + b->bytes,
-					 sizeof(b->buf) - b->bytes );
+			n = socket_read(&b->sock, b->buf + b->bytes,
+					 sizeof(b->buf) - b->bytes);
 
 			if (n <= 0)
 				return -1;
@@ -294,12 +397,12 @@
 		}
 
 		if (b->buf[b->offset] == '\r') {
-			assert( b->offset + 1 < b->bytes );
+			assert(b->offset + 1 < b->bytes);
 			if (b->buf[b->offset + 1] == '\n') {
 				b->buf[b->offset] = 0;  /* terminate the string */
 				b->offset += 2; /* next line */
 				if (Verbose)
-					puts( *s );
+					puts(*s);
 				return 0;
 			}
 		}
@@ -309,39 +412,36 @@
 	/* not reached */
 }
 
-static void
-imap_info( const char *msg, ... )
+static void imap_info(const char *msg, ...)
 {
 	va_list va;
 
 	if (!Quiet) {
-		va_start( va, msg );
-		vprintf( msg, va );
-		va_end( va );
-		fflush( stdout );
+		va_start(va, msg);
+		vprintf(msg, va);
+		va_end(va);
+		fflush(stdout);
 	}
 }
 
-static void
-imap_warn( const char *msg, ... )
+static void imap_warn(const char *msg, ...)
 {
 	va_list va;
 
 	if (Quiet < 2) {
-		va_start( va, msg );
-		vfprintf( stderr, msg, va );
-		va_end( va );
+		va_start(va, msg);
+		vfprintf(stderr, msg, va);
+		va_end(va);
 	}
 }
 
-static char *
-next_arg( char **s )
+static char *next_arg(char **s)
 {
 	char *ret;
 
 	if (!s || !*s)
 		return NULL;
-	while (isspace( (unsigned char) **s ))
+	while (isspace((unsigned char) **s))
 		(*s)++;
 	if (!**s) {
 		*s = NULL;
@@ -350,10 +450,10 @@
 	if (**s == '"') {
 		++*s;
 		ret = *s;
-		*s = strchr( *s, '"' );
+		*s = strchr(*s, '"');
 	} else {
 		ret = *s;
-		while (**s && !isspace( (unsigned char) **s ))
+		while (**s && !isspace((unsigned char) **s))
 			(*s)++;
 	}
 	if (*s) {
@@ -365,27 +465,25 @@
 	return ret;
 }
 
-static void
-free_generic_messages( message_t *msgs )
+static void free_generic_messages(struct message *msgs)
 {
-	message_t *tmsg;
+	struct message *tmsg;
 
 	for (; msgs; msgs = tmsg) {
 		tmsg = msgs->next;
-		free( msgs );
+		free(msgs);
 	}
 }
 
-static int
-nfsnprintf( char *buf, int blen, const char *fmt, ... )
+static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 {
 	int ret;
 	va_list va;
 
-	va_start( va, fmt );
-	if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
-		die( "Fatal: buffer too small. Please report a bug.\n");
-	va_end( va );
+	va_start(va, fmt);
+	if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
+		die("Fatal: buffer too small. Please report a bug.\n");
+	va_end(va);
 	return ret;
 }
 
@@ -393,21 +491,20 @@
 	unsigned char i, j, s[256];
 } rs;
 
-static void
-arc4_init( void )
+static void arc4_init(void)
 {
 	int i, fd;
 	unsigned char j, si, dat[128];
 
-	if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
-		fprintf( stderr, "Fatal: no random number source available.\n" );
-		exit( 3 );
+	if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
+		fprintf(stderr, "Fatal: no random number source available.\n");
+		exit(3);
 	}
-	if (read_in_full( fd, dat, 128 ) != 128) {
-		fprintf( stderr, "Fatal: cannot read random number source.\n" );
-		exit( 3 );
+	if (read_in_full(fd, dat, 128) != 128) {
+		fprintf(stderr, "Fatal: cannot read random number source.\n");
+		exit(3);
 	}
-	close( fd );
+	close(fd);
 
 	for (i = 0; i < 256; i++)
 		rs.s[i] = i;
@@ -423,8 +520,7 @@
 		arc4_getbyte();
 }
 
-static unsigned char
-arc4_getbyte( void )
+static unsigned char arc4_getbyte(void)
 {
 	unsigned char si, sj;
 
@@ -437,54 +533,53 @@
 	return rs.s[(si + sj) & 0xff];
 }
 
-static struct imap_cmd *
-v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
-		  const char *fmt, va_list ap )
+static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
+					 struct imap_cmd_cb *cb,
+					 const char *fmt, va_list ap)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd *cmd;
 	int n, bufl;
 	char buf[1024];
 
-	cmd = xmalloc( sizeof(struct imap_cmd) );
-	nfvasprintf( &cmd->cmd, fmt, ap );
+	cmd = xmalloc(sizeof(struct imap_cmd));
+	nfvasprintf(&cmd->cmd, fmt, ap);
 	cmd->tag = ++imap->nexttag;
 
 	if (cb)
 		cmd->cb = *cb;
 	else
-		memset( &cmd->cb, 0, sizeof(cmd->cb) );
+		memset(&cmd->cb, 0, sizeof(cmd->cb));
 
 	while (imap->literal_pending)
-		get_cmd_result( ctx, NULL );
+		get_cmd_result(ctx, NULL);
 
-	bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
+	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
 			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen );
+			   cmd->tag, cmd->cmd, cmd->cb.dlen);
 	if (Verbose) {
 		if (imap->num_in_progress)
-			printf( "(%d in progress) ", imap->num_in_progress );
-		if (memcmp( cmd->cmd, "LOGIN", 5 ))
-			printf( ">>> %s", buf );
+			printf("(%d in progress) ", imap->num_in_progress);
+		if (memcmp(cmd->cmd, "LOGIN", 5))
+			printf(">>> %s", buf);
 		else
-			printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag );
+			printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
 	}
-	if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
-		free( cmd->cmd );
-		free( cmd );
+	if (socket_write(&imap->buf.sock, buf, bufl) != bufl) {
+		free(cmd->cmd);
+		free(cmd);
 		if (cb)
-			free( cb->data );
+			free(cb->data);
 		return NULL;
 	}
 	if (cmd->cb.data) {
 		if (CAP(LITERALPLUS)) {
-			n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );
-			free( cmd->cb.data );
+			n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
+			free(cmd->cb.data);
 			if (n != cmd->cb.dlen ||
-			    (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2)
-			{
-				free( cmd->cmd );
-				free( cmd );
+			    (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) {
+				free(cmd->cmd);
+				free(cmd);
 				return NULL;
 			}
 			cmd->cb.data = NULL;
@@ -499,109 +594,106 @@
 	return cmd;
 }
 
-static struct imap_cmd *
-issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
+				       struct imap_cmd_cb *cb,
+				       const char *fmt, ...)
 {
 	struct imap_cmd *ret;
 	va_list ap;
 
-	va_start( ap, fmt );
-	ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	ret = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	return ret;
 }
 
-static int
-imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
+		     const char *fmt, ...)
 {
 	va_list ap;
 	struct imap_cmd *cmdp;
 
-	va_start( ap, fmt );
-	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	if (!cmdp)
 		return RESP_BAD;
 
-	return get_cmd_result( ctx, cmdp );
+	return get_cmd_result(ctx, cmdp);
 }
 
-static int
-imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
+static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
+		       const char *fmt, ...)
 {
 	va_list ap;
 	struct imap_cmd *cmdp;
 
-	va_start( ap, fmt );
-	cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
-	va_end( ap );
+	va_start(ap, fmt);
+	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
+	va_end(ap);
 	if (!cmdp)
 		return DRV_STORE_BAD;
 
-	switch (get_cmd_result( ctx, cmdp )) {
+	switch (get_cmd_result(ctx, cmdp)) {
 	case RESP_BAD: return DRV_STORE_BAD;
 	case RESP_NO: return DRV_MSG_BAD;
 	default: return DRV_OK;
 	}
 }
 
-static int
-is_atom( list_t *list )
+static int is_atom(struct imap_list *list)
 {
 	return list && list->val && list->val != NIL && list->val != LIST;
 }
 
-static int
-is_list( list_t *list )
+static int is_list(struct imap_list *list)
 {
 	return list && list->val == LIST;
 }
 
-static void
-free_list( list_t *list )
+static void free_list(struct imap_list *list)
 {
-	list_t *tmp;
+	struct imap_list *tmp;
 
 	for (; list; list = tmp) {
 		tmp = list->next;
-		if (is_list( list ))
-			free_list( list->child );
-		else if (is_atom( list ))
-			free( list->val );
-		free( list );
+		if (is_list(list))
+			free_list(list->child);
+		else if (is_atom(list))
+			free(list->val);
+		free(list);
 	}
 }
 
-static int
-parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
+static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
 {
-	list_t *cur;
+	struct imap_list *cur;
 	char *s = *sp, *p;
 	int n, bytes;
 
 	for (;;) {
-		while (isspace( (unsigned char)*s ))
+		while (isspace((unsigned char)*s))
 			s++;
 		if (level && *s == ')') {
 			s++;
 			break;
 		}
-		*curp = cur = xmalloc( sizeof(*cur) );
+		*curp = cur = xmalloc(sizeof(*cur));
 		curp = &cur->next;
 		cur->val = NULL; /* for clean bail */
 		if (*s == '(') {
 			/* sublist */
 			s++;
 			cur->val = LIST;
-			if (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))
+			if (parse_imap_list_l(imap, &s, &cur->child, level + 1))
 				goto bail;
 		} else if (imap && *s == '{') {
 			/* literal */
-			bytes = cur->len = strtol( s + 1, &s, 10 );
+			bytes = cur->len = strtol(s + 1, &s, 10);
 			if (*s != '}')
 				goto bail;
 
-			s = cur->val = xmalloc( cur->len );
+			s = cur->val = xmalloc(cur->len);
 
 			/* dump whats left over in the input buffer */
 			n = imap->buf.bytes - imap->buf.offset;
@@ -610,7 +702,7 @@
 				/* the entire message fit in the buffer */
 				n = bytes;
 
-			memcpy( s, imap->buf.buf + imap->buf.offset, n );
+			memcpy(s, imap->buf.buf + imap->buf.offset, n);
 			s += n;
 			bytes -= n;
 
@@ -619,13 +711,13 @@
 
 			/* now read the rest of the message */
 			while (bytes > 0) {
-				if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)
+				if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0)
 					goto bail;
 				s += n;
 				bytes -= n;
 			}
 
-			if (buffer_gets( &imap->buf, &s ))
+			if (buffer_gets(&imap->buf, &s))
 				goto bail;
 		} else if (*s == '"') {
 			/* quoted string */
@@ -640,15 +732,14 @@
 		} else {
 			/* atom */
 			p = s;
-			for (; *s && !isspace( (unsigned char)*s ); s++)
+			for (; *s && !isspace((unsigned char)*s); s++)
 				if (level && *s == ')')
 					break;
 			cur->len = s - p;
-			if (cur->len == 3 && !memcmp ("NIL", p, 3)) {
+			if (cur->len == 3 && !memcmp("NIL", p, 3))
 				cur->val = NIL;
-			} else {
+			else
 				cur->val = xmemdupz(p, cur->len);
-			}
 		}
 
 		if (!level)
@@ -660,127 +751,122 @@
 	*curp = NULL;
 	return 0;
 
-  bail:
+bail:
 	*curp = NULL;
 	return -1;
 }
 
-static list_t *
-parse_imap_list( imap_t *imap, char **sp )
+static struct imap_list *parse_imap_list(struct imap *imap, char **sp)
 {
-	list_t *head;
+	struct imap_list *head;
 
-	if (!parse_imap_list_l( imap, sp, &head, 0 ))
+	if (!parse_imap_list_l(imap, sp, &head, 0))
 		return head;
-	free_list( head );
+	free_list(head);
 	return NULL;
 }
 
-static list_t *
-parse_list( char **sp )
+static struct imap_list *parse_list(char **sp)
 {
-	return parse_imap_list( NULL, sp );
+	return parse_imap_list(NULL, sp);
 }
 
-static void
-parse_capability( imap_t *imap, char *cmd )
+static void parse_capability(struct imap *imap, char *cmd)
 {
 	char *arg;
 	unsigned i;
 
 	imap->caps = 0x80000000;
-	while ((arg = next_arg( &cmd )))
+	while ((arg = next_arg(&cmd)))
 		for (i = 0; i < ARRAY_SIZE(cap_list); i++)
-			if (!strcmp( cap_list[i], arg ))
+			if (!strcmp(cap_list[i], arg))
 				imap->caps |= 1 << i;
 	imap->rcaps = imap->caps;
 }
 
-static int
-parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )
+static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
+			       char *s)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	char *arg, *p;
 
 	if (*s != '[')
 		return RESP_OK;		/* no response code */
 	s++;
-	if (!(p = strchr( s, ']' ))) {
-		fprintf( stderr, "IMAP error: malformed response code\n" );
+	if (!(p = strchr(s, ']'))) {
+		fprintf(stderr, "IMAP error: malformed response code\n");
 		return RESP_BAD;
 	}
 	*p++ = 0;
-	arg = next_arg( &s );
-	if (!strcmp( "UIDVALIDITY", arg )) {
-		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {
-			fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" );
+	arg = next_arg(&s);
+	if (!strcmp("UIDVALIDITY", arg)) {
+		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
 			return RESP_BAD;
 		}
-	} else if (!strcmp( "UIDNEXT", arg )) {
-		if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {
-			fprintf( stderr, "IMAP error: malformed NEXTUID status\n" );
+	} else if (!strcmp("UIDNEXT", arg)) {
+		if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
 			return RESP_BAD;
 		}
-	} else if (!strcmp( "CAPABILITY", arg )) {
-		parse_capability( imap, s );
-	} else if (!strcmp( "ALERT", arg )) {
+	} else if (!strcmp("CAPABILITY", arg)) {
+		parse_capability(imap, s);
+	} else if (!strcmp("ALERT", arg)) {
 		/* RFC2060 says that these messages MUST be displayed
 		 * to the user
 		 */
-		for (; isspace( (unsigned char)*p ); p++);
-		fprintf( stderr, "*** IMAP ALERT *** %s\n", p );
-	} else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) {
-		if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||
-		    !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))
-		{
-			fprintf( stderr, "IMAP error: malformed APPENDUID status\n" );
+		for (; isspace((unsigned char)*p); p++);
+		fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
+	} else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
+		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) ||
+		    !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
+			fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
 			return RESP_BAD;
 		}
 	}
 	return RESP_OK;
 }
 
-static int
-get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 {
-	imap_t *imap = ctx->imap;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd *cmdp, **pcmdp, *ncmdp;
 	char *cmd, *arg, *arg1, *p;
 	int n, resp, resp2, tag;
 
 	for (;;) {
-		if (buffer_gets( &imap->buf, &cmd ))
+		if (buffer_gets(&imap->buf, &cmd))
 			return RESP_BAD;
 
-		arg = next_arg( &cmd );
+		arg = next_arg(&cmd);
 		if (*arg == '*') {
-			arg = next_arg( &cmd );
+			arg = next_arg(&cmd);
 			if (!arg) {
-				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
 				return RESP_BAD;
 			}
 
-			if (!strcmp( "NAMESPACE", arg )) {
-				imap->ns_personal = parse_list( &cmd );
-				imap->ns_other = parse_list( &cmd );
-				imap->ns_shared = parse_list( &cmd );
-			} else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) ||
-				   !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) {
-				if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK)
+			if (!strcmp("NAMESPACE", arg)) {
+				imap->ns_personal = parse_list(&cmd);
+				imap->ns_other = parse_list(&cmd);
+				imap->ns_shared = parse_list(&cmd);
+			} else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
+				   !strcmp("NO", arg) || !strcmp("BYE", arg)) {
+				if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
 					return resp;
-			} else if (!strcmp( "CAPABILITY", arg ))
-				parse_capability( imap, cmd );
-			else if ((arg1 = next_arg( &cmd ))) {
-				if (!strcmp( "EXISTS", arg1 ))
-					ctx->gen.count = atoi( arg );
-				else if (!strcmp( "RECENT", arg1 ))
-					ctx->gen.recent = atoi( arg );
+			} else if (!strcmp("CAPABILITY", arg))
+				parse_capability(imap, cmd);
+			else if ((arg1 = next_arg(&cmd))) {
+				if (!strcmp("EXISTS", arg1))
+					ctx->gen.count = atoi(arg);
+				else if (!strcmp("RECENT", arg1))
+					ctx->gen.recent = atoi(arg);
 			} else {
-				fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
+				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
 				return RESP_BAD;
 			}
 		} else if (!imap->in_progress) {
-			fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
+			fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
 			return RESP_BAD;
 		} else if (*arg == '+') {
 			/* This can happen only with the last command underway, as
@@ -788,57 +874,57 @@
 			cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
 			       offsetof(struct imap_cmd, next));
 			if (cmdp->cb.data) {
-				n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );
-				free( cmdp->cb.data );
+				n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
+				free(cmdp->cb.data);
 				cmdp->cb.data = NULL;
 				if (n != (int)cmdp->cb.dlen)
 					return RESP_BAD;
 			} else if (cmdp->cb.cont) {
-				if (cmdp->cb.cont( ctx, cmdp, cmd ))
+				if (cmdp->cb.cont(ctx, cmdp, cmd))
 					return RESP_BAD;
 			} else {
-				fprintf( stderr, "IMAP error: unexpected command continuation request\n" );
+				fprintf(stderr, "IMAP error: unexpected command continuation request\n");
 				return RESP_BAD;
 			}
-			if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2)
+			if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
 				return RESP_BAD;
 			if (!cmdp->cb.cont)
 				imap->literal_pending = 0;
 			if (!tcmd)
 				return DRV_OK;
 		} else {
-			tag = atoi( arg );
+			tag = atoi(arg);
 			for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
 				if (cmdp->tag == tag)
 					goto gottag;
-			fprintf( stderr, "IMAP error: unexpected tag %s\n", arg );
+			fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
 			return RESP_BAD;
-		  gottag:
+		gottag:
 			if (!(*pcmdp = cmdp->next))
 				imap->in_progress_append = pcmdp;
 			imap->num_in_progress--;
 			if (cmdp->cb.cont || cmdp->cb.data)
 				imap->literal_pending = 0;
-			arg = next_arg( &cmd );
-			if (!strcmp( "OK", arg ))
+			arg = next_arg(&cmd);
+			if (!strcmp("OK", arg))
 				resp = DRV_OK;
 			else {
-				if (!strcmp( "NO", arg )) {
-					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */
-						p = strchr( cmdp->cmd, '"' );
-						if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) {
+				if (!strcmp("NO", arg)) {
+					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
+						p = strchr(cmdp->cmd, '"');
+						if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
 							resp = RESP_BAD;
 							goto normal;
 						}
 						/* not waiting here violates the spec, but a server that does not
 						   grok this nonetheless violates it too. */
 						cmdp->cb.create = 0;
-						if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) {
+						if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) {
 							resp = RESP_BAD;
 							goto normal;
 						}
-						free( cmdp->cmd );
-						free( cmdp );
+						free(cmdp->cmd);
+						free(cmdp);
 						if (!tcmd)
 							return 0;	/* ignored */
 						if (cmdp == tcmd)
@@ -846,21 +932,21 @@
 						continue;
 					}
 					resp = RESP_NO;
-				} else /*if (!strcmp( "BAD", arg ))*/
+				} else /*if (!strcmp("BAD", arg))*/
 					resp = RESP_BAD;
-				fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n",
-					 memcmp (cmdp->cmd, "LOGIN", 5) ?
+				fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
+					 memcmp(cmdp->cmd, "LOGIN", 5) ?
 							cmdp->cmd : "LOGIN <user> <pass>",
 							arg, cmd ? cmd : "");
 			}
-			if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)
+			if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
 				resp = resp2;
-		  normal:
+		normal:
 			if (cmdp->cb.done)
-				cmdp->cb.done( ctx, cmdp, resp );
-			free( cmdp->cb.data );
-			free( cmdp->cmd );
-			free( cmdp );
+				cmdp->cb.done(ctx, cmdp, resp);
+			free(cmdp->cb.data);
+			free(cmdp->cmd);
+			free(cmdp);
 			if (!tcmd || tcmd == cmdp)
 				return resp;
 		}
@@ -868,170 +954,184 @@
 	/* not reached */
 }
 
-static void
-imap_close_server( imap_store_t *ictx )
+static void imap_close_server(struct imap_store *ictx)
 {
-	imap_t *imap = ictx->imap;
+	struct imap *imap = ictx->imap;
 
 	if (imap->buf.sock.fd != -1) {
-		imap_exec( ictx, NULL, "LOGOUT" );
-		close( imap->buf.sock.fd );
+		imap_exec(ictx, NULL, "LOGOUT");
+		socket_shutdown(&imap->buf.sock);
 	}
-	free_list( imap->ns_personal );
-	free_list( imap->ns_other );
-	free_list( imap->ns_shared );
-	free( imap );
+	free_list(imap->ns_personal);
+	free_list(imap->ns_other);
+	free_list(imap->ns_shared);
+	free(imap);
 }
 
-static void
-imap_close_store( store_t *ctx )
+static void imap_close_store(struct store *ctx)
 {
-	imap_close_server( (imap_store_t *)ctx );
-	free_generic_messages( ctx->msgs );
-	free( ctx );
+	imap_close_server((struct imap_store *)ctx);
+	free_generic_messages(ctx->msgs);
+	free(ctx);
 }
 
-static store_t *
-imap_open_store( imap_server_conf_t *srvc )
+static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
-	imap_store_t *ctx;
-	imap_t *imap;
+	struct imap_store *ctx;
+	struct imap *imap;
 	char *arg, *rsp;
 	struct hostent *he;
 	struct sockaddr_in addr;
 	int s, a[2], preauth;
 	pid_t pid;
 
-	ctx = xcalloc( sizeof(*ctx), 1 );
+	ctx = xcalloc(sizeof(*ctx), 1);
 
-	ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
+	ctx->imap = imap = xcalloc(sizeof(*imap), 1);
 	imap->buf.sock.fd = -1;
 	imap->in_progress_append = &imap->in_progress;
 
 	/* open connection to IMAP server */
 
 	if (srvc->tunnel) {
-		imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
+		imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-		if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
-			perror( "socketpair" );
-			exit( 1 );
+		if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
+			perror("socketpair");
+			exit(1);
 		}
 
 		pid = fork();
 		if (pid < 0)
-			_exit( 127 );
+			_exit(127);
 		if (!pid) {
-			if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
-				_exit( 127 );
-			close( a[0] );
-			close( a[1] );
-			execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL );
-			_exit( 127 );
+			if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
+				_exit(127);
+			close(a[0]);
+			close(a[1]);
+			execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
+			_exit(127);
 		}
 
-		close (a[0]);
+		close(a[0]);
 
 		imap->buf.sock.fd = a[1];
 
-		imap_info( "ok\n" );
+		imap_info("ok\n");
 	} else {
-		memset( &addr, 0, sizeof(addr) );
-		addr.sin_port = htons( srvc->port );
+		memset(&addr, 0, sizeof(addr));
+		addr.sin_port = htons(srvc->port);
 		addr.sin_family = AF_INET;
 
-		imap_info( "Resolving %s... ", srvc->host );
-		he = gethostbyname( srvc->host );
+		imap_info("Resolving %s... ", srvc->host);
+		he = gethostbyname(srvc->host);
 		if (!he) {
-			perror( "gethostbyname" );
+			perror("gethostbyname");
 			goto bail;
 		}
-		imap_info( "ok\n" );
+		imap_info("ok\n");
 
 		addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
 
-		s = socket( PF_INET, SOCK_STREAM, 0 );
+		s = socket(PF_INET, SOCK_STREAM, 0);
 
-		imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
-		if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
-			close( s );
-			perror( "connect" );
+		imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+		if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
+			close(s);
+			perror("connect");
 			goto bail;
 		}
-		imap_info( "ok\n" );
 
 		imap->buf.sock.fd = s;
 
+		if (srvc->use_ssl &&
+		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
+			close(s);
+			goto bail;
+		}
+		imap_info("ok\n");
 	}
 
 	/* read the greeting string */
-	if (buffer_gets( &imap->buf, &rsp )) {
-		fprintf( stderr, "IMAP error: no greeting response\n" );
+	if (buffer_gets(&imap->buf, &rsp)) {
+		fprintf(stderr, "IMAP error: no greeting response\n");
 		goto bail;
 	}
-	arg = next_arg( &rsp );
-	if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
-		fprintf( stderr, "IMAP error: invalid greeting response\n" );
+	arg = next_arg(&rsp);
+	if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
+		fprintf(stderr, "IMAP error: invalid greeting response\n");
 		goto bail;
 	}
 	preauth = 0;
-	if (!strcmp( "PREAUTH", arg ))
+	if (!strcmp("PREAUTH", arg))
 		preauth = 1;
-	else if (strcmp( "OK", arg ) != 0) {
-		fprintf( stderr, "IMAP error: unknown greeting response\n" );
+	else if (strcmp("OK", arg) != 0) {
+		fprintf(stderr, "IMAP error: unknown greeting response\n");
 		goto bail;
 	}
-	parse_response_code( ctx, NULL, rsp );
-	if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK)
+	parse_response_code(ctx, NULL, rsp);
+	if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
 		goto bail;
 
 	if (!preauth) {
-
-		imap_info ("Logging in...\n");
+#ifndef NO_OPENSSL
+		if (!srvc->use_ssl && CAP(STARTTLS)) {
+			if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
+				goto bail;
+			if (ssl_socket_connect(&imap->buf.sock, 1,
+					       srvc->ssl_verify))
+				goto bail;
+			/* capabilities may have changed, so get the new capabilities */
+			if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
+				goto bail;
+		}
+#endif
+		imap_info("Logging in...\n");
 		if (!srvc->user) {
-			fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
+			fprintf(stderr, "Skipping server %s, no user\n", srvc->host);
 			goto bail;
 		}
 		if (!srvc->pass) {
 			char prompt[80];
-			sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
-			arg = getpass( prompt );
+			sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
+			arg = getpass(prompt);
 			if (!arg) {
-				perror( "getpass" );
-				exit( 1 );
+				perror("getpass");
+				exit(1);
 			}
 			if (!*arg) {
-				fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
+				fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
 				goto bail;
 			}
 			/*
 			 * getpass() returns a pointer to a static buffer.  make a copy
 			 * for long term storage.
 			 */
-			srvc->pass = xstrdup( arg );
+			srvc->pass = xstrdup(arg);
 		}
 		if (CAP(NOLOGIN)) {
-			fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
+			fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
 			goto bail;
 		}
-		imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
-		if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
-			fprintf( stderr, "IMAP error: LOGIN failed\n" );
+		if (!imap->buf.sock.ssl)
+			imap_warn("*** IMAP Warning *** Password is being "
+				  "sent in the clear\n");
+		if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+			fprintf(stderr, "IMAP error: LOGIN failed\n");
 			goto bail;
 		}
 	} /* !preauth */
 
 	ctx->prefix = "";
 	ctx->trashnc = 1;
-	return (store_t *)ctx;
+	return (struct store *)ctx;
 
-  bail:
-	imap_close_store( &ctx->gen );
+bail:
+	imap_close_store(&ctx->gen);
 	return NULL;
 }
 
-static int
-imap_make_flags( int flags, char *buf )
+static int imap_make_flags(int flags, char *buf)
 {
 	const char *s;
 	unsigned i, d;
@@ -1050,11 +1150,10 @@
 
 #define TUIDL 8
 
-static int
-imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
+static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
 {
-	imap_store_t *ctx = (imap_store_t *)gctx;
-	imap_t *imap = ctx->imap;
+	struct imap_store *ctx = (struct imap_store *)gctx;
+	struct imap *imap = ctx->imap;
 	struct imap_cmd_cb cb;
 	char *fmap, *buf;
 	const char *prefix, *box;
@@ -1062,14 +1161,14 @@
 	int start, sbreak = 0, ebreak = 0;
 	char flagstr[128], tuid[TUIDL * 2 + 1];
 
-	memset( &cb, 0, sizeof(cb) );
+	memset(&cb, 0, sizeof(cb));
 
 	fmap = data->data;
 	len = data->len;
 	nocr = !data->crlf;
 	extra = 0, i = 0;
 	if (!CAP(UIDPLUS) && uid) {
-	  nloop:
+	nloop:
 		start = i;
 		while (i < len)
 			if (fmap[i++] == '\n') {
@@ -1078,18 +1177,18 @@
 					sbreak = ebreak = i - 2 + nocr;
 					goto mktid;
 				}
-				if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
+				if (!memcmp(fmap + start, "X-TUID: ", 8)) {
 					extra -= (ebreak = i) - (sbreak = start) + nocr;
 					goto mktid;
 				}
 				goto nloop;
 			}
 		/* invalid message */
-		free( fmap );
+		free(fmap);
 		return DRV_MSG_BAD;
-	 mktid:
+	mktid:
 		for (j = 0; j < TUIDL; j++)
-			sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
+			sprintf(tuid + j * 2, "%02x", arc4_getbyte());
 		extra += 8 + TUIDL * 2 + 2;
 	}
 	if (nocr)
@@ -1098,7 +1197,7 @@
 				extra++;
 
 	cb.dlen = len + extra;
-	buf = cb.data = xmalloc( cb.dlen );
+	buf = cb.data = xmalloc(cb.dlen);
 	i = 0;
 	if (!CAP(UIDPLUS) && uid) {
 		if (nocr) {
@@ -1109,12 +1208,12 @@
 				} else
 					*buf++ = fmap[i];
 		} else {
-			memcpy( buf, fmap, sbreak );
+			memcpy(buf, fmap, sbreak);
 			buf += sbreak;
 		}
-		memcpy( buf, "X-TUID: ", 8 );
+		memcpy(buf, "X-TUID: ", 8);
 		buf += 8;
-		memcpy( buf, tuid, TUIDL * 2 );
+		memcpy(buf, tuid, TUIDL * 2);
 		buf += TUIDL * 2;
 		*buf++ = '\r';
 		*buf++ = '\n';
@@ -1128,13 +1227,13 @@
 			} else
 				*buf++ = fmap[i];
 	} else
-		memcpy( buf, fmap + i, len - i );
+		memcpy(buf, fmap + i, len - i);
 
-	free( fmap );
+	free(fmap);
 
 	d = 0;
 	if (data->flags) {
-		d = imap_make_flags( data->flags, flagstr );
+		d = imap_make_flags(data->flags, flagstr);
 		flagstr[d++] = ' ';
 	}
 	flagstr[d] = 0;
@@ -1147,11 +1246,11 @@
 			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
 	} else {
 		box = gctx->name;
-		prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
+		prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
 		cb.create = 0;
 	}
 	cb.ctx = uid;
-	ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
+	ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
 	imap->caps = imap->rcaps;
 	if (ret != DRV_OK)
 		return ret;
@@ -1165,8 +1264,7 @@
 
 #define CHUNKSIZE 0x1000
 
-static int
-read_message( FILE *f, msg_data_t *msg )
+static int read_message(FILE *f, struct msg_data *msg)
 {
 	struct strbuf buf;
 
@@ -1183,8 +1281,7 @@
 	return msg->len;
 }
 
-static int
-count_messages( msg_data_t *msg )
+static int count_messages(struct msg_data *msg)
 {
 	int count = 0;
 	char *p = msg->data;
@@ -1194,7 +1291,7 @@
 			count++;
 			p += 5;
 		}
-		p = strstr( p+5, "\nFrom ");
+		p = strstr(p+5, "\nFrom ");
 		if (!p)
 			break;
 		p++;
@@ -1202,22 +1299,21 @@
 	return count;
 }
 
-static int
-split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
+static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 {
 	char *p, *data;
 
-	memset( msg, 0, sizeof *msg );
+	memset(msg, 0, sizeof *msg);
 	if (*ofs >= all_msgs->len)
 		return 0;
 
-	data = &all_msgs->data[ *ofs ];
+	data = &all_msgs->data[*ofs];
 	msg->len = all_msgs->len - *ofs;
 
 	if (msg->len < 5 || prefixcmp(data, "From "))
 		return 0;
 
-	p = strchr( data, '\n' );
+	p = strchr(data, '\n');
 	if (p) {
 		p = &p[1];
 		msg->len -= p-data;
@@ -1225,7 +1321,7 @@
 		data = p;
 	}
 
-	p = strstr( data, "\nFrom " );
+	p = strstr(data, "\nFrom ");
 	if (p)
 		msg->len = &p[1] - data;
 
@@ -1234,24 +1330,24 @@
 	return 1;
 }
 
-static imap_server_conf_t server =
-{
+static struct imap_server_conf server = {
 	NULL,	/* name */
 	NULL,	/* tunnel */
 	NULL,	/* host */
 	0,	/* port */
 	NULL,	/* user */
 	NULL,	/* pass */
+	0,   	/* use_ssl */
+	1,   	/* ssl_verify */
 };
 
 static char *imap_folder;
 
-static int
-git_imap_config(const char *key, const char *val, void *cb)
+static int git_imap_config(const char *key, const char *val, void *cb)
 {
 	char imap_key[] = "imap.";
 
-	if (strncmp( key, imap_key, sizeof imap_key - 1 ))
+	if (strncmp(key, imap_key, sizeof imap_key - 1))
 		return 0;
 
 	if (!val)
@@ -1259,90 +1355,96 @@
 
 	key += sizeof imap_key - 1;
 
-	if (!strcmp( "folder", key )) {
-		imap_folder = xstrdup( val );
-	} else if (!strcmp( "host", key )) {
-		{
-			if (!prefixcmp(val, "imap:"))
-				val += 5;
-			if (!server.port)
-				server.port = 143;
+	if (!strcmp("folder", key)) {
+		imap_folder = xstrdup(val);
+	} else if (!strcmp("host", key)) {
+		if (!prefixcmp(val, "imap:"))
+			val += 5;
+		else if (!prefixcmp(val, "imaps:")) {
+			val += 6;
+			server.use_ssl = 1;
 		}
 		if (!prefixcmp(val, "//"))
 			val += 2;
-		server.host = xstrdup( val );
-	}
-	else if (!strcmp( "user", key ))
-		server.user = xstrdup( val );
-	else if (!strcmp( "pass", key ))
-		server.pass = xstrdup( val );
-	else if (!strcmp( "port", key ))
-		server.port = git_config_int( key, val );
-	else if (!strcmp( "tunnel", key ))
-		server.tunnel = xstrdup( val );
+		server.host = xstrdup(val);
+	} else if (!strcmp("user", key))
+		server.user = xstrdup(val);
+	else if (!strcmp("pass", key))
+		server.pass = xstrdup(val);
+	else if (!strcmp("port", key))
+		server.port = git_config_int(key, val);
+	else if (!strcmp("tunnel", key))
+		server.tunnel = xstrdup(val);
+	else if (!strcmp("sslverify", key))
+		server.ssl_verify = git_config_bool(key, val);
 	return 0;
 }
 
-int
-main(int argc, char **argv)
+int main(int argc, char **argv)
 {
-	msg_data_t all_msgs, msg;
-	store_t *ctx = NULL;
+	struct msg_data all_msgs, msg;
+	struct store *ctx = NULL;
 	int uid = 0;
 	int ofs = 0;
 	int r;
 	int total, n = 0;
+	int nongit_ok;
 
 	/* init the random number generator */
 	arc4_init();
 
+	setup_git_directory_gently(&nongit_ok);
 	git_config(git_imap_config, NULL);
 
+	if (!server.port)
+		server.port = server.use_ssl ? 993 : 143;
+
 	if (!imap_folder) {
-		fprintf( stderr, "no imap store specified\n" );
+		fprintf(stderr, "no imap store specified\n");
 		return 1;
 	}
 	if (!server.host) {
 		if (!server.tunnel) {
-			fprintf( stderr, "no imap host specified\n" );
+			fprintf(stderr, "no imap host specified\n");
 			return 1;
 		}
 		server.host = "tunnel";
 	}
 
 	/* read the messages */
-	if (!read_message( stdin, &all_msgs )) {
-		fprintf(stderr,"nothing to send\n");
+	if (!read_message(stdin, &all_msgs)) {
+		fprintf(stderr, "nothing to send\n");
 		return 1;
 	}
 
-	total = count_messages( &all_msgs );
+	total = count_messages(&all_msgs);
 	if (!total) {
-		fprintf(stderr,"no messages to send\n");
+		fprintf(stderr, "no messages to send\n");
 		return 1;
 	}
 
 	/* write it to the imap server */
-	ctx = imap_open_store( &server );
+	ctx = imap_open_store(&server);
 	if (!ctx) {
-		fprintf( stderr,"failed to open store\n");
+		fprintf(stderr, "failed to open store\n");
 		return 1;
 	}
 
-	fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" );
+	fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
 	ctx->name = imap_folder;
 	while (1) {
 		unsigned percent = n * 100 / total;
-		fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total );
-		if (!split_msg( &all_msgs, &msg, &ofs ))
+		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
+		if (!split_msg(&all_msgs, &msg, &ofs))
 			break;
-		r = imap_store_msg( ctx, &msg, &uid );
-		if (r != DRV_OK) break;
+		r = imap_store_msg(ctx, &msg, &uid);
+		if (r != DRV_OK)
+			break;
 		n++;
 	}
-	fprintf( stderr,"\n" );
+	fprintf(stderr, "\n");
 
-	imap_close_store( ctx );
+	imap_close_store(ctx);
 
 	return 0;
 }
diff --git a/log-tree.c b/log-tree.c
index bd8b9e4..30cd5bb 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -432,7 +432,7 @@
 	struct commit_list *parents;
 	unsigned const char *sha1 = commit->object.sha1;
 
-	if (!opt->diff)
+	if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
 		return 0;
 
 	/* Root commit? */
diff --git a/pager.c b/pager.c
index 6b5c9e4..aa0966c 100644
--- a/pager.c
+++ b/pager.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "run-command.h"
 
 /*
  * This is split up from the rest of git so that we can do
@@ -8,7 +9,7 @@
 static int spawned_pager;
 
 #ifndef __MINGW32__
-static void run_pager(const char *pager)
+static void pager_preexec(void)
 {
 	/*
 	 * Work around bug in "less" by not starting it until we
@@ -20,17 +21,13 @@
 	FD_SET(0, &in);
 	select(1, &in, NULL, &in, NULL);
 
-	execlp(pager, pager, NULL);
-	execl("/bin/sh", "sh", "-c", pager, NULL);
+	setenv("LESS", "FRSX", 0);
 }
-#else
-#include "run-command.h"
+#endif
 
 static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
-static struct child_process pager_process = {
-	.argv = pager_argv,
-	.in = -1
-};
+static struct child_process pager_process;
+
 static void wait_for_pager(void)
 {
 	fflush(stdout);
@@ -40,14 +37,9 @@
 	close(2);
 	finish_command(&pager_process);
 }
-#endif
 
 void setup_pager(void)
 {
-#ifndef __MINGW32__
-	pid_t pid;
-	int fd[2];
-#endif
 	const char *pager = getenv("GIT_PAGER");
 
 	if (!isatty(1))
@@ -66,37 +58,13 @@
 
 	spawned_pager = 1; /* means we are emitting to terminal */
 
-#ifndef __MINGW32__
-	if (pipe(fd) < 0)
-		return;
-	pid = fork();
-	if (pid < 0) {
-		close(fd[0]);
-		close(fd[1]);
-		return;
-	}
-
-	/* return in the child */
-	if (!pid) {
-		dup2(fd[1], 1);
-		dup2(fd[1], 2);
-		close(fd[0]);
-		close(fd[1]);
-		return;
-	}
-
-	/* The original process turns into the PAGER */
-	dup2(fd[0], 0);
-	close(fd[0]);
-	close(fd[1]);
-
-	setenv("LESS", "FRSX", 0);
-	run_pager(pager);
-	die("unable to execute pager '%s'", pager);
-	exit(255);
-#else
 	/* spawn the pager */
 	pager_argv[2] = pager;
+	pager_process.argv = pager_argv;
+	pager_process.in = -1;
+#ifndef __MINGW32__
+	pager_process.preexec_cb = pager_preexec;
+#endif
 	if (start_command(&pager_process))
 		return;
 
@@ -107,7 +75,6 @@
 
 	/* this makes sure that the parent terminates after the pager */
 	atexit(wait_for_pager);
-#endif
 }
 
 int pager_in_use(void)
diff --git a/read-cache.c b/read-cache.c
index 35fec46..5150c1e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -8,6 +8,11 @@
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
+#include "tree.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
 
 /* Index extensions.
  *
@@ -1118,6 +1123,10 @@
 	ce->ce_size  = ntohl(ondisk->size);
 	/* On-disk flags are just 16 bits */
 	ce->ce_flags = ntohs(ondisk->flags);
+
+	/* For future extension: we do not understand this entry yet */
+	if (ce->ce_flags & CE_EXTENDED)
+		die("Unknown index entry format");
 	hashcpy(ce->sha1, ondisk->sha1);
 
 	len = ce->ce_flags & CE_NAMEMASK;
@@ -1479,3 +1488,59 @@
 	istate->cache_nr = dst - istate->cache;
 	return !!last;
 }
+
+struct update_callback_data
+{
+	int flags;
+	int add_errors;
+};
+
+static void update_callback(struct diff_queue_struct *q,
+			    struct diff_options *opt, void *cbdata)
+{
+	int i;
+	struct update_callback_data *data = cbdata;
+
+	for (i = 0; i < q->nr; i++) {
+		struct diff_filepair *p = q->queue[i];
+		const char *path = p->one->path;
+		switch (p->status) {
+		default:
+			die("unexpected diff status %c", p->status);
+		case DIFF_STATUS_UNMERGED:
+		case DIFF_STATUS_MODIFIED:
+		case DIFF_STATUS_TYPE_CHANGED:
+			if (add_file_to_index(&the_index, path, data->flags)) {
+				if (!(data->flags & ADD_CACHE_IGNORE_ERRORS))
+					die("updating files failed");
+				data->add_errors++;
+			}
+			break;
+		case DIFF_STATUS_DELETED:
+			if (data->flags & ADD_CACHE_IGNORE_REMOVAL)
+				break;
+			if (!(data->flags & ADD_CACHE_PRETEND))
+				remove_file_from_index(&the_index, path);
+			if (data->flags & (ADD_CACHE_PRETEND|ADD_CACHE_VERBOSE))
+				printf("remove '%s'\n", path);
+			break;
+		}
+	}
+}
+
+int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
+{
+	struct update_callback_data data;
+	struct rev_info rev;
+	init_revisions(&rev, prefix);
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.prune_data = pathspec;
+	rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+	rev.diffopt.format_callback = update_callback;
+	data.flags = flags;
+	data.add_errors = 0;
+	rev.diffopt.format_callback_data = &data;
+	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
+	return !!data.add_errors;
+}
+
diff --git a/remote.c b/remote.c
index 105668f..3ef09a4 100644
--- a/remote.c
+++ b/remote.c
@@ -449,6 +449,26 @@
 	return result;
 }
 
+/*
+ * This function frees a refspec array.
+ * Warning: code paths should be checked to ensure that the src
+ *          and dst pointers are always freeable pointers as well
+ *          as the refspec pointer itself.
+ */
+void free_refspecs(struct refspec *refspec, int nr_refspec)
+{
+	int i;
+
+	if (!refspec)
+		return;
+
+	for (i = 0; i < nr_refspec; i++) {
+		free(refspec[i].src);
+		free(refspec[i].dst);
+	}
+	free(refspec);
+}
+
 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
 {
 	int i;
@@ -567,7 +587,12 @@
 
  invalid:
 	if (verify) {
-		free(rs);
+		/*
+		 * nr_refspec must be greater than zero and i must be valid
+		 * since it is only possible to reach this point from within
+		 * the for loop above.
+		 */
+		free_refspecs(rs, i+1);
 		return NULL;
 	}
 	die("Invalid refspec '%s'", refspec[i]);
@@ -579,7 +604,7 @@
 	struct refspec *refspec;
 
 	refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
-	free(refspec);
+	free_refspecs(refspec, 1);
 	return !!refspec;
 }
 
diff --git a/remote.h b/remote.h
index 091b1d0..2601f6e 100644
--- a/remote.h
+++ b/remote.h
@@ -78,6 +78,7 @@
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
 struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
+void free_refspecs(struct refspec *refspec, int nr_refspec);
 
 int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 	       int nr_refspec, const char **refspec, int all);
diff --git a/run-command.c b/run-command.c
index bbb9c77..caab374 100644
--- a/run-command.c
+++ b/run-command.c
@@ -111,6 +111,8 @@
 					unsetenv(*cmd->env);
 			}
 		}
+		if (cmd->preexec_cb)
+			cmd->preexec_cb();
 		if (cmd->git_cmd) {
 			execv_git_cmd(cmd->argv);
 		} else {
diff --git a/run-command.h b/run-command.h
index 5203a9e..4f2b7d7 100644
--- a/run-command.h
+++ b/run-command.h
@@ -42,6 +42,7 @@
 	unsigned no_stderr:1;
 	unsigned git_cmd:1; /* if this is to be git sub-command */
 	unsigned stdout_to_stderr:1;
+	void (*preexec_cb)(void);
 };
 
 int start_command(struct child_process *);
diff --git a/sha1_file.c b/sha1_file.c
index 477d3fb..9ee1ed1 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2361,51 +2361,22 @@
 	return has_loose_object(sha1);
 }
 
-int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
+static int index_mem(unsigned char *sha1, void *buf, size_t size,
+		     int write_object, enum object_type type, const char *path)
 {
-	struct strbuf buf;
-	int ret;
-
-	strbuf_init(&buf, 0);
-	if (strbuf_read(&buf, fd, 4096) < 0) {
-		strbuf_release(&buf);
-		return -1;
-	}
-
-	if (!type)
-		type = blob_type;
-	if (write_object)
-		ret = write_sha1_file(buf.buf, buf.len, type, sha1);
-	else
-		ret = hash_sha1_file(buf.buf, buf.len, type, sha1);
-	strbuf_release(&buf);
-
-	return ret;
-}
-
-int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
-	     enum object_type type, const char *path)
-{
-	size_t size = xsize_t(st->st_size);
-	void *buf = NULL;
 	int ret, re_allocated = 0;
 
-	if (size)
-		buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
-	close(fd);
-
 	if (!type)
 		type = OBJ_BLOB;
 
 	/*
 	 * Convert blobs to git internal format
 	 */
-	if ((type == OBJ_BLOB) && S_ISREG(st->st_mode)) {
+	if ((type == OBJ_BLOB) && path) {
 		struct strbuf nbuf;
 		strbuf_init(&nbuf, 0);
 		if (convert_to_git(path, buf, size, &nbuf,
 		                   write_object ? safe_crlf : 0)) {
-			munmap(buf, size);
 			buf = strbuf_detach(&nbuf, &size);
 			re_allocated = 1;
 		}
@@ -2415,12 +2386,33 @@
 		ret = write_sha1_file(buf, size, typename(type), sha1);
 	else
 		ret = hash_sha1_file(buf, size, typename(type), sha1);
-	if (re_allocated) {
+	if (re_allocated)
 		free(buf);
-		return ret;
-	}
-	if (size)
+	return ret;
+}
+
+int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object,
+	     enum object_type type, const char *path)
+{
+	int ret;
+	size_t size = xsize_t(st->st_size);
+
+	if (!S_ISREG(st->st_mode)) {
+		struct strbuf sbuf;
+		strbuf_init(&sbuf, 0);
+		if (strbuf_read(&sbuf, fd, 4096) >= 0)
+			ret = index_mem(sha1, sbuf.buf, sbuf.len, write_object,
+					type, path);
+		else
+			ret = -1;
+		strbuf_release(&sbuf);
+	} else if (size) {
+		void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+		ret = index_mem(sha1, buf, size, write_object, type, path);
 		munmap(buf, size);
+	} else
+		ret = index_mem(sha1, NULL, size, write_object, type, path);
+	close(fd);
 	return ret;
 }
 
diff --git a/sha1_name.c b/sha1_name.c
index 4fb77f8..41b6809 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -349,7 +349,10 @@
 			else
 				nth = -1;
 		}
-		if (0 <= nth)
+		if (100000000 <= nth) {
+			at_time = nth;
+			nth = -1;
+		} else if (0 <= nth)
 			at_time = 0;
 		else {
 			char *tmp = xstrndup(str + at + 2, reflog_len);
diff --git a/t/.gitignore b/t/.gitignore
index b27e280..7dcbb23 100644
--- a/t/.gitignore
+++ b/t/.gitignore
@@ -1,2 +1,2 @@
-/trash directory
+/trash directory*
 /test-results
diff --git a/t/Makefile b/t/Makefile
index 0d65ced..ed49c20 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -14,7 +14,8 @@
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
 TSVN = $(wildcard t91[0-9][0-9]-*.sh)
 
-all: pre-clean $(T) aggregate-results clean
+all: pre-clean
+	$(MAKE) aggregate-results-and-cleanup
 
 $(T):
 	@echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
@@ -25,6 +26,10 @@
 clean:
 	$(RM) -r 'trash directory' test-results
 
+aggregate-results-and-cleanup: $(T)
+	$(MAKE) aggregate-results
+	$(MAKE) clean
+
 aggregate-results:
 	'$(SHELL_PATH_SQ)' ./aggregate-results.sh test-results/t*-*
 
@@ -34,4 +39,3 @@
 	$(MAKE) $(TSVN) GIT_SVN_NO_OPTIMIZE_COMMITS=0 LC_ALL=en_US.UTF-8
 
 .PHONY: pre-clean $(T) aggregate-results clean
-.NOTPARALLEL:
diff --git a/t/lib-httpd.sh b/t/lib-httpd.sh
index dc473df..6ac312b 100644
--- a/t/lib-httpd.sh
+++ b/t/lib-httpd.sh
@@ -14,7 +14,7 @@
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-'/usr/sbin/apache2'}
 LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'8111'}
 
-TEST_PATH="$PWD"/../lib-httpd
+TEST_PATH="$TEST_DIRECTORY"/lib-httpd
 HTTPD_ROOT_PATH="$PWD"/httpd
 HTTPD_DOCUMENT_ROOT_PATH=$HTTPD_ROOT_PATH/www
 
diff --git a/t/t0022-crlf-rename.sh b/t/t0022-crlf-rename.sh
index 7d1ce2d..f1e1d48 100755
--- a/t/t0022-crlf-rename.sh
+++ b/t/t0022-crlf-rename.sh
Binary files differ
diff --git a/t/t0055-beyond-symlinks.sh b/t/t0055-beyond-symlinks.sh
new file mode 100755
index 0000000..b29c37a
--- /dev/null
+++ b/t/t0055-beyond-symlinks.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='update-index and add refuse to add beyond symlinks'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	>a &&
+	mkdir b &&
+	ln -s b c &&
+	>c/d &&
+	git update-index --add a b/d
+'
+
+test_expect_success 'update-index --add beyond symlinks' '
+	test_must_fail git update-index --add c/d &&
+	! ( git ls-files | grep c/d )
+'
+
+test_expect_success 'add beyond symlinks' '
+	test_must_fail git add c/d &&
+	! ( git ls-files | grep c/d )
+'
+
+test_done
diff --git a/t/t1000-read-tree-m-3way.sh b/t/t1000-read-tree-m-3way.sh
index 807fb83..22ba7a5 100755
--- a/t/t1000-read-tree-m-3way.sh
+++ b/t/t1000-read-tree-m-3way.sh
@@ -72,7 +72,7 @@
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 ################################################################
 # Trivial "majority when 3 stages exist" merge plus #2ALT, #3ALT
diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh
index 1ec0535..fcdd15a 100755
--- a/t/t1007-hash-object.sh
+++ b/t/t1007-hash-object.sh
@@ -49,16 +49,28 @@
 # Argument checking
 
 test_expect_success "multiple '--stdin's are rejected" '
-	test_must_fail git hash-object --stdin --stdin < example
+	echo example | test_must_fail git hash-object --stdin --stdin
 '
 
 test_expect_success "Can't use --stdin and --stdin-paths together" '
-	test_must_fail git hash-object --stdin --stdin-paths &&
-	test_must_fail git hash-object --stdin-paths --stdin
+	echo example | test_must_fail git hash-object --stdin --stdin-paths &&
+	echo example | test_must_fail git hash-object --stdin-paths --stdin
 '
 
 test_expect_success "Can't pass filenames as arguments with --stdin-paths" '
-	test_must_fail git hash-object --stdin-paths hello < example
+	echo example | test_must_fail git hash-object --stdin-paths hello
+'
+
+test_expect_success "Can't use --path with --stdin-paths" '
+	echo example | test_must_fail git hash-object --stdin-paths --path=foo
+'
+
+test_expect_success "Can't use --stdin-paths with --no-filters" '
+	echo example | test_must_fail git hash-object --stdin-paths --no-filters
+'
+
+test_expect_success "Can't use --path with --no-filters" '
+	test_must_fail git hash-object --no-filters --path=foo
 '
 
 # Behavior
@@ -93,6 +105,42 @@
 	test "$obname1" = "$obname1new"
 '
 
+test_expect_success 'check that appropriate filter is invoke when --path is used' '
+	echo fooQ | tr Q "\\015" >file0 &&
+	cp file0 file1 &&
+	echo "file0 -crlf" >.gitattributes &&
+	echo "file1 crlf" >>.gitattributes &&
+	git config core.autocrlf true &&
+	file0_sha=$(git hash-object file0) &&
+	file1_sha=$(git hash-object file1) &&
+	test "$file0_sha" != "$file1_sha" &&
+	path1_sha=$(git hash-object --path=file1 file0) &&
+	path0_sha=$(git hash-object --path=file0 file1) &&
+	test "$file0_sha" = "$path0_sha" &&
+	test "$file1_sha" = "$path1_sha" &&
+	path1_sha=$(cat file0 | git hash-object --path=file1 --stdin) &&
+	path0_sha=$(cat file1 | git hash-object --path=file0 --stdin) &&
+	test "$file0_sha" = "$path0_sha" &&
+	test "$file1_sha" = "$path1_sha" &&
+	git config --unset core.autocrlf
+'
+
+test_expect_success 'check that --no-filters option works' '
+	echo fooQ | tr Q "\\015" >file0 &&
+	cp file0 file1 &&
+	echo "file0 -crlf" >.gitattributes &&
+	echo "file1 crlf" >>.gitattributes &&
+	git config core.autocrlf true &&
+	file0_sha=$(git hash-object file0) &&
+	file1_sha=$(git hash-object file1) &&
+	test "$file0_sha" != "$file1_sha" &&
+	nofilters_file1=$(git hash-object --no-filters file1) &&
+	test "$file0_sha" = "$nofilters_file1" &&
+	nofilters_file1=$(cat file1 | git hash-object --stdin) &&
+	test "$file0_sha" = "$nofilters_file1" &&
+	git config --unset core.autocrlf
+'
+
 pop_repo
 
 for args in "-w --stdin" "--stdin -w"; do
diff --git a/t/t3504-cherry-pick-rerere.sh b/t/t3504-cherry-pick-rerere.sh
new file mode 100755
index 0000000..f7b3518
--- /dev/null
+++ b/t/t3504-cherry-pick-rerere.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+test_description='cherry-pick should rerere for conflicts'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo foo >foo &&
+	git add foo && test_tick && git commit -q -m 1 &&
+	echo foo-master >foo &&
+	git add foo && test_tick && git commit -q -m 2 &&
+
+	git checkout -b dev HEAD^ &&
+	echo foo-dev >foo &&
+	git add foo && test_tick && git commit -q -m 3 &&
+	git config rerere.enabled true
+'
+
+test_expect_success 'conflicting merge' '
+	test_must_fail git merge master
+'
+
+test_expect_success 'fixup' '
+	echo foo-dev >foo &&
+	git add foo && test_tick && git commit -q -m 4 &&
+	git reset --hard HEAD^
+	echo foo-dev >expect
+'
+
+test_expect_success 'cherry-pick conflict' '
+	test_must_fail git cherry-pick master &&
+	test_cmp expect foo
+'
+
+test_expect_success 'reconfigure' '
+	git config rerere.enabled false
+	git reset --hard
+'
+
+test_expect_success 'cherry-pick conflict without rerere' '
+	test_must_fail git cherry-pick master &&
+	test_must_fail test_cmp expect foo
+'
+
+test_done
diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh
index 883281d..f4f4184 100755
--- a/t/t3900-i18n-commit.sh
+++ b/t/t3900-i18n-commit.sh
@@ -16,7 +16,7 @@
 	: >F &&
 	git add F &&
 	T=$(git write-tree) &&
-	C=$(git commit-tree $T <../t3900/1-UTF-8.txt) &&
+	C=$(git commit-tree $T <"$TEST_DIRECTORY"/t3900/1-UTF-8.txt) &&
 	git update-ref HEAD $C &&
 	git-tag C0
 '
@@ -32,7 +32,7 @@
 		git config i18n.commitencoding $H &&
 		git-checkout -b $H C0 &&
 		echo $H >F &&
-		git-commit -a -F ../t3900/$H.txt
+		git-commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt
 	'
 done
 
@@ -57,13 +57,13 @@
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-	compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+	compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
 	test_expect_success "$H should be shown in UTF-8 now" '
-		compare_with '$H' ../t3900/2-UTF-8.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
 	'
 done
 
@@ -82,7 +82,7 @@
 do
 	test_expect_success "$H should be shown in itself now" '
 		git config i18n.commitencoding '$H' &&
-		compare_with '$H' ../t3900/'$H'.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/'$H'.txt
 	'
 done
 
@@ -91,13 +91,13 @@
 '
 
 test_expect_success 'ISO-8859-1 should be shown in UTF-8 now' '
-	compare_with ISO-8859-1 ../t3900/1-UTF-8.txt
+	compare_with ISO-8859-1 "$TEST_DIRECTORY"/t3900/1-UTF-8.txt
 '
 
 for H in EUCJP ISO-2022-JP
 do
 	test_expect_success "$H should be shown in UTF-8 now" '
-		compare_with '$H' ../t3900/2-UTF-8.txt
+		compare_with '$H' "$TEST_DIRECTORY"/t3900/2-UTF-8.txt
 	'
 done
 
@@ -107,7 +107,7 @@
 	for H in EUCJP ISO-2022-JP
 	do
 		test_expect_success "$H should be shown in $J now" '
-			compare_with '$H' ../t3900/'$J'.txt
+			compare_with '$H' "$TEST_DIRECTORY"/t3900/'$J'.txt
 		'
 	done
 done
@@ -115,7 +115,7 @@
 for H in ISO-8859-1 EUCJP ISO-2022-JP
 do
 	test_expect_success "No conversion with $H" '
-		compare_with "--encoding=none '$H'" ../t3900/'$H'.txt
+		compare_with "--encoding=none '$H'" "$TEST_DIRECTORY"/t3900/'$H'.txt
 	'
 done
 
diff --git a/t/t3901-i18n-patch.sh b/t/t3901-i18n-patch.sh
index 235f372..f68ff53 100755
--- a/t/t3901-i18n-patch.sh
+++ b/t/t3901-i18n-patch.sh
@@ -35,7 +35,7 @@
 
 	# use UTF-8 in author and committer name to match the
 	# i18n.commitencoding settings
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	test_tick &&
 	echo "$GIT_AUTHOR_NAME" >mine &&
@@ -57,7 +57,7 @@
 	# the second one on the side branch is ISO-8859-1
 	git config i18n.commitencoding ISO-8859-1 &&
 	# use author and committer name in ISO-8859-1 to match it.
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	test_tick &&
 	echo Yet another >theirs &&
 	git add theirs &&
@@ -101,7 +101,7 @@
 
 	# The result will be committed by GIT_COMMITTER_NAME --
 	# we want UTF-8 encoded name.
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 	git checkout -b test &&
 	git-rebase master &&
 
@@ -111,7 +111,7 @@
 test_expect_success 'rebase (U/L)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git-rebase master &&
@@ -123,7 +123,7 @@
 	# In this test we want ISO-8859-1 encoded commits as the result
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git-rebase master &&
@@ -136,7 +136,7 @@
 	# to get ISO-8859-1 results.
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git-rebase master &&
@@ -149,7 +149,7 @@
 
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -164,7 +164,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -179,7 +179,7 @@
 
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -195,7 +195,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard master &&
 	git cherry-pick side^ &&
@@ -208,7 +208,7 @@
 test_expect_success 'rebase --merge (U/U)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git-rebase --merge master &&
@@ -219,7 +219,7 @@
 test_expect_success 'rebase --merge (U/L)' '
 	git config i18n.commitencoding UTF-8 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-utf8.txt &&
+	. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 
 	git reset --hard side &&
 	git-rebase --merge master &&
@@ -231,7 +231,7 @@
 	# In this test we want ISO-8859-1 encoded commits as the result
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding ISO-8859-1 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git-rebase --merge master &&
@@ -244,7 +244,7 @@
 	# to get ISO-8859-1 results.
 	git config i18n.commitencoding ISO-8859-1 &&
 	git config i18n.logoutputencoding UTF-8 &&
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 
 	git reset --hard side &&
 	git-rebase --merge master &&
diff --git a/t/t4000-diff-format.sh b/t/t4000-diff-format.sh
index c44b27a..6ddd469 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh
index a326924..71bac83 100755
--- a/t/t4001-diff-rename.sh
+++ b/t/t4001-diff-rename.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 echo >path0 'Line 1
 Line 2
diff --git a/t/t4002-diff-basic.sh b/t/t4002-diff-basic.sh
index a4cfde6..cc3681f 100755
--- a/t/t4002-diff-basic.sh
+++ b/t/t4002-diff-basic.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../lib-read-tree-m-3way.sh
+. "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
 cat >.test-plain-OA <<\EOF
 :000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A	AA
@@ -169,6 +169,20 @@
      cmp -s .test-a .test-recursive-AB'
 
 test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-plain-ABx &&
+     cat .test-plain-AB >> .test-plain-ABx &&
+     cmp -s .test-a .test-plain-ABx'
+
+test_expect_success \
+    'diff-tree --stdin of known trees.' \
+    'echo $tree_A $tree_B | git diff-tree -r --stdin > .test-a &&
+     echo $tree_A $tree_B > .test-recursive-ABx &&
+     cat .test-recursive-AB >> .test-recursive-ABx &&
+     cmp -s .test-a .test-recursive-ABx'
+
+test_expect_success \
     'diff-cache O with A in cache' \
     'git read-tree $tree_A &&
      git diff-index --cached $tree_O >.test-a &&
diff --git a/t/t4003-diff-rename-1.sh b/t/t4003-diff-rename-1.sh
index 8b1f875..c6130c4 100755
--- a/t/t4003-diff-rename-1.sh
+++ b/t/t4003-diff-rename-1.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
diff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh
index 3d25be7..b35af9b 100755
--- a/t/t4004-diff-rename-symlink.sh
+++ b/t/t4004-diff-rename-symlink.sh
@@ -10,7 +10,7 @@
 by an edit for them.
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 test_expect_success \
     'prepare reference tree' \
diff --git a/t/t4005-diff-rename-2.sh b/t/t4005-diff-rename-2.sh
index 6630017..1ba359d 100755
--- a/t/t4005-diff-rename-2.sh
+++ b/t/t4005-diff-rename-2.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -71,7 +71,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -C --find-copies-harder $tree >current
diff --git a/t/t4007-rename-3.sh b/t/t4007-rename-3.sh
index 104a4e1..42072d7 100755
--- a/t/t4007-rename-3.sh
+++ b/t/t4007-rename-3.sh
@@ -7,12 +7,12 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git update-index --add path0/COPYING &&
     tree=$(git write-tree) &&
     echo $tree'
diff --git a/t/t4008-diff-break-rewrite.sh b/t/t4008-diff-break-rewrite.sh
index 26c2e4a..7e343a9 100755
--- a/t/t4008-diff-break-rewrite.sh
+++ b/t/t4008-diff-break-rewrite.sh
@@ -22,12 +22,12 @@
 Further, with -B and -M together, these should turn into two renames.
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
-    'cat ../../README >file0 &&
-     cat ../../COPYING >file1 &&
+    'cat "$TEST_DIRECTORY"/../README >file0 &&
+     cat "$TEST_DIRECTORY"/../COPYING >file1 &&
     git update-index --add file0 file1 &&
     tree=$(git write-tree) &&
     echo "$tree"'
diff --git a/t/t4009-diff-rename-4.sh b/t/t4009-diff-rename-4.sh
index d2b45e7..de3f174 100755
--- a/t/t4009-diff-rename-4.sh
+++ b/t/t4009-diff-rename-4.sh
@@ -7,11 +7,11 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -78,7 +78,7 @@
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat ../../COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/../COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
diff --git a/t/t4010-diff-pathspec.sh b/t/t4010-diff-pathspec.sh
index ad3d9e4..9322298 100755
--- a/t/t4010-diff-pathspec.sh
+++ b/t/t4010-diff-pathspec.sh
@@ -10,7 +10,7 @@
         path1/file1
 '
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
diff --git a/t/t4011-diff-symlink.sh b/t/t4011-diff-symlink.sh
index c6d1369..02efeca 100755
--- a/t/t4011-diff-symlink.sh
+++ b/t/t4011-diff-symlink.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 cat > expected << EOF
 diff --git a/frotz b/frotz
diff --git a/t/t4012-diff-binary.sh b/t/t4012-diff-binary.sh
index eced1f3..6993499 100755
--- a/t/t4012-diff-binary.sh
+++ b/t/t4012-diff-binary.sh
@@ -12,7 +12,7 @@
 	'echo AIT >a && echo BIT >b && echo CIT >c && echo DIT >d &&
 	 git update-index --add a b c d &&
 	 echo git >a &&
-	 cat ../test4012.png >b &&
+	 cat "$TEST_DIRECTORY"/test4012.png >b &&
 	 echo git >c &&
 	 cat b b >d'
 
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 9337b81..1a6b522 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -99,7 +99,7 @@
 	test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
 	cnt=`expr $test_count + 1`
 	pfx=`printf "%04d" $cnt`
-	expect="../t4013/diff.$test"
+	expect="$TEST_DIRECTORY/t4013/diff.$test"
 	actual="$pfx-diff.$test"
 
 	test_expect_success "git $cmd" '
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 7fe853c..9d99dc2 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -230,4 +230,29 @@
 
 '
 
+cat > expect << EOF
+---
+ file |   16 ++++++++++++++++
+ 1 files changed, 16 insertions(+), 0 deletions(-)
+
+diff --git a/file b/file
+index 40f36c6..2dc5c23 100644
+--- a/file
++++ b/file
+@@ -13,4 +13,20 @@ C
+ 10
+ D
+ E
+ F
++5
+EOF
+
+test_expect_success 'format-patch respects -U' '
+
+	git format-patch -U4 -2 &&
+	sed -e "1,/^$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+	test_cmp expect output
+
+'
+
 test_done
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index b1cbd36..fc2307e 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -7,7 +7,7 @@
 
 '
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 # Ray Lehtiniemi's example
 
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 637b4e1..dfe3fbc 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -104,7 +104,7 @@
 test_expect_success 'force diff with "diff"' '
 	echo >.gitattributes "file diff" &&
 	git diff >actual &&
-	test_cmp ../t4020/diff.NUL actual
+	test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
 '
 
 test_done
diff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh
index bf996fc..2a537a2 100755
--- a/t/t4022-diff-rewrite.sh
+++ b/t/t4022-diff-rewrite.sh
@@ -6,12 +6,12 @@
 
 test_expect_success setup '
 
-	cat ../../COPYING >test &&
+	cat "$TEST_DIRECTORY"/../COPYING >test &&
 	git add test &&
 	tr \
 	  "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" \
 	  "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM" \
-	  <../../COPYING >test
+	  <"$TEST_DIRECTORY"/../COPYING >test
 
 '
 
diff --git a/t/t4023-diff-rename-typechange.sh b/t/t4023-diff-rename-typechange.sh
index 4dbfc6e..297ddb5 100755
--- a/t/t4023-diff-rename-typechange.sh
+++ b/t/t4023-diff-rename-typechange.sh
@@ -7,21 +7,21 @@
 test_expect_success setup '
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
 	ln -s linklink bar &&
 	git add foo bar &&
 	git commit -a -m Initial &&
 	git tag one &&
 
 	rm -f foo bar &&
-	cat ../../COPYING >bar &&
+	cat "$TEST_DIRECTORY"/../COPYING >bar &&
 	ln -s linklink foo &&
 	git add foo bar &&
 	git commit -a -m Second &&
 	git tag two &&
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
 	git add foo &&
 	git commit -a -m Third &&
 	git tag three &&
@@ -35,15 +35,15 @@
 	# This is purely for sanity check
 
 	rm -f foo bar &&
-	cat ../../COPYING >foo &&
-	cat ../../Makefile >bar &&
+	cat "$TEST_DIRECTORY"/../COPYING >foo &&
+	cat "$TEST_DIRECTORY"/../Makefile >bar &&
 	git add foo bar &&
 	git commit -a -m Fifth &&
 	git tag five &&
 
 	rm -f foo bar &&
-	cat ../../Makefile >foo &&
-	cat ../../COPYING >bar &&
+	cat "$TEST_DIRECTORY"/../Makefile >foo &&
+	cat "$TEST_DIRECTORY"/../COPYING >bar &&
 	git add foo bar &&
 	git commit -a -m Sixth &&
 	git tag six
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index ba6679c..1c2edeb 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -3,7 +3,7 @@
 test_description='difference in submodules'
 
 . ./test-lib.sh
-. ../diff-lib.sh
+. "$TEST_DIRECTORY"/diff-lib.sh
 
 _z40=0000000000000000000000000000000000000000
 test_expect_success setup '
diff --git a/t/t4029-diff-trailing-space.sh b/t/t4029-diff-trailing-space.sh
new file mode 100755
index 0000000..4ca65e0
--- /dev/null
+++ b/t/t4029-diff-trailing-space.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+#
+# Copyright (c) Jim Meyering
+#
+test_description='diff honors config option, diff.suppress-blank-empty'
+
+. ./test-lib.sh
+
+cat <<\EOF > exp ||
+diff --git a/f b/f
+index 5f6a263..8cb8bae 100644
+--- a/f
++++ b/f
+@@ -1,2 +1,2 @@
+
+-x
++y
+EOF
+exit 1
+
+test_expect_success \
+    "$test_description" \
+    'printf "\nx\n" > f &&
+     git add f &&
+     git commit -q -m. f &&
+     printf "\ny\n" > f &&
+     git config --bool diff.suppress-blank-empty true &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     perl -i.bak -p -e "s/^\$/ /" exp &&
+     git config --bool diff.suppress-blank-empty false &&
+     git diff f > actual &&
+     test_cmp exp actual &&
+     git config --bool --unset diff.suppress-blank-empty &&
+     git diff f > actual &&
+     test_cmp exp actual
+     '
+
+test_done
diff --git a/t/t4100-apply-stat.sh b/t/t4100-apply-stat.sh
index e0c6774..9b433de 100755
--- a/t/t4100-apply-stat.sh
+++ b/t/t4100-apply-stat.sh
@@ -17,13 +17,13 @@
 	test_expect_success "$title" '
 		git apply --stat --summary \
 			<"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-		test_cmp ../t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 
 	test_expect_success "$title with recount" '
 		sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
 		git apply --recount --stat --summary >current &&
-		test_cmp ../t4100/t-apply-$num.expect current
+		test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
 	'
 done <<\EOF
 rename
diff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh
index da8abcf..e3443d0 100755
--- a/t/t4101-apply-nonl.sh
+++ b/t/t4101-apply-nonl.sh
@@ -21,9 +21,10 @@
   do
     test $i -eq $j && continue
     cat frotz.$i >frotz
-    test_expect_success \
-        "apply diff between $i and $j" \
-	"git apply <../t4101/diff.$i-$j && diff frotz.$j frotz"
+    test_expect_success "apply diff between $i and $j" '
+	git apply <"$TEST_DIRECTORY"/t4101/diff.$i-$j &&
+	test_cmp frotz.$j frotz
+    '
   done
 done
 
diff --git a/t/t5100-mailinfo.sh b/t/t5100-mailinfo.sh
index 198e350..fe14589 100755
--- a/t/t5100-mailinfo.sh
+++ b/t/t5100-mailinfo.sh
@@ -8,27 +8,28 @@
 . ./test-lib.sh
 
 test_expect_success 'split sample box' \
-	'git mailsplit -o. ../t5100/sample.mbox >last &&
+	'git mailsplit -o. "$TEST_DIRECTORY"/t5100/sample.mbox >last &&
 	last=`cat last` &&
 	echo total is $last &&
 	test `cat last` = 11'
 
 for mail in `echo 00*`
 do
-	test_expect_success "mailinfo $mail" \
-		"git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
+	test_expect_success "mailinfo $mail" '
+		git mailinfo -u msg$mail patch$mail <$mail >info$mail &&
 		echo msg &&
-		diff ../t5100/msg$mail msg$mail &&
+		test_cmp "$TEST_DIRECTORY"/t5100/msg$mail msg$mail &&
 		echo patch &&
-		diff ../t5100/patch$mail patch$mail &&
+		test_cmp "$TEST_DIRECTORY"/t5100/patch$mail patch$mail &&
 		echo info &&
-		diff ../t5100/info$mail info$mail"
+		test_cmp "$TEST_DIRECTORY"/t5100/info$mail info$mail
+	'
 done
 
 test_expect_success 'respect NULs' '
 
-	git mailsplit -d3 -o. ../t5100/nul-plain &&
-	cmp ../t5100/nul-plain 001 &&
+	git mailsplit -d3 -o. "$TEST_DIRECTORY"/t5100/nul-plain &&
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-plain 001 &&
 	(cat 001 | git mailinfo msg patch) &&
 	test 4 = $(wc -l < patch)
 
@@ -36,10 +37,10 @@
 
 test_expect_success 'Preserve NULs out of MIME encoded message' '
 
-	git mailsplit -d5 -o. ../t5100/nul-b64.in &&
-	cmp ../t5100/nul-b64.in 00001 &&
+	git mailsplit -d5 -o. "$TEST_DIRECTORY"/t5100/nul-b64.in &&
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.in 00001 &&
 	git mailinfo msg patch <00001 &&
-	cmp ../t5100/nul-b64.expect patch
+	test_cmp "$TEST_DIRECTORY"/t5100/nul-b64.expect patch
 
 '
 
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 362cf7e..7125bae 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -137,7 +137,7 @@
 	"test \"in-pack: 18\" = \"$(grep in-pack count.shallow)\""
 
 count_output () {
-	sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/: 0$/d' "$1"
+	sed -e '/^in-pack:/d' -e '/^packs:/d' -e '/^size-pack:/d' -e '/: 0$/d' "$1"
 }
 
 test_expect_success "clone shallow object count (part 2)" '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 8becbc3..1f4608d 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -131,9 +131,9 @@
 	test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
 	cnt=`expr $test_count + 1`
 	pfx=`printf "%04d" $cnt`
-	expect_f="../../t5515/fetch.$test"
+	expect_f="$TEST_DIRECTORY/t5515/fetch.$test"
 	actual_f="$pfx-fetch.$test"
-	expect_r="../../t5515/refs.$test"
+	expect_r="$TEST_DIRECTORY/t5515/refs.$test"
 	actual_r="$pfx-refs.$test"
 
 	test_expect_success "$cmd" '
diff --git a/t/t5540-http-push.sh b/t/t5540-http-push.sh
index b0d242e..da95886 100755
--- a/t/t5540-http-push.sh
+++ b/t/t5540-http-push.sh
@@ -19,7 +19,7 @@
 	exit
 fi
 
-. ../lib-httpd.sh
+. "$TEST_DIRECTORY"/lib-httpd.sh
 
 if ! start_httpd >&3 2>&4
 then
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index 8f5de09..b4e8fba 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -5,7 +5,7 @@
 test_description='Tests git rev-list --bisect functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 # usage: test_bisection max-diff bisect-option head ^prune...
 #
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 5daa0be..2c73f2d 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -6,7 +6,7 @@
 test_description='Tests git rev-list --topo-order functionality'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 list_duplicates()
 {
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index b6e57b2..04e4b7c 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -108,4 +108,52 @@
     'MB=$(git merge-base --all PL PR) &&
      expr "$(git name-rev "$MB")" : "[0-9a-f]* tags/C2"'
 
+# Another set to demonstrate base between one commit and a merge
+# in the documentation.
+
+test_expect_success 'merge-base for octopus-step (setup)' '
+	test_tick && git commit --allow-empty -m root && git tag MMR &&
+	test_tick && git commit --allow-empty -m 1 && git tag MM1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m A && git tag MMA &&
+	git checkout MM1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m B && git tag MMB &&
+	git checkout MMR &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m C && git tag MMC
+'
+
+test_expect_success 'merge-base A B C' '
+	MB=$(git merge-base --all MMA MMB MMC) &&
+	MM1=$(git rev-parse --verify MM1) &&
+	test "$MM1" = "$MB"
+'
+
+test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
+	git reset --hard MMR &&
+	test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
+	git reset --hard E &&
+	test_tick && git commit --allow-empty -m 2 && git tag CC2 &&
+	test_tick && git merge -s ours CC1 &&
+	test_tick && git commit --allow-empty -m o &&
+	test_tick && git commit --allow-empty -m B && git tag CCB &&
+	git reset --hard CC1 &&
+	test_tick && git merge -s ours CC2 &&
+	test_tick && git commit --allow-empty -m A && git tag CCA
+'
+
+test_expect_success 'merge-base B A^^ A^^2' '
+	MB0=$(git merge-base --all CCB CCA^^ CCA^^2 | sort) &&
+	MB1=$(git rev-parse CC1 CC2 | sort) &&
+	test "$MB0" = "$MB1"
+'
+
 test_done
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index f674c48..42620e0 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -136,7 +136,7 @@
 
 test_expect_success 'binary files cannot be merged' '
 	test_must_fail git merge-file -p \
-		orig.txt ../test4012.png new1.txt 2> merge.err &&
+		orig.txt "$TEST_DIRECTORY"/test4012.png new1.txt 2> merge.err &&
 	grep "Cannot merge binary files" merge.err
 '
 
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index 92ca1f0..b519626 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -6,7 +6,7 @@
 
 test_expect_success setup '
 
-	cat ../test4012.png >m &&
+	cat "$TEST_DIRECTORY"/test4012.png >m &&
 	git add m &&
 	git ls-files -s | sed -e "s/ 0	/ 1	/" >E1 &&
 	test_tick &&
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index 919552a..f105fab 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -6,7 +6,7 @@
 test_description='Test git rev-parse with different parent options'
 
 . ./test-lib.sh
-. ../t6000lib.sh # t6xxx specific functions
+. "$TEST_DIRECTORY"/t6000lib.sh # t6xxx specific functions
 
 date >path0
 git update-index --add path0
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
index bc74349..8f5a06f 100755
--- a/t/t6200-fmt-merge-msg.sh
+++ b/t/t6200-fmt-merge-msg.sh
@@ -83,13 +83,13 @@
 '
 
 cat >expected <<EOF
-Merge branch 'left' of ../$test
+Merge branch 'left' of $TEST_DIRECTORY/$test
 EOF
 
 test_expect_success 'merge-msg test #2' '
 
 	git checkout master &&
-	git fetch ../"$test" left &&
+	git fetch "$TEST_DIRECTORY/$test" left &&
 
 	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
 	test_cmp expected actual
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 910a28c..7816798 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -6,7 +6,7 @@
 test_expect_success \
     'prepare reference tree' \
     'mkdir path0 path1 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git-commit -m add -a'
 
@@ -40,7 +40,7 @@
 
 test_expect_success \
     'adding another file' \
-    'cp ../../README path0/README &&
+    'cp "$TEST_DIRECTORY"/../README path0/README &&
      git add path0/README &&
      git-commit -m add2 -a'
 
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index a0ab096..f92d414 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -250,4 +250,12 @@
 	test_cmp expect actual
 '
 
+test_expect_success 'Tag name filtering allows slashes in tag names' '
+	git tag -m tag-with-slash X/1 &&
+	git cat-file tag X/1 | sed -e s,X/1,X/2, > expect &&
+	git filter-branch -f --tag-name-filter "echo X/2" &&
+	git cat-file tag X/2 > actual &&
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 8d44c2e..198244c 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -625,7 +625,7 @@
 # Name and email: C O Mitter <committer@example.com>
 # No password given, to enable non-interactive operation.
 
-cp -R ../t7004 ./gpghome
+cp -R "$TEST_DIRECTORY"/t7004 ./gpghome
 chmod 0700 gpghome
 GNUPGHOME="$(pwd)/gpghome"
 export GNUPGHOME
diff --git a/t/t7101-reset.sh b/t/t7101-reset.sh
index 0d9874b..ffaeb39 100755
--- a/t/t7101-reset.sh
+++ b/t/t7101-reset.sh
@@ -9,7 +9,7 @@
 test_expect_success \
     'creating initial files' \
     'mkdir path0 &&
-     cp ../../COPYING path0/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING &&
      git add path0/COPYING &&
      git-commit -m add -a'
 
@@ -17,10 +17,10 @@
     'creating second files' \
     'mkdir path1 &&
      mkdir path1/path2 &&
-     cp ../../COPYING path1/path2/COPYING &&
-     cp ../../COPYING path1/COPYING &&
-     cp ../../COPYING COPYING &&
-     cp ../../COPYING path0/COPYING-TOO &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/path2/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path1/COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING COPYING &&
+     cp "$TEST_DIRECTORY"/../COPYING path0/COPYING-TOO &&
      git add path1/path2/COPYING &&
      git add path1/COPYING &&
      git add COPYING &&
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 9ad5d63..1dff84d 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -337,4 +337,36 @@
     test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
     test_must_fail git checkout --track -b track'
 
+test_expect_success \
+    'checkout with --track fakes a sensible -b <name>' '
+    git update-ref refs/remotes/origin/koala/bear renamer &&
+    git update-ref refs/new/koala/bear renamer &&
+
+    git checkout --track origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track remotes/origin/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)" &&
+
+    git checkout master && git branch -D koala/bear &&
+
+    git checkout --track refs/new/koala/bear &&
+    test "refs/heads/koala/bear" = "$(git symbolic-ref HEAD)" &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse renamer)"
+'
+
+test_expect_success \
+    'checkout with --track, but without -b, fails with too short tracked name' '
+    test_must_fail git checkout --track renamer'
+
 test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 809bdba..7ae0bd0 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -46,15 +46,24 @@
 '
 
 test_expect_success 'a Signed-off-by line by itself should not commit' '
-	! GIT_EDITOR=../t7500/add-signed-off git commit --template "$TEMPLATE"
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-signed-off &&
+		test_must_fail git commit --template "$TEMPLATE"
+	)
 '
 
 test_expect_success 'adding comments to a template should not commit' '
-	! GIT_EDITOR=../t7500/add-comments git commit --template "$TEMPLATE"
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-comments &&
+		test_must_fail git commit --template "$TEMPLATE"
+	)
 '
 
 test_expect_success 'adding real content to a template should commit' '
-	GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit --template "$TEMPLATE"
+	) &&
 	commit_msg_is "template linecommit message"
 '
 
@@ -62,7 +71,10 @@
 	echo "short template" > "$TEMPLATE" &&
 	echo "new content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit -t "$TEMPLATE" &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit -t "$TEMPLATE"
+	) &&
 	commit_msg_is "short templatecommit message"
 '
 
@@ -71,7 +83,10 @@
 	git config commit.template "$TEMPLATE" &&
 	echo "more content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit
+	) &&
 	git config --unset commit.template &&
 	commit_msg_is "new templatecommit message"
 '
@@ -79,7 +94,7 @@
 test_expect_success 'explicit commit message should override template' '
 	echo "still more content" >> foo &&
 	git add foo &&
-	GIT_EDITOR=../t7500/add-content git commit --template "$TEMPLATE" \
+	GIT_EDITOR="$TEST_DIRECTORY"/t7500/add-content git commit --template "$TEMPLATE" \
 		-m "command line msg" &&
 	commit_msg_is "command line msg"
 '
@@ -88,8 +103,10 @@
 	echo "content galore" >> foo &&
 	git add foo &&
 	echo "standard input msg" |
-		GIT_EDITOR=../t7500/add-content git commit \
-			--template "$TEMPLATE" --file - &&
+	(
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit --template "$TEMPLATE" --file -
+	) &&
 	commit_msg_is "standard input msg"
 '
 
@@ -132,10 +149,12 @@
 
 test_expect_success '--signoff' '
 	echo "yet another content *narf*" >> foo &&
-	echo "zort" |
-		GIT_EDITOR=../t7500/add-content git commit -s -F - foo &&
+	echo "zort" | (
+		test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
+		git commit -s -F - foo
+	) &&
 	git cat-file commit HEAD | sed "1,/^$/d" > output &&
-	diff expect output
+	test_cmp expect output
 '
 
 test_expect_success 'commit message from file (1)' '
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index dbc90bc..6a2b125 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -230,6 +230,10 @@
 	test_must_fail git merge
 '
 
+test_expect_success 'reject non-strategy with a git-merge-foo name' '
+	test_must_fail git merge -s index c1
+'
+
 test_expect_success 'merge c0 with c1' '
 	git reset --hard c0 &&
 	git merge c1 &&
diff --git a/t/t7606-merge-custom.sh b/t/t7606-merge-custom.sh
new file mode 100755
index 0000000..de9b6ed
--- /dev/null
+++ b/t/t7606-merge-custom.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Testing a custom strategy.'
+
+. ./test-lib.sh
+
+cat >git-merge-theirs <<EOF
+#!$SHELL_PATH
+eval git read-tree --reset -u \\\$\$#
+EOF
+chmod +x git-merge-theirs
+PATH=.:$PATH
+export PATH
+
+test_expect_success 'setup' '
+	echo c0 >c0.c &&
+	git add c0.c &&
+	git commit -m c0 &&
+	git tag c0 &&
+	echo c1 >c1.c &&
+	git add c1.c &&
+	git commit -m c1 &&
+	git tag c1 &&
+	git reset --hard c0 &&
+	echo c1c1 >c1.c &&
+	echo c2 >c2.c &&
+	git add c1.c c2.c &&
+	git commit -m c2 &&
+	git tag c2
+'
+
+test_expect_success 'merge c2 with a custom strategy' '
+	git reset --hard c1 &&
+	git merge -s theirs c2 &&
+	test "$(git rev-parse c1)" != "$(git rev-parse HEAD)" &&
+	test "$(git rev-parse c1)" = "$(git rev-parse HEAD^1)" &&
+	test "$(git rev-parse c2)" = "$(git rev-parse HEAD^2)" &&
+	test "$(git rev-parse c2^{tree})" = "$(git rev-parse HEAD^{tree})" &&
+	git diff --exit-code &&
+	git diff --exit-code c2 HEAD &&
+	git diff --exit-code c2 &&
+	test -f c0.c &&
+	grep c1c1 c1.c &&
+	test -f c2.c
+'
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index eabec2e..45cb60e 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -4,7 +4,7 @@
 . ./test-lib.sh
 
 PROG='git annotate'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_expect_success \
     'Annotating an old revision works' \
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index 92ece30..597cf04 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -4,6 +4,6 @@
 . ./test-lib.sh
 
 PROG='git blame -c'
-. ../annotate-tests.sh
+. "$TEST_DIRECTORY"/annotate-tests.sh
 
 test_done
diff --git a/t/t9110-git-svn-use-svm-props.sh b/t/t9110-git-svn-use-svm-props.sh
index 04d2a65..83bd1cf 100755
--- a/t/t9110-git-svn-use-svm-props.sh
+++ b/t/t9110-git-svn-use-svm-props.sh
@@ -8,7 +8,7 @@
 . ./lib-git-svn.sh
 
 test_expect_success 'load svm repo' '
-	svnadmin load -q "$rawsvnrepo" < ../t9110/svm.dump &&
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9110/svm.dump &&
 	git-svn init --minimize-url -R arr -i bar "$svnrepo"/mirror/arr &&
 	git-svn init --minimize-url -R argh -i dir "$svnrepo"/mirror/argh &&
 	git-svn init --minimize-url -R argh -i e \
diff --git a/t/t9111-git-svn-use-svnsync-props.sh b/t/t9111-git-svn-use-svnsync-props.sh
index a8d74dc..c5dfd61 100755
--- a/t/t9111-git-svn-use-svnsync-props.sh
+++ b/t/t9111-git-svn-use-svnsync-props.sh
@@ -8,7 +8,7 @@
 . ./lib-git-svn.sh
 
 test_expect_success 'load svnsync repo' '
-	svnadmin load -q "$rawsvnrepo" < ../t9111/svnsync.dump &&
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9111/svnsync.dump &&
 	git-svn init --minimize-url -R arr -i bar "$svnrepo"/bar &&
 	git-svn init --minimize-url -R argh -i dir "$svnrepo"/dir &&
 	git-svn init --minimize-url -R argh -i e "$svnrepo"/dir/a/b/c/d/e &&
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index f0fbd3a..b0ba1f0 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -8,7 +8,7 @@
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
-	svnadmin load -q "$rawsvnrepo" < ../t9115/funky-names.dump &&
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9115/funky-names.dump &&
 	start_httpd gtk+
 	'
 
diff --git a/t/t9121-git-svn-fetch-renamed-dir.sh b/t/t9121-git-svn-fetch-renamed-dir.sh
index 99230b0..92e69a2 100755
--- a/t/t9121-git-svn-fetch-renamed-dir.sh
+++ b/t/t9121-git-svn-fetch-renamed-dir.sh
@@ -8,7 +8,7 @@
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with renamed directory' '
-	svnadmin load -q "$rawsvnrepo" < ../t9121/renamed-dir.dump
+	svnadmin load -q "$rawsvnrepo" < "$TEST_DIRECTORY"/t9121/renamed-dir.dump
 	'
 
 test_expect_success 'init and fetch repository' '
diff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh
index 3e32e84..988c3ac 100755
--- a/t/t9200-git-cvsexportcommit.sh
+++ b/t/t9200-git-cvsexportcommit.sh
@@ -45,8 +45,8 @@
     'mkdir A B C D E F &&
      echo hello1 >A/newfile1.txt &&
      echo hello2 >B/newfile2.txt &&
-     cp ../test9200a.png C/newfile3.png &&
-     cp ../test9200a.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png C/newfile3.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png D/newfile4.png &&
      git add A/newfile1.txt &&
      git add B/newfile2.txt &&
      git add C/newfile3.png &&
@@ -71,8 +71,8 @@
      rm -f B/newfile2.txt &&
      rm -f C/newfile3.png &&
      echo Hello5  >E/newfile5.txt &&
-     cp ../test9200b.png D/newfile4.png &&
-     cp ../test9200a.png F/newfile6.png &&
+     cp "$TEST_DIRECTORY"/test9200b.png D/newfile4.png &&
+     cp "$TEST_DIRECTORY"/test9200a.png F/newfile6.png &&
      git add E/newfile5.txt &&
      git add F/newfile6.png &&
      git commit -a -m "Test: Remove, add and update" &&
@@ -160,7 +160,7 @@
      'mkdir "G g" &&
       echo ok then >"G g/with spaces.txt" &&
       git add "G g/with spaces.txt" && \
-      cp ../test9200a.png "G g/with spaces.png" && \
+      cp "$TEST_DIRECTORY"/test9200a.png "G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "With spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -172,7 +172,7 @@
 test_expect_success \
      'Update file with spaces in file name' \
      'echo Ok then >>"G g/with spaces.txt" &&
-      cat ../test9200a.png >>"G g/with spaces.png" && \
+      cat "$TEST_DIRECTORY"/test9200a.png >>"G g/with spaces.png" && \
       git add "G g/with spaces.png" &&
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
@@ -197,7 +197,7 @@
      'mkdir -p Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö &&
       echo Foo >Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.txt &&
-      cp ../test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
+      cp "$TEST_DIRECTORY"/test9200a.png Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git add Å/goo/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/å/ä/ö/gårdetsågårdet.png &&
       git commit -a -m "Går det så går det" && \
       id=$(git rev-list --max-count=1 HEAD) &&
diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh
index c6bc0a6..bd5d5af 100755
--- a/t/t9300-fast-import.sh
+++ b/t/t9300-fast-import.sh
@@ -5,7 +5,7 @@
 
 test_description='test git-fast-import utility'
 . ./test-lib.sh
-. ../diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
 
 file2_data='file2
 second line of EOF'
diff --git a/t/t9301-fast-export.sh b/t/t9301-fast-export.sh
index c19b4a2..3cb9f80 100755
--- a/t/t9301-fast-export.sh
+++ b/t/t9301-fast-export.sh
@@ -67,7 +67,7 @@
 
 	git config i18n.commitencoding ISO-8859-1 &&
 	# use author and committer name in ISO-8859-1 to match it.
-	. ../t3901-8859-1.txt &&
+	. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	test_tick &&
 	echo rosten >file &&
 	git commit -s -m den file &&
diff --git a/t/t9500-gitweb-standalone-no-errors.sh b/t/t9500-gitweb-standalone-no-errors.sh
index ae7082b..46ba19b 100755
--- a/t/t9500-gitweb-standalone-no-errors.sh
+++ b/t/t9500-gitweb-standalone-no-errors.sh
@@ -25,9 +25,9 @@
 our \$site_header = "";
 our \$site_footer = "";
 our \$home_text = "indextext.html";
-our @stylesheets = ("file:///$safe_pwd/../../gitweb/gitweb.css");
-our \$logo = "file:///$safe_pwd/../../gitweb/git-logo.png";
-our \$favicon = "file:///$safe_pwd/../../gitweb/git-favicon.png";
+our @stylesheets = ("file:///$TEST_DIRECTORY/../gitweb/gitweb.css");
+our \$logo = "file:///$TEST_DIRECTORY/../gitweb/git-logo.png";
+our \$favicon = "file:///$TEST_DIRECTORY/../gitweb/git-favicon.png";
 our \$projects_list = "";
 our \$export_ok = "";
 our \$strict_export = "";
@@ -54,7 +54,7 @@
 	# written to web server logs, so we are not interested in that:
 	# we are interested only in properly formatted errors/warnings
 	rm -f gitweb.log &&
-	perl -- "$(pwd)/../../gitweb/gitweb.perl" \
+	perl -- "$TEST_DIRECTORY/../gitweb/gitweb.perl" \
 		>/dev/null 2>gitweb.log &&
 	if grep -q -s "^[[]" gitweb.log >/dev/null; then false; else true; fi
 
@@ -525,20 +525,20 @@
 
 test_expect_success \
 	'encode(commit): utf8' \
-	'. ../t3901-utf8.txt &&
+	'. "$TEST_DIRECTORY"/t3901-utf8.txt &&
 	 echo "UTF-8" >> file &&
 	 git add file &&
-	 git commit -F ../t3900/1-UTF-8.txt &&
+	 git commit -F "$TEST_DIRECTORY"/t3900/1-UTF-8.txt &&
 	 gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
 
 test_expect_success \
 	'encode(commit): iso-8859-1' \
-	'. ../t3901-8859-1.txt &&
+	'. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
 	 echo "ISO-8859-1" >> file &&
 	 git add file &&
 	 git config i18n.commitencoding ISO-8859-1 &&
-	 git commit -F ../t3900/ISO-8859-1.txt &&
+	 git commit -F "$TEST_DIRECTORY"/t3900/ISO-8859-1.txt &&
 	 git config --unset i18n.commitencoding &&
 	 gitweb_run "p=.git;a=commit"'
 test_debug 'cat gitweb.log'
diff --git a/t/t9700-perl-git.sh b/t/t9700-perl-git.sh
index 9706ee5..0f04ba0 100755
--- a/t/t9700-perl-git.sh
+++ b/t/t9700-perl-git.sh
@@ -39,6 +39,6 @@
 
 test_external_without_stderr \
     'Perl API' \
-    perl ../t9700/test.pl
+    perl "$TEST_DIRECTORY"/t9700/test.pl
 
 test_done
diff --git a/t/t9700/test.pl b/t/t9700/test.pl
index 4d23125..851cea4 100755
--- a/t/t9700/test.pl
+++ b/t/t9700/test.pl
@@ -14,10 +14,7 @@
 BEGIN { use_ok('Git') }
 
 # set up
-our $repo_dir = "trash directory";
 our $abs_repo_dir = Cwd->cwd;
-die "this must be run by calling the t/t97* shell script(s)\n"
-    if basename(Cwd->cwd) ne $repo_dir;
 ok(our $r = Git->repository(Directory => "."), "open repository");
 
 # config
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 11c0275..e2b106c 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -406,7 +406,7 @@
 	error "bug in the test script: not 1 parameter to test-create-repo"
 	owd=`pwd`
 	repo="$1"
-	mkdir "$repo"
+	mkdir -p "$repo"
 	cd "$repo" || error "Cannot setup test environment"
 	"$GIT_EXEC_PATH/git" init "--template=$GIT_EXEC_PATH/templates/blt/" >&3 2>&4 ||
 	error "cannot run git init -- have you built things yet?"
@@ -449,6 +449,11 @@
 		# we will leave things as they are.
 
 		say_color pass "passed all $msg"
+
+		test -d "$remove_trash" &&
+		cd "$(dirname "$remove_trash")" &&
+		rm -rf "$(basename "$remove_trash")"
+
 		exit 0 ;;
 
 	*)
@@ -485,7 +490,8 @@
 . ../GIT-BUILD-OPTIONS
 
 # Test repository
-test="trash directory"
+test="trash directory.$(basename "$0" .sh)"
+test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
 rm -fr "$test" || {
 	trap - exit
 	echo >&5 "FATAL: Cannot prepare test area"
diff --git a/templates/Makefile b/templates/Makefile
index cc3fc30..0722a92 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -23,13 +23,13 @@
 
 bpsrc = $(filter-out %~,$(wildcard *--*))
 boilerplates.made : $(bpsrc)
-	$(QUIET)ls *--* 2>/dev/null | \
+	$(QUIET)umask 022 && ls *--* 2>/dev/null | \
 	while read boilerplate; \
 	do \
 		case "$$boilerplate" in *~) continue ;; esac && \
 		dst=`echo "$$boilerplate" | sed -e 's|^this|.|;s|--|/|g'` && \
 		dir=`expr "$$dst" : '\(.*\)/'` && \
-		$(INSTALL) -d -m 755 blt/$$dir && \
+		mkdir -p blt/$$dir && \
 		case "$$boilerplate" in \
 		*--) ;; \
 		*) cp -p $$boilerplate blt/$$dst ;; \
diff --git a/xdiff-interface.c b/xdiff-interface.c
index 61dc5c5..944ad98 100644
--- a/xdiff-interface.c
+++ b/xdiff-interface.c
@@ -1,5 +1,12 @@
 #include "cache.h"
 #include "xdiff-interface.h"
+#include "strbuf.h"
+
+struct xdiff_emit_state {
+	xdiff_emit_consume_fn consume;
+	void *consume_callback_data;
+	struct strbuf remainder;
+};
 
 static int parse_num(char **cp_p, int *num_p)
 {
@@ -55,13 +62,13 @@
 		unsigned long this_size;
 		ep = memchr(s, '\n', size);
 		this_size = (ep == NULL) ? size : (ep - s + 1);
-		priv->consume(priv, s, this_size);
+		priv->consume(priv->consume_callback_data, s, this_size);
 		size -= this_size;
 		s += this_size;
 	}
 }
 
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+static int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf)
 {
 	struct xdiff_emit_state *priv = priv_;
 	int i;
@@ -69,36 +76,22 @@
 	for (i = 0; i < nbuf; i++) {
 		if (mb[i].ptr[mb[i].size-1] != '\n') {
 			/* Incomplete line */
-			priv->remainder = xrealloc(priv->remainder,
-						   priv->remainder_size +
-						   mb[i].size);
-			memcpy(priv->remainder + priv->remainder_size,
-			       mb[i].ptr, mb[i].size);
-			priv->remainder_size += mb[i].size;
+			strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
 			continue;
 		}
 
 		/* we have a complete line */
-		if (!priv->remainder) {
+		if (!priv->remainder.len) {
 			consume_one(priv, mb[i].ptr, mb[i].size);
 			continue;
 		}
-		priv->remainder = xrealloc(priv->remainder,
-					   priv->remainder_size +
-					   mb[i].size);
-		memcpy(priv->remainder + priv->remainder_size,
-		       mb[i].ptr, mb[i].size);
-		consume_one(priv, priv->remainder,
-			    priv->remainder_size + mb[i].size);
-		free(priv->remainder);
-		priv->remainder = NULL;
-		priv->remainder_size = 0;
+		strbuf_add(&priv->remainder, mb[i].ptr, mb[i].size);
+		consume_one(priv, priv->remainder.buf, priv->remainder.len);
+		strbuf_reset(&priv->remainder);
 	}
-	if (priv->remainder) {
-		consume_one(priv, priv->remainder, priv->remainder_size);
-		free(priv->remainder);
-		priv->remainder = NULL;
-		priv->remainder_size = 0;
+	if (priv->remainder.len) {
+		consume_one(priv, priv->remainder.buf, priv->remainder.len);
+		strbuf_reset(&priv->remainder);
 	}
 	return 0;
 }
@@ -141,6 +134,25 @@
 	return xdl_diff(&a, &b, xpp, xecfg, xecb);
 }
 
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+		  xdiff_emit_consume_fn fn, void *consume_callback_data,
+		  xpparam_t const *xpp,
+		  xdemitconf_t const *xecfg, xdemitcb_t *xecb)
+{
+	int ret;
+	struct xdiff_emit_state state;
+
+	memset(&state, 0, sizeof(state));
+	state.consume = fn;
+	state.consume_callback_data = consume_callback_data;
+	xecb->outf = xdiff_outf;
+	xecb->priv = &state;
+	strbuf_init(&state.remainder, 0);
+	ret = xdi_diff(mf1, mf2, xpp, xecfg, xecb);
+	strbuf_release(&state.remainder);
+	return ret;
+}
+
 int read_mmfile(mmfile_t *ptr, const char *filename)
 {
 	struct stat st;
diff --git a/xdiff-interface.h b/xdiff-interface.h
index f7f791d..558492b 100644
--- a/xdiff-interface.h
+++ b/xdiff-interface.h
@@ -3,18 +3,13 @@
 
 #include "xdiff/xdiff.h"
 
-struct xdiff_emit_state;
-
 typedef void (*xdiff_emit_consume_fn)(void *, char *, unsigned long);
 
-struct xdiff_emit_state {
-	xdiff_emit_consume_fn consume;
-	char *remainder;
-	unsigned long remainder_size;
-};
-
 int xdi_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, xdemitconf_t const *xecfg, xdemitcb_t *ecb);
-int xdiff_outf(void *priv_, mmbuffer_t *mb, int nbuf);
+int xdi_diff_outf(mmfile_t *mf1, mmfile_t *mf2,
+		  xdiff_emit_consume_fn fn, void *consume_callback_data,
+		  xpparam_t const *xpp,
+		  xdemitconf_t const *xecfg, xdemitcb_t *xecb);
 int parse_hunk_header(char *line, int len,
 		      int *ob, int *on,
 		      int *nb, int *nn);