GIT 1.4.2
diff --git a/.gitignore b/.gitignore
index 7b954d5..fb0fa3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,7 @@
 git-imap-send
 git-index-pack
 git-init-db
+git-instaweb
 git-local-fetch
 git-log
 git-lost-found
@@ -106,6 +107,7 @@
 git-ssh-upload
 git-status
 git-stripspace
+git-svn
 git-svnimport
 git-symbolic-ref
 git-tag
@@ -135,4 +137,10 @@
 *.[ao]
 *.py[co]
 config.mak
+autom4te.cache
+config.log
+config.status
+config.mak.in
+config.mak.autogen
+configure
 git-blame
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 2b0efe7..0d9ffb4 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -25,10 +25,10 @@
 DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
 
 prefix?=$(HOME)
-bin=$(prefix)/bin
-mandir=$(prefix)/man
-man1=$(mandir)/man1
-man7=$(mandir)/man7
+bindir?=$(prefix)/bin
+mandir?=$(prefix)/man
+man1dir=$(mandir)/man1
+man7dir=$(mandir)/man7
 # DESTDIR=
 
 INSTALL?=install
@@ -46,15 +46,16 @@
 
 html: $(DOC_HTML)
 
+$(DOC_HTML) $(DOC_MAN1) $(DOC_MAN7): asciidoc.conf
 
 man: man1 man7
 man1: $(DOC_MAN1)
 man7: $(DOC_MAN7)
 
 install: man
-	$(INSTALL) -d -m755 $(DESTDIR)$(man1) $(DESTDIR)$(man7)
-	$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1)
-	$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7)
+	$(INSTALL) -d -m755 $(DESTDIR)$(man1dir) $(DESTDIR)$(man7dir)
+	$(INSTALL) $(DOC_MAN1) $(DESTDIR)$(man1dir)
+	$(INSTALL) $(DOC_MAN7) $(DESTDIR)$(man7dir)
 
 
 #
diff --git a/Documentation/SubmittingPatches b/Documentation/SubmittingPatches
index 8601949..90722c2 100644
--- a/Documentation/SubmittingPatches
+++ b/Documentation/SubmittingPatches
@@ -49,7 +49,7 @@
 comment on the changes you are submitting.  It is important for
 a developer to be able to "quote" your changes, using standard
 e-mail tools, so that they may comment on specific portions of
-your code.  For this reason, all patches should be submited
+your code.  For this reason, all patches should be submitted
 "inline".  WARNING: Be wary of your MUAs word-wrap
 corrupting your patch.  Do not cut-n-paste your patch; you can
 lose tabs that way if you are not careful.
diff --git a/Documentation/asciidoc.conf b/Documentation/asciidoc.conf
index 7ce7151..8196d78 100644
--- a/Documentation/asciidoc.conf
+++ b/Documentation/asciidoc.conf
@@ -9,6 +9,8 @@
 
 [attributes]
 caret=^
+startsb=[
+endsb=]
 
 ifdef::backend-docbook[]
 [gitlink-inlinemacro]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index a04c5ad..ce722a2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -83,14 +83,29 @@
 	version.
 
 core.sharedRepository::
-	If true, the repository is made shareable between several users
-	in a group (making sure all the files and objects are group-writable).
-	See gitlink:git-init-db[1]. False by default.
+	When 'group' (or 'true'), the repository is made shareable between
+	several users in a group (making sure all the files and objects are
+	group-writable). When 'all' (or 'world' or 'everybody'), the
+	repository will be readable by all users, additionally to being
+	group-shareable. When 'umask' (or 'false'), git will use permissions
+	reported by umask(2). See gitlink:git-init-db[1]. False by default.
 
 core.warnAmbiguousRefs::
 	If true, git will warn you if the ref name you passed it is ambiguous
 	and might match multiple refs in the .git/refs/ tree. True by default.
 
+core.compression::
+	An integer -1..9, indicating the compression level for objects that
+	are not in a pack file. -1 is the zlib and git default. 0 means no
+	compression, and 1..9 are various speed/size tradeoffs, 9 being
+	slowest.
+
+core.legacyheaders::
+	A boolean which enables the legacy object header format in case
+	you want to interoperate with old clients accessing the object
+	database directly (where the "http://" and "rsync://" protocols
+	count as direct access).
+
 alias.*::
 	Command aliases for the gitlink:git[1] command wrapper - e.g.
 	after defining "alias.last = cat-file commit HEAD", the invocation
@@ -104,10 +119,35 @@
 	Tells `git-apply` how to handle whitespaces, in the same way
 	as the '--whitespace' option. See gitlink:git-apply[1].
 
+pager.color::
+	A boolean to enable/disable colored output when the pager is in
+	use (default is true).
+
+diff.color::
+	When true (or `always`), always use colors in patch.
+	When false (or `never`), never.  When set to `auto`, use
+	colors only when the output is to the terminal.
+
+diff.color.<slot>::
+	Use customized color for diff colorization.  `<slot>`
+	specifies which part of the patch to use the specified
+	color, and is one of `plain` (context text), `meta`
+	(metainformation), `frag` (hunk header), `old` (removed
+	lines), or `new` (added lines).  The value for these
+	configuration variables can be one of: `normal`, `bold`,
+	`dim`, `ul`, `blink`, `reverse`, `reset`, `black`,
+	`red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, or
+	`white`.
+
 diff.renameLimit::
 	The number of files to consider when performing the copy/rename
 	detection; equivalent to the git diff option '-l'.
 
+diff.renames::
+	Tells git to detect renames.  If set to any boolean value, it
+	will enable basic rename detection.  If set to "copies" or
+	"copy", it will detect copies, as well.
+
 format.headers::
 	Additional email headers to include in a patch to be submitted
 	by mail.  See gitlink:git-format-patch[1].
@@ -166,6 +206,10 @@
 	Whether to include summaries of merged commits in newly created
 	merge commit messages. False by default.
 
+pack.window::
+	The size of the window used by gitlink:git-pack-objects[1] when no
+	window size is given on the command line. Defaults to 10.
+
 pull.octopus::
 	The default merge strategy to use when pulling multiple branches
 	at once.
@@ -181,6 +225,17 @@
 	The default set of branches for gitlink:git-show-branch[1].
 	See gitlink:git-show-branch[1].
 
+tar.umask::
+	By default, gitlink:git-tar-tree[1] sets file and directories modes
+	to 0666 or 0777. While this is both useful and acceptable for projects
+	such as the Linux Kernel, it might be excessive for other projects.
+	With this variable, it becomes possible to tell
+	gitlink:git-tar-tree[1] to apply a specific umask to the modes above.
+	The special value "user" indicates that the user's current umask will
+	be used. This should be enough for most projects, as it will lead to
+	the same permissions as gitlink:git-checkout[1] would use. The default
+	value remains 0, which means world read-write.
+
 user.email::
 	Your email address to be recorded in any newly created commits.
 	Can be overridden by the 'GIT_AUTHOR_EMAIL' and 'GIT_COMMITTER_EMAIL'
diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
index 1fbca83..d2b0bd3 100644
--- a/Documentation/cvs-migration.txt
+++ b/Documentation/cvs-migration.txt
@@ -93,7 +93,7 @@
 full shell on the machine, there is a restricted shell which only allows
 users to do git pushes and pulls; see gitlink:git-shell[1].
 
-Put all the committers should in the same group, and make the repository
+Put all the committers in the same group, and make the repository
 writable by that group:
 
 ------------------------------------------------
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index f523ec2..47ba9a4 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -4,18 +4,21 @@
 -u::
 	Synonym for "-p".
 
+--raw::
+	Generate the raw format.
+
 --patch-with-raw::
-	Generate patch but keep also the default raw diff output.
+	Synonym for "-p --raw".
 
 --stat::
-	Generate a diffstat instead of a patch.
+	Generate a diffstat.
 
 --summary::
 	Output a condensed summary of extended header information
 	such as creations, renames and mode changes.
 
 --patch-with-stat::
-	Generate patch and prepend its diffstat.
+	Synonym for "-p --stat".
 
 -z::
 	\0 line termination on output
@@ -26,10 +29,25 @@
 --name-status::
 	Show only names and status of changed files.
 
+--color::
+	Show colored diff.
+
+--no-color::
+	Turn off colored diff, even when the configuration file
+	gives the default to color output.
+
+--no-renames::
+	Turn off rename detection, even when the configuration
+	file gives the default to do so.
+
 --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.	
+	line when generating a patch format output.
+
+--binary::
+	In addition to --full-index, output "binary diff" that
+	can be applied with "git apply".
 
 --abbrev[=<n>]::
 	Instead of showing the full 40-byte hexadecimal object
@@ -94,5 +112,11 @@
 	Swap two inputs; that is, show differences from index or
 	on-disk file to tree contents.
 
+--text::
+	Treat all files as text.
+
+-a::
+	Shorthand for "--text".
+
 For more detailed explanation on these common options, see also
 link:diffcore.html[diffcore documentation].
diff --git a/Documentation/git-cvsexportcommit.txt b/Documentation/git-cvsexportcommit.txt
index 56bd3e5..092d0d6 100644
--- a/Documentation/git-cvsexportcommit.txt
+++ b/Documentation/git-cvsexportcommit.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-cvsexportcommmit' [-h] [-v] [-c] [-p] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
+'git-cvsexportcommit' [-h] [-v] [-c] [-p] [-a] [-f] [-m msgprefix] [PARENTCOMMIT] COMMITID
 
 
 DESCRIPTION
@@ -36,9 +36,13 @@
 	commit if any hunks fail to apply or there were other problems.
 
 -p::
-	Be pedantic (paranoid) when applying patches. Invokes patch with 
+	Be pedantic (paranoid) when applying patches. Invokes patch with
 	--fuzz=0
 
+-a::
+	Add authorship information. Adds Author line, and Committer (if
+	different from Author) to the message.
+
 -f::
 	Force the merge even if the files are not up to date.
 
diff --git a/Documentation/git-cvsimport.txt b/Documentation/git-cvsimport.txt
index b0c6d7c..d21d66b 100644
--- a/Documentation/git-cvsimport.txt
+++ b/Documentation/git-cvsimport.txt
@@ -116,7 +116,7 @@
 +
 It is not recommended to use this feature if you intend to
 export changes back to CVS again later with
-git-link[1]::git-cvsexportcommit.
+gitlink:git-cvsexportcommit[1].
 
 OUTPUT
 ------
diff --git a/Documentation/git-daemon.txt b/Documentation/git-daemon.txt
index 4c357da..0f7d274 100644
--- a/Documentation/git-daemon.txt
+++ b/Documentation/git-daemon.txt
@@ -11,7 +11,7 @@
 'git-daemon' [--verbose] [--syslog] [--inetd | --port=n] [--export-all]
              [--timeout=n] [--init-timeout=n] [--strict-paths]
              [--base-path=path] [--user-path | --user-path=path]
-	     [directory...]
+	     [--reuseaddr] [--detach] [--pid-file=file] [directory...]
 
 DESCRIPTION
 -----------
@@ -82,6 +82,17 @@
 --verbose::
 	Log details about the incoming connections and requested files.
 
+--reuseaddr::
+	Use SO_REUSEADDR when binding the listening socket.
+	This allows the server to restart without waiting for
+	old connections to time out.
+
+--detach::
+	Detach from the shell. Implies --syslog.
+
+--pid-file=file::
+	Save the process id in 'file'.
+
 <directory>::
 	A directory to add to the whitelist of allowed directories. Unless
 	--strict-paths is specified this will also include subdirectories
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
index 481b8b3..7248b35 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -37,7 +37,7 @@
 	commit with these flags.
 
 -q::
-	Remain silent even on nonexisting files
+	Remain silent even on nonexistent files
 
 Output format
 -------------
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index 7ab2080..228c4d9 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -8,24 +8,24 @@
 
 SYNOPSIS
 --------
-'git-diff' [ --diff-options ] <ent>{0,2} [<path>...]
+'git-diff' [ --diff-options ] <tree-ish>{0,2} [<path>...]
 
 DESCRIPTION
 -----------
-Show changes between two ents, an ent and the working tree, an
-ent and the index file, or the index file and the working tree.
+Show changes between two trees, a tree and the working tree, a
+tree and the index file, or the index file and the working tree.
 The combination of what is compared with what is determined by
-the number of ents given to the command.
+the number of trees given to the command.
 
-* When no <ent> is given, the working tree and the index
-  file is compared, using `git-diff-files`.
+* When no <tree-ish> is given, the working tree and the index
+  file are compared, using `git-diff-files`.
 
-* When one <ent> is given, the working tree and the named
-  tree is compared, using `git-diff-index`.  The option
+* When one <tree-ish> is given, the working tree and the named
+  tree are compared, using `git-diff-index`.  The option
   `--cached` can be given to compare the index file and
   the named tree.
 
-* When two <ent>s are given, these two trees are compared
+* When two <tree-ish>s are given, these two trees are compared
   using `git-diff-tree`.
 
 OPTIONS
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 4ca0014..67425dc 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -9,8 +9,9 @@
 SYNOPSIS
 --------
 [verse]
-'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach]
+'git-format-patch' [-n | -k] [-o <dir> | --stdout] [--attach] [--thread]
 	           [-s | --signoff] [--diff-options] [--start-number <n>]
+		   [--in-reply-to=Message-Id]
 		   <since>[..<until>]
 
 DESCRIPTION
@@ -35,6 +36,10 @@
 If -n is specified, instead of "[PATCH] Subject", the first line
 is formatted as "[PATCH n/m] Subject".
 
+If given --thread, git-format-patch will generate In-Reply-To and
+References headers to make the second and subsequent patch mails appear
+as replies to the first mail; this also generates a Message-Id header to
+reference.
 
 OPTIONS
 -------
@@ -63,6 +68,15 @@
 --attach::
 	Create attachments instead of inlining patches.
 
+--thread::
+	Add In-Reply-To and References headers to make the second and
+	subsequent mails appear as replies to the first.  Also generates
+	the Message-Id header to reference.
+
+--in-reply-to=Message-Id::
+	Make the first mail (or all the mails with --no-thread) appear as a
+	reply to the given Message-Id, which avoids breaking threads to
+	provide a new patch series.
 
 CONFIGURATION
 -------------
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 62a8e7f..dc76833 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -16,7 +16,7 @@
 	   [-n] [-l | --files-with-matches] [-L | --files-without-match]
 	   [-c | --count]
 	   [-A <post-context>] [-B <pre-context>] [-C <context>]
-	   [-f <file>] [-e] <pattern>
+	   [-f <file>] [-e] <pattern> [--and|--or|--not|(|)|-e <pattern>...]
 	   [<tree>...]
 	   [--] [<path>...]
 
@@ -74,16 +74,30 @@
 -e::
 	The next parameter is the pattern. This option has to be
 	used for patterns starting with - and should be used in
-	scripts passing user input to grep.
+	scripts passing user input to grep.  Multiple patterns are
+	combined by 'or'.
+
+--and | --or | --not | ( | )::
+	Specify how multiple patterns are combined using boolean
+	expressions.  `--or` is the default operator.  `--and` has
+	higher precedence than `--or`.  `-e` has to be used for all
+	patterns.
 
 `<tree>...`::
 	Search blobs in the trees for specified patterns.
 
-`--`::
+\--::
 	Signals the end of options; the rest of the parameters
 	are <path> limiters.
 
 
+Example
+-------
+
+git grep -e \'#define\' --and \( -e MAX_PATH -e PATH_MAX \)::
+	Looks for a line that has `#define` and either `MAX_PATH` or
+	`PATH_MAX`.
+
 Author
 ------
 Originally written by Linus Torvalds <torvalds@osdl.org>, later
diff --git a/Documentation/git-http-fetch.txt b/Documentation/git-http-fetch.txt
index bc1a132..3d50809 100644
--- a/Documentation/git-http-fetch.txt
+++ b/Documentation/git-http-fetch.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] <commit> <url>
+'git-http-fetch' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [--stdin] <commit> <url>
 
 DESCRIPTION
 -----------
@@ -33,6 +33,12 @@
         Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
+--stdin::
+	Instead of a commit id on the commandline (which is not expected in this
+	case), 'git-http-fetch' expects lines on stdin in the format
+
+		<commit-id>['\t'<filename-as-in--w>]
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index 8a150d8..63cd5da 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -8,33 +8,57 @@
 
 SYNOPSIS
 --------
-'git-init-db' [--template=<template_directory>] [--shared]
+'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
 
 
 OPTIONS
 -------
---template=<template_directory>::
-	Provide the directory from which templates will be used.
-	The default template directory is `/usr/share/git-core/templates`.
 
---shared::
-	Specify that the git repository is to be shared amongst several users.
+--
+
+--template=<template_directory>::
+
+Provide the directory from which templates will be used.  The default template
+directory is `/usr/share/git-core/templates`.
+
+When specified, `<template_directory>` is used as the source of the template
+files rather than the default.  The template files include some directory
+structure, some suggested "exclude patterns", and copies of non-executing
+"hook" files.  The suggested patterns and hook files are all modifiable and
+extensible.
+
+--shared[={false|true|umask|group|all|world|everybody}]::
+
+Specify that the git repository is to be shared amongst several users.  This
+allows users belonging to the same group to push into that
+repository.  When specified, the config variable "core.sharedRepository" is
+set so that files and directories under `$GIT_DIR` are created with the
+requested permissions.  When not specified, git will use permissions reported
+by umask(2).
+
+The option can have the following values, defaulting to 'group' if no value
+is given:
+
+ - 'umask' (or 'false'): Use permissions reported by umask(2). The default,
+   when `--shared` is not specified.
+
+ - 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
+   the git group may be not the primary group of all users).
+
+ - 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
+   readable by all users.
+
+--
 
 
 DESCRIPTION
 -----------
 This command creates an empty git repository - basically a `.git` directory
 with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
-templated files.
+template files.
 An initial `HEAD` file that references the HEAD of the master branch
 is also created.
 
-If `--template=<template_directory>` is specified, `<template_directory>`
-is used as the source of the template files rather than the default.
-The template files include some directory structure, some suggested
-"exclude patterns", and copies of non-executing "hook" files.  The
-suggested patterns and hook files are all modifiable and extensible.
-
 If the `$GIT_DIR` environment variable is set then it specifies a path
 to use instead of `./.git` for the base of the repository.
 
@@ -42,11 +66,6 @@
 environment variable then the sha1 directories are created underneath -
 otherwise the default `$GIT_DIR/objects` directory is used.
 
-A shared repository allows users belonging to the same group to push into that
-repository. When specifying `--shared` the config variable "core.sharedRepository" 
-is set to 'true' so that directories under `$GIT_DIR` are made group writable
-(and g+sx, since the git group may be not the primary group of all users).
-
 Running `git-init-db` in an existing repository is safe. It will not overwrite
 things that are already there. The primary reason for rerunning `git-init-db`
 is to pick up newly added templates.
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
new file mode 100644
index 0000000..7dd393b
--- /dev/null
+++ b/Documentation/git-instaweb.txt
@@ -0,0 +1,84 @@
+git-instaweb(1)
+===============
+
+NAME
+----
+git-instaweb - instantly browse your working repository in gitweb
+
+SYNOPSIS
+--------
+'git-instaweb' [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
+
+'git-instaweb' [--start] [--stop] [--restart]
+
+DESCRIPTION
+-----------
+A simple script to setup gitweb and a web server for browsing the local
+repository.
+
+OPTIONS
+-------
+
+-l|--local::
+	Only bind the web server to the local IP (127.0.0.1).
+
+-d|--httpd::
+	The HTTP daemon command-line that will be executed.
+	Command-line options may be specified here, and the
+	configuration file will be added at the end of the command-line.
+	Currently, lighttpd and apache2 are the only supported servers.
+	(Default: lighttpd)
+
+-m|--module-path::
+	The module path (only needed if httpd is Apache).
+	(Default: /usr/lib/apache2/modules)
+
+-p|--port::
+	The port number to bind the httpd to.  (Default: 1234)
+
+-b|--browser::
+
+	The web browser command-line to execute to view the gitweb page.
+	If blank, the URL of the gitweb instance will be printed to
+	stdout.  (Default: 'firefox')
+
+--start::
+	Start the httpd instance and exit.  This does not generate
+	any of the configuration files for spawning a new instance.
+
+--stop::
+	Stop the httpd instance and exit.  This does not generate
+	any of the configuration files for spawning a new instance,
+	nor does it close the browser.
+
+--restart::
+	Restart the httpd instance and exit.  This does not generate
+	any of the configuration files for spawning a new instance.
+
+CONFIGURATION
+-------------
+
+You may specify configuration in your .git/config
+
+-----------------------------------------------------------------------
+[instaweb]
+	local = true
+	httpd = apache2 -f
+	port = 4321
+	browser = konqueror
+	modulepath = /usr/lib/apache2/modules
+
+-----------------------------------------------------------------------
+
+Author
+------
+Written by Eric Wong <normalperson@yhbt.net>
+
+Documentation
+--------------
+Documentation by Eric Wong <normalperson@yhbt.net>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Documentation/git-local-fetch.txt b/Documentation/git-local-fetch.txt
index 87abec1..2fbdfe0 100644
--- a/Documentation/git-local-fetch.txt
+++ b/Documentation/git-local-fetch.txt
@@ -29,6 +29,12 @@
         Writes the commit-id into the filename under $GIT_DIR/refs/<filename> on
         the local end after the transfer is complete.
 
+--stdin::
+	Instead of a commit id on the commandline (which is not expected in this
+	case), 'git-local-fetch' expects lines on stdin in the format
+
+		<commit-id>['\t'<filename-as-in--w>]
+
 Author
 ------
 Written by Junio C Hamano <junkio@cox.net>
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
index 4d8a2ad..8520b97 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -207,7 +207,7 @@
 An example:
 
 --------------------------------------------------------------
-    $ cat .git/ignore
+    $ cat .git/info/exclude
     # ignore objects and archives, anywhere in the tree.
     *.[oa]
     $ cat Documentation/.gitignore
@@ -217,7 +217,7 @@
     !foo.html
     $ git-ls-files --ignored \
         --exclude='Documentation/*.[0-9]' \
-        --exclude-from=.git/ignore \
+        --exclude-from=.git/info/exclude \
         --exclude-per-directory=.gitignore
 --------------------------------------------------------------
 
diff --git a/Documentation/git-mailsplit.txt b/Documentation/git-mailsplit.txt
index 209e36b..5a17801 100644
--- a/Documentation/git-mailsplit.txt
+++ b/Documentation/git-mailsplit.txt
@@ -25,7 +25,7 @@
 
 -b::
 	If any file doesn't begin with a From line, assume it is a
-	single mail message instead of signalling error.
+	single mail message instead of signaling error.
 
 -d<prec>::
 	Instead of the default 4 digits with leading zeros,
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index 4ce799b..bebf30a 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -83,7 +83,7 @@
 stops before touching anything.
 
 So in the above two "failed merge" case, you do not have to
-worry about lossage of data --- you simply were not ready to do
+worry about loss of data --- you simply were not ready to do
 a merge, so no merge happened at all.  You may want to finish
 whatever you were in the middle of doing, and retry the same
 pull after you are done and ready.
diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index 39a1434..37fbf66 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -26,14 +26,14 @@
 	List all commits reachable from all refs
 
 --stdin::
-	Read from stdin, append "(<rev_name>)" to all sha1's of name'able
+	Read from stdin, append "(<rev_name>)" to all sha1's of nameable
 	commits, and pass to stdout
 
 EXAMPLE
 -------
 
 Given a commit, find out where it is relative to the local refs. Say somebody
-wrote you about that phantastic commit 33db5f4d9027a10e477ccf054b2c1ab94f74c85a.
+wrote you about that fantastic commit 33db5f4d9027a10e477ccf054b2c1ab94f74c85a.
 Of course, you look into the commit, but that only tells you what happened, but
 not the context.
 
diff --git a/Documentation/git-p4import.txt b/Documentation/git-p4import.txt
index 0858e5e..ee9e8fa 100644
--- a/Documentation/git-p4import.txt
+++ b/Documentation/git-p4import.txt
@@ -128,7 +128,7 @@
 A git tag of the form p4/xx is created for every change imported from
 the Perforce repository where xx is the Perforce changeset number.
 Therefore after the import you can use git to access any commit by its
-Perforce number, eg. git show p4/327.
+Perforce number, e.g. git show p4/327.
 
 The tag associated with the HEAD commit is also how `git-p4import`
 determines if there are new changes to incrementally import from the
@@ -143,7 +143,7 @@
 
 Notes
 -----
-You can interrupt the import (eg. ctrl-c) at any time and restart it
+You can interrupt the import (e.g. ctrl-c) at any time and restart it
 without worry.
 
 Author information is automatically determined by querying the
diff --git a/Documentation/git-pack-redundant.txt b/Documentation/git-pack-redundant.txt
index 8fb0659..7d54b17 100644
--- a/Documentation/git-pack-redundant.txt
+++ b/Documentation/git-pack-redundant.txt
@@ -29,7 +29,7 @@
 
 
 --all::
-	Processes all packs. Any filenames on the commandline are ignored.
+	Processes all packs. Any filenames on the command line are ignored.
 
 --alt-odb::
 	Don't require objects present in packs from alternate object
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index d5b5ca1..d4ae99f 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-push' [--all] [--tags] [--force] <repository> <refspec>...
+'git-push' [--all] [--tags] [-f | --force] <repository> <refspec>...
 
 DESCRIPTION
 -----------
@@ -67,7 +67,7 @@
 
 -f, \--force::
 	Usually, the command refuses to update a remote ref that is
-	not a descendent of the local ref used to overwrite it.
+	not a descendant of the local ref used to overwrite it.
 	This flag disables the check.  This can cause the
 	remote repository to lose commits; use it with care.
 
diff --git a/Documentation/git-repo-config.txt b/Documentation/git-repo-config.txt
index 803c0d5..b03d66f 100644
--- a/Documentation/git-repo-config.txt
+++ b/Documentation/git-repo-config.txt
@@ -119,8 +119,8 @@
 % git repo-config core.filemode true
 ------------
 
-The hypothetic proxy command entries actually have a postfix to discern
-to what URL they apply. Here is how to change the entry for kernel.org
+The hypothetical proxy command entries actually have a postfix to discern
+what URL they apply to. Here is how to change the entry for kernel.org
 to "ssh".
 
 ------------
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index ad6d14c..dd9fff1 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -15,12 +15,14 @@
 	     [ \--sparse ]
 	     [ \--no-merges ]
 	     [ \--remove-empty ]
+	     [ \--not ]
 	     [ \--all ]
 	     [ \--topo-order ]
 	     [ \--parents ]
 	     [ [\--objects | \--objects-edge] [ \--unpacked ] ]
 	     [ \--pretty | \--header ]
 	     [ \--bisect ]
+	     [ \--merge ]
 	     <commit>... [ \-- <paths>... ]
 
 DESCRIPTION
@@ -37,6 +39,14 @@
 A special notation <commit1>..<commit2> can be used as a
 short-hand for {caret}<commit1> <commit2>.
 
+Another special notation is <commit1>...<commit2> which is useful for
+merges.  The resulting set of commits is the symmetric difference
+between the two operands.  The following two commands are equivalent:
+
+------------
+$ git-rev-list A B --not $(git-merge-base --all A B)
+$ git-rev-list A...B
+------------
 
 OPTIONS
 -------
@@ -47,6 +57,9 @@
 	Print the contents of the commit in raw-format; each
 	record is separated with a NUL character.
 
+--parents::
+	Print the parents of the commit.
+
 --objects::
 	Print the object IDs of any object referenced by the listed commits.
 	'git-rev-list --objects foo ^bar' thus means "send me all object IDs
@@ -55,7 +68,7 @@
 
 --objects-edge::
 	Similar to `--objects`, but also print the IDs of
-	excluded commits refixed with a `-` character.  This is
+	excluded commits prefixed with a `-` character.  This is
 	used by `git-pack-objects` to build 'thin' pack, which
 	records objects in deltified form based on objects
 	contained in these excluded commits to reduce network
@@ -93,6 +106,14 @@
 --remove-empty::
 	Stop when a given path disappears from the tree.
 
+--no-merges::
+	Do not print commits with more than one parent.
+
+--not::
+	Reverses the meaning of the '{caret}' prefix (or lack
+	thereof) for all following revision specifiers, up to
+	the next `--not`.
+
 --all::
 	Pretend as if all the refs in `$GIT_DIR/refs/` are
 	listed on the command line as <commit>.
@@ -103,6 +124,10 @@
 	topological order (i.e. descendant commits are shown
 	before their parents).
 
+--merge::
+	After a failed merge, show refs that touch files having a
+	conflict and don't exist on all heads to merge.
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt
index 627cde8..b761b4b 100644
--- a/Documentation/git-rev-parse.txt
+++ b/Documentation/git-rev-parse.txt
@@ -156,11 +156,6 @@
   and dereference the tag recursively until a non-tag object is
   found.
 
-'git-rev-parse' also accepts a prefix '{caret}' to revision parameter,
-which is passed to 'git-rev-list'.  Two revision parameters
-concatenated with '..' is a short-hand for writing a range
-between them.  I.e. 'r1..r2' is equivalent to saying '{caret}r1 r2'
-
 Here is an illustration, by Jon Loeliger.  Both node B and C are
 a commit parents of commit node A.  Parent commits are ordered
 left-to-right.
@@ -168,9 +163,9 @@
     G   H   I   J
      \ /     \ /
       D   E   F
-       \  |  /
-        \ | /
-         \|/
+       \  |  / \
+        \ | /   |
+         \|/    |
           B     C
            \   /
             \ /
@@ -188,6 +183,40 @@
     J = F^2  = B^3^2   = A^^3^2
 
 
+SPECIFYING RANGES
+-----------------
+
+History traversing commands such as `git-log` operate on a set
+of commits, not just a single commit.  To these commands,
+specifying a single revision with the notation described in the
+previous section means the set of commits reachable from that
+commit, following the commit ancestry chain.
+
+To exclude commits reachable from a commit, a prefix `{caret}`
+notation is used.  E.g. "`{caret}r1 r2`" means commits reachable
+from `r2` but exclude the ones reachable from `r1`.
+
+This set operation appears so often that there is a shorthand
+for it.  "`r1..r2`" is equivalent to "`{caret}r1 r2`".  It is
+the difference of two sets (subtract the set of commits
+reachable from `r1` from the set of commits reachable from
+`r2`).
+
+A similar notation "`r1\...r2`" is called symmetric difference
+of `r1` and `r2` and is defined as
+"`r1 r2 --not $(git-merge-base --all r1 r2)`".
+It it the set of commits that are reachable from either one of
+`r1` or `r2` but not from both.
+
+Here are a few examples:
+
+   D                A B D
+   D F              A B C D F
+   ^A G		    B D
+   ^A F             B C F
+   G...I            C D F G I
+   ^B G I	    C D F G I
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org> and
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index f115b45..a2445a4 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -52,6 +52,11 @@
         appear in topological order (i.e., descendant commits
         are shown before their parents).
 
+--sparse::
+	By default, the output omits merges that are reachable
+	from only one tip being shown.  This option makes them
+	visible.
+
 --more=<n>::
 	Usually the command stops output upon showing the commit
 	that is the common ancestor of all the branches.  This
diff --git a/Documentation/git-status.txt b/Documentation/git-status.txt
index e446f48..ce7857e 100644
--- a/Documentation/git-status.txt
+++ b/Documentation/git-status.txt
@@ -8,7 +8,7 @@
 
 SYNOPSIS
 --------
-'git-status'
+'git-status' <options>...
 
 DESCRIPTION
 -----------
@@ -23,6 +23,10 @@
 the current HEAD commit, the command exits with non-zero
 status.
 
+The command takes the same set of options as `git-commit`; it
+shows what would be committed if the same options are given to
+`git-commit`.
+
 
 OUTPUT
 ------
diff --git a/contrib/git-svn/git-svn.txt b/Documentation/git-svn.txt
similarity index 99%
rename from contrib/git-svn/git-svn.txt
rename to Documentation/git-svn.txt
index f7d3de4..7d86809 100644
--- a/contrib/git-svn/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -224,7 +224,7 @@
 with Subversion is cumbersome as a result.  git-svn completely forgoes
 any automated merge/branch tracking on the Subversion side and leaves it
 entirely up to the user on the git side.  It's simply not worth it to do
-a useful translation when the the original signal is weak.
+a useful translation when the original signal is weak.
 
 TRACKING MULTIPLE REPOSITORIES OR BRANCHES
 ------------------------------------------
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
index f2675c4..1e1c7fa 100644
--- a/Documentation/git-tar-tree.txt
+++ b/Documentation/git-tar-tree.txt
@@ -37,7 +37,20 @@
 	Instead of making a tar archive from local repository,
 	retrieve a tar archive from a remote repository.
 
-Examples
+CONFIGURATION
+-------------
+By default, file and directories modes are set to 0666 or 0777. It is
+possible to change this by setting the "umask" variable in the
+repository configuration as follows :
+
+[tar]
+        umask = 002	;# group friendly
+
+The special umask value "user" indicates that the user's current umask
+will be used instead. The default value remains 0, which means world
+readable/writable files and directories.
+
+EXAMPLES
 --------
 git tar-tree HEAD junk | (cd /var/tmp/ && tar xf -)::
 
@@ -58,6 +71,11 @@
 
 	Get a tarball v1.4.0 from example.com.
 
+git tar-tree HEAD:Documentation/ git-docs > git-1.4.0-docs.tar::
+
+	Put everything in the current head's Documentation/ directory
+	into 'git-1.4.0-docs.tar', with the prefix 'git-docs/'.
+
 Author
 ------
 Written by Rene Scharfe.
diff --git a/Documentation/git-tools.txt b/Documentation/git-tools.txt
index d79523f..0914cbb 100644
--- a/Documentation/git-tools.txt
+++ b/Documentation/git-tools.txt
@@ -42,7 +42,7 @@
 
    - *gitk* (shipped with git-core)
 
-   gitk is a simple TK GUI for browsing history of GIT repositories easily.
+   gitk is a simple Tk GUI for browsing history of GIT repositories easily.
 
 
    - *gitview*  (contrib/)
diff --git a/Documentation/git-upload-tar.txt b/Documentation/git-upload-tar.txt
index a1019a0..394af62 100644
--- a/Documentation/git-upload-tar.txt
+++ b/Documentation/git-upload-tar.txt
@@ -17,7 +17,7 @@
 
 This command is usually not invoked directly by the end user.
 The UI for the protocol is on the 'git-tar-tree' side, and the
-program pair is meant to be used to get a tar achive from a
+program pair is meant to be used to get a tar archive from a
 remote repository.
 
 
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 51f20c6..bcf187a 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -8,7 +8,8 @@
 
 SYNOPSIS
 --------
-'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ARGS]
+'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate]
+	[--bare] [--git-dir=GIT_DIR] [--help] COMMAND [ARGS]
 
 DESCRIPTION
 -----------
@@ -41,6 +42,15 @@
 	environment variable. If no path is given 'git' will print
 	the current setting and then exit.
 
+-p|--paginate::
+	Pipe all output into 'less' (or if set, $PAGER).
+
+--git-dir=<path>::
+	Set the path to the repository. This can also be controlled by
+	setting the GIT_DIR environment variable.
+
+--bare::
+	Same as --git-dir=`pwd`.
 
 FURTHER DOCUMENTATION
 ---------------------
@@ -387,6 +397,9 @@
 gitlink:git-relink[1]::
 	Hardlink common objects in local repositories.
 
+gitlink:git-svn[1]::
+	Bidirectional operation between a single Subversion branch and git.
+
 gitlink:git-svnimport[1]::
 	Import a SVN repository into git.
 
@@ -432,6 +445,9 @@
 gitlink:git-imap-send[1]::
 	Dump a mailbox from stdin into an imap folder.
 
+gitlink:git-instaweb[1]::
+	Instantly browse your working repository in gitweb.
+
 gitlink:git-mailinfo[1]::
 	Extracts patch and authorship information from a single
 	e-mail message, optionally transliterating the commit
@@ -478,7 +494,7 @@
 
 Starting from 0.99.9 (actually mid 0.99.8.GIT), `.git/config` file
 is used to hold per-repository configuration options.  It is a
-simple text file modelled after `.ini` format familiar to some
+simple text file modeled after `.ini` format familiar to some
 people.  Here is an example:
 
 ------------
@@ -615,6 +631,13 @@
 	gitlink:git-diff-files[1];
 	gitlink:git-diff-tree[1]
 
+other
+~~~~~
+'GIT_TRACE'::
+	If this variable is set git will print `trace:` messages on
+	stderr telling about alias expansion, built-in command
+	execution and external command execution.
+
 Discussion[[Discussion]]
 ------------------------
 include::README[]
diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt
index 116ddb7..14449ca 100644
--- a/Documentation/glossary.txt
+++ b/Documentation/glossary.txt
@@ -86,7 +86,7 @@
 ent::
 	Favorite synonym to "tree-ish" by some total geeks. See
 	`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
-	explanation.
+	explanation.  Avoid this term, not to confuse people.
 
 fast forward::
 	A fast-forward is a special type of merge where you have
diff --git a/Documentation/howto/isolate-bugs-with-bisect.txt b/Documentation/howto/isolate-bugs-with-bisect.txt
index edbcd4c..926bbdc 100644
--- a/Documentation/howto/isolate-bugs-with-bisect.txt
+++ b/Documentation/howto/isolate-bugs-with-bisect.txt
@@ -28,7 +28,7 @@
 
 and at this point "git bisect" will churn for a while, and tell you what 
 the mid-point between those two commits are, and check that state out as 
-the head of the bew "bisect" branch.
+the head of the new "bisect" branch.
 
 Compile and reboot.
 
diff --git a/Documentation/howto/rebase-from-internal-branch.txt b/Documentation/howto/rebase-from-internal-branch.txt
index c2d4a91..fcd64e9 100644
--- a/Documentation/howto/rebase-from-internal-branch.txt
+++ b/Documentation/howto/rebase-from-internal-branch.txt
@@ -124,7 +124,7 @@
 
 The two commits #2' and #3' in the above picture record the same
 changes your e-mail submission for #2 and #3 contained, but
-probably with the new sign-off line added by the upsteam
+probably with the new sign-off line added by the upstream
 maintainer and definitely with different committer and ancestry
 information, they are different objects from #2 and #3 commits.
 
diff --git a/Documentation/howto/setup-git-server-over-http.txt b/Documentation/howto/setup-git-server-over-http.txt
new file mode 100644
index 0000000..ba19156
--- /dev/null
+++ b/Documentation/howto/setup-git-server-over-http.txt
@@ -0,0 +1,256 @@
+From: Rutger Nijlunsing <rutger@nospam.com>
+Subject: Setting up a git repository which can be pushed into and pulled from over HTTP.
+Date: Thu, 10 Aug 2006 22:00:26 +0200
+
+Since Apache is one of those packages people like to compile
+themselves while others prefer the bureaucrat's dream Debian, it is
+impossible to give guidelines which will work for everyone. Just send
+some feedback to the mailing list at git@vger.kernel.org to get this
+document tailored to your favorite distro.
+
+
+What's needed:
+
+- Have an Apache web-server
+
+  On Debian:
+    $ apt-get install apache2
+    To get apache2 by default started,
+    edit /etc/default/apache2 and set NO_START=0
+
+- can edit the configuration of it.
+
+  This could be found under /etc/httpd, or refer to your Apache documentation.
+
+  On Debian: this means being able to edit files under /etc/apache2
+
+- can restart it.
+
+  'apachectl --graceful' might do. If it doesn't, just stop and
+  restart apache. Be warning that active connections to your server
+  might be aborted by this.
+
+  On Debian:
+    $ /etc/init.d/apache2 restart
+  or
+    $ /etc/init.d/apache2 force-reload
+    (which seems to do the same)
+  This adds symlinks from the /etc/apache2/mods-enabled to
+  /etc/apache2/mods-available.
+
+- have permissions to chown a directory
+
+- have git installed at the server _and_ client
+
+In effect, this probably means you're going to be root.
+
+
+Step 1: setup a bare GIT repository
+-----------------------------------
+
+At the time of writing, git-http-push cannot remotely create a GIT
+repository. So we have to do that at the server side with git. Another
+option would be to generate an empty repository at the client and copy
+it to the server with WebDAV. But then you're probably the first to
+try that out :)
+
+Create the directory under the DocumentRoot of the directories served
+by Apache. As an example we take /usr/local/apache2, but try "grep
+DocumentRoot /where/ever/httpd.conf" to find your root:
+
+    $ cd /usr/local/apache/htdocs
+    $ mkdir my-new-repo.git
+
+  On Debian:
+
+    $ cd /var/www
+    $ mkdir my-new-repo.git
+
+
+Initialize a bare repository
+
+    $ cd my-new-repo.git
+    $ git --bare init-db
+
+
+Change the ownership to your web-server's credentials. Use "grep ^User
+httpd.conf" and "grep ^Group httpd.conf" to find out:
+
+    $ chown -R www.www .
+
+  On Debian:
+
+    $ chown -R www-data.www-data .
+
+
+If you do not know which user Apache runs as, you can alternatively do
+a "chmod -R a+w .", inspect the files which are created later on, and
+set the permissions appropriately.
+
+Restart apache2, and check whether http://server/my-new-repo.git gives
+a directory listing. If not, check whether apache started up
+successfully.
+
+
+Step 2: enable DAV on this repository
+-------------------------------------
+
+First make sure the dav_module is loaded. For this, insert in httpd.conf:
+
+    LoadModule dav_module libexec/httpd/libdav.so
+    AddModule mod_dav.c
+
+Also make sure that this line exists which is the file used for
+locking DAV operations:
+
+  DAVLockDB "/usr/local/apache2/temp/DAV.lock"
+
+  On Debian these steps can be performed with:
+
+    Enable the dav and dav_fs modules of apache:
+    $ a2enmod dav_fs
+    (just to be sure. dav_fs might be unneeded, I don't know)
+    $ a2enmod dav
+    The DAV lock is located in /etc/apache2/mods-available/dav_fs.conf:
+      DAVLockDB /var/lock/apache2/DAVLock
+
+Of course, it can point somewhere else, but the string is actually just a
+prefix in some Apache configurations, and therefore the _directory_ has to
+be writable by the user Apache runs as.
+
+Then, add something like this to your httpd.conf
+
+  <Location /my-new-repo.git>
+     DAV on
+     AuthType Basic
+     AuthName "Git"
+     AuthUserFile /usr/local/apache2/conf/passwd.git
+     Require valid-user
+  </Location>
+
+  On Debian:
+    Create (or add to) /etc/apache2/conf.d/git.conf :
+
+    <Location /my-new-repo.git>
+       DAV on
+       AuthType Basic
+       AuthName "Git"
+       AuthUserFile /etc/apache2/passwd.git
+       Require valid-user
+    </Location>
+
+    Debian automatically reads all files under /etc/apach2/conf.d.
+
+The password file can be somewhere else, but it has to be readable by
+Apache and preferably not readable by the world.
+
+Create this file by
+    $ htpasswd -c /usr/local/apache2/conf/passwd.git <user>
+
+    On Debian:
+      $ htpasswd -c /etc/apache2/passwd.git <user>
+
+You will be asked a password, and the file is created. Subsequent calls
+to htpasswd should omit the '-c' option, since you want to append to the
+existing file.
+
+You need to restart Apache.
+
+Now go to http://<username>@<servername>/my-new-repo.git in your
+browser to check whether it asks for a password and accepts the right
+password.
+
+On Debian:
+
+   To test the WebDAV part, do:
+
+   $ apt-get install litmus
+   $ litmus http://<servername>/my-new-repo.git <username> <password>
+
+   Most tests should pass.
+
+A command line tool to test WebDAV is cadaver.
+
+If you're into Windows, from XP onwards Internet Explorer supports
+WebDAV. For this, do Internet Explorer -> Open Location ->
+http://<servername>/my-new-repo.git [x] Open as webfolder -> login .
+
+
+Step 3: setup the client
+------------------------
+
+Make sure that you have HTTP support, i.e. your git was built with curl.
+The easiest way to check is to look for the executable 'git-http-push'.
+
+Then, add the following to your $HOME/.netrc (you can do without, but will be
+asked to input your password a _lot_ of times):
+
+    machine <servername>
+    login <username>
+    password <password>
+
+...and set permissions:
+     chmod 600 ~/.netrc
+
+If you want to access the web-server by its IP, you have to type that in,
+instead of the server name.
+
+To check whether all is OK, do:
+
+   curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/
+
+...this should give a directory listing in HTML of /var/www/my-new-repo.git .
+
+
+Now, add the remote in your existing repository which contains the project
+you want to export:
+
+   $ git-repo-config remote.upload.url \
+       http://<username>@<servername>/my-new-repo.git/
+
+It is important to put the last '/'; Without it, the server will send
+a redirect which git-http-push does not (yet) understand, and git-http-push
+will repeat the request infinitely.
+
+
+Step 4: make the initial push
+-----------------------------
+
+From your client repository, do
+
+   $ git push upload master
+
+This pushes branch 'master' (which is assumed to be the branch you
+want to export) to repository called 'upload', which we previously
+defined with git-repo-config.
+
+
+Troubleshooting:
+----------------
+
+If git-http-push says
+
+   Error: no DAV locking support on remote repo http://...
+
+then it means the web-server did not accept your authentication. Make sure
+that the user name and password matches in httpd.conf, .netrc and the URL
+you are uploading to.
+
+If git-http-push shows you an error (22/502) when trying to MOVE a blob,
+it means that your web-server somehow does not recognize its name in the
+request; This can happen when you start Apache, but then disable the
+network interface. A simple restart of Apache helps.
+
+Errors like (22/502) are of format (curl error code/http error
+code). So (22/404) means something like 'not found' at the server.
+
+Reading /usr/local/apache2/logs/error_log is often helpful.
+
+  On Debian: Read /var/log/apache2/error.log instead.
+
+
+Debian References: http://www.debian-administration.org/articles/285
+
+Authors
+  Johannes Schindelin <Johannes.Schindelin@gmx.de>
+  Rutger Nijlunsing <git@wingding.demon.nl>
diff --git a/Documentation/repository-layout.txt b/Documentation/repository-layout.txt
index b52dfdc..275d18b 100644
--- a/Documentation/repository-layout.txt
+++ b/Documentation/repository-layout.txt
@@ -120,9 +120,11 @@
 
 info/exclude::
 	This file, by convention among Porcelains, stores the
-	exclude pattern list.  `git status` looks at it, but
-	otherwise it is not looked at by any of the core git
-	commands.
+	exclude pattern list. `.gitignore` is the per-directory
+	ignore file.  `git status`, `git add`, `git rm` and `git
+	clean` look at it but the core git commands do not look
+	at it.  See also: gitlink:git-ls-files[1] `--exclude-from`
+	and `--exclude-per-directory`.
 
 remotes::
 	Stores shorthands to be used to give URL and default
diff --git a/Documentation/technical/pack-heuristics.txt b/Documentation/technical/pack-heuristics.txt
index 9aadd5c..103eb5d 100644
--- a/Documentation/technical/pack-heuristics.txt
+++ b/Documentation/technical/pack-heuristics.txt
@@ -73,7 +73,7 @@
 
     <pasky> yes
 
-And Bable-like confusion flowed.
+And Babel-like confusion flowed.
 
     <njs`> oh, hmm, and I'm not sure what this sliding window means either
 
@@ -257,7 +257,7 @@
         (type, basename, size)).
 
         Then we walk through this list, and calculate a delta of
-        each object against the last n (tunable paramater) objects,
+        each object against the last n (tunable parameter) objects,
         and pick the smallest of these deltas.
 
 Vastly simplified, but the essence is there!
@@ -395,7 +395,7 @@
         do "object name->location in packfile" translation.
 
     <njs`> I'm assuming the real win for delta-ing large->small is
-        more homogenous statistics for gzip to run over?
+        more homogeneous statistics for gzip to run over?
 
         (You have to put the bytes in one place or another, but
         putting them in a larger blob wins on compression)
@@ -448,7 +448,7 @@
 
         Bugs happen, but they are "simple" bugs. And bugs that
         actually get some object store detail wrong are almost always
-        so obious that they never go anywhere.
+        so obvious that they never go anywhere.
 
     <njs`> Yeah.
 
diff --git a/Documentation/tutorial-2.txt b/Documentation/tutorial-2.txt
index 894ca5e..2f4fe12 100644
--- a/Documentation/tutorial-2.txt
+++ b/Documentation/tutorial-2.txt
@@ -244,6 +244,7 @@
 $ git cat-file -t 513feba2
 blob
 $ git cat-file blob 513feba2
+hello world!
 hello world, again
 ------------------------------------------------
 
diff --git a/Documentation/urls.txt b/Documentation/urls.txt
index 7477413..26ecba5 100644
--- a/Documentation/urls.txt
+++ b/Documentation/urls.txt
@@ -10,20 +10,21 @@
 - https://host.xz/path/to/repo.git/
 - git://host.xz/path/to/repo.git/
 - git://host.xz/~user/path/to/repo.git/
-- ssh://host.xz/path/to/repo.git/
-- ssh://host.xz/~user/path/to/repo.git/
-- ssh://host.xz/~/path/to/repo.git
+- ssh://{startsb}user@{endsb}host.xz/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~user/path/to/repo.git/
+- ssh://{startsb}user@{endsb}host.xz/~/path/to/repo.git
 ===============================================================
 
-SSH Is the default transport protocol and also supports an
-scp-like syntax.  Both syntaxes support username expansion,
+SSH is the default transport protocol.  You can optionally specify
+which user to log-in as, and an alternate, scp-like syntax is also
+supported.  Both syntaxes support username expansion,
 as does the native git protocol. The following three are
 identical to the last three above, respectively:
 
 ===============================================================
-- host.xz:/path/to/repo.git/
-- host.xz:~user/path/to/repo.git/
-- host.xz:path/to/repo.git
+- {startsb}user@{endsb}host.xz:/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:~user/path/to/repo.git/
+- {startsb}user@{endsb}host.xz:path/to/repo.git
 ===============================================================
 
 To sync with a local directory, use:
@@ -47,7 +48,7 @@
 <repository> without <refspec> parameters on the command
 line, <refspec> specified on `Push:` lines or `Pull:`
 lines are used for `git-push` and `git-fetch`/`git-pull`,
-respectively.  Multiple `Push:` and and `Pull:` lines may
+respectively.  Multiple `Push:` and `Pull:` lines may
 be specified for additional branch mappings.
 
 The name of a file in `$GIT_DIR/branches` directory can be
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 5d25b7e..14923c9 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,11 +1,19 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.4.GIT
+DEF_VER=v1.4.2.GIT
+
+LF='
+'
 
 # First try git-describe, then see if there is a version file
 # (included in release tarballs), then default
-if VN=$(git describe --abbrev=4 HEAD 2>/dev/null); then
+if VN=$(git describe --abbrev=4 HEAD 2>/dev/null) &&
+   case "$VN" in
+   *$LF*) (exit 1) ;;
+   v[0-9]*) : happy ;;
+   esac
+then
 	VN=$(echo "$VN" | sed -e 's/-/./g');
 elif test -f version
 then
diff --git a/INSTALL b/INSTALL
index f8337e2..ba9778c 100644
--- a/INSTALL
+++ b/INSTALL
@@ -13,6 +13,15 @@
 which are derived from $prefix, so "make all; make prefix=/usr
 install" would not work.
 
+Alternatively you can use autoconf generated ./configure script to
+set up install paths (via config.mak.autogen), so you can write instead
+
+	$ autoconf ;# as yourself if ./configure doesn't exist yet
+	$ ./configure --prefix=/usr ;# as yourself
+	$ make all doc ;# as yourself
+	# make install install-doc ;# as root
+
+
 Issues of note:
 
  - git normally installs a helper script wrapper called "git", which
@@ -44,7 +53,7 @@
 
 	- "libcurl" and "curl" executable.  git-http-fetch and
 	  git-fetch use them.  If you do not use http
-	  transfer, you are probabaly OK if you do not have
+	  transfer, you are probably OK if you do not have
 	  them.
 
 	- expat library; git-http-push uses it for remote lock
@@ -69,7 +78,7 @@
 	  git, and if you only use git to track other peoples work you'll
 	  never notice the lack of it. 
 
-        - "wish", the TCL/Tk windowing shell is used in gitk to show the
+        - "wish", the Tcl/Tk windowing shell is used in gitk to show the
           history graphically
 
 	- "ssh" is used to push and pull over the net
diff --git a/Makefile b/Makefile
index 76abcc4..0761d6c 100644
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,11 @@
 # Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
 # d_type in struct dirent (latest Cygwin -- will be fixed soonish).
 #
+# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
+# do not support the 'size specifiers' introduced by C99, namely ll, hh,
+# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
+# some c compilers supported these specifiers prior to C99 as an extension.
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
 # Define NO_STRLCPY if you don't have strlcpy.
@@ -33,6 +38,22 @@
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
 # Enable it on Windows.  By default, symrefs are still used.
 #
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink
+# installed in /sw, but don't want GIT to link against any libraries
+# installed there.  If defined you may specify your own (or Fink's)
+# include directories and library directories by defining CFLAGS
+# and LDFLAGS appropriately.
+#
+# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,
+# have DarwinPorts installed in /opt/local, but don't want GIT to
+# link against any libraries installed there.  If defined you may
+# specify your own (or DarwinPort's) include directories and
+# library directories by defining CFLAGS and LDFLAGS appropriately.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -60,7 +81,7 @@
 # Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
 # a missing newline at the end of the file.
 #
-# Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
 #
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
@@ -100,6 +121,8 @@
 GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
 
+export prefix bindir gitexecdir template_dir GIT_PYTHON_DIR
+
 CC = gcc
 AR = ar
 TAR = tar
@@ -120,7 +143,7 @@
 	git-fetch.sh \
 	git-ls-remote.sh \
 	git-merge-one-file.sh git-parse-remote.sh \
-	git-prune.sh git-pull.sh git-rebase.sh \
+	git-pull.sh git-rebase.sh \
 	git-repack.sh git-request-pull.sh git-reset.sh \
 	git-resolve.sh git-revert.sh git-sh-setup.sh \
 	git-tag.sh git-verify-tag.sh \
@@ -131,10 +154,10 @@
 
 SCRIPT_PERL = \
 	git-archimport.perl git-cvsimport.perl git-relink.perl \
-	git-shortlog.perl git-fmt-merge-msg.perl git-rerere.perl \
+	git-shortlog.perl git-rerere.perl \
 	git-annotate.perl git-cvsserver.perl \
-	git-svnimport.perl git-mv.perl git-cvsexportcommit.perl \
-	git-send-email.perl
+	git-svnimport.perl git-cvsexportcommit.perl \
+	git-send-email.perl git-svn.perl
 
 SCRIPT_PYTHON = \
 	git-merge-recursive.py
@@ -142,7 +165,7 @@
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
 	  $(patsubst %.perl,%,$(SCRIPT_PERL)) \
 	  $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
-	  git-cherry-pick git-status
+	  git-cherry-pick git-status git-instaweb
 
 # The ones that do not have to link with lcrypto, lz nor xdiff.
 SIMPLE_PROGRAMS = \
@@ -155,14 +178,14 @@
 	git-hash-object$X git-index-pack$X git-local-fetch$X \
 	git-merge-base$X \
 	git-merge-index$X git-mktag$X git-mktree$X git-pack-objects$X git-patch-id$X \
-	git-peek-remote$X git-prune-packed$X git-receive-pack$X \
+	git-peek-remote$X git-receive-pack$X \
 	git-send-pack$X git-shell$X \
 	git-show-index$X git-ssh-fetch$X \
 	git-ssh-upload$X git-unpack-file$X \
 	git-unpack-objects$X git-update-server-info$X \
 	git-upload-pack$X git-verify-pack$X \
 	git-symbolic-ref$X \
-	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
+	git-name-rev$X git-pack-redundant$X git-var$X \
 	git-describe$X git-merge-tree$X git-blame$X git-imap-send$X
 
 BUILT_INS = git-log$X git-whatchanged$X git-show$X git-update-ref$X \
@@ -173,7 +196,9 @@
 	git-ls-files$X git-ls-tree$X git-get-tar-commit-id$X \
 	git-read-tree$X git-commit-tree$X git-write-tree$X \
 	git-apply$X git-show-branch$X git-diff-files$X git-update-index$X \
-	git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X
+	git-diff-index$X git-diff-stages$X git-diff-tree$X git-cat-file$X \
+	git-fmt-merge-msg$X git-prune$X git-mv$X git-prune-packed$X \
+	git-repo-config$X
 
 # what 'all' will build and 'install' will install, in gitexecdir
 ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
@@ -202,7 +227,7 @@
 	blob.h cache.h commit.h csum-file.h delta.h \
 	diff.h object.h pack.h pkt-line.h quote.h refs.h \
 	run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
-	tree-walk.h log-tree.h dir.h
+	tree-walk.h log-tree.h dir.h path-list.h
 
 DIFF_OBJS = \
 	diff.o diff-lib.o diffcore-break.o diffcore-order.o \
@@ -217,7 +242,7 @@
 	server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
 	tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
 	fetch-clone.o revision.o pager.o tree-walk.o xdiff-interface.o \
-	alloc.o $(DIFF_OBJS)
+	alloc.o merge-file.o path-list.o $(DIFF_OBJS)
 
 BUILTIN_OBJS = \
 	builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \
@@ -229,7 +254,8 @@
 	builtin-apply.o builtin-show-branch.o builtin-diff-files.o \
 	builtin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \
 	builtin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \
-	builtin-update-ref.o
+	builtin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \
+	builtin-mv.o builtin-prune-packed.o builtin-repo-config.o
 
 GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
 LIBS = $(GITLIBS) -lz
@@ -245,19 +271,24 @@
 ifeq ($(uname_S),Linux)
 	NO_STRLCPY = YesPlease
 endif
+ifeq ($(uname_S),GNU/kFreeBSD)
+	NO_STRLCPY = YesPlease
+endif
 ifeq ($(uname_S),Darwin)
 	NEEDS_SSL_WITH_CRYPTO = YesPlease
 	NEEDS_LIBICONV = YesPlease
 	NO_STRLCPY = YesPlease
-	## fink
-	ifeq ($(shell test -d /sw/lib && echo y),y)
-		ALL_CFLAGS += -I/sw/include
-		ALL_LDFLAGS += -L/sw/lib
+	ifndef NO_FINK
+		ifeq ($(shell test -d /sw/lib && echo y),y)
+			ALL_CFLAGS += -I/sw/include
+			ALL_LDFLAGS += -L/sw/lib
+		endif
 	endif
-	## darwinports
-	ifeq ($(shell test -d /opt/local/lib && echo y),y)
-		ALL_CFLAGS += -I/opt/local/include
-		ALL_LDFLAGS += -L/opt/local/lib
+	ifndef NO_DARWIN_PORTS
+		ifeq ($(shell test -d /opt/local/lib && echo y),y)
+			ALL_CFLAGS += -I/opt/local/include
+			ALL_LDFLAGS += -L/opt/local/lib
+		endif
 	endif
 endif
 ifeq ($(uname_S),SunOS)
@@ -283,9 +314,9 @@
 	NO_D_TYPE_IN_DIRENT = YesPlease
 	NO_D_INO_IN_DIRENT = YesPlease
 	NO_STRCASESTR = YesPlease
-	NO_STRLCPY = YesPlease
 	NO_SYMLINK_HEAD = YesPlease
 	NEEDS_LIBICONV = YesPlease
+	NO_C99_FORMAT = YesPlease
 	# There are conflicting reports about this.
 	# On some boxes NO_MMAP is needed, and not so elsewhere.
 	# Try uncommenting this if you see things break -- YMMV.
@@ -331,6 +362,7 @@
 	ARM_SHA1 = YesPlease
 endif
 
+-include config.mak.autogen
 -include config.mak
 
 ifdef WITH_OWN_SUBPROCESS_PY
@@ -406,6 +438,9 @@
 ifdef NO_D_INO_IN_DIRENT
 	ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
 endif
+ifdef NO_C99_FORMAT
+	ALL_CFLAGS += -DNO_C99_FORMAT
+endif
 ifdef NO_SYMLINK_HEAD
 	ALL_CFLAGS += -DNO_SYMLINK_HEAD
 endif
@@ -468,7 +503,7 @@
 	ALL_CFLAGS += -DNO_ACCURATE_DIFF
 endif
 
-# Shell quote (do not use $(call) to accomodate ancient setups);
+# Shell quote (do not use $(call) to accommodate ancient setups);
 
 SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 
@@ -513,6 +548,7 @@
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
 	rm -f $@ $@+
 	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	    -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
 	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
 	    -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
@@ -545,6 +581,20 @@
 	cp $< $@+
 	mv $@+ $@
 
+git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
+	rm -f $@ $@+
+	sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+	    -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
+	    -e 's/@@NO_CURL@@/$(NO_CURL)/g' \
+	    -e 's/@@NO_PYTHON@@/$(NO_PYTHON)/g' \
+	    -e '/@@GITWEB_CGI@@/r gitweb/gitweb.cgi' \
+	    -e '/@@GITWEB_CGI@@/d' \
+	    -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
+	    -e '/@@GITWEB_CSS@@/d' \
+	    $@.sh | sed "s|/usr/bin/git|$(bindir)/git|" > $@+
+	chmod +x $@+
+	mv $@+ $@
+
 # These can record GIT_VERSION
 git$X git.spec \
 	$(patsubst %.sh,%,$(SCRIPT_SH)) \
@@ -578,6 +628,8 @@
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 		$(LIB_FILE) $(SIMPLE_LIB)
 
+ssh-pull.o: ssh-fetch.c
+ssh-push.o: ssh-upload.c
 git-local-fetch$X: fetch.o
 git-ssh-fetch$X: rsh.o fetch.o
 git-ssh-upload$X: rsh.o
@@ -637,6 +689,7 @@
 # with that.
 
 export NO_PYTHON
+export NO_SVN_TESTS
 
 test: all
 	$(MAKE) -C t/ all
@@ -650,6 +703,12 @@
 test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS)
 	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
+test-sha1$X: test-sha1.o $(GITLIBS)
+	$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+
+check-sha1:: test-sha1$X
+	./test-sha1.sh
+
 check:
 	for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done
 
@@ -662,7 +721,7 @@
 	$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 	$(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexecdir_SQ)'
 	$(INSTALL) git$X gitk '$(DESTDIR_SQ)$(bindir_SQ)'
-	$(MAKE) -C templates install
+	$(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 	$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
 	$(INSTALL) $(PYMODULES) '$(DESTDIR_SQ)$(GIT_PYTHON_DIR_SQ)'
 	if test 'z$(bindir_SQ)' != 'z$(gitexecdir_SQ)'; \
@@ -712,8 +771,8 @@
 	rm -fr .doc-tmp-dir
 	mkdir .doc-tmp-dir .doc-tmp-dir/man1 .doc-tmp-dir/man7
 	$(MAKE) -C Documentation DESTDIR=./ \
-		man1=../.doc-tmp-dir/man1 \
-		man7=../.doc-tmp-dir/man7 \
+		man1dir=../.doc-tmp-dir/man1 \
+		man7dir=../.doc-tmp-dir/man7 \
 		install
 	cd .doc-tmp-dir && $(TAR) cf ../$(manpages).tar .
 	gzip -n -9 -f $(manpages).tar
@@ -726,6 +785,8 @@
 		$(LIB_FILE) $(XDIFF_LIB)
 	rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
 	rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
+	rm -rf autom4te.cache
+	rm -f config.log config.mak.autogen configure config.status config.cache
 	rm -rf $(GIT_TARNAME) .doc-tmp-dir
 	rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
 	rm -f $(htmldocs).tar.gz $(manpages).tar.gz
diff --git a/alloc.c b/alloc.c
index e3b22f4..460db19 100644
--- a/alloc.c
+++ b/alloc.c
@@ -39,8 +39,21 @@
 DEFINE_ALLOCATOR(commit)
 DEFINE_ALLOCATOR(tag)
 
+#ifdef NO_C99_FORMAT
+#define SZ_FMT "%u"
+#else
+#define SZ_FMT "%zu"
+#endif
+
+static void report(const char* name, unsigned int count, size_t size)
+{
+    fprintf(stderr, "%10s: %8u (" SZ_FMT " kB)\n", name, count, size);
+}
+
+#undef SZ_FMT
+
 #define REPORT(name)	\
-	fprintf(stderr, "%10s: %8u (%zu kB)\n", #name, name##_allocs, name##_allocs*sizeof(struct name) >> 10)
+    report(#name, name##_allocs, name##_allocs*sizeof(struct name) >> 10)
 
 void alloc_report(void)
 {
diff --git a/blame.c b/blame.c
index c86e2fd..7099b53 100644
--- a/blame.c
+++ b/blame.c
@@ -20,12 +20,12 @@
 
 #define DEBUG 0
 
-static const char blame_usage[] = "[-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
-	"  -c, --compability Use the same output mode as git-annotate (Default: off)\n"
-	"  -l, --long        Show long commit SHA1 (Default: off)\n"
-	"  -t, --time        Show raw timestamp (Default: off)\n"
-	"  -S, --revs-file   Use revisions from revs-file instead of calling git-rev-list\n"
-	"  -h, --help        This message";
+static const char blame_usage[] = "git-blame [-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
+	"  -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
+	"  -l, --long          Show long commit SHA1 (Default: off)\n"
+	"  -t, --time          Show raw timestamp (Default: off)\n"
+	"  -S, --revs-file     Use revisions from revs-file instead of calling git-rev-list\n"
+	"  -h, --help          This message";
 
 static struct commit **blame_lines;
 static int num_blame_lines;
@@ -44,8 +44,8 @@
 };
 
 struct chunk {
-	int off1, len1;	// ---
-	int off2, len2;	// +++
+	int off1, len1;	/* --- */
+	int off2, len2;	/* +++ */
 };
 
 struct patch {
@@ -255,7 +255,7 @@
 }
 #endif
 
-// p is a patch from commit to other.
+/* p is a patch from commit to other. */
 static void fill_line_map(struct commit *commit, struct commit *other,
 			  struct patch *p)
 {
@@ -747,7 +747,7 @@
 	const char *filename = NULL, *commit = NULL;
 	char filename_buf[256];
 	int sha1_len = 8;
-	int compability = 0;
+	int compatibility = 0;
 	int show_raw_time = 0;
 	int options = 1;
 	struct commit* start_commit;
@@ -774,8 +774,8 @@
 				sha1_len = 40;
 				continue;
 			} else if(!strcmp(argv[i], "-c") ||
-				  !strcmp(argv[i], "--compability")) {
-				compability = 1;
+				  !strcmp(argv[i], "--compatibility")) {
+				compatibility = 1;
 				continue;
 			} else if(!strcmp(argv[i], "-t") ||
 				  !strcmp(argv[i], "--time")) {
@@ -784,7 +784,7 @@
 			} else if(!strcmp(argv[i], "-S")) {
 				if (i + 1 < argc &&
 				    !read_ancestry(argv[i + 1], &sha1_p)) {
-					compability = 1;
+					compatibility = 1;
 					i++;
 					continue;
 				}
@@ -834,7 +834,7 @@
 	}
 
 
-	init_revisions(&rev);
+	init_revisions(&rev, setup_git_directory());
 	rev.remove_empty_trees = 1;
 	rev.topo_order = 1;
 	rev.prune_fn = simplify_commit;
@@ -884,7 +884,7 @@
 		u = c->util;
 		get_commit_info(c, &ci);
 		fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
-		if(compability) {
+		if(compatibility) {
 			printf("\t(%10s\t%10s\t%d)", ci.author,
 			       format_time(ci.author_time, ci.author_tz,
 					   show_raw_time),
diff --git a/blob.c b/blob.c
index 496f270..d1af2e6 100644
--- a/blob.c
+++ b/blob.c
@@ -10,12 +10,12 @@
 	if (!obj) {
 		struct blob *ret = alloc_blob_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_BLOB;
+		ret->object.type = OBJ_BLOB;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_BLOB;
-	if (obj->type != TYPE_BLOB) {
+		obj->type = OBJ_BLOB;
+	if (obj->type != OBJ_BLOB) {
 		error("Object %s is a %s, not a blob",
 		      sha1_to_hex(sha1), typename(obj->type));
 		return NULL;
diff --git a/builtin-add.c b/builtin-add.c
index bfbbb1b..0cb9c81 100644
--- a/builtin-add.c
+++ b/builtin-add.c
@@ -21,8 +21,7 @@
 
 	for (specs = 0; pathspec[specs];  specs++)
 		/* nothing */;
-	seen = xmalloc(specs);
-	memset(seen, 0, specs);
+	seen = xcalloc(specs, 1);
 
 	src = dst = dir->entries;
 	i = dir->nr;
@@ -83,60 +82,18 @@
 		prune_directory(dir, pathspec, baselen);
 }
 
-static int add_file_to_index(const char *path, int verbose)
-{
-	int size, namelen;
-	struct stat st;
-	struct cache_entry *ce;
-
-	if (lstat(path, &st))
-		die("%s: unable to stat (%s)", path, strerror(errno));
-
-	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
-		die("%s: can only add regular files or symbolic links", path);
-
-	namelen = strlen(path);
-	size = cache_entry_size(namelen);
-	ce = xcalloc(1, size);
-	memcpy(ce->name, path, namelen);
-	ce->ce_flags = htons(namelen);
-	fill_stat_cache_info(ce, &st);
-
-	ce->ce_mode = create_ce_mode(st.st_mode);
-	if (!trust_executable_bit) {
-		/* If there is an existing entry, pick the mode bits
-		 * from it.
-		 */
-		int pos = cache_name_pos(path, namelen);
-		if (pos >= 0)
-			ce->ce_mode = active_cache[pos]->ce_mode;
-	}
-
-	if (index_path(ce->sha1, path, &st, 1))
-		die("unable to index file %s", path);
-	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
-		die("unable to add %s to index",path);
-	if (verbose)
-		printf("add '%s'\n", path);
-	cache_tree_invalidate_path(active_cache_tree, path);
-	return 0;
-}
-
 static struct lock_file lock_file;
 
-int cmd_add(int argc, const char **argv, char **envp)
+int cmd_add(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
 	int verbose = 0, show_only = 0;
-	const char *prefix = setup_git_directory();
 	const char **pathspec;
 	struct dir_struct dir;
 
 	git_config(git_default_config);
 
-	newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-	if (newfd < 0)
-		die("unable to create new index file");
+	newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
 
 	if (read_cache() < 0)
 		die("index file corrupt");
@@ -158,9 +115,8 @@
 			verbose = 1;
 			continue;
 		}
-		die(builtin_add_usage);
+		usage(builtin_add_usage);
 	}
-	git_config(git_default_config);
 	pathspec = get_pathspec(prefix, argv + i);
 
 	fill_directory(&dir, pathspec);
@@ -181,7 +137,7 @@
 
 	if (active_cache_changed) {
 		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_lock_file(&lock_file))
+		    close(newfd) || commit_lock_file(&lock_file))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin-apply.c b/builtin-apply.c
index e9ead00..9cf477c 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -14,14 +14,15 @@
 #include "delta.h"
 #include "builtin.h"
 
-//  --check turns on checking that the working tree matches the
-//    files that are being modified, but doesn't apply the patch
-//  --stat does just a diffstat, and doesn't actually apply
-//  --numstat does numeric diffstat, and doesn't actually apply
-//  --index-info shows the old and new index info for paths if available.
-//  --index updates the cache as well.
-//  --cached updates only the cache without ever touching the working tree.
-//
+/*
+ *  --check turns on checking that the working tree matches the
+ *    files that are being modified, but doesn't apply the patch
+ *  --stat does just a diffstat, and doesn't actually apply
+ *  --numstat does numeric diffstat, and doesn't actually apply
+ *  --index-info shows the old and new index info for paths if available.
+ *  --index updates the cache as well.
+ *  --cached updates only the cache without ever touching the working tree.
+ */
 static const char *prefix;
 static int prefix_length = -1;
 static int newfd = -1;
@@ -119,7 +120,7 @@
 struct patch {
 	char *new_name, *old_name, *def_name;
 	unsigned int old_mode, new_mode;
-	int is_rename, is_copy, is_new, is_delete, is_binary;
+	int is_rename, is_copy, is_new, is_delete, is_binary, is_reverse;
 #define BINARY_DELTA_DEFLATED 1
 #define BINARY_LITERAL_DEFLATED 2
 	unsigned long deflate_origlen;
@@ -284,8 +285,8 @@
 {
 	char *name;
 
-	first += 4;	// skip "--- "
-	second += 4;	// skip "+++ "
+	first += 4;	/* skip "--- " */
+	second += 4;	/* skip "+++ " */
 	if (is_dev_null(first)) {
 		patch->is_new = 1;
 		patch->is_delete = 0;
@@ -765,7 +766,7 @@
 			continue;
 
 		/*
-		 * Make sure we don't find any unconnected patch fragmants.
+		 * Make sure we don't find any unconnected patch fragments.
 		 * That's a sign that we didn't find a header, and that a
 		 * patch has become corrupted/broken up.
 		 */
@@ -990,7 +991,7 @@
 	 * so one line can fit up to 13 groups that would decode
 	 * to 52 bytes max.  The length byte 'A'-'Z' corresponds
 	 * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
-	 * The end of binary is signalled with an empty line.
+	 * The end of binary is signaled with an empty line.
 	 */
 	int llen, used;
 	struct fragment *fragment;
@@ -1118,6 +1119,34 @@
 	return offset + hdrsize + patchsize;
 }
 
+#define swap(a,b) myswap((a),(b),sizeof(a))
+
+#define myswap(a, b, size) do {		\
+	unsigned char mytmp[size];	\
+	memcpy(mytmp, &a, size);		\
+	memcpy(&a, &b, size);		\
+	memcpy(&b, mytmp, size);		\
+} while (0)
+
+static void reverse_patches(struct patch *p)
+{
+	for (; p; p = p->next) {
+		struct fragment *frag = p->fragments;
+
+		swap(p->new_name, p->old_name);
+		swap(p->new_mode, p->old_mode);
+		swap(p->is_new, p->is_delete);
+		swap(p->lines_added, p->lines_deleted);
+		swap(p->old_sha1_prefix, p->new_sha1_prefix);
+
+		for (; frag; frag = frag->next) {
+			swap(frag->newpos, frag->oldpos);
+			swap(frag->newlines, frag->oldlines);
+		}
+		p->is_reverse = !p->is_reverse;
+	}
+}
+
 static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
 static const char minuses[]= "----------------------------------------------------------------------";
 
@@ -1335,7 +1364,7 @@
 }
 
 static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag,
-	int inaccurate_eof)
+	int reverse, int inaccurate_eof)
 {
 	int match_beginning, match_end;
 	char *buf = desc->buffer;
@@ -1349,6 +1378,7 @@
 	int pos, lines;
 
 	while (size > 0) {
+		char first;
 		int len = linelen(patch, size);
 		int plen;
 
@@ -1365,16 +1395,23 @@
 		plen = len-1;
 		if (len < size && patch[len] == '\\')
 			plen--;
-		switch (*patch) {
+		first = *patch;
+		if (reverse) {
+			if (first == '-')
+				first = '+';
+			else if (first == '+')
+				first = '-';
+		}
+		switch (first) {
 		case ' ':
 		case '-':
 			memcpy(old + oldsize, patch + 1, plen);
 			oldsize += plen;
-			if (*patch == '-')
+			if (first == '-')
 				break;
 		/* Fall-through for ' ' */
 		case '+':
-			if (*patch != '+' || !no_add)
+			if (first != '+' || !no_add)
 				newsize += apply_line(new + newsize, patch,
 						      plen);
 			break;
@@ -1498,6 +1535,12 @@
 	void *data;
 	void *result;
 
+	/* Binary patch is irreversible */
+	if (patch->is_reverse)
+		return error("cannot reverse-apply a binary patch to '%s'",
+			     patch->new_name
+			     ? patch->new_name : patch->old_name);
+
 	data = inflate_it(fragment->patch, fragment->size,
 			  patch->deflate_origlen);
 	if (!data)
@@ -1614,7 +1657,8 @@
 		return apply_binary(desc, patch);
 
 	while (frag) {
-		if (apply_one_fragment(desc, frag, patch->inaccurate_eof) < 0)
+		if (apply_one_fragment(desc, frag, patch->is_reverse,
+					patch->inaccurate_eof) < 0)
 			return error("patch failed: %s:%ld",
 				     name, frag->oldpos);
 		frag = frag->next;
@@ -1654,6 +1698,12 @@
 	desc.buffer = buf;
 	if (apply_fragments(&desc, patch) < 0)
 		return -1;
+
+	/* NUL terminate the result */
+	if (desc.alloc <= desc.size)
+		desc.buffer = xrealloc(desc.buffer, desc.size + 1);
+	desc.buffer[desc.size] = 0;
+
 	patch->result = desc.buffer;
 	patch->resultsize = desc.size;
 
@@ -1663,13 +1713,14 @@
 	return 0;
 }
 
-static int check_patch(struct patch *patch)
+static int check_patch(struct patch *patch, struct patch *prev_patch)
 {
 	struct stat st;
 	const char *old_name = patch->old_name;
 	const char *new_name = patch->new_name;
 	const char *name = old_name ? old_name : new_name;
 	struct cache_entry *ce = NULL;
+	int ok_if_exists;
 
 	if (old_name) {
 		int changed = 0;
@@ -1727,13 +1778,33 @@
 				old_name, st_mode, patch->old_mode);
 	}
 
+	if (new_name && prev_patch && prev_patch->is_delete &&
+	    !strcmp(prev_patch->old_name, new_name))
+		/* A type-change diff is always split into a patch to
+		 * delete old, immediately followed by a patch to
+		 * create new (see diff.c::run_diff()); in such a case
+		 * it is Ok that the entry to be deleted by the
+		 * previous patch is still in the working tree and in
+		 * the index.
+		 */
+		ok_if_exists = 1;
+	else
+		ok_if_exists = 0;
+
 	if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
-		if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+		if (check_index &&
+		    cache_name_pos(new_name, strlen(new_name)) >= 0 &&
+		    !ok_if_exists)
 			return error("%s: already exists in index", new_name);
 		if (!cached) {
-			if (!lstat(new_name, &st))
-				return error("%s: already exists in working directory", new_name);
-			if (errno != ENOENT)
+			struct stat nst;
+			if (!lstat(new_name, &nst)) {
+				if (S_ISDIR(nst.st_mode) || ok_if_exists)
+					; /* ok */
+				else
+					return error("%s: already exists in working directory", new_name);
+			}
+			else if ((errno != ENOENT) && (errno != ENOTDIR))
 				return error("%s: %s", new_name, strerror(errno));
 		}
 		if (!patch->new_mode) {
@@ -1761,10 +1832,13 @@
 
 static int check_patch_list(struct patch *patch)
 {
+	struct patch *prev_patch = NULL;
 	int error = 0;
 
-	for (;patch ; patch = patch->next)
-		error |= check_patch(patch);
+	for (prev_patch = NULL; patch ; patch = patch->next) {
+		error |= check_patch(patch, prev_patch);
+		prev_patch = patch;
+	}
 	return error;
 }
 
@@ -1972,6 +2046,9 @@
 	int fd;
 
 	if (S_ISLNK(mode))
+		/* Although buf:size is counted string, it also is NUL
+		 * terminated.
+		 */
 		return symlink(buf, path);
 	fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
 	if (fd < 0)
@@ -2009,6 +2086,16 @@
 			return;
 	}
 
+	if (errno == EEXIST || errno == EACCES) {
+		/* We may be trying to create a file where a directory
+		 * used to be.
+		 */
+		struct stat st;
+		errno = 0;
+		if (!lstat(path, &st) && S_ISDIR(st.st_mode) && !rmdir(path))
+			errno = EEXIST;
+	}
+
 	if (errno == EEXIST) {
 		unsigned int nr = getpid();
 
@@ -2043,32 +2130,42 @@
 	cache_tree_invalidate_path(active_cache_tree, path);
 }
 
-static void write_out_one_result(struct patch *patch)
+/* phase zero is to remove, phase one is to create */
+static void write_out_one_result(struct patch *patch, int phase)
 {
 	if (patch->is_delete > 0) {
-		remove_file(patch);
+		if (phase == 0)
+			remove_file(patch);
 		return;
 	}
 	if (patch->is_new > 0 || patch->is_copy) {
-		create_file(patch);
+		if (phase == 1)
+			create_file(patch);
 		return;
 	}
 	/*
 	 * Rename or modification boils down to the same
 	 * thing: remove the old, write the new
 	 */
-	remove_file(patch);
+	if (phase == 0)
+		remove_file(patch);
+	if (phase == 1)
 	create_file(patch);
 }
 
 static void write_out_results(struct patch *list, int skipped_patch)
 {
+	int phase;
+
 	if (!list && !skipped_patch)
 		die("No changes");
 
-	while (list) {
-		write_out_one_result(list);
-		list = list->next;
+	for (phase = 0; phase < 2; phase++) {
+		struct patch *l = list;
+		while (l) {
+			write_out_one_result(l, phase);
+			l = l->next;
+		}
 	}
 }
 
@@ -2097,7 +2194,8 @@
 	return 1;
 }
 
-static int apply_patch(int fd, const char *filename, int inaccurate_eof)
+static int apply_patch(int fd, const char *filename,
+		int reverse, int inaccurate_eof)
 {
 	unsigned long offset, size;
 	char *buffer = read_patch_file(fd, &size);
@@ -2117,6 +2215,8 @@
 		nr = parse_chunk(buffer + offset, size, patch);
 		if (nr < 0)
 			break;
+		if (reverse)
+			reverse_patches(patch);
 		if (use_patch(patch)) {
 			patch_stats(patch);
 			*listp = patch;
@@ -2134,12 +2234,9 @@
 		apply = 0;
 
 	write_index = check_index && apply;
-	if (write_index && newfd < 0) {
+	if (write_index && newfd < 0)
 		newfd = hold_lock_file_for_update(&lock_file,
-						  get_index_file());
-		if (newfd < 0)
-			die("unable to create new index file");
-	}
+						  get_index_file(), 1);
 	if (check_index) {
 		if (read_cache() < 0)
 			die("unable to read index file");
@@ -2177,10 +2274,11 @@
 }
 
 
-int cmd_apply(int argc, const char **argv, char **envp)
+int cmd_apply(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	int read_stdin = 1;
+	int reverse = 0;
 	int inaccurate_eof = 0;
 
 	const char *whitespace_option = NULL;
@@ -2191,7 +2289,7 @@
 		int fd;
 
 		if (!strcmp(arg, "-")) {
-			apply_patch(0, "<stdin>", inaccurate_eof);
+			apply_patch(0, "<stdin>", reverse, inaccurate_eof);
 			read_stdin = 0;
 			continue;
 		}
@@ -2268,6 +2366,10 @@
 			parse_whitespace_option(arg + 13);
 			continue;
 		}
+		if (!strcmp(arg, "-R") || !strcmp(arg, "--reverse")) {
+			reverse = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--inaccurate-eof")) {
 			inaccurate_eof = 1;
 			continue;
@@ -2288,12 +2390,12 @@
 			usage(apply_usage);
 		read_stdin = 0;
 		set_default_whitespace_mode(whitespace_option);
-		apply_patch(fd, arg, inaccurate_eof);
+		apply_patch(fd, arg, reverse, inaccurate_eof);
 		close(fd);
 	}
 	set_default_whitespace_mode(whitespace_option);
 	if (read_stdin)
-		apply_patch(0, "<stdin>", inaccurate_eof);
+		apply_patch(0, "<stdin>", reverse, inaccurate_eof);
 	if (whitespace_error) {
 		if (squelch_whitespace_errors &&
 		    squelch_whitespace_errors < whitespace_error) {
@@ -2323,7 +2425,7 @@
 
 	if (write_index) {
 		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_lock_file(&lock_file))
+		    close(newfd) || commit_lock_file(&lock_file))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index 4d36817..814fb07 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -94,7 +94,7 @@
 	return 0;
 }
 
-int cmd_cat_file(int argc, const char **argv, char **envp)
+int cmd_cat_file(int argc, const char **argv, const char *prefix)
 {
 	unsigned char sha1[20];
 	char type[20];
@@ -102,7 +102,6 @@
 	unsigned long size;
 	int opt;
 
-	setup_git_directory();
 	git_config(git_default_config);
 	if (argc != 3)
 		usage("git-cat-file [-t|-s|-e|-p|<type>] <sha1>");
diff --git a/builtin-check-ref-format.c b/builtin-check-ref-format.c
index 4a23936..fe04be7 100644
--- a/builtin-check-ref-format.c
+++ b/builtin-check-ref-format.c
@@ -6,9 +6,9 @@
 #include "refs.h"
 #include "builtin.h"
 
-int cmd_check_ref_format(int argc, const char **argv, char **envp)
+int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
 {
 	if (argc != 2)
-		usage("git check-ref-format refname");
+		usage("git-check-ref-format refname");
 	return !!check_ref_format(argv[1]);
 }
diff --git a/builtin-commit-tree.c b/builtin-commit-tree.c
index ec082bf..9c98796 100644
--- a/builtin-commit-tree.c
+++ b/builtin-commit-tree.c
@@ -77,7 +77,7 @@
 	return 1;
 }
 
-int cmd_commit_tree(int argc, const char **argv, char **envp)
+int cmd_commit_tree(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	int parents = 0;
@@ -88,8 +88,6 @@
 	unsigned int size;
 
 	setup_ident();
-	setup_git_directory();
-
 	git_config(git_default_config);
 
 	if (argc < 2)
diff --git a/builtin-count.c b/builtin-count.c
index 5ee72df..1d3729a 100644
--- a/builtin-count.c
+++ b/builtin-count.c
@@ -67,7 +67,7 @@
 	}
 }
 
-int cmd_count_objects(int ac, const char **av, char **ep)
+int cmd_count_objects(int ac, const char **av, const char *prefix)
 {
 	int i;
 	int verbose = 0;
diff --git a/builtin-diff-files.c b/builtin-diff-files.c
index 5afc1d7..ac13db7 100644
--- a/builtin-diff-files.c
+++ b/builtin-diff-files.c
@@ -13,13 +13,13 @@
 "git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_files(int argc, const char **argv, char **envp)
+int cmd_diff_files(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
 	int silent = 0;
 
-	git_config(git_diff_config);
-	init_revisions(&rev);
+	init_revisions(&rev, prefix);
+	git_config(git_default_config); /* no "diff" UI options */
 	rev.abbrev = 0;
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
@@ -36,6 +36,9 @@
 			usage(diff_files_usage);
 		argv++; argc--;
 	}
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+
 	/*
 	 * Make sure there are NO revision (i.e. pending object) parameter,
 	 * rev.max_count is reasonable (0 <= n <= 3),
diff --git a/builtin-diff-index.c b/builtin-diff-index.c
index c42ef9a..95a3db1 100644
--- a/builtin-diff-index.c
+++ b/builtin-diff-index.c
@@ -9,14 +9,14 @@
 "[<common diff options>] <tree-ish> [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_index(int argc, const char **argv, char **envp)
+int cmd_diff_index(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
 	int cached = 0;
 	int i;
 
-	git_config(git_diff_config);
-	init_revisions(&rev);
+	init_revisions(&rev, prefix);
+	git_config(git_default_config); /* no "diff" UI options */
 	rev.abbrev = 0;
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
@@ -28,6 +28,9 @@
 		else
 			usage(diff_cache_usage);
 	}
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+
 	/*
 	 * Make sure there is one revision (i.e. pending object),
 	 * and there is no revision filtering parameters.
diff --git a/builtin-diff-stages.c b/builtin-diff-stages.c
index 7c157ca..5960e08 100644
--- a/builtin-diff-stages.c
+++ b/builtin-diff-stages.c
@@ -55,13 +55,12 @@
 	}
 }
 
-int cmd_diff_stages(int ac, const char **av, char **envp)
+int cmd_diff_stages(int ac, const char **av, const char *prefix)
 {
 	int stage1, stage2;
-	const char *prefix = setup_git_directory();
 	const char **pathspec = NULL;
 
-	git_config(git_diff_config);
+	git_config(git_default_config); /* no "diff" UI options */
 	read_cache();
 	diff_setup(&diff_options);
 	while (1 < ac && av[1][0] == '-') {
@@ -85,6 +84,9 @@
 		ac--; av++;
 	}
 
+	if (!diff_options.output_format)
+		diff_options.output_format = DIFF_FORMAT_RAW;
+
 	if (ac < 3 ||
 	    sscanf(av[1], "%d", &stage1) != 1 ||
 	    ! (0 <= stage1 && stage1 <= 3) ||
diff --git a/builtin-diff-tree.c b/builtin-diff-tree.c
index 3409a39..24cb2d7f 100644
--- a/builtin-diff-tree.c
+++ b/builtin-diff-tree.c
@@ -59,7 +59,7 @@
 "  --root        include the initial commit as diff against /dev/null\n"
 COMMON_DIFF_OPTIONS_HELP;
 
-int cmd_diff_tree(int argc, const char **argv, char **envp)
+int cmd_diff_tree(int argc, const char **argv, const char *prefix)
 {
 	int nr_sha1;
 	char line[1000];
@@ -67,9 +67,9 @@
 	static struct rev_info *opt = &log_tree_opt;
 	int read_stdin = 0;
 
-	git_config(git_diff_config);
+	init_revisions(opt, prefix);
+	git_config(git_default_config); /* no "diff" UI options */
 	nr_sha1 = 0;
-	init_revisions(opt);
 	opt->abbrev = 0;
 	opt->diff = 1;
 	argc = setup_revisions(argc, argv, opt, NULL);
@@ -84,6 +84,9 @@
 		usage(diff_tree_usage);
 	}
 
+	if (!opt->diffopt.output_format)
+		opt->diffopt.output_format = DIFF_FORMAT_RAW;
+
 	/*
 	 * NOTE! We expect "a ^b" to be equal to "a..b", so we
 	 * reverse the order of the objects if the second one
diff --git a/builtin-diff.c b/builtin-diff.c
index 99a2f76..a090e29 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -23,7 +23,7 @@
 };
 
 static const char builtin_diff_usage[] =
-"diff <options> <rev>{0,2} -- <path>*";
+"git-diff <options> <rev>{0,2} -- <path>*";
 
 static int builtin_diff_files(struct rev_info *revs,
 			      int argc, const char **argv)
@@ -39,8 +39,6 @@
 			revs->max_count = 3;
 		else if (!strcmp(arg, "-q"))
 			silent = 1;
-		else if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
 		else
 			usage(builtin_diff_usage);
 		argv++; argc--;
@@ -56,7 +54,7 @@
 	    3 < revs->max_count)
 		usage(builtin_diff_usage);
 	if (revs->max_count < 0 &&
-	    (revs->diffopt.output_format == DIFF_FORMAT_PATCH))
+	    (revs->diffopt.output_format & DIFF_FORMAT_PATCH))
 		revs->combine_merges = revs->dense_combined_merges = 1;
 	/*
 	 * Backward compatibility wart - "diff-files -s" used to
@@ -107,14 +105,9 @@
 	/* Blob vs file in the working tree*/
 	struct stat st;
 
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
-		else
-			usage(builtin_diff_usage);
-		argv++; argc--;
-	}
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
 	if (lstat(path, &st))
 		die("'%s': %s", path, strerror(errno));
 	if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
@@ -132,23 +125,15 @@
 			      int argc, const char **argv,
 			      struct blobinfo *blob)
 {
-	/* Blobs: the arguments are reversed when setup_revisions()
-	 * picked them up.
-	 */
 	unsigned mode = canon_mode(S_IFREG | 0644);
 
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
-		else
-			usage(builtin_diff_usage);
-		argv++; argc--;
-	}
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
 	stuff_change(&revs->diffopt,
 		     mode, mode,
-		     blob[1].sha1, blob[0].sha1,
-		     blob[0].name, blob[0].name);
+		     blob[0].sha1, blob[1].sha1,
+		     blob[0].name, blob[1].name);
 	diffcore_std(&revs->diffopt);
 	diff_flush(&revs->diffopt);
 	return 0;
@@ -162,8 +147,6 @@
 		const char *arg = argv[1];
 		if (!strcmp(arg, "--cached"))
 			cached = 1;
-		else if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
 		else
 			usage(builtin_diff_usage);
 		argv++; argc--;
@@ -185,17 +168,12 @@
 {
 	const unsigned char *(sha1[2]);
 	int swap = 0;
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
-		else
-			usage(builtin_diff_usage);
-		argv++; argc--;
-	}
+
+	if (argc > 1)
+		usage(builtin_diff_usage);
 
 	/* We saw two trees, ent[0] and ent[1].
-	 * if ent[1] is unintesting, they are swapped
+	 * if ent[1] is uninteresting, they are swapped
 	 */
 	if (ent[1].item->flags & UNINTERESTING)
 		swap = 1;
@@ -214,14 +192,9 @@
 	const unsigned char (*parent)[20];
 	int i;
 
-	while (1 < argc) {
-		const char *arg = argv[1];
-		if (!strcmp(arg, "--raw"))
-			revs->diffopt.output_format = DIFF_FORMAT_RAW;
-		else
-			usage(builtin_diff_usage);
-		argv++; argc--;
-	}
+	if (argc > 1)
+		usage(builtin_diff_usage);
+
 	if (!revs->dense_combined_merges && !revs->combine_merges)
 		revs->dense_combined_merges = revs->combine_merges = 1;
 	parent = xmalloc(ents * sizeof(*parent));
@@ -245,7 +218,7 @@
 	add_pending_object(revs, obj, "HEAD");
 }
 
-int cmd_diff(int argc, const char **argv, char **envp)
+int cmd_diff(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	struct rev_info rev;
@@ -274,11 +247,16 @@
 	 * Other cases are errors.
 	 */
 
-	git_config(git_diff_config);
-	init_revisions(&rev);
-	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	git_config(git_diff_ui_config);
+	init_revisions(&rev, prefix);
 
 	argc = setup_revisions(argc, argv, &rev, NULL);
+	if (!rev.diffopt.output_format) {
+		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+		if (diff_setup_done(&rev.diffopt) < 0)
+			die("diff_setup_done failed");
+	}
+
 	/* Do we have --cached and not have a pending object, then
 	 * default to HEAD by hand.  Eek.
 	 */
@@ -305,9 +283,9 @@
 		obj = deref_tag(obj, NULL, 0);
 		if (!obj)
 			die("invalid object '%s' given.", name);
-		if (obj->type == TYPE_COMMIT)
+		if (obj->type == OBJ_COMMIT)
 			obj = &((struct commit *)obj)->tree->object;
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			if (ARRAY_SIZE(ent) <= ents)
 				die("more than %d trees given: '%s'",
 				    (int) ARRAY_SIZE(ent), name);
@@ -317,7 +295,7 @@
 			ents++;
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			if (2 <= blobs)
 				die("more than two blobs given: '%s'", name);
 			memcpy(blob[blobs].sha1, obj->sha1, 20);
@@ -366,7 +344,16 @@
 		return builtin_diff_index(&rev, argc, argv);
 	else if (ents == 2)
 		return builtin_diff_tree(&rev, argc, argv, ent);
+	else if ((ents == 3) && (ent[0].item->flags & UNINTERESTING)) {
+		/* diff A...B where there is one sane merge base between
+		 * A and B.  We have ent[0] == merge-base, ent[1] == A,
+		 * and ent[2] == B.  Show diff between the base and B.
+		 */
+		ent[1] = ent[2];
+		return builtin_diff_tree(&rev, argc, argv, ent);
+	}
 	else
-		return builtin_diff_combined(&rev, argc, argv, ent, ents);
+		return builtin_diff_combined(&rev, argc, argv,
+					     ent, ents);
 	usage(builtin_diff_usage);
 }
diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c
new file mode 100644
index 0000000..485ede7
--- /dev/null
+++ b/builtin-fmt-merge-msg.c
@@ -0,0 +1,360 @@
+#include "builtin.h"
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "tag.h"
+
+static const char *fmt_merge_msg_usage =
+	"git-fmt-merge-msg [--summary] [--no-summary] [--file <file>]";
+
+static int merge_summary = 0;
+
+static int fmt_merge_msg_config(const char *key, const char *value)
+{
+	if (!strcmp("merge.summary", key))
+		merge_summary = git_config_bool(key, value);
+	return 0;
+}
+
+struct list {
+	char **list;
+	void **payload;
+	unsigned nr, alloc;
+};
+
+static void append_to_list(struct list *list, char *value, void *payload)
+{
+	if (list->nr == list->alloc) {
+		list->alloc += 32;
+		list->list = realloc(list->list, sizeof(char *) * list->alloc);
+		list->payload = realloc(list->payload,
+				sizeof(char *) * list->alloc);
+	}
+	list->payload[list->nr] = payload;
+	list->list[list->nr++] = value;
+}
+
+static int find_in_list(struct list *list, char *value)
+{
+	int i;
+
+	for (i = 0; i < list->nr; i++)
+		if (!strcmp(list->list[i], value))
+			return i;
+
+	return -1;
+}
+
+static void free_list(struct list *list)
+{
+	int i;
+
+	if (list->alloc == 0)
+		return;
+
+	for (i = 0; i < list->nr; i++) {
+		free(list->list[i]);
+		if (list->payload[i])
+			free(list->payload[i]);
+	}
+	free(list->list);
+	free(list->payload);
+	list->nr = list->alloc = 0;
+}
+
+struct src_data {
+	struct list branch, tag, r_branch, generic;
+	int head_status;
+};
+
+static struct list srcs = { NULL, NULL, 0, 0};
+static struct list origins = { NULL, NULL, 0, 0};
+
+static int handle_line(char *line)
+{
+	int i, len = strlen(line);
+	unsigned char *sha1;
+	char *src, *origin;
+	struct src_data *src_data;
+	int pulling_head = 0;
+
+	if (len < 43 || line[40] != '\t')
+		return 1;
+
+	if (!strncmp(line + 41, "not-for-merge", 13))
+		return 0;
+
+	if (line[41] != '\t')
+		return 2;
+
+	line[40] = 0;
+	sha1 = xmalloc(20);
+	i = get_sha1(line, sha1);
+	line[40] = '\t';
+	if (i)
+		return 3;
+
+	if (line[len - 1] == '\n')
+		line[len - 1] = 0;
+	line += 42;
+
+	src = strstr(line, " of ");
+	if (src) {
+		*src = 0;
+		src += 4;
+		pulling_head = 0;
+	} else {
+		src = line;
+		pulling_head = 1;
+	}
+
+	i = find_in_list(&srcs, src);
+	if (i < 0) {
+		i = srcs.nr;
+		append_to_list(&srcs, strdup(src),
+				xcalloc(1, sizeof(struct src_data)));
+	}
+	src_data = srcs.payload[i];
+
+	if (pulling_head) {
+		origin = strdup(src);
+		src_data->head_status |= 1;
+	} else if (!strncmp(line, "branch ", 7)) {
+		origin = strdup(line + 7);
+		append_to_list(&src_data->branch, origin, NULL);
+		src_data->head_status |= 2;
+	} else if (!strncmp(line, "tag ", 4)) {
+		origin = line;
+		append_to_list(&src_data->tag, strdup(origin + 4), NULL);
+		src_data->head_status |= 2;
+	} else if (!strncmp(line, "remote branch ", 14)) {
+		origin = strdup(line + 14);
+		append_to_list(&src_data->r_branch, origin, NULL);
+		src_data->head_status |= 2;
+	} else {
+		origin = strdup(src);
+		append_to_list(&src_data->generic, strdup(line), NULL);
+		src_data->head_status |= 2;
+	}
+
+	if (!strcmp(".", src) || !strcmp(src, origin)) {
+		int len = strlen(origin);
+		if (origin[0] == '\'' && origin[len - 1] == '\'') {
+			char *new_origin = malloc(len - 1);
+			memcpy(new_origin, origin + 1, len - 2);
+			new_origin[len - 1] = 0;
+			origin = new_origin;
+		} else
+			origin = strdup(origin);
+	} else {
+		char *new_origin = malloc(strlen(origin) + strlen(src) + 5);
+		sprintf(new_origin, "%s of %s", origin, src);
+		origin = new_origin;
+	}
+	append_to_list(&origins, origin, sha1);
+	return 0;
+}
+
+static void print_joined(const char *singular, const char *plural,
+		struct list *list)
+{
+	if (list->nr == 0)
+		return;
+	if (list->nr == 1) {
+		printf("%s%s", singular, list->list[0]);
+	} else {
+		int i;
+		printf("%s", plural);
+		for (i = 0; i < list->nr - 1; i++)
+			printf("%s%s", i > 0 ? ", " : "", list->list[i]);
+		printf(" and %s", list->list[list->nr - 1]);
+	}
+}
+
+static void shortlog(const char *name, unsigned char *sha1,
+		struct commit *head, struct rev_info *rev, int limit)
+{
+	int i, count = 0;
+	struct commit *commit;
+	struct object *branch;
+	struct list subjects = { NULL, NULL, 0, 0 };
+	int flags = UNINTERESTING | TREECHANGE | SEEN | SHOWN | ADDED;
+
+	branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+	if (!branch || branch->type != OBJ_COMMIT)
+		return;
+
+	setup_revisions(0, NULL, rev, NULL);
+	rev->ignore_merges = 1;
+	add_pending_object(rev, branch, name);
+	add_pending_object(rev, &head->object, "^HEAD");
+	head->object.flags |= UNINTERESTING;
+	prepare_revision_walk(rev);
+	while ((commit = get_revision(rev)) != NULL) {
+		char *oneline, *bol, *eol;
+
+		/* ignore merges */
+		if (commit->parents && commit->parents->next)
+			continue;
+
+		count++;
+		if (subjects.nr > limit)
+			continue;
+
+		bol = strstr(commit->buffer, "\n\n");
+		if (!bol) {
+			append_to_list(&subjects, strdup(sha1_to_hex(
+							commit->object.sha1)),
+					NULL);
+			continue;
+		}
+
+		bol += 2;
+		eol = strchr(bol, '\n');
+
+		if (eol) {
+			int len = eol - bol;
+			oneline = malloc(len + 1);
+			memcpy(oneline, bol, len);
+			oneline[len] = 0;
+		} else
+			oneline = strdup(bol);
+		append_to_list(&subjects, oneline, NULL);
+	}
+
+	if (count > limit)
+		printf("\n* %s: (%d commits)\n", name, count);
+	else
+		printf("\n* %s:\n", name);
+
+	for (i = 0; i < subjects.nr; i++)
+		if (i >= limit)
+			printf("  ...\n");
+		else
+			printf("  %s\n", subjects.list[i]);
+
+	clear_commit_marks((struct commit *)branch, flags);
+	clear_commit_marks(head, flags);
+	free_commit_list(rev->commits);
+	rev->commits = NULL;
+	rev->pending.nr = 0;
+
+	free_list(&subjects);
+}
+
+int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
+{
+	int limit = 20, i = 0;
+	char line[1024];
+	FILE *in = stdin;
+	const char *sep = "";
+	unsigned char head_sha1[20];
+	const char *head, *current_branch;
+
+	git_config(fmt_merge_msg_config);
+
+	while (argc > 1) {
+		if (!strcmp(argv[1], "--summary"))
+			merge_summary = 1;
+		else if (!strcmp(argv[1], "--no-summary"))
+			merge_summary = 0;
+		else if (!strcmp(argv[1], "-F") || !strcmp(argv[1], "--file")) {
+			if (argc < 2)
+				die ("Which file?");
+			if (!strcmp(argv[2], "-"))
+				in = stdin;
+			else {
+				fclose(in);
+				in = fopen(argv[2], "r");
+			}
+			argc--; argv++;
+		} else
+			break;
+		argc--; argv++;
+	}
+
+	if (argc > 1)
+		usage(fmt_merge_msg_usage);
+
+	/* get current branch */
+	head = strdup(git_path("HEAD"));
+	current_branch = resolve_ref(head, head_sha1, 1);
+	current_branch += strlen(head) - 4;
+	free((char *)head);
+	if (!strncmp(current_branch, "refs/heads/", 11))
+		current_branch += 11;
+
+	while (fgets(line, sizeof(line), in)) {
+		i++;
+		if (line[0] == 0)
+			continue;
+		if (handle_line(line))
+			die ("Error in line %d: %s", i, line);
+	}
+
+	printf("Merge ");
+	for (i = 0; i < srcs.nr; i++) {
+		struct src_data *src_data = srcs.payload[i];
+		const char *subsep = "";
+
+		printf(sep);
+		sep = "; ";
+
+		if (src_data->head_status == 1) {
+			printf(srcs.list[i]);
+			continue;
+		}
+		if (src_data->head_status == 3) {
+			subsep = ", ";
+			printf("HEAD");
+		}
+		if (src_data->branch.nr) {
+			printf(subsep);
+			subsep = ", ";
+			print_joined("branch ", "branches ", &src_data->branch);
+		}
+		if (src_data->r_branch.nr) {
+			printf(subsep);
+			subsep = ", ";
+			print_joined("remote branch ", "remote branches ",
+					&src_data->r_branch);
+		}
+		if (src_data->tag.nr) {
+			printf(subsep);
+			subsep = ", ";
+			print_joined("tag ", "tags ", &src_data->tag);
+		}
+		if (src_data->generic.nr) {
+			printf(subsep);
+			print_joined("commit ", "commits ", &src_data->generic);
+		}
+		if (strcmp(".", srcs.list[i]))
+			printf(" of %s", srcs.list[i]);
+	}
+
+	if (!strcmp("master", current_branch))
+		putchar('\n');
+	else
+		printf(" into %s\n", current_branch);
+
+	if (merge_summary) {
+		struct commit *head;
+		struct rev_info rev;
+
+		head = lookup_commit(head_sha1);
+		init_revisions(&rev, prefix);
+		rev.commit_format = CMIT_FMT_ONELINE;
+		rev.ignore_merges = 1;
+		rev.limited = 1;
+
+		for (i = 0; i < origins.nr; i++)
+			shortlog(origins.list[i], origins.payload[i],
+					head, &rev, limit);
+	}
+
+	/* No cleanup yet; is standalone anyway */
+
+	return 0;
+}
+
diff --git a/builtin-grep.c b/builtin-grep.c
index 2e7986c..93b7e07 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -82,17 +82,47 @@
 	return 0;
 }
 
+enum grep_pat_token {
+	GREP_PATTERN,
+	GREP_AND,
+	GREP_OPEN_PAREN,
+	GREP_CLOSE_PAREN,
+	GREP_NOT,
+	GREP_OR,
+};
+
 struct grep_pat {
 	struct grep_pat *next;
 	const char *origin;
 	int no;
+	enum grep_pat_token token;
 	const char *pattern;
 	regex_t regexp;
 };
 
+enum grep_expr_node {
+	GREP_NODE_ATOM,
+	GREP_NODE_NOT,
+	GREP_NODE_AND,
+	GREP_NODE_OR,
+};
+
+struct grep_expr {
+	enum grep_expr_node node;
+	union {
+		struct grep_pat *atom;
+		struct grep_expr *unary;
+		struct {
+			struct grep_expr *left;
+			struct grep_expr *right;
+		} binary;
+	} u;
+};
+
 struct grep_opt {
 	struct grep_pat *pattern_list;
 	struct grep_pat **pattern_tail;
+	struct grep_expr *pattern_expression;
 	regex_t regexp;
 	unsigned linenum:1;
 	unsigned invert:1;
@@ -105,43 +135,224 @@
 #define GREP_BINARY_NOMATCH	1
 #define GREP_BINARY_TEXT	2
 	unsigned binary:2;
+	unsigned extended:1;
 	int regflags;
 	unsigned pre_context;
 	unsigned post_context;
 };
 
 static void add_pattern(struct grep_opt *opt, const char *pat,
-			const char *origin, int no)
+			const char *origin, int no, enum grep_pat_token t)
 {
 	struct grep_pat *p = xcalloc(1, sizeof(*p));
 	p->pattern = pat;
 	p->origin = origin;
 	p->no = no;
+	p->token = t;
 	*opt->pattern_tail = p;
 	opt->pattern_tail = &p->next;
 	p->next = NULL;
 }
 
+static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
+{
+	int err = regcomp(&p->regexp, p->pattern, opt->regflags);
+	if (err) {
+		char errbuf[1024];
+		char where[1024];
+		if (p->no)
+			sprintf(where, "In '%s' at %d, ",
+				p->origin, p->no);
+		else if (p->origin)
+			sprintf(where, "%s, ", p->origin);
+		else
+			where[0] = 0;
+		regerror(err, &p->regexp, errbuf, 1024);
+		regfree(&p->regexp);
+		die("%s'%s': %s", where, p->pattern, errbuf);
+	}
+}
+
+#if DEBUG
+static inline void indent(int in)
+{
+	int i;
+	for (i = 0; i < in; i++) putchar(' ');
+}
+
+static void dump_pattern_exp(struct grep_expr *x, int in)
+{
+	switch (x->node) {
+	case GREP_NODE_ATOM:
+		indent(in);
+		puts(x->u.atom->pattern);
+		break;
+	case GREP_NODE_NOT:
+		indent(in);
+		puts("--not");
+		dump_pattern_exp(x->u.unary, in+1);
+		break;
+	case GREP_NODE_AND:
+		dump_pattern_exp(x->u.binary.left, in+1);
+		indent(in);
+		puts("--and");
+		dump_pattern_exp(x->u.binary.right, in+1);
+		break;
+	case GREP_NODE_OR:
+		dump_pattern_exp(x->u.binary.left, in+1);
+		indent(in);
+		puts("--or");
+		dump_pattern_exp(x->u.binary.right, in+1);
+		break;
+	}
+}
+
+static void looking_at(const char *msg, struct grep_pat **list)
+{
+	struct grep_pat *p = *list;
+	fprintf(stderr, "%s: looking at ", msg);
+	if (!p)
+		fprintf(stderr, "empty\n");
+	else
+		fprintf(stderr, "<%s>\n", p->pattern);
+}
+#else
+#define looking_at(a,b) do {} while(0)
+#endif
+
+static struct grep_expr *compile_pattern_expr(struct grep_pat **);
+static struct grep_expr *compile_pattern_atom(struct grep_pat **list)
+{
+	struct grep_pat *p;
+	struct grep_expr *x;
+
+	looking_at("atom", list);
+
+	p = *list;
+	switch (p->token) {
+	case GREP_PATTERN: /* atom */
+		x = xcalloc(1, sizeof (struct grep_expr));
+		x->node = GREP_NODE_ATOM;
+		x->u.atom = p;
+		*list = p->next;
+		return x;
+	case GREP_OPEN_PAREN:
+		*list = p->next;
+		x = compile_pattern_expr(list);
+		if (!x)
+			return NULL;
+		if (!*list || (*list)->token != GREP_CLOSE_PAREN)
+			die("unmatched parenthesis");
+		*list = (*list)->next;
+		return x;
+	default:
+		return NULL;
+	}
+}
+
+static struct grep_expr *compile_pattern_not(struct grep_pat **list)
+{
+	struct grep_pat *p;
+	struct grep_expr *x;
+
+	looking_at("not", list);
+
+	p = *list;
+	switch (p->token) {
+	case GREP_NOT:
+		if (!p->next)
+			die("--not not followed by pattern expression");
+		*list = p->next;
+		x = xcalloc(1, sizeof (struct grep_expr));
+		x->node = GREP_NODE_NOT;
+		x->u.unary = compile_pattern_not(list);
+		if (!x->u.unary)
+			die("--not followed by non pattern expression");
+		return x;
+	default:
+		return compile_pattern_atom(list);
+	}
+}
+
+static struct grep_expr *compile_pattern_and(struct grep_pat **list)
+{
+	struct grep_pat *p;
+	struct grep_expr *x, *y, *z;
+
+	looking_at("and", list);
+
+	x = compile_pattern_not(list);
+	p = *list;
+	if (p && p->token == GREP_AND) {
+		if (!p->next)
+			die("--and not followed by pattern expression");
+		*list = p->next;
+		y = compile_pattern_and(list);
+		if (!y)
+			die("--and not followed by pattern expression");
+		z = xcalloc(1, sizeof (struct grep_expr));
+		z->node = GREP_NODE_AND;
+		z->u.binary.left = x;
+		z->u.binary.right = y;
+		return z;
+	}
+	return x;
+}
+
+static struct grep_expr *compile_pattern_or(struct grep_pat **list)
+{
+	struct grep_pat *p;
+	struct grep_expr *x, *y, *z;
+
+	looking_at("or", list);
+
+	x = compile_pattern_and(list);
+	p = *list;
+	if (x && p && p->token != GREP_CLOSE_PAREN) {
+		y = compile_pattern_or(list);
+		if (!y)
+			die("not a pattern expression %s", p->pattern);
+		z = xcalloc(1, sizeof (struct grep_expr));
+		z->node = GREP_NODE_OR;
+		z->u.binary.left = x;
+		z->u.binary.right = y;
+		return z;
+	}
+	return x;
+}
+
+static struct grep_expr *compile_pattern_expr(struct grep_pat **list)
+{
+	looking_at("expr", list);
+
+	return compile_pattern_or(list);
+}
+
 static void compile_patterns(struct grep_opt *opt)
 {
 	struct grep_pat *p;
+
+	/* First compile regexps */
 	for (p = opt->pattern_list; p; p = p->next) {
-		int err = regcomp(&p->regexp, p->pattern, opt->regflags);
-		if (err) {
-			char errbuf[1024];
-			char where[1024];
-			if (p->no)
-				sprintf(where, "In '%s' at %d, ",
-					p->origin, p->no);
-			else if (p->origin)
-				sprintf(where, "%s, ", p->origin);
-			else
-				where[0] = 0;
-			regerror(err, &p->regexp, errbuf, 1024);
-			regfree(&p->regexp);
-			die("%s'%s': %s", where, p->pattern, errbuf);
-		}
+		if (p->token == GREP_PATTERN)
+			compile_regexp(p, opt);
+		else
+			opt->extended = 1;
 	}
+
+	if (!opt->extended)
+		return;
+
+	/* Then bundle them up in an expression.
+	 * A classic recursive descent parser would do.
+	 */
+	p = opt->pattern_list;
+	opt->pattern_expression = compile_pattern_expr(&p);
+#if DEBUG
+	dump_pattern_exp(opt->pattern_expression, 0);
+#endif
+	if (p)
+		die("incomplete pattern expression: %s", p->pattern);
 }
 
 static char *end_of_line(char *cp, unsigned long *left)
@@ -196,6 +407,94 @@
 	}
 }
 
+static int match_one_pattern(struct grep_opt *opt, struct grep_pat *p, char *bol, char *eol)
+{
+	int hit = 0;
+	int at_true_bol = 1;
+	regmatch_t pmatch[10];
+
+ again:
+	if (!opt->fixed) {
+		regex_t *exp = &p->regexp;
+		hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
+			       pmatch, 0);
+	}
+	else {
+		hit = !fixmatch(p->pattern, bol, pmatch);
+	}
+
+	if (hit && opt->word_regexp) {
+		if ((pmatch[0].rm_so < 0) ||
+		    (eol - bol) <= pmatch[0].rm_so ||
+		    (pmatch[0].rm_eo < 0) ||
+		    (eol - bol) < pmatch[0].rm_eo)
+			die("regexp returned nonsense");
+
+		/* Match beginning must be either beginning of the
+		 * line, or at word boundary (i.e. the last char must
+		 * not be a word char).  Similarly, match end must be
+		 * either end of the line, or at word boundary
+		 * (i.e. the next char must not be a word char).
+		 */
+		if ( ((pmatch[0].rm_so == 0 && at_true_bol) ||
+		      !word_char(bol[pmatch[0].rm_so-1])) &&
+		     ((pmatch[0].rm_eo == (eol-bol)) ||
+		      !word_char(bol[pmatch[0].rm_eo])) )
+			;
+		else
+			hit = 0;
+
+		if (!hit && pmatch[0].rm_so + bol + 1 < eol) {
+			/* There could be more than one match on the
+			 * line, and the first match might not be
+			 * strict word match.  But later ones could be!
+			 */
+			bol = pmatch[0].rm_so + bol + 1;
+			at_true_bol = 0;
+			goto again;
+		}
+	}
+	return hit;
+}
+
+static int match_expr_eval(struct grep_opt *opt,
+			   struct grep_expr *x,
+			   char *bol, char *eol)
+{
+	switch (x->node) {
+	case GREP_NODE_ATOM:
+		return match_one_pattern(opt, x->u.atom, bol, eol);
+		break;
+	case GREP_NODE_NOT:
+		return !match_expr_eval(opt, x->u.unary, bol, eol);
+	case GREP_NODE_AND:
+		return (match_expr_eval(opt, x->u.binary.left, bol, eol) &&
+			match_expr_eval(opt, x->u.binary.right, bol, eol));
+	case GREP_NODE_OR:
+		return (match_expr_eval(opt, x->u.binary.left, bol, eol) ||
+			match_expr_eval(opt, x->u.binary.right, bol, eol));
+	}
+	die("Unexpected node type (internal error) %d\n", x->node);
+}
+
+static int match_expr(struct grep_opt *opt, char *bol, char *eol)
+{
+	struct grep_expr *x = opt->pattern_expression;
+	return match_expr_eval(opt, x, bol, eol);
+}
+
+static int match_line(struct grep_opt *opt, char *bol, char *eol)
+{
+	struct grep_pat *p;
+	if (opt->extended)
+		return match_expr(opt, bol, eol);
+	for (p = opt->pattern_list; p; p = p->next) {
+		if (match_one_pattern(opt, p, bol, eol))
+			return 1;
+	}
+	return 0;
+}
+
 static int grep_buffer(struct grep_opt *opt, const char *name,
 		       char *buf, unsigned long size)
 {
@@ -231,46 +530,15 @@
 		hunk_mark = "--\n";
 
 	while (left) {
-		regmatch_t pmatch[10];
 		char *eol, ch;
 		int hit = 0;
-		struct grep_pat *p;
 
 		eol = end_of_line(bol, &left);
 		ch = *eol;
 		*eol = 0;
 
-		for (p = opt->pattern_list; p; p = p->next) {
-			if (!opt->fixed) {
-				regex_t *exp = &p->regexp;
-				hit = !regexec(exp, bol, ARRAY_SIZE(pmatch),
-					       pmatch, 0);
-			}
-			else {
-				hit = !fixmatch(p->pattern, bol, pmatch);
-			}
+		hit = match_line(opt, bol, eol);
 
-			if (hit && opt->word_regexp) {
-				/* Match beginning must be either
-				 * beginning of the line, or at word
-				 * boundary (i.e. the last char must
-				 * not be alnum or underscore).
-				 */
-				if ((pmatch[0].rm_so < 0) ||
-				    (eol - bol) <= pmatch[0].rm_so ||
-				    (pmatch[0].rm_eo < 0) ||
-				    (eol - bol) < pmatch[0].rm_eo)
-					die("regexp returned nonsense");
-				if (pmatch[0].rm_so != 0 &&
-				    word_char(bol[pmatch[0].rm_so-1]))
-					hit = 0;
-				if (pmatch[0].rm_eo != (eol-bol) &&
-				    word_char(bol[pmatch[0].rm_eo]))
-					hit = 0;
-			}
-			if (hit)
-				break;
-		}
 		/* "grep -v -e foo -e bla" should list lines
 		 * that do not have either, so inversion should
 		 * be done outside.
@@ -446,12 +714,14 @@
 
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
-	int i, nr, argc, hit, len;
+	int i, nr, argc, hit, len, status;
 	const char *argv[MAXARGS+1];
 	char randarg[ARGBUF];
 	char *argptr = randarg;
 	struct grep_pat *p;
 
+	if (opt->extended)
+		return -1;
 	len = nr = 0;
 	push_arg("grep");
 	if (opt->fixed)
@@ -536,12 +806,17 @@
 		argv[argc++] = name;
 		if (argc < MAXARGS)
 			continue;
-		hit += exec_grep(argc, argv);
+		status = exec_grep(argc, argv);
+		if (0 < status)
+			hit = 1;
 		argc = nr;
 	}
-	if (argc > nr)
-		hit += exec_grep(argc, argv);
-	return 0;
+	if (argc > nr) {
+		status = exec_grep(argc, argv);
+		if (0 < status)
+			hit = 1;
+	}
+	return hit;
 }
 
 static int grep_cache(struct grep_opt *opt, const char **paths, int cached)
@@ -631,9 +906,9 @@
 static int grep_object(struct grep_opt *opt, const char **paths,
 		       struct object *obj, const char *name)
 {
-	if (obj->type == TYPE_BLOB)
+	if (obj->type == OBJ_BLOB)
 		return grep_sha1(opt, obj->sha1, name);
-	if (obj->type == TYPE_COMMIT || obj->type == TYPE_TREE) {
+	if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
 		struct tree_desc tree;
 		void *data;
 		int hit;
@@ -652,14 +927,20 @@
 static const char builtin_grep_usage[] =
 "git-grep <option>* <rev>* [-e] <pattern> [<path>...]";
 
-int cmd_grep(int argc, const char **argv, char **envp)
+static const char emsg_invalid_context_len[] =
+"%s: invalid context length argument";
+static const char emsg_missing_context_len[] =
+"missing context length argument";
+static const char emsg_missing_argument[] =
+"option requires an argument -%s";
+
+int cmd_grep(int argc, const char **argv, const char *prefix)
 {
 	int hit = 0;
 	int cached = 0;
 	int seen_dashdash = 0;
 	struct grep_opt opt;
 	struct object_array list = { 0, 0, NULL };
-	const char *prefix = setup_git_directory();
 	const char **paths = NULL;
 	int i;
 
@@ -674,7 +955,7 @@
 	 * pattern, but then what follows it must be zero or more
 	 * valid refs up to the -- (if exists), and then existing
 	 * paths.  If there is an explicit pattern, then the first
-	 * unrecocnized non option is the beginning of the refs list
+	 * unrecognized non option is the beginning of the refs list
 	 * that continues up to the -- (if exists), and then paths.
 	 */
 
@@ -759,7 +1040,7 @@
 			case 'A': case 'B': case 'C':
 				if (!arg[2]) {
 					if (argc <= 1)
-						usage(builtin_grep_usage);
+						die(emsg_missing_context_len);
 					scan = *++argv;
 					argc--;
 				}
@@ -771,7 +1052,7 @@
 				break;
 			}
 			if (sscanf(scan, "%u", &num) != 1)
-				usage(builtin_grep_usage);
+				die(emsg_invalid_context_len, scan);
 			switch (arg[1]) {
 			case 'A':
 				opt.post_context = num;
@@ -790,7 +1071,7 @@
 			int lno = 0;
 			char buf[1024];
 			if (argc <= 1)
-				usage(builtin_grep_usage);
+				die(emsg_missing_argument, arg);
 			patterns = fopen(argv[1], "r");
 			if (!patterns)
 				die("'%s': %s", argv[1], strerror(errno));
@@ -801,30 +1082,55 @@
 				/* ignore empty line like grep does */
 				if (!buf[0])
 					continue;
-				add_pattern(&opt, strdup(buf), argv[1], ++lno);
+				add_pattern(&opt, strdup(buf), argv[1], ++lno,
+					    GREP_PATTERN);
 			}
 			fclose(patterns);
 			argv++;
 			argc--;
 			continue;
 		}
+		if (!strcmp("--not", arg)) {
+			add_pattern(&opt, arg, "command line", 0, GREP_NOT);
+			continue;
+		}
+		if (!strcmp("--and", arg)) {
+			add_pattern(&opt, arg, "command line", 0, GREP_AND);
+			continue;
+		}
+		if (!strcmp("--or", arg))
+			continue; /* no-op */
+		if (!strcmp("(", arg)) {
+			add_pattern(&opt, arg, "command line", 0, GREP_OPEN_PAREN);
+			continue;
+		}
+		if (!strcmp(")", arg)) {
+			add_pattern(&opt, arg, "command line", 0, GREP_CLOSE_PAREN);
+			continue;
+		}
 		if (!strcmp("-e", arg)) {
 			if (1 < argc) {
-				add_pattern(&opt, argv[1], "-e option", 0);
+				add_pattern(&opt, argv[1], "-e option", 0,
+					    GREP_PATTERN);
 				argv++;
 				argc--;
 				continue;
 			}
-			usage(builtin_grep_usage);
+			die(emsg_missing_argument, arg);
 		}
-		if (!strcmp("--", arg))
+		if (!strcmp("--", arg)) {
+			/* later processing wants to have this at argv[1] */
+			argv--;
+			argc++;
 			break;
+		}
 		if (*arg == '-')
 			usage(builtin_grep_usage);
 
 		/* First unrecognized non-option token */
 		if (!opt.pattern_list) {
-			add_pattern(&opt, arg, "command line", 0);
+			add_pattern(&opt, arg, "command line", 0,
+				    GREP_PATTERN);
 			break;
 		}
 		else {
diff --git a/builtin-help.c b/builtin-help.c
index 7470faa..6484cb9 100644
--- a/builtin-help.c
+++ b/builtin-help.c
@@ -9,10 +9,8 @@
 #include "exec_cmd.h"
 #include "common-cmds.h"
 
-static const char git_usage[] =
-	"Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
 
-/* most gui terms set COLUMNS (although some don't export it) */
+/* most GUI terminals set COLUMNS (although some don't export it) */
 static int term_columns(void)
 {
 	char *col_string = getenv("COLUMNS");
@@ -142,7 +140,7 @@
 			continue;
 
 		entlen = strlen(de->d_name);
-		if (4 < entlen && !strcmp(de->d_name + entlen - 4, ".exe"))
+		if (has_extension(de->d_name, ".exe"))
 			entlen -= 4;
 
 		if (longest < entlen)
@@ -178,31 +176,6 @@
 	puts("(use 'git help -a' to get a list of all installed git commands)");
 }
 
-void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
-{
-	if (fmt) {
-		va_list ap;
-
-		va_start(ap, fmt);
-		printf("git: ");
-		vprintf(fmt, ap);
-		va_end(ap);
-		putchar('\n');
-	}
-	else
-		puts(git_usage);
-
-	if (exec_path) {
-		putchar('\n');
-		if (show_all)
-			list_commands(exec_path, "git-*");
-		else
-			list_common_cmds_help();
-        }
-
-	exit(1);
-}
-
 static void show_man_page(const char *git_cmd)
 {
 	const char *page;
@@ -221,21 +194,40 @@
 	execlp("man", "man", page, NULL);
 }
 
-int cmd_version(int argc, const char **argv, char **envp)
+void help_unknown_cmd(const char *cmd)
+{
+	printf("git: '%s' is not a git-command\n\n", cmd);
+	list_common_cmds_help();
+	exit(1);
+}
+
+int cmd_version(int argc, const char **argv, const char *prefix)
 {
 	printf("git version %s\n", git_version_string);
 	return 0;
 }
 
-int cmd_help(int argc, const char **argv, char **envp)
+int cmd_help(int argc, const char **argv, const char *prefix)
 {
-	const char *help_cmd = argv[1];
-	if (!help_cmd)
-		cmd_usage(0, git_exec_path(), NULL);
-	else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a"))
-		cmd_usage(1, git_exec_path(), NULL);
+	const char *help_cmd = argc > 1 ? argv[1] : NULL;
+	const char *exec_path = git_exec_path();
+
+	if (!help_cmd) {
+		printf("usage: %s\n\n", git_usage_string);
+		list_common_cmds_help();
+		exit(1);
+	}
+
+	else if (!strcmp(help_cmd, "--all") || !strcmp(help_cmd, "-a")) {
+		printf("usage: %s\n\n", git_usage_string);
+		if(exec_path)
+			list_commands(exec_path, "git-*");
+		exit(1);
+	}
+
 	else
 		show_man_page(help_cmd);
+
 	return 0;
 }
 
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 7fdd2fa..5085018 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -250,7 +250,7 @@
  * On the other hand, it might just make lookup slower and messier. You
  * be the judge.  The default case is to have one DB per managed directory.
  */
-int cmd_init_db(int argc, const char **argv, char **envp)
+int cmd_init_db(int argc, const char **argv, const char *prefix)
 {
 	const char *git_dir;
 	const char *sha1_dir;
@@ -267,7 +267,7 @@
 		else if (!strncmp(arg, "--shared=", 9))
 			shared_repository = git_config_perm("arg", arg+9);
 		else
-			die(init_db_usage);
+			usage(init_db_usage);
 	}
 
 	/*
diff --git a/builtin-log.c b/builtin-log.c
index f9515a8..691cf3a 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -10,32 +10,30 @@
 #include "revision.h"
 #include "log-tree.h"
 #include "builtin.h"
+#include <time.h>
+#include <sys/time.h>
 
 /* this is in builtin-diff.c */
 void add_head(struct rev_info *revs);
 
-static int cmd_log_wc(int argc, const char **argv, char **envp,
+static void cmd_log_init(int argc, const char **argv, const char *prefix,
 		      struct rev_info *rev)
 {
-	struct commit *commit;
-
 	rev->abbrev = DEFAULT_ABBREV;
 	rev->commit_format = CMIT_FMT_DEFAULT;
 	rev->verbose_header = 1;
 	argc = setup_revisions(argc, argv, rev, "HEAD");
-	if (rev->always_show_header) {
-		if (rev->diffopt.pickaxe || rev->diffopt.filter) {
-			rev->always_show_header = 0;
-			if (rev->diffopt.output_format == DIFF_FORMAT_RAW)
-				rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
-		}
-	}
-
+	if (rev->diffopt.pickaxe || rev->diffopt.filter)
+		rev->always_show_header = 0;
 	if (argc > 1)
 		die("unrecognized argument: %s", argv[1]);
+}
+
+static int cmd_log_walk(struct rev_info *rev)
+{
+	struct commit *commit;
 
 	prepare_revision_walk(rev);
-	setup_pager();
 	while ((commit = get_revision(rev)) != NULL) {
 		log_tree_commit(rev, commit);
 		free(commit->buffer);
@@ -46,22 +44,27 @@
 	return 0;
 }
 
-int cmd_whatchanged(int argc, const char **argv, char **envp)
+int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
 
-	init_revisions(&rev);
+	git_config(git_diff_ui_config);
+	init_revisions(&rev, prefix);
 	rev.diff = 1;
 	rev.diffopt.recursive = 1;
 	rev.simplify_history = 0;
-	return cmd_log_wc(argc, argv, envp, &rev);
+	cmd_log_init(argc, argv, prefix, &rev);
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_RAW;
+	return cmd_log_walk(&rev);
 }
 
-int cmd_show(int argc, const char **argv, char **envp)
+int cmd_show(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
 
-	init_revisions(&rev);
+	git_config(git_diff_ui_config);
+	init_revisions(&rev, prefix);
 	rev.diff = 1;
 	rev.diffopt.recursive = 1;
 	rev.combine_merges = 1;
@@ -69,17 +72,19 @@
 	rev.always_show_header = 1;
 	rev.ignore_merges = 0;
 	rev.no_walk = 1;
-	return cmd_log_wc(argc, argv, envp, &rev);
+	cmd_log_init(argc, argv, prefix, &rev);
+	return cmd_log_walk(&rev);
 }
 
-int cmd_log(int argc, const char **argv, char **envp)
+int cmd_log(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info rev;
 
-	init_revisions(&rev);
+	git_config(git_diff_ui_config);
+	init_revisions(&rev, prefix);
 	rev.always_show_header = 1;
-	rev.diffopt.recursive = 1;
-	return cmd_log_wc(argc, argv, envp, &rev);
+	cmd_log_init(argc, argv, prefix, &rev);
+	return cmd_log_walk(&rev);
 }
 
 static int istitlechar(char c)
@@ -101,7 +106,10 @@
 		strcat(extra_headers, value);
 		return 0;
 	}
-	return git_default_config(var, value);
+	if (!strcmp(var, "diff.color")) {
+		return 0;
+	}
+	return git_diff_ui_config(var, value);
 }
 
 
@@ -169,7 +177,7 @@
 	return diff_flush_patch_id(options, sha1);
 }
 
-static void get_patch_ids(struct rev_info *rev, struct diff_options *options)
+static void get_patch_ids(struct rev_info *rev, struct diff_options *options, const char *prefix)
 {
 	struct rev_info check_rev;
 	struct commit *commit;
@@ -194,7 +202,7 @@
 		die("diff_setup_done failed");
 
 	/* given a range a..b get all patch ids for b..a */
-	init_revisions(&check_rev);
+	init_revisions(&check_rev, prefix);
 	o1->flags ^= UNINTERESTING;
 	o2->flags ^= UNINTERESTING;
 	add_pending_object(&check_rev, o1, "o1");
@@ -219,7 +227,19 @@
 	o2->flags = flags2;
 }
 
-int cmd_format_patch(int argc, const char **argv, char **envp)
+static void gen_message_id(char *dest, unsigned int length, char *base)
+{
+	const char *committer = git_committer_info(1);
+	const char *email_start = strrchr(committer, '<');
+	const char *email_end = strrchr(committer, '>');
+	if(!email_start || !email_end || email_start > email_end - 1)
+		die("Could not extract email from committer identity.");
+	snprintf(dest, length, "%s.%lu.git.%.*s", base,
+		 (unsigned long) time(NULL),
+		 (int)(email_end - email_start - 1), email_start + 1);
+}
+
+int cmd_format_patch(int argc, const char **argv, const char *prefix)
 {
 	struct commit *commit;
 	struct commit **list = NULL;
@@ -230,20 +250,24 @@
 	int start_number = -1;
 	int keep_subject = 0;
 	int ignore_if_in_upstream = 0;
+	int thread = 0;
+	const char *in_reply_to = NULL;
 	struct diff_options patch_id_opts;
 	char *add_signoff = NULL;
+	char message_id[1024];
+	char ref_message_id[1024];
 
-	init_revisions(&rev);
+	setup_ident();
+	git_config(git_format_config);
+	init_revisions(&rev, prefix);
 	rev.commit_format = CMIT_FMT_EMAIL;
 	rev.verbose_header = 1;
 	rev.diff = 1;
-	rev.diffopt.with_raw = 0;
-	rev.diffopt.with_stat = 1;
 	rev.combine_merges = 0;
 	rev.ignore_merges = 1;
-	rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+	rev.diffopt.msg_sep = "";
+	rev.diffopt.recursive = 1;
 
-	git_config(git_format_config);
 	rev.extra_headers = extra_headers;
 
 	/*
@@ -283,7 +307,6 @@
 			 !strcmp(argv[i], "-s")) {
 			const char *committer;
 			const char *endpos;
-			setup_ident();
 			committer = git_committer_info(1);
 			endpos = strchr(committer, '>');
 			if (!endpos)
@@ -298,6 +321,16 @@
 			rev.mime_boundary = argv[i] + 9;
 		else if (!strcmp(argv[i], "--ignore-if-in-upstream"))
 			ignore_if_in_upstream = 1;
+		else if (!strcmp(argv[i], "--thread"))
+			thread = 1;
+		else if (!strncmp(argv[i], "--in-reply-to=", 14))
+			in_reply_to = argv[i] + 14;
+		else if (!strcmp(argv[i], "--in-reply-to")) {
+			i++;
+			if (i == argc)
+				die("Need a Message-Id for --in-reply-to");
+			in_reply_to = argv[i];
+		}
 		else
 			argv[j++] = argv[i];
 	}
@@ -312,6 +345,9 @@
 	if (argc > 1)
 		die ("unrecognized argument: %s", argv[1]);
 
+	if (!rev.diffopt.output_format)
+		rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
+
 	if (output_directory) {
 		if (use_stdout)
 			die("standard output, or directory, which one?");
@@ -326,7 +362,7 @@
 	}
 
 	if (ignore_if_in_upstream)
-		get_patch_ids(&rev, &patch_id_opts);
+		get_patch_ids(&rev, &patch_id_opts, prefix);
 
 	if (!use_stdout)
 		realstdout = fdopen(dup(1), "w");
@@ -352,10 +388,23 @@
 	if (numbered)
 		rev.total = total + start_number - 1;
 	rev.add_signoff = add_signoff;
+	rev.ref_message_id = in_reply_to;
 	while (0 <= --nr) {
 		int shown;
 		commit = list[nr];
 		rev.nr = total - nr + (start_number - 1);
+		/* Make the second and subsequent mails replies to the first */
+		if (thread) {
+			if (nr == (total - 2)) {
+				strncpy(ref_message_id, message_id,
+					sizeof(ref_message_id));
+				ref_message_id[sizeof(ref_message_id)-1]='\0';
+				rev.ref_message_id = ref_message_id;
+			}
+			gen_message_id(message_id, sizeof(message_id),
+				       sha1_to_hex(commit->object.sha1));
+			rev.message_id = message_id;
+		}
 		if (!use_stdout)
 			reopen_stdout(commit, rev.nr, keep_subject);
 		shown = log_tree_commit(&rev, commit);
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 8dae9f7..11386c4 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -24,7 +24,6 @@
 static int line_terminator = '\n';
 
 static int prefix_len = 0, prefix_offset = 0;
-static const char *prefix = NULL;
 static const char **pathspec = NULL;
 static int error_unmatch = 0;
 static char *ps_matched = NULL;
@@ -207,7 +206,7 @@
 	}
 }
 
-static void show_files(struct dir_struct *dir)
+static void show_files(struct dir_struct *dir, const char *prefix)
 {
 	int i;
 
@@ -253,7 +252,7 @@
 /*
  * Prune the index to only contain stuff starting with "prefix"
  */
-static void prune_cache(void)
+static void prune_cache(const char *prefix)
 {
 	int pos = cache_name_pos(prefix, prefix_len);
 	unsigned int first, last;
@@ -276,7 +275,7 @@
 	active_nr = last;
 }
 
-static void verify_pathspec(void)
+static const char *verify_pathspec(const char *prefix)
 {
 	const char **p, *n, *prev;
 	char *real_prefix;
@@ -313,7 +312,7 @@
 		memcpy(real_prefix, prev, max);
 		real_prefix[max] = 0;
 	}
-	prefix = real_prefix;
+	return real_prefix;
 }
 
 static const char ls_files_usage[] =
@@ -322,14 +321,13 @@
 	"[ --exclude-per-directory=<filename> ] [--full-name] [--abbrev] "
 	"[--] [<file>]*";
 
-int cmd_ls_files(int argc, const char **argv, char** envp)
+int cmd_ls_files(int argc, const char **argv, const char *prefix)
 {
 	int i;
 	int exc_given = 0;
 	struct dir_struct dir;
 
 	memset(&dir, 0, sizeof(dir));
-	prefix = setup_git_directory();
 	if (prefix)
 		prefix_offset = strlen(prefix);
 	git_config(git_default_config);
@@ -454,7 +452,7 @@
 
 	/* Verify that the pathspec matches the prefix */
 	if (pathspec)
-		verify_pathspec();
+		prefix = verify_pathspec(prefix);
 
 	/* Treat unmatching pathspec elements as errors */
 	if (pathspec && error_unmatch) {
@@ -477,8 +475,8 @@
 
 	read_cache();
 	if (prefix)
-		prune_cache();
-	show_files(&dir);
+		prune_cache(prefix);
+	show_files(&dir, prefix);
 
 	if (ps_matched) {
 		/* We need to make sure all pathspec matched otherwise
diff --git a/builtin-ls-tree.c b/builtin-ls-tree.c
index b8d0d88..261147f 100644
--- a/builtin-ls-tree.c
+++ b/builtin-ls-tree.c
@@ -18,7 +18,7 @@
 static int ls_options = 0;
 static const char **pathspec;
 static int chomp_prefix = 0;
-static const char *prefix;
+static const char *ls_tree_prefix;
 
 static const char ls_tree_usage[] =
 	"git-ls-tree [-d] [-r] [-t] [-z] [--name-only] [--name-status] [--full-name] [--abbrev[=<n>]] <tree-ish> [path...]";
@@ -71,7 +71,7 @@
 		return 0;
 
 	if (chomp_prefix &&
-	    (baselen < chomp_prefix || memcmp(prefix, base, chomp_prefix)))
+	    (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))
 		return 0;
 
 	if (!(ls_options & LS_NAME_ONLY))
@@ -85,13 +85,13 @@
 	return retval;
 }
 
-int cmd_ls_tree(int argc, const char **argv, char **envp)
+int cmd_ls_tree(int argc, const char **argv, const char *prefix)
 {
 	unsigned char sha1[20];
 	struct tree *tree;
 
-	prefix = setup_git_directory();
 	git_config(git_default_config);
+	ls_tree_prefix = prefix;
 	if (prefix && *prefix)
 		chomp_prefix = strlen(prefix);
 	while (1 < argc && argv[1][0] == '-') {
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 3e40747..24a4fc6 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -348,7 +348,7 @@
 	}
 }
 
-static void decode_header_bq(char *it);
+static void decode_header(char *it);
 typedef int (*header_fn_t)(char *);
 struct header_def {
 	const char *name;
@@ -371,7 +371,7 @@
 			/* Unwrap inline B and Q encoding, and optionally
 			 * normalize the meta information to utf8.
 			 */
-			decode_header_bq(line + len + 2);
+			decode_header(line + len + 2);
 			header[i].func(line + len + 2);
 			break;
 		}
@@ -446,7 +446,7 @@
 			break;
 	}
 	/* Count mbox From headers as headers */
-	if (!ofs && !memcmp(line, "From ", 5))
+	if (!ofs && (!memcmp(line, "From ", 5) || !memcmp(line, ">From ", 6)))
 		ofs = 1;
 	return ofs;
 }
@@ -566,16 +566,19 @@
 #endif
 }
 
-static void decode_header_bq(char *it)
+static int decode_header_bq(char *it)
 {
 	char *in, *out, *ep, *cp, *sp;
 	char outbuf[1000];
+	int rfc2047 = 0;
 
 	in = it;
 	out = outbuf;
 	while ((ep = strstr(in, "=?")) != NULL) {
 		int sz, encoding;
 		char charset_q[256], piecebuf[256];
+		rfc2047 = 1;
+
 		if (in != ep) {
 			sz = ep - in;
 			memcpy(out, in, sz);
@@ -589,19 +592,19 @@
 		ep += 2;
 		cp = strchr(ep, '?');
 		if (!cp)
-			return; /* no munging */
+			return rfc2047; /* no munging */
 		for (sp = ep; sp < cp; sp++)
 			charset_q[sp - ep] = tolower(*sp);
 		charset_q[cp - ep] = 0;
 		encoding = cp[1];
 		if (!encoding || cp[2] != '?')
-			return; /* no munging */
+			return rfc2047; /* no munging */
 		ep = strstr(cp + 3, "?=");
 		if (!ep)
-			return; /* no munging */
+			return rfc2047; /* no munging */
 		switch (tolower(encoding)) {
 		default:
-			return; /* no munging */
+			return rfc2047; /* no munging */
 		case 'b':
 			sz = decode_b_segment(cp + 3, piecebuf, ep);
 			break;
@@ -610,7 +613,7 @@
 			break;
 		}
 		if (sz < 0)
-			return;
+			return rfc2047;
 		if (metainfo_charset)
 			convert_to_utf8(piecebuf, charset_q);
 		strcpy(out, piecebuf);
@@ -619,6 +622,19 @@
 	}
 	strcpy(out, in);
 	strcpy(it, outbuf);
+	return rfc2047;
+}
+
+static void decode_header(char *it)
+{
+
+	if (decode_header_bq(it))
+		return;
+	/* otherwise "it" is a straight copy of the input.
+	 * This can be binary guck but there is no charset specified.
+	 */
+	if (metainfo_charset)
+		convert_to_utf8(it, "");
 }
 
 static void decode_transfer_encoding(char *line)
@@ -820,7 +836,7 @@
 static const char mailinfo_usage[] =
 	"git-mailinfo [-k] [-u | --encoding=<encoding>] msg patch <mail >info";
 
-int cmd_mailinfo(int argc, const char **argv, char **envp)
+int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
 	/* NEEDSWORK: might want to do the optional .git/ directory
 	 * discovery
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index e2a0058..91a699d 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -138,7 +138,7 @@
 	free(name);
 	return ret;
 }
-int cmd_mailsplit(int argc, const char **argv, char **envp)
+int cmd_mailsplit(int argc, const char **argv, const char *prefix)
 {
 	int nr = 0, nr_prec = 4, ret;
 	int allow_bare = 0;
diff --git a/builtin-mv.c b/builtin-mv.c
new file mode 100644
index 0000000..a731f8d
--- /dev/null
+++ b/builtin-mv.c
@@ -0,0 +1,295 @@
+/*
+ * "git mv" builtin command
+ *
+ * Copyright (C) 2006 Johannes Schindelin
+ */
+#include <fnmatch.h>
+
+#include "cache.h"
+#include "builtin.h"
+#include "dir.h"
+#include "cache-tree.h"
+#include "path-list.h"
+
+static const char builtin_mv_usage[] =
+"git-mv [-n] [-f] (<source> <destination> | [-k] <source>... <destination>)";
+
+static const char **copy_pathspec(const char *prefix, const char **pathspec,
+				  int count, int base_name)
+{
+	const char **result = xmalloc((count + 1) * sizeof(const char *));
+	memcpy(result, pathspec, count * sizeof(const char *));
+	result[count] = NULL;
+	if (base_name) {
+		int i;
+		for (i = 0; i < count; i++) {
+			const char *last_slash = strrchr(result[i], '/');
+			if (last_slash)
+				result[i] = last_slash + 1;
+		}
+	}
+	return get_pathspec(prefix, result);
+}
+
+static void show_list(const char *label, struct path_list *list)
+{
+	if (list->nr > 0) {
+		int i;
+		printf("%s", label);
+		for (i = 0; i < list->nr; i++)
+			printf("%s%s", i > 0 ? ", " : "", list->items[i].path);
+		putchar('\n');
+	}
+}
+
+static const char *add_slash(const char *path)
+{
+	int len = strlen(path);
+	if (path[len - 1] != '/') {
+		char *with_slash = xmalloc(len + 2);
+		memcpy(with_slash, path, len);
+		with_slash[len++] = '/';
+		with_slash[len] = 0;
+		return with_slash;
+	}
+	return path;
+}
+
+static struct lock_file lock_file;
+
+int cmd_mv(int argc, const char **argv, const char *prefix)
+{
+	int i, newfd, count;
+	int verbose = 0, show_only = 0, force = 0, ignore_errors = 0;
+	const char **source, **destination, **dest_path;
+	enum update_mode { BOTH = 0, WORKING_DIRECTORY, INDEX } *modes;
+	struct stat st;
+	struct path_list overwritten = {NULL, 0, 0, 0};
+	struct path_list src_for_dst = {NULL, 0, 0, 0};
+	struct path_list added = {NULL, 0, 0, 0};
+	struct path_list deleted = {NULL, 0, 0, 0};
+	struct path_list changed = {NULL, 0, 0, 0};
+
+	git_config(git_default_config);
+
+	newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
+	if (read_cache() < 0)
+		die("index file corrupt");
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+
+		if (arg[0] != '-')
+			break;
+		if (!strcmp(arg, "--")) {
+			i++;
+			break;
+		}
+		if (!strcmp(arg, "-n")) {
+			show_only = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-f")) {
+			force = 1;
+			continue;
+		}
+		if (!strcmp(arg, "-k")) {
+			ignore_errors = 1;
+			continue;
+		}
+		usage(builtin_mv_usage);
+	}
+	count = argc - i - 1;
+	if (count < 1)
+		usage(builtin_mv_usage);
+
+	source = copy_pathspec(prefix, argv + i, count, 0);
+	modes = xcalloc(count, sizeof(enum update_mode));
+	dest_path = copy_pathspec(prefix, argv + argc - 1, 1, 0);
+
+	if (!lstat(dest_path[0], &st) &&
+			S_ISDIR(st.st_mode)) {
+		dest_path[0] = add_slash(dest_path[0]);
+		destination = copy_pathspec(dest_path[0], argv + i, count, 1);
+	} else {
+		if (count != 1)
+			usage(builtin_mv_usage);
+		destination = dest_path;
+	}
+
+	/* Checking */
+	for (i = 0; i < count; i++) {
+		const char *bad = NULL;
+
+		if (show_only)
+			printf("Checking rename of '%s' to '%s'\n",
+				source[i], destination[i]);
+
+		if (lstat(source[i], &st) < 0)
+			bad = "bad source";
+
+		if (S_ISDIR(st.st_mode)) {
+			const char *dir = source[i], *dest_dir = destination[i];
+			int first, last, len = strlen(dir);
+
+			if (lstat(dest_dir, &st) == 0) {
+				bad = "cannot move directory over file";
+				goto next;
+			}
+
+			modes[i] = WORKING_DIRECTORY;
+
+			first = cache_name_pos(source[i], len);
+			if (first >= 0)
+				die ("Huh? %s/ is in index?", dir);
+
+			first = -1 - first;
+			for (last = first; last < active_nr; last++) {
+				const char *path = active_cache[last]->name;
+				if (strncmp(path, dir, len) || path[len] != '/')
+					break;
+			}
+
+			if (last - first < 1)
+				bad = "source directory is empty";
+			else if (!bad) {
+				int j, dst_len = strlen(dest_dir);
+
+				if (last - first > 0) {
+					source = realloc(source,
+							(count + last - first)
+							* sizeof(char *));
+					destination = realloc(destination,
+							(count + last - first)
+							* sizeof(char *));
+					modes = realloc(modes,
+							(count + last - first)
+							* sizeof(enum update_mode));
+				}
+
+				dest_dir = add_slash(dest_dir);
+
+				for (j = 0; j < last - first; j++) {
+					const char *path =
+						active_cache[first + j]->name;
+					source[count + j] = path;
+					destination[count + j] =
+						prefix_path(dest_dir, dst_len,
+							path + len);
+					modes[count + j] = INDEX;
+				}
+				count += last - first;
+			}
+
+			goto next;
+		}
+
+		if (!bad && lstat(destination[i], &st) == 0) {
+			bad = "destination exists";
+			if (force) {
+				/*
+				 * only files can overwrite each other:
+				 * check both source and destination
+				 */
+				if (S_ISREG(st.st_mode)) {
+					fprintf(stderr, "Warning: %s;"
+							" will overwrite!\n",
+							bad);
+					bad = NULL;
+					path_list_insert(destination[i],
+							&overwritten);
+				} else
+					bad = "Cannot overwrite";
+			}
+		}
+
+		if (!bad &&
+		    !strncmp(destination[i], source[i], strlen(source[i])))
+			bad = "can not move directory into itself";
+
+		if (!bad && cache_name_pos(source[i], strlen(source[i])) < 0)
+			bad = "not under version control";
+
+		if (!bad) {
+			if (path_list_has_path(&src_for_dst, destination[i]))
+				bad = "multiple sources for the same target";
+			else
+				path_list_insert(destination[i], &src_for_dst);
+		}
+
+next:
+		if (bad) {
+			if (ignore_errors) {
+				if (--count > 0) {
+					memmove(source + i, source + i + 1,
+						(count - i) * sizeof(char *));
+					memmove(destination + i,
+						destination + i + 1,
+						(count - i) * sizeof(char *));
+				}
+			} else
+				die ("%s, source=%s, destination=%s",
+				     bad, source[i], destination[i]);
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		if (show_only || verbose)
+			printf("Renaming %s to %s\n",
+			       source[i], destination[i]);
+		if (!show_only && modes[i] != INDEX &&
+		    rename(source[i], destination[i]) < 0 &&
+		    !ignore_errors)
+			die ("renaming %s failed: %s",
+			     source[i], strerror(errno));
+
+		if (modes[i] == WORKING_DIRECTORY)
+			continue;
+
+		if (cache_name_pos(source[i], strlen(source[i])) >= 0) {
+			path_list_insert(source[i], &deleted);
+
+			/* destination can be a directory with 1 file inside */
+			if (path_list_has_path(&overwritten, destination[i]))
+				path_list_insert(destination[i], &changed);
+			else
+				path_list_insert(destination[i], &added);
+		} else
+			path_list_insert(destination[i], &added);
+	}
+
+        if (show_only) {
+		show_list("Changed  : ", &changed);
+		show_list("Adding   : ", &added);
+		show_list("Deleting : ", &deleted);
+	} else {
+		for (i = 0; i < changed.nr; i++) {
+			const char *path = changed.items[i].path;
+			int i = cache_name_pos(path, strlen(path));
+			struct cache_entry *ce = active_cache[i];
+
+			if (i < 0)
+				die ("Huh? Cache entry for %s unknown?", path);
+			refresh_cache_entry(ce, 0);
+		}
+
+		for (i = 0; i < added.nr; i++) {
+			const char *path = added.items[i].path;
+			add_file_to_index(path, verbose);
+		}
+
+		for (i = 0; i < deleted.nr; i++) {
+			const char *path = deleted.items[i].path;
+			remove_file_from_cache(path);
+		}
+
+		if (active_cache_changed) {
+			if (write_cache(newfd, active_cache, active_nr) ||
+			    close(newfd) ||
+			    commit_lock_file(&lock_file))
+				die("Unable to write new index file");
+		}
+	}
+
+	return 0;
+}
diff --git a/prune-packed.c b/builtin-prune-packed.c
similarity index 94%
rename from prune-packed.c
rename to builtin-prune-packed.c
index d24b097..d3dd94d 100644
--- a/prune-packed.c
+++ b/builtin-prune-packed.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
 #include "cache.h"
 
 static const char prune_packed_usage[] =
@@ -54,12 +55,10 @@
 	}
 }
 
-int main(int argc, char **argv)
+int cmd_prune_packed(int argc, const char **argv, const char *prefix)
 {
 	int i;
 
-	setup_git_directory();
-
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
 
diff --git a/builtin-prune.c b/builtin-prune.c
new file mode 100644
index 0000000..89ec7f1
--- /dev/null
+++ b/builtin-prune.c
@@ -0,0 +1,259 @@
+#include "cache.h"
+#include "refs.h"
+#include "tag.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tree-walk.h"
+#include "diff.h"
+#include "revision.h"
+#include "builtin.h"
+#include "cache-tree.h"
+
+static const char prune_usage[] = "git-prune [-n]";
+static int show_only = 0;
+static struct rev_info revs;
+
+static int prune_object(char *path, const char *filename, const unsigned char *sha1)
+{
+	if (show_only) {
+		printf("would prune %s/%s\n", path, filename);
+		return 0;
+	}
+	unlink(mkpath("%s/%s", path, filename));
+	rmdir(path);
+	return 0;
+}
+
+static int prune_dir(int i, char *path)
+{
+	DIR *dir = opendir(path);
+	struct dirent *de;
+
+	if (!dir)
+		return 0;
+
+	while ((de = readdir(dir)) != NULL) {
+		char name[100];
+		unsigned char sha1[20];
+		int len = strlen(de->d_name);
+
+		switch (len) {
+		case 2:
+			if (de->d_name[1] != '.')
+				break;
+		case 1:
+			if (de->d_name[0] != '.')
+				break;
+			continue;
+		case 38:
+			sprintf(name, "%02x", i);
+			memcpy(name+2, de->d_name, len+1);
+			if (get_sha1_hex(name, sha1) < 0)
+				break;
+
+			/*
+			 * Do we know about this object?
+			 * It must have been reachable
+			 */
+			if (lookup_object(sha1))
+				continue;
+
+			prune_object(path, de->d_name, sha1);
+			continue;
+		}
+		fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
+	}
+	closedir(dir);
+	return 0;
+}
+
+static void prune_object_dir(const char *path)
+{
+	int i;
+	for (i = 0; i < 256; i++) {
+		static char dir[4096];
+		sprintf(dir, "%s/%02x", path, i);
+		prune_dir(i, dir);
+	}
+}
+
+static void process_blob(struct blob *blob,
+			 struct object_array *p,
+			 struct name_path *path,
+			 const char *name)
+{
+	struct object *obj = &blob->object;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+	/* Nothing to do, really .. The blob lookup was the important part */
+}
+
+static void process_tree(struct tree *tree,
+			 struct object_array *p,
+			 struct name_path *path,
+			 const char *name)
+{
+	struct object *obj = &tree->object;
+	struct tree_desc desc;
+	struct name_entry entry;
+	struct name_path me;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+	if (parse_tree(tree) < 0)
+		die("bad tree object %s", sha1_to_hex(obj->sha1));
+	name = strdup(name);
+	add_object(obj, p, path, name);
+	me.up = path;
+	me.elem = name;
+	me.elem_len = strlen(name);
+
+	desc.buf = tree->buffer;
+	desc.size = tree->size;
+
+	while (tree_entry(&desc, &entry)) {
+		if (S_ISDIR(entry.mode))
+			process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
+		else
+			process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
+	}
+	free(tree->buffer);
+	tree->buffer = NULL;
+}
+
+static void process_tag(struct tag *tag, struct object_array *p, const char *name)
+{
+	struct object *obj = &tag->object;
+	struct name_path me;
+
+	if (obj->flags & SEEN)
+		return;
+	obj->flags |= SEEN;
+
+	me.up = NULL;
+	me.elem = "tag:/";
+	me.elem_len = 5;
+
+	if (parse_tag(tag) < 0)
+		die("bad tag object %s", sha1_to_hex(obj->sha1));
+	add_object(tag->tagged, p, NULL, name);
+}
+
+static void walk_commit_list(struct rev_info *revs)
+{
+	int i;
+	struct commit *commit;
+	struct object_array objects = { 0, 0, NULL };
+
+	/* Walk all commits, process their trees */
+	while ((commit = get_revision(revs)) != NULL)
+		process_tree(commit->tree, &objects, NULL, "");
+
+	/* Then walk all the pending objects, recursively processing them too */
+	for (i = 0; i < revs->pending.nr; i++) {
+		struct object_array_entry *pending = revs->pending.objects + i;
+		struct object *obj = pending->item;
+		const char *name = pending->name;
+		if (obj->type == OBJ_TAG) {
+			process_tag((struct tag *) obj, &objects, name);
+			continue;
+		}
+		if (obj->type == OBJ_TREE) {
+			process_tree((struct tree *)obj, &objects, NULL, name);
+			continue;
+		}
+		if (obj->type == OBJ_BLOB) {
+			process_blob((struct blob *)obj, &objects, NULL, name);
+			continue;
+		}
+		die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+	}
+}
+
+static int add_one_ref(const char *path, const unsigned char *sha1)
+{
+	struct object *object = parse_object(sha1);
+	if (!object)
+		die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
+	add_pending_object(&revs, object, "");
+	return 0;
+}
+
+static void add_one_tree(const unsigned char *sha1)
+{
+	struct tree *tree = lookup_tree(sha1);
+	add_pending_object(&revs, &tree->object, "");
+}
+
+static void add_cache_tree(struct cache_tree *it)
+{
+	int i;
+
+	if (it->entry_count >= 0)
+		add_one_tree(it->sha1);
+	for (i = 0; i < it->subtree_nr; i++)
+		add_cache_tree(it->down[i]->cache_tree);
+}
+
+static void add_cache_refs(void)
+{
+	int i;
+
+	read_cache();
+	for (i = 0; i < active_nr; i++) {
+		lookup_blob(active_cache[i]->sha1);
+		/*
+		 * We could add the blobs to the pending list, but quite
+		 * frankly, we don't care. Once we've looked them up, and
+		 * added them as objects, we've really done everything
+		 * there is to do for a blob
+		 */
+	}
+	if (active_cache_tree)
+		add_cache_tree(active_cache_tree);
+}
+
+int cmd_prune(int argc, const char **argv, const char *prefix)
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		if (!strcmp(arg, "-n")) {
+			show_only = 1;
+			continue;
+		}
+		usage(prune_usage);
+	}
+
+	/*
+	 * Set up revision parsing, and mark us as being interested
+	 * in all object types, not just commits.
+	 */
+	init_revisions(&revs, prefix);
+	revs.tag_objects = 1;
+	revs.blob_objects = 1;
+	revs.tree_objects = 1;
+
+	/* Add all external refs */
+	for_each_ref(add_one_ref);
+
+	/* Add all refs from the index file */
+	add_cache_refs();
+
+	/*
+	 * Set up the revision walk - this will move all commits
+	 * from the pending list to the commit walking list.
+	 */
+	prepare_revision_walk(&revs);
+
+	walk_commit_list(&revs);
+
+	prune_object_dir(get_object_directory());
+
+	return 0;
+}
diff --git a/builtin-push.c b/builtin-push.c
index 66b9407..53bc378 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -8,7 +8,7 @@
 
 #define MAX_URI (16)
 
-static const char push_usage[] = "git push [--all] [--tags] [--force] <repository> [<refspec>...]";
+static const char push_usage[] = "git-push [--all] [--tags] [-f | --force] <repository> [<refspec>...]";
 
 static int all = 0, tags = 0, force = 0, thin = 1;
 static const char *execute = NULL;
@@ -104,7 +104,7 @@
 			if (n < MAX_URI)
 				uri[n++] = strdup(s);
 			else
-				error("more than %d URL's specified, ignoreing the rest", MAX_URI);
+				error("more than %d URL's specified, ignoring the rest", MAX_URI);
 		}
 		else if (is_refspec && !has_explicit_refspec)
 			add_refspec(strdup(s));
@@ -270,10 +270,10 @@
 	return 0;
 }
 
-int cmd_push(int argc, const char **argv, char **envp)
+int cmd_push(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	const char *repo = "origin";	// default repository
+	const char *repo = "origin";	/* default repository */
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
@@ -291,7 +291,7 @@
 			tags = 1;
 			continue;
 		}
-		if (!strcmp(arg, "--force")) {
+		if (!strcmp(arg, "--force") || !strcmp(arg, "-f")) {
 			force = 1;
 			continue;
 		}
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 9a2099d..71a7026 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -43,10 +43,7 @@
 	const unsigned char *sha1;
 };
 
-static struct tree_entry_list df_conflict_list = {
-	.name = NULL,
-	.next = &df_conflict_list
-};
+static struct tree_entry_list df_conflict_list;
 
 typedef int (*merge_fn_t)(struct cache_entry **src);
 
@@ -333,14 +330,9 @@
 	setitimer(ITIMER_REAL, &v, NULL);
 }
 
+static struct checkout state;
 static void check_updates(struct cache_entry **src, int nr)
 {
-	static struct checkout state = {
-		.base_dir = "",
-		.force = 1,
-		.quiet = 1,
-		.refresh_cache = 1,
-	};
 	unsigned short mask = htons(CE_UPDATE);
 	unsigned last_percent = 200, cnt = 0, total = 0;
 
@@ -515,7 +507,7 @@
 	}
 
 	merge->ce_flags &= ~htons(CE_STAGEMASK);
-	add_cache_entry(merge, ADD_CACHE_OK_TO_ADD);
+	add_cache_entry(merge, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 	return 1;
 }
 
@@ -526,7 +518,7 @@
 	else
 		verify_absent(ce->name, "removed");
 	ce->ce_mode = 0;
-	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD);
+	add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 	invalidate_ce_path(ce);
 	return 1;
 }
@@ -878,18 +870,21 @@
 
 static struct lock_file lock_file;
 
-int cmd_read_tree(int argc, const char **argv, char **envp)
+int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 {
 	int i, newfd, stage = 0;
 	unsigned char sha1[20];
 	merge_fn_t fn = NULL;
 
-	setup_git_directory();
+	df_conflict_list.next = &df_conflict_list;
+	state.base_dir = "";
+	state.force = 1;
+	state.quiet = 1;
+	state.refresh_cache = 1;
+
 	git_config(git_default_config);
 
-	newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-	if (newfd < 0)
-		die("unable to create new index file");
+	newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
 
 	git_config(git_default_config);
 
@@ -1038,7 +1033,7 @@
 	}
 
 	if (write_cache(newfd, active_cache, active_nr) ||
-	    commit_lock_file(&lock_file))
+	    close(newfd) || commit_lock_file(&lock_file))
 		die("unable to write new index file");
 	return 0;
 }
diff --git a/repo-config.c b/builtin-repo-config.c
similarity index 97%
rename from repo-config.c
rename to builtin-repo-config.c
index 743f02b..c821e22 100644
--- a/repo-config.c
+++ b/builtin-repo-config.c
@@ -1,3 +1,4 @@
+#include "builtin.h"
 #include "cache.h"
 #include <regex.h>
 
@@ -128,7 +129,7 @@
 	return ret;
 }
 
-int main(int argc, const char **argv)
+int cmd_repo_config(int argc, const char **argv, const char *prefix)
 {
 	int nongit = 0;
 	setup_git_directory_gently(&nongit);
diff --git a/builtin-rev-list.c b/builtin-rev-list.c
index 63bad0e..0dee173 100644
--- a/builtin-rev-list.c
+++ b/builtin-rev-list.c
@@ -167,16 +167,16 @@
 		const char *name = pending->name;
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
-		if (obj->type == TYPE_TAG) {
+		if (obj->type == OBJ_TAG) {
 			obj->flags |= SEEN;
 			add_object_array(obj, name, &objects);
 			continue;
 		}
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			process_tree((struct tree *)obj, &objects, NULL, name);
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			process_blob((struct blob *)obj, &objects, NULL, name);
 			continue;
 		}
@@ -306,12 +306,12 @@
 	}
 }
 
-int cmd_rev_list(int argc, const char **argv, char **envp)
+int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
 	struct commit_list *list;
 	int i;
 
-	init_revisions(&revs);
+	init_revisions(&revs, prefix);
 	revs.abbrev = 0;
 	revs.commit_format = CMIT_FMT_UNSPECIFIED;
 	argc = setup_revisions(argc, argv, &revs, NULL);
diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c
index 5f5ade4..aca4a36 100644
--- a/builtin-rev-parse.c
+++ b/builtin-rev-parse.c
@@ -164,17 +164,60 @@
 	return 0;
 }
 
-int cmd_rev_parse(int argc, const char **argv, char **envp)
+static int try_difference(const char *arg)
+{
+	char *dotdot;
+	unsigned char sha1[20];
+	unsigned char end[20];
+	const char *next;
+	const char *this;
+	int symmetric;
+
+	if (!(dotdot = strstr(arg, "..")))
+		return 0;
+	next = dotdot + 2;
+	this = arg;
+	symmetric = (*next == '.');
+
+	*dotdot = 0;
+	next += symmetric;
+
+	if (!*next)
+		next = "HEAD";
+	if (dotdot == arg)
+		this = "HEAD";
+	if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
+		show_rev(NORMAL, end, next);
+		show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
+		if (symmetric) {
+			struct commit_list *exclude;
+			struct commit *a, *b;
+			a = lookup_commit_reference(sha1);
+			b = lookup_commit_reference(end);
+			exclude = get_merge_bases(a, b, 1);
+			while (exclude) {
+				struct commit_list *n = exclude->next;
+				show_rev(REVERSED,
+					 exclude->item->object.sha1,NULL);
+				free(exclude);
+				exclude = n;
+			}
+		}
+		return 1;
+	}
+	*dotdot = '.';
+	return 0;
+}
+
+int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
 	int i, as_is = 0, verify = 0;
 	unsigned char sha1[20];
-	const char *prefix = setup_git_directory();
 
 	git_config(git_default_config);
 
 	for (i = 1; i < argc; i++) {
 		const char *arg = argv[i];
-		char *dotdot;
 
 		if (as_is) {
 			if (show_file(arg) && as_is < 2)
@@ -326,23 +369,8 @@
 		}
 
 		/* Not a flag argument */
-		dotdot = strstr(arg, "..");
-		if (dotdot) {
-			unsigned char end[20];
-			const char *next = dotdot + 2;
-			const char *this = arg;
-			*dotdot = 0;
-			if (!*next)
-				next = "HEAD";
-			if (dotdot == arg)
-				this = "HEAD";
-			if (!get_sha1(this, sha1) && !get_sha1(next, end)) {
-				show_rev(NORMAL, end, next);
-				show_rev(REVERSED, sha1, this);
-				continue;
-			}
-			*dotdot = '.';
-		}
+		if (try_difference(arg))
+			continue;
 		if (!get_sha1(arg, sha1)) {
 			show_rev(NORMAL, sha1, arg);
 			continue;
diff --git a/builtin-rm.c b/builtin-rm.c
index 4d56a1f..593d867 100644
--- a/builtin-rm.c
+++ b/builtin-rm.c
@@ -43,19 +43,16 @@
 
 static struct lock_file lock_file;
 
-int cmd_rm(int argc, const char **argv, char **envp)
+int cmd_rm(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd;
 	int verbose = 0, show_only = 0, force = 0;
-	const char *prefix = setup_git_directory();
 	const char **pathspec;
 	char *seen;
 
 	git_config(git_default_config);
 
-	newfd = hold_lock_file_for_update(&lock_file, get_index_file());
-	if (newfd < 0)
-		die("unable to create new index file");
+	newfd = hold_lock_file_for_update(&lock_file, get_index_file(), 1);
 
 	if (read_cache() < 0)
 		die("index file corrupt");
@@ -81,7 +78,7 @@
 			force = 1;
 			continue;
 		}
-		die(builtin_rm_usage);
+		usage(builtin_rm_usage);
 	}
 	if (argc <= i)
 		usage(builtin_rm_usage);
@@ -90,8 +87,7 @@
 	seen = NULL;
 	for (i = 0; pathspec[i] ; i++)
 		/* nothing */;
-	seen = xmalloc(i);
-	memset(seen, 0, i);
+	seen = xcalloc(i, 1);
 
 	for (i = 0; i < active_nr; i++) {
 		struct cache_entry *ce = active_cache[i];
@@ -117,7 +113,7 @@
 		printf("rm '%s'\n", path);
 
 		if (remove_file_from_cache(path))
-			die("git rm: unable to remove %s", path);
+			die("git-rm: unable to remove %s", path);
 		cache_tree_invalidate_path(active_cache_tree, path);
 	}
 
@@ -129,7 +125,7 @@
 	 * workspace. If we fail to remove the first one, we
 	 * abort the "git rm" (but once we've successfully removed
 	 * any file at all, we'll go ahead and commit to it all:
-	 * by then we've already committed ourself and can't fail
+	 * by then we've already committed ourselves and can't fail
 	 * in the middle)
 	 */
 	if (force) {
@@ -141,13 +137,13 @@
 				continue;
 			}
 			if (!removed)
-				die("git rm: %s: %s", path, strerror(errno));
+				die("git-rm: %s: %s", path, strerror(errno));
 		}
 	}
 
 	if (active_cache_changed) {
 		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_lock_file(&lock_file))
+		    close(newfd) || commit_lock_file(&lock_file))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 09d8227..2a1b848 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -6,7 +6,7 @@
 #include "builtin.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--dense] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
+"git-show-branch [--sparse] [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [--topics] [<refs>...]";
 
 static int default_num = 0;
 static int default_alloc = 0;
@@ -89,6 +89,8 @@
 			name_parent(c, p);
 			i++;
 		}
+		else
+			break;
 		c = p;
 	}
 	return i;
@@ -172,7 +174,7 @@
 static int mark_seen(struct commit *commit, struct commit_list **seen_p)
 {
 	if (!commit->object.flags) {
-		insert_by_date(commit, seen_p);
+		commit_list_insert(commit, seen_p);
 		return 1;
 	}
 	return 0;
@@ -218,9 +220,8 @@
 	 * Postprocess to complete well-poisoning.
 	 *
 	 * At this point we have all the commits we have seen in
-	 * seen_p list (which happens to be sorted chronologically but
-	 * it does not really matter).  Mark anything that can be
-	 * reached from uninteresting commits not interesting.
+	 * seen_p list.  Mark anything that can be reached from
+	 * uninteresting commits not interesting.
 	 */
 	for (;;) {
 		int changed = 0;
@@ -549,7 +550,7 @@
 	return 0;
 }
 
-int cmd_show_branch(int ac, const char **av, char **envp)
+int cmd_show_branch(int ac, const char **av, const char *prefix)
 {
 	struct commit *rev[MAX_REVS], *commit;
 	struct commit_list *list = NULL, *seen = NULL;
@@ -572,7 +573,6 @@
 	int topics = 0;
 	int dense = 1;
 
-	setup_git_directory();
 	git_config(git_show_branch_config);
 
 	/* If nothing is specified, try the default first */
@@ -701,6 +701,8 @@
 	if (0 <= extra)
 		join_revs(&list, &seen, num_rev, extra);
 
+	sort_by_date(&seen);
+
 	if (merge_base)
 		return show_merge_base(seen, num_rev);
 
diff --git a/builtin-stripspace.c b/builtin-stripspace.c
index 2ce1264..09cc910 100644
--- a/builtin-stripspace.c
+++ b/builtin-stripspace.c
@@ -54,7 +54,7 @@
 		fputc('\n', out);
 }
 
-int cmd_stripspace(int argc, const char **argv, char **envp)
+int cmd_stripspace(int argc, const char **argv, const char *prefix)
 {
 	stripspace(stdin, stdout);
 	return 0;
diff --git a/builtin-tar-tree.c b/builtin-tar-tree.c
index f2e48aa..215892b 100644
--- a/builtin-tar-tree.c
+++ b/builtin-tar-tree.c
@@ -20,6 +20,7 @@
 static unsigned long offset;
 
 static time_t archive_time;
+static int tar_umask;
 
 /* tries hard to write, either succeeds or dies in the attempt */
 static void reliable_write(const void *data, unsigned long size)
@@ -188,13 +189,13 @@
 	} else {
 		if (S_ISDIR(mode)) {
 			*header.typeflag = TYPEFLAG_DIR;
-			mode |= 0777;
+			mode = (mode | 0777) & ~tar_umask;
 		} else if (S_ISLNK(mode)) {
 			*header.typeflag = TYPEFLAG_LNK;
 			mode |= 0777;
 		} else if (S_ISREG(mode)) {
 			*header.typeflag = TYPEFLAG_REG;
-			mode |= (mode & 0100) ? 0777 : 0666;
+			mode = (mode | ((mode & 0100) ? 0777 : 0666)) & ~tar_umask;
 		} else {
 			error("unsupported file mode: 0%o (SHA1: %s)",
 			      mode, sha1_to_hex(sha1));
@@ -293,19 +294,33 @@
 	}
 }
 
-static int generate_tar(int argc, const char **argv, char** envp)
+int git_tar_config(const char *var, const char *value)
+{
+	if (!strcmp(var, "tar.umask")) {
+		if (!strcmp(value, "user")) {
+			tar_umask = umask(0);
+			umask(tar_umask);
+		} else {
+			tar_umask = git_config_int(var, value);
+		}
+		return 0;
+	}
+	return git_default_config(var, value);
+}
+
+static int generate_tar(int argc, const char **argv, const char *prefix)
 {
 	unsigned char sha1[20], tree_sha1[20];
 	struct commit *commit;
 	struct tree_desc tree;
 	struct strbuf current_path;
+	void *buffer;
 
 	current_path.buf = xmalloc(PATH_MAX);
 	current_path.alloc = PATH_MAX;
 	current_path.len = current_path.eof = 0;
 
-	setup_git_directory();
-	git_config(git_default_config);
+	git_config(git_tar_config);
 
 	switch (argc) {
 	case 3:
@@ -327,8 +342,8 @@
 	} else
 		archive_time = time(NULL);
 
-	tree.buf = read_object_with_reference(sha1, tree_type, &tree.size,
-	                                      tree_sha1);
+	tree.buf = buffer = read_object_with_reference(sha1, tree_type,
+	                                               &tree.size, tree_sha1);
 	if (!tree.buf)
 		die("not a reference to a tag, commit or tree object: %s",
 		    sha1_to_hex(sha1));
@@ -337,6 +352,7 @@
 		write_entry(tree_sha1, &current_path, 040777, NULL, 0);
 	traverse_tree(&tree, &current_path);
 	write_trailer();
+	free(buffer);
 	free(current_path.buf);
 	return 0;
 }
@@ -387,19 +403,19 @@
 	return !!ret;
 }
 
-int cmd_tar_tree(int argc, const char **argv, char **envp)
+int cmd_tar_tree(int argc, const char **argv, const char *prefix)
 {
 	if (argc < 2)
 		usage(tar_tree_usage);
 	if (!strncmp("--remote=", argv[1], 9))
 		return remote_tar(argc, argv);
-	return generate_tar(argc, argv, envp);
+	return generate_tar(argc, argv, prefix);
 }
 
 /* ustar header + extended global header content */
 #define HEADERSIZE (2 * RECORDSIZE)
 
-int cmd_get_tar_commit_id(int argc, const char **argv, char **envp)
+int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
 {
 	char buffer[HEADERSIZE];
 	struct ustar_header *header = (struct ustar_header *)buffer;
diff --git a/builtin-update-index.c b/builtin-update-index.c
index ef50243..d2556f3 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -476,12 +476,11 @@
 	return 0;
 }
 
-int cmd_update_index(int argc, const char **argv, char **envp)
+int cmd_update_index(int argc, const char **argv, const char *prefix)
 {
 	int i, newfd, entries, has_errors = 0, line_termination = '\n';
 	int allow_options = 1;
 	int read_from_stdin = 0;
-	const char *prefix = setup_git_directory();
 	int prefix_length = prefix ? strlen(prefix) : 0;
 	char set_executable_bit = 0;
 	unsigned int refresh_flags = 0;
@@ -492,9 +491,7 @@
 	/* We can't free this memory, it becomes part of a linked list parsed atexit() */
 	lock_file = xcalloc(1, sizeof(struct lock_file));
 
-	newfd = hold_lock_file_for_update(lock_file, get_index_file());
-	if (newfd < 0)
-		die("unable to create new cachefile");
+	newfd = hold_lock_file_for_update(lock_file, get_index_file(), 1);
 
 	entries = read_cache();
 	if (entries < 0)
@@ -648,7 +645,7 @@
  finish:
 	if (active_cache_changed) {
 		if (write_cache(newfd, active_cache, active_nr) ||
-		    commit_lock_file(lock_file))
+		    close(newfd) || commit_lock_file(lock_file))
 			die("Unable to write new index file");
 	}
 
diff --git a/builtin-update-ref.c b/builtin-update-ref.c
index 00333c7..5bd7182 100644
--- a/builtin-update-ref.c
+++ b/builtin-update-ref.c
@@ -5,14 +5,14 @@
 static const char git_update_ref_usage[] =
 "git-update-ref <refname> <value> [<oldval>] [-m <reason>]";
 
-int cmd_update_ref(int argc, const char **argv, char **envp)
+int cmd_update_ref(int argc, const char **argv, const char *prefix)
 {
 	const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL;
 	struct ref_lock *lock;
 	unsigned char sha1[20], oldsha1[20];
 	int i;
 
-	setup_git_directory();
+	setup_ident();
 	git_config(git_default_config);
 
 	for (i = 1; i < argc; i++) {
diff --git a/builtin-upload-tar.c b/builtin-upload-tar.c
index d4fa7b5..7b401bb 100644
--- a/builtin-upload-tar.c
+++ b/builtin-upload-tar.c
@@ -15,7 +15,7 @@
 	return 1;
 }
 
-int cmd_upload_tar(int argc, const char **argv, char **envp)
+int cmd_upload_tar(int argc, const char **argv, const char *prefix)
 {
 	int len;
 	const char *dir = argv[1];
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 70e9b6f..ca06149 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -18,7 +18,7 @@
 	/* We can't free this memory, it becomes part of a linked list parsed atexit() */
 	struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 
-	newfd = hold_lock_file_for_update(lock_file, get_index_file());
+	newfd = hold_lock_file_for_update(lock_file, get_index_file(), 0);
 
 	entries = read_cache();
 	if (entries < 0)
@@ -35,7 +35,8 @@
 				      missing_ok, 0) < 0)
 			die("git-write-tree: error building trees");
 		if (0 <= newfd) {
-			if (!write_cache(newfd, active_cache, active_nr))
+			if (!write_cache(newfd, active_cache, active_nr)
+					&& !close(newfd))
 				commit_lock_file(lock_file);
 		}
 		/* Not being able to write is fine -- we are only interested
@@ -59,14 +60,12 @@
 	return 0;
 }
 
-int cmd_write_tree(int argc, const char **argv, char **envp)
+int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
 {
 	int missing_ok = 0, ret;
 	const char *prefix = NULL;
 	unsigned char sha1[20];
 
-	setup_git_directory();
-
 	while (1 < argc) {
 		const char *arg = argv[1];
 		if (!strcmp(arg, "--missing-ok"))
@@ -74,7 +73,7 @@
 		else if (!strncmp(arg, "--prefix=", 9))
 			prefix = arg + 9;
 		else
-			die(write_tree_usage);
+			usage(write_tree_usage);
 		argc--; argv++;
 	}
 
diff --git a/builtin.h b/builtin.h
index f12d5e6..26ebcaf 100644
--- a/builtin.h
+++ b/builtin.h
@@ -2,63 +2,63 @@
 #define BUILTIN_H
 
 #include <stdio.h>
-
-#ifndef PATH_MAX
-# define PATH_MAX 4096
-#endif
+#include <limits.h>
 
 extern const char git_version_string[];
+extern const char git_usage_string[];
 
-void cmd_usage(int show_all, const char *exec_path, const char *fmt, ...)
-#ifdef __GNUC__
-	__attribute__((__format__(__printf__, 3, 4), __noreturn__))
-#endif
-	;
+extern void help_unknown_cmd(const char *cmd);
 
-extern int cmd_help(int argc, const char **argv, char **envp);
-extern int cmd_version(int argc, const char **argv, char **envp);
+extern int cmd_help(int argc, const char **argv, const char *prefix);
+extern int cmd_version(int argc, const char **argv, const char *prefix);
 
-extern int cmd_whatchanged(int argc, const char **argv, char **envp);
-extern int cmd_show(int argc, const char **argv, char **envp);
-extern int cmd_log(int argc, const char **argv, char **envp);
-extern int cmd_diff(int argc, const char **argv, char **envp);
-extern int cmd_format_patch(int argc, const char **argv, char **envp);
-extern int cmd_count_objects(int argc, const char **argv, char **envp);
+extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);
+extern int cmd_show(int argc, const char **argv, const char *prefix);
+extern int cmd_log(int argc, const char **argv, const char *prefix);
+extern int cmd_diff(int argc, const char **argv, const char *prefix);
+extern int cmd_format_patch(int argc, const char **argv, const char *prefix);
+extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
 
-extern int cmd_push(int argc, const char **argv, char **envp);
-extern int cmd_grep(int argc, const char **argv, char **envp);
-extern int cmd_rm(int argc, const char **argv, char **envp);
-extern int cmd_add(int argc, const char **argv, char **envp);
-extern int cmd_rev_list(int argc, const char **argv, char **envp);
-extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
-extern int cmd_init_db(int argc, const char **argv, char **envp);
-extern int cmd_tar_tree(int argc, const char **argv, char **envp);
-extern int cmd_upload_tar(int argc, const char **argv, char **envp);
-extern int cmd_get_tar_commit_id(int argc, const char **argv, char **envp);
-extern int cmd_ls_files(int argc, const char **argv, char **envp);
-extern int cmd_ls_tree(int argc, const char **argv, char **envp);
-extern int cmd_read_tree(int argc, const char **argv, char **envp);
-extern int cmd_commit_tree(int argc, const char **argv, char **envp);
-extern int cmd_apply(int argc, const char **argv, char **envp);
-extern int cmd_show_branch(int argc, const char **argv, char **envp);
-extern int cmd_diff_files(int argc, const char **argv, char **envp);
-extern int cmd_diff_index(int argc, const char **argv, char **envp);
-extern int cmd_diff_stages(int argc, const char **argv, char **envp);
-extern int cmd_diff_tree(int argc, const char **argv, char **envp);
-extern int cmd_cat_file(int argc, const char **argv, char **envp);
-extern int cmd_rev_parse(int argc, const char **argv, char **envp);
-extern int cmd_update_index(int argc, const char **argv, char **envp);
-extern int cmd_update_ref(int argc, const char **argv, char **envp);
+extern int cmd_prune(int argc, const char **argv, const char *prefix);
+extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 
-extern int cmd_write_tree(int argc, const char **argv, char **envp);
+extern int cmd_push(int argc, const char **argv, const char *prefix);
+extern int cmd_grep(int argc, const char **argv, const char *prefix);
+extern int cmd_rm(int argc, const char **argv, const char *prefix);
+extern int cmd_add(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_list(int argc, const char **argv, const char *prefix);
+extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
+extern int cmd_init_db(int argc, const char **argv, const char *prefix);
+extern int cmd_tar_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_upload_tar(int argc, const char **argv, const char *prefix);
+extern int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_files(int argc, const char **argv, const char *prefix);
+extern int cmd_ls_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_apply(int argc, const char **argv, const char *prefix);
+extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_stages(int argc, const char **argv, const char *prefix);
+extern int cmd_diff_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
+extern int cmd_rev_parse(int argc, const char **argv, const char *prefix);
+extern int cmd_update_index(int argc, const char **argv, const char *prefix);
+extern int cmd_update_ref(int argc, const char **argv, const char *prefix);
+extern int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix);
+extern int cmd_mv(int argc, const char **argv, const char *prefix);
+extern int cmd_repo_config(int argc, const char **argv, const char *prefix);
+
+extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
 extern int write_tree(unsigned char *sha1, int missing_ok, const char *prefix);
 
-extern int cmd_mailsplit(int argc, const char **argv, char **envp);
+extern int cmd_mailsplit(int argc, const char **argv, const char *prefix);
 extern int split_mbox(const char **mbox, const char *dir, int allow_bare, int nr_prec, int skip);
 
-extern int cmd_mailinfo(int argc, const char **argv, char **envp);
+extern int cmd_mailinfo(int argc, const char **argv, const char *prefix);
 extern int mailinfo(FILE *in, FILE *out, int ks, const char *encoding, const char *msg, const char *patch);
 
-extern int cmd_stripspace(int argc, const char **argv, char **envp);
+extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern void stripspace(FILE *in, FILE *out);
 #endif
diff --git a/cache.h b/cache.h
index 8719939..b2ab208 100644
--- a/cache.h
+++ b/cache.h
@@ -115,6 +115,7 @@
 extern struct cache_entry **active_cache;
 extern unsigned int active_nr, active_alloc, active_cache_changed;
 extern struct cache_tree *active_cache_tree;
+extern int cache_errno;
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
@@ -142,6 +143,7 @@
 
 /* Initialize and use the cache information */
 extern int read_cache(void);
+extern int read_cache_from(const char *path);
 extern int write_cache(int newfd, struct cache_entry **cache, int entries);
 extern int verify_path(const char *path);
 extern int cache_name_pos(const char *name, int namelen);
@@ -149,8 +151,10 @@
 #define ADD_CACHE_OK_TO_REPLACE 2	/* Ok to replace file/directory */
 #define ADD_CACHE_SKIP_DFCHECK 4	/* Ok to skip DF conflict checks */
 extern int add_cache_entry(struct cache_entry *ce, int option);
+extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern int remove_cache_entry_at(int pos);
 extern int remove_file_from_cache(const char *path);
+extern int add_file_to_index(const char *path, int verbose);
 extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
 extern int ce_match_stat(struct cache_entry *ce, struct stat *st, int);
 extern int ce_modified(struct cache_entry *ce, struct stat *st, int);
@@ -171,11 +175,12 @@
 	struct lock_file *next;
 	char filename[PATH_MAX];
 };
-extern int hold_lock_file_for_update(struct lock_file *, const char *path);
+extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
 extern int commit_lock_file(struct lock_file *);
 extern void rollback_lock_file(struct lock_file *);
 
 /* Environment bits from configuration mechanism */
+extern int use_legacy_headers;
 extern int trust_executable_bit;
 extern int assume_unchanged;
 extern int prefer_symlink_refs;
@@ -183,6 +188,7 @@
 extern int warn_ambiguous_refs;
 extern int shared_repository;
 extern const char *apply_default_whitespace;
+extern int zlib_compression_level;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
@@ -218,8 +224,6 @@
 char *enter_repo(char *path, int strict);
 
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
-extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
-extern int parse_sha1_header(char *hdr, char *type, unsigned long *sizep);
 extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
 extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
 extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
@@ -321,13 +325,17 @@
 	char name[FLEX_ARRAY]; /* more */
 };
 
+#define REF_NORMAL	(1u << 0)
+#define REF_HEADS	(1u << 1)
+#define REF_TAGS	(1u << 2)
+
 extern int git_connect(int fd[2], char *url, const char *prog);
 extern int finish_connect(pid_t pid);
 extern int path_match(const char *path, int nr, char **match);
 extern int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
 		      int nr_refspec, char **refspec, int all);
 extern int get_ack(int fd, unsigned char *result_sha1);
-extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, int ignore_funny);
+extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);
 extern int server_supports(const char *feature);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1);
@@ -377,6 +385,8 @@
 
 /* pager.c */
 extern void setup_pager(void);
+extern int pager_in_use;
+extern int pager_use_color;
 
 /* base85 */
 int decode_85(char *dst, char *line, int linelen);
diff --git a/checkout-index.c b/checkout-index.c
index ea40bc2..dfb1c44 100644
--- a/checkout-index.c
+++ b/checkout-index.c
@@ -49,14 +49,7 @@
 static int to_tempfile;
 static char topath[4][MAXPATHLEN+1];
 
-static struct checkout state = {
-	.base_dir = "",
-	.base_dir_len = 0,
-	.force = 0,
-	.quiet = 0,
-	.not_new = 0,
-	.refresh_cache = 0,
-};
+static struct checkout state;
 
 static void write_tempfile_record (const char *name)
 {
@@ -177,6 +170,7 @@
 	int all = 0;
 	int read_from_stdin = 0;
 
+	state.base_dir = "";
 	prefix = setup_git_directory();
 	git_config(git_default_config);
 	prefix_length = prefix ? strlen(prefix) : 0;
@@ -212,7 +206,7 @@
 			state.refresh_cache = 1;
 			if (newfd < 0)
 				newfd = hold_lock_file_for_update
-					(&lock_file, get_index_file());
+					(&lock_file, get_index_file(), 1);
 			if (newfd < 0)
 				die("cannot open index.lock file.");
 			continue;
@@ -311,7 +305,7 @@
 
 	if (0 <= newfd &&
 	    (write_cache(newfd, active_cache, active_nr) ||
-	     commit_lock_file(&lock_file)))
+	     close(newfd) || commit_lock_file(&lock_file)))
 		die("Unable to write new index file");
 	return 0;
 }
diff --git a/combine-diff.c b/combine-diff.c
index 2254221..ba8baca 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -320,7 +320,7 @@
 	unsigned long i;
 
 	/* Two groups of interesting lines may have a short gap of
-	 * unintersting lines.  Connect such groups to give them a
+	 * uninteresting lines.  Connect such groups to give them a
 	 * bit of context.
 	 *
 	 * We first start from what the interesting() function says,
@@ -497,11 +497,17 @@
 	printf(" -%lu,%lu", l0, l1-l0);
 }
 
-static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent)
+static void dump_sline(struct sline *sline, unsigned long cnt, int num_parent,
+		       int use_color)
 {
 	unsigned long mark = (1UL<<num_parent);
 	int i;
 	unsigned long lno = 0;
+	const char *c_frag = diff_get_color(use_color, DIFF_FRAGINFO);
+	const char *c_new = diff_get_color(use_color, DIFF_FILE_NEW);
+	const char *c_old = diff_get_color(use_color, DIFF_FILE_OLD);
+	const char *c_plain = diff_get_color(use_color, DIFF_PLAIN);
+	const char *c_reset = diff_get_color(use_color, DIFF_RESET);
 
 	if (!cnt)
 		return; /* result deleted */
@@ -522,12 +528,13 @@
 		rlines = hunk_end - lno;
 		if (cnt < hunk_end)
 			rlines--; /* pointing at the last delete hunk */
+		fputs(c_frag, stdout);
 		for (i = 0; i <= num_parent; i++) putchar(combine_marker);
 		for (i = 0; i < num_parent; i++)
 			show_parent_lno(sline, lno, hunk_end, i);
 		printf(" +%lu,%lu ", lno+1, rlines);
 		for (i = 0; i <= num_parent; i++) putchar(combine_marker);
-		putchar('\n');
+		printf("%s\n", c_reset);
 		while (lno < hunk_end) {
 			struct lline *ll;
 			int j;
@@ -535,18 +542,23 @@
 			sl = &sline[lno++];
 			ll = sl->lost_head;
 			while (ll) {
+				fputs(c_old, stdout);
 				for (j = 0; j < num_parent; j++) {
 					if (ll->parent_map & (1UL<<j))
 						putchar('-');
 					else
 						putchar(' ');
 				}
-				puts(ll->line);
+				printf("%s%s\n", ll->line, c_reset);
 				ll = ll->next;
 			}
 			if (cnt < lno)
 				break;
 			p_mask = 1;
+			if (!(sl->flag & (mark-1)))
+				fputs(c_plain, stdout);
+			else
+				fputs(c_new, stdout);
 			for (j = 0; j < num_parent; j++) {
 				if (p_mask & sl->flag)
 					putchar('+');
@@ -554,7 +566,7 @@
 					putchar(' ');
 				p_mask <<= 1;
 			}
-			printf("%.*s\n", sl->len, sl->bol);
+			printf("%.*s%s\n", sl->len, sl->bol, c_reset);
 		}
 	}
 }
@@ -586,14 +598,15 @@
 	sline->p_lno[i] = sline->p_lno[j];
 }
 
-static void dump_quoted_path(const char *prefix, const char *path)
+static void dump_quoted_path(const char *prefix, const char *path,
+			     const char *c_meta, const char *c_reset)
 {
-	fputs(prefix, stdout);
+	printf("%s%s", c_meta, prefix);
 	if (quote_c_style(path, NULL, NULL, 0))
 		quote_c_style(path, NULL, stdout, 0);
 	else
 		printf("%s", path);
-	putchar('\n');
+	printf("%s\n", c_reset);
 }
 
 static int show_patch_diff(struct combine_diff_path *elem, int num_parent,
@@ -639,8 +652,7 @@
 			/* deleted file */
 			result_size = 0;
 			elem->mode = 0;
-			result = xmalloc(1);
-			result[0] = 0;
+			result = xcalloc(1, 1);
 		}
 		if (0 <= fd)
 			close(fd);
@@ -700,18 +712,22 @@
 
 	if (show_hunks || mode_differs || working_tree_file) {
 		const char *abb;
+		int use_color = opt->color_diff;
+		const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
+		const char *c_reset = diff_get_color(use_color, DIFF_RESET);
 
 		if (rev->loginfo)
-			show_log(rev, rev->loginfo, "\n");
-		dump_quoted_path(dense ? "diff --cc " : "diff --combined ", elem->path);
-		printf("index ");
+			show_log(rev, opt->msg_sep);
+		dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
+				 elem->path, c_meta, c_reset);
+		printf("%sindex ", c_meta);
 		for (i = 0; i < num_parent; i++) {
 			abb = find_unique_abbrev(elem->parent[i].sha1,
 						 abbrev);
 			printf("%s%s", i ? "," : "", abb);
 		}
 		abb = find_unique_abbrev(elem->sha1, abbrev);
-		printf("..%s\n", abb);
+		printf("..%s%s\n", abb, c_reset);
 
 		if (mode_differs) {
 			int added = !!elem->mode;
@@ -720,10 +736,11 @@
 				    DIFF_STATUS_ADDED)
 					added = 0;
 			if (added)
-				printf("new file mode %06o", elem->mode);
+				printf("%snew file mode %06o",
+				       c_meta, elem->mode);
 			else {
 				if (!elem->mode)
-					printf("deleted file ");
+					printf("%sdeleted file ", c_meta);
 				printf("mode ");
 				for (i = 0; i < num_parent; i++) {
 					printf("%s%06o", i ? "," : "",
@@ -732,11 +749,11 @@
 				if (elem->mode)
 					printf("..%06o", elem->mode);
 			}
-			putchar('\n');
+			printf("%s\n", c_reset);
 		}
-		dump_quoted_path("--- a/", elem->path);
-		dump_quoted_path("+++ b/", elem->path);
-		dump_sline(sline, cnt, num_parent);
+		dump_quoted_path("--- a/", elem->path, c_meta, c_reset);
+		dump_quoted_path("+++ b/", elem->path, c_meta, c_reset);
+		dump_sline(sline, cnt, num_parent, opt->color_diff);
 	}
 	free(result);
 
@@ -770,9 +787,9 @@
 		inter_name_termination = 0;
 
 	if (rev->loginfo)
-		show_log(rev, rev->loginfo, "\n");
+		show_log(rev, opt->msg_sep);
 
-	if (opt->output_format == DIFF_FORMAT_RAW) {
+	if (opt->output_format & DIFF_FORMAT_RAW) {
 		offset = strlen(COLONS) - num_parent;
 		if (offset < 0)
 			offset = 0;
@@ -792,8 +809,7 @@
 		printf(" %s ", diff_unique_abbrev(p->sha1, opt->abbrev));
 	}
 
-	if (opt->output_format == DIFF_FORMAT_RAW ||
-	    opt->output_format == DIFF_FORMAT_NAME_STATUS) {
+	if (opt->output_format & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS)) {
 		for (i = 0; i < num_parent; i++)
 			putchar(p->parent[i].status);
 		putchar(inter_name_termination);
@@ -819,17 +835,12 @@
 	struct diff_options *opt = &rev->diffopt;
 	if (!p->len)
 		return;
-	switch (opt->output_format) {
-	case DIFF_FORMAT_RAW:
-	case DIFF_FORMAT_NAME_STATUS:
-	case DIFF_FORMAT_NAME:
+	if (opt->output_format & (DIFF_FORMAT_RAW |
+				  DIFF_FORMAT_NAME |
+				  DIFF_FORMAT_NAME_STATUS)) {
 		show_raw_diff(p, num_parent, rev);
-		return;
-	case DIFF_FORMAT_PATCH:
+	} else if (opt->output_format & DIFF_FORMAT_PATCH) {
 		show_patch_diff(p, num_parent, dense, rev);
-		return;
-	default:
-		return;
 	}
 }
 
@@ -842,22 +853,20 @@
 	struct diff_options *opt = &rev->diffopt;
 	struct diff_options diffopts;
 	struct combine_diff_path *p, *paths = NULL;
-	int i, num_paths;
-	int do_diffstat;
+	int i, num_paths, needsep, show_log_first;
 
-	do_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||
-		       opt->with_stat);
 	diffopts = *opt;
-	diffopts.with_raw = 0;
-	diffopts.with_stat = 0;
+	diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
 	diffopts.recursive = 1;
 
+	show_log_first = !!rev->loginfo;
+	needsep = 0;
 	/* find set of paths that everybody touches */
 	for (i = 0; i < num_parent; i++) {
 		/* show stat against the first parent even
 		 * when doing combined diff.
 		 */
-		if (i == 0 && do_diffstat)
+		if (i == 0 && opt->output_format & DIFF_FORMAT_DIFFSTAT)
 			diffopts.output_format = DIFF_FORMAT_DIFFSTAT;
 		else
 			diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -865,12 +874,12 @@
 		diffcore_std(&diffopts);
 		paths = intersect_paths(paths, i, num_parent);
 
-		if (do_diffstat && rev->loginfo)
-			show_log(rev, rev->loginfo,
-				 opt->with_stat ? "---\n" : "\n");
+		if (show_log_first && i == 0) {
+			show_log(rev, opt->msg_sep);
+			if (rev->verbose_header && opt->output_format)
+				putchar(opt->line_termination);
+		}
 		diff_flush(&diffopts);
-		if (opt->with_stat)
-			putchar('\n');
 	}
 
 	/* find out surviving paths */
@@ -879,17 +888,25 @@
 			num_paths++;
 	}
 	if (num_paths) {
-		if (opt->with_raw) {
-			int saved_format = opt->output_format;
-			opt->output_format = DIFF_FORMAT_RAW;
+		if (opt->output_format & (DIFF_FORMAT_RAW |
+					  DIFF_FORMAT_NAME |
+					  DIFF_FORMAT_NAME_STATUS)) {
 			for (p = paths; p; p = p->next) {
-				show_combined_diff(p, num_parent, dense, rev);
+				if (p->len)
+					show_raw_diff(p, num_parent, rev);
 			}
-			opt->output_format = saved_format;
-			putchar(opt->line_termination);
+			needsep = 1;
 		}
-		for (p = paths; p; p = p->next) {
-			show_combined_diff(p, num_parent, dense, rev);
+		else if (opt->output_format & DIFF_FORMAT_DIFFSTAT)
+			needsep = 1;
+		if (opt->output_format & DIFF_FORMAT_PATCH) {
+			if (needsep)
+				putchar(opt->line_termination);
+			for (p = paths; p; p = p->next) {
+				if (p->len)
+					show_patch_diff(p, num_parent, dense,
+							rev);
+			}
 		}
 	}
 
diff --git a/commit.c b/commit.c
index e51ffa1..77f0ca1 100644
--- a/commit.c
+++ b/commit.c
@@ -56,7 +56,7 @@
 				   const unsigned char *sha1,
 				   int quiet)
 {
-	if (obj->type != TYPE_COMMIT) {
+	if (obj->type != OBJ_COMMIT) {
 		if (!quiet)
 			error("Object %s is a %s, not a commit",
 			      sha1_to_hex(sha1), typename(obj->type));
@@ -86,11 +86,11 @@
 	if (!obj) {
 		struct commit *ret = alloc_commit_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_COMMIT;
+		ret->object.type = OBJ_COMMIT;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_COMMIT;
+		obj->type = OBJ_COMMIT;
 	return check_commit(obj, sha1, 0);
 }
 
@@ -397,12 +397,13 @@
 {
 	struct commit_list *parents;
 
-	parents = commit->parents;
 	commit->object.flags &= ~mark;
+	parents = commit->parents;
 	while (parents) {
 		struct commit *parent = parents->item;
-		if (parent && parent->object.parsed &&
-		    (parent->object.flags & mark))
+
+		/* Have we already cleared this? */
+		if (mark & parent->object.flags)
 			clear_commit_marks(parent, mark);
 		parents = parents->next;
 	}
@@ -654,6 +655,9 @@
 			continue;
 		}
 
+		if (!subject)
+			body = 1;
+
 		if (is_empty_line(line, &linelen)) {
 			if (!body)
 				continue;
@@ -661,8 +665,6 @@
 				continue;
 			if (fmt == CMIT_FMT_SHORT)
 				break;
-		} else {
-			body = 1;
 		}
 
 		if (subject) {
@@ -701,6 +703,12 @@
 	/* Make sure there is an EOLN for the non-oneline case */
 	if (fmt != CMIT_FMT_ONELINE)
 		buf[offset++] = '\n';
+	/*
+	 * make sure there is another EOLN to separate the headers from whatever
+	 * body the caller appends if we haven't already written a body
+	 */
+	if (fmt == CMIT_FMT_EMAIL && !body)
+		buf[offset++] = '\n';
 	buf[offset] = '\0';
 	return offset;
 }
@@ -846,3 +854,147 @@
 	}
 	free(nodes);
 }
+
+/* merge-rebase stuff */
+
+/* bits #0..7 in revision.h */
+#define PARENT1		(1u<< 8)
+#define PARENT2		(1u<< 9)
+#define STALE		(1u<<10)
+#define RESULT		(1u<<11)
+
+static struct commit *interesting(struct commit_list *list)
+{
+	while (list) {
+		struct commit *commit = list->item;
+		list = list->next;
+		if (commit->object.flags & STALE)
+			continue;
+		return commit;
+	}
+	return NULL;
+}
+
+static struct commit_list *merge_bases(struct commit *one, struct commit *two)
+{
+	struct commit_list *list = NULL;
+	struct commit_list *result = NULL;
+
+	if (one == two)
+		/* We do not mark this even with RESULT so we do not
+		 * have to clean it up.
+		 */
+		return commit_list_insert(one, &result);
+
+	parse_commit(one);
+	parse_commit(two);
+
+	one->object.flags |= PARENT1;
+	two->object.flags |= PARENT2;
+	insert_by_date(one, &list);
+	insert_by_date(two, &list);
+
+	while (interesting(list)) {
+		struct commit *commit;
+		struct commit_list *parents;
+		struct commit_list *n;
+		int flags;
+
+		commit = list->item;
+		n = list->next;
+		free(list);
+		list = n;
+
+		flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
+		if (flags == (PARENT1 | PARENT2)) {
+			if (!(commit->object.flags & RESULT)) {
+				commit->object.flags |= RESULT;
+				insert_by_date(commit, &result);
+			}
+			/* Mark parents of a found merge stale */
+			flags |= STALE;
+		}
+		parents = commit->parents;
+		while (parents) {
+			struct commit *p = parents->item;
+			parents = parents->next;
+			if ((p->object.flags & flags) == flags)
+				continue;
+			parse_commit(p);
+			p->object.flags |= flags;
+			insert_by_date(p, &list);
+		}
+	}
+
+	/* Clean up the result to remove stale ones */
+	list = result; result = NULL;
+	while (list) {
+		struct commit_list *n = list->next;
+		if (!(list->item->object.flags & STALE))
+			insert_by_date(list->item, &result);
+		free(list);
+		list = n;
+	}
+	return result;
+}
+
+struct commit_list *get_merge_bases(struct commit *one,
+				    struct commit *two,
+                                    int cleanup)
+{
+	const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
+	struct commit_list *list;
+	struct commit **rslt;
+	struct commit_list *result;
+	int cnt, i, j;
+
+	result = merge_bases(one, two);
+	if (one == two)
+		return result;
+	if (!result || !result->next) {
+		if (cleanup) {
+			clear_commit_marks(one, all_flags);
+			clear_commit_marks(two, all_flags);
+		}
+		return result;
+	}
+
+	/* There are more than one */
+	cnt = 0;
+	list = result;
+	while (list) {
+		list = list->next;
+		cnt++;
+	}
+	rslt = xcalloc(cnt, sizeof(*rslt));
+	for (list = result, i = 0; list; list = list->next)
+		rslt[i++] = list->item;
+	free_commit_list(result);
+
+	clear_commit_marks(one, all_flags);
+	clear_commit_marks(two, all_flags);
+	for (i = 0; i < cnt - 1; i++) {
+		for (j = i+1; j < cnt; j++) {
+			if (!rslt[i] || !rslt[j])
+				continue;
+			result = merge_bases(rslt[i], rslt[j]);
+			clear_commit_marks(rslt[i], all_flags);
+			clear_commit_marks(rslt[j], all_flags);
+			for (list = result; list; list = list->next) {
+				if (rslt[i] == list->item)
+					rslt[i] = NULL;
+				if (rslt[j] == list->item)
+					rslt[j] = NULL;
+			}
+		}
+	}
+
+	/* Surviving ones in rslt[] are the independent results */
+	result = NULL;
+	for (i = 0; i < cnt; i++) {
+		if (rslt[i])
+			insert_by_date(rslt[i], &result);
+	}
+	free(rslt);
+	return result;
+}
diff --git a/commit.h b/commit.h
index 7c9ca3f..779ed82 100644
--- a/commit.h
+++ b/commit.h
@@ -105,4 +105,6 @@
 int register_commit_graft(struct commit_graft *, int);
 int read_graft_file(const char *graft_file);
 
+extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
+
 #endif /* COMMIT_H */
diff --git a/compat/subprocess.py b/compat/subprocess.py
index bbd26c7..6474eab 100644
--- a/compat/subprocess.py
+++ b/compat/subprocess.py
@@ -568,7 +568,7 @@
         # Windows methods
         #
         def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tupel with IO objects:
+            """Construct and return tuple with IO objects:
             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
             """
             if stdin == None and stdout == None and stderr == None:
@@ -635,7 +635,7 @@
 
 
         def _find_w9xpopen(self):
-            """Find and return absolut path to w9xpopen.exe"""
+            """Find and return absolute path to w9xpopen.exe"""
             w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)),
                                     "w9xpopen.exe")
             if not os.path.exists(w9xpopen):
@@ -812,7 +812,7 @@
         # POSIX methods
         #
         def _get_handles(self, stdin, stdout, stderr):
-            """Construct and return tupel with IO objects:
+            """Construct and return tuple with IO objects:
             p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite
             """
             p2cread, p2cwrite = None, None
diff --git a/config.c b/config.c
index ec44827..82b3562 100644
--- a/config.c
+++ b/config.c
@@ -244,9 +244,9 @@
 		return 1;
 	if (!*value)
 		return 0;
-	if (!strcasecmp(value, "true"))
+	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes"))
 		return 1;
-	if (!strcasecmp(value, "false"))
+	if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
 		return 0;
 	return git_config_int(name, value) != 0;
 }
@@ -279,6 +279,21 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "core.legacyheaders")) {
+		use_legacy_headers = git_config_bool(var, value);
+		return 0;
+	}
+
+	if (!strcmp(var, "core.compression")) {
+		int level = git_config_int(var, value);
+		if (level == -1)
+			level = Z_DEFAULT_COMPRESSION;
+		else if (level < 0 || level > Z_BEST_COMPRESSION)
+			die("bad zlib compression level %d", level);
+		zlib_compression_level = level;
+		return 0;
+	}
+
 	if (!strcmp(var, "user.name")) {
 		strlcpy(git_default_name, value, sizeof(git_default_name));
 		return 0;
@@ -294,6 +309,11 @@
 		return 0;
 	}
 
+	if (!strcmp(var, "pager.color")) {
+		pager_use_color = git_config_bool(var,value);
+		return 0;
+	}
+
 	/* Add other config variables here and to Documentation/config.txt. */
 	return 0;
 }
diff --git a/config.mak.in b/config.mak.in
new file mode 100644
index 0000000..04f508a
--- /dev/null
+++ b/config.mak.in
@@ -0,0 +1,24 @@
+# git Makefile configuration, included in main Makefile
+# @configure_input@
+
+CC = @CC@
+AR = @AR@
+TAR = @TAR@
+#INSTALL = @INSTALL@		# needs install-sh or install.sh in sources
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+#gitexecdir = @libexecdir@/git-core/
+datarootdir = @datarootdir@
+template_dir = @datadir@/git-core/templates/
+GIT_PYTHON_DIR = @datadir@/git-core/python
+
+mandir=@mandir@
+
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+export exec_prefix mandir
+export srcdir VPATH
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..a9c88c6
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,183 @@
+#                                               -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+AC_INIT([git], [1.4.2], [git@vger.kernel.org])
+
+AC_CONFIG_SRCDIR([git.c])
+
+config_file=config.mak.autogen
+config_append=config.mak.append
+config_in=config.mak.in
+
+echo "# ${config_append}.  Generated by configure." > "${config_append}"
+
+
+## Definitions of macros
+# GIT_CONF_APPEND_LINE(LINE)
+# --------------------------
+# Append LINE to file ${config_append}
+AC_DEFUN([GIT_CONF_APPEND_LINE],
+[echo "$1" >> "${config_append}"])# GIT_CONF_APPEND_LINE
+
+
+## Checks for programs.
+AC_MSG_NOTICE([CHECKS for programs])
+#
+AC_PROG_CC
+#AC_PROG_INSTALL		# needs install-sh or install.sh in sources
+AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_PROGS(TAR, [gtar tar])
+#
+# Define NO_PYTHON if you want to lose all benefits of the recursive merge.
+
+
+## Checks for libraries.
+AC_MSG_NOTICE([CHECKS for libraries])
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
+AC_CHECK_LIB([ssl], [SHA1_Init],[],
+[AC_CHECK_LIB([crypto], [SHA1_INIT],
+ [GIT_CONF_APPEND_LINE(NEEDS_SSL_WITH_CRYPTO=YesPlease)],
+ [GIT_CONF_APPEND_LINE(NO_OPENSSL=YesPlease)])])
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+AC_CHECK_LIB([curl], [curl_global_init],[],
+[GIT_CONF_APPEND_LINE(NO_CURL=YesPlease)])
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+AC_CHECK_LIB([expat], [XML_ParserCreate],[],
+[GIT_CONF_APPEND_LINE(NO_EXPAT=YesPlease)])
+#
+# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
+AC_CHECK_LIB([c], [iconv],[],
+[AC_CHECK_LIB([iconv],[iconv],
+ [GIT_CONF_APPEND_LINE(NEEDS_LIBICONV=YesPlease)],[])])
+#
+# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,
+# Patrick Mauritz).
+AC_CHECK_LIB([c], [socket],[],
+[AC_CHECK_LIB([socket],[socket],
+ [GIT_CONF_APPEND_LINE(NEEDS_SOCKET=YesPlease)],[])])
+
+
+## Checks for header files.
+
+
+## Checks for typedefs, structures, and compiler characteristics.
+AC_MSG_NOTICE([CHECKS for typedefs, structures, and compiler characteristics])
+#
+# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
+AC_CHECK_MEMBER(struct dirent.d_ino,[],
+[GIT_CONF_APPEND_LINE(NO_D_INO_IN_DIRENT=YesPlease)],
+[#include <dirent.h>])
+#
+# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
+# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
+AC_CHECK_MEMBER(struct dirent.d_type,[],
+[GIT_CONF_APPEND_LINE(NO_D_TYPE_IN_DIRENT=YesPlease)],
+[#include <dirent.h>])
+#
+# Define NO_SOCKADDR_STORAGE if your platform does not have struct
+# sockaddr_storage.
+AC_CHECK_TYPE(struct sockaddr_storage,[],
+[GIT_CONF_APPEND_LINE(NO_SOCKADDR_STORAGE=YesPlease)],
+[#include <netinet/in.h>])
+
+
+## Checks for library functions.
+## (in default C library and libraries checked by AC_CHECK_LIB)
+AC_MSG_NOTICE([CHECKS for library functions])
+#
+# Define NO_STRCASESTR if you don't have strcasestr.
+AC_CHECK_FUNC(strcasestr,[],
+[GIT_CONF_APPEND_LINE(NO_STRCASESTR=YesPlease)])
+#
+# Define NO_STRLCPY if you don't have strlcpy.
+AC_CHECK_FUNC(strlcpy,[],
+[GIT_CONF_APPEND_LINE(NO_STRLCPY=YesPlease)])
+#
+# Define NO_SETENV if you don't have setenv in the C library.
+AC_CHECK_FUNC(setenv,[],
+[GIT_CONF_APPEND_LINE(NO_SETENV=YesPlease)])
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
+#
+# Define NO_ICONV if your libc does not properly support iconv.
+
+
+## Other checks.
+# Define USE_PIC if you need the main git objects to be built with -fPIC
+# in order to build and link perl/Git.so.  x86-64 seems to need this.
+#
+# Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
+# Enable it on Windows.  By default, symrefs are still used.
+#
+# Define WITH_OWN_SUBPROCESS_PY if you want to use with python 2.3.
+#
+# Define NO_ACCURATE_DIFF if your diff program at least sometimes misses
+# a missing newline at the end of the file.
+
+
+## Site configuration
+## --with-PACKAGE[=ARG] and --without-PACKAGE
+# Define NO_SVN_TESTS if you want to skip time-consuming SVN interopability
+# tests.  These tests take up a significant amount of the total test time
+# but are not needed unless you plan to talk to SVN repos.
+#
+# Define MOZILLA_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
+# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
+# choice) has very fast version optimized for i586.
+#
+# Define PPC_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for PowerPC.
+#
+# Define ARM_SHA1 environment variable when running make to make use of
+# a bundled SHA1 routine optimized for ARM.
+#
+# Define NO_OPENSSL environment variable if you do not have OpenSSL.
+# This also implies MOZILLA_SHA1.
+#
+# Define NO_CURL if you do not have curl installed.  git-http-pull and
+# git-http-push are not built, and you cannot use http:// and https://
+# transports.
+#
+# Define CURLDIR=/foo/bar if your curl header and library files are in
+# /foo/bar/include and /foo/bar/lib directories.
+#
+# Define NO_EXPAT if you do not have expat installed.  git-http-push is
+# not built, and you cannot push using http:// and https:// transports.
+#
+# Define NO_MMAP if you want to avoid mmap.
+#
+# Define NO_PYTHON if you want to loose all benefits of the recursive merge.
+#
+## --enable-FEATURE[=ARG] and --disable-FEATURE
+# Define COLLISION_CHECK below if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# sufficient guarantee that no collisions between objects will ever happen.
+#
+# Define USE_NSEC below if you want git to care about sub-second file mtimes
+# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
+# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
+# randomly break unless your underlying filesystem supports those sub-second
+# times (my ext3 doesn't).
+#
+# Define USE_STDEV below if you want git to care about the underlying device
+# change being considered an inode change from the update-cache perspective.
+
+
+## Output files
+AC_CONFIG_FILES(["${config_file}":"${config_in}":"${config_append}"])
+AC_OUTPUT
+
+
+## Cleanup
+rm -f "${config_append}"
diff --git a/connect.c b/connect.c
index 9a87bd9..4422a0d 100644
--- a/connect.c
+++ b/connect.c
@@ -12,11 +12,40 @@
 
 static char *server_capabilities = NULL;
 
+static int check_ref(const char *name, int len, unsigned int flags)
+{
+	if (!flags)
+		return 1;
+
+	if (len > 45 || memcmp(name, "refs/", 5))
+		return 0;
+
+	/* Skip the "refs/" part */
+	name += 5;
+	len -= 5;
+
+	/* REF_NORMAL means that we don't want the magic fake tag refs */
+	if ((flags & REF_NORMAL) && check_ref_format(name) < 0)
+		return 0;
+
+	/* REF_HEADS means that we want regular branch heads */
+	if ((flags & REF_HEADS) && !memcmp(name, "heads/", 6))
+		return 1;
+
+	/* REF_TAGS means that we want tags */
+	if ((flags & REF_TAGS) && !memcmp(name, "tags/", 5))
+		return 1;
+
+	/* All type bits clear means that we are ok with anything */
+	return !(flags & ~REF_NORMAL);
+}
+
 /*
  * Read all the refs from the other end
  */
 struct ref **get_remote_heads(int in, struct ref **list,
-			      int nr_match, char **match, int ignore_funny)
+			      int nr_match, char **match,
+			      unsigned int flags)
 {
 	*list = NULL;
 	for (;;) {
@@ -43,10 +72,8 @@
 			server_capabilities = strdup(name + name_len + 1);
 		}
 
-		if (ignore_funny && 45 < len && !memcmp(name, "refs/", 5) &&
-		    check_ref_format(name + 5))
+		if (!check_ref(name, name_len, flags))
 			continue;
-
 		if (nr_match && !path_match(name, nr_match, match))
 			continue;
 		ref = xcalloc(1, sizeof(*ref) + len - 40);
diff --git a/contrib/colordiff/colordiff.perl b/contrib/colordiff/colordiff.perl
index 5789cfb..9566a76 100755
--- a/contrib/colordiff/colordiff.perl
+++ b/contrib/colordiff/colordiff.perl
@@ -110,7 +110,7 @@
     }
 }
 
-# colordiff specfic options here.  Need to pre-declare if using variables
+# colordiff specific options here.  Need to pre-declare if using variables
 GetOptions(
     "no-banner" => sub { $show_banner = 0 },
     "plain-text=s" => \&set_color,
diff --git a/contrib/emacs/Makefile b/contrib/emacs/Makefile
index d3619db..350846d 100644
--- a/contrib/emacs/Makefile
+++ b/contrib/emacs/Makefile
@@ -3,9 +3,9 @@
 EMACS = emacs
 
 ELC = git.elc vc-git.elc
-INSTALL = install
+INSTALL ?= install
 INSTALL_ELC = $(INSTALL) -m 644
-prefix = $(HOME)
+prefix ?= $(HOME)
 emacsdir = $(prefix)/share/emacs/site-lisp
 
 all: $(ELC)
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index ebd00ef..68de9be 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -55,18 +55,21 @@
 ;;;; ------------------------------------------------------------
 
 (defgroup git nil
-  "Git user interface")
+  "A user interface for the git versioning system."
+  :group 'tools)
 
 (defcustom git-committer-name nil
   "User name to use for commits.
-The default is to fall back to the repository config, then to `add-log-full-name' and then to `user-full-name'."
+The default is to fall back to the repository config,
+then to `add-log-full-name' and then to `user-full-name'."
   :group 'git
   :type '(choice (const :tag "Default" nil)
                  (string :tag "Name")))
 
 (defcustom git-committer-email nil
   "Email address to use for commits.
-The default is to fall back to the git repository config, then to `add-log-mailing-address' and then to `user-mail-address'."
+The default is to fall back to the git repository config,
+then to `add-log-mailing-address' and then to `user-mail-address'."
   :group 'git
   :type '(choice (const :tag "Default" nil)
                  (string :tag "Email")))
@@ -81,11 +84,18 @@
   :group 'git
   :type 'boolean)
 
+(defcustom git-reuse-status-buffer t
+  "Whether `git-status' should try to reuse an existing buffer
+if there is already one that displays the same directory."
+  :group 'git
+  :type 'boolean)
+
 (defcustom git-per-dir-ignore-file ".gitignore"
   "Name of the per-directory ignore file."
   :group 'git
   :type 'string)
 
+
 (defface git-status-face
   '((((class color) (background light)) (:foreground "purple")))
   "Git mode face used to highlight added and modified files."
@@ -149,7 +159,8 @@
     (apply #'call-process "git" nil buffer nil args)))
 
 (defun git-call-process-env-string (env &rest args)
-  "Wrapper for call-process that sets environment strings, and returns the process output as a string."
+  "Wrapper for call-process that sets environment strings,
+and returns the process output as a string."
   (with-temp-buffer
     (and (eq 0 (apply #' git-call-process-env t env args))
          (buffer-string))))
@@ -254,7 +265,7 @@
     (set-buffer (find-file-noselect ignore-name))
     (goto-char (point-max))
     (unless (zerop (current-column)) (insert "\n"))
-    (insert name "\n")
+    (insert "/" name "\n")
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
@@ -580,6 +591,8 @@
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
                             (with-current-buffer buffer (erase-buffer))
                             (git-set-files-state files 'uptodate)
+                            (when (file-directory-p ".git/rr-cache")
+                              (git-run-command nil nil "rerere"))
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
                             (message "Committed %s." commit))
@@ -939,6 +952,8 @@
   (let ((map (make-keymap))
         (diff-map (make-sparse-keymap)))
     (suppress-keymap map)
+    (define-key map "?"   'git-help)
+    (define-key map "h"   'git-help)
     (define-key map " "   'git-next-file)
     (define-key map "a"   'git-add-file)
     (define-key map "c"   'git-commit-file)
@@ -995,12 +1010,28 @@
   (set (make-local-variable 'list-buffers-directory) default-directory)
   (run-hooks 'git-status-mode-hook)))
 
+(defun git-find-status-buffer (dir)
+  "Find the git status buffer handling a specified directory."
+  (let ((list (buffer-list))
+        (fulldir (expand-file-name dir))
+        found)
+    (while (and list (not found))
+      (let ((buffer (car list)))
+        (with-current-buffer buffer
+          (when (and list-buffers-directory
+                     (string-equal fulldir (expand-file-name list-buffers-directory))
+                     (string-match "\\*git-status\\*$" (buffer-name buffer)))
+            (setq found buffer))))
+      (setq list (cdr list)))
+    found))
+
 (defun git-status (dir)
   "Entry point into git-status mode."
   (interactive "DSelect directory: ")
   (setq dir (git-get-top-dir dir))
   (if (file-directory-p (concat (file-name-as-directory dir) ".git"))
-      (let ((buffer (create-file-buffer (expand-file-name "*git-status*" dir))))
+      (let ((buffer (or (and git-reuse-status-buffer (git-find-status-buffer dir))
+                        (create-file-buffer (expand-file-name "*git-status*" dir)))))
         (switch-to-buffer buffer)
         (cd dir)
         (git-status-mode)
@@ -1008,5 +1039,10 @@
         (goto-char (point-min)))
     (message "%s is not a git working tree." dir)))
 
+(defun git-help ()
+  "Display help for Git mode."
+  (interactive)
+  (describe-function 'git-status-mode))
+
 (provide 'git)
 ;;; git.el ends here
diff --git a/contrib/emacs/vc-git.el b/contrib/emacs/vc-git.el
index 2453cdc..3f6ed69 100644
--- a/contrib/emacs/vc-git.el
+++ b/contrib/emacs/vc-git.el
@@ -95,16 +95,17 @@
   "Register FILE into the git version-control system."
   (vc-git--run-command file "update-index" "--add" "--"))
 
-(defun vc-git-print-log (file)
+(defun vc-git-print-log (file &optional buffer)
   (let ((name (file-relative-name file))
         (coding-system-for-read git-commits-coding-system))
-    (vc-do-command nil 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
+    (vc-do-command buffer 'async "git" name "rev-list" "--pretty" "HEAD" "--")))
 
-(defun vc-git-diff (file &optional rev1 rev2)
-  (let ((name (file-relative-name file)))
+(defun vc-git-diff (file &optional rev1 rev2 buffer)
+  (let ((name (file-relative-name file))
+        (buf (or buffer "*vc-diff*")))
     (if (and rev1 rev2)
-        (vc-do-command "*vc-diff*" 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
-      (vc-do-command "*vc-diff*" 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
+        (vc-do-command buf 0 "git" name "diff-tree" "-p" rev1 rev2 "--")
+      (vc-do-command buf 0 "git" name "diff-index" "-p" (or rev1 "HEAD") "--"))
     ; git-diff-index doesn't set exit status like diff does
     (if (vc-git-workfile-unchanged-p file) 0 1)))
 
diff --git a/contrib/git-svn/.gitignore b/contrib/git-svn/.gitignore
deleted file mode 100644
index d8d87e3..0000000
--- a/contrib/git-svn/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-git-svn
-git-svn.xml
-git-svn.html
-git-svn.1
diff --git a/contrib/git-svn/Makefile b/contrib/git-svn/Makefile
deleted file mode 100644
index 7c20946..0000000
--- a/contrib/git-svn/Makefile
+++ /dev/null
@@ -1,44 +0,0 @@
-all: git-svn
-
-prefix?=$(HOME)
-bindir=$(prefix)/bin
-mandir=$(prefix)/man
-man1=$(mandir)/man1
-INSTALL?=install
-doc_conf=../../Documentation/asciidoc.conf
--include ../../config.mak
-
-git-svn: git-svn.perl
-	cp $< $@
-	chmod +x $@
-
-install: all
-	$(INSTALL) -d -m755 $(DESTDIR)$(bindir)
-	$(INSTALL) git-svn $(DESTDIR)$(bindir)
-
-install-doc: doc
-	$(INSTALL) git-svn.1 $(DESTDIR)$(man1)
-
-doc: git-svn.1
-git-svn.1 : git-svn.xml
-	xmlto man git-svn.xml
-git-svn.xml : git-svn.txt
-	asciidoc -b docbook -d manpage \
-		-f ../../Documentation/asciidoc.conf $<
-git-svn.html : git-svn.txt
-	asciidoc -b xhtml11 -d manpage \
-		-f ../../Documentation/asciidoc.conf $<
-test: git-svn
-	cd t && for i in t????-*.sh; do $(SHELL) ./$$i $(TEST_FLAGS); done
-
-# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
-full-test:
-	$(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-	$(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
-	$(MAKE) test GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-							LC_ALL=en_US.UTF-8
-	$(MAKE) test GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
-							LC_ALL=en_US.UTF-8
-
-clean:
-	rm -f git-svn *.xml *.html *.1
diff --git a/contrib/git-svn/t/lib-git-svn.sh b/contrib/git-svn/t/lib-git-svn.sh
deleted file mode 100644
index d7f972a..0000000
--- a/contrib/git-svn/t/lib-git-svn.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-PATH=$PWD/../:$PATH
-if test -d ../../../t
-then
-    cd ../../../t
-else
-    echo "Must be run in contrib/git-svn/t" >&2
-    exit 1
-fi
-
-. ./test-lib.sh
-
-GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
-SVN_TREE=$GIT_SVN_DIR/svn-tree
-
-svnadmin >/dev/null 2>&1
-if test $? != 1
-then
-    test_expect_success 'skipping contrib/git-svn test' :
-    test_done
-    exit
-fi
-
-svn >/dev/null 2>&1
-if test $? != 1
-then
-    test_expect_success 'skipping contrib/git-svn test' :
-    test_done
-    exit
-fi
-
-svnrepo=$PWD/svnrepo
-
-set -e
-
-if svnadmin create --help | grep fs-type >/dev/null
-then
-	svnadmin create --fs-type fsfs "$svnrepo"
-else
-	svnadmin create "$svnrepo"
-fi
-
-svnrepo="file://$svnrepo/test-git-svn"
-
-
diff --git a/convert-objects.c b/convert-objects.c
index 0fabd89..168771e 100644
--- a/convert-objects.c
+++ b/convert-objects.c
@@ -1,5 +1,6 @@
 #define _XOPEN_SOURCE 500 /* glibc2 and AIX 5.3L need this */
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
+#define _GNU_SOURCE
 #include <time.h>
 #include "cache.h"
 #include "blob.h"
@@ -240,14 +241,14 @@
 {
 	char *new = xmalloc(size + 100);
 	unsigned long newlen = 0;
-	
-	// "tree <sha1>\n"
+
+	/* "tree <sha1>\n" */
 	memcpy(new + newlen, buffer, 46);
 	newlen += 46;
 	buffer = (char *) buffer + 46;
 	size -= 46;
 
-	// "parent <sha1>\n"
+	/* "parent <sha1>\n" */
 	while (!memcmp(buffer, "parent ", 7)) {
 		memcpy(new + newlen, buffer, 48);
 		newlen += 48;
@@ -255,12 +256,12 @@
 		size -= 48;
 	}
 
-	// "author xyz <xyz> date"
+	/* "author xyz <xyz> date" */
 	newlen += convert_date_line(new + newlen, &buffer, &size);
-	// "committer xyz <xyz> date"
+	/* "committer xyz <xyz> date" */
 	newlen += convert_date_line(new + newlen, &buffer, &size);
 
-	// Rest
+	/* Rest */
 	memcpy(new + newlen, buffer, size);
 	newlen += size;
 
diff --git a/csum-file.c b/csum-file.c
index ebaad03..6a7b40f 100644
--- a/csum-file.c
+++ b/csum-file.c
@@ -122,7 +122,7 @@
 	void *out;
 
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, Z_DEFAULT_COMPRESSION);
+	deflateInit(&stream, zlib_compression_level);
 	maxsize = deflateBound(&stream, size);
 	out = xmalloc(maxsize);
 
diff --git a/daemon.c b/daemon.c
index e096bd7..810837f 100644
--- a/daemon.c
+++ b/daemon.c
@@ -19,7 +19,7 @@
 "git-daemon [--verbose] [--syslog] [--inetd | --port=n] [--export-all]\n"
 "           [--timeout=n] [--init-timeout=n] [--strict-paths]\n"
 "           [--base-path=path] [--user-path | --user-path=path]\n"
-"           [--reuseaddr] [directory...]";
+"           [--reuseaddr] [--detach] [--pid-file=file] [directory...]";
 
 /* List of acceptable pathname prefixes */
 static char **ok_paths = NULL;
@@ -95,6 +95,12 @@
 	va_end(params);
 }
 
+static void NORETURN daemon_die(const char *err, va_list params)
+{
+	logreport(LOG_ERR, err, params);
+	exit(1);
+}
+
 static int avoid_alias(char *p)
 {
 	int sl, ndot;
@@ -656,6 +662,45 @@
 	}
 }
 
+/* if any standard file descriptor is missing open it to /dev/null */
+static void sanitize_stdfds(void)
+{
+	int fd = open("/dev/null", O_RDWR, 0);
+	while (fd != -1 && fd < 2)
+		fd = dup(fd);
+	if (fd == -1)
+		die("open /dev/null or dup failed: %s", strerror(errno));
+	if (fd > 2)
+		close(fd);
+}
+
+static void daemonize(void)
+{
+	switch (fork()) {
+		case 0:
+			break;
+		case -1:
+			die("fork failed: %s", strerror(errno));
+		default:
+			exit(0);
+	}
+	if (setsid() == -1)
+		die("setsid failed: %s", strerror(errno));
+	close(0);
+	close(1);
+	close(2);
+	sanitize_stdfds();
+}
+
+static void store_pid(const char *path)
+{
+	FILE *f = fopen(path, "w");
+	if (!f)
+		die("cannot open pid file %s: %s", path, strerror(errno));
+	fprintf(f, "%d\n", getpid());
+	fclose(f);
+}
+
 static int serve(int port)
 {
 	int socknum, *socklist;
@@ -671,6 +716,8 @@
 {
 	int port = DEFAULT_GIT_PORT;
 	int inetd_mode = 0;
+	const char *pid_file = NULL;
+	int detach = 0;
 	int i;
 
 	/* Without this we cannot rely on waitpid() to tell
@@ -735,6 +782,15 @@
 			user_path = arg + 12;
 			continue;
 		}
+		if (!strncmp(arg, "--pid-file=", 11)) {
+			pid_file = arg + 11;
+			continue;
+		}
+		if (!strcmp(arg, "--detach")) {
+			detach = 1;
+			log_syslog = 1;
+			continue;
+		}
 		if (!strcmp(arg, "--")) {
 			ok_paths = &argv[i+1];
 			break;
@@ -746,17 +802,14 @@
 		usage(daemon_usage);
 	}
 
-	if (log_syslog)
+	if (log_syslog) {
 		openlog("git-daemon", 0, LOG_DAEMON);
-
-	if (strict_paths && (!ok_paths || !*ok_paths)) {
-		if (!inetd_mode)
-			die("git-daemon: option --strict-paths requires a whitelist");
-
-		logerror("option --strict-paths requires a whitelist");
-		exit (1);
+		set_die_routine(daemon_die);
 	}
 
+	if (strict_paths && (!ok_paths || !*ok_paths))
+		die("option --strict-paths requires a whitelist");
+
 	if (inetd_mode) {
 		struct sockaddr_storage ss;
 		struct sockaddr *peer = (struct sockaddr *)&ss;
@@ -770,5 +823,13 @@
 		return execute(peer);
 	}
 
+	if (detach)
+		daemonize();
+	else
+		sanitize_stdfds();
+
+	if (pid_file)
+		store_pid(pid_file);
+
 	return serve(port);
 }
diff --git a/describe.c b/describe.c
index 8e68d5d..324ca89 100644
--- a/describe.c
+++ b/describe.c
@@ -67,7 +67,7 @@
 	 * Otherwise only annotated tags are used.
 	 */
 	if (!strncmp(path, "refs/tags/", 10)) {
-		if (object->type == TYPE_TAG)
+		if (object->type == OBJ_TAG)
 			prio = 2;
 		else
 			prio = 1;
diff --git a/diff-delta.c b/diff-delta.c
index 8b9172a..7da9205 100644
--- a/diff-delta.c
+++ b/diff-delta.c
@@ -148,7 +148,7 @@
 		return NULL;
 
 	/* Determine index hash size.  Note that indexing skips the
-	   first byte to allow for optimizing the rabin polynomial
+	   first byte to allow for optimizing the Rabin's polynomial
 	   initialization in create_delta(). */
 	entries = (bufsize - 1)  / RABIN_WINDOW;
 	hsize = entries / 4;
@@ -205,7 +205,7 @@
 
 	/*
 	 * Determine a limit on the number of entries in the same hash
-	 * bucket.  This guard us against patological data sets causing
+	 * bucket.  This guards us against pathological data sets causing
 	 * really bad hash distribution with most entries in the same hash
 	 * bucket that would bring us to O(m*n) computing costs (m and n
 	 * corresponding to reference and target buffer sizes).
@@ -240,7 +240,7 @@
 
 /*
  * The maximum size for any opcode sequence, including the initial header
- * plus rabin window plus biggest copy.
+ * plus Rabin window plus biggest copy.
  */
 #define MAX_OP_SIZE	(5 + 5 + 1 + RABIN_WINDOW + 7)
 
diff --git a/diff.c b/diff.c
index 5a71489..b3b1781 100644
--- a/diff.c
+++ b/diff.c
@@ -13,42 +13,19 @@
 
 static int use_size_cache;
 
+static int diff_detect_rename_default = 0;
 static int diff_rename_limit_default = -1;
 static int diff_use_color_default = 0;
 
-enum color_diff {
-	DIFF_RESET = 0,
-	DIFF_PLAIN = 1,
-	DIFF_METAINFO = 2,
-	DIFF_FRAGINFO = 3,
-	DIFF_FILE_OLD = 4,
-	DIFF_FILE_NEW = 5,
-};
-
-#define COLOR_NORMAL  ""
-#define COLOR_BOLD    "\033[1m"
-#define COLOR_DIM     "\033[2m"
-#define COLOR_UL      "\033[4m"
-#define COLOR_BLINK   "\033[5m"
-#define COLOR_REVERSE "\033[7m"
-#define COLOR_RESET   "\033[m"
-
-#define COLOR_BLACK   "\033[30m"
-#define COLOR_RED     "\033[31m"
-#define COLOR_GREEN   "\033[32m"
-#define COLOR_YELLOW  "\033[33m"
-#define COLOR_BLUE    "\033[34m"
-#define COLOR_MAGENTA "\033[35m"
-#define COLOR_CYAN    "\033[36m"
-#define COLOR_WHITE   "\033[37m"
-
-static const char *diff_colors[] = {
-	[DIFF_RESET]    = COLOR_RESET,
-	[DIFF_PLAIN]    = COLOR_NORMAL,
-	[DIFF_METAINFO] = COLOR_BOLD,
-	[DIFF_FRAGINFO] = COLOR_CYAN,
-	[DIFF_FILE_OLD] = COLOR_RED,
-	[DIFF_FILE_NEW] = COLOR_GREEN,
+/* "\033[1;38;5;2xx;48;5;2xxm\0" is 23 bytes */
+static char diff_colors[][24] = {
+	"\033[m",	/* reset */
+	"",		/* normal */
+	"\033[1m",	/* bold */
+	"\033[36m",	/* cyan */
+	"\033[31m",	/* red */
+	"\033[32m",	/* green */
+	"\033[33m"	/* yellow */
 };
 
 static int parse_diff_color_slot(const char *var, int ofs)
@@ -63,45 +40,131 @@
 		return DIFF_FILE_OLD;
 	if (!strcasecmp(var+ofs, "new"))
 		return DIFF_FILE_NEW;
+	if (!strcasecmp(var+ofs, "commit"))
+		return DIFF_COMMIT;
 	die("bad config variable '%s'", var);
 }
 
-static const char *parse_diff_color_value(const char *value, const char *var)
+static int parse_color(const char *name, int len)
 {
-	if (!strcasecmp(value, "normal"))
-		return COLOR_NORMAL;
-	if (!strcasecmp(value, "bold"))
-		return COLOR_BOLD;
-	if (!strcasecmp(value, "dim"))
-		return COLOR_DIM;
-	if (!strcasecmp(value, "ul"))
-		return COLOR_UL;
-	if (!strcasecmp(value, "blink"))
-		return COLOR_BLINK;
-	if (!strcasecmp(value, "reverse"))
-		return COLOR_REVERSE;
-	if (!strcasecmp(value, "reset"))
-		return COLOR_RESET;
-	if (!strcasecmp(value, "black"))
-		return COLOR_BLACK;
-	if (!strcasecmp(value, "red"))
-		return COLOR_RED;
-	if (!strcasecmp(value, "green"))
-		return COLOR_GREEN;
-	if (!strcasecmp(value, "yellow"))
-		return COLOR_YELLOW;
-	if (!strcasecmp(value, "blue"))
-		return COLOR_BLUE;
-	if (!strcasecmp(value, "magenta"))
-		return COLOR_MAGENTA;
-	if (!strcasecmp(value, "cyan"))
-		return COLOR_CYAN;
-	if (!strcasecmp(value, "white"))
-		return COLOR_WHITE;
+	static const char * const color_names[] = {
+		"normal", "black", "red", "green", "yellow",
+		"blue", "magenta", "cyan", "white"
+	};
+	char *end;
+	int i;
+	for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+		const char *str = color_names[i];
+		if (!strncasecmp(name, str, len) && !str[len])
+			return i - 1;
+	}
+	i = strtol(name, &end, 10);
+	if (*name && !*end && i >= -1 && i <= 255)
+		return i;
+	return -2;
+}
+
+static int parse_attr(const char *name, int len)
+{
+	static const int attr_values[] = { 1, 2, 4, 5, 7 };
+	static const char * const attr_names[] = {
+		"bold", "dim", "ul", "blink", "reverse"
+	};
+	int i;
+	for (i = 0; i < ARRAY_SIZE(attr_names); i++) {
+		const char *str = attr_names[i];
+		if (!strncasecmp(name, str, len) && !str[len])
+			return attr_values[i];
+	}
+	return -1;
+}
+
+static void parse_diff_color_value(const char *value, const char *var, char *dst)
+{
+	const char *ptr = value;
+	int attr = -1;
+	int fg = -2;
+	int bg = -2;
+
+	if (!strcasecmp(value, "reset")) {
+		strcpy(dst, "\033[m");
+		return;
+	}
+
+	/* [fg [bg]] [attr] */
+	while (*ptr) {
+		const char *word = ptr;
+		int val, len = 0;
+
+		while (word[len] && !isspace(word[len]))
+			len++;
+
+		ptr = word + len;
+		while (*ptr && isspace(*ptr))
+			ptr++;
+
+		val = parse_color(word, len);
+		if (val >= -1) {
+			if (fg == -2) {
+				fg = val;
+				continue;
+			}
+			if (bg == -2) {
+				bg = val;
+				continue;
+			}
+			goto bad;
+		}
+		val = parse_attr(word, len);
+		if (val < 0 || attr != -1)
+			goto bad;
+		attr = val;
+	}
+
+	if (attr >= 0 || fg >= 0 || bg >= 0) {
+		int sep = 0;
+
+		*dst++ = '\033';
+		*dst++ = '[';
+		if (attr >= 0) {
+			*dst++ = '0' + attr;
+			sep++;
+		}
+		if (fg >= 0) {
+			if (sep++)
+				*dst++ = ';';
+			if (fg < 8) {
+				*dst++ = '3';
+				*dst++ = '0' + fg;
+			} else {
+				dst += sprintf(dst, "38;5;%d", fg);
+			}
+		}
+		if (bg >= 0) {
+			if (sep++)
+				*dst++ = ';';
+			if (bg < 8) {
+				*dst++ = '4';
+				*dst++ = '0' + bg;
+			} else {
+				dst += sprintf(dst, "48;5;%d", bg);
+			}
+		}
+		*dst++ = 'm';
+	}
+	*dst = 0;
+	return;
+bad:
 	die("bad config value '%s' for variable '%s'", value, var);
 }
 
-int git_diff_config(const char *var, const char *value)
+/*
+ * These are to give UI layer defaults.
+ * The core-level commands such as git-diff-files should
+ * never be affected by the setting of diff.renames
+ * the user happens to have in the configuration file.
+ */
+int git_diff_ui_config(const char *var, const char *value)
 {
 	if (!strcmp(var, "diff.renamelimit")) {
 		diff_rename_limit_default = git_config_int(var, value);
@@ -110,8 +173,14 @@
 	if (!strcmp(var, "diff.color")) {
 		if (!value)
 			diff_use_color_default = 1; /* bool */
-		else if (!strcasecmp(value, "auto"))
-			diff_use_color_default = isatty(1);
+		else if (!strcasecmp(value, "auto")) {
+			diff_use_color_default = 0;
+			if (isatty(1) || (pager_in_use && pager_use_color)) {
+				char *term = getenv("TERM");
+				if (term && strcmp(term, "dumb"))
+					diff_use_color_default = 1;
+			}
+		}
 		else if (!strcasecmp(value, "never"))
 			diff_use_color_default = 0;
 		else if (!strcasecmp(value, "always"))
@@ -120,9 +189,19 @@
 			diff_use_color_default = git_config_bool(var, value);
 		return 0;
 	}
+	if (!strcmp(var, "diff.renames")) {
+		if (!value)
+			diff_detect_rename_default = DIFF_DETECT_RENAME;
+		else if (!strcasecmp(value, "copies") ||
+			 !strcasecmp(value, "copy"))
+			diff_detect_rename_default = DIFF_DETECT_COPY;
+		else if (git_config_bool(var,value))
+			diff_detect_rename_default = DIFF_DETECT_RENAME;
+		return 0;
+	}
 	if (!strncmp(var, "diff.color.", 11)) {
 		int slot = parse_diff_color_slot(var, 11);
-		diff_colors[slot] = parse_diff_color_value(value, var);
+		parse_diff_color_value(value, var, diff_colors[slot]);
 		return 0;
 	}
 	return git_default_config(var, value);
@@ -285,7 +364,7 @@
 	const char **label_path;
 };
 
-static inline const char *get_color(int diff_use_color, enum color_diff ix)
+const char *diff_get_color(int diff_use_color, enum color_diff ix)
 {
 	if (diff_use_color)
 		return diff_colors[ix];
@@ -296,8 +375,8 @@
 {
 	int i;
 	struct emit_callback *ecbdata = priv;
-	const char *set = get_color(ecbdata->color_diff, DIFF_METAINFO);
-	const char *reset = get_color(ecbdata->color_diff, DIFF_RESET);
+	const char *set = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
+	const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
 
 	if (ecbdata->label_path[0]) {
 		printf("%s--- %s%s\n", set, ecbdata->label_path[0], reset);
@@ -312,7 +391,7 @@
 		;
 	if (2 <= i && i < len && line[i] == ' ') {
 		ecbdata->nparents = i - 1;
-		set = get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+		set = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
 	}
 	else if (len < ecbdata->nparents)
 		set = reset;
@@ -325,11 +404,13 @@
 			else if (line[i] == '+')
 				color = DIFF_FILE_NEW;
 		}
-		set = get_color(ecbdata->color_diff, color);
+		set = diff_get_color(ecbdata->color_diff, color);
 	}
 	if (len > 0 && line[len-1] == '\n')
 		len--;
-	printf("%s%.*s%s\n", set, (int) len, line, reset);
+	fputs (set, stdout);
+	fwrite (line, len, 1, stdout);
+	puts (reset);
 }
 
 static char *pprint_rename(const char *a, const char *b)
@@ -583,7 +664,7 @@
 	z_stream stream;
 
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, Z_BEST_COMPRESSION);
+	deflateInit(&stream, zlib_compression_level);
 	bound = deflateBound(&stream, size);
 	deflated = xmalloc(bound);
 	stream.next_out = deflated;
@@ -680,8 +761,8 @@
 	mmfile_t mf1, mf2;
 	const char *lbl[2];
 	char *a_one, *b_two;
-	const char *set = get_color(o->color_diff, DIFF_METAINFO);
-	const char *reset = get_color(o->color_diff, DIFF_RESET);
+	const char *set = diff_get_color(o->color_diff, DIFF_METAINFO);
+	const char *reset = diff_get_color(o->color_diff, DIFF_RESET);
 
 	a_one = quote_two("a/", name_a);
 	b_two = quote_two("b/", name_b);
@@ -721,7 +802,7 @@
 	if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
 		die("unable to read files to diff");
 
-	if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+	if (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
 		/* Quite common confusing case */
 		if (mf1.size == mf2.size &&
 		    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1420,40 +1501,45 @@
 void diff_setup(struct diff_options *options)
 {
 	memset(options, 0, sizeof(*options));
-	options->output_format = DIFF_FORMAT_RAW;
 	options->line_termination = '\n';
 	options->break_opt = -1;
 	options->rename_limit = -1;
 	options->context = 3;
+	options->msg_sep = "";
 
 	options->change = diff_change;
 	options->add_remove = diff_addremove;
 	options->color_diff = diff_use_color_default;
+	options->detect_rename = diff_detect_rename_default;
 }
 
 int diff_setup_done(struct diff_options *options)
 {
-	if ((options->find_copies_harder &&
-	     options->detect_rename != DIFF_DETECT_COPY) ||
-	    (0 <= options->rename_limit && !options->detect_rename))
-		return -1;
+	if (options->find_copies_harder)
+		options->detect_rename = DIFF_DETECT_COPY;
+
+	if (options->output_format & (DIFF_FORMAT_NAME |
+				      DIFF_FORMAT_NAME_STATUS |
+				      DIFF_FORMAT_CHECKDIFF |
+				      DIFF_FORMAT_NO_OUTPUT))
+		options->output_format &= ~(DIFF_FORMAT_RAW |
+					    DIFF_FORMAT_DIFFSTAT |
+					    DIFF_FORMAT_SUMMARY |
+					    DIFF_FORMAT_PATCH);
 
 	/*
 	 * These cases always need recursive; we do not drop caller-supplied
 	 * recursive bits for other formats here.
 	 */
-	if ((options->output_format == DIFF_FORMAT_PATCH) ||
-	    (options->output_format == DIFF_FORMAT_DIFFSTAT) ||
-	    (options->output_format == DIFF_FORMAT_CHECKDIFF))
+	if (options->output_format & (DIFF_FORMAT_PATCH |
+				      DIFF_FORMAT_DIFFSTAT |
+				      DIFF_FORMAT_CHECKDIFF))
 		options->recursive = 1;
-
 	/*
-	 * These combinations do not make sense.
+	 * Also pickaxe would not work very well if you do not say recursive
 	 */
-	if (options->output_format == DIFF_FORMAT_RAW)
-		options->with_raw = 0;
-	if (options->output_format == DIFF_FORMAT_DIFFSTAT)
-		options->with_stat  = 0;
+	if (options->pickaxe)
+		options->recursive = 1;
 
 	if (options->detect_rename && options->rename_limit < 0)
 		options->rename_limit = diff_rename_limit_default;
@@ -1526,22 +1612,22 @@
 {
 	const char *arg = av[0];
 	if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
-		options->output_format = DIFF_FORMAT_PATCH;
+		options->output_format |= DIFF_FORMAT_PATCH;
 	else if (opt_arg(arg, 'U', "unified", &options->context))
-		options->output_format = DIFF_FORMAT_PATCH;
+		options->output_format |= DIFF_FORMAT_PATCH;
+	else if (!strcmp(arg, "--raw"))
+		options->output_format |= DIFF_FORMAT_RAW;
 	else if (!strcmp(arg, "--patch-with-raw")) {
-		options->output_format = DIFF_FORMAT_PATCH;
-		options->with_raw = 1;
+		options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
 	}
 	else if (!strcmp(arg, "--stat"))
-		options->output_format = DIFF_FORMAT_DIFFSTAT;
+		options->output_format |= DIFF_FORMAT_DIFFSTAT;
 	else if (!strcmp(arg, "--check"))
-		options->output_format = DIFF_FORMAT_CHECKDIFF;
+		options->output_format |= DIFF_FORMAT_CHECKDIFF;
 	else if (!strcmp(arg, "--summary"))
-		options->summary = 1;
+		options->output_format |= DIFF_FORMAT_SUMMARY;
 	else if (!strcmp(arg, "--patch-with-stat")) {
-		options->output_format = DIFF_FORMAT_PATCH;
-		options->with_stat = 1;
+		options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
 	}
 	else if (!strcmp(arg, "-z"))
 		options->line_termination = 0;
@@ -1550,19 +1636,23 @@
 	else if (!strcmp(arg, "--full-index"))
 		options->full_index = 1;
 	else if (!strcmp(arg, "--binary")) {
-		options->output_format = DIFF_FORMAT_PATCH;
+		options->output_format |= DIFF_FORMAT_PATCH;
 		options->full_index = options->binary = 1;
 	}
+	else if (!strcmp(arg, "-a") || !strcmp(arg, "--text")) {
+		options->text = 1;
+	}
 	else if (!strcmp(arg, "--name-only"))
-		options->output_format = DIFF_FORMAT_NAME;
+		options->output_format |= DIFF_FORMAT_NAME;
 	else if (!strcmp(arg, "--name-status"))
-		options->output_format = DIFF_FORMAT_NAME_STATUS;
+		options->output_format |= DIFF_FORMAT_NAME_STATUS;
 	else if (!strcmp(arg, "-R"))
 		options->reverse_diff = 1;
 	else if (!strncmp(arg, "-S", 2))
 		options->pickaxe = arg + 2;
-	else if (!strcmp(arg, "-s"))
-		options->output_format = DIFF_FORMAT_NO_OUTPUT;
+	else if (!strcmp(arg, "-s")) {
+		options->output_format |= DIFF_FORMAT_NO_OUTPUT;
+	}
 	else if (!strncmp(arg, "-O", 2))
 		options->orderfile = arg + 2;
 	else if (!strncmp(arg, "--diff-filter=", 14))
@@ -1601,10 +1691,14 @@
 	}
 	else if (!strcmp(arg, "--color"))
 		options->color_diff = 1;
+	else if (!strcmp(arg, "--no-color"))
+		options->color_diff = 0;
 	else if (!strcmp(arg, "-w") || !strcmp(arg, "--ignore-all-space"))
 		options->xdl_opts |= XDF_IGNORE_WHITESPACE;
 	else if (!strcmp(arg, "-b") || !strcmp(arg, "--ignore-space-change"))
 		options->xdl_opts |= XDF_IGNORE_WHITESPACE_CHANGE;
+	else if (!strcmp(arg, "--no-renames"))
+		options->detect_rename = 0;
 	else
 		return 0;
 	return 1;
@@ -1690,13 +1784,9 @@
 				 struct diff_filespec *one,
 				 struct diff_filespec *two)
 {
-	struct diff_filepair *dp = xmalloc(sizeof(*dp));
+	struct diff_filepair *dp = xcalloc(1, sizeof(*dp));
 	dp->one = one;
 	dp->two = two;
-	dp->score = 0;
-	dp->status = 0;
-	dp->source_stays = 0;
-	dp->broken_pair = 0;
 	if (queue)
 		diff_q(queue, dp);
 	return dp;
@@ -1737,15 +1827,17 @@
 }
 
 static void diff_flush_raw(struct diff_filepair *p,
-			   int line_termination,
-			   int inter_name_termination,
-			   struct diff_options *options,
-			   int output_format)
+			   struct diff_options *options)
 {
 	int two_paths;
 	char status[10];
 	int abbrev = options->abbrev;
 	const char *path_one, *path_two;
+	int inter_name_termination = '\t';
+	int line_termination = options->line_termination;
+
+	if (!line_termination)
+		inter_name_termination = 0;
 
 	path_one = p->one->path;
 	path_two = p->two->path;
@@ -1774,7 +1866,7 @@
 		two_paths = 0;
 		break;
 	}
-	if (output_format != DIFF_FORMAT_NAME_STATUS) {
+	if (!(options->output_format & DIFF_FORMAT_NAME_STATUS)) {
 		printf(":%06o %06o %s ",
 		       p->one->mode, p->two->mode,
 		       diff_unique_abbrev(p->one->sha1, abbrev));
@@ -1983,48 +2075,30 @@
 	diff_debug_queue("resolve-rename-copy done", q);
 }
 
-static void flush_one_pair(struct diff_filepair *p,
-			   int diff_output_format,
-			   struct diff_options *options,
-			   struct diffstat_t *diffstat)
+static int check_pair_status(struct diff_filepair *p)
 {
-	int inter_name_termination = '\t';
-	int line_termination = options->line_termination;
-	if (!line_termination)
-		inter_name_termination = 0;
-
 	switch (p->status) {
 	case DIFF_STATUS_UNKNOWN:
-		break;
+		return 0;
 	case 0:
 		die("internal error in diff-resolve-rename-copy");
-		break;
 	default:
-		switch (diff_output_format) {
-		case DIFF_FORMAT_DIFFSTAT:
-			diff_flush_stat(p, options, diffstat);
-			break;
-		case DIFF_FORMAT_CHECKDIFF:
-			diff_flush_checkdiff(p, options);
-			break;
-		case DIFF_FORMAT_PATCH:
-			diff_flush_patch(p, options);
-			break;
-		case DIFF_FORMAT_RAW:
-		case DIFF_FORMAT_NAME_STATUS:
-			diff_flush_raw(p, line_termination,
-				       inter_name_termination,
-				       options, diff_output_format);
-			break;
-		case DIFF_FORMAT_NAME:
-			diff_flush_name(p, line_termination);
-			break;
-		case DIFF_FORMAT_NO_OUTPUT:
-			break;
-		}
+		return 1;
 	}
 }
 
+static void flush_one_pair(struct diff_filepair *p, struct diff_options *opt)
+{
+	int fmt = opt->output_format;
+
+	if (fmt & DIFF_FORMAT_CHECKDIFF)
+		diff_flush_checkdiff(p, opt);
+	else if (fmt & (DIFF_FORMAT_RAW | DIFF_FORMAT_NAME_STATUS))
+		diff_flush_raw(p, opt);
+	else if (fmt & DIFF_FORMAT_NAME)
+		diff_flush_name(p, opt->line_termination);
+}
+
 static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs)
 {
 	if (fs->mode)
@@ -2243,58 +2317,96 @@
 	return result;
 }
 
+static int is_summary_empty(const struct diff_queue_struct *q)
+{
+	int i;
+
+	for (i = 0; i < q->nr; i++) {
+		const struct diff_filepair *p = q->queue[i];
+
+		switch (p->status) {
+		case DIFF_STATUS_DELETED:
+		case DIFF_STATUS_ADDED:
+		case DIFF_STATUS_COPIED:
+		case DIFF_STATUS_RENAMED:
+			return 0;
+		default:
+			if (p->score)
+				return 0;
+			if (p->one->mode && p->two->mode &&
+			    p->one->mode != p->two->mode)
+				return 0;
+			break;
+		}
+	}
+	return 1;
+}
+
 void diff_flush(struct diff_options *options)
 {
 	struct diff_queue_struct *q = &diff_queued_diff;
-	int i;
-	int diff_output_format = options->output_format;
-	struct diffstat_t *diffstat = NULL;
+	int i, output_format = options->output_format;
+	int separator = 0;
 
-	if (diff_output_format == DIFF_FORMAT_DIFFSTAT || options->with_stat) {
-		diffstat = xcalloc(sizeof (struct diffstat_t), 1);
-		diffstat->xm.consume = diffstat_consume;
-	}
+	/*
+	 * Order: raw, stat, summary, patch
+	 * or:    name/name-status/checkdiff (other bits clear)
+	 */
+	if (!q->nr)
+		goto free_queue;
 
-	if (options->with_raw) {
+	if (output_format & (DIFF_FORMAT_RAW |
+			     DIFF_FORMAT_NAME |
+			     DIFF_FORMAT_NAME_STATUS |
+			     DIFF_FORMAT_CHECKDIFF)) {
 		for (i = 0; i < q->nr; i++) {
 			struct diff_filepair *p = q->queue[i];
-			flush_one_pair(p, DIFF_FORMAT_RAW, options, NULL);
+			if (check_pair_status(p))
+				flush_one_pair(p, options);
 		}
-		putchar(options->line_termination);
+		separator++;
 	}
-	if (options->with_stat) {
+
+	if (output_format & DIFF_FORMAT_DIFFSTAT) {
+		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];
-			flush_one_pair(p, DIFF_FORMAT_DIFFSTAT, options,
-				       diffstat);
+			if (check_pair_status(p))
+				diff_flush_stat(p, options, &diffstat);
 		}
-		show_stats(diffstat);
-		free(diffstat);
-		diffstat = NULL;
-		if (options->summary)
-			for (i = 0; i < q->nr; i++)
-				diff_summary(q->queue[i]);
-		if (options->stat_sep)
-			fputs(options->stat_sep, stdout);
-		else
-			putchar(options->line_termination);
-	}
-	for (i = 0; i < q->nr; i++) {
-		struct diff_filepair *p = q->queue[i];
-		flush_one_pair(p, diff_output_format, options, diffstat);
+		show_stats(&diffstat);
+		separator++;
 	}
 
-	if (diffstat) {
-		show_stats(diffstat);
-		free(diffstat);
-	}
-
-	for (i = 0; i < q->nr; i++) {
-		if (diffstat && options->summary)
+	if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
+		for (i = 0; i < q->nr; i++)
 			diff_summary(q->queue[i]);
-		diff_free_filepair(q->queue[i]);
+		separator++;
 	}
 
+	if (output_format & DIFF_FORMAT_PATCH) {
+		if (separator) {
+			if (options->stat_sep) {
+				/* attach patch instead of inline */
+				fputs(options->stat_sep, stdout);
+			} else {
+				putchar(options->line_termination);
+			}
+		}
+
+		for (i = 0; i < q->nr; i++) {
+			struct diff_filepair *p = q->queue[i];
+			if (check_pair_status(p))
+				diff_flush_patch(p, options);
+		}
+	}
+
+	for (i = 0; i < q->nr; i++)
+		diff_free_filepair(q->queue[i]);
+free_queue:
 	free(q->queue);
 	q->queue = NULL;
 	q->nr = q->alloc = 0;
diff --git a/diff.h b/diff.h
index d5068af..2cced53 100644
--- a/diff.h
+++ b/diff.h
@@ -20,19 +20,32 @@
 		    const unsigned char *sha1,
 		    const char *base, const char *path);
 
+#define DIFF_FORMAT_RAW		0x0001
+#define DIFF_FORMAT_DIFFSTAT	0x0002
+#define DIFF_FORMAT_SUMMARY	0x0004
+#define DIFF_FORMAT_PATCH	0x0008
+
+/* These override all above */
+#define DIFF_FORMAT_NAME	0x0010
+#define DIFF_FORMAT_NAME_STATUS	0x0020
+#define DIFF_FORMAT_CHECKDIFF	0x0040
+
+/* Same as output_format = 0 but we know that -s flag was given
+ * and we should not give default value to output_format.
+ */
+#define DIFF_FORMAT_NO_OUTPUT	0x0080
+
 struct diff_options {
 	const char *filter;
 	const char *orderfile;
 	const char *pickaxe;
 	unsigned recursive:1,
-		 with_raw:1,
-		 with_stat:1,
 		 tree_in_recursive:1,
 		 binary:1,
+		 text:1,
 		 full_index:1,
 		 silent_on_remove:1,
 		 find_copies_harder:1,
-		 summary:1,
 		 color_diff:1;
 	int context;
 	int break_opt;
@@ -45,6 +58,7 @@
 	int rename_limit;
 	int setup;
 	int abbrev;
+	const char *msg_sep;
 	const char *stat_sep;
 	long xdl_opts;
 
@@ -55,6 +69,17 @@
 	add_remove_fn_t add_remove;
 };
 
+enum color_diff {
+	DIFF_RESET = 0,
+	DIFF_PLAIN = 1,
+	DIFF_METAINFO = 2,
+	DIFF_FRAGINFO = 3,
+	DIFF_FILE_OLD = 4,
+	DIFF_FILE_NEW = 5,
+	DIFF_COMMIT = 6,
+};
+const char *diff_get_color(int diff_use_color, enum color_diff ix);
+
 extern const char mime_boundary_leader[];
 
 extern void diff_tree_setup_paths(const char **paths, struct diff_options *);
@@ -109,7 +134,7 @@
 #define DIFF_SETUP_USE_CACHE		2
 #define DIFF_SETUP_USE_SIZE_CACHE	4
 
-extern int git_diff_config(const char *var, const char *value);
+extern int git_diff_ui_config(const char *var, const char *value);
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int);
 extern int diff_setup_done(struct diff_options *);
@@ -148,18 +173,10 @@
 "  -O<file>      reorder diffs according to the <file>.\n" \
 "  -S<string>    find filepair whose only one side contains the string.\n" \
 "  --pickaxe-all\n" \
-"                show all files diff when -S is used and hit is found.\n"
+"                show all files diff when -S is used and hit is found.\n" \
+"  -a  --text    treat all files as text.\n"
 
 extern int diff_queue_is_empty(void);
-
-#define DIFF_FORMAT_RAW		1
-#define DIFF_FORMAT_PATCH	2
-#define DIFF_FORMAT_NO_OUTPUT	3
-#define DIFF_FORMAT_NAME	4
-#define DIFF_FORMAT_NAME_STATUS	5
-#define DIFF_FORMAT_DIFFSTAT	6
-#define DIFF_FORMAT_CHECKDIFF	7
-
 extern void diff_flush(struct diff_options*);
 
 /* diff-raw status letters */
diff --git a/diffcore-rename.c b/diffcore-rename.c
index d57e865..0ec488a 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -96,11 +96,15 @@
 	return &(rename_src[first]);
 }
 
-static int is_exact_match(struct diff_filespec *src, struct diff_filespec *dst)
+static int is_exact_match(struct diff_filespec *src,
+			  struct diff_filespec *dst,
+			  int contents_too)
 {
 	if (src->sha1_valid && dst->sha1_valid &&
 	    !memcmp(src->sha1, dst->sha1, 20))
 		return 1;
+	if (!contents_too)
+		return 0;
 	if (diff_populate_filespec(src, 1) || diff_populate_filespec(dst, 1))
 		return 0;
 	if (src->size != dst->size)
@@ -201,6 +205,7 @@
 	fill_filespec(two, dst->sha1, dst->mode);
 
 	dp = diff_queue(NULL, one, two);
+	dp->renamed_pair = 1;
 	if (!strcmp(src->path, dst->path))
 		dp->score = rename_src[src_index].score;
 	else
@@ -242,7 +247,7 @@
 	struct diff_queue_struct *q = &diff_queued_diff;
 	struct diff_queue_struct outq;
 	struct diff_score *mx;
-	int i, j, rename_count;
+	int i, j, rename_count, contents_too;
 	int num_create, num_src, dst_cnt;
 
 	if (!minimum_score)
@@ -273,16 +278,23 @@
 
 	/* We really want to cull the candidates list early
 	 * with cheap tests in order to avoid doing deltas.
+	 * The first round matches up the up-to-date entries,
+	 * and then during the second round we try to match
+	 * cache-dirty entries as well.
 	 */
-	for (i = 0; i < rename_dst_nr; i++) {
-		struct diff_filespec *two = rename_dst[i].two;
-		for (j = 0; j < rename_src_nr; j++) {
-			struct diff_filespec *one = rename_src[j].one;
-			if (!is_exact_match(one, two))
-				continue;
-			record_rename_pair(i, j, MAX_SCORE);
-			rename_count++;
-			break; /* we are done with this entry */
+	for (contents_too = 0; contents_too < 2; contents_too++) {
+		for (i = 0; i < rename_dst_nr; i++) {
+			struct diff_filespec *two = rename_dst[i].two;
+			if (rename_dst[i].pair)
+				continue; /* dealt with an earlier round */
+			for (j = 0; j < rename_src_nr; j++) {
+				struct diff_filespec *one = rename_src[j].one;
+				if (!is_exact_match(one, two, contents_too))
+					continue;
+				record_rename_pair(i, j, MAX_SCORE);
+				rename_count++;
+				break; /* we are done with this entry */
+			}
 		}
 	}
 
diff --git a/diffcore.h b/diffcore.h
index 73c7842..2249bc2 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -53,11 +53,12 @@
 	char status; /* M C R N D U (see Documentation/diff-format.txt) */
 	unsigned source_stays : 1; /* all of R/C are copies */
 	unsigned broken_pair : 1;
+	unsigned renamed_pair : 1;
 };
 #define DIFF_PAIR_UNMERGED(p) \
 	(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
 
-#define DIFF_PAIR_RENAME(p) (strcmp((p)->one->path, (p)->two->path))
+#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)
 
 #define DIFF_PAIR_BROKEN(p) \
 	( (!DIFF_FILE_VALID((p)->one) != !DIFF_FILE_VALID((p)->two)) && \
diff --git a/dir.c b/dir.c
index d778ecd..092d077 100644
--- a/dir.c
+++ b/dir.c
@@ -336,7 +336,7 @@
 				if (dir->show_other_directories &&
 				    (subdir || !dir->hide_empty_directories) &&
 				    !dir_exists(fullname, baselen + len)) {
-					// Rewind the read subdirectory
+					/* Rewind the read subdirectory */
 					while (dir->nr > rewind_base)
 						free(dir->entries[--dir->nr]);
 					break;
diff --git a/environment.c b/environment.c
index 3de8eb3..87162b2 100644
--- a/environment.c
+++ b/environment.c
@@ -11,6 +11,7 @@
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
+int use_legacy_headers = 1;
 int trust_executable_bit = 1;
 int assume_unchanged = 0;
 int prefer_symlink_refs = 0;
@@ -20,6 +21,9 @@
 char git_commit_encoding[MAX_ENCODING_LENGTH] = "utf-8";
 int shared_repository = PERM_UMASK;
 const char *apply_default_whitespace = NULL;
+int zlib_compression_level = Z_DEFAULT_COMPRESSION;
+int pager_in_use;
+int pager_use_color = 1;
 
 static char *git_dir, *git_object_dir, *git_index_file, *git_refs_dir,
 	*git_graft_file;
diff --git a/exec_cmd.c b/exec_cmd.c
index c1539d1..62f51fc 100644
--- a/exec_cmd.c
+++ b/exec_cmd.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "exec_cmd.h"
+#include "quote.h"
 #define MAX_ARGS	32
 
 extern char **environ;
@@ -96,9 +97,27 @@
 		tmp = argv[0];
 		argv[0] = git_command;
 
+		if (getenv("GIT_TRACE")) {
+			const char **p = argv;
+			fputs("trace: exec:", stderr);
+			while (*p) {
+				fputc(' ', stderr);
+				sq_quote_print(stderr, *p);
+				++p;
+			}
+			putc('\n', stderr);
+			fflush(stderr);
+		}
+
 		/* execve() can only ever return if it fails */
 		execve(git_command, (char **)argv, environ);
 
+		if (getenv("GIT_TRACE")) {
+			fprintf(stderr, "trace: exec failed: %s\n",
+				strerror(errno));
+			fflush(stderr);
+		}
+
 		argv[0] = tmp;
 	}
 	return -1;
diff --git a/fetch-clone.c b/fetch-clone.c
index c16b0c4..5e84c46 100644
--- a/fetch-clone.c
+++ b/fetch-clone.c
@@ -129,10 +129,12 @@
 			len--;
 			switch (buf[0] & 0xFF) {
 			case 3:
+				safe_write(2, "remote: ", 8);
 				safe_write(2, buf+1, len);
-				fprintf(stderr, "\n");
+				safe_write(2, "\n", 1);
 				exit(1);
 			case 2:
+				safe_write(2, "remote: ", 8);
 				safe_write(2, buf+1, len);
 				continue;
 			case 1:
@@ -198,8 +200,8 @@
 
 /*
  * A "binary msec" is a power-of-two-msec, aka 1/1024th of a second.
- * Keeing the time in that format means that "bytes / msecs" means
- * is the same as kB/s (modulo rounding).
+ * Keeping the time in that format means that "bytes / msecs" means
+ * the same as kB/s (modulo rounding).
  *
  * 1000512 is a magic number (usecs in a second, rounded up by half
  * of 1024, to make "rounding" come out right ;)
diff --git a/fetch-pack.c b/fetch-pack.c
index f2c51eb..b7824db 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -46,7 +46,7 @@
 {
 	struct object *o = deref_tag(parse_object(sha1), path, 0);
 
-	if (o && o->type == TYPE_COMMIT)
+	if (o && o->type == OBJ_COMMIT)
 		rev_list_push((struct commit *)o, SEEN);
 
 	return 0;
@@ -256,14 +256,14 @@
 {
 	struct object *o = parse_object(sha1);
 
-	while (o && o->type == TYPE_TAG) {
+	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
 		if (!t->tagged)
 			break; /* broken repository */
 		o->flags |= COMPLETE;
 		o = parse_object(t->tagged->sha1);
 	}
-	if (o && o->type == TYPE_COMMIT) {
+	if (o && o->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)o;
 		commit->object.flags |= COMPLETE;
 		insert_by_date(commit, &complete);
@@ -357,7 +357,7 @@
 		 * in sync with the other side at some time after
 		 * that (it is OK if we guess wrong here).
 		 */
-		if (o->type == TYPE_COMMIT) {
+		if (o->type == OBJ_COMMIT) {
 			struct commit *commit = (struct commit *)o;
 			if (!cutoff || cutoff < commit->date)
 				cutoff = commit->date;
@@ -376,7 +376,7 @@
 		struct object *o = deref_tag(lookup_object(ref->old_sha1),
 					     NULL, 0);
 
-		if (!o || o->type != TYPE_COMMIT || !(o->flags & COMPLETE))
+		if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
 			continue;
 
 		if (!(o->flags & SEEN)) {
diff --git a/fetch.c b/fetch.c
index 238032b..aeb6bf2 100644
--- a/fetch.c
+++ b/fetch.c
@@ -7,9 +7,7 @@
 #include "tag.h"
 #include "blob.h"
 #include "refs.h"
-
-const char *write_ref = NULL;
-const char *write_ref_log_details = NULL;
+#include "strbuf.h"
 
 int get_tree = 0;
 int get_history = 0;
@@ -118,20 +116,20 @@
 
 static int process_object(struct object *obj)
 {
-	if (obj->type == TYPE_COMMIT) {
+	if (obj->type == OBJ_COMMIT) {
 		if (process_commit((struct commit *)obj))
 			return -1;
 		return 0;
 	}
-	if (obj->type == TYPE_TREE) {
+	if (obj->type == OBJ_TREE) {
 		if (process_tree((struct tree *)obj))
 			return -1;
 		return 0;
 	}
-	if (obj->type == TYPE_BLOB) {
+	if (obj->type == OBJ_BLOB) {
 		return 0;
 	}
-	if (obj->type == TYPE_TAG) {
+	if (obj->type == OBJ_TAG) {
 		if (process_tag((struct tag *)obj))
 			return -1;
 		return 0;
@@ -213,54 +211,106 @@
 	return 0;
 }
 
-int pull(char *target)
+int pull_targets_stdin(char ***target, const char ***write_ref)
 {
-	struct ref_lock *lock = NULL;
-	unsigned char sha1[20];
+	int targets = 0, targets_alloc = 0;
+	struct strbuf buf;
+	*target = NULL; *write_ref = NULL;
+	strbuf_init(&buf);
+	while (1) {
+		char *rf_one = NULL;
+		char *tg_one;
+
+		read_line(&buf, stdin, '\n');
+		if (buf.eof)
+			break;
+		tg_one = buf.buf;
+		rf_one = strchr(tg_one, '\t');
+		if (rf_one)
+			*rf_one++ = 0;
+
+		if (targets >= targets_alloc) {
+			targets_alloc = targets_alloc ? targets_alloc * 2 : 64;
+			*target = xrealloc(*target, targets_alloc * sizeof(**target));
+			*write_ref = xrealloc(*write_ref, targets_alloc * sizeof(**write_ref));
+		}
+		(*target)[targets] = strdup(tg_one);
+		(*write_ref)[targets] = rf_one ? strdup(rf_one) : NULL;
+		targets++;
+	}
+	return targets;
+}
+
+void pull_targets_free(int targets, char **target, const char **write_ref)
+{
+	while (targets--) {
+		free(target[targets]);
+		if (write_ref && write_ref[targets])
+			free((char *) write_ref[targets]);
+	}
+}
+
+int pull(int targets, char **target, const char **write_ref,
+         const char *write_ref_log_details)
+{
+	struct ref_lock **lock = xcalloc(targets, sizeof(struct ref_lock *));
+	unsigned char *sha1 = xmalloc(targets * 20);
 	char *msg;
 	int ret;
+	int i;
 
 	save_commit_buffer = 0;
 	track_object_refs = 0;
-	if (write_ref) {
-		lock = lock_ref_sha1(write_ref, NULL, 0);
-		if (!lock) {
-			error("Can't lock ref %s", write_ref);
-			return -1;
+
+	for (i = 0; i < targets; i++) {
+		if (!write_ref || !write_ref[i])
+			continue;
+
+		lock[i] = lock_ref_sha1(write_ref[i], NULL, 0);
+		if (!lock[i]) {
+			error("Can't lock ref %s", write_ref[i]);
+			goto unlock_and_fail;
 		}
 	}
 
 	if (!get_recover)
 		for_each_ref(mark_complete);
 
-	if (interpret_target(target, sha1)) {
-		error("Could not interpret %s as something to pull", target);
-		if (lock)
-			unlock_ref(lock);
-		return -1;
-	}
-	if (process(lookup_unknown_object(sha1))) {
-		if (lock)
-			unlock_ref(lock);
-		return -1;
-	}
-	if (loop()) {
-		if (lock)
-			unlock_ref(lock);
-		return -1;
+	for (i = 0; i < targets; i++) {
+		if (interpret_target(target[i], &sha1[20 * i])) {
+			error("Could not interpret %s as something to pull", target[i]);
+			goto unlock_and_fail;
+		}
+		if (process(lookup_unknown_object(&sha1[20 * i])))
+			goto unlock_and_fail;
 	}
 
-	if (write_ref) {
-		if (write_ref_log_details) {
-			msg = xmalloc(strlen(write_ref_log_details) + 12);
-			sprintf(msg, "fetch from %s", write_ref_log_details);
-		}
-		else
-			msg = NULL;
-		ret = write_ref_sha1(lock, sha1, msg ? msg : "fetch (unknown)");
-		if (msg)
-			free(msg);
-		return ret;
+	if (loop())
+		goto unlock_and_fail;
+
+	if (write_ref_log_details) {
+		msg = xmalloc(strlen(write_ref_log_details) + 12);
+		sprintf(msg, "fetch from %s", write_ref_log_details);
+	} else {
+		msg = NULL;
 	}
+	for (i = 0; i < targets; i++) {
+		if (!write_ref || !write_ref[i])
+			continue;
+		ret = write_ref_sha1(lock[i], &sha1[20 * i], msg ? msg : "fetch (unknown)");
+		lock[i] = NULL;
+		if (ret)
+			goto unlock_and_fail;
+	}
+	if (msg)
+		free(msg);
+
 	return 0;
+
+
+unlock_and_fail:
+	for (i = 0; i < targets; i++)
+		if (lock[i])
+			unlock_ref(lock[i]);
+	return -1;
 }
diff --git a/fetch.h b/fetch.h
index 841bb1a..be48c6f 100644
--- a/fetch.h
+++ b/fetch.h
@@ -22,12 +22,6 @@
  */
 extern int fetch_ref(char *ref, unsigned char *sha1);
 
-/* If set, the ref filename to write the target value to. */
-extern const char *write_ref;
-
-/* If set additional text will appear in the ref log. */
-extern const char *write_ref_log_details;
-
 /* Set to fetch the target tree. */
 extern int get_tree;
 
@@ -46,6 +40,15 @@
 /* Report what we got under get_verbosely */
 extern void pull_say(const char *, const char *);
 
-extern int pull(char *target);
+/* Load pull targets from stdin */
+extern int pull_targets_stdin(char ***target, const char ***write_ref);
+
+/* Free up loaded targets */
+extern void pull_targets_free(int targets, char **target, const char **write_ref);
+
+/* If write_ref is set, the ref filename to write the target value to. */
+/* If write_ref_log_details is set, additional text will appear in the ref log. */
+extern int pull(int targets, char **target, const char **write_ref,
+		const char *write_ref_log_details);
 
 #endif /* PULL_H */
diff --git a/fsck-objects.c b/fsck-objects.c
index ef54a8a..e167f41 100644
--- a/fsck-objects.c
+++ b/fsck-objects.c
@@ -297,13 +297,13 @@
 	if (obj->flags & SEEN)
 		return 0;
 	obj->flags |= SEEN;
-	if (obj->type == TYPE_BLOB)
+	if (obj->type == OBJ_BLOB)
 		return 0;
-	if (obj->type == TYPE_TREE)
+	if (obj->type == OBJ_TREE)
 		return fsck_tree((struct tree *) obj);
-	if (obj->type == TYPE_COMMIT)
+	if (obj->type == OBJ_COMMIT)
 		return fsck_commit((struct commit *) obj);
-	if (obj->type == TYPE_TAG)
+	if (obj->type == OBJ_TAG)
 		return fsck_tag((struct tag *) obj);
 	/* By now, parse_object() would've returned NULL instead. */
 	return objerror(obj, "unknown type '%d' (internal fsck error)", obj->type);
@@ -472,7 +472,7 @@
 		}
 		mark_reachable(obj, REACHABLE);
 		obj->used = 1;
-		if (obj->type != TYPE_TREE)
+		if (obj->type != OBJ_TREE)
 			err |= objerror(obj, "non-tree in cache-tree");
 	}
 	for (i = 0; i < it->subtree_nr; i++)
diff --git a/git-am.sh b/git-am.sh
index 679045a..d0af786 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -45,6 +45,12 @@
 	this=$next
 }
 
+cannot_fallback () {
+	echo "$1"
+	echo "Cannot fall back to three-way merge."
+	exit 1
+}
+
 fall_back_3way () {
     O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
 
@@ -52,19 +58,23 @@
     mkdir "$dotest/patch-merge-tmp-dir"
 
     # First see if the patch records the index info that we can use.
-    if git-apply -z --index-info "$dotest/patch" \
-	>"$dotest/patch-merge-index-info" 2>/dev/null &&
-	GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-	git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
-	GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
-	git-write-tree >"$dotest/patch-merge-base+" &&
-	# index has the base tree now.
-	GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+    git-apply -z --index-info "$dotest/patch" \
+	>"$dotest/patch-merge-index-info" &&
+    GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+    git-update-index -z --index-info <"$dotest/patch-merge-index-info" &&
+    GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
+    git-write-tree >"$dotest/patch-merge-base+" ||
+    cannot_fallback "Patch does not record usable index information."
+
+    echo Using index info to reconstruct a base tree...
+    if GIT_INDEX_FILE="$dotest/patch-merge-tmp-index" \
 	git-apply $binary --cached <"$dotest/patch"
     then
-	echo Using index info to reconstruct a base tree...
 	mv "$dotest/patch-merge-base+" "$dotest/patch-merge-base"
 	mv "$dotest/patch-merge-tmp-index" "$dotest/patch-merge-index"
+    else
+        cannot_fallback "Did you hand edit your patch?
+It does not apply to blobs recorded in its index."
     fi
 
     test -f "$dotest/patch-merge-index" &&
@@ -77,7 +87,7 @@
     # This is not so wrong.  Depending on which base we picked,
     # orig_tree may be wildly different from ours, but his_tree
     # has the same set of wildly different changes in parts the
-    # patch did not touch, so resolve ends up cancelling them,
+    # patch did not touch, so resolve ends up canceling them,
     # saying that we reverted all those changes.
 
     git-merge-resolve $orig_tree -- HEAD $his_tree || {
@@ -91,6 +101,7 @@
 }
 
 prec=4
+rloga=am
 dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg=
 
 while case "$#" in 0) break;; esac
@@ -130,6 +141,9 @@
 	--resolvemsg=*)
 	resolvemsg=$(echo "$1" | sed -e "s/^--resolvemsg=//"); shift ;;
 
+	--reflog-action=*)
+	rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`; shift ;;
+
 	--)
 	shift; break ;;
 	-*)
@@ -152,8 +166,10 @@
 
 if test -d "$dotest"
 then
-	test ",$#," = ",0," ||
-	die "previous dotest directory $dotest still exists but mbox given."
+	if test ",$#," != ",0," || ! tty -s
+	then
+		die "previous dotest directory $dotest still exists but mbox given."
+	fi
 	resume=yes
 else
 	# Make sure we are not given --skip nor --resolved
@@ -413,7 +429,7 @@
 	parent=$(git-rev-parse --verify HEAD) &&
 	commit=$(git-commit-tree $tree -p $parent <"$dotest/final-commit") &&
 	echo Committed: $commit &&
-	git-update-ref -m "am: $SUBJECT" HEAD $commit $parent ||
+	git-update-ref -m "$rloga: $SUBJECT" HEAD $commit $parent ||
 	stop_here $this
 
 	if test -x "$GIT_DIR"/hooks/post-applypatch
diff --git a/git-annotate.perl b/git-annotate.perl
index a6a7a48..215ed26 100755
--- a/git-annotate.perl
+++ b/git-annotate.perl
@@ -102,10 +102,10 @@
 push @revqueue, $head;
 init_claim( defined $starting_rev ? $head : 'dirty');
 unless (defined $starting_rev) {
-	my $diff = open_pipe("git","diff","-R", "HEAD", "--",$filename)
+	my $diff = open_pipe("git","diff","HEAD", "--",$filename)
 		or die "Failed to call git diff to check for dirty state: $!";
 
-	_git_diff_parse($diff, $head, "dirty", (
+	_git_diff_parse($diff, [$head], "dirty", (
 				'author' => gitvar_name("GIT_AUTHOR_IDENT"),
 				'author_date' => sprintf("%s +0000",time()),
 				)
@@ -147,21 +147,20 @@
 
 
 sub handle_rev {
-	my $i = 0;
+	my $revseen = 0;
 	my %seen;
 	while (my $rev = shift @revqueue) {
 		next if $seen{$rev}++;
 
 		my %revinfo = git_commit_info($rev);
 
-		foreach my $p (@{$revs{$rev}{'parents'}}) {
+		if (exists $revs{$rev}{parents} &&
+		    scalar @{$revs{$rev}{parents}} != 0) {
 
-			git_diff_parse($p, $rev, %revinfo);
-			push @revqueue, $p;
-		}
+			git_diff_parse($revs{$rev}{'parents'}, $rev, %revinfo);
+			push @revqueue, @{$revs{$rev}{'parents'}};
 
-
-		if (scalar @{$revs{$rev}{parents}} == 0) {
+		} else {
 			# We must be at the initial rev here, so claim everything that is left.
 			for (my $i = 0; $i < @{$revs{$rev}{lines}}; $i++) {
 				if (ref ${$revs{$rev}{lines}}[$i] eq '' || ${$revs{$rev}{lines}}[$i][1] eq '') {
@@ -248,93 +247,290 @@
 	return $parent;
 }
 
+sub git_find_all_parents {
+	my ($rev) = @_;
+
+	my $revparent = open_pipe("git-rev-list","--remove-empty", "--parents","--max-count=1","$rev")
+		or die "Failed to open git-rev-list to find a single parent: $!";
+
+	my $parentline = <$revparent>;
+	chomp $parentline;
+	my ($origrev, @parents) = split m/\s+/, $parentline;
+
+	close($revparent);
+
+	return @parents;
+}
+
+sub git_merge_base {
+	my ($rev1, $rev2) = @_;
+
+	my $mb = open_pipe("git-merge-base", $rev1, $rev2)
+	        or die "Failed to open git-merge-base: $!";
+
+	my $base = <$mb>;
+	chomp $base;
+
+	close($mb);
+
+	return $base;
+}
+
+# Construct a set of pseudo parents that are in the same order,
+# and the same quantity as the real parents,
+# but whose SHA1s are as similar to the logical parents
+# as possible.
+sub get_pseudo_parents {
+	my ($all, $fake) = @_;
+
+	my @all = @$all;
+	my @fake = @$fake;
+
+	my @pseudo;
+
+	my %fake = map {$_ => 1} @fake;
+	my %seenfake;
+
+	my $fakeidx = 0;
+	foreach my $p (@all) {
+		if (exists $fake{$p}) {
+			if ($fake[$fakeidx] ne $p) {
+				die sprintf("parent mismatch: %s != %s\nall:%s\nfake:%s\n",
+					    $fake[$fakeidx], $p,
+					    join(", ", @all),
+					    join(", ", @fake),
+					   );
+			}
+
+			push @pseudo, $p;
+			$fakeidx++;
+			$seenfake{$p}++;
+
+		} else {
+			my $base = git_merge_base($fake[$fakeidx], $p);
+			if ($base ne $fake[$fakeidx]) {
+				die sprintf("Result of merge-base doesn't match fake: %s,%s != %s\n",
+				       $fake[$fakeidx], $p, $base);
+			}
+
+			# The details of how we parse the diffs
+			# mean that we cannot have a duplicate
+			# revision in the list, so if we've already
+			# seen the revision we would normally add, just use
+			# the actual revision.
+			if ($seenfake{$base}) {
+				push @pseudo, $p;
+			} else {
+				push @pseudo, $base;
+				$seenfake{$base}++;
+			}
+		}
+	}
+
+	return @pseudo;
+}
+
 
 # Get a diff between the current revision and a parent.
 # Record the commit information that results.
 sub git_diff_parse {
-	my ($parent, $rev, %revinfo) = @_;
+	my ($parents, $rev, %revinfo) = @_;
 
-	my $diff = open_pipe("git-diff-tree","-M","-p",$rev,$parent,"--",
-			$revs{$rev}{'filename'}, $revs{$parent}{'filename'})
+	my @pseudo_parents;
+	my @command = ("git-diff-tree");
+	my $revision_spec;
+
+	if (scalar @$parents == 1) {
+
+		$revision_spec = join("..", $parents->[0], $rev);
+		@pseudo_parents = @$parents;
+	} else {
+		my @all_parents = git_find_all_parents($rev);
+
+		if (@all_parents !=  @$parents) {
+			@pseudo_parents = get_pseudo_parents(\@all_parents, $parents);
+		} else {
+			@pseudo_parents = @$parents;
+		}
+
+		$revision_spec = $rev;
+		push @command, "-c";
+	}
+
+	my @filenames = ( $revs{$rev}{'filename'} );
+
+	foreach my $parent (@$parents) {
+		push @filenames, $revs{$parent}{'filename'};
+	}
+
+	push @command, "-p", "-M", $revision_spec, "--", @filenames;
+
+
+	my $diff = open_pipe( @command )
 		or die "Failed to call git-diff for annotation: $!";
 
-	_git_diff_parse($diff, $parent, $rev, %revinfo);
+	_git_diff_parse($diff, \@pseudo_parents, $rev, %revinfo);
 
 	close($diff);
 }
 
 sub _git_diff_parse {
-	my ($diff, $parent, $rev, %revinfo) = @_;
+	my ($diff, $parents, $rev, %revinfo) = @_;
 
-	my ($ri, $pi) = (0,0);
+	my $ri = 0;
+
 	my $slines = $revs{$rev}{'lines'};
-	my @plines;
+	my (%plines, %pi);
 
 	my $gotheader = 0;
 	my ($remstart);
-	my ($hunk_start, $hunk_index);
+	my $parent_count = @$parents;
+
+	my $diff_header_regexp = "^@";
+	$diff_header_regexp .= "@" x @$parents;
+	$diff_header_regexp .= ' -\d+,\d+' x @$parents;
+	$diff_header_regexp .= ' \+(\d+),\d+';
+	$diff_header_regexp .= " " . ("@" x @$parents);
+
+	my %claim_regexps;
+	my $allparentplus = '^' . '\\+' x @$parents . '(.*)$';
+
+	{
+		my $i = 0;
+		foreach my $parent (@$parents) {
+
+			$pi{$parent} = 0;
+			my $r = '^' . '.' x @$parents . '(.*)$';
+			my $p = $r;
+			substr($p,$i+1, 1) = '\\+';
+
+			my $m = $r;
+			substr($m,$i+1, 1) = '-';
+
+			$claim_regexps{$parent}{plus} = $p;
+			$claim_regexps{$parent}{minus} = $m;
+
+			$plines{$parent} = [];
+
+			$i++;
+		}
+	}
+
+	DIFF:
 	while(<$diff>) {
 		chomp;
-		if (m/^@@ -(\d+),(\d+) \+(\d+),(\d+)/) {
-			$remstart = $1;
-			# Adjust for 0-based arrays
-			$remstart--;
-			# Reinit hunk tracking.
-			$hunk_start = $remstart;
-			$hunk_index = 0;
+		#printf("%d:%s:\n", $gotheader, $_);
+		if (m/$diff_header_regexp/) {
+			$remstart = $1 - 1;
+			# (0-based arrays)
+
 			$gotheader = 1;
 
-			for (my $i = $ri; $i < $remstart; $i++) {
-				$plines[$pi++] = $slines->[$i];
-				$ri++;
+			foreach my $parent (@$parents) {
+				for (my $i = $ri; $i < $remstart; $i++) {
+					$plines{$parent}[$pi{$parent}++] = $slines->[$i];
+				}
 			}
-			next;
+			$ri = $remstart;
+
+			next DIFF;
+
 		} elsif (!$gotheader) {
-			next;
+			# Skip over the leadin.
+			next DIFF;
 		}
 
-		if (m/^\+(.*)$/) {
-			my $line = $1;
-			$plines[$pi++] = [ $line, '', '', '', 0 ];
-			next;
-
-		} elsif (m/^-(.*)$/) {
-			my $line = $1;
-			if (get_line($slines, $ri) eq $line) {
-				# Found a match, claim
-				claim_line($ri, $rev, $slines, %revinfo);
-			} else {
-				die sprintf("Sync error: %d/%d\n|%s\n|%s\n%s => %s\n",
-						$ri, $hunk_start + $hunk_index,
-						$line,
-						get_line($slines, $ri),
-						$rev, $parent);
-			}
-			$ri++;
-
-		} elsif (m/^\\/) {
+		if (m/^\\/) {
 			;
 			# Skip \No newline at end of file.
 			# But this can be internationalized, so only look
 			# for an initial \
 
 		} else {
-			if (substr($_,1) ne get_line($slines,$ri) ) {
-				die sprintf("Line %d (%d) does not match:\n|%s\n|%s\n%s => %s\n",
-						$hunk_start + $hunk_index, $ri,
-						substr($_,1),
-						get_line($slines,$ri),
-						$rev, $parent);
+			my %claims = ();
+			my $negclaim = 0;
+			my $allclaimed = 0;
+			my $line;
+
+			if (m/$allparentplus/) {
+				claim_line($ri, $rev, $slines, %revinfo);
+				$allclaimed = 1;
+
 			}
-			$plines[$pi++] = $slines->[$ri++];
+
+			PARENT:
+			foreach my $parent (keys %claim_regexps) {
+				my $m = $claim_regexps{$parent}{minus};
+				my $p = $claim_regexps{$parent}{plus};
+
+				if (m/$m/) {
+					$line = $1;
+					$plines{$parent}[$pi{$parent}++] = [ $line, '', '', '', 0 ];
+					$negclaim++;
+
+				} elsif (m/$p/) {
+					$line = $1;
+					if (get_line($slines, $ri) eq $line) {
+						# Found a match, claim
+						$claims{$parent}++;
+
+					} else {
+						die sprintf("Sync error: %d\n|%s\n|%s\n%s => %s\n",
+								$ri, $line,
+								get_line($slines, $ri),
+								$rev, $parent);
+					}
+				}
+			}
+
+			if (%claims) {
+				foreach my $parent (@$parents) {
+					next if $claims{$parent} || $allclaimed;
+					$plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+					    #[ $line, '', '', '', 0 ];
+				}
+				$ri++;
+
+			} elsif ($negclaim) {
+				next DIFF;
+
+			} else {
+				if (substr($_,scalar @$parents) ne get_line($slines,$ri) ) {
+				        foreach my $parent (@$parents) {
+						printf("parent %s is on line %d\n", $parent, $pi{$parent});
+					}
+
+					my @context;
+					for (my $i = -2; $i < 2; $i++) {
+						push @context, get_line($slines, $ri + $i);
+					}
+					my $context = join("\n", @context);
+
+					my $justline = substr($_, scalar @$parents);
+					die sprintf("Line %d, does not match:\n|%s|\n|%s|\n%s\n",
+						    $ri,
+						    $justline,
+						    $context);
+				}
+				foreach my $parent (@$parents) {
+					$plines{$parent}[$pi{$parent}++] = $slines->[$ri];
+				}
+				$ri++;
+			}
 		}
-		$hunk_index++;
-	}
-	for (my $i = $ri; $i < @{$slines} ; $i++) {
-		push @plines, $slines->[$ri++];
 	}
 
-	$revs{$parent}{lines} = \@plines;
+	for (my $i = $ri; $i < @{$slines} ; $i++) {
+		foreach my $parent (@$parents) {
+			push @{$plines{$parent}}, $slines->[$ri];
+		}
+		$ri++;
+	}
+
+	foreach my $parent (@$parents) {
+		$revs{$parent}{lines} = $plines{$parent};
+	}
+
 	return;
 }
 
diff --git a/git-applypatch.sh b/git-applypatch.sh
index e4b0947..8df2aee 100755
--- a/git-applypatch.sh
+++ b/git-applypatch.sh
@@ -182,7 +182,7 @@
 	# This is not so wrong.  Depending on which base we picked,
 	# orig_tree may be wildly different from ours, but his_tree
 	# has the same set of wildly different changes in parts the
-	# patch did not touch, so resolve ends up cancelling them,
+	# patch did not touch, so resolve ends up canceling them,
 	# saying that we reverted all those changes.
 
 	if git-merge-resolve $orig_tree -- HEAD $his_tree
diff --git a/git-archimport.perl b/git-archimport.perl
index 740bc1f..ada60ec 100755
--- a/git-archimport.perl
+++ b/git-archimport.perl
@@ -14,7 +14,7 @@
 
 Imports a project from one or more Arch repositories. It will follow branches
 and repositories within the namespaces defined by the <archive/branch>
-parameters suppplied. If it cannot find the remote branch a merge comes from
+parameters supplied. If it cannot find the remote branch a merge comes from
 it will just import it as a regular commit. If it can find it, it will mark it 
 as a merge whenever possible.
 
@@ -88,7 +88,7 @@
 # $arch_branches:
 # values associated with keys:
 #   =1 - Arch version / git 'branch' detected via abrowse on a limit
-#   >1 - Arch version / git 'branch' of an auxilliary branch we've merged
+#   >1 - Arch version / git 'branch' of an auxiliary branch we've merged
 my %arch_branches = map { $_ => 1 } @ARGV;
 
 $ENV{'TMPDIR'} = $opt_t if $opt_t; # $ENV{TMPDIR} will affect tempdir() calls:
@@ -667,7 +667,7 @@
     if (`find $tmp/changeset/patches -type f -name '*.patch'`) {
         # this can be sped up considerably by doing
         #    (find | xargs cat) | patch
-        # but that cna get mucked up by patches
+        # but that can get mucked up by patches
         # with missing trailing newlines or the standard 
         # 'missing newline' flag in the patch - possibly
         # produced with an old/buggy diff.
@@ -1026,7 +1026,7 @@
 }
 
 
-# an alterative to `command` that allows input to be passed as an array
+# an alternative to `command` that allows input to be passed as an array
 # to work around shell problems with weird characters in arguments
 sub safe_pipe_capture {
     my @output;
diff --git a/git-bisect.sh b/git-bisect.sh
index 03df143..06a8d26 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -13,7 +13,7 @@
 . git-sh-setup
 
 sq() {
-	perl -e '
+	@@PERL@@ -e '
 		for (@ARGV) {
 			s/'\''/'\'\\\\\'\''/g;
 			print " '\''$_'\''";
diff --git a/git-checkout.sh b/git-checkout.sh
index 5613bfc..580a9e8 100755
--- a/git-checkout.sh
+++ b/git-checkout.sh
@@ -67,6 +67,10 @@
 			set x "$arg" "$@"
 			shift
 		fi
+		case "$1" in
+		--)
+			shift ;;
+		esac
 		break
 		;;
     esac
diff --git a/git-clone.sh b/git-clone.sh
index 6a14b25..7060bda 100755
--- a/git-clone.sh
+++ b/git-clone.sh
@@ -205,7 +205,7 @@
 [ -e "$dir" ] && echo "$dir already exists." && usage
 mkdir -p "$dir" &&
 D=$(cd "$dir" && pwd) &&
-trap 'err=$?; cd ..; rm -r "$D"; exit $err' 0
+trap 'err=$?; cd ..; rm -rf "$D"; exit $err' 0
 case "$bare" in
 yes)
 	GIT_DIR="$D" ;;
@@ -266,7 +266,7 @@
 	    echo "$repo/objects" >> "$GIT_DIR/objects/info/alternates"
 	    ;;
 	esac
-	git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
+	git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
 	;;
 *)
 	case "$repo" in
@@ -296,9 +296,9 @@
 		    done
 		    rm -f "$GIT_DIR/TMP_ALT"
 		fi
-		git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD"
+		git-ls-remote "$repo" >"$GIT_DIR/CLONE_HEAD" || exit 1
 		;;
-	http://*)
+	https://*|http://*)
 		if test -z "@@NO_CURL@@"
 		then
 			clone_dumb_http "$repo" "$D"
@@ -324,7 +324,8 @@
 if test -f "$GIT_DIR/CLONE_HEAD"
 then
 	# Read git-fetch-pack -k output and store the remote branches.
-	perl -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin"
+	@@PERL@@ -e "$copy_refs" "$GIT_DIR" "$use_separate_remote" "$origin" ||
+	exit
 fi
 
 cd "$D" || exit
diff --git a/git-commit.sh b/git-commit.sh
index 22c4ce8..4cf3fab 100755
--- a/git-commit.sh
+++ b/git-commit.sh
@@ -138,32 +138,26 @@
         if test -z "$untracked_files"; then
             option="--directory --no-empty-directory"
         fi
+	hdr_shown=
 	if test -f "$GIT_DIR/info/exclude"
 	then
-	    git-ls-files -z --others $option \
+	    git-ls-files --others $option \
 		--exclude-from="$GIT_DIR/info/exclude" \
 		--exclude-per-directory=.gitignore
 	else
-	    git-ls-files -z --others $option \
+	    git-ls-files --others $option \
 		--exclude-per-directory=.gitignore
 	fi |
-	perl -e '$/ = "\0";
-	    my $shown = 0;
-	    while (<>) {
-		chomp;
-		s|\\|\\\\|g;
-		s|\t|\\t|g;
-		s|\n|\\n|g;
-		s/^/#	/;
-		if (!$shown) {
-		    print "#\n# Untracked files:\n";
-		    print "#   (use \"git add\" to add to commit)\n";
-		    print "#\n";
-		    $shown = 1;
-		}
-		print "$_\n";
-	    }
-	'
+	while read line; do
+	    if [ -z "$hdr_shown" ]; then
+		echo '#'
+		echo '# Untracked files:'
+		echo '#   (use "git add" to add to commit)'
+		echo '#'
+		hdr_shown=1
+	    fi
+	    echo "#	$line"
+	done
 
 	if test -n "$verbose" -a -z "$IS_INITIAL"
 	then
@@ -599,7 +593,7 @@
 	GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
 	test '' != "$GIT_AUTHOR_NAME" &&
 	test '' != "$GIT_AUTHOR_EMAIL" ||
-	die "malformatted --author parameter"
+	die "malformed --author parameter"
 	export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 elif test '' != "$use_commit"
 then
@@ -635,9 +629,12 @@
 PARENTS="-p HEAD"
 if test -z "$initial_commit"
 then
+	rloga='commit'
 	if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
+		rloga='commit (merge)'
 		PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
 	elif test -n "$amend"; then
+		rloga='commit (amend)'
 		PARENTS=$(git-cat-file commit HEAD |
 			sed -n -e '/^$/q' -e 's/^parent /-p /p')
 	fi
@@ -649,6 +646,7 @@
 	fi
 	PARENTS=""
 	current=
+	rloga='commit (initial)'
 fi
 
 if test -z "$no_edit"
@@ -724,7 +722,7 @@
 	fi &&
 	commit=$(cat "$GIT_DIR"/COMMIT_MSG | git-commit-tree $tree $PARENTS) &&
 	rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
-	git-update-ref -m "commit: $rlogm" HEAD $commit $current &&
+	git-update-ref -m "$rloga: $rlogm" HEAD $commit $current &&
 	rm -f -- "$GIT_DIR/MERGE_HEAD" &&
 	if test -f "$NEXT_INDEX"
 	then
diff --git a/git-compat-util.h b/git-compat-util.h
index 93f5580..b2e18954 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -91,6 +91,9 @@
 		ret = malloc(1);
 	if (!ret)
 		die("Out of memory, malloc failed");
+#ifdef XMALLOC_POISON
+	memset(ret, 0xA5, size);
+#endif
 	return ret;
 }
 
@@ -136,6 +139,13 @@
 	}
 }
 
+static inline int has_extension(const char *filename, const char *ext)
+{
+	size_t len = strlen(filename);
+	size_t extlen = strlen(ext);
+	return len > extlen && !memcmp(filename + len - extlen, ext, extlen);
+}
+
 /* Sane ctype - no locale, and works with signed chars */
 #undef isspace
 #undef isdigit
diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl
index d1051d0..99b3dc3 100755
--- a/git-cvsexportcommit.perl
+++ b/git-cvsexportcommit.perl
@@ -16,9 +16,9 @@
     die "GIT_DIR is not defined or is unreadable";
 }
 
-our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_m );
+our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m );
 
-getopts('hpvcfm:');
+getopts('hpvcfam:');
 
 $opt_h && usage();
 
@@ -29,7 +29,6 @@
 				     TMPDIR => 1,
 				     CLEANUP => 1);
 
-print Dumper(@ARGV);
 # resolve target commit
 my $commit;
 $commit = pop @ARGV;
@@ -53,25 +52,45 @@
 # find parents from the commit itself
 my @commit  = safe_pipe_capture('git-cat-file', 'commit', $commit);
 my @parents;
-foreach my $p (@commit) {
-    if ($p =~ m/^$/) { # end of commit headers, we're done
-	last;
+my $committer;
+my $author;
+my $stage = 'headers'; # headers, msg
+my $title;
+my $msg = '';
+
+foreach my $line (@commit) {
+    chomp $line;
+    if ($stage eq 'headers' && $line eq '') {
+	$stage = 'msg';
+	next;
     }
-    if ($p =~ m/^parent (\w{40})$/) { # found a parent
-	push @parents, $1;
+
+    if ($stage eq 'headers') {
+	if ($line =~ m/^parent (\w{40})$/) { # found a parent
+	    push @parents, $1;
+	} elsif ($line =~ m/^author (.+) \d+ \+\d+$/) {
+	    $author = $1;
+	} elsif ($line =~ m/^committer (.+) \d+ \+\d+$/) {
+	    $committer = $1;
+	}
+    } else {
+	$msg .= $line . "\n";
+	unless ($title) {
+	    $title = $line;
+	}
     }
 }
 
 if ($parent) {
+    my $found;
     # double check that it's a valid parent
     foreach my $p (@parents) {
-	my $found;
 	if ($p eq $parent) {
 	    $found = 1;
 	    last;
 	}; # found it
-	die "Did not find $parent in the parents for this commit!";
     }
+    die "Did not find $parent in the parents for this commit!" if !$found;
 } else { # we don't have a parent from the cmdline...
     if (@parents == 1) { # it's safe to get it from the commit
 	$parent = $parents[0];
@@ -84,12 +103,18 @@
 
 # grab the commit message
 open(MSG, ">.msg") or die "Cannot open .msg for writing";
-print MSG $opt_m;
+if ($opt_m) {
+    print MSG $opt_m;
+}
+print MSG $msg;
+if ($opt_a) {
+    print MSG "\n\nAuthor: $author\n";
+    if ($author ne $committer) {
+	print MSG "Committer: $committer\n";
+    }
+}
 close MSG;
 
-`git-cat-file commit $commit | sed -e '1,/^\$/d' >> .msg`;
-$? && die "Error extracting the commit message";
-
 my (@afiles, @dfiles, @mfiles, @dirs);
 my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit);
 #print @files;
@@ -179,7 +204,7 @@
 @bfiles = map { chomp } @bfiles;
 foreach my $f (@bfiles) {
     # check that the file in cvs matches the "old" file
-    # extract the file to $tmpdir and comparre with cmp
+    # extract the file to $tmpdir and compare with cmp
     my $tree = safe_pipe_capture('git-rev-parse', "$parent^{tree}");
     chomp $tree;
     my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`;
@@ -233,6 +258,7 @@
 }
 
 print "Commit to CVS\n";
+print "Patch: $title\n";
 my $commitfiles = join(' ', @afiles, @mfiles, @dfiles);
 my $cmd = "cvs commit -F .msg $commitfiles";
 
@@ -273,7 +299,7 @@
     }
 }
 
-# An alterative to `command` that allows input to be passed as an array
+# An alternative to `command` that allows input to be passed as an array
 # to work around shell problems with weird characters in arguments
 # if the exec returns non-zero we die
 sub safe_pipe_capture {
diff --git a/git-cvsserver.perl b/git-cvsserver.perl
index 5ccca4f..2130d57 100755
--- a/git-cvsserver.perl
+++ b/git-cvsserver.perl
@@ -81,7 +81,7 @@
 
 # $state holds all the bits of information the clients sends us that could
 # potentially be useful when it comes to actually _doing_ something.
-my $state = {};
+my $state = { prependdir => '' };
 $log->info("--------------- STARTING -----------------");
 
 my $TEMP_DIR = tempdir( CLEANUP => 1 );
@@ -547,12 +547,15 @@
 {
     my ( $cmd, $data ) = @_;
 
-    # TODO :  Not quite sure how Argument and Argumentx differ, but I assume
-    # it's for multi-line arguments ... somehow ...
+    # Argumentx means: append to last Argument (with a newline in front)
 
     $log->debug("$cmd : $data");
 
-    push @{$state->{arguments}}, $data;
+    if ( $cmd eq 'Argumentx') {
+        ${$state->{arguments}}[$#{$state->{arguments}}] .= "\n" . $data;
+    } else {
+        push @{$state->{arguments}}, $data;
+    }
 }
 
 # expand-modules \n
@@ -779,7 +782,7 @@
 
     #$log->debug("update state : " . Dumper($state));
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1031,7 +1034,7 @@
 
     my @committedfiles = ();
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         my $committedfile = $filename;
@@ -1139,13 +1142,11 @@
         exit;
     }
 
-    open FILE, ">", "$ENV{GIT_DIR}refs/heads/$state->{module}";
-    print FILE $commithash;
-    close FILE;
+    print LOCKFILE $commithash;
 
     $updater->update();
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @committedfiles )
     {
         $filename = filecleanup($filename);
@@ -1168,7 +1169,9 @@
     }
 
     close LOCKFILE;
-    unlink($lockfile);
+    my $reffile = "$ENV{GIT_DIR}refs/heads/$state->{module}";
+    unlink($reffile);
+    rename($lockfile, $reffile);
     chdir "/";
 
     print "ok\n";
@@ -1190,7 +1193,7 @@
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1291,7 +1294,7 @@
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1433,7 +1436,7 @@
     # if no files were specified, we need to work out what files we should be providing status on ...
     argsfromdir($updater);
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -1519,7 +1522,7 @@
 
     chdir $tmpdir;
 
-    # foreach file specified on the commandline ...
+    # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
         $filename = filecleanup($filename);
@@ -2129,12 +2132,6 @@
     # first lets get the commit list
     $ENV{GIT_DIR} = $self->{git_path};
 
-    # prepare database queries
-    my $db_insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-    my $db_insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
-    my $db_delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
-    my $db_insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
-
     my $commitinfo = `git-cat-file commit $self->{module} 2>&1`;
     unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
     {
@@ -2323,7 +2320,7 @@
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "M" )
                 {
@@ -2337,7 +2334,7 @@
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 elsif ( $3 eq "A" )
                 {
@@ -2351,7 +2348,7 @@
                         author => $commit->{author},
                         mode => $git_perms,
                     };
-                    $db_insert_rev->execute($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($4, $head->{$4}{revision}, $2, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
                 else
                 {
@@ -2408,7 +2405,7 @@
                     };
 
 
-                    $db_insert_rev->execute($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
+                    $self->insert_rev($git_filename, $newrevision, $git_hash, $commit->{hash}, $commit->{date}, $commit->{author}, $git_perms);
                 }
             }
             close FILELIST;
@@ -2424,7 +2421,7 @@
                     $head->{$file}{modified} = $commit->{date};
                     $head->{$file}{author} = $commit->{author};
 
-                    $db_insert_rev->execute($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $commit->{date}, $commit->{author}, $head->{$file}{mode});
+                    $self->insert_rev($file, $head->{$file}{revision}, $head->{$file}{filehash}, $commit->{hash}, $commit->{date}, $commit->{author}, $head->{$file}{mode});
                 }
             }
             # END : "Detect deleted files"
@@ -2433,7 +2430,7 @@
 
         if (exists $commit->{mergemsg})
         {
-            $db_insert_mergelog->execute($commit->{hash}, $commit->{mergemsg});
+            $self->insert_mergelog($commit->{hash}, $commit->{mergemsg});
         }
 
         $lastpicked = $commit->{hash};
@@ -2441,10 +2438,10 @@
         $self->_set_prop("last_commit", $commit->{hash});
     }
 
-    $db_delete_head->execute();
+    $self->delete_head();
     foreach my $file ( keys %$head )
     {
-        $db_insert_head->execute(
+        $self->insert_head(
             $file,
             $head->{$file}{revision},
             $head->{$file}{filehash},
@@ -2462,6 +2459,54 @@
     $self->{dbh}->commit() or die "Failed to commit changes to SQLite";
 }
 
+sub insert_rev
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_rev = $self->{dbh}->prepare_cached("INSERT INTO revision (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_rev->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
+sub insert_mergelog
+{
+    my $self = shift;
+    my $key = shift;
+    my $value = shift;
+
+    my $insert_mergelog = $self->{dbh}->prepare_cached("INSERT INTO commitmsgs (key, value) VALUES (?,?)",{},1);
+    $insert_mergelog->execute($key, $value);
+}
+
+sub delete_head
+{
+    my $self = shift;
+
+    my $delete_head = $self->{dbh}->prepare_cached("DELETE FROM head",{},1);
+    $delete_head->execute();
+}
+
+sub insert_head
+{
+    my $self = shift;
+    my $name = shift;
+    my $revision = shift;
+    my $filehash = shift;
+    my $commithash = shift;
+    my $modified = shift;
+    my $author = shift;
+    my $mode = shift;
+
+    my $insert_head = $self->{dbh}->prepare_cached("INSERT INTO head (name, revision, filehash, commithash, modified, author, mode) VALUES (?,?,?,?,?,?,?)",{},1);
+    $insert_head->execute($name, $revision, $filehash, $commithash, $modified, $author, $mode);
+}
+
 sub _headrev
 {
     my $self = shift;
diff --git a/git-fetch.sh b/git-fetch.sh
index 48818f8..c2eebee 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -11,6 +11,7 @@
 '
 IFS="$LF"
 
+rloga=fetch
 no_tags=
 tags=
 append=
@@ -19,6 +20,7 @@
 update_head_ok=
 exec=
 upload_pack=
+keep=--thin
 while case "$#" in 0) break ;; esac
 do
 	case "$1" in
@@ -51,6 +53,9 @@
 	-k|--k|--ke|--kee|--keep)
 		keep=--keep
 		;;
+	--reflog-action=*)
+		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		;;
 	-*)
 		usage
 		;;
@@ -65,7 +70,8 @@
 0)
 	test -f "$GIT_DIR/branches/origin" ||
 		test -f "$GIT_DIR/remotes/origin" ||
-			die "Where do you want to fetch from today?"
+			git-repo-config --get remote.origin.url >/dev/null ||
+				die "Where do you want to fetch from today?"
 	set origin ;;
 esac
 
@@ -75,6 +81,9 @@
 rref=
 rsync_slurped_objects=
 
+rloga="$rloga $remote_nick"
+test "$remote_nick" = "$remote" || rloga="$rloga $remote"
+
 if test "" = "$append"
 then
 	: >"$GIT_DIR/FETCH_HEAD"
@@ -146,14 +155,15 @@
 	then
 		if now_=$(cat "$GIT_DIR/$1") && test "$now_" = "$2"
 		then
-			[ "$verbose" ] && echo >&2 "* $1: same as $3"
+			[ "$verbose" ] && echo >&2 "* $1: same as $3" ||:
 		else
 			echo >&2 "* $1: updating with $3"
+			git-update-ref -m "$rloga: updating tag" "$1" "$2"
 		fi
 	else
 		echo >&2 "* $1: storing $3"
+		git-update-ref -m "$rloga: storing tag" "$1" "$2"
 	fi
-	git-update-ref "$1" "$2"
 	;;
 
     refs/heads/* | refs/remotes/*)
@@ -174,7 +184,7 @@
 	    *,$local)
 		echo >&2 "* $1: fast forward to $3"
 		echo >&2 "  from $local to $2"
-		git-update-ref "$1" "$2" "$local"
+		git-update-ref -m "$rloga: fast-forward" "$1" "$2" "$local"
 		;;
 	    *)
 		false
@@ -184,7 +194,7 @@
 		case ",$force,$single_force," in
 		*,t,*)
 			echo >&2 "  forcing update."
-			git-update-ref "$1" "$2" "$local"
+			git-update-ref -m "$rloga: forced-update" "$1" "$2" "$local"
 			;;
 		*)
 			echo >&2 "  not updating."
@@ -194,7 +204,7 @@
 	    }
 	else
 	    echo >&2 "* $1: storing $3"
-	    git-update-ref "$1" "$2"
+	    git-update-ref -m "$rloga: storing head" "$1" "$2"
 	fi
 	;;
     esac
@@ -215,9 +225,16 @@
 if test "$tags"
 then
 	taglist=`IFS="	" &&
-		  git-ls-remote $upload_pack --tags "$remote" |
+		  (
+			git-ls-remote $upload_pack --tags "$remote" ||
+			echo fail ouch
+		  ) |
 	          while read sha1 name
 		  do
+			case "$sha1" in
+			fail)
+				exit 1
+			esac
 			case "$name" in
 			*^*) continue ;;
 			esac
@@ -227,7 +244,7 @@
 			else
 			    echo >&2 "warning: tag ${name} ignored"
 			fi
-		  done`
+		  done` || exit
 	if test "$#" -gt 1
 	then
 		# remote URL plus explicit refspecs; we need to merge them.
@@ -278,7 +295,7 @@
 	  head="ref: $remote_name"
 	  while (expr "z$head" : "zref:" && expr $depth \< $max_depth) >/dev/null
 	  do
-	    remote_name_quoted=$(perl -e '
+	    remote_name_quoted=$(@@PERL@@ -e '
 	      my $u = $ARGV[0];
               $u =~ s/^ref:\s*//;
 	      $u =~ s{([^-a-zA-Z0-9/.])}{sprintf"%%%02x",ord($1)}eg;
@@ -339,7 +356,7 @@
     ( : subshell because we muck with IFS
       IFS=" 	$LF"
       (
-	  git-fetch-pack $exec $keep --thin "$remote" $rref || echo failed "$remote"
+	  git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
       ) |
       while read sha1 remote_name
       do
@@ -422,7 +439,9 @@
 	curr_head=$(git-rev-parse --verify HEAD 2>/dev/null)
 	if test "$curr_head" != "$orig_head"
 	then
-	    	git-update-ref HEAD "$orig_head"
+	    git-update-ref \
+			-m "$rloga: Undoing incorrectly fetched HEAD." \
+			HEAD "$orig_head"
 		die "Cannot fetch into the current branch."
 	fi
 	;;
diff --git a/git-fmt-merge-msg.perl b/git-fmt-merge-msg.perl
deleted file mode 100755
index 5986e54..0000000
--- a/git-fmt-merge-msg.perl
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/perl -w
-#
-# Copyright (c) 2005 Junio C Hamano
-#
-# Read .git/FETCH_HEAD and make a human readable merge message
-# by grouping branches and tags together to form a single line.
-
-use strict;
-
-my @src;
-my %src;
-sub andjoin {
-	my ($label, $labels, $stuff) = @_;
-	my $l = scalar @$stuff;
-	my $m = '';
-	if ($l == 0) {
-		return ();
-	}
-	if ($l == 1) {
-		$m = "$label$stuff->[0]";
-	}
-	else {
-		$m = ("$labels" .
-		      join (', ', @{$stuff}[0..$l-2]) .
-		      " and $stuff->[-1]");
-	}
-	return ($m);
-}
-
-sub repoconfig {
-	my ($val) = qx{git-repo-config --get merge.summary};
-	return $val;
-}
-
-sub current_branch {
-	my ($bra) = qx{git-symbolic-ref HEAD};
-	chomp($bra);
-	$bra =~ s|^refs/heads/||;
-	if ($bra ne 'master') {
-		$bra = " into $bra";
-	} else {
-		$bra = "";
-	}
-	return $bra;
-}
-
-sub shortlog {
-	my ($tip) = @_;
-	my @result;
-	foreach ( qx{git-log --no-merges --topo-order --pretty=oneline $tip ^HEAD} ) {
-		s/^[0-9a-f]{40}\s+//;
-		push @result, $_;
-	}
-	die "git-log failed\n" if $?;
-	return @result;
-}
-
-my @origin = ();
-while (<>) {
-	my ($bname, $tname, $gname, $src, $sha1, $origin);
-	chomp;
-	s/^([0-9a-f]*)	//;
-	$sha1 = $1;
-	next if (/^not-for-merge/);
-	s/^	//;
-	if (s/ of (.*)$//) {
-		$src = $1;
-	} else {
-		# Pulling HEAD
-		$src = $_;
-		$_ = 'HEAD';
-	}
-	if (! exists $src{$src}) {
-		push @src, $src;
-		$src{$src} = {
-			BRANCH => [],
-			TAG => [],
-			R_BRANCH => [],
-			GENERIC => [],
-			# &1 == has HEAD.
-			# &2 == has others.
-			HEAD_STATUS => 0,
-		};
-	}
-	if (/^branch (.*)$/) {
-		$origin = $1;
-		push @{$src{$src}{BRANCH}}, $1;
-		$src{$src}{HEAD_STATUS} |= 2;
-	}
-	elsif (/^tag (.*)$/) {
-		$origin = $_;
-		push @{$src{$src}{TAG}}, $1;
-		$src{$src}{HEAD_STATUS} |= 2;
-	}
-	elsif (/^remote branch (.*)$/) {
-		$origin = $1;
-		push @{$src{$src}{R_BRANCH}}, $1;
-		$src{$src}{HEAD_STATUS} |= 2;
-	}
-	elsif (/^HEAD$/) {
-		$origin = $src;
-		$src{$src}{HEAD_STATUS} |= 1;
-	}
-	else {
-		push @{$src{$src}{GENERIC}}, $_;
-		$src{$src}{HEAD_STATUS} |= 2;
-		$origin = $src;
-	}
-	if ($src eq '.' || $src eq $origin) {
-		$origin =~ s/^'(.*)'$/$1/;
-		push @origin, [$sha1, "$origin"];
-	}
-	else {
-		push @origin, [$sha1, "$origin of $src"];
-	}
-}
-
-my @msg;
-for my $src (@src) {
-	if ($src{$src}{HEAD_STATUS} == 1) {
-		# Only HEAD is fetched, nothing else.
-		push @msg, $src;
-		next;
-	}
-	my @this;
-	if ($src{$src}{HEAD_STATUS} == 3) {
-		# HEAD is fetched among others.
-		push @this, andjoin('', '', ['HEAD']);
-	}
-	push @this, andjoin("branch ", "branches ",
-			   $src{$src}{BRANCH});
-	push @this, andjoin("remote branch ", "remote branches ",
-			   $src{$src}{R_BRANCH});
-	push @this, andjoin("tag ", "tags ",
-			   $src{$src}{TAG});
-	push @this, andjoin("commit ", "commits ",
-			    $src{$src}{GENERIC});
-	my $this = join(', ', @this);
-	if ($src ne '.') {
-		$this .= " of $src";
-	}
-	push @msg, $this;
-}
-
-my $into = current_branch();
-
-print "Merge ", join("; ", @msg), $into, "\n";
-
-if (!repoconfig) {
-	exit(0);
-}
-
-# We limit the merge message to the latst 20 or so per each branch.
-my $limit = 20;
-
-for (@origin) {
-	my ($sha1, $name) = @$_;
-	my @log = shortlog($sha1);
-	if ($limit + 1 <= @log) {
-		print "\n* $name: (" . scalar(@log) . " commits)\n";
-	}
-	else {
-		print "\n* $name:\n";
-	}
-	my $cnt = 0;
-	for my $log (@log) {
-		if ($limit < ++$cnt) {
-			print "  ...\n";
-			last;
-		}
-		print "  $log";
-	}
-}
diff --git a/git-instaweb.sh b/git-instaweb.sh
new file mode 100755
index 0000000..16cd351
--- /dev/null
+++ b/git-instaweb.sh
@@ -0,0 +1,242 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+USAGE='[--start] [--stop] [--restart]
+  [--local] [--httpd=<httpd>] [--port=<port>] [--browser=<browser>]
+  [--module-path=<path> (for Apache2 only)]'
+
+. git-sh-setup
+
+case "$GIT_DIR" in
+/*)
+	fqgitdir="$GIT_DIR" ;;
+*)
+	fqgitdir="$PWD/$GIT_DIR" ;;
+esac
+
+local="`git repo-config --bool --get instaweb.local`"
+httpd="`git repo-config --get instaweb.httpd`"
+browser="`git repo-config --get instaweb.browser`"
+port=`git repo-config --get instaweb.port`
+module_path="`git repo-config --get instaweb.modulepath`"
+
+conf=$GIT_DIR/gitweb/httpd.conf
+
+# Defaults:
+
+# if installed, it doesn't need further configuration (module_path)
+test -z "$httpd" && httpd='lighttpd -f'
+
+# probably the most popular browser among gitweb users
+test -z "$browser" && browser='firefox'
+
+# any untaken local port will do...
+test -z "$port" && port=1234
+
+start_httpd () {
+	httpd_only="`echo $httpd | cut -f1 -d' '`"
+	if test "`expr index $httpd_only /`" -eq '1' || \
+				which $httpd_only >/dev/null
+	then
+		$httpd $fqgitdir/gitweb/httpd.conf
+	else
+		# many httpds are installed in /usr/sbin or /usr/local/sbin
+		# these days and those are not in most users $PATHs
+		for i in /usr/local/sbin /usr/sbin
+		do
+			if test -x "$i/$httpd_only"
+			then
+				# don't quote $httpd, there can be
+				# arguments to it (-f)
+				$i/$httpd "$fqgitdir/gitweb/httpd.conf"
+				return
+			fi
+		done
+	fi
+	if test $? != 0; then
+		echo "Could not execute http daemon $httpd."
+		exit 1
+	fi
+}
+
+stop_httpd () {
+	test -f "$fqgitdir/pid" && kill `cat "$fqgitdir/pid"`
+}
+
+while case "$#" in 0) break ;; esac
+do
+	case "$1" in
+	--stop|stop)
+		stop_httpd
+		exit 0
+		;;
+	--start|start)
+		start_httpd
+		exit 0
+		;;
+	--restart|restart)
+		stop_httpd
+		start_httpd
+		exit 0
+		;;
+	--local|-l)
+		local=true
+		;;
+	-d|--httpd|--httpd=*)
+		case "$#,$1" in
+		*,*=*)
+			httpd=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			httpd="$2"
+			shift ;;
+		esac
+		;;
+	-b|--browser|--browser=*)
+		case "$#,$1" in
+		*,*=*)
+			browser=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			browser="$2"
+			shift ;;
+		esac
+		;;
+	-p|--port|--port=*)
+		case "$#,$1" in
+		*,*=*)
+			port=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			port="$2"
+			shift ;;
+		esac
+		;;
+	-m|--module-path=*|--module-path)
+		case "$#,$1" in
+		*,*=*)
+			module_path=`expr "$1" : '-[^=]*=\(.*\)'` ;;
+		1,*)
+			usage ;;
+		*)
+			module_path="$2"
+			shift ;;
+		esac
+		;;
+	*)
+		usage
+		;;
+	esac
+	shift
+done
+
+mkdir -p "$GIT_DIR/gitweb/tmp"
+GIT_EXEC_PATH="`git --exec-path`"
+GIT_DIR="$fqgitdir"
+export GIT_EXEC_PATH GIT_DIR
+
+
+lighttpd_conf () {
+	cat > "$conf" <<EOF
+server.document-root = "$fqgitdir/gitweb"
+server.port = $port
+server.modules = ( "mod_cgi" )
+server.indexfiles = ( "gitweb.cgi" )
+server.pid-file = "$fqgitdir/pid"
+cgi.assign = ( ".cgi" => "" )
+mimetype.assign = ( ".css" => "text/css" )
+EOF
+	test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf"
+}
+
+apache2_conf () {
+	test -z "$module_path" && module_path=/usr/lib/apache2/modules
+	mkdir -p "$GIT_DIR/gitweb/logs"
+	bind=
+	test "$local" = true && bind='127.0.0.1:'
+	echo 'text/css css' > $fqgitdir/mime.types
+	cat > "$conf" <<EOF
+ServerRoot "$fqgitdir/gitweb"
+DocumentRoot "$fqgitdir/gitweb"
+PidFile "$fqgitdir/pid"
+Listen $bind$port
+TypesConfig $fqgitdir/mime.types
+DirectoryIndex gitweb.cgi
+EOF
+
+	# check to see if Dennis Stosberg's mod_perl compatibility patch
+	# (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
+	if test -f "$module_path/mod_perl.so" && grep '^our $gitbin' \
+				"$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
+	then
+		# favor mod_perl if available
+		cat >> "$conf" <<EOF
+LoadModule perl_module $module_path/mod_perl.so
+PerlPassEnv GIT_DIR
+PerlPassEnv GIT_EXEC_DIR
+<Location /gitweb.cgi>
+	SetHandler perl-script
+	PerlResponseHandler ModPerl::Registry
+	PerlOptions +ParseHeaders
+	Options +ExecCGI
+</Location>
+EOF
+	else
+		# plain-old CGI
+		list_mods=`echo "$httpd" | sed "s/-f$/-l/"`
+		$list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
+		echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+		cat >> "$conf" <<EOF
+AddHandler cgi-script .cgi
+<Location /gitweb.cgi>
+	Options +ExecCGI
+</Location>
+EOF
+	fi
+}
+
+script='
+s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";#
+s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";#
+s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
+s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
+
+gitweb_cgi () {
+	cat > "$1.tmp" <<\EOFGITWEB
+@@GITWEB_CGI@@
+EOFGITWEB
+	sed "$script" "$1.tmp"  > "$1"
+	chmod +x "$1"
+	rm -f "$1.tmp"
+}
+
+gitweb_css () {
+	cat > "$1" <<\EOFGITWEB
+@@GITWEB_CSS@@
+EOFGITWEB
+}
+
+gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi
+gitweb_css $GIT_DIR/gitweb/gitweb.css
+
+case "$httpd" in
+*lighttpd*)
+	lighttpd_conf
+	;;
+*apache2*)
+	apache2_conf
+	;;
+*)
+	echo "Unknown httpd specified: $httpd"
+	exit 1
+	;;
+esac
+
+start_httpd
+test -z "$browser" && browser=echo
+url=http://127.0.0.1:$port
+$browser $url || echo $url
diff --git a/git-lost-found.sh b/git-lost-found.sh
index ba6d587..b928f2c 100755
--- a/git-lost-found.sh
+++ b/git-lost-found.sh
@@ -12,7 +12,7 @@
 laf="$GIT_DIR/lost-found"
 rm -fr "$laf" && mkdir -p "$laf/commit" "$laf/other" || exit
 
-git fsck-objects |
+git fsck-objects --full |
 while read dangling type sha1
 do
 	case "$dangling" in
diff --git a/git-merge-one-file.sh b/git-merge-one-file.sh
index 5619409..fba4b0c 100755
--- a/git-merge-one-file.sh
+++ b/git-merge-one-file.sh
@@ -8,7 +8,7 @@
 #   $2 - file in branch1 SHA1 (or empty)
 #   $3 - file in branch2 SHA1 (or empty)
 #   $4 - pathname in repository
-#   $5 - orignal file mode (or empty)
+#   $5 - original file mode (or empty)
 #   $6 - file in branch1 mode (or empty)
 #   $7 - file in branch2 mode (or empty)
 #
diff --git a/git-merge-recursive.py b/git-merge-recursive.py
index ce8a31f..4039435 100755
--- a/git-merge-recursive.py
+++ b/git-merge-recursive.py
@@ -47,7 +47,7 @@
 
 def merge(h1, h2, branch1Name, branch2Name, graph, callDepth=0, ancestor=None):
     '''Merge the commits h1 and h2, return the resulting virtual
-    commit object and a flag indicating the cleaness of the merge.'''
+    commit object and a flag indicating the cleanness of the merge.'''
     assert(isinstance(h1, Commit) and isinstance(h2, Commit))
 
     global outputIndent
diff --git a/git-merge.sh b/git-merge.sh
index 24e3b50..a9cfafb 100755
--- a/git-merge.sh
+++ b/git-merge.sh
@@ -58,7 +58,13 @@
 }
 
 finish () {
-	test '' = "$2" || echo "$2"
+	if test '' = "$2"
+	then
+		rlogm="$rloga"
+	else
+		echo "$2"
+		rlogm="$rloga: $2"
+	fi
 	case "$squash" in
 	t)
 		echo "Squash commit -- not updating HEAD"
@@ -70,7 +76,7 @@
 			echo "No merge message -- not updating HEAD"
 			;;
 		*)
-			git-update-ref HEAD "$1" "$head" || exit 1
+			git-update-ref -m "$rlogm" HEAD "$1" "$head" || exit 1
 			;;
 		esac
 		;;
@@ -88,6 +94,7 @@
 	esac
 }
 
+rloga=
 while case "$#" in 0) break ;; esac
 do
 	case "$1" in
@@ -117,6 +124,9 @@
 			die "available strategies are: $all_strategies" ;;
 		esac
 		;;
+	--reflog-action=*)
+		rloga=`expr "z$1" : 'z-[^=]*=\(.*\)'`
+		;;
 	-*)	usage ;;
 	*)	break ;;
 	esac
@@ -131,6 +141,7 @@
 
 # All the rest are remote heads
 test "$#" = 0 && usage ;# we need at least one remote head.
+test "$rloga" = '' && rloga="merge: $@"
 
 remoteheads=
 for remote
@@ -316,7 +327,7 @@
 then
     parents=$(git-show-branch --independent "$head" "$@" | sed -e 's/^/-p /')
     result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree $parents) || exit
-    finish "$result_commit" "Merge $result_commit, made by $wt_strategy."
+    finish "$result_commit" "Merge made by $wt_strategy."
     dropsave
     exit 0
 fi
diff --git a/git-mv.perl b/git-mv.perl
deleted file mode 100755
index 75aa8fe..0000000
--- a/git-mv.perl
+++ /dev/null
@@ -1,250 +0,0 @@
-#!/usr/bin/perl
-#
-# Copyright 2005, Ryan Anderson <ryan@michonline.com>
-#                 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
-#
-# This file is licensed under the GPL v2, or a later version
-# at the discretion of Linus Torvalds.
-
-
-use warnings;
-use strict;
-use Getopt::Std;
-
-sub usage() {
-	print <<EOT;
-$0 [-f] [-n] <source> <destination>
-$0 [-f] [-n] [-k] <source> ... <destination directory>
-EOT
-	exit(1);
-}
-
-our ($opt_n, $opt_f, $opt_h, $opt_k, $opt_v);
-getopts("hnfkv") || usage;
-usage() if $opt_h;
-@ARGV >= 1 or usage;
-
-my $GIT_DIR = `git rev-parse --git-dir`;
-exit 1 if $?; # rev-parse would have given "not a git dir" message.
-chomp($GIT_DIR);
-
-my (@srcArgs, @dstArgs, @srcs, @dsts);
-my ($src, $dst, $base, $dstDir);
-
-# remove any trailing slash in arguments
-for (@ARGV) { s/\/*$//; }
-
-my $argCount = scalar @ARGV;
-if (-d $ARGV[$argCount-1]) {
-	$dstDir = $ARGV[$argCount-1];
-	@srcArgs = @ARGV[0..$argCount-2];
-
-	foreach $src (@srcArgs) {
-		$base = $src;
-		$base =~ s/^.*\///;
-		$dst = "$dstDir/". $base;
-		push @dstArgs, $dst;
-	}
-}
-else {
-    if ($argCount < 2) {
-	print "Error: need at least two arguments\n";
-	exit(1);
-    }
-    if ($argCount > 2) {
-	print "Error: moving to directory '"
-	    . $ARGV[$argCount-1]
-	    . "' not possible; not existing\n";
-	exit(1);
-    }
-    @srcArgs = ($ARGV[0]);
-    @dstArgs = ($ARGV[1]);
-    $dstDir = "";
-}
-
-my $subdir_prefix = `git rev-parse --show-prefix`;
-chomp($subdir_prefix);
-
-# run in git base directory, so that git-ls-files lists all revisioned files
-chdir "$GIT_DIR/..";
-
-# normalize paths, needed to compare against versioned files and update-index
-# also, this is nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
-for (@srcArgs, @dstArgs) {
-    # prepend git prefix as we run from base directory
-    $_ = $subdir_prefix.$_;
-    s|^\./||;
-    s|/\./|/| while (m|/\./|);
-    s|//+|/|g;
-    # Also "a/b/../c" ==> "a/c"
-    1 while (s,(^|/)[^/]+/\.\./,$1,);
-}
-
-my (@allfiles,@srcfiles,@dstfiles);
-my $safesrc;
-my (%overwritten, %srcForDst);
-
-$/ = "\0";
-open(F, 'git-ls-files -z |')
-        or die "Failed to open pipe from git-ls-files: " . $!;
-
-@allfiles = map { chomp; $_; } <F>;
-close(F);
-
-
-my ($i, $bad);
-while(scalar @srcArgs > 0) {
-    $src = shift @srcArgs;
-    $dst = shift @dstArgs;
-    $bad = "";
-
-    for ($src, $dst) {
-	# Be nicer to end-users by doing ".//a/./b/.//./c" ==> "a/b/c"
-	s|^\./||;
-	s|/\./|/| while (m|/\./|);
-	s|//+|/|g;
-	# Also "a/b/../c" ==> "a/c"
-	1 while (s,(^|/)[^/]+/\.\./,$1,);
-    }
-
-    if ($opt_v) {
-	print "Checking rename of '$src' to '$dst'\n";
-    }
-
-    unless (-f $src || -l $src || -d $src) {
-	$bad = "bad source '$src'";
-    }
-
-    $safesrc = quotemeta($src);
-    @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
-
-    $overwritten{$dst} = 0;
-    if (($bad eq "") && -e $dst) {
-	$bad = "destination '$dst' already exists";
-	if ($opt_f) {
-	    # only files can overwrite each other: check both source and destination
-	    if (-f $dst && (scalar @srcfiles == 1)) {
-		print "Warning: $bad; will overwrite!\n";
-		$bad = "";
-		$overwritten{$dst} = 1;
-	    }
-	    else {
-		$bad = "Can not overwrite '$src' with '$dst'";
-	    }
-	}
-    }
-    
-    if (($bad eq "") && ($dst =~ /^$safesrc\//)) {
-	$bad = "can not move directory '$src' into itself";
-    }
-
-    if ($bad eq "") {
-        if (scalar @srcfiles == 0) {
-	    $bad = "'$src' not under version control";
-	}
-    }
-
-    if ($bad eq "") {
-       if (defined $srcForDst{$dst}) {
-           $bad = "can not move '$src' to '$dst'; already target of ";
-           $bad .= "'".$srcForDst{$dst}."'";
-       }
-       else {
-           $srcForDst{$dst} = $src;
-       }
-    }
-
-    if ($bad ne "") {
-	if ($opt_k) {
-	    print "Warning: $bad; skipping\n";
-	    next;
-	}
-	print "Error: $bad\n";
-	exit(1);
-    }
-    push @srcs, $src;
-    push @dsts, $dst;
-}
-
-# Final pass: rename/move
-my (@deletedfiles,@addedfiles,@changedfiles);
-$bad = "";
-while(scalar @srcs > 0) {
-    $src = shift @srcs;
-    $dst = shift @dsts;
-
-    if ($opt_n || $opt_v) { print "Renaming $src to $dst\n"; }
-    if (!$opt_n) {
-	if (!rename($src,$dst)) {
-	    $bad = "renaming '$src' failed: $!";
-	    if ($opt_k) {
-		print "Warning: skipped: $bad\n";
-		$bad = "";
-		next;
-	    }
-	    last;
-	}
-    }
-
-    $safesrc = quotemeta($src);
-    @srcfiles = grep /^$safesrc(\/|$)/, @allfiles;
-    @dstfiles = @srcfiles;
-    s/^$safesrc(\/|$)/$dst$1/ for @dstfiles;
-
-    push @deletedfiles, @srcfiles;
-    if (scalar @srcfiles == 1) {
-	# $dst can be a directory with 1 file inside
-	if ($overwritten{$dst} ==1) {
-	    push @changedfiles, $dstfiles[0];
-
-	} else {
-	    push @addedfiles, $dstfiles[0];
-	}
-    }
-    else {
-	push @addedfiles, @dstfiles;
-    }
-}
-
-if ($opt_n) {
-    if (@changedfiles) {
-	print "Changed  : ". join(", ", @changedfiles) ."\n";
-    }
-    if (@addedfiles) {
-	print "Adding   : ". join(", ", @addedfiles) ."\n";
-    }
-    if (@deletedfiles) {
-	print "Deleting : ". join(", ", @deletedfiles) ."\n";
-    }
-}
-else {
-    if (@changedfiles) {
-	open(H, "| git-update-index -z --stdin")
-		or die "git-update-index failed to update changed files with code $!\n";
-	foreach my $fileName (@changedfiles) {
-		print H "$fileName\0";
-	}
-	close(H);
-    }
-    if (@addedfiles) {
-	open(H, "| git-update-index --add -z --stdin")
-		or die "git-update-index failed to add new names with code $!\n";
-	foreach my $fileName (@addedfiles) {
-		print H "$fileName\0";
-	}
-	close(H);
-    }
-    if (@deletedfiles) {
-	open(H, "| git-update-index --remove -z --stdin")
-		or die "git-update-index failed to remove old names with code $!\n";
-	foreach my $fileName (@deletedfiles) {
-		print H "$fileName\0";
-	}
-	close(H);
-    }
-}
-
-if ($bad ne "") {
-    print "Error: $bad\n";
-    exit(1);
-}
diff --git a/git-prune.sh b/git-prune.sh
deleted file mode 100755
index c5a5d29..0000000
--- a/git-prune.sh
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh
-
-USAGE='[-n] [--] [<head>...]'
-. git-sh-setup
-
-dryrun=
-echo=
-while case "$#" in 0) break ;; esac
-do
-    case "$1" in
-    -n) dryrun=-n echo=echo ;;
-    --) break ;;
-    -*) usage ;;
-    *)  break ;;
-    esac
-    shift;
-done
-
-sync
-case "$#" in
-0) git-fsck-objects --full --cache --unreachable ;;
-*) git-fsck-objects --full --cache --unreachable $(git-rev-parse --all) "$@" ;;
-esac |
-
-sed -ne '/unreachable /{
-    s/unreachable [^ ][^ ]* //
-    s|\(..\)|\1/|p
-}' | {
-	cd "$GIT_OBJECT_DIRECTORY" || exit
-	xargs $echo rm -f
-	rmdir 2>/dev/null [0-9a-f][0-9a-f]
-}
-
-git-prune-packed $dryrun
-
-if redundant=$(git-pack-redundant --all 2>/dev/null) && test "" != "$redundant"
-then
-	if test "" = "$dryrun"
-	then
-		echo "$redundant" | xargs rm -f
-	else
-		echo rm -f "$redundant"
-	fi
-fi
diff --git a/git-pull.sh b/git-pull.sh
index 076785c..f380437 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -45,7 +45,7 @@
 done
 
 orig_head=$(git-rev-parse --verify HEAD) || die "Pulling into a black hole?"
-git-fetch --update-head-ok "$@" || exit 1
+git-fetch --update-head-ok --reflog-action=pull "$@" || exit 1
 
 curr_head=$(git-rev-parse --verify HEAD)
 if test "$curr_head" != "$orig_head"
@@ -102,5 +102,6 @@
 esac
 
 merge_name=$(git-fmt-merge-msg <"$GIT_DIR/FETCH_HEAD") || exit
-git-merge $no_summary $no_commit $squash $strategy_args \
+git-merge "--reflog-action=pull $*" \
+	$no_summary $no_commit $squash $strategy_args \
 	"$merge_name" HEAD $merge_head
diff --git a/git-push.sh b/git-push.sh
deleted file mode 100755
index f10cadb..0000000
--- a/git-push.sh
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/bin/sh
-
-USAGE='[--all] [--tags] [--force] <repository> [<refspec>...]'
-. git-sh-setup
-
-# Parse out parameters and then stop at remote, so that we can
-# translate it using .git/branches information
-has_all=
-has_force=
-has_exec=
-has_thin=--thin
-remote=
-do_tags=
-
-while case "$#" in 0) break ;; esac
-do
-	case "$1" in
-	--all)
-		has_all=--all ;;
-	--tags)
-		do_tags=yes ;;
-	--force)
-		has_force=--force ;;
-	--exec=*)
-		has_exec="$1" ;;
-	--thin)
-		;; # noop
-	--no-thin)
-		has_thin= ;;
-	-*)
-                usage ;;
-        *)
-		set x "$@"
-		shift
-		break ;;
-	esac
-	shift
-done
-case "$#" in
-0)
-	echo "Where would you want to push today?"
-        usage ;;
-esac
-
-. git-parse-remote
-remote=$(get_remote_url "$@")
-
-case "$has_all" in
---all)
-	set x ;;
-'')
-	case "$do_tags,$#" in
-	yes,1)
-		set x $(cd "$GIT_DIR/refs" && find tags -type f -print) ;;
-	yes,*)
-		set x $(cd "$GIT_DIR/refs" && find tags -type f -print) \
-		    $(get_remote_refs_for_push "$@") ;;
-	,*)
-		set x $(get_remote_refs_for_push "$@") ;;
-	esac
-esac
-
-shift ;# away the initial 'x'
-
-# $# is now 0 if there was no explicit refspec on the command line
-# and there was no defalt refspec to push from remotes/ file.
-# we will let git-send-pack to do its "matching refs" thing.
-
-case "$remote" in
-git://*)
-	die "Cannot use READ-ONLY transport to push to $remote" ;;
-rsync://*)
-        die "Pushing with rsync transport is deprecated" ;;
-esac
-
-set x "$remote" "$@"; shift
-test "$has_all" && set x "$has_all" "$@" && shift
-test "$has_force" && set x "$has_force" "$@" && shift
-test "$has_exec" && set x "$has_exec" "$@" && shift
-test "$has_thin" && set x "$has_thin" "$@" && shift
-
-case "$remote" in
-http://* | https://*)
-	exec git-http-push "$@";;
-*)
-	exec git-send-pack "$@";;
-esac
diff --git a/git-quiltimport.sh b/git-quiltimport.sh
index 86b51ab..10135da 100755
--- a/git-quiltimport.sh
+++ b/git-quiltimport.sh
@@ -49,7 +49,7 @@
 	quilt_author_email=$(expr "z$quilt_author" : '.*<\([^>]*\)') &&
 	test '' != "$quilt_author_name" &&
 	test '' != "$quilt_author_email" ||
-	die "malformatted --author parameter"
+	die "malformed --author parameter"
 fi
 
 # Quilt patch directory
@@ -112,7 +112,7 @@
 		git-apply --index -C1 "$tmp_patch" &&
 		tree=$(git-write-tree) &&
 		commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
-		git-update-ref HEAD $commit || exit 4
+		git-update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
 	fi
 done
 rm -rf $tmp_dir || exit 5
diff --git a/git-rebase.sh b/git-rebase.sh
index 3945e06..7d3a5d0 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -131,7 +131,8 @@
 			finish_rb_merge
 			exit
 		fi
-		git am --resolved --3way --resolvemsg="$RESOLVEMSG"
+		git am --resolved --3way --resolvemsg="$RESOLVEMSG" \
+			--reflog-action=rebase
 		exit
 		;;
 	--skip)
@@ -150,7 +151,8 @@
 			finish_rb_merge
 			exit
 		fi
-		git am -3 --skip --resolvemsg="$RESOLVEMSG"
+		git am -3 --skip --resolvemsg="$RESOLVEMSG" \
+			--reflog-action=rebase
 		exit
 		;;
 	--abort)
@@ -264,14 +266,11 @@
 
 # Check if we are already based on $onto, but this should be
 # done only when upstream and onto are the same.
-if test "$upstream" = "$onto"
+mb=$(git-merge-base "$onto" "$branch")
+if test "$upstream" = "$onto" && test "$mb" = "$onto"
 then
-	mb=$(git-merge-base "$onto" "$branch")
-	if test "$mb" = "$onto"
-	then
-		echo >&2 "Current branch $branch_name is up to date."
-		exit 0
-	fi
+	echo >&2 "Current branch $branch_name is up to date."
+	exit 0
 fi
 
 # Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
@@ -279,16 +278,17 @@
 
 # If the $onto is a proper descendant of the tip of the branch, then
 # we just fast forwarded.
-if test "$mb" = "$onto"
+if test "$mb" = "$branch"
 then
-	echo >&2 "Fast-forwarded $branch to $newbase."
+	echo >&2 "Fast-forwarded $branch_name to $onto_name."
 	exit 0
 fi
 
 if test -z "$do_merge"
 then
 	git-format-patch -k --stdout --full-index "$upstream"..ORIG_HEAD |
-	git am --binary -3 -k --resolvemsg="$RESOLVEMSG"
+	git am --binary -3 -k --resolvemsg="$RESOLVEMSG" \
+		--reflog-action=rebase
 	exit $?
 fi
 
@@ -311,7 +311,7 @@
 
 msgnum=0
 for cmt in `git-rev-list --no-merges "$upstream"..ORIG_HEAD \
-			| perl -e 'print reverse <>'`
+			| @@PERL@@ -e 'print reverse <>'`
 do
 	msgnum=$(($msgnum + 1))
 	echo "$cmt" > "$dotest/cmt.$msgnum"
diff --git a/git-repack.sh b/git-repack.sh
index 640ad8d..9da92fb 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -43,7 +43,9 @@
 	;;
 esac
 pack_objects="$pack_objects $local $quiet $no_reuse_delta$extra"
-name=$(git-rev-list --objects --all $rev_list 2>&1 |
+name=$( { git-rev-list --objects --all $rev_list ||
+	  echo "git-rev-list died with exit code $?"
+	} |
 	git-pack-objects --non-empty $pack_objects .tmp-pack) ||
 	exit 1
 if [ -z "$name" ]; then
diff --git a/git-reset.sh b/git-reset.sh
index 46451d0..36fc8ce 100755
--- a/git-reset.sh
+++ b/git-reset.sh
@@ -17,7 +17,11 @@
         usage ;;
 esac
 
-rev=$(git-rev-parse --verify --default HEAD "$@") || exit
+case $# in
+0) rev=HEAD ;;
+1) rev=$(git-rev-parse --verify "$1") || exit ;;
+*) usage ;;
+esac
 rev=$(git-rev-parse --verify $rev^0) || exit
 
 # We need to remember the set of paths that _could_ be left
@@ -48,7 +52,8 @@
 else
 	rm -f "$GIT_DIR/ORIG_HEAD"
 fi
-git-update-ref -m "reset $reset_type $@" HEAD "$rev"
+git-update-ref -m "reset $reset_type $*" HEAD "$rev"
+update_ref_status=$?
 
 case "$reset_type" in
 --hard )
@@ -62,3 +67,5 @@
 esac
 
 rm -f "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/rr-cache/MERGE_RR" "$GIT_DIR/SQUASH_MSG"
+
+exit $update_ref_status
diff --git a/git-resolve.sh b/git-resolve.sh
index 1c7aaef..a7bc680 100755
--- a/git-resolve.sh
+++ b/git-resolve.sh
@@ -15,6 +15,7 @@
 
 head=$(git-rev-parse --verify "$1"^0) &&
 merge=$(git-rev-parse --verify "$2"^0) &&
+merge_name="$2" &&
 merge_msg="$3" || usage
 
 #
@@ -43,7 +44,8 @@
 "$head")
 	echo "Updating from $head to $merge"
 	git-read-tree -u -m $head $merge || exit 1
-	git-update-ref HEAD "$merge" "$head"
+	git-update-ref -m "resolve $merge_name: Fast forward" \
+		HEAD "$merge" "$head"
 	git-diff-tree -p $head $merge | git-apply --stat
 	dropheads
 	exit 0
@@ -100,6 +102,7 @@
 fi
 result_commit=$(echo "$merge_msg" | git-commit-tree $result_tree -p $head -p $merge)
 echo "Committed merge $result_commit"
-git-update-ref HEAD "$result_commit" "$head"
+git-update-ref -m "resolve $merge_name: In-index merge" \
+	HEAD "$result_commit" "$head"
 git-diff-tree -p $head $result_commit | git-apply --stat
 dropheads
diff --git a/git-revert.sh b/git-revert.sh
index de8b5f0..2bf35d1 100755
--- a/git-revert.sh
+++ b/git-revert.sh
@@ -84,7 +84,7 @@
 		s/^[^ ]* /Revert "/
 		s/$/"/'
 	echo
-	echo "This reverts $commit commit."
+	echo "This reverts commit $commit."
 	test "$rev" = "$commit" ||
 	echo "(original 'git revert' arguments: $@)"
 	base=$commit next=$prev
diff --git a/git-send-email.perl b/git-send-email.perl
index c5d9e73..a83c7e9 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -22,9 +22,55 @@
 use Getopt::Long;
 use Data::Dumper;
 
+package FakeTerm;
+sub new {
+	my ($class, $reason) = @_;
+	return bless \$reason, shift;
+}
+sub readline {
+	my $self = shift;
+	die "Cannot use readline on FakeTerm: $$self";
+}
+package main;
+
 # most mail servers generate the Date: header, but not all...
-$ENV{LC_ALL} = 'C';
-use POSIX qw/strftime/;
+sub format_2822_time {
+	my ($time) = @_;
+	my @localtm = localtime($time);
+	my @gmttm = gmtime($time);
+	my $localmin = $localtm[1] + $localtm[2] * 60;
+	my $gmtmin = $gmttm[1] + $gmttm[2] * 60;
+	if ($localtm[0] != $gmttm[0]) {
+		die "local zone differs from GMT by a non-minute interval\n";
+	}
+	if ((($gmttm[6] + 1) % 7) == $localtm[6]) {
+		$localmin += 1440;
+	} elsif ((($gmttm[6] - 1) % 7) == $localtm[6]) {
+		$localmin -= 1440;
+	} elsif ($gmttm[6] != $localtm[6]) {
+		die "local time offset greater than or equal to 24 hours\n";
+	}
+	my $offset = $localmin - $gmtmin;
+	my $offhour = $offset / 60;
+	my $offmin = abs($offset % 60);
+	if (abs($offhour) >= 24) {
+		die ("local time offset greater than or equal to 24 hours\n");
+	}
+
+	return sprintf("%s, %2d %s %d %02d:%02d:%02d %s%02d%02d",
+		       qw(Sun Mon Tue Wed Thu Fri Sat)[$localtm[6]],
+		       $localtm[3],
+		       qw(Jan Feb Mar Apr May Jun
+			  Jul Aug Sep Oct Nov Dec)[$localtm[4]],
+		       $localtm[5]+1900,
+		       $localtm[2],
+		       $localtm[1],
+		       $localtm[0],
+		       ($offset >= 0) ? '+' : '-',
+		       abs($offhour),
+		       $offmin,
+		       );
+}
 
 my $have_email_valid = eval { require Email::Valid; 1 };
 my $smtp;
@@ -46,7 +92,12 @@
 # Example reply to:
 #$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
 
-my $term = new Term::ReadLine 'git-send-email';
+my $term = eval {
+	new Term::ReadLine 'git-send-email';
+};
+if ($@) {
+	$term = new FakeTerm "$@: going non-interactive";
+}
 
 # Begin by accumulating all the variables (defined above), that we will end up
 # needing, first, from the command line:
@@ -310,7 +361,7 @@
    --smtp-server  If set, specifies the outgoing SMTP server to use.
                   Defaults to localhost.
 
-  --suppress-from Supress sending emails to yourself if your address
+  --suppress-from Suppress sending emails to yourself if your address
                   appears in a From: line.
 
    --quiet	Make git-send-email less verbose.  One line per email should be
@@ -371,7 +422,7 @@
 	my @recipients = unique_email_list(@to);
 	my $to = join (",\n\t", @recipients);
 	@recipients = unique_email_list(@recipients,@cc,@bcclist);
-	my $date = strftime('%a, %d %b %Y %H:%M:%S %z', localtime($time++));
+	my $date = format_2822_time($time++);
 	my $gitversion = '@@GIT_VERSION@@';
 	if ($gitversion =~ m/..GIT_VERSION../) {
 	    $gitversion = `git --version`;
@@ -384,7 +435,6 @@
 To: $to
 Cc: $cc
 Subject: $subject
-Reply-To: $from
 Date: $date
 Message-Id: $message_id
 X-Mailer: git-send-email $gitversion
diff --git a/git-sh-setup.sh b/git-sh-setup.sh
index d15747f..42f9b1c 100755
--- a/git-sh-setup.sh
+++ b/git-sh-setup.sh
@@ -35,17 +35,12 @@
 	exit
 esac
 
+# Make sure we are in a valid repository of a vintage we understand.
 if [ -z "$SUBDIRECTORY_OK" ]
 then
 	: ${GIT_DIR=.git}
-	: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
-
-	# Make sure we are in a valid repository of a vintage we understand.
-	GIT_DIR="$GIT_DIR" git repo-config --get core.nosuch >/dev/null
-	if test $? = 128
-	then
-	    exit
-	fi
+	GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || exit
 else
 	GIT_DIR=$(git-rev-parse --git-dir) || exit
 fi
+: ${GIT_OBJECT_DIRECTORY="$GIT_DIR/objects"}
diff --git a/contrib/git-svn/git-svn.perl b/git-svn.perl
similarity index 97%
rename from contrib/git-svn/git-svn.perl
rename to git-svn.perl
index 1e19aa1..0d58bb9 100755
--- a/contrib/git-svn/git-svn.perl
+++ b/git-svn.perl
@@ -8,7 +8,7 @@
 		$GIT_SVN_INDEX $GIT_SVN
 		$GIT_DIR $GIT_SVN_DIR $REVDB/;
 $AUTHOR = 'Eric Wong <normalperson@yhbt.net>';
-$VERSION = '1.1.1-broken';
+$VERSION = '@@GIT_VERSION@@';
 
 use Cwd qw/abs_path/;
 $GIT_DIR = abs_path($ENV{GIT_DIR} || '.git');
@@ -31,6 +31,7 @@
 use File::Path qw/mkpath/;
 use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev pass_through/;
 use File::Spec qw//;
+use File::Copy qw/copy/;
 use POSIX qw/strftime/;
 use IPC::Open3;
 use Memoize;
@@ -77,9 +78,6 @@
 		'copy-similarity|C=i'=> \$_cp_similarity
 );
 
-# yes, 'native' sets "\n".  Patches to fix this for non-*nix systems welcome:
-my %EOL = ( CR => "\015", LF => "\012", CRLF => "\015\012", native => "\012" );
-
 my %cmd = (
 	fetch => [ \&fetch, "Download new revisions from SVN",
 			{ 'revision|r=s' => \$_revision, %fc_opts } ],
@@ -147,7 +145,7 @@
 load_authors() if $_authors;
 load_all_refs() if $_branch_all_refs;
 svn_compat_check() unless $_use_lib;
-migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/;
+migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init|commit-diff)$/;
 $cmd{$cmd}->[0]->(@ARGV);
 exit 0;
 
@@ -760,7 +758,7 @@
 		exit 1;
 	}
 	if (defined $_file) {
-		$_message = file_to_s($_message);
+		$_message = file_to_s($_file);
 	} else {
 		$_message ||= get_commit_message($tb,
 					"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
@@ -1160,27 +1158,24 @@
 		}
 	}
 
-	my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
-	$path =~ s#^/+##;
-	my @paths = split(m#/+#, $path);
-
 	if ($_use_lib) {
-		while (1) {
-			$SVN = libsvn_connect($url);
-			last if (defined $SVN &&
-				defined eval { $SVN->get_latest_revnum });
-			my $n = shift @paths || last;
-			$url .= "/$n";
-		}
+		my $tmp = libsvn_connect($full_url);
+		my $url = $tmp->get_repos_root;
+		$full_url =~ s#^\Q$url\E/*##;
+		push @repo_path_split_cache, qr/^(\Q$url\E)/;
+		return ($url, $full_url);
 	} else {
+		my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
+		$path =~ s#^/+##;
+		my @paths = split(m#/+#, $path);
 		while (quiet_run(qw/svn ls --non-interactive/, $url)) {
 			my $n = shift @paths || last;
 			$url .= "/$n";
 		}
+		push @repo_path_split_cache, qr/^(\Q$url\E)/;
+		$path = join('/',@paths);
+		return ($url, $path);
 	}
-	push @repo_path_split_cache, qr/^(\Q$url\E)/;
-	$path = join('/',@paths);
-	return ($url, $path);
 }
 
 sub setup_git_svn {
@@ -1518,12 +1513,12 @@
 	open my $msg, '>', $commit_msg or croak $!;
 
 	chomp(my $type = `git-cat-file -t $commit`);
-	if ($type eq 'commit') {
+	if ($type eq 'commit' || $type eq 'tag') {
 		my $pid = open my $msg_fh, '-|';
 		defined $pid or croak $!;
 
 		if ($pid == 0) {
-			exec(qw(git-cat-file commit), $commit) or croak $!;
+			exec('git-cat-file', $type, $commit) or croak $!;
 		}
 		my $in_msg = 0;
 		while (<$msg_fh>) {
@@ -1760,43 +1755,6 @@
 
 sub sys { system(@_) == 0 or croak $? }
 
-sub eol_cp {
-	my ($from, $to) = @_;
-	my $es = svn_propget_base('svn:eol-style', $to);
-	open my $rfd, '<', $from or croak $!;
-	binmode $rfd or croak $!;
-	open my $wfd, '>', $to or croak $!;
-	binmode $wfd or croak $!;
-	eol_cp_fd($rfd, $wfd, $es);
-	close $rfd or croak $!;
-	close $wfd or croak $!;
-}
-
-sub eol_cp_fd {
-	my ($rfd, $wfd, $es) = @_;
-	my $eol = defined $es ? $EOL{$es} : undef;
-	my $buf;
-	use bytes;
-	while (1) {
-		my ($r, $w, $t);
-		defined($r = sysread($rfd, $buf, 4096)) or croak $!;
-		return unless $r;
-		if ($eol) {
-			if ($buf =~ /\015$/) {
-				my $c;
-				defined($r = sysread($rfd,$c,1)) or croak $!;
-				$buf .= $c if $r > 0;
-			}
-			$buf =~ s/(?:\015\012|\015|\012)/$eol/gs;
-			$r = length($buf);
-		}
-		for ($w = 0; $w < $r; $w += $t) {
-			$t = syswrite($wfd, $buf, $r - $w, $w) or croak $!;
-		}
-	}
-	no bytes;
-}
-
 sub do_update_index {
 	my ($z_cmd, $cmd, $no_text_base) = @_;
 
@@ -1824,9 +1782,11 @@
 						'text-base',"$f.svn-base");
 				$tb =~ s#^/##;
 			}
+			my @s = stat($x);
 			unlink $x or croak $!;
-			eol_cp($tb, $x);
+			copy($tb, $x);
 			chmod(($mode &~ umask), $x) or croak $!;
+			utime $s[8], $s[9], $x;
 		}
 		print $ui $x,"\0";
 	}
@@ -2429,7 +2389,7 @@
 							\s([a-f\d\-]+)$/x);
 	if (!$rev || !$uuid || !$url) {
 		# some of the original repositories I made had
-		# indentifiers like this:
+		# identifiers like this:
 		($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
 	}
 	return ($url, $rev, $uuid);
@@ -2617,7 +2577,9 @@
 sub libsvn_get_file {
 	my ($gui, $f, $rev) = @_;
 	my $p = $f;
-	return unless ($p =~ s#^\Q$SVN_PATH\E/?##);
+	if (length $SVN_PATH > 0) {
+		return unless ($p =~ s#^\Q$SVN_PATH\E/##);
+	}
 
 	my ($hash, $pid, $in, $out);
 	my $pool = SVN::Pool->new;
@@ -2664,6 +2626,7 @@
 	if (defined $_authors && ! defined $users{$author}) {
 		die "Author: $author not defined in $_authors file\n";
 	}
+	$msg = '' if ($rev == 0 && !defined $msg);
 	return { revision => $rev, date => "+0000 $Y-$m-$d $H:$M:$S",
 		author => $author, msg => $msg."\n", parents => $parents || [] }
 }
@@ -2709,6 +2672,12 @@
 			} else {
 				die "Unrecognized action: $m, ($f r$rev)\n";
 			}
+		} elsif ($t == $SVN::Node::dir && $m =~ /^[AR]$/) {
+			my @traversed = ();
+			libsvn_traverse($gui, '', $f, $rev, \@traversed);
+			foreach (@traversed) {
+				push @amr, [ $m, $_ ]
+			}
 		}
 		$pool->clear;
 	}
@@ -2778,7 +2747,7 @@
 }
 
 sub libsvn_traverse {
-	my ($gui, $pfx, $path, $rev) = @_;
+	my ($gui, $pfx, $path, $rev, $files) = @_;
 	my $cwd = "$pfx/$path";
 	my $pool = SVN::Pool->new;
 	$cwd =~ s#^/+##g;
@@ -2786,10 +2755,15 @@
 	foreach my $d (keys %$dirent) {
 		my $t = $dirent->{$d}->kind;
 		if ($t == $SVN::Node::dir) {
-			libsvn_traverse($gui, $cwd, $d, $rev);
+			libsvn_traverse($gui, $cwd, $d, $rev, $files);
 		} elsif ($t == $SVN::Node::file) {
-			print "\tA\t$cwd/$d\n" unless $_q;
-			libsvn_get_file($gui, "$cwd/$d", $rev);
+			my $file = "$cwd/$d";
+			if (defined $files) {
+				push @$files, $file;
+			} else {
+				print "\tA\t$file\n" unless $_q;
+				libsvn_get_file($gui, $file, $rev);
+			}
 		}
 	}
 	$pool->clear;
@@ -2913,9 +2887,7 @@
 	}
 	my ($paths, $rev, $author, $date, $msg) = @_;
 	open my $gui, '| git-update-index -z --index-info' or croak $!;
-	my $pool = SVN::Pool->new;
-	libsvn_traverse($gui, '', $SVN_PATH, $rev, $pool);
-	$pool->clear;
+	libsvn_traverse($gui, '', $SVN_PATH, $rev);
 	close $gui or croak $?;
 	return libsvn_log_entry($rev, $author, $date, $msg);
 }
diff --git a/git.c b/git.c
index ca8961f..18ba14a 100644
--- a/git.c
+++ b/git.c
@@ -11,9 +11,13 @@
 #include "git-compat-util.h"
 #include "exec_cmd.h"
 #include "cache.h"
+#include "quote.h"
 
 #include "builtin.h"
 
+const char git_usage_string[] =
+	"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
+
 static void prepend_to_path(const char *dir, int len)
 {
 	const char *old_path = getenv("PATH");
@@ -34,6 +38,59 @@
 	setenv("PATH", path, 1);
 }
 
+static int handle_options(const char*** argv, int* argc)
+{
+	int handled = 0;
+
+	while (*argc > 0) {
+		const char *cmd = (*argv)[0];
+		if (cmd[0] != '-')
+			break;
+
+		/*
+		 * For legacy reasons, the "version" and "help"
+		 * commands can be written with "--" prepended
+		 * to make them look like flags.
+		 */
+		if (!strcmp(cmd, "--help") || !strcmp(cmd, "--version"))
+			break;
+
+		/*
+		 * Check remaining flags.
+		 */
+		if (!strncmp(cmd, "--exec-path", 11)) {
+			cmd += 11;
+			if (*cmd == '=')
+				git_set_exec_path(cmd + 1);
+			else {
+				puts(git_exec_path());
+				exit(0);
+			}
+		} else if (!strcmp(cmd, "-p") || !strcmp(cmd, "--paginate")) {
+			setup_pager();
+		} else if (!strcmp(cmd, "--git-dir")) {
+			if (*argc < 1)
+				return -1;
+			setenv("GIT_DIR", (*argv)[1], 1);
+			(*argv)++;
+			(*argc)--;
+		} else if (!strncmp(cmd, "--git-dir=", 10)) {
+			setenv("GIT_DIR", cmd + 10, 1);
+		} else if (!strcmp(cmd, "--bare")) {
+			static char git_dir[1024];
+			setenv("GIT_DIR", getcwd(git_dir, 1024), 1);
+		} else {
+			fprintf(stderr, "Unknown option: %s\n", cmd);
+			usage(git_usage_string);
+		}
+
+		(*argv)++;
+		(*argc)--;
+		handled++;
+	}
+	return handled;
+}
+
 static const char *alias_command;
 static char *alias_string = NULL;
 
@@ -102,37 +159,48 @@
 {
 	int nongit = 0, ret = 0, saved_errno = errno;
 	const char *subdir;
+	int count, option_count;
+	const char** new_argv;
 
 	subdir = setup_git_directory_gently(&nongit);
-	if (!nongit) {
-		int count;
-		const char** new_argv;
 
-		alias_command = (*argv)[0];
-		git_config(git_alias_config);
-		if (alias_string) {
+	alias_command = (*argv)[0];
+	git_config(git_alias_config);
+	if (alias_string) {
+		count = split_cmdline(alias_string, &new_argv);
+		option_count = handle_options(&new_argv, &count);
+		memmove(new_argv - option_count, new_argv,
+				count * sizeof(char *));
+		new_argv -= option_count;
 
-			count = split_cmdline(alias_string, &new_argv);
+		if (count < 1)
+			die("empty alias for %s", alias_command);
 
-			if (count < 1)
-				die("empty alias for %s", alias_command);
+		if (!strcmp(alias_command, new_argv[0]))
+			die("recursive alias: %s", alias_command);
 
-			if (!strcmp(alias_command, new_argv[0]))
-				die("recursive alias: %s", alias_command);
-
-			/* insert after command name */
-			if (*argcp > 1) {
-				new_argv = realloc(new_argv, sizeof(char*) *
-						   (count + *argcp));
-				memcpy(new_argv + count, *argv + 1,
-				       sizeof(char*) * *argcp);
+		if (getenv("GIT_TRACE")) {
+			int i;
+			fprintf(stderr, "trace: alias expansion: %s =>",
+				alias_command);
+			for (i = 0; i < count; ++i) {
+				fputc(' ', stderr);
+				sq_quote_print(stderr, new_argv[i]);
 			}
-
-			*argv = new_argv;
-			*argcp += count - 1;
-
-			ret = 1;
+			fputc('\n', stderr);
+			fflush(stderr);
 		}
+
+		new_argv = realloc(new_argv, sizeof(char*) *
+				   (count + *argcp + 1));
+		/* insert after command name */
+		memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);
+		new_argv[count+*argcp] = NULL;
+
+		*argv = new_argv;
+		*argcp += count - 1;
+
+		ret = 1;
 	}
 
 	if (subdir)
@@ -145,49 +213,58 @@
 
 const char git_version_string[] = GIT_VERSION;
 
+#define NEEDS_PREFIX 1
+#define USE_PAGER 2
+
 static void handle_internal_command(int argc, const char **argv, char **envp)
 {
 	const char *cmd = argv[0];
 	static struct cmd_struct {
 		const char *cmd;
-		int (*fn)(int, const char **, char **);
+		int (*fn)(int, const char **, const char *);
+		int option;
 	} commands[] = {
 		{ "version", cmd_version },
 		{ "help", cmd_help },
-		{ "log", cmd_log },
-		{ "whatchanged", cmd_whatchanged },
-		{ "show", cmd_show },
-		{ "push", cmd_push },
-		{ "format-patch", cmd_format_patch },
+		{ "log", cmd_log, NEEDS_PREFIX | USE_PAGER },
+		{ "whatchanged", cmd_whatchanged, NEEDS_PREFIX | USE_PAGER },
+		{ "show", cmd_show, NEEDS_PREFIX | USE_PAGER },
+		{ "push", cmd_push, NEEDS_PREFIX },
+		{ "format-patch", cmd_format_patch, NEEDS_PREFIX },
 		{ "count-objects", cmd_count_objects },
-		{ "diff", cmd_diff },
-		{ "grep", cmd_grep },
-		{ "rm", cmd_rm },
-		{ "add", cmd_add },
-		{ "rev-list", cmd_rev_list },
+		{ "diff", cmd_diff, NEEDS_PREFIX },
+		{ "grep", cmd_grep, NEEDS_PREFIX },
+		{ "rm", cmd_rm, NEEDS_PREFIX },
+		{ "add", cmd_add, NEEDS_PREFIX },
+		{ "rev-list", cmd_rev_list, NEEDS_PREFIX },
 		{ "init-db", cmd_init_db },
 		{ "get-tar-commit-id", cmd_get_tar_commit_id },
 		{ "upload-tar", cmd_upload_tar },
 		{ "check-ref-format", cmd_check_ref_format },
-		{ "ls-files", cmd_ls_files },
-		{ "ls-tree", cmd_ls_tree },
-		{ "tar-tree", cmd_tar_tree },
-		{ "read-tree", cmd_read_tree },
-		{ "commit-tree", cmd_commit_tree },
+		{ "ls-files", cmd_ls_files, NEEDS_PREFIX },
+		{ "ls-tree", cmd_ls_tree, NEEDS_PREFIX },
+		{ "tar-tree", cmd_tar_tree, NEEDS_PREFIX },
+		{ "read-tree", cmd_read_tree, NEEDS_PREFIX },
+		{ "commit-tree", cmd_commit_tree, NEEDS_PREFIX },
 		{ "apply", cmd_apply },
-		{ "show-branch", cmd_show_branch },
-		{ "diff-files", cmd_diff_files },
-		{ "diff-index", cmd_diff_index },
-		{ "diff-stages", cmd_diff_stages },
-		{ "diff-tree", cmd_diff_tree },
-		{ "cat-file", cmd_cat_file },
-		{ "rev-parse", cmd_rev_parse },
-		{ "write-tree", cmd_write_tree },
+		{ "show-branch", cmd_show_branch, NEEDS_PREFIX },
+		{ "diff-files", cmd_diff_files, NEEDS_PREFIX },
+		{ "diff-index", cmd_diff_index, NEEDS_PREFIX },
+		{ "diff-stages", cmd_diff_stages, NEEDS_PREFIX },
+		{ "diff-tree", cmd_diff_tree, NEEDS_PREFIX },
+		{ "cat-file", cmd_cat_file, NEEDS_PREFIX },
+		{ "rev-parse", cmd_rev_parse, NEEDS_PREFIX },
+		{ "write-tree", cmd_write_tree, NEEDS_PREFIX },
 		{ "mailsplit", cmd_mailsplit },
 		{ "mailinfo", cmd_mailinfo },
 		{ "stripspace", cmd_stripspace },
-		{ "update-index", cmd_update_index },
-		{ "update-ref", cmd_update_ref }
+		{ "update-index", cmd_update_index, NEEDS_PREFIX },
+		{ "update-ref", cmd_update_ref, NEEDS_PREFIX },
+		{ "fmt-merge-msg", cmd_fmt_merge_msg, NEEDS_PREFIX },
+		{ "prune", cmd_prune, NEEDS_PREFIX },
+		{ "mv", cmd_mv, NEEDS_PREFIX },
+		{ "prune-packed", cmd_prune_packed, NEEDS_PREFIX },
+		{ "repo-config", cmd_repo_config },
 	};
 	int i;
 
@@ -199,9 +276,27 @@
 
 	for (i = 0; i < ARRAY_SIZE(commands); i++) {
 		struct cmd_struct *p = commands+i;
+		const char *prefix;
 		if (strcmp(p->cmd, cmd))
 			continue;
-		exit(p->fn(argc, argv, envp));
+
+		prefix = NULL;
+		if (p->option & NEEDS_PREFIX)
+			prefix = setup_git_directory();
+		if (p->option & USE_PAGER)
+			setup_pager();
+		if (getenv("GIT_TRACE")) {
+			int i;
+			fprintf(stderr, "trace: built-in: git");
+			for (i = 0; i < argc; ++i) {
+				fputc(' ', stderr);
+				sq_quote_print(stderr, argv[i]);
+			}
+			putc('\n', stderr);
+			fflush(stderr);
+		}
+
+		exit(p->fn(argc, argv, prefix));
 	}
 }
 
@@ -242,46 +337,19 @@
 		die("cannot handle %s internally", cmd);
 	}
 
-	/* Default command: "help" */
-	cmd = "help";
-
 	/* Look for flags.. */
-	while (argc > 1) {
-		cmd = *++argv;
-		argc--;
-
-		if (strncmp(cmd, "--", 2))
-			break;
-
-		cmd += 2;
-
-		/*
-		 * For legacy reasons, the "version" and "help"
-		 * commands can be written with "--" prepended
-		 * to make them look like flags.
-		 */
-		if (!strcmp(cmd, "help"))
-			break;
-		if (!strcmp(cmd, "version"))
-			break;
-
-		/*
-		 * Check remaining flags (which by now must be
-		 * "--exec-path", but maybe we will accept
-		 * other arguments some day)
-		 */
-		if (!strncmp(cmd, "exec-path", 9)) {
-			cmd += 9;
-			if (*cmd == '=') {
-				git_set_exec_path(cmd + 1);
-				continue;
-			}
-			puts(git_exec_path());
-			exit(0);
-		}
-		cmd_usage(0, NULL, NULL);
+	argv++;
+	argc--;
+	handle_options(&argv, &argc);
+	if (argc > 0) {
+		if (!strncmp(argv[0], "--", 2))
+			argv[0] += 2;
+	} else {
+		/* Default command: "help" */
+		argv[0] = "help";
+		argc = 1;
 	}
-	argv[0] = cmd;
+	cmd = argv[0];
 
 	/*
 	 * We search for git commands in the following order:
@@ -312,7 +380,7 @@
 	}
 
 	if (errno == ENOENT)
-		cmd_usage(0, exec_path, "'%s' is not a git-command", cmd);
+		help_unknown_cmd(cmd);
 
 	fprintf(stderr, "Failed to run command '%s': %s\n",
 		cmd, strerror(errno));
diff --git a/gitk b/gitk
index ba4644f..a92ab00 100755
--- a/gitk
+++ b/gitk
@@ -312,7 +312,7 @@
 
 proc readrefs {} {
     global tagids idtags headids idheads tagcontents
-    global otherrefids idotherrefs
+    global otherrefids idotherrefs mainhead
 
     foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
 	catch {unset $v}
@@ -358,6 +358,13 @@
 	}
     }
     close $refd
+    set mainhead {}
+    catch {
+	set thehead [exec git symbolic-ref HEAD]
+	if {[string match "refs/heads/*" $thehead]} {
+	    set mainhead [string range $thehead 11 end]
+	}
+    }
 }
 
 proc show_error {w top msg} {
@@ -386,6 +393,7 @@
     global rowctxmenu mergemax wrapcomment
     global highlight_files gdttype
     global searchstring sstring
+    global bgcolor fgcolor bglist fglist diffcolors
 
     menu .bar
     .bar add cascade -label "File" -menu .bar.file
@@ -446,18 +454,19 @@
     .ctop add .ctop.top
     set canv .ctop.top.clist.canv
     canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
-	-bg white -bd 0 \
+	-background $bgcolor -bd 0 \
 	-yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
     .ctop.top.clist add $canv
     set canv2 .ctop.top.clist.canv2
     canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
-	-bg white -bd 0 -yscrollincr $linespc
+	-background $bgcolor -bd 0 -yscrollincr $linespc
     .ctop.top.clist add $canv2
     set canv3 .ctop.top.clist.canv3
     canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
-	-bg white -bd 0 -yscrollincr $linespc
+	-background $bgcolor -bd 0 -yscrollincr $linespc
     .ctop.top.clist add $canv3
     bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
+    lappend bglist $canv $canv2 $canv3
 
     set sha1entry .ctop.top.bar.sha1
     set entries $sha1entry
@@ -563,19 +572,22 @@
     trace add variable searchstring write incrsearch
     pack $sstring -side left -expand 1 -fill x
     set ctext .ctop.cdet.left.ctext
-    text $ctext -bg white -state disabled -font $textfont \
+    text $ctext -background $bgcolor -foreground $fgcolor \
+	-state disabled -font $textfont \
 	-width $geometry(ctextw) -height $geometry(ctexth) \
 	-yscrollcommand scrolltext -wrap none
     scrollbar .ctop.cdet.left.sb -command "$ctext yview"
     pack .ctop.cdet.left.sb -side right -fill y
     pack $ctext -side left -fill both -expand 1
     .ctop.cdet add .ctop.cdet.left
+    lappend bglist $ctext
+    lappend fglist $ctext
 
     $ctext tag conf comment -wrap $wrapcomment
     $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
-    $ctext tag conf hunksep -fore blue
-    $ctext tag conf d0 -fore red
-    $ctext tag conf d1 -fore "#00a000"
+    $ctext tag conf hunksep -fore [lindex $diffcolors 2]
+    $ctext tag conf d0 -fore [lindex $diffcolors 0]
+    $ctext tag conf d1 -fore [lindex $diffcolors 1]
     $ctext tag conf m0 -fore red
     $ctext tag conf m1 -fore blue
     $ctext tag conf m2 -fore green
@@ -608,11 +620,15 @@
     pack .ctop.cdet.right.mode -side top -fill x
     set cflist .ctop.cdet.right.cfiles
     set indent [font measure $mainfont "nn"]
-    text $cflist -width $geometry(cflistw) -background white -font $mainfont \
+    text $cflist -width $geometry(cflistw) \
+	-background $bgcolor -foreground $fgcolor \
+	-font $mainfont \
 	-tabs [list $indent [expr {2 * $indent}]] \
 	-yscrollcommand ".ctop.cdet.right.sb set" \
 	-cursor [. cget -cursor] \
 	-spacing1 1 -spacing3 1
+    lappend bglist $cflist
+    lappend fglist $cflist
     scrollbar .ctop.cdet.right.sb -command "$cflist yview"
     pack .ctop.cdet.right.sb -side right -fill y
     pack $cflist -side left -fill both -expand 1
@@ -747,6 +763,7 @@
     global maxwidth showneartags
     global viewname viewfiles viewargs viewperm nextviewnum
     global cmitmode wrapcomment
+    global colors bgcolor fgcolor diffcolors
 
     if {$stuffsaved} return
     if {![winfo viewable .]} return
@@ -761,6 +778,10 @@
 	puts $f [list set cmitmode $cmitmode]
 	puts $f [list set wrapcomment $wrapcomment]
 	puts $f [list set showneartags $showneartags]
+	puts $f [list set bgcolor $bgcolor]
+	puts $f [list set fgcolor $fgcolor]
+	puts $f [list set colors $colors]
+	puts $f [list set diffcolors $diffcolors]
 	puts $f "set geometry(width) [winfo width .ctop]"
 	puts $f "set geometry(height) [winfo height .ctop]"
 	puts $f "set geometry(canv1) [expr {[winfo width $canv]-2}]"
@@ -2870,11 +2891,11 @@
 }
 
 proc drawcmittext {id row col rmx} {
-    global linespc canv canv2 canv3 canvy0
+    global linespc canv canv2 canv3 canvy0 fgcolor
     global commitlisted commitinfo rowidlist
     global rowtextx idpos idtags idheads idotherrefs
     global linehtag linentag linedtag
-    global mainfont canvxmax boldrows boldnamerows
+    global mainfont canvxmax boldrows boldnamerows fgcolor
 
     set ofill [expr {[lindex $commitlisted $row]? "blue": "white"}]
     set x [xc $row $col]
@@ -2882,7 +2903,7 @@
     set orad [expr {$linespc / 3}]
     set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
 	       [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
-	       -fill $ofill -outline black -width 1]
+	       -fill $ofill -outline $fgcolor -width 1 -tags circle]
     $canv raise $t
     $canv bind $t <1> {selcanvline {} %x %y}
     set xt [xc $row [llength [lindex $rowidlist $row]]]
@@ -2910,13 +2931,13 @@
 	    lappend nfont bold
 	}
     }
-    set linehtag($row) [$canv create text $xt $y -anchor w \
-			    -text $headline -font $font]
+    set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
+			    -text $headline -font $font -tags text]
     $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
-    set linentag($row) [$canv2 create text 3 $y -anchor w \
-			    -text $name -font $nfont]
-    set linedtag($row) [$canv3 create text 3 $y -anchor w \
-			    -text $date -font $mainfont]
+    set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
+			    -text $name -font $nfont -tags text]
+    set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
+			    -text $date -font $mainfont -tags text]
     set xr [expr {$xt + [font measure $mainfont $headline]}]
     if {$xr > $canvxmax} {
 	set canvxmax $xr
@@ -3136,9 +3157,9 @@
 }
 
 proc drawtags {id x xt y1} {
-    global idtags idheads idotherrefs
+    global idtags idheads idotherrefs mainhead
     global linespc lthickness
-    global canv mainfont commitrow rowtextx curview
+    global canv mainfont commitrow rowtextx curview fgcolor bgcolor
 
     set marks {}
     set ntags 0
@@ -3163,8 +3184,14 @@
     set yb [expr {$yt + $linespc - 1}]
     set xvals {}
     set wvals {}
+    set i -1
     foreach tag $marks {
-	set wid [font measure $mainfont $tag]
+	incr i
+	if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
+	    set wid [font measure [concat $mainfont bold] $tag]
+	} else {
+	    set wid [font measure $mainfont $tag]
+	}
 	lappend xvals $xt
 	lappend wvals $wid
 	set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
@@ -3175,6 +3202,7 @@
     foreach tag $marks x $xvals wid $wvals {
 	set xl [expr {$x + $delta}]
 	set xr [expr {$x + $delta + $wid + $lthickness}]
+	set font $mainfont
 	if {[incr ntags -1] >= 0} {
 	    # draw a tag
 	    set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
@@ -3186,6 +3214,9 @@
 	    # draw a head or other ref
 	    if {[incr nheads -1] >= 0} {
 		set col green
+		if {$tag eq $mainhead} {
+		    lappend font bold
+		}
 	    } else {
 		set col "#ddddff"
 	    }
@@ -3201,8 +3232,8 @@
 			-width 0 -fill "#ffddaa" -tags tag.$id
 	    }
 	}
-	set t [$canv create text $xl $y1 -anchor w -text $tag \
-		   -font $mainfont -tags tag.$id]
+	set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
+		   -font $font -tags [list tag.$id text]]
 	if {$ntags >= 0} {
 	    $canv bind $t <1> [list showtag $tag 1]
 	}
@@ -3223,10 +3254,11 @@
 }
 
 proc show_status {msg} {
-    global canv mainfont
+    global canv mainfont fgcolor
 
     clear_display
-    $canv create text 3 3 -anchor nw -text $msg -font $mainfont -tags textitems
+    $canv create text 3 3 -anchor nw -text $msg -font $mainfont \
+	-tags text -fill $fgcolor
 }
 
 proc finishcommits {} {
@@ -4574,7 +4606,8 @@
     set t [$canv create rectangle $x0 $y0 $x1 $y1 \
 	       -fill \#ffff80 -outline black -width 1 -tags hover]
     $canv raise $t
-    set t [$canv create text $x $y -anchor nw -text $text -tags hover -font $mainfont]
+    set t [$canv create text $x $y -anchor nw -text $text -tags hover \
+	       -font $mainfont]
     $canv raise $t
 }
 
@@ -4901,7 +4934,7 @@
 
 proc redrawtags {id} {
     global canv linehtag commitrow idpos selectedline curview
-    global mainfont
+    global mainfont canvxmax
 
     if {![info exists commitrow($curview,$id)]} return
     drawcmitrow $commitrow($curview,$id)
@@ -5242,6 +5275,7 @@
 proc doprefs {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
+    global bgcolor fgcolor ctext diffcolors
 
     set top .gitkprefs
     set prefstop $top
@@ -5265,6 +5299,7 @@
 	-font optionfont
     spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
     grid x $top.maxpctl $top.maxpct -sticky w
+
     label $top.ddisp -text "Diff display options"
     grid $top.ddisp - -sticky w -pady 10
     label $top.diffoptl -text "Options for diff program" \
@@ -5276,6 +5311,34 @@
     checkbutton $top.ntag.b -variable showneartags
     pack $top.ntag.b $top.ntag.l -side left
     grid x $top.ntag -sticky w
+
+    label $top.cdisp -text "Colors: press to choose"
+    grid $top.cdisp - -sticky w -pady 10
+    label $top.bg -padx 40 -relief sunk -background $bgcolor
+    button $top.bgbut -text "Background" -font optionfont \
+	-command [list choosecolor bgcolor 0 $top.bg background setbg]
+    grid x $top.bgbut $top.bg -sticky w
+    label $top.fg -padx 40 -relief sunk -background $fgcolor
+    button $top.fgbut -text "Foreground" -font optionfont \
+	-command [list choosecolor fgcolor 0 $top.fg foreground setfg]
+    grid x $top.fgbut $top.fg -sticky w
+    label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
+    button $top.diffoldbut -text "Diff: old lines" -font optionfont \
+	-command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
+		      [list $ctext tag conf d0 -foreground]]
+    grid x $top.diffoldbut $top.diffold -sticky w
+    label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
+    button $top.diffnewbut -text "Diff: new lines" -font optionfont \
+	-command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
+		      [list $ctext tag conf d1 -foreground]]
+    grid x $top.diffnewbut $top.diffnew -sticky w
+    label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
+    button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
+	-command [list choosecolor diffcolors 2 $top.hunksep \
+		      "diff hunk header" \
+		      [list $ctext tag conf hunksep -foreground]]
+    grid x $top.hunksepbut $top.hunksep -sticky w
+
     frame $top.buts
     button $top.buts.ok -text "OK" -command prefsok
     button $top.buts.can -text "Cancel" -command prefscan
@@ -5285,6 +5348,35 @@
     grid $top.buts - - -pady 10 -sticky ew
 }
 
+proc choosecolor {v vi w x cmd} {
+    global $v
+
+    set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
+	       -title "Gitk: choose color for $x"]
+    if {$c eq {}} return
+    $w conf -background $c
+    lset $v $vi $c
+    eval $cmd $c
+}
+
+proc setbg {c} {
+    global bglist
+
+    foreach w $bglist {
+	$w conf -background $c
+    }
+}
+
+proc setfg {c} {
+    global fglist canv
+
+    foreach w $fglist {
+	$w conf -foreground $c
+    }
+    allcanvs itemconf text -fill $c
+    $canv itemconf circle -outline $c
+}
+
 proc prefscan {} {
     global maxwidth maxgraphpct diffopts
     global oldprefs prefstop showneartags
@@ -5620,6 +5712,9 @@
 set showneartags 1
 
 set colors {green red blue magenta darkgrey brown orange}
+set bgcolor white
+set fgcolor black
+set diffcolors {red "#00a000" blue}
 
 catch {source ~/.gitk}
 
diff --git a/gitweb/gitweb.cgi b/gitweb/gitweb.cgi
index 035e76d..e5fca63 100755
--- a/gitweb/gitweb.cgi
+++ b/gitweb/gitweb.cgi
@@ -16,54 +16,56 @@
 use Fcntl ':mode';
 binmode STDOUT, ':utf8';
 
-my $cgi = new CGI;
-my $version = "267";
-my $my_url = $cgi->url();
-my $my_uri = $cgi->url(-absolute => 1);
-my $rss_link = "";
+our $cgi = new CGI;
+our $version = "267";
+our $my_url = $cgi->url();
+our $my_uri = $cgi->url(-absolute => 1);
+our $rss_link = "";
 
-# location of the git-core binaries
-my $gitbin = "/usr/bin";
+# core git executable to use
+# this can just be "git" if your webserver has a sensible PATH
+our $GIT = "/usr/bin/git";
 
 # absolute fs-path which will be prepended to the project path
-#my $projectroot = "/pub/scm";
-my $projectroot = "/home/kay/public_html/pub/scm";
+#our $projectroot = "/pub/scm";
+our $projectroot = "/home/kay/public_html/pub/scm";
 
-# version of the git-core binaries
-my $git_version = qx($gitbin/git --version);
-if ($git_version =~ m/git version (.*)$/) {
-	$git_version = $1;
-} else {
-	$git_version = "unknown";
-}
+# version of the core git binary
+our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
 
 # location for temporary files needed for diffs
-my $git_temp = "/tmp/gitweb";
+our $git_temp = "/tmp/gitweb";
+if (! -d $git_temp) {
+    mkdir($git_temp, 0700) || die_error("Couldn't mkdir $git_temp");
+}
 
 # target of the home link on top of all pages
-my $home_link = $my_uri;
+our $home_link = $my_uri;
+
+# name of your site or organization to appear in page titles
+# replace this with something more descriptive for clearer bookmarks
+our $site_name = $ENV{'SERVER_NAME'} || "Untitled";
 
 # html text to include at home page
-my $home_text = "indextext.html";
+our $home_text = "indextext.html";
 
 # URI of default stylesheet
-my $stylesheet = "gitweb.css";
+our $stylesheet = "gitweb.css";
 
 # source of projects list
-#my $projects_list = $projectroot;
-my $projects_list = "index/index.aux";
+#our $projects_list = $projectroot;
+our $projects_list = "index/index.aux";
 
 # default blob_plain mimetype and default charset for text/plain blob
-my $default_blob_plain_mimetype = 'text/plain';
-my $default_text_plain_charset  = undef;
+our $default_blob_plain_mimetype = 'text/plain';
+our $default_text_plain_charset  = undef;
 
 # file to use for guessing MIME types before trying /etc/mime.types
 # (relative to the current git repository)
-my $mimetypes_file = undef;
-
+our $mimetypes_file = undef;
 
 # input validation and dispatch
-my $action = $cgi->param('a');
+our $action = $cgi->param('a');
 if (defined $action) {
 	if ($action =~ m/[^0-9a-zA-Z\.\-_]/) {
 		undef $action;
@@ -78,7 +80,7 @@
 	}
 }
 
-my $order = $cgi->param('o');
+our $order = $cgi->param('o');
 if (defined $order) {
 	if ($order =~ m/[^0-9a-zA-Z_]/) {
 		undef $order;
@@ -86,7 +88,7 @@
 	}
 }
 
-my $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
+our $project = ($cgi->param('p') || $ENV{'PATH_INFO'});
 if (defined $project) {
 	$project =~ s|^/||; $project =~ s|/$||;
 	$project = validate_input($project);
@@ -109,7 +111,7 @@
 	exit;
 }
 
-my $file_name = $cgi->param('f');
+our $file_name = $cgi->param('f');
 if (defined $file_name) {
 	$file_name = validate_input($file_name);
 	if (!defined($file_name)) {
@@ -117,7 +119,7 @@
 	}
 }
 
-my $hash = $cgi->param('h');
+our $hash = $cgi->param('h');
 if (defined $hash) {
 	$hash = validate_input($hash);
 	if (!defined($hash)) {
@@ -125,7 +127,7 @@
 	}
 }
 
-my $hash_parent = $cgi->param('hp');
+our $hash_parent = $cgi->param('hp');
 if (defined $hash_parent) {
 	$hash_parent = validate_input($hash_parent);
 	if (!defined($hash_parent)) {
@@ -133,7 +135,7 @@
 	}
 }
 
-my $hash_base = $cgi->param('hb');
+our $hash_base = $cgi->param('hb');
 if (defined $hash_base) {
 	$hash_base = validate_input($hash_base);
 	if (!defined($hash_base)) {
@@ -141,7 +143,7 @@
 	}
 }
 
-my $page = $cgi->param('pg');
+our $page = $cgi->param('pg');
 if (defined $page) {
 	if ($page =~ m/[^0-9]$/) {
 		undef $page;
@@ -149,7 +151,7 @@
 	}
 }
 
-my $searchtext = $cgi->param('s');
+our $searchtext = $cgi->param('s');
 if (defined $searchtext) {
 	if ($searchtext =~ m/[^a-zA-Z0-9_\.\/\-\+\:\@ ]/) {
 		undef $searchtext;
@@ -225,7 +227,7 @@
 	git_tag();
 	exit;
 } elsif ($action eq "blame") {
-	git_blame();
+	git_blame2();
 	exit;
 } else {
 	undef $action;
@@ -278,7 +280,7 @@
 	my $status = shift || "200 OK";
 	my $expires = shift;
 
-	my $title = "git";
+	my $title = "$site_name git";
 	if (defined $project) {
 		$title .= " - $project";
 		if (defined $action) {
@@ -291,7 +293,17 @@
 			}
 		}
 	}
-	print $cgi->header(-type=>'text/html',  -charset => 'utf-8', -status=> $status, -expires => $expires);
+	my $content_type;
+	# require explicit support from the UA if we are to send the page as
+	# 'application/xhtml+xml', otherwise send it as plain old 'text/html'.
+	# we have to do this because MSIE sometimes globs '*/*', pretending to
+	# support xhtml+xml but choking when it gets what it asked for.
+	if ($cgi->http('HTTP_ACCEPT') =~ m/(,|;|\s|^)application\/xhtml\+xml(,|;|\s|$)/ && $cgi->Accept('application/xhtml+xml') != 0) {
+		$content_type = 'application/xhtml+xml';
+	} else {
+		$content_type = 'text/html';
+	}
+	print $cgi->header(-type=>$content_type,  -charset => 'utf-8', -status=> $status, -expires => $expires);
 	print <<EOF;
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -299,7 +311,7 @@
 <!-- git web interface v$version, (C) 2005-2006, Kay Sievers <kay.sievers\@vrfy.org>, Christian Gierke -->
 <!-- git core binaries version $git_version -->
 <head>
-<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+<meta http-equiv="content-type" content="$content_type; charset=utf-8"/>
 <meta name="robots" content="index, nofollow"/>
 <title>$title</title>
 <link rel="stylesheet" type="text/css" href="$stylesheet"/>
@@ -376,7 +388,7 @@
 sub git_get_type {
 	my $hash = shift;
 
-	open my $fd, "-|", "$gitbin/git-cat-file -t $hash" or return;
+	open my $fd, "-|", "$GIT cat-file -t $hash" or return;
 	my $type = <$fd>;
 	close $fd or return;
 	chomp $type;
@@ -388,7 +400,7 @@
 	my $oENV = $ENV{'GIT_DIR'};
 	my $retval = undef;
 	$ENV{'GIT_DIR'} = "$projectroot/$project";
-	if (open my $fd, "-|", "$gitbin/git-rev-parse", "--verify", "HEAD") {
+	if (open my $fd, "-|", $GIT, "rev-parse", "--verify", "HEAD") {
 		my $head = <$fd>;
 		close $fd;
 		if (defined $head && $head =~ /^([0-9a-fA-F]{40})$/) {
@@ -428,7 +440,7 @@
 	my %tag;
 	my @comment;
 
-	open my $fd, "-|", "$gitbin/git-cat-file tag $tag_id" or return;
+	open my $fd, "-|", "$GIT cat-file tag $tag_id" or return;
 	$tag{'id'} = $tag_id;
 	while (my $line = <$fd>) {
 		chomp $line;
@@ -500,7 +512,7 @@
 		@commit_lines = @$commit_text;
 	} else {
 		$/ = "\0";
-		open my $fd, "-|", "$gitbin/git-rev-list --header --parents --max-count=1 $commit_id" or return;
+		open my $fd, "-|", "$GIT rev-list --header --parents --max-count=1 $commit_id" or return;
 		@commit_lines = split '\n', <$fd>;
 		close $fd or return;
 		$/ = "\n";
@@ -598,7 +610,7 @@
 	if (defined $from) {
 		$from_tmp = "$git_temp/gitweb_" . $$ . "_from";
 		open my $fd2, "> $from_tmp";
-		open my $fd, "-|", "$gitbin/git-cat-file blob $from";
+		open my $fd, "-|", "$GIT cat-file blob $from";
 		my @file = <$fd>;
 		print $fd2 @file;
 		close $fd2;
@@ -609,7 +621,7 @@
 	if (defined $to) {
 		$to_tmp = "$git_temp/gitweb_" . $$ . "_to";
 		open my $fd2, "> $to_tmp";
-		open my $fd, "-|", "$gitbin/git-cat-file blob $to";
+		open my $fd, "-|", "$GIT cat-file blob $to";
 		my @file = <$fd>;
 		print $fd2 @file;
 		close $fd2;
@@ -783,7 +795,7 @@
 	if (-d $projects_list) {
 		# search in directory
 		my $dir = $projects_list;
-		opendir my $dh, $dir or return undef;
+		opendir my ($dh), $dir or return undef;
 		while (my $dir = readdir($dh)) {
 			if (-e "$projectroot/$dir/HEAD") {
 				my $pr = {
@@ -798,7 +810,7 @@
 		# 'git%2Fgit.git Linus+Torvalds'
 		# 'libs%2Fklibc%2Fklibc.git H.+Peter+Anvin'
 		# 'linux%2Fhotplug%2Fudev.git Greg+Kroah-Hartman'
-		open my $fd , $projects_list or return undef;
+		open my ($fd), $projects_list or return undef;
 		while (my $line = <$fd>) {
 			chomp $line;
 			my ($path, $owner) = split ' ', $line;
@@ -828,7 +840,7 @@
 	$key =~ s/^gitweb\.//;
 	return if ($key =~ m/\W/);
 
-	my $val = qx($gitbin/git-repo-config --get gitweb.$key);
+	my $val = qx($GIT repo-config --get gitweb.$key);
 	return ($val);
 }
 
@@ -1050,7 +1062,7 @@
 	      "<tr><td>owner</td><td>$owner</td></tr>\n" .
 	      "<tr><td>last change</td><td>$cd{'rfc2822'}</td></tr>\n" .
 	      "</table>\n";
-	open my $fd, "-|", "$gitbin/git-rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list --max-count=17 " . git_read_head($project) or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 	print "<div>\n" .
@@ -1126,7 +1138,7 @@
 				      "</td>\n" .
 				      "<td>";
 				if (defined($comment)) {
-				      print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, $comment);
+				      print $cgi->a({-class => "list", -href => "$my_uri?" . esc_param("p=$project;a=tag;h=$tag{'id'}")}, esc_html($comment));
 				}
 				print "</td>\n" .
 				      "<td class=\"link\">";
@@ -1187,6 +1199,20 @@
 	git_footer_html();
 }
 
+sub git_print_page_path {
+	my $name = shift;
+	my $type = shift;
+
+	if (!defined $name) {
+		print "<div class=\"page_path\"><b>/</b></div>\n";
+	} elsif ($type =~ "blob") {
+		print "<div class=\"page_path\"><b>" .
+			$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;f=$file_name")}, esc_html($name)) . "</b><br/></div>\n";
+	} else {
+		print "<div class=\"page_path\"><b>" . esc_html($name) . "</b><br/></div>\n";
+	}
+}
+
 sub git_tag {
 	my $head = git_read_head($project);
 	git_header_html();
@@ -1226,6 +1252,73 @@
 	git_footer_html();
 }
 
+sub git_blame2 {
+	my $fd;
+	my $ftype;
+	die_error(undef, "Permission denied.") if (!git_get_project_config_bool ('blame'));
+	die_error('404 Not Found', "File name not defined") if (!$file_name);
+	$hash_base ||= git_read_head($project);
+	die_error(undef, "Reading commit failed") unless ($hash_base);
+	my %co = git_read_commit($hash_base)
+		or die_error(undef, "Reading commit failed");
+	if (!defined $hash) {
+		$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
+			or die_error(undef, "Error looking up file");
+	}
+	$ftype = git_get_type($hash);
+	if ($ftype !~ "blob") {
+		die_error("400 Bad Request", "object is not a blob");
+	}
+	open ($fd, "-|", $GIT, "blame", '-l', $file_name, $hash_base)
+		or die_error(undef, "Open failed");
+	git_header_html();
+	print "<div class=\"page_nav\">\n" .
+		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
+	print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash;hb=$hash_base;f=$file_name")}, "blob") .
+		" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;f=$file_name")}, "head") . "<br/>\n";
+	print "</div>\n".
+		"<div>" .
+		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
+		"</div>\n";
+	git_print_page_path($file_name, $ftype);
+	my @rev_color = (qw(light dark));
+	my $num_colors = scalar(@rev_color);
+	my $current_color = 0;
+	my $last_rev;
+	print "<div class=\"page_body\">\n";
+	print "<table class=\"blame\">\n";
+	print "<tr><th>Commit</th><th>Line</th><th>Data</th></tr>\n";
+	while (<$fd>) {
+		/^([0-9a-fA-F]{40}).*?(\d+)\)\s{1}(\s*.*)/;
+		my $full_rev = $1;
+		my $rev = substr($full_rev, 0, 8);
+		my $lineno = $2;
+		my $data = $3;
+
+		if (!defined $last_rev) {
+			$last_rev = $full_rev;
+		} elsif ($last_rev ne $full_rev) {
+			$last_rev = $full_rev;
+			$current_color = ++$current_color % $num_colors;
+		}
+		print "<tr class=\"$rev_color[$current_color]\">\n";
+		print "<td class=\"sha1\">" .
+			$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$full_rev;f=$file_name")}, esc_html($rev)) . "</td>\n";
+		print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" . esc_html($lineno) . "</a></td>\n";
+		print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
+		print "</tr>\n";
+	}
+	print "</table>\n";
+	print "</div>";
+	close $fd or print "Reading blob failed\n";
+	git_footer_html();
+}
+
 sub git_blame {
 	my $fd;
 	die_error('403 Permission denied', "Permission denied.") if (!git_get_project_config_bool ('blame'));
@@ -1238,7 +1331,7 @@
 		$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
 			or die_error(undef, "Error lookup file.");
 	}
-	open ($fd, "-|", "$gitbin/git-annotate", '-l', '-t', '-r', $file_name, $hash_base)
+	open ($fd, "-|", $GIT, "annotate", '-l', '-t', '-r', $file_name, $hash_base)
 		or die_error(undef, "Open failed.");
 	git_header_html();
 	print "<div class=\"page_nav\">\n" .
@@ -1254,7 +1347,7 @@
 		"<div>" .
 		$cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
 		"</div>\n";
-	print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
+	git_print_page_path($file_name);
 	print "<div class=\"page_body\">\n";
 	print <<HTML;
 <table class="blame">
@@ -1433,7 +1526,7 @@
 	my $tree = $base;
 	my @parts = split '/', $path;
 	while (my $part = shift @parts) {
-		open my $fd, "-|", "$gitbin/git-ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
+		open my $fd, "-|", "$GIT ls-tree $tree" or die_error(undef, "Open git-ls-tree failed.");
 		my (@entries) = map { chomp; $_ } <$fd>;
 		close $fd or return undef;
 		foreach my $line (@entries) {
@@ -1456,61 +1549,6 @@
 	}
 }
 
-sub git_blob {
-	if (!defined $hash && defined $file_name) {
-		my $base = $hash_base || git_read_head($project);
-		$hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
-	}
-	my $have_blame = git_get_project_config_bool ('blame');
-	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or die_error(undef, "Open failed.");
-	git_header_html();
-	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
-		print "<div class=\"page_nav\">\n" .
-		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
-		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
-		if (defined $file_name) {
-			if ($have_blame) {
-				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") .  " | ";
-			}
-			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") .
-			" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head") . "<br/>\n";
-		} else {
-			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "<br/>\n";
-		}
-		print "</div>\n".
-		       "<div>" .
-		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
-		      "</div>\n";
-	} else {
-		print "<div class=\"page_nav\">\n" .
-		      "<br/><br/></div>\n" .
-		      "<div class=\"title\">$hash</div>\n";
-	}
-	if (defined $file_name) {
-		print "<div class=\"page_path\"><b>" . esc_html($file_name) . "</b></div>\n";
-	}
-	print "<div class=\"page_body\">\n";
-	my $nr;
-	while (my $line = <$fd>) {
-		chomp $line;
-		$nr++;
-		while ((my $pos = index($line, "\t")) != -1) {
-			if (my $count = (8 - ($pos % 8))) {
-				my $spaces = ' ' x $count;
-				$line =~ s/\t/$spaces/;
-			}
-		}
-		printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line);
-	}
-	close $fd or print "Reading blob failed.\n";
-	print "</div>";
-	git_footer_html();
-}
-
 sub mimetype_guess_file {
 	my $filename = shift;
 	my $mimemap = shift;
@@ -1549,14 +1587,14 @@
 	my $fd = shift;
 	my $filename = shift;
 
-	# just in case
-	return $default_blob_plain_mimetype unless $fd;
-
 	if ($filename) {
 		my $mime = mimetype_guess($filename);
 		$mime and return $mime;
 	}
 
+	# just in case
+	return $default_blob_plain_mimetype unless $fd;
+
 	if (-T $fd) {
 		return 'text/plain' .
 		       ($default_text_plain_charset ? '; charset='.$default_text_plain_charset : '');
@@ -1574,8 +1612,18 @@
 }
 
 sub git_blob_plain {
-	open my $fd, "-|", "$gitbin/git-cat-file blob $hash" or return;
-	my $type = git_blob_plain_mimetype($fd, $file_name);
+	if (!defined $hash) {
+                if (defined $file_name) {
+                        my $base = $hash_base || git_read_head($project);
+                        $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+                } else {
+                        die_error(undef, "No file name defined.");
+                }
+        }
+	my $type = shift;
+	open my $fd, "-|", "$GIT cat-file blob $hash" or die_error("Couldn't cat $file_name, $hash");
+
+	$type ||= git_blob_plain_mimetype($fd, $file_name);
 
 	# save as filename, even when no $file_name is given
 	my $save_as = "$hash";
@@ -1594,6 +1642,68 @@
 	close $fd;
 }
 
+sub git_blob {
+	if (!defined $hash) {
+                if (defined $file_name) {
+                        my $base = $hash_base || git_read_head($project);
+                        $hash = git_get_hash_by_path($base, $file_name, "blob") || die_error(undef, "Error lookup file.");
+                } else {
+                        die_error(undef, "No file name defined.");
+                }
+        }
+	my $have_blame = git_get_project_config_bool ('blame');
+	open my $fd, "-|", "$GIT cat-file blob $hash" or die_error(undef, "Open failed.");
+	my $mimetype = git_blob_plain_mimetype($fd, $file_name);
+	if ($mimetype !~ m/^text\//) {
+		close $fd;
+		return git_blob_plain($mimetype);
+	}
+	git_header_html();
+	if (defined $hash_base && (my %co = git_read_commit($hash_base))) {
+		print "<div class=\"page_nav\">\n" .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
+		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
+		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
+		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+		      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") . "<br/>\n";
+		if (defined $file_name) {
+			if ($have_blame) {
+				print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$hash;hb=$hash_base;f=$file_name")}, "blame") .  " | ";
+			}
+			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash;f=$file_name")}, "plain") .
+			" | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=HEAD;f=$file_name")}, "head") . "<br/>\n";
+		} else {
+			print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$hash")}, "plain") . "<br/>\n";
+		}
+		print "</div>\n".
+		       "<div>" .
+		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) .
+		      "</div>\n";
+	} else {
+		print "<div class=\"page_nav\">\n" .
+		      "<br/><br/></div>\n" .
+		      "<div class=\"title\">$hash</div>\n";
+	}
+	git_print_page_path($file_name, "blob");
+	print "<div class=\"page_body\">\n";
+	my $nr;
+	while (my $line = <$fd>) {
+		chomp $line;
+		$nr++;
+		while ((my $pos = index($line, "\t")) != -1) {
+			if (my $count = (8 - ($pos % 8))) {
+				my $spaces = ' ' x $count;
+				$line =~ s/\t/$spaces/;
+			}
+		}
+		printf "<div class=\"pre\"><a id=\"l%i\" href=\"#l%i\" class=\"linenr\">%4i</a> %s</div>\n", $nr, $nr, $nr, esc_html($line);
+	}
+	close $fd or print "Reading blob failed.\n";
+	print "</div>";
+	git_footer_html();
+}
+
 sub git_tree {
 	if (!defined $hash) {
 		$hash = git_read_head($project);
@@ -1606,7 +1716,7 @@
 		}
 	}
 	$/ = "\0";
-	open my $fd, "-|", "$gitbin/git-ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
+	open my $fd, "-|", "$GIT ls-tree -z $hash" or die_error(undef, "Open git-ls-tree failed.");
 	chomp (my (@entries) = <$fd>);
 	close $fd or die_error(undef, "Reading tree failed.");
 	$/ = "\n";
@@ -1640,10 +1750,8 @@
 	}
 	if (defined $file_name) {
 		$base = esc_html("$file_name/");
-		print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
-	} else {
-		print "<div class=\"page_path\"><b>/</b></div>\n";
 	}
+	git_print_page_path($file_name);
 	print "<div class=\"page_body\">\n";
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 0;
@@ -1668,7 +1776,8 @@
 			      "<td class=\"link\">" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$t_hash$base_key;f=$base$t_name")}, "blob") .
 #			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;h=$t_hash$base_key;f=$base$t_name")}, "blame") .
-			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash_base;f=$base$t_name")}, "history") .
+			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$t_hash;hb=$hash_base;f=$base$t_name")}, "history") .
+			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob_plain;h=$t_hash;f=$base$t_name")}, "raw") .
 			      "</td>\n";
 		} elsif ($t_type eq "tree") {
 			print "<td class=\"list\">" .
@@ -1676,6 +1785,7 @@
 			      "</td>\n" .
 			      "<td class=\"link\">" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$t_hash$base_key;f=$base$t_name")}, "tree") .
+			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash_base;f=$base$t_name")}, "history") .
 			      "</td>\n";
 		}
 		print "</tr>\n";
@@ -1687,7 +1797,7 @@
 
 sub git_rss {
 	# http://www.notestips.com/80256B3A007F2692/1/NAMO5P9UPQ
-	open my $fd, "-|", "$gitbin/git-rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list --max-count=150 " . git_read_head($project) or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading rev-list failed.");
 	print $cgi->header(-type => 'text/xml', -charset => 'utf-8');
@@ -1707,7 +1817,7 @@
 			last;
 		}
 		my %cd = date_str($co{'committer_epoch'});
-		open $fd, "-|", "$gitbin/git-diff-tree -r $co{'parent'} $co{'id'}" or next;
+		open $fd, "-|", "$GIT diff-tree -r $co{'parent'} $co{'id'}" or next;
 		my @difftree = map { chomp; $_ } <$fd>;
 		close $fd or next;
 		print "<item>\n" .
@@ -1749,7 +1859,7 @@
 	print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n".
 	      "<opml version=\"1.0\">\n".
 	      "<head>".
-	      "  <title>Git OPML Export</title>\n".
+	      "  <title>$site_name Git OPML Export</title>\n".
 	      "</head>\n".
 	      "<body>\n".
 	      "<outline text=\"git RSS feeds\">\n";
@@ -1795,7 +1905,7 @@
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 
@@ -1886,7 +1996,7 @@
 		$root = " --root";
 		$parent = "";
 	}
-	open my $fd, "-|", "$gitbin/git-diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r -M $root $parent $hash" or die_error(undef, "Open failed.");
 	@difftree = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
@@ -1910,7 +2020,13 @@
 		print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff");
 	}
 	print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") . "\n" .
-	      "<br/><br/></div>\n";
+		"<br/>\n";
+	if (defined $file_name && defined $co{'parent'}) {
+		my $parent = $co{'parent'};
+		print $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blame;hb=$parent;f=$file_name")}, "blame") . "\n";
+	}
+	print "<br/></div>\n";
+
 	if (defined $co{'parent'}) {
 		print "<div>\n" .
 		      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash"), -class => "title"}, esc_html($co{'title'}) . $ref) . "\n" .
@@ -2020,7 +2136,7 @@
 			      "<td><span class=\"file_status deleted\">[deleted " . file_type($from_mode). "]</span></td>\n" .
 			      "<td class=\"link\">" .
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$from_id;hb=$hash;f=$file")}, "blob") .
-			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") .
+			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") .
 			      "</td>\n"
 		} elsif ($status eq "M" || $status eq "T") {
 			my $mode_chnge = "";
@@ -2051,7 +2167,7 @@
 			if ($to_id ne $from_id) {
 				print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blobdiff;h=$to_id;hp=$from_id;hb=$hash;f=$file")}, "diff");
 			}
-			print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;h=$hash;f=$file")}, "history") . "\n";
+			print " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=history;hb=$hash;f=$file")}, "history") . "\n";
 			print "</td>\n";
 		} elsif ($status eq "R") {
 			my ($from_file, $to_file) = split "\t", $file;
@@ -2099,9 +2215,7 @@
 		      "<br/><br/></div>\n" .
 		      "<div class=\"title\">$hash vs $hash_parent</div>\n";
 	}
-	if (defined $file_name) {
-		print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b></div>\n";
-	}
+	git_print_page_path($file_name, "blob");
 	print "<div class=\"page_body\">\n" .
 	      "<div class=\"diff_info\">blob:" .
 	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;h=$hash_parent;hb=$hash_base;f=$file_name")}, $hash_parent) .
@@ -2128,7 +2242,7 @@
 	if (!defined $hash_parent) {
 		$hash_parent = $co{'parent'};
 	}
-	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
 	my (@difftree) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
@@ -2218,14 +2332,14 @@
 
 sub git_commitdiff_plain {
 	mkdir($git_temp, 0700);
-	open my $fd, "-|", "$gitbin/git-diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT diff-tree -r $hash_parent $hash" or die_error(undef, "Open failed.");
 	my (@difftree) = map { chomp; $_ } <$fd>;
 	close $fd or die_error(undef, "Reading diff-tree failed.");
 
 	# try to figure out the next tag after this commit
 	my $tagname;
 	my $refs = read_info_ref("tags");
-	open $fd, "-|", "$gitbin/git-rev-list HEAD";
+	open $fd, "-|", "$GIT rev-list HEAD";
 	chomp (my (@commits) = <$fd>);
 	close $fd;
 	foreach my $commit (@commits) {
@@ -2272,10 +2386,11 @@
 }
 
 sub git_history {
-	if (!defined $hash) {
-		$hash = git_read_head($project);
+	if (!defined $hash_base) {
+		$hash_base = git_read_head($project);
 	}
-	my %co = git_read_commit($hash);
+	my $ftype;
+	my %co = git_read_commit($hash_base);
 	if (!%co) {
 		die_error(undef, "Unknown commit object.");
 	}
@@ -2285,26 +2400,29 @@
 	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=summary")}, "summary") .
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=shortlog")}, "shortlog") .
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=log")}, "log") .
-	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash")}, "commit") .
-	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash")}, "commitdiff") .
-	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash")}, "tree") .
+	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base")}, "commit") .
+	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$hash_base")}, "commitdiff") .
+	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$co{'tree'};hb=$hash_base")}, "tree") .
 	      "<br/><br/>\n" .
 	      "</div>\n";
 	print "<div>\n" .
-	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash"), -class => "title"}, esc_html($co{'title'})) . "\n" .
+	      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$hash_base"), -class => "title"}, esc_html($co{'title'})) . "\n" .
 	      "</div>\n";
-	print "<div class=\"page_path\"><b>/" . esc_html($file_name) . "</b><br/></div>\n";
+	if (!defined $hash && defined $file_name) {
+		$hash = git_get_hash_by_path($hash_base, $file_name);
+	}
+	if (defined $hash) {
+		$ftype = git_get_type($hash);
+	}
+	git_print_page_path($file_name, $ftype);
 
-	open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -- \'$file_name\'";
-	my $commit;
+	open my $fd, "-|",
+		"$GIT rev-list --full-history $hash_base -- \'$file_name\'";
 	print "<table cellspacing=\"0\">\n";
 	my $alternate = 0;
 	while (my $line = <$fd>) {
 		if ($line =~ m/^([0-9a-fA-F]{40})/){
-			$commit = $1;
-			next;
-		}
-		if ($line =~ m/^:([0-7]{6}) ([0-7]{6}) ([0-9a-fA-F]{40}) ([0-9a-fA-F]{40}) (.)\t(.*)$/ && (defined $commit)) {
+			my $commit = $1;
 			my %co = git_read_commit($commit);
 			if (!%co) {
 				next;
@@ -2327,7 +2445,7 @@
 			      $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commit;h=$commit")}, "commit") .
 			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=commitdiff;h=$commit")}, "commitdiff") .
 			      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=blob;hb=$commit;f=$file_name")}, "blob");
-			my $blob = git_get_hash_by_path($hash, $file_name);
+			my $blob = git_get_hash_by_path($hash_base, $file_name);
 			my $blob_parent = git_get_hash_by_path($commit, $file_name);
 			if (defined $blob && defined $blob_parent && $blob ne $blob_parent) {
 				print " | " .
@@ -2336,7 +2454,6 @@
 			}
 			print "</td>\n" .
 			      "</tr>\n";
-			undef $commit;
 		}
 	}
 	print "</table>\n";
@@ -2387,7 +2504,7 @@
 	my $alternate = 0;
 	if ($commit_search) {
 		$/ = "\0";
-		open my $fd, "-|", "$gitbin/git-rev-list --header --parents $hash" or next;
+		open my $fd, "-|", "$GIT rev-list --header --parents $hash" or next;
 		while (my $commit_text = <$fd>) {
 			if (!grep m/$searchtext/i, $commit_text) {
 				next;
@@ -2437,7 +2554,7 @@
 
 	if ($pickaxe_search) {
 		$/ = "\n";
-		open my $fd, "-|", "$gitbin/git-rev-list $hash | $gitbin/git-diff-tree -r --stdin -S\'$searchtext\'";
+		open my $fd, "-|", "$GIT rev-list $hash | $GIT diff-tree -r --stdin -S\'$searchtext\'";
 		undef %co;
 		my @files;
 		while (my $line = <$fd>) {
@@ -2508,7 +2625,7 @@
 	      " | " . $cgi->a({-href => "$my_uri?" . esc_param("p=$project;a=tree;h=$hash;hb=$hash")}, "tree") . "<br/>\n";
 
 	my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
-	open my $fd, "-|", "$gitbin/git-rev-list $limit $hash" or die_error(undef, "Open failed.");
+	open my $fd, "-|", "$GIT rev-list $limit $hash" or die_error(undef, "Open failed.");
 	my (@revlist) = map { chomp; $_ } <$fd>;
 	close $fd;
 
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 98410f5..fffdb13 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -60,6 +60,7 @@
 
 div.page_body {
 	padding: 8px;
+	font-family: monospace;
 }
 
 div.title, a.title {
@@ -79,6 +80,7 @@
 	padding: 6px 0px;
 	border: solid #d9d8d1;
 	border-width: 0px 0px 1px;
+	font-family: monospace;
 }
 
 div.log_body {
@@ -142,10 +144,15 @@
 	padding: 8px 4px;
 }
 
-table.project_list, table.diff_tree {
+table.project_list {
 	border-spacing: 0;
 }
 
+table.diff_tree {
+	border-spacing: 0;
+	font-family: monospace;
+}
+
 table.blame {
 	border-collapse: collapse;
 }
diff --git a/hash-object.c b/hash-object.c
index 43bd93b..5f89e64 100644
--- a/hash-object.c
+++ b/hash-object.c
@@ -46,7 +46,7 @@
 		if (!no_more_flags && argv[i][0] == '-') {
 			if (!strcmp(argv[i], "-t")) {
 				if (argc <= ++i)
-					die(hash_object_usage);
+					usage(hash_object_usage);
 				type = argv[i];
 			}
 			else if (!strcmp(argv[i], "-w")) {
@@ -66,8 +66,8 @@
 				hash_stdin(type, write_object);
 			}
 			else
-				die(hash_object_usage);
-		} 
+				usage(hash_object_usage);
+		}
 		else {
 			const char *arg = argv[i];
 			if (0 <= prefix_length)
diff --git a/http-fetch.c b/http-fetch.c
index 44eba5f..de5fc44 100644
--- a/http-fetch.c
+++ b/http-fetch.c
@@ -36,6 +36,8 @@
 #define PREV_BUF_SIZE 4096
 #define RANGE_HEADER_SIZE 30
 
+static int commits_on_stdin = 0;
+
 static int got_alternates = -1;
 static int corrupt_object_found = 0;
 
@@ -43,7 +45,7 @@
 
 struct alt_base
 {
-	char *base;
+	const char *base;
 	int path_len;
 	int got_indices;
 	struct packed_git *packs;
@@ -81,7 +83,7 @@
 };
 
 struct alternates_request {
-	char *base;
+	const char *base;
 	char *url;
 	struct buffer *buffer;
 	struct active_request_slot *slot;
@@ -142,7 +144,7 @@
 	return size;
 }
 
-static void fetch_alternates(char *base);
+static void fetch_alternates(const char *base);
 
 static void process_object_response(void *callback_data);
 
@@ -490,7 +492,7 @@
 {
 	struct packed_git *new_pack;
 	if (has_pack_file(sha1))
-		return 0; // don't list this as something we can get
+		return 0; /* don't list this as something we can get */
 
 	if (fetch_index(repo, sha1))
 		return -1;
@@ -507,7 +509,7 @@
 		(struct alternates_request *)callback_data;
 	struct active_request_slot *slot = alt_req->slot;
 	struct alt_base *tail = alt;
-	char *base = alt_req->base;
+	const char *base = alt_req->base;
 	static const char null_byte = '\0';
 	char *data;
 	int i = 0;
@@ -570,7 +572,7 @@
 						 base[serverlen - 1] != '/');
 					i += 3;
 				}
-				// If the server got removed, give up.
+				/* If the server got removed, give up. */
 				okay = strchr(base, ':') - base + 3 <
 					serverlen;
 			} else if (alt_req->http_specific) {
@@ -581,7 +583,7 @@
 					okay = 1;
 				}
 			}
-			// skip 'objects' at end
+			/* skip 'objects' at end */
 			if (okay) {
 				target = xmalloc(serverlen + posn - i - 6);
 				strlcpy(target, base, serverlen);
@@ -612,7 +614,7 @@
 	got_alternates = 1;
 }
 
-static void fetch_alternates(char *base)
+static void fetch_alternates(const char *base)
 {
 	struct buffer buffer;
 	char *url;
@@ -868,7 +870,7 @@
 
 	if (strlen(ls->dentry_name) == 63 &&
 	    !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
-	    !strncmp(ls->dentry_name+58, ".pack", 5)) {
+	    has_extension(ls->dentry_name, ".pack")) {
 		get_sha1_hex(ls->dentry_name + 18, sha1);
 		setup_index(ls->repo, sha1);
 	}
@@ -1185,7 +1187,7 @@
         char *url;
         char hex[42];
         struct buffer buffer;
-	char *base = alt->base;
+	const char *base = alt->base;
 	struct active_request_slot *slot;
 	struct slot_results results;
         buffer.size = 41;
@@ -1214,14 +1216,17 @@
         return 0;
 }
 
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-	char *commit_id;
-	char *url;
+	int commits;
+	const char **write_ref = NULL;
+	char **commit_id;
+	const char *url;
 	char *path;
 	int arg = 1;
 	int rc = 0;
 
+	setup_ident();
 	setup_git_directory();
 	git_config(git_default_config);
 
@@ -1237,20 +1242,26 @@
 		} else if (argv[arg][1] == 'v') {
 			get_verbosely = 1;
 		} else if (argv[arg][1] == 'w') {
-			write_ref = argv[arg + 1];
+			write_ref = &argv[arg + 1];
 			arg++;
 		} else if (!strcmp(argv[arg], "--recover")) {
 			get_recover = 1;
+		} else if (!strcmp(argv[arg], "--stdin")) {
+			commits_on_stdin = 1;
 		}
 		arg++;
 	}
-	if (argc < arg + 2) {
-		usage("git-http-fetch [-c] [-t] [-a] [-d] [-v] [--recover] [-w ref] commit-id url");
+	if (argc < arg + 2 - commits_on_stdin) {
+		usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
 		return 1;
 	}
-	commit_id = argv[arg];
-	url = argv[arg + 1];
-	write_ref_log_details = url;
+	if (commits_on_stdin) {
+		commits = pull_targets_stdin(&commit_id, &write_ref);
+	} else {
+		commit_id = (char **) &argv[arg++];
+		commits = 1;
+	}
+	url = argv[arg];
 
 	http_init();
 
@@ -1268,13 +1279,16 @@
 			alt->path_len = strlen(path);
 	}
 
-	if (pull(commit_id))
+	if (pull(commits, commit_id, write_ref, url))
 		rc = 1;
 
 	http_cleanup();
 
 	curl_slist_free_all(no_pragma_header);
 
+	if (commits_on_stdin)
+		pull_targets_free(commits, commit_id, write_ref);
+
 	if (corrupt_object_found) {
 		fprintf(stderr,
 "Some loose object were found to be corrupt, but they might be just\n"
diff --git a/http-push.c b/http-push.c
index e281f70..d45733e 100644
--- a/http-push.c
+++ b/http-push.c
@@ -492,7 +492,7 @@
 
 	/* Set it up */
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, Z_BEST_COMPRESSION);
+	deflateInit(&stream, zlib_compression_level);
 	size = deflateBound(&stream, len + hdrlen);
 	request->buffer.buffer = xmalloc(size);
 
@@ -530,7 +530,7 @@
 	request->dest = xmalloc(strlen(request->url) + 14);
 	sprintf(request->dest, "Destination: %s", request->url);
 	posn += 38;
-	*(posn++) = '.';
+	*(posn++) = '_';
 	strcpy(posn, request->lock->token);
 
 	slot = get_active_slot();
@@ -1784,16 +1784,16 @@
 
 		if (obj->flags & (UNINTERESTING | SEEN))
 			continue;
-		if (obj->type == TYPE_TAG) {
+		if (obj->type == OBJ_TAG) {
 			obj->flags |= SEEN;
 			p = add_one_object(obj, p);
 			continue;
 		}
-		if (obj->type == TYPE_TREE) {
+		if (obj->type == OBJ_TREE) {
 			p = process_tree((struct tree *)obj, p, NULL, name);
 			continue;
 		}
-		if (obj->type == TYPE_BLOB) {
+		if (obj->type == OBJ_BLOB) {
 			p = process_blob((struct blob *)obj, p, NULL, name);
 			continue;
 		}
@@ -1960,12 +1960,12 @@
 	 * old.  Otherwise we require --force.
 	 */
 	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	old = (struct commit *) o;
 
 	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	new = (struct commit *) o;
 
@@ -2044,7 +2044,7 @@
 	fwrite_buffer(ref_info, 1, len, buf);
 	free(ref_info);
 
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, ls->dentry_name, 0);
 		if (o) {
 			len = strlen(ls->dentry_name) + 45;
@@ -2521,7 +2521,7 @@
 			commit_argv[3] = old_sha1_hex;
 			commit_argc++;
 		}
-		init_revisions(&revs);
+		init_revisions(&revs, setup_git_directory());
 		setup_revisions(commit_argc, commit_argv, &revs, NULL);
 		free(new_sha1_hex);
 		if (old_sha1_hex) {
diff --git a/index-pack.c b/index-pack.c
index b39953d..b20659c 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -447,7 +447,7 @@
 		usage(index_pack_usage);
 	if (!index_name) {
 		int len = strlen(pack_name);
-		if (len < 5 || strcmp(pack_name + len - 5, ".pack"))
+		if (!has_extension(pack_name, ".pack"))
 			die("packfile name '%s' does not end with '.pack'",
 			    pack_name);
 		index_name_buf = xmalloc(len);
diff --git a/local-fetch.c b/local-fetch.c
index ffa4887..7d01845 100644
--- a/local-fetch.c
+++ b/local-fetch.c
@@ -8,8 +8,9 @@
 static int use_link = 0;
 static int use_symlink = 0;
 static int use_filecopy = 1;
+static int commits_on_stdin = 0;
 
-static char *path; /* "Remote" git repository */
+static const char *path; /* "Remote" git repository */
 
 void prefetch(unsigned char *sha1)
 {
@@ -42,8 +43,8 @@
 		return -1;
 	while ((de = readdir(dir)) != NULL) {
 		int namelen = strlen(de->d_name);
-		if (namelen != 50 || 
-		    strcmp(de->d_name + namelen - 5, ".pack"))
+		if (namelen != 50 ||
+		    !has_extension(de->d_name, ".pack"))
 			continue;
 		get_sha1_hex(de->d_name + 5, sha1);
 		setup_index(sha1);
@@ -194,19 +195,22 @@
 }
 
 static const char local_pull_usage[] =
-"git-local-fetch [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] commit-id path";
+"git-local-fetch [-c] [-t] [-a] [-v] [-w filename] [--recover] [-l] [-s] [-n] [--stdin] commit-id path";
 
-/* 
+/*
  * By default we only use file copy.
  * If -l is specified, a hard link is attempted.
  * If -s is specified, then a symlink is attempted.
  * If -n is _not_ specified, then a regular file-to-file copy is done.
  */
-int main(int argc, char **argv)
+int main(int argc, const char **argv)
 {
-	char *commit_id;
+	int commits;
+	const char **write_ref = NULL;
+	char **commit_id;
 	int arg = 1;
 
+	setup_ident();
 	setup_git_directory();
 	git_config(git_default_config);
 
@@ -229,21 +233,30 @@
 		else if (argv[arg][1] == 'v')
 			get_verbosely = 1;
 		else if (argv[arg][1] == 'w')
-			write_ref = argv[++arg];
+			write_ref = &argv[++arg];
 		else if (!strcmp(argv[arg], "--recover"))
 			get_recover = 1;
+		else if (!strcmp(argv[arg], "--stdin"))
+			commits_on_stdin = 1;
 		else
 			usage(local_pull_usage);
 		arg++;
 	}
-	if (argc < arg + 2)
+	if (argc < arg + 2 - commits_on_stdin)
 		usage(local_pull_usage);
-	commit_id = argv[arg];
-	path = argv[arg + 1];
-	write_ref_log_details = path;
+	if (commits_on_stdin) {
+		commits = pull_targets_stdin(&commit_id, &write_ref);
+	} else {
+		commit_id = (char **) &argv[arg++];
+		commits = 1;
+	}
+	path = argv[arg];
 
-	if (pull(commit_id))
+	if (pull(commits, commit_id, write_ref, path))
 		return 1;
 
+	if (commits_on_stdin)
+		pull_targets_free(commits, commit_id, write_ref);
+
 	return 0;
 }
diff --git a/lockfile.c b/lockfile.c
index 2346e0e..2a2fea3 100644
--- a/lockfile.c
+++ b/lockfile.c
@@ -22,7 +22,7 @@
 	raise(signo);
 }
 
-int hold_lock_file_for_update(struct lock_file *lk, const char *path)
+static int lock_file(struct lock_file *lk, const char *path)
 {
 	int fd;
 	sprintf(lk->filename, "%s.lock", path);
@@ -41,6 +41,14 @@
 	return fd;
 }
 
+int hold_lock_file_for_update(struct lock_file *lk, const char *path, int die_on_error)
+{
+	int fd = lock_file(lk, path);
+	if (fd < 0 && die_on_error)
+		die("unable to create '%s': %s", path, strerror(errno));
+	return fd;
+}
+
 int commit_lock_file(struct lock_file *lk)
 {
 	char result_file[PATH_MAX];
diff --git a/log-tree.c b/log-tree.c
index ebb49f2..05ede0c 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -43,9 +43,10 @@
 	return at;
 }
 
-void show_log(struct rev_info *opt, struct log_info *log, const char *sep)
+void show_log(struct rev_info *opt, const char *sep)
 {
 	static char this_header[16384];
+	struct log_info *log = opt->loginfo;
 	struct commit *commit = log->commit, *parent = log->parent;
 	int abbrev = opt->diffopt.abbrev;
 	int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
@@ -58,7 +59,7 @@
 		fputs(diff_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
 		if (opt->parents)
 			show_parents(commit, abbrev_commit);
-		putchar('\n');
+		putchar(opt->diffopt.line_termination);
 		return;
 	}
 
@@ -96,6 +97,11 @@
 			subject = "Subject: ";
 
 		printf("From %s Mon Sep 17 00:00:00 2001\n", sha1);
+		if (opt->message_id)
+			printf("Message-Id: <%s>\n", opt->message_id);
+		if (opt->ref_message_id)
+			printf("In-Reply-To: <%s>\nReferences: <%s>\n",
+			       opt->ref_message_id, opt->ref_message_id);
 		if (opt->mime_boundary) {
 			static char subject_buffer[1024];
 			static char buffer[1024];
@@ -128,7 +134,8 @@
 			opt->diffopt.stat_sep = buffer;
 		}
 	} else {
-		printf("%s%s",
+		printf("%s%s%s",
+		       diff_get_color(opt->diffopt.color_diff, DIFF_COMMIT),
 		       opt->commit_format == CMIT_FMT_ONELINE ? "" : "commit ",
 		       diff_unique_abbrev(commit->object.sha1, abbrev_commit));
 		if (opt->parents)
@@ -137,6 +144,8 @@
 			printf(" (from %s)",
 			       diff_unique_abbrev(parent->object.sha1,
 						  abbrev_commit));
+		printf("%s",
+		       diff_get_color(opt->diffopt.color_diff, DIFF_RESET));
 		putchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\n');
 	}
 
@@ -163,8 +172,22 @@
 		return 0;
 	}
 
-	if (opt->loginfo && !opt->no_commit_id)
-		show_log(opt, opt->loginfo, opt->diffopt.with_stat ? "---\n" : "\n");
+	if (opt->loginfo && !opt->no_commit_id) {
+		/* When showing a verbose header (i.e. log message),
+		 * and not in --pretty=oneline format, we would want
+		 * an extra newline between the end of log and the
+		 * output for readability.
+		 */
+		show_log(opt, opt->diffopt.msg_sep);
+		if (opt->verbose_header &&
+		    opt->commit_format != CMIT_FMT_ONELINE) {
+			int pch = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH;
+			if ((pch & opt->diffopt.output_format) == pch)
+				printf("---%c", opt->diffopt.line_termination);
+			else
+				putchar(opt->diffopt.line_termination);
+		}
+	}
 	diff_flush(&opt->diffopt);
 	return 1;
 }
@@ -261,7 +284,7 @@
 	shown = log_tree_diff(opt, commit, &log);
 	if (!shown && opt->loginfo && opt->always_show_header) {
 		log.parent = NULL;
-		show_log(opt, opt->loginfo, "");
+		show_log(opt, "");
 		shown = 1;
 	}
 	opt->loginfo = NULL;
diff --git a/log-tree.h b/log-tree.h
index a26e484..e82b56a 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -11,6 +11,6 @@
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
 int log_tree_opt_parse(struct rev_info *, const char **, int);
-void show_log(struct rev_info *opt, struct log_info *log, const char *sep);
+void show_log(struct rev_info *opt, const char *sep);
 
 #endif
diff --git a/merge-base.c b/merge-base.c
index 4856ca0..59f723f 100644
--- a/merge-base.c
+++ b/merge-base.c
@@ -2,232 +2,22 @@
 #include "cache.h"
 #include "commit.h"
 
-#define PARENT1 1
-#define PARENT2 2
-#define UNINTERESTING 4
-
-static struct commit *interesting(struct commit_list *list)
-{
-	while (list) {
-		struct commit *commit = list->item;
-		list = list->next;
-		if (commit->object.flags & UNINTERESTING)
-			continue;
-		return commit;
-	}
-	return NULL;
-}
-
-/*
- * A pathological example of how this thing works.
- *
- * Suppose we had this commit graph, where chronologically
- * the timestamp on the commit are A <= B <= C <= D <= E <= F
- * and we are trying to figure out the merge base for E and F
- * commits.
- *
- *                  F
- *                 / \
- *            E   A   D
- *             \ /   /  
- *              B   /
- *               \ /
- *                C
- *
- * First we push E and F to list to be processed.  E gets bit 1
- * and F gets bit 2.  The list becomes:
- *
- *     list=F(2) E(1), result=empty
- *
- * Then we pop F, the newest commit, from the list.  Its flag is 2.
- * We scan its parents, mark them reachable from the side that F is
- * reachable from, and push them to the list:
- *
- *     list=E(1) D(2) A(2), result=empty
- *
- * Next pop E and do the same.
- *
- *     list=D(2) B(1) A(2), result=empty
- *
- * Next pop D and do the same.
- *
- *     list=C(2) B(1) A(2), result=empty
- *
- * Next pop C and do the same.
- *
- *     list=B(1) A(2), result=empty
- *
- * Now it is B's turn.  We mark its parent, C, reachable from B's side,
- * and push it to the list:
- *
- *     list=C(3) A(2), result=empty
- *
- * Now pop C and notice it has flags==3.  It is placed on the result list,
- * and the list now contains:
- *
- *     list=A(2), result=C(3)
- *
- * We pop A and do the same.
- * 
- *     list=B(3), result=C(3)
- *
- * Next, we pop B and something very interesting happens.  It has flags==3
- * so it is also placed on the result list, and its parents are marked
- * uninteresting, retroactively, and placed back on the list:
- *
- *    list=C(7), result=C(7) B(3)
- * 
- * Now, list does not have any interesting commit.  So we find the newest
- * commit from the result list that is not marked uninteresting.  Which is
- * commit B.
- *
- *
- * Another pathological example how this thing used to fail to mark an
- * ancestor of a merge base as UNINTERESTING before we introduced the
- * postprocessing phase (mark_reachable_commits).
- *
- *		  2
- *		  H
- *	    1    / \
- *	    G   A   \
- *	    |\ /     \ 
- *	    | B       \
- *	    |  \       \
- *	     \  C       F
- *	      \  \     / 
- *	       \  D   /   
- *		\ |  /
- *		 \| /
- *		  E
- *
- *	 list			A B C D E F G H
- *	 G1 H2			- - - - - - 1 2
- *	 H2 E1 B1		- 1 - - 1 - 1 2
- *	 F2 E1 B1 A2		2 1 - - 1 2 1 2
- *	 E3 B1 A2		2 1 - - 3 2 1 2
- *	 B1 A2			2 1 - - 3 2 1 2
- *	 C1 A2			2 1 1 - 3 2 1 2
- *	 D1 A2			2 1 1 1 3 2 1 2
- *	 A2			2 1 1 1 3 2 1 2
- *	 B3			2 3 1 1 3 2 1 2
- *	 C7			2 3 7 1 3 2 1 2
- *
- * At this point, unfortunately, everybody in the list is
- * uninteresting, so we fail to complete the following two
- * steps to fully marking uninteresting commits.
- *
- *	 D7			2 3 7 7 3 2 1 2
- *	 E7			2 3 7 7 7 2 1 2
- *
- * and we ended up showing E as an interesting merge base.
- * The postprocessing phase re-injects C and continues traversal
- * to contaminate D and E.
- */
-
 static int show_all = 0;
 
-static void mark_reachable_commits(struct commit_list *result,
-				   struct commit_list *list)
-{
-	struct commit_list *tmp;
-
-	/*
-	 * Postprocess to fully contaminate the well.
-	 */
-	for (tmp = result; tmp; tmp = tmp->next) {
-		struct commit *c = tmp->item;
-		/* Reinject uninteresting ones to list,
-		 * so we can scan their parents.
-		 */
-		if (c->object.flags & UNINTERESTING)
-			commit_list_insert(c, &list);
-	}
-	while (list) {
-		struct commit *c = list->item;
-		struct commit_list *parents;
-
-		tmp = list;
-		list = list->next;
-		free(tmp);
-
-		/* Anything taken out of the list is uninteresting, so
-		 * mark all its parents uninteresting.  We do not
-		 * parse new ones (we already parsed all the relevant
-		 * ones).
-		 */
-		parents = c->parents;
-		while (parents) {
-			struct commit *p = parents->item;
-			parents = parents->next;
-			if (!(p->object.flags & UNINTERESTING)) {
-				p->object.flags |= UNINTERESTING;
-				commit_list_insert(p, &list);
-			}
-		}
-	}
-}
-
 static int merge_base(struct commit *rev1, struct commit *rev2)
 {
-	struct commit_list *list = NULL;
-	struct commit_list *result = NULL;
-	struct commit_list *tmp = NULL;
-
-	if (rev1 == rev2) {
-		printf("%s\n", sha1_to_hex(rev1->object.sha1));
-		return 0;
-	}
-
-	parse_commit(rev1);
-	parse_commit(rev2);
-
-	rev1->object.flags |= 1;
-	rev2->object.flags |= 2;
-	insert_by_date(rev1, &list);
-	insert_by_date(rev2, &list);
-
-	while (interesting(list)) {
-		struct commit *commit = list->item;
-		struct commit_list *parents;
-		int flags = commit->object.flags & 7;
-
-		tmp = list;
-		list = list->next;
-		free(tmp);
-		if (flags == 3) {
-			insert_by_date(commit, &result);
-
-			/* Mark parents of a found merge uninteresting */
-			flags |= UNINTERESTING;
-		}
-		parents = commit->parents;
-		while (parents) {
-			struct commit *p = parents->item;
-			parents = parents->next;
-			if ((p->object.flags & flags) == flags)
-				continue;
-			parse_commit(p);
-			p->object.flags |= flags;
-			insert_by_date(p, &list);
-		}
-	}
+	struct commit_list *result = get_merge_bases(rev1, rev2, 0);
 
 	if (!result)
 		return 1;
 
-	if (result->next && list)
-		mark_reachable_commits(result, list);
-
 	while (result) {
-		struct commit *commit = result->item;
-		result = result->next;
-		if (commit->object.flags & UNINTERESTING)
-			continue;
-		printf("%s\n", sha1_to_hex(commit->object.sha1));
+		printf("%s\n", sha1_to_hex(result->item->object.sha1));
 		if (!show_all)
 			return 0;
-		commit->object.flags |= UNINTERESTING;
+		result = result->next;
 	}
+
 	return 0;
 }
 
diff --git a/merge-file.c b/merge-file.c
new file mode 100644
index 0000000..f32c653
--- /dev/null
+++ b/merge-file.c
@@ -0,0 +1,166 @@
+#include "cache.h"
+#include "run-command.h"
+#include "xdiff-interface.h"
+#include "blob.h"
+
+static void rm_temp_file(const char *filename)
+{
+	unlink(filename);
+	free((void *)filename);
+}
+
+static const char *write_temp_file(mmfile_t *f)
+{
+	int fd;
+	const char *tmp = getenv("TMPDIR");
+	char *filename;
+
+	if (!tmp)
+		tmp = "/tmp";
+	filename = mkpath("%s/%s", tmp, "git-tmp-XXXXXX");
+	fd = mkstemp(filename);
+	if (fd < 0)
+		return NULL;
+	filename = strdup(filename);
+	if (f->size != xwrite(fd, f->ptr, f->size)) {
+		rm_temp_file(filename);
+		return NULL;
+	}
+	close(fd);
+	return filename;
+}
+
+static void *read_temp_file(const char *filename, unsigned long *size)
+{
+	struct stat st;
+	char *buf = NULL;
+	int fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+	if (!fstat(fd, &st)) {
+		*size = st.st_size;
+		buf = xmalloc(st.st_size);
+		if (st.st_size != xread(fd, buf, st.st_size)) {
+			free(buf);
+			buf = NULL;
+		}
+	}
+	close(fd);
+	return buf;
+}
+
+static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
+{
+	void *buf;
+	unsigned long size;
+	char type[20];
+
+	buf = read_sha1_file(obj->object.sha1, type, &size);
+	if (!buf)
+		return -1;
+	if (strcmp(type, blob_type))
+		return -1;
+	f->ptr = buf;
+	f->size = size;
+	return 0;
+}
+
+static void free_mmfile(mmfile_t *f)
+{
+	free(f->ptr);
+}
+
+static void *three_way_filemerge(mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
+{
+	void *res;
+	const char *t1, *t2, *t3;
+
+	t1 = write_temp_file(base);
+	t2 = write_temp_file(our);
+	t3 = write_temp_file(their);
+	res = NULL;
+	if (t1 && t2 && t3) {
+		int code = run_command("merge", t2, t1, t3, NULL);
+		if (!code || code == -1)
+			res = read_temp_file(t2, size);
+	}
+	rm_temp_file(t1);
+	rm_temp_file(t2);
+	rm_temp_file(t3);
+	return res;
+}
+
+static int common_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	mmfile_t *dst = priv_;
+
+	for (i = 0; i < nbuf; i++) {
+		memcpy(dst->ptr + dst->size, mb[i].ptr, mb[i].size);
+		dst->size += mb[i].size;
+	}
+	return 0;
+}
+
+static int generate_common_file(mmfile_t *res, mmfile_t *f1, mmfile_t *f2)
+{
+	unsigned long size = f1->size < f2->size ? f1->size : f2->size;
+	void *ptr = xmalloc(size);
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 3;
+	xecfg.flags = XDL_EMIT_COMMON;
+	ecb.outf = common_outf;
+
+	res->ptr = ptr;
+	res->size = 0;
+
+	ecb.priv = res;
+	return xdl_diff(f1, f2, &xpp, &xecfg, &ecb);
+}
+
+void *merge_file(struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+{
+	void *res = NULL;
+	mmfile_t f1, f2, common;
+
+	/*
+	 * Removed in either branch?
+	 *
+	 * NOTE! This depends on the caller having done the
+	 * proper warning about removing a file that got
+	 * modified in the other branch!
+	 */
+	if (!our || !their) {
+		char type[20];
+		if (base)
+			return NULL;
+		if (!our)
+			our = their;
+		return read_sha1_file(our->object.sha1, type, size);
+	}
+
+	if (fill_mmfile_blob(&f1, our) < 0)
+		goto out_no_mmfile;
+	if (fill_mmfile_blob(&f2, their) < 0)
+		goto out_free_f1;
+
+	if (base) {
+		if (fill_mmfile_blob(&common, base) < 0)
+			goto out_free_f2_f1;
+	} else {
+		if (generate_common_file(&common, &f1, &f2) < 0)
+			goto out_free_f2_f1;
+	}
+	res = three_way_filemerge(&common, &f1, &f2, size);
+	free_mmfile(&common);
+out_free_f2_f1:
+	free_mmfile(&f2);
+out_free_f1:
+	free_mmfile(&f1);
+out_no_mmfile:
+	return res;
+}
diff --git a/merge-tree.c b/merge-tree.c
index 9dcaab7..7cf00be 100644
--- a/merge-tree.c
+++ b/merge-tree.c
@@ -1,11 +1,152 @@
 #include "cache.h"
 #include "tree-walk.h"
+#include "xdiff-interface.h"
+#include "blob.h"
 
 static const char merge_tree_usage[] = "git-merge-tree <base-tree> <branch1> <branch2>";
 static int resolve_directories = 1;
 
+struct merge_list {
+	struct merge_list *next;
+	struct merge_list *link;	/* other stages for this object */
+
+	unsigned int stage : 2,
+		     flags : 30;
+	unsigned int mode;
+	const char *path;
+	struct blob *blob;
+};
+
+static struct merge_list *merge_result, **merge_result_end = &merge_result;
+
+static void add_merge_entry(struct merge_list *entry)
+{
+	*merge_result_end = entry;
+	merge_result_end = &entry->next;
+}
+
 static void merge_trees(struct tree_desc t[3], const char *base);
 
+static const char *explanation(struct merge_list *entry)
+{
+	switch (entry->stage) {
+	case 0:
+		return "merged";
+	case 3:
+		return "added in remote";
+	case 2:
+		if (entry->link)
+			return "added in both";
+		return "added in local";
+	}
+
+	/* Existed in base */
+	entry = entry->link;
+	if (!entry)
+		return "removed in both";
+
+	if (entry->link)
+		return "changed in both";
+
+	if (entry->stage == 3)
+		return "removed in local";
+	return "removed in remote";
+}
+
+extern void *merge_file(struct blob *, struct blob *, struct blob *, unsigned long *);
+
+static void *result(struct merge_list *entry, unsigned long *size)
+{
+	char type[20];
+	struct blob *base, *our, *their;
+
+	if (!entry->stage)
+		return read_sha1_file(entry->blob->object.sha1, type, size);
+	base = NULL;
+	if (entry->stage == 1) {
+		base = entry->blob;
+		entry = entry->link;
+	}
+	our = NULL;
+	if (entry && entry->stage == 2) {
+		our = entry->blob;
+		entry = entry->link;
+	}
+	their = NULL;
+	if (entry)
+		their = entry->blob;
+	return merge_file(base, our, their, size);
+}
+
+static void *origin(struct merge_list *entry, unsigned long *size)
+{
+	char type[20];
+	while (entry) {
+		if (entry->stage == 2)
+			return read_sha1_file(entry->blob->object.sha1, type, size);
+		entry = entry->link;
+	}
+	return NULL;
+}
+
+static int show_outf(void *priv_, mmbuffer_t *mb, int nbuf)
+{
+	int i;
+	for (i = 0; i < nbuf; i++)
+		printf("%.*s", (int) mb[i].size, mb[i].ptr);
+	return 0;
+}
+
+static void show_diff(struct merge_list *entry)
+{
+	unsigned long size;
+	mmfile_t src, dst;
+	xpparam_t xpp;
+	xdemitconf_t xecfg;
+	xdemitcb_t ecb;
+
+	xpp.flags = XDF_NEED_MINIMAL;
+	xecfg.ctxlen = 3;
+	xecfg.flags = 0;
+	ecb.outf = show_outf;
+	ecb.priv = NULL;
+
+	src.ptr = origin(entry, &size);
+	if (!src.ptr)
+		size = 0;
+	src.size = size;
+	dst.ptr = result(entry, &size);
+	if (!dst.ptr)
+		size = 0;
+	dst.size = size;
+	xdl_diff(&src, &dst, &xpp, &xecfg, &ecb);
+	free(src.ptr);
+	free(dst.ptr);
+}
+
+static void show_result_list(struct merge_list *entry)
+{
+	printf("%s\n", explanation(entry));
+	do {
+		struct merge_list *link = entry->link;
+		static const char *desc[4] = { "result", "base", "our", "their" };
+		printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, sha1_to_hex(entry->blob->object.sha1), entry->path);
+		entry = link;
+	} while (entry);
+}
+
+static void show_result(void)
+{
+	struct merge_list *walk;
+
+	walk = merge_result;
+	while (walk) {
+		show_result_list(walk);
+		show_diff(walk);
+		walk = walk->next;
+	}
+}
+
 /* An empty entry never compares same, not even to another empty entry */
 static int same_entry(struct name_entry *a, struct name_entry *b)
 {
@@ -15,24 +156,34 @@
 		a->mode == b->mode;
 }
 
-static const char *sha1_to_hex_zero(const unsigned char *sha1)
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
 {
-	if (sha1)
-		return sha1_to_hex(sha1);
-	return "0000000000000000000000000000000000000000";
+	struct merge_list *res = xmalloc(sizeof(*res));
+
+	memset(res, 0, sizeof(*res));
+	res->stage = stage;
+	res->path = path;
+	res->mode = mode;
+	res->blob = lookup_blob(sha1);
+	return res;
 }
 
 static void resolve(const char *base, struct name_entry *branch1, struct name_entry *result)
 {
+	struct merge_list *orig, *final;
+	const char *path;
+
 	/* If it's already branch1, don't bother showing it */
 	if (!branch1)
 		return;
 
-	printf("0 %06o->%06o %s->%s %s%s\n",
-		branch1->mode, result->mode,
-		sha1_to_hex_zero(branch1->sha1),
-		sha1_to_hex_zero(result->sha1),
-		base, result->path);
+	path = strdup(mkpath("%s%s", base, result->path));
+	orig = create_entry(2, branch1->mode, branch1->sha1, path);
+	final = create_entry(0, result->mode, result->sha1, path);
+
+	final->link = orig;
+
+	add_merge_entry(final);
 }
 
 static int unresolved_directory(const char *base, struct name_entry n[3])
@@ -71,16 +222,40 @@
 	return 1;
 }
 
+
+static struct merge_list *link_entry(unsigned stage, const char *base, struct name_entry *n, struct merge_list *entry)
+{
+	const char *path;
+	struct merge_list *link;
+
+	if (!n->mode)
+		return entry;
+	if (entry)
+		path = entry->path;
+	else
+		path = strdup(mkpath("%s%s", base, n->path));
+	link = create_entry(stage, n->mode, n->sha1, path);
+	link->link = entry;
+	return link;
+}
+
 static void unresolved(const char *base, struct name_entry n[3])
 {
+	struct merge_list *entry = NULL;
+
 	if (unresolved_directory(base, n))
 		return;
-	if (n[0].sha1)
-		printf("1 %06o %s %s%s\n", n[0].mode, sha1_to_hex(n[0].sha1), base, n[0].path);
-	if (n[1].sha1)
-		printf("2 %06o %s %s%s\n", n[1].mode, sha1_to_hex(n[1].sha1), base, n[1].path);
-	if (n[2].sha1)
-		printf("3 %06o %s %s%s\n", n[2].mode, sha1_to_hex(n[2].sha1), base, n[2].path);
+
+	/*
+	 * Do them in reverse order so that the resulting link
+	 * list has the stages in order - link_entry adds new
+	 * links at the front.
+	 */
+	entry = link_entry(3, base, n + 2, entry);
+	entry = link_entry(2, base, n + 1, entry);
+	entry = link_entry(1, base, n + 0, entry);
+
+	add_merge_entry(entry);
 }
 
 /*
@@ -172,5 +347,7 @@
 	free(buf1);
 	free(buf2);
 	free(buf3);
+
+	show_result();
 	return 0;
 }
diff --git a/mktag.c b/mktag.c
index f0fe528..be23e58 100644
--- a/mktag.c
+++ b/mktag.c
@@ -2,10 +2,11 @@
 #include "tag.h"
 
 /*
- * A signature file has a very simple fixed format: three lines
- * of "object <sha1>" + "type <typename>" + "tag <tagname>",
- * followed by some free-form signature that git itself doesn't
- * care about, but that can be verified with gpg or similar.
+ * A signature file has a very simple fixed format: four lines
+ * of "object <sha1>" + "type <typename>" + "tag <tagname>" +
+ * "tagger <committer>", followed by a blank line, a free-form tag
+ * message and a signature block that git itself doesn't care about,
+ * but that can be verified with gpg or similar.
  *
  * The first three lines are guaranteed to be at least 63 bytes:
  * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
@@ -17,7 +18,7 @@
  * in that size, you're doing something wrong.
  */
 
-// Some random size
+/* Some random size */
 #define MAXSIZE (8192)
 
 /*
@@ -38,6 +39,12 @@
 	return ret;
 }
 
+#ifdef NO_C99_FORMAT
+#define PD_FMT "%d"
+#else
+#define PD_FMT "%td"
+#endif
+
 static int verify_tag(char *buffer, unsigned long size)
 {
 	int typelen;
@@ -46,45 +53,42 @@
 	const char *object, *type_line, *tag_line, *tagger_line;
 
 	if (size < 64)
-		return error("wanna fool me ? you obviously got the size wrong !\n");
+		return error("wanna fool me ? you obviously got the size wrong !");
 
 	buffer[size] = 0;
 
 	/* Verify object line */
 	object = buffer;
 	if (memcmp(object, "object ", 7))
-		return error("char%d: does not start with \"object \"\n", 0);
+		return error("char%d: does not start with \"object \"", 0);
 
 	if (get_sha1_hex(object + 7, sha1))
-		return error("char%d: could not get SHA1 hash\n", 7);
+		return error("char%d: could not get SHA1 hash", 7);
 
 	/* Verify type line */
 	type_line = object + 48;
 	if (memcmp(type_line - 1, "\ntype ", 6))
-		return error("char%d: could not find \"\\ntype \"\n", 47);
+		return error("char%d: could not find \"\\ntype \"", 47);
 
 	/* Verify tag-line */
 	tag_line = strchr(type_line, '\n');
 	if (!tag_line)
-		return error("char%td: could not find next \"\\n\"\n", type_line - buffer);
+		return error("char" PD_FMT ": could not find next \"\\n\"", type_line - buffer);
 	tag_line++;
 	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
-		return error("char%td: no \"tag \" found\n", tag_line - buffer);
+		return error("char" PD_FMT ": no \"tag \" found", tag_line - buffer);
 
 	/* Get the actual type */
 	typelen = tag_line - type_line - strlen("type \n");
 	if (typelen >= sizeof(type))
-		return error("char%td: type too long\n", type_line+5 - buffer);
+		return error("char" PD_FMT ": type too long", type_line+5 - buffer);
 
 	memcpy(type, type_line+5, typelen);
 	type[typelen] = 0;
 
 	/* Verify that the object matches */
-	if (get_sha1_hex(object + 7, sha1))
-		return error("char%d: could not get SHA1 hash but this is really odd since i got it before !\n", 7);
-
 	if (verify_object(sha1, type))
-		return error("char%d: could not verify object %s\n", 7, sha1);
+		return error("char%d: could not verify object %s", 7, sha1_to_hex(sha1));
 
 	/* Verify the tag-name: we don't allow control characters or spaces in it */
 	tag_line += 4;
@@ -94,19 +98,24 @@
 			break;
 		if (c > ' ')
 			continue;
-		return error("char%td: could not verify tag name\n", tag_line - buffer);
+		return error("char" PD_FMT ": could not verify tag name", tag_line - buffer);
 	}
 
 	/* Verify the tagger line */
 	tagger_line = tag_line;
 
 	if (memcmp(tagger_line, "tagger", 6) || (tagger_line[6] == '\n'))
-		return error("char%td: could not find \"tagger\"\n", tagger_line - buffer);
+		return error("char" PD_FMT ": could not find \"tagger\"", tagger_line - buffer);
+
+	/* TODO: check for committer info + blank line? */
+	/* Also, the minimum length is probably + "tagger .", or 63+8=71 */
 
 	/* The actual stuff afterwards we don't care about.. */
 	return 0;
 }
 
+#undef PD_FMT
+
 int main(int argc, char **argv)
 {
 	unsigned long size = 4096;
@@ -114,7 +123,7 @@
 	unsigned char result_sha1[20];
 
 	if (argc != 1)
-		usage("cat <signaturefile> | git-mktag");
+		usage("git-mktag < signaturefile");
 
 	setup_git_directory();
 
@@ -123,7 +132,8 @@
 		die("could not read from stdin");
 	}
 
-	// Verify it for some basic sanity: it needs to start with "object <sha1>\ntype\ntagger "
+	/* Verify it for some basic sanity: it needs to start with
+	   "object <sha1>\ntype\ntagger " */
 	if (verify_tag(buffer, size) < 0)
 		die("invalid tag signature file");
 
diff --git a/mktree.c b/mktree.c
index ab63cd9..9a6f0d2 100644
--- a/mktree.c
+++ b/mktree.c
@@ -71,7 +71,7 @@
 	write_sha1_file(buffer, offset, tree_type, sha1);
 }
 
-static const char mktree_usage[] = "mktree [-z]";
+static const char mktree_usage[] = "git-mktree [-z]";
 
 int main(int ac, char **av)
 {
diff --git a/name-rev.c b/name-rev.c
index 6a23f2d..f92f14e 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -5,7 +5,7 @@
 #include "refs.h"
 
 static const char name_rev_usage[] =
-	"git-name-rev [--tags] ( --all | --stdin | commitish [commitish...] )\n";
+	"git-name-rev [--tags] ( --all | --stdin | committish [committish...] )\n";
 
 typedef struct rev_name {
 	const char *tip_name;
@@ -84,14 +84,14 @@
 	if (tags_only && strncmp(path, "refs/tags/", 10))
 		return 0;
 
-	while (o && o->type == TYPE_TAG) {
+	while (o && o->type == OBJ_TAG) {
 		struct tag *t = (struct tag *) o;
 		if (!t->tagged)
 			break; /* broken repository */
 		o = parse_object(t->tagged->sha1);
 		deref = 1;
 	}
-	if (o && o->type == TYPE_COMMIT) {
+	if (o && o->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)o;
 
 		if (!strncmp(path, "refs/heads/", 11))
@@ -111,7 +111,7 @@
 	struct rev_name *n;
 	struct commit *c;
 
-	if (o->type != TYPE_COMMIT)
+	if (o->type != OBJ_COMMIT)
 		return "undefined";
 	c = (struct commit *) o;
 	n = c->util;
@@ -172,7 +172,7 @@
 		}
 
 		o = deref_tag(parse_object(sha1), *argv, 0);
-		if (!o || o->type != TYPE_COMMIT) {
+		if (!o || o->type != OBJ_COMMIT) {
 			fprintf(stderr, "Could not get commit for %s. Skipping.\n",
 					*argv);
 			continue;
diff --git a/object.c b/object.c
index 37277f9..b5d8ed4 100644
--- a/object.c
+++ b/object.c
@@ -19,7 +19,8 @@
 }
 
 const char *type_names[] = {
-	"none", "blob", "tree", "commit", "bad"
+	"none", "commit", "tree", "blob", "tag",
+	"bad type 5", "bad type 6", "delta", "bad",
 };
 
 static unsigned int hash_obj(struct object *obj, unsigned int n)
@@ -88,7 +89,7 @@
 {
 	obj->parsed = 0;
 	obj->used = 0;
-	obj->type = TYPE_NONE;
+	obj->type = OBJ_NONE;
 	obj->flags = 0;
 	memcpy(obj->sha1, sha1, 20);
 
@@ -131,7 +132,7 @@
 	if (!obj) {
 		union any_object *ret = xcalloc(1, sizeof(*ret));
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_NONE;
+		ret->object.type = OBJ_NONE;
 		return &ret->object;
 	}
 	return obj;
diff --git a/object.h b/object.h
index e0125e1..733faac 100644
--- a/object.h
+++ b/object.h
@@ -24,12 +24,19 @@
 #define TYPE_BITS   3
 #define FLAG_BITS  27
 
-#define TYPE_NONE   0
-#define TYPE_BLOB   1
-#define TYPE_TREE   2
-#define TYPE_COMMIT 3
-#define TYPE_TAG    4
-#define TYPE_BAD    5
+/*
+ * The object type is stored in 3 bits.
+ */
+enum object_type {
+	OBJ_NONE = 0,
+	OBJ_COMMIT = 1,
+	OBJ_TREE = 2,
+	OBJ_BLOB = 3,
+	OBJ_TAG = 4,
+	/* 5/6 for future expansion */
+	OBJ_DELTA = 7,
+	OBJ_BAD,
+};
 
 struct object {
 	unsigned parsed : 1;
@@ -40,14 +47,14 @@
 };
 
 extern int track_object_refs;
-extern const char *type_names[];
+extern const char *type_names[9];
 
 extern unsigned int get_max_object_index(void);
 extern struct object *get_indexed_object(unsigned int);
 
 static inline const char *typename(unsigned int type)
 {
-	return type_names[type > TYPE_TAG ? TYPE_BAD : type];
+	return type_names[type > OBJ_BAD ? OBJ_BAD : type];
 }
 
 extern struct object_refs *lookup_object_refs(struct object *);
diff --git a/pack-objects.c b/pack-objects.c
index b486ea5..861c7f0 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -28,7 +28,7 @@
 	struct object_entry *delta;	/* delta base object */
 	struct packed_git *in_pack; 	/* already in pack */
 	unsigned int in_pack_offset;
-	struct object_entry *delta_child; /* delitified objects who bases me */
+	struct object_entry *delta_child; /* deltified objects who bases me */
 	struct object_entry *delta_sibling; /* other deltified objects who
 					     * uses the same base as me
 					     */
@@ -39,7 +39,7 @@
 };
 
 /*
- * Objects we are going to pack are colected in objects array (dynamically
+ * Objects we are going to pack are collected in objects array (dynamically
  * expanded).  nr_objects & nr_alloc controls this array.  They are stored
  * in the order we see -- typically rev-list --objects order that gives us
  * nice "minimum seek" order.
@@ -63,6 +63,7 @@
 static unsigned char pack_file_sha1[20];
 static int progress = 1;
 static volatile sig_atomic_t progress_update = 0;
+static int window = 10;
 
 /*
  * The object names in objects array are hashed with this hashtable,
@@ -1216,16 +1217,26 @@
 	setitimer(ITIMER_REAL, &v, NULL);
 }
 
+static int git_pack_config(const char *k, const char *v)
+{
+	if(!strcmp(k, "pack.window")) {
+		window = git_config_int(k, v);
+		return 0;
+	}
+	return git_default_config(k, v);
+}
+
 int main(int argc, char **argv)
 {
 	SHA_CTX ctx;
 	char line[40 + 1 + PATH_MAX + 2];
-	int window = 10, depth = 10, pack_to_stdout = 0;
+	int depth = 10, pack_to_stdout = 0;
 	struct object_entry **list;
 	int num_preferred_base = 0;
 	int i;
 
 	setup_git_directory();
+	git_config(git_pack_config);
 
 	progress = isatty(2);
 	for (i = 1; i < argc; i++) {
diff --git a/pack.h b/pack.h
index 694e0c5..eb07b03 100644
--- a/pack.h
+++ b/pack.h
@@ -1,20 +1,7 @@
 #ifndef PACK_H
 #define PACK_H
 
-/*
- * The packed object type is stored in 3 bits.
- * The type value 0 is a reserved prefix if ever there is more than 7
- * object types, or any future format extensions.
- */
-enum object_type {
-	OBJ_EXT = 0,
-	OBJ_COMMIT = 1,
-	OBJ_TREE = 2,
-	OBJ_BLOB = 3,
-	OBJ_TAG = 4,
-	/* 5/6 for future expansion */
-	OBJ_DELTA = 7,
-};
+#include "object.h"
 
 /*
  * Packed object header
diff --git a/pager.c b/pager.c
index 2d186e8..280f57f 100644
--- a/pager.c
+++ b/pager.c
@@ -24,6 +24,8 @@
 	else if (!*pager || !strcmp(pager, "cat"))
 		return;
 
+	pager_in_use = 1; /* means we are emitting to terminal */
+
 	if (pipe(fd) < 0)
 		return;
 	pid = fork();
diff --git a/path-list.c b/path-list.c
new file mode 100644
index 0000000..f15a10d
--- /dev/null
+++ b/path-list.c
@@ -0,0 +1,105 @@
+#include <stdio.h>
+#include "cache.h"
+#include "path-list.h"
+
+/* if there is no exact match, point to the index where the entry could be
+ * inserted */
+static int get_entry_index(const struct path_list *list, const char *path,
+		int *exact_match)
+{
+	int left = -1, right = list->nr;
+
+	while (left + 1 < right) {
+		int middle = (left + right) / 2;
+		int compare = strcmp(path, list->items[middle].path);
+		if (compare < 0)
+			right = middle;
+		else if (compare > 0)
+			left = middle;
+		else {
+			*exact_match = 1;
+			return middle;
+		}
+	}
+
+	*exact_match = 0;
+	return right;
+}
+
+/* returns -1-index if already exists */
+static int add_entry(struct path_list *list, const char *path)
+{
+	int exact_match;
+	int index = get_entry_index(list, path, &exact_match);
+
+	if (exact_match)
+		return -1 - index;
+
+	if (list->nr + 1 >= list->alloc) {
+		list->alloc += 32;
+		list->items = xrealloc(list->items, list->alloc
+				* sizeof(struct path_list_item));
+	}
+	if (index < list->nr)
+		memmove(list->items + index + 1, list->items + index,
+				(list->nr - index)
+				* sizeof(struct path_list_item));
+	list->items[index].path = list->strdup_paths ?
+		strdup(path) : (char *)path;
+	list->items[index].util = NULL;
+	list->nr++;
+
+	return index;
+}
+
+struct path_list_item *path_list_insert(const char *path, struct path_list *list)
+{
+	int index = add_entry(list, path);
+
+	if (index < 0)
+		index = 1 - index;
+
+	return list->items + index;
+}
+
+int path_list_has_path(const struct path_list *list, const char *path)
+{
+	int exact_match;
+	get_entry_index(list, path, &exact_match);
+	return exact_match;
+}
+
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list)
+{
+	int exact_match, i = get_entry_index(list, path, &exact_match);
+	if (!exact_match)
+		return NULL;
+	return list->items + i;
+}
+
+void path_list_clear(struct path_list *list, int free_items)
+{
+	if (list->items) {
+		int i;
+		if (free_items)
+			for (i = 0; i < list->nr; i++) {
+				if (list->strdup_paths)
+					free(list->items[i].path);
+				if (list->items[i].util)
+					free(list->items[i].util);
+			}
+		free(list->items);
+	}
+	list->items = NULL;
+	list->nr = list->alloc = 0;
+}
+
+void print_path_list(const char *text, const struct path_list *p)
+{
+	int i;
+	if ( text )
+		printf("%s\n", text);
+	for (i = 0; i < p->nr; i++)
+		printf("%s:%p\n", p->items[i].path, p->items[i].util);
+}
+
diff --git a/path-list.h b/path-list.h
new file mode 100644
index 0000000..d6401ea
--- /dev/null
+++ b/path-list.h
@@ -0,0 +1,22 @@
+#ifndef _PATH_LIST_H_
+#define _PATH_LIST_H_
+
+struct path_list_item {
+	char *path;
+	void *util;
+};
+struct path_list
+{
+	struct path_list_item *items;
+	unsigned int nr, alloc;
+	unsigned int strdup_paths:1;
+};
+
+void print_path_list(const char *text, const struct path_list *p);
+
+int path_list_has_path(const struct path_list *list, const char *path);
+void path_list_clear(struct path_list *list, int free_items);
+struct path_list_item *path_list_insert(const char *path, struct path_list *list);
+struct path_list_item *path_list_lookup(const char *path, struct path_list *list);
+
+#endif /* _PATH_LIST_H_ */
diff --git a/peek-remote.c b/peek-remote.c
index a90cf22..2b30980 100644
--- a/peek-remote.c
+++ b/peek-remote.c
@@ -7,11 +7,11 @@
 "git-peek-remote [--exec=upload-pack] [host:]directory";
 static const char *exec = "git-upload-pack";
 
-static int peek_remote(int fd[2])
+static int peek_remote(int fd[2], unsigned flags)
 {
 	struct ref *ref;
 
-	get_remote_heads(fd[0], &ref, 0, NULL, 0);
+	get_remote_heads(fd[0], &ref, 0, NULL, flags);
 	packet_flush(fd[1]);
 
 	while (ref) {
@@ -28,6 +28,7 @@
 	int fd[2];
 	pid_t pid;
 	int nongit = 0;
+	unsigned flags = 0;
 
 	setup_git_directory_gently(&nongit);
 
@@ -35,22 +36,35 @@
 		char *arg = argv[i];
 
 		if (*arg == '-') {
-			if (!strncmp("--exec=", arg, 7))
+			if (!strncmp("--exec=", arg, 7)) {
 				exec = arg + 7;
-			else
-				usage(peek_remote_usage);
-			continue;
+				continue;
+			}
+			if (!strcmp("--tags", arg)) {
+				flags |= REF_TAGS;
+				continue;
+			}
+			if (!strcmp("--heads", arg)) {
+				flags |= REF_HEADS;
+				continue;
+			}
+			if (!strcmp("--refs", arg)) {
+				flags |= REF_NORMAL;
+				continue;
+			}
+			usage(peek_remote_usage);
 		}
 		dest = arg;
 		break;
 	}
+
 	if (!dest || i != argc - 1)
 		usage(peek_remote_usage);
 
 	pid = git_connect(fd, dest, exec);
 	if (pid < 0)
 		return 1;
-	ret = peek_remote(fd);
+	ret = peek_remote(fd, flags);
 	close(fd[0]);
 	close(fd[1]);
 	finish_connect(pid);
diff --git a/ppc/sha1ppc.S b/ppc/sha1ppc.S
index e85611a..140cb53 100644
--- a/ppc/sha1ppc.S
+++ b/ppc/sha1ppc.S
@@ -3,183 +3,222 @@
  *
  * Copyright (C) 2005 Paul Mackerras <paulus@samba.org>
  */
-#define FS	80
 
 /*
- * We roll the registers for T, A, B, C, D, E around on each
- * iteration; T on iteration t is A on iteration t+1, and so on.
- * We use registers 7 - 12 for this.
+ * PowerPC calling convention:
+ * %r0 - volatile temp
+ * %r1 - stack pointer.
+ * %r2 - reserved
+ * %r3-%r12 - Incoming arguments & return values; volatile.
+ * %r13-%r31 - Callee-save registers
+ * %lr - Return address, volatile
+ * %ctr - volatile
+ *
+ * Register usage in this routine:
+ * %r0 - temp
+ * %r3 - argument (pointer to 5 words of SHA state)
+ * %r4 - argument (pointer to data to hash)
+ * %r5 - Contant K in SHA round (initially number of blocks to hash)
+ * %r6-%r10 - Working copies of SHA variables A..E (actually E..A order)
+ * %r11-%r26 - Data being hashed W[].
+ * %r27-%r31 - Previous copies of A..E, for final add back.
+ * %ctr - loop count
  */
-#define RT(t)	((((t)+5)%6)+7)
-#define RA(t)	((((t)+4)%6)+7)
-#define RB(t)	((((t)+3)%6)+7)
-#define RC(t)	((((t)+2)%6)+7)
-#define RD(t)	((((t)+1)%6)+7)
-#define RE(t)	((((t)+0)%6)+7)
 
-/* We use registers 16 - 31 for the W values */
-#define W(t)	(((t)%16)+16)
 
-#define STEPD0(t)				\
-	and	%r6,RB(t),RC(t);		\
-	andc	%r0,RD(t),RB(t);		\
-	rotlwi	RT(t),RA(t),5;			\
-	rotlwi	RB(t),RB(t),30;			\
-	or	%r6,%r6,%r0;			\
-	add	%r0,RE(t),%r15;			\
-	add	RT(t),RT(t),%r6;		\
-	add	%r0,%r0,W(t);			\
-	add	RT(t),RT(t),%r0
+/*
+ * We roll the registers for A, B, C, D, E around on each
+ * iteration; E on iteration t is D on iteration t+1, and so on.
+ * We use registers 6 - 10 for this.  (Registers 27 - 31 hold
+ * the previous values.)
+ */
+#define RA(t)	(((t)+4)%5+6)
+#define RB(t)	(((t)+3)%5+6)
+#define RC(t)	(((t)+2)%5+6)
+#define RD(t)	(((t)+1)%5+6)
+#define RE(t)	(((t)+0)%5+6)
 
-#define STEPD1(t)				\
-	xor	%r6,RB(t),RC(t);		\
-	rotlwi	RT(t),RA(t),5;			\
-	rotlwi	RB(t),RB(t),30;			\
-	xor	%r6,%r6,RD(t);			\
-	add	%r0,RE(t),%r15;			\
-	add	RT(t),RT(t),%r6;		\
-	add	%r0,%r0,W(t);			\
-	add	RT(t),RT(t),%r0
+/* We use registers 11 - 26 for the W values */
+#define W(t)	((t)%16+11)
 
-#define STEPD2(t)				\
-	and	%r6,RB(t),RC(t);		\
-	and	%r0,RB(t),RD(t);		\
-	rotlwi	RT(t),RA(t),5;			\
-	rotlwi	RB(t),RB(t),30;			\
-	or	%r6,%r6,%r0;			\
-	and	%r0,RC(t),RD(t);		\
-	or	%r6,%r6,%r0;			\
-	add	%r0,RE(t),%r15;			\
-	add	RT(t),RT(t),%r6;		\
-	add	%r0,%r0,W(t);			\
-	add	RT(t),RT(t),%r0
+/* Register 5 is used for the constant k */
 
-#define LOADW(t)				\
-	lwz	W(t),(t)*4(%r4)
+/*
+ * The basic SHA-1 round function is:
+ * E += ROTL(A,5) + F(B,C,D) + W[i] + K;  B = ROTL(B,30)
+ * Then the variables are renamed: (A,B,C,D,E) = (E,A,B,C,D).
+ *
+ * Every 20 rounds, the function F() and the contant K changes:
+ * - 20 rounds of f0(b,c,d) = "bit wise b ? c : d" =  (^b & d) + (b & c)
+ * - 20 rounds of f1(b,c,d) = b^c^d = (b^d)^c
+ * - 20 rounds of f2(b,c,d) = majority(b,c,d) = (b&d) + ((b^d)&c)
+ * - 20 more rounds of f1(b,c,d)
+ *
+ * These are all scheduled for near-optimal performance on a G4.
+ * The G4 is a 3-issue out-of-order machine with 3 ALUs, but it can only
+ * *consider* starting the oldest 3 instructions per cycle.  So to get
+ * maximum performace out of it, you have to treat it as an in-order
+ * machine.  Which means interleaving the computation round t with the
+ * computation of W[t+4].
+ *
+ * The first 16 rounds use W values loaded directly from memory, while the
+ * remaining 64 use values computed from those first 16.  We preload
+ * 4 values before starting, so there are three kinds of rounds:
+ * - The first 12 (all f0) also load the W values from memory.
+ * - The next 64 compute W(i+4) in parallel. 8*f0, 20*f1, 20*f2, 16*f1.
+ * - The last 4 (all f1) do not do anything with W.
+ *
+ * Therefore, we have 6 different round functions:
+ * STEPD0_LOAD(t,s) - Perform round t and load W(s).  s < 16
+ * STEPD0_UPDATE(t,s) - Perform round t and compute W(s).  s >= 16.
+ * STEPD1_UPDATE(t,s)
+ * STEPD2_UPDATE(t,s)
+ * STEPD1(t) - Perform round t with no load or update.
+ *
+ * The G5 is more fully out-of-order, and can find the parallelism
+ * by itself.  The big limit is that it has a 2-cycle ALU latency, so
+ * even though it's 2-way, the code has to be scheduled as if it's
+ * 4-way, which can be a limit.  To help it, we try to schedule the
+ * read of RA(t) as late as possible so it doesn't stall waiting for
+ * the previous round's RE(t-1), and we try to rotate RB(t) as early
+ * as possible while reading RC(t) (= RB(t-1)) as late as possible.
+ */
 
-#define UPDATEW(t)				\
-	xor	%r0,W((t)-3),W((t)-8);		\
-	xor	W(t),W((t)-16),W((t)-14);	\
-	xor	W(t),W(t),%r0;			\
-	rotlwi	W(t),W(t),1
+/* the initial loads. */
+#define LOADW(s) \
+	lwz	W(s),(s)*4(%r4)
 
-#define STEP0LD4(t)				\
-	STEPD0(t);   LOADW((t)+4);		\
-	STEPD0((t)+1); LOADW((t)+5);		\
-	STEPD0((t)+2); LOADW((t)+6);		\
-	STEPD0((t)+3); LOADW((t)+7)
+/*
+ * Perform a step with F0, and load W(s).  Uses W(s) as a temporary
+ * before loading it.
+ * This is actually 10 instructions, which is an awkward fit.
+ * It can execute grouped as listed, or delayed one instruction.
+ * (If delayed two instructions, there is a stall before the start of the
+ * second line.)  Thus, two iterations take 7 cycles, 3.5 cycles per round.
+ */
+#define STEPD0_LOAD(t,s) \
+add RE(t),RE(t),W(t); andc   %r0,RD(t),RB(t);  and    W(s),RC(t),RB(t); \
+add RE(t),RE(t),%r0;  rotlwi %r0,RA(t),5;      rotlwi RB(t),RB(t),30;   \
+add RE(t),RE(t),W(s); add    %r0,%r0,%r5;      lwz    W(s),(s)*4(%r4);  \
+add RE(t),RE(t),%r0
 
-#define STEPUP4(t, fn)				\
-	STEP##fn(t);   UPDATEW((t)+4);		\
-	STEP##fn((t)+1); UPDATEW((t)+5);	\
-	STEP##fn((t)+2); UPDATEW((t)+6);	\
-	STEP##fn((t)+3); UPDATEW((t)+7)
+/*
+ * This is likewise awkward, 13 instructions.  However, it can also
+ * execute starting with 2 out of 3 possible moduli, so it does 2 rounds
+ * in 9 cycles, 4.5 cycles/round.
+ */
+#define STEPD0_UPDATE(t,s,loadk...) \
+add RE(t),RE(t),W(t); andc   %r0,RD(t),RB(t); xor    W(s),W((s)-16),W((s)-3); \
+add RE(t),RE(t),%r0;  and    %r0,RC(t),RB(t); xor    W(s),W(s),W((s)-8);      \
+add RE(t),RE(t),%r0;  rotlwi %r0,RA(t),5;     xor    W(s),W(s),W((s)-14);     \
+add RE(t),RE(t),%r5;  loadk; rotlwi RB(t),RB(t),30;  rotlwi W(s),W(s),1;     \
+add RE(t),RE(t),%r0
 
-#define STEPUP20(t, fn)				\
-	STEPUP4(t, fn);				\
-	STEPUP4((t)+4, fn);			\
-	STEPUP4((t)+8, fn);			\
-	STEPUP4((t)+12, fn);			\
-	STEPUP4((t)+16, fn)
+/* Nicely optimal.  Conveniently, also the most common. */
+#define STEPD1_UPDATE(t,s,loadk...) \
+add RE(t),RE(t),W(t); xor    %r0,RD(t),RB(t); xor    W(s),W((s)-16),W((s)-3); \
+add RE(t),RE(t),%r5;  loadk; xor %r0,%r0,RC(t);  xor W(s),W(s),W((s)-8);      \
+add RE(t),RE(t),%r0;  rotlwi %r0,RA(t),5;     xor    W(s),W(s),W((s)-14);     \
+add RE(t),RE(t),%r0;  rotlwi RB(t),RB(t),30;  rotlwi W(s),W(s),1
+
+/*
+ * The naked version, no UPDATE, for the last 4 rounds.  3 cycles per.
+ * We could use W(s) as a temp register, but we don't need it.
+ */
+#define STEPD1(t) \
+                        add   RE(t),RE(t),W(t); xor    %r0,RD(t),RB(t); \
+rotlwi RB(t),RB(t),30;  add   RE(t),RE(t),%r5;  xor    %r0,%r0,RC(t);   \
+add    RE(t),RE(t),%r0; rotlwi %r0,RA(t),5;     /* spare slot */        \
+add    RE(t),RE(t),%r0
+
+/*
+ * 14 instructions, 5 cycles per.  The majority function is a bit
+ * awkward to compute.  This can execute with a 1-instruction delay,
+ * but it causes a 2-instruction delay, which triggers a stall.
+ */
+#define STEPD2_UPDATE(t,s,loadk...) \
+add RE(t),RE(t),W(t); and    %r0,RD(t),RB(t); xor    W(s),W((s)-16),W((s)-3); \
+add RE(t),RE(t),%r0;  xor    %r0,RD(t),RB(t); xor    W(s),W(s),W((s)-8);      \
+add RE(t),RE(t),%r5;  loadk; and %r0,%r0,RC(t);  xor W(s),W(s),W((s)-14);     \
+add RE(t),RE(t),%r0;  rotlwi %r0,RA(t),5;     rotlwi W(s),W(s),1;             \
+add RE(t),RE(t),%r0;  rotlwi RB(t),RB(t),30
+
+#define STEP0_LOAD4(t,s)		\
+	STEPD0_LOAD(t,s);		\
+	STEPD0_LOAD((t+1),(s)+1);	\
+	STEPD0_LOAD((t)+2,(s)+2);	\
+	STEPD0_LOAD((t)+3,(s)+3)
+
+#define STEPUP4(fn, t, s, loadk...)		\
+	STEP##fn##_UPDATE(t,s,);		\
+	STEP##fn##_UPDATE((t)+1,(s)+1,);	\
+	STEP##fn##_UPDATE((t)+2,(s)+2,);	\
+	STEP##fn##_UPDATE((t)+3,(s)+3,loadk)
+
+#define STEPUP20(fn, t, s, loadk...)	\
+	STEPUP4(fn, t, s,);		\
+	STEPUP4(fn, (t)+4, (s)+4,);	\
+	STEPUP4(fn, (t)+8, (s)+8,);	\
+	STEPUP4(fn, (t)+12, (s)+12,);	\
+	STEPUP4(fn, (t)+16, (s)+16, loadk)
 
 	.globl	sha1_core
 sha1_core:
-	stwu	%r1,-FS(%r1)
-	stw	%r15,FS-68(%r1)
-	stw	%r16,FS-64(%r1)
-	stw	%r17,FS-60(%r1)
-	stw	%r18,FS-56(%r1)
-	stw	%r19,FS-52(%r1)
-	stw	%r20,FS-48(%r1)
-	stw	%r21,FS-44(%r1)
-	stw	%r22,FS-40(%r1)
-	stw	%r23,FS-36(%r1)
-	stw	%r24,FS-32(%r1)
-	stw	%r25,FS-28(%r1)
-	stw	%r26,FS-24(%r1)
-	stw	%r27,FS-20(%r1)
-	stw	%r28,FS-16(%r1)
-	stw	%r29,FS-12(%r1)
-	stw	%r30,FS-8(%r1)
-	stw	%r31,FS-4(%r1)
+	stwu	%r1,-80(%r1)
+	stmw	%r13,4(%r1)
 
 	/* Load up A - E */
-	lwz	RA(0),0(%r3)	/* A */
-	lwz	RB(0),4(%r3)	/* B */
-	lwz	RC(0),8(%r3)	/* C */
-	lwz	RD(0),12(%r3)	/* D */
-	lwz	RE(0),16(%r3)	/* E */
+	lmw	%r27,0(%r3)
 
 	mtctr	%r5
 
-1:	LOADW(0)
+1:
+	LOADW(0)
+	lis	%r5,0x5a82
+	mr	RE(0),%r31
 	LOADW(1)
+	mr	RD(0),%r30
+	mr	RC(0),%r29
 	LOADW(2)
+	ori	%r5,%r5,0x7999	/* K0-19 */
+	mr	RB(0),%r28
 	LOADW(3)
+	mr	RA(0),%r27
 
-	lis	%r15,0x5a82	/* K0-19 */
-	ori	%r15,%r15,0x7999
-	STEP0LD4(0)
-	STEP0LD4(4)
-	STEP0LD4(8)
-	STEPUP4(12, D0)
-	STEPUP4(16, D0)
+	STEP0_LOAD4(0, 4)
+	STEP0_LOAD4(4, 8)
+	STEP0_LOAD4(8, 12)
+	STEPUP4(D0, 12, 16,)
+	STEPUP4(D0, 16, 20, lis %r5,0x6ed9)
 
-	lis	%r15,0x6ed9	/* K20-39 */
-	ori	%r15,%r15,0xeba1
-	STEPUP20(20, D1)
+	ori	%r5,%r5,0xeba1	/* K20-39 */
+	STEPUP20(D1, 20, 24, lis %r5,0x8f1b)
 
-	lis	%r15,0x8f1b	/* K40-59 */
-	ori	%r15,%r15,0xbcdc
-	STEPUP20(40, D2)
+	ori	%r5,%r5,0xbcdc	/* K40-59 */
+	STEPUP20(D2, 40, 44, lis %r5,0xca62)
 
-	lis	%r15,0xca62	/* K60-79 */
-	ori	%r15,%r15,0xc1d6
-	STEPUP4(60, D1)
-	STEPUP4(64, D1)
-	STEPUP4(68, D1)
-	STEPUP4(72, D1)
+	ori	%r5,%r5,0xc1d6	/* K60-79 */
+	STEPUP4(D1, 60, 64,)
+	STEPUP4(D1, 64, 68,)
+	STEPUP4(D1, 68, 72,)
+	STEPUP4(D1, 72, 76,)
+	addi	%r4,%r4,64
 	STEPD1(76)
 	STEPD1(77)
 	STEPD1(78)
 	STEPD1(79)
 
-	lwz	%r20,16(%r3)
-	lwz	%r19,12(%r3)
-	lwz	%r18,8(%r3)
-	lwz	%r17,4(%r3)
-	lwz	%r16,0(%r3)
-	add	%r20,RE(80),%r20
-	add	RD(0),RD(80),%r19
-	add	RC(0),RC(80),%r18
-	add	RB(0),RB(80),%r17
-	add	RA(0),RA(80),%r16
-	mr	RE(0),%r20
-	stw	RA(0),0(%r3)
-	stw	RB(0),4(%r3)
-	stw	RC(0),8(%r3)
-	stw	RD(0),12(%r3)
-	stw	RE(0),16(%r3)
+	/* Add results to original values */
+	add	%r31,%r31,RE(0)
+	add	%r30,%r30,RD(0)
+	add	%r29,%r29,RC(0)
+	add	%r28,%r28,RB(0)
+	add	%r27,%r27,RA(0)
 
-	addi	%r4,%r4,64
 	bdnz	1b
 
-	lwz	%r15,FS-68(%r1)
-	lwz	%r16,FS-64(%r1)
-	lwz	%r17,FS-60(%r1)
-	lwz	%r18,FS-56(%r1)
-	lwz	%r19,FS-52(%r1)
-	lwz	%r20,FS-48(%r1)
-	lwz	%r21,FS-44(%r1)
-	lwz	%r22,FS-40(%r1)
-	lwz	%r23,FS-36(%r1)
-	lwz	%r24,FS-32(%r1)
-	lwz	%r25,FS-28(%r1)
-	lwz	%r26,FS-24(%r1)
-	lwz	%r27,FS-20(%r1)
-	lwz	%r28,FS-16(%r1)
-	lwz	%r29,FS-12(%r1)
-	lwz	%r30,FS-8(%r1)
-	lwz	%r31,FS-4(%r1)
-	addi	%r1,%r1,FS
+	/* Save final hash, restore registers, and return */
+	stmw	%r27,0(%r3)
+	lmw	%r13,4(%r1)
+	addi	%r1,%r1,80
 	blr
diff --git a/quote.c b/quote.c
index 1910d00..e220dcc 100644
--- a/quote.c
+++ b/quote.c
@@ -45,6 +45,23 @@
 	return len;
 }
 
+void sq_quote_print(FILE *stream, const char *src)
+{
+	char c;
+
+	fputc('\'', stream);
+	while ((c = *src++)) {
+		if (need_bs_quote(c)) {
+			fputs("'\\", stream);
+			fputc(c, stream);
+			fputc('\'', stream);
+		} else {
+			fputc(c, stream);
+		}
+	}
+	fputc('\'', stream);
+}
+
 char *sq_quote(const char *src)
 {
 	char *buf;
diff --git a/quote.h b/quote.h
index c1ab378..fc5481e 100644
--- a/quote.h
+++ b/quote.h
@@ -29,6 +29,7 @@
  */
 
 extern char *sq_quote(const char *src);
+extern void sq_quote_print(FILE *stream, const char *src);
 extern size_t sq_quote_buf(char *dst, size_t n, const char *src);
 
 /* This unwraps what sq_quote() produces in place, but returns
diff --git a/read-cache.c b/read-cache.c
index 3c32aae..f92cdaa 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -24,6 +24,11 @@
 
 struct cache_tree *active_cache_tree = NULL;
 
+int cache_errno = 0;
+
+static void *cache_mmap = NULL;
+static size_t cache_mmap_size = 0;
+
 /*
  * This only updates the "non-critical" parts of the directory
  * cache, ie the parts that aren't tracked by GIT, and only used
@@ -56,7 +61,7 @@
 		unsigned char sha1[20];
 		if (!index_fd(sha1, fd, st, 0, NULL))
 			match = memcmp(sha1, ce->sha1, 20);
-		close(fd);
+		/* index_fd() closed the file descriptor already */
 	}
 	return match;
 }
@@ -314,6 +319,45 @@
 	return 0;
 }
 
+int add_file_to_index(const char *path, int verbose)
+{
+	int size, namelen;
+	struct stat st;
+	struct cache_entry *ce;
+
+	if (lstat(path, &st))
+		die("%s: unable to stat (%s)", path, strerror(errno));
+
+	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))
+		die("%s: can only add regular files or symbolic links", path);
+
+	namelen = strlen(path);
+	size = cache_entry_size(namelen);
+	ce = xcalloc(1, size);
+	memcpy(ce->name, path, namelen);
+	ce->ce_flags = htons(namelen);
+	fill_stat_cache_info(ce, &st);
+
+	ce->ce_mode = create_ce_mode(st.st_mode);
+	if (!trust_executable_bit) {
+		/* If there is an existing entry, pick the mode bits
+		 * from it.
+		 */
+		int pos = cache_name_pos(path, namelen);
+		if (pos >= 0)
+			ce->ce_mode = active_cache[pos]->ce_mode;
+	}
+
+	if (index_path(ce->sha1, path, &st, 1))
+		die("unable to index file %s", path);
+	if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD))
+		die("unable to add %s to index",path);
+	if (verbose)
+		printf("add '%s'\n", path);
+	cache_tree_invalidate_path(active_cache_tree, path);
+	return 0;
+}
+
 int ce_same_name(struct cache_entry *a, struct cache_entry *b)
 {
 	int len = ce_namelen(a);
@@ -577,22 +621,6 @@
 	return 0;
 }
 
-/* Three functions to allow overloaded pointer return; see linux/err.h */
-static inline void *ERR_PTR(long error)
-{
-	return (void *) error;
-}
-
-static inline long PTR_ERR(const void *ptr)
-{
-	return (long) ptr;
-}
-
-static inline long IS_ERR(const void *ptr)
-{
-	return (unsigned long)ptr > (unsigned long)-1000L;
-}
-
 /*
  * "refresh" does not calculate a new sha1 file or bring the
  * cache up-to-date for mode/content changes. But what it
@@ -604,14 +632,16 @@
  * For example, you'd want to do this after doing a "git-read-tree",
  * to link up the stat cache details with the proper files.
  */
-static struct cache_entry *refresh_entry(struct cache_entry *ce, int really)
+struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
 {
 	struct stat st;
 	struct cache_entry *updated;
 	int changed, size;
 
-	if (lstat(ce->name, &st) < 0)
-		return ERR_PTR(-errno);
+	if (lstat(ce->name, &st) < 0) {
+		cache_errno = errno;
+		return NULL;
+	}
 
 	changed = ce_match_stat(ce, &st, really);
 	if (!changed) {
@@ -619,11 +649,13 @@
 		    !(ce->ce_flags & htons(CE_VALID)))
 			; /* mark this one VALID again */
 		else
-			return NULL;
+			return ce;
 	}
 
-	if (ce_modified(ce, &st, really))
-		return ERR_PTR(-EINVAL);
+	if (ce_modified(ce, &st, really)) {
+		cache_errno = EINVAL;
+		return NULL;
+	}
 
 	size = ce_size(ce);
 	updated = xmalloc(size);
@@ -666,13 +698,13 @@
 			continue;
 		}
 
-		new = refresh_entry(ce, really);
-		if (!new)
+		new = refresh_cache_entry(ce, really);
+		if (new == ce)
 			continue;
-		if (IS_ERR(new)) {
-			if (not_new && PTR_ERR(new) == -ENOENT)
+		if (!new) {
+			if (not_new && cache_errno == ENOENT)
 				continue;
-			if (really && PTR_ERR(new) == -EINVAL) {
+			if (really && cache_errno == EINVAL) {
 				/* If we are doing --really-refresh that
 				 * means the index is not valid anymore.
 				 */
@@ -729,39 +761,43 @@
 
 int read_cache(void)
 {
+	return read_cache_from(get_index_file());
+}
+
+/* remember to discard_cache() before reading a different cache! */
+int read_cache_from(const char *path)
+{
 	int fd, i;
 	struct stat st;
-	unsigned long size, offset;
-	void *map;
+	unsigned long offset;
 	struct cache_header *hdr;
 
 	errno = EBUSY;
-	if (active_cache)
+	if (cache_mmap)
 		return active_nr;
 
 	errno = ENOENT;
 	index_file_timestamp = 0;
-	fd = open(get_index_file(), O_RDONLY);
+	fd = open(path, O_RDONLY);
 	if (fd < 0) {
 		if (errno == ENOENT)
 			return 0;
 		die("index file open failed (%s)", strerror(errno));
 	}
 
-	size = 0; // avoid gcc warning
-	map = MAP_FAILED;
+	cache_mmap = MAP_FAILED;
 	if (!fstat(fd, &st)) {
-		size = st.st_size;
+		cache_mmap_size = st.st_size;
 		errno = EINVAL;
-		if (size >= sizeof(struct cache_header) + 20)
-			map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+		if (cache_mmap_size >= sizeof(struct cache_header) + 20)
+			cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
 	}
 	close(fd);
-	if (map == MAP_FAILED)
+	if (cache_mmap == MAP_FAILED)
 		die("index file mmap failed (%s)", strerror(errno));
 
-	hdr = map;
-	if (verify_hdr(hdr, size) < 0)
+	hdr = cache_mmap;
+	if (verify_hdr(hdr, cache_mmap_size) < 0)
 		goto unmap;
 
 	active_nr = ntohl(hdr->hdr_entries);
@@ -770,12 +806,12 @@
 
 	offset = sizeof(*hdr);
 	for (i = 0; i < active_nr; i++) {
-		struct cache_entry *ce = (struct cache_entry *) ((char *) map + offset);
+		struct cache_entry *ce = (struct cache_entry *) ((char *) cache_mmap + offset);
 		offset = offset + ce_size(ce);
 		active_cache[i] = ce;
 	}
 	index_file_timestamp = st.st_mtime;
-	while (offset <= size - 20 - 8) {
+	while (offset <= cache_mmap_size - 20 - 8) {
 		/* After an array of active_nr index entries,
 		 * there can be arbitrary number of extended
 		 * sections, each of which is prefixed with
@@ -783,10 +819,10 @@
 		 * in 4-byte network byte order.
 		 */
 		unsigned long extsize;
-		memcpy(&extsize, (char *) map + offset + 4, 4);
+		memcpy(&extsize, (char *) cache_mmap + offset + 4, 4);
 		extsize = ntohl(extsize);
-		if (read_index_extension(((const char *) map) + offset,
-					 (char *) map + offset + 8,
+		if (read_index_extension(((const char *) cache_mmap) + offset,
+					 (char *) cache_mmap + offset + 8,
 					 extsize) < 0)
 			goto unmap;
 		offset += 8;
@@ -795,7 +831,7 @@
 	return active_nr;
 
 unmap:
-	munmap(map, size);
+	munmap(cache_mmap, cache_mmap_size);
 	errno = EINVAL;
 	die("index file corrupt");
 }
diff --git a/refs.c b/refs.c
index 713ca46..86ef916 100644
--- a/refs.c
+++ b/refs.c
@@ -147,7 +147,7 @@
 			namelen = strlen(de->d_name);
 			if (namelen > 255)
 				continue;
-			if (namelen>5 && !strcmp(de->d_name+namelen-5,".lock"))
+			if (has_extension(de->d_name, ".lock"))
 				continue;
 			memcpy(path + baselen, de->d_name, namelen+1);
 			if (stat(git_path("%s", path), &st) < 0)
@@ -294,6 +294,7 @@
 	int plen,
 	const unsigned char *old_sha1, int mustexist)
 {
+	const char *orig_path = path;
 	struct ref_lock *lock;
 	struct stat st;
 
@@ -303,7 +304,11 @@
 	plen = strlen(path) - plen;
 	path = resolve_ref(path, lock->old_sha1, mustexist);
 	if (!path) {
+		int last_errno = errno;
+		error("unable to resolve reference %s: %s",
+			orig_path, strerror(errno));
 		unlock_ref(lock);
+		errno = last_errno;
 		return NULL;
 	}
 	lock->lk = xcalloc(1, sizeof(struct lock_file));
@@ -314,13 +319,7 @@
 
 	if (safe_create_leading_directories(lock->ref_file))
 		die("unable to create directory for %s", lock->ref_file);
-	lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file);
-	if (lock->lock_fd < 0) {
-		error("Couldn't open lock file %s: %s",
-		      lock->lk->filename, strerror(errno));
-		unlock_ref(lock);
-		return NULL;
-	}
+	lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1);
 
 	return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 }
@@ -362,7 +361,7 @@
 	int logfd, written, oflags = O_APPEND | O_WRONLY;
 	unsigned maxlen, len;
 	char *logrec;
-	const char *comitter;
+	const char *committer;
 
 	if (log_all_ref_updates) {
 		if (safe_create_leading_directories(lock->log_file) < 0)
@@ -379,24 +378,23 @@
 			lock->log_file, strerror(errno));
 	}
 
-	setup_ident();
-	comitter = git_committer_info(1);
+	committer = git_committer_info(1);
 	if (msg) {
-		maxlen = strlen(comitter) + strlen(msg) + 2*40 + 5;
+		maxlen = strlen(committer) + strlen(msg) + 2*40 + 5;
 		logrec = xmalloc(maxlen);
 		len = snprintf(logrec, maxlen, "%s %s %s\t%s\n",
 			sha1_to_hex(lock->old_sha1),
 			sha1_to_hex(sha1),
-			comitter,
+			committer,
 			msg);
 	}
 	else {
-		maxlen = strlen(comitter) + 2*40 + 4;
+		maxlen = strlen(committer) + 2*40 + 4;
 		logrec = xmalloc(maxlen);
 		len = snprintf(logrec, maxlen, "%s %s %s\n",
 			sha1_to_hex(lock->old_sha1),
 			sha1_to_hex(sha1),
-			comitter);
+			committer);
 	}
 	written = len <= maxlen ? write(logfd, logrec, len) : -1;
 	free(logrec);
diff --git a/revision.c b/revision.c
index 880fb7b..5a91d06 100644
--- a/revision.c
+++ b/revision.c
@@ -135,7 +135,7 @@
 	/*
 	 * Tag object? Look what it points to..
 	 */
-	while (object->type == TYPE_TAG) {
+	while (object->type == OBJ_TAG) {
 		struct tag *tag = (struct tag *) object;
 		if (revs->tag_objects && !(flags & UNINTERESTING))
 			add_pending_object(revs, object, tag->tag);
@@ -148,7 +148,7 @@
 	 * Commit object? Just return it, we'll do all the complex
 	 * reachability crud.
 	 */
-	if (object->type == TYPE_COMMIT) {
+	if (object->type == OBJ_COMMIT) {
 		struct commit *commit = (struct commit *)object;
 		if (parse_commit(commit) < 0)
 			die("unable to parse commit %s", name);
@@ -164,7 +164,7 @@
 	 * Tree object? Either mark it uniniteresting, or add it
 	 * to the list of objects to look at later..
 	 */
-	if (object->type == TYPE_TREE) {
+	if (object->type == OBJ_TREE) {
 		struct tree *tree = (struct tree *)object;
 		if (!revs->tree_objects)
 			return NULL;
@@ -179,7 +179,7 @@
 	/*
 	 * Blob object? You know the drill by now..
 	 */
-	if (object->type == TYPE_BLOB) {
+	if (object->type == OBJ_BLOB) {
 		struct blob *blob = (struct blob *)object;
 		if (!revs->blob_objects)
 			return NULL;
@@ -494,11 +494,11 @@
 		return 0;
 	while (1) {
 		it = get_reference(revs, arg, sha1, 0);
-		if (it->type != TYPE_TAG)
+		if (it->type != OBJ_TAG)
 			break;
 		memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
 	}
-	if (it->type != TYPE_COMMIT)
+	if (it->type != OBJ_COMMIT)
 		return 0;
 	commit = (struct commit *)it;
 	for (parents = commit->parents; parents; parents = parents->next) {
@@ -509,7 +509,7 @@
 	return 1;
 }
 
-void init_revisions(struct rev_info *revs)
+void init_revisions(struct rev_info *revs, const char *prefix)
 {
 	memset(revs, 0, sizeof(*revs));
 
@@ -521,7 +521,7 @@
 	revs->pruning.change = file_change;
 	revs->lifo = 1;
 	revs->dense = 1;
-	revs->prefix = setup_git_directory();
+	revs->prefix = prefix;
 	revs->max_age = -1;
 	revs->min_age = -1;
 	revs->max_count = -1;
@@ -537,6 +537,61 @@
 	diff_setup(&revs->diffopt);
 }
 
+static void add_pending_commit_list(struct rev_info *revs,
+                                    struct commit_list *commit_list,
+                                    unsigned int flags)
+{
+	while (commit_list) {
+		struct object *object = &commit_list->item->object;
+		object->flags |= flags;
+		add_pending_object(revs, object, sha1_to_hex(object->sha1));
+		commit_list = commit_list->next;
+	}
+}
+
+static void prepare_show_merge(struct rev_info *revs)
+{
+	struct commit_list *bases;
+	struct commit *head, *other;
+	unsigned char sha1[20];
+	const char **prune = NULL;
+	int i, prune_num = 1; /* counting terminating NULL */
+
+	if (get_sha1("HEAD", sha1) || !(head = lookup_commit(sha1)))
+		die("--merge without HEAD?");
+	if (get_sha1("MERGE_HEAD", sha1) || !(other = lookup_commit(sha1)))
+		die("--merge without MERGE_HEAD?");
+	add_pending_object(revs, &head->object, "HEAD");
+	add_pending_object(revs, &other->object, "MERGE_HEAD");
+	bases = get_merge_bases(head, other, 1);
+	while (bases) {
+		struct commit *it = bases->item;
+		struct commit_list *n = bases->next;
+		free(bases);
+		bases = n;
+		it->object.flags |= UNINTERESTING;
+		add_pending_object(revs, &it->object, "(merge-base)");
+	}
+
+	if (!active_nr)
+		read_cache();
+	for (i = 0; i < active_nr; i++) {
+		struct cache_entry *ce = active_cache[i];
+		if (!ce_stage(ce))
+			continue;
+		if (ce_path_match(ce, revs->prune_data)) {
+			prune_num++;
+			prune = xrealloc(prune, sizeof(*prune) * prune_num);
+			prune[prune_num-2] = ce->name;
+			prune[prune_num-1] = NULL;
+		}
+		while ((i+1 < active_nr) &&
+		       ce_same_name(ce, active_cache[i+1]))
+			i++;
+	}
+	revs->prune_data = prune;
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -546,7 +601,7 @@
  */
 int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)
 {
-	int i, flags, seen_dashdash;
+	int i, flags, seen_dashdash, show_merge;
 	const char **unrecognized = argv + 1;
 	int left = 1;
 
@@ -563,7 +618,7 @@
 		break;
 	}
 
-	flags = 0;
+	flags = show_merge = 0;
 	for (i = 1; i < argc; i++) {
 		struct object *object;
 		const char *arg = argv[i];
@@ -630,6 +685,10 @@
 				def = argv[i];
 				continue;
 			}
+			if (!strcmp(arg, "--merge")) {
+				show_merge = 1;
+				continue;
+			}
 			if (!strcmp(arg, "--topo-order")) {
 				revs->topo_order = 1;
 				continue;
@@ -772,27 +831,46 @@
 			unsigned char from_sha1[20];
 			const char *next = dotdot + 2;
 			const char *this = arg;
+			int symmetric = *next == '.';
+			unsigned int flags_exclude = flags ^ UNINTERESTING;
+
 			*dotdot = 0;
+			next += symmetric;
+
 			if (!*next)
 				next = "HEAD";
 			if (dotdot == arg)
 				this = "HEAD";
 			if (!get_sha1(this, from_sha1) &&
 			    !get_sha1(next, sha1)) {
-				struct object *exclude;
-				struct object *include;
+				struct commit *a, *b;
+				struct commit_list *exclude;
 
-				exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
-				include = get_reference(revs, next, sha1, flags);
-				if (!exclude || !include)
-					die("Invalid revision range %s..%s", arg, next);
+				a = lookup_commit_reference(from_sha1);
+				b = lookup_commit_reference(sha1);
+				if (!a || !b) {
+					die(symmetric ?
+					    "Invalid symmetric difference expression %s...%s" :
+					    "Invalid revision range %s..%s",
+					    arg, next);
+				}
 
 				if (!seen_dashdash) {
 					*dotdot = '.';
 					verify_non_filename(revs->prefix, arg);
 				}
-				add_pending_object(revs, exclude, this);
-				add_pending_object(revs, include, next);
+
+				if (symmetric) {
+					exclude = get_merge_bases(a, b, 1);
+					add_pending_commit_list(revs, exclude,
+					                        flags_exclude);
+					free_commit_list(exclude);
+					a->object.flags |= flags;
+				} else
+					a->object.flags |= flags_exclude;
+				b->object.flags |= flags;
+				add_pending_object(revs, &a->object, this);
+				add_pending_object(revs, &b->object, next);
 				continue;
 			}
 			*dotdot = '.';
@@ -832,6 +910,8 @@
 		object = get_reference(revs, arg, sha1, flags ^ local_flags);
 		add_pending_object(revs, object, arg);
 	}
+	if (show_merge)
+		prepare_show_merge(revs);
 	if (def && !revs->pending.nr) {
 		unsigned char sha1[20];
 		struct object *object;
@@ -852,12 +932,12 @@
 	}
 	if (revs->combine_merges) {
 		revs->ignore_merges = 0;
-		if (revs->dense_combined_merges &&
-		    (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT))
+		if (revs->dense_combined_merges && !revs->diffopt.output_format)
 			revs->diffopt.output_format = DIFF_FORMAT_PATCH;
 	}
 	revs->diffopt.abbrev = revs->abbrev;
-	diff_setup_done(&revs->diffopt);
+	if (diff_setup_done(&revs->diffopt) < 0)
+		die("diff_setup_done failed");
 
 	return left;
 }
diff --git a/revision.h b/revision.h
index c010a08..0c3b8d9 100644
--- a/revision.h
+++ b/revision.h
@@ -61,6 +61,8 @@
 	struct log_info *loginfo;
 	int		nr, total;
 	const char	*mime_boundary;
+	const char	*message_id;
+	const char	*ref_message_id;
 	const char	*add_signoff;
 	const char	*extra_headers;
 
@@ -85,7 +87,7 @@
 extern int rev_same_tree_as_empty(struct rev_info *, struct tree *t1);
 extern int rev_compare_tree(struct rev_info *, struct tree *t1, struct tree *t2);
 
-extern void init_revisions(struct rev_info *revs);
+extern void init_revisions(struct rev_info *revs, const char *prefix);
 extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);
 extern void prepare_revision_walk(struct rev_info *revs);
 extern struct commit *get_revision(struct rev_info *revs);
diff --git a/send-pack.c b/send-pack.c
index af93b11..10bc8bc 100644
--- a/send-pack.c
+++ b/send-pack.c
@@ -151,12 +151,12 @@
 	 * old.  Otherwise we require --force.
 	 */
 	o = deref_tag(parse_object(old_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	old = (struct commit *) o;
 
 	o = deref_tag(parse_object(new_sha1), NULL, 0);
-	if (!o || o->type != TYPE_COMMIT)
+	if (!o || o->type != OBJ_COMMIT)
 		return 0;
 	new = (struct commit *) o;
 
@@ -239,7 +239,7 @@
 	int expect_status_report = 0;
 
 	/* No funny business with the matcher */
-	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, 1);
+	remote_tail = get_remote_heads(in, &remote_refs, 0, NULL, REF_NORMAL);
 	get_local_heads();
 
 	/* Does the other end support the reporting? */
diff --git a/server-info.c b/server-info.c
index 0eb5132..7df628f 100644
--- a/server-info.c
+++ b/server-info.c
@@ -12,7 +12,7 @@
 	struct object *o = parse_object(sha1);
 
 	fprintf(info_ref_fp, "%s	%s\n", sha1_to_hex(sha1), path);
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, path, 0);
 		if (o)
 			fprintf(info_ref_fp, "%s	%s^{}\n",
@@ -94,7 +94,7 @@
 
 	fp = fopen(infofile, "r");
 	if (!fp)
-		return 1; /* nonexisting is not an error. */
+		return 1; /* nonexistent is not an error. */
 
 	while (fgets(line, sizeof(line), fp)) {
 		int len = strlen(line);
diff --git a/setup.c b/setup.c
index 4612f11..2afdba4 100644
--- a/setup.c
+++ b/setup.c
@@ -184,6 +184,10 @@
 		}
 		return NULL;
 	bad_dir_environ:
+		if (nongit_ok) {
+			*nongit_ok = 1;
+			return NULL;
+		}
 		path[len] = 0;
 		die("Not a git repository: '%s'", path);
 	}
diff --git a/sha1_file.c b/sha1_file.c
index 8179630..3db956d 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -453,7 +453,7 @@
 {
 	if (!p->pack_size) {
 		struct stat st;
-		// We created the struct before we had the pack
+		/* We created the struct before we had the pack */
 		stat(p->pack_name, &st);
 		if (!S_ISREG(st.st_mode))
 			die("packfile %s not a regular file", p->pack_name);
@@ -590,7 +590,7 @@
 		int namelen = strlen(de->d_name);
 		struct packed_git *p;
 
-		if (strcmp(de->d_name + namelen - 4, ".idx"))
+		if (!has_extension(de->d_name, ".idx"))
 			continue;
 
 		/* we have .idx.  Is it a file we can map? */
@@ -684,26 +684,74 @@
 	return map;
 }
 
-int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size)
+static int unpack_sha1_header(z_stream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz)
 {
+	unsigned char c;
+	unsigned int word, bits;
+	unsigned long size;
+	static const char *typename[8] = {
+		NULL,	/* OBJ_EXT */
+		"commit", "tree", "blob", "tag",
+		NULL, NULL, NULL
+	};
+	const char *type;
+
 	/* Get the data stream */
 	memset(stream, 0, sizeof(*stream));
 	stream->next_in = map;
 	stream->avail_in = mapsize;
 	stream->next_out = buffer;
-	stream->avail_out = size;
+	stream->avail_out = bufsiz;
 
+	/*
+	 * Is it a zlib-compressed buffer? If so, the first byte
+	 * must be 0x78 (15-bit window size, deflated), and the
+	 * first 16-bit word is evenly divisible by 31
+	 */
+	word = (map[0] << 8) + map[1];
+	if (map[0] == 0x78 && !(word % 31)) {
+		inflateInit(stream);
+		return inflate(stream, 0);
+	}
+
+	c = *map++;
+	mapsize--;
+	type = typename[(c >> 4) & 7];
+	if (!type)
+		return -1;
+
+	bits = 4;
+	size = c & 0xf;
+	while ((c & 0x80)) {
+		if (bits >= 8*sizeof(long))
+			return -1;
+		c = *map++;
+		size += (c & 0x7f) << bits;
+		bits += 7;
+		mapsize--;
+	}
+
+	/* Set up the stream for the rest.. */
+	stream->next_in = map;
+	stream->avail_in = mapsize;
 	inflateInit(stream);
-	return inflate(stream, 0);
+
+	/* And generate the fake traditional header */
+	stream->total_out = 1 + snprintf(buffer, bufsiz, "%s %lu", type, size);
+	return 0;
 }
 
 static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
 {
 	int bytes = strlen(buffer) + 1;
 	unsigned char *buf = xmalloc(1+size);
+	unsigned long n;
 
-	memcpy(buf, (char *) buffer + bytes, stream->total_out - bytes);
-	bytes = stream->total_out - bytes;
+	n = stream->total_out - bytes;
+	if (n > size)
+		n = size;
+	memcpy(buf, (char *) buffer + bytes, n);
+	bytes = n;
 	if (bytes < size) {
 		stream->next_out = buf + bytes;
 		stream->avail_out = size - bytes;
@@ -720,7 +768,7 @@
  * too permissive for what we want to check. So do an anal
  * object header parse by hand.
  */
-int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
+static int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
 {
 	int i;
 	unsigned long size;
@@ -1331,31 +1379,29 @@
 static int link_temp_to_file(const char *tmpfile, char *filename)
 {
 	int ret;
+	char *dir;
 
 	if (!link(tmpfile, filename))
 		return 0;
 
 	/*
-	 * Try to mkdir the last path component if that failed
-	 * with an ENOENT.
+	 * Try to mkdir the last path component if that failed.
 	 *
 	 * Re-try the "link()" regardless of whether the mkdir
 	 * succeeds, since a race might mean that somebody
 	 * else succeeded.
 	 */
 	ret = errno;
-	if (ret == ENOENT) {
-		char *dir = strrchr(filename, '/');
-		if (dir) {
-			*dir = 0;
-			mkdir(filename, 0777);
-			if (adjust_shared_perm(filename))
-				return -2;
-			*dir = '/';
-			if (!link(tmpfile, filename))
-				return 0;
-			ret = errno;
-		}
+	dir = strrchr(filename, '/');
+	if (dir) {
+		*dir = 0;
+		mkdir(filename, 0777);
+		if (adjust_shared_perm(filename))
+			return -2;
+		*dir = '/';
+		if (!link(tmpfile, filename))
+			return 0;
+		ret = errno;
 	}
 	return ret;
 }
@@ -1414,6 +1460,49 @@
 	return 0;
 }
 
+static int write_binary_header(unsigned char *hdr, enum object_type type, unsigned long len)
+{
+	int hdr_len;
+	unsigned char c;
+
+	c = (type << 4) | (len & 15);
+	len >>= 4;
+	hdr_len = 1;
+	while (len) {
+		*hdr++ = c | 0x80;
+		hdr_len++;
+		c = (len & 0x7f);
+		len >>= 7;
+	}
+	*hdr = c;
+	return hdr_len;
+}
+
+static void setup_object_header(z_stream *stream, const char *type, unsigned long len)
+{
+	int obj_type, hdr;
+
+	if (use_legacy_headers) {
+		while (deflate(stream, 0) == Z_OK)
+			/* nothing */;
+		return;
+	}
+	if (!strcmp(type, blob_type))
+		obj_type = OBJ_BLOB;
+	else if (!strcmp(type, tree_type))
+		obj_type = OBJ_TREE;
+	else if (!strcmp(type, commit_type))
+		obj_type = OBJ_COMMIT;
+	else if (!strcmp(type, tag_type))
+		obj_type = OBJ_TAG;
+	else
+		die("trying to generate bogus object of type '%s'", type);
+	hdr = write_binary_header(stream->next_out, obj_type, len);
+	stream->total_out = hdr;
+	stream->next_out += hdr;
+	stream->avail_out -= hdr;
+}
+
 int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
 	int size;
@@ -1458,8 +1547,8 @@
 
 	/* Set it up */
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, Z_BEST_COMPRESSION);
-	size = deflateBound(&stream, len+hdrlen);
+	deflateInit(&stream, zlib_compression_level);
+	size = 8 + deflateBound(&stream, len+hdrlen);
 	compressed = xmalloc(size);
 
 	/* Compress it */
@@ -1469,8 +1558,7 @@
 	/* First header.. */
 	stream.next_in = hdr;
 	stream.avail_in = hdrlen;
-	while (deflate(&stream, 0) == Z_OK)
-		/* nothing */;
+	setup_object_header(&stream, type, len);
 
 	/* Then the data itself.. */
 	stream.next_in = buf;
@@ -1504,14 +1592,14 @@
 	int hdrlen;
 	void *buf;
 
-	// need to unpack and recompress it by itself
+	/* need to unpack and recompress it by itself */
 	unpacked = read_packed_sha1(sha1, type, &len);
 
 	hdrlen = sprintf(hdr, "%s %lu", type, len) + 1;
 
 	/* Set it up */
 	memset(&stream, 0, sizeof(stream));
-	deflateInit(&stream, Z_BEST_COMPRESSION);
+	deflateInit(&stream, zlib_compression_level);
 	size = deflateBound(&stream, len + hdrlen);
 	buf = xmalloc(size);
 
@@ -1660,7 +1748,7 @@
 
 /*
  * reads from fd as long as possible into a supplied buffer of size bytes.
- * If neccessary the buffer's size is increased using realloc()
+ * If necessary the buffer's size is increased using realloc()
  *
  * returns 0 if anything went fine and -1 otherwise
  *
diff --git a/sha1_name.c b/sha1_name.c
index f2cbafa..c5a05fa 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -193,7 +193,7 @@
 
 	is_null = !memcmp(sha1, null_sha1, 20);
 	memcpy(hex, sha1_to_hex(sha1), 40);
-	if (len == 40)
+	if (len == 40 || !len)
 		return hex;
 	while (len < 40) {
 		unsigned char sha1_ret[20];
@@ -381,13 +381,13 @@
 
 	sp++; /* beginning of type name, or closing brace for empty */
 	if (!strncmp(commit_type, sp, 6) && sp[6] == '}')
-		expected_type = TYPE_COMMIT;
+		expected_type = OBJ_COMMIT;
 	else if (!strncmp(tree_type, sp, 4) && sp[4] == '}')
-		expected_type = TYPE_TREE;
+		expected_type = OBJ_TREE;
 	else if (!strncmp(blob_type, sp, 4) && sp[4] == '}')
-		expected_type = TYPE_BLOB;
+		expected_type = OBJ_BLOB;
 	else if (sp[0] == '}')
-		expected_type = TYPE_NONE;
+		expected_type = OBJ_NONE;
 	else
 		return -1;
 
@@ -416,9 +416,9 @@
 				memcpy(sha1, o->sha1, 20);
 				return 0;
 			}
-			if (o->type == TYPE_TAG)
+			if (o->type == OBJ_TAG)
 				o = ((struct tag*) o)->tagged;
-			else if (o->type == TYPE_COMMIT)
+			else if (o->type == OBJ_COMMIT)
 				o = &(((struct commit *) o)->tree->object);
 			else
 				return error("%.*s: expected %s type, but the object dereferences to %s type",
diff --git a/ssh-fetch.c b/ssh-fetch.c
index 1e59cd2..c7d8fa8 100644
--- a/ssh-fetch.c
+++ b/ssh-fetch.c
@@ -68,7 +68,7 @@
 	struct object_list *temp;
 
 	if (memcmp(sha1, in_transit->item->sha1, 20)) {
-		// we must have already fetched it to clean the queue
+		/* we must have already fetched it to clean the queue */
 		return has_sha1_file(sha1) ? 0 : -1;
 	}
 	prefetches--;
@@ -85,7 +85,7 @@
 		if (read(fd_in, &remote, 1) < 1)
 			return -1;
 	}
-	//fprintf(stderr, "Got %d\n", remote);
+	/* fprintf(stderr, "Got %d\n", remote); */
 	if (remote < 0)
 		return remote;
 	ret = write_sha1_from_fd(sha1, fd_in, conn_buf, 4096, &conn_buf_posn);
@@ -120,9 +120,10 @@
 
 static const char ssh_fetch_usage[] =
   MY_PROGRAM_NAME
-  " [-c] [-t] [-a] [-v] [-d] [--recover] [-w ref] commit-id url";
+  " [-c] [-t] [-a] [-v] [--recover] [-w ref] commit-id url";
 int main(int argc, char **argv)
 {
+	const char *write_ref = NULL;
 	char *commit_id;
 	char *url;
 	int arg = 1;
@@ -131,6 +132,7 @@
 	prog = getenv("GIT_SSH_PUSH");
 	if (!prog) prog = "git-ssh-upload";
 
+	setup_ident();
 	setup_git_directory();
 	git_config(git_default_config);
 
@@ -159,7 +161,6 @@
 	}
 	commit_id = argv[arg];
 	url = argv[arg + 1];
-	write_ref_log_details = url;
 
 	if (setup_connection(&fd_in, &fd_out, prog, url, arg, argv + 1))
 		return 1;
@@ -167,7 +168,7 @@
 	if (get_version())
 		return 1;
 
-	if (pull(commit_id))
+	if (pull(1, &commit_id, &write_ref, url))
 		return 1;
 
 	return 0;
diff --git a/t/Makefile b/t/Makefile
index 632c55f..8983509 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -11,6 +11,7 @@
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
 
 T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+TSVN = $(wildcard t91[0-9][0-9]-*.sh)
 
 ifdef NO_PYTHON
 	GIT_TEST_OPTS += --no-python
@@ -24,6 +25,15 @@
 clean:
 	rm -fr trash
 
+# we can test NO_OPTIMIZE_COMMITS independently of LC_ALL
+full-svn-test:
+	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=1 LC_ALL=C
+	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=1 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+							LC_ALL=en_US.UTF-8
+	$(MAKE) $(TSVN) GIT_SVN_NO_LIB=0 GIT_SVN_NO_OPTIMIZE_COMMITS=0 \
+							LC_ALL=en_US.UTF-8
+
 .PHONY: $(T) clean
 .NOTPARALLEL:
 
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index 1148b02..8baf2fe 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -4,7 +4,8 @@
 check_count () {
 	head=
 	case "$1" in -h) head="$2"; shift; shift ;; esac
-	$PROG file $head | perl -e '
+	$PROG file $head >.result || return 1
+	cat .result | perl -e '
 		my %expect = (@ARGV);
 		my %count = ();
 		while (<STDIN>) {
@@ -94,7 +95,7 @@
 test_expect_success \
     'merge-setup part 4' \
     'echo "evil merge." >>file &&
-     EDITOR=: VISUAL=: git commit -a --amend'
+     git commit -a --amend'
 
 test_expect_success \
     'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
new file mode 100644
index 0000000..29a1e72
--- /dev/null
+++ b/t/lib-git-svn.sh
@@ -0,0 +1,50 @@
+. ./test-lib.sh
+
+if test -n "$NO_SVN_TESTS"
+then
+	test_expect_success 'skipping git-svn tests, NO_SVN_TESTS defined' :
+	test_done
+	exit
+fi
+
+GIT_DIR=$PWD/.git
+GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+SVN_TREE=$GIT_SVN_DIR/svn-tree
+
+perl -e 'use SVN::Core' >/dev/null 2>&1
+if test $? -ne 0
+then
+   echo 'Perl SVN libraries not found, tests requiring those will be skipped'
+   GIT_SVN_NO_LIB=1
+fi
+
+svnadmin >/dev/null 2>&1
+if test $? -ne 1
+then
+    test_expect_success 'skipping git-svn tests, svnadmin not found' :
+    test_done
+    exit
+fi
+
+svn >/dev/null 2>&1
+if test $? -ne 1
+then
+    test_expect_success 'skipping git-svn tests, svn not found' :
+    test_done
+    exit
+fi
+
+svnrepo=$PWD/svnrepo
+
+set -e
+
+if svnadmin create --help | grep fs-type >/dev/null
+then
+	svnadmin create --fs-type fsfs "$svnrepo"
+else
+	svnadmin create "$svnrepo"
+fi
+
+svnrepo="file://$svnrepo/test-git-svn"
+
+
diff --git a/t/t1003-read-tree-prefix.sh b/t/t1003-read-tree-prefix.sh
new file mode 100755
index 0000000..48ab117
--- /dev/null
+++ b/t/t1003-read-tree-prefix.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git-read-tree --prefix test.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	echo hello >one &&
+	git-update-index --add one &&
+	tree=`git-write-tree` &&
+	echo tree is $tree
+'
+
+echo 'one
+two/one' >expect
+
+test_expect_success 'read-tree --prefix' '
+	git-read-tree --prefix=two/ $tree &&
+	git-ls-files >actual &&
+	cmp expect actual
+'
+
+test_done
diff --git a/t/t1020-subdirectory.sh b/t/t1020-subdirectory.sh
new file mode 100755
index 0000000..4409b87
--- /dev/null
+++ b/t/t1020-subdirectory.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Try various core-level commands in subdirectory.
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	long="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" &&
+	for c in $long; do echo $c; done >one &&
+	mkdir dir &&
+	for c in x y z $long a b c; do echo $c; done >dir/two &&
+	cp one original.one &&
+	cp dir/two original.two
+'
+HERE=`pwd`
+LF='
+'
+
+test_expect_success 'update-index and ls-files' '
+	cd $HERE &&
+	git-update-index --add one &&
+	case "`git-ls-files`" in
+	one) echo ok one ;;
+	*) echo bad one; exit 1 ;;
+	esac &&
+	cd dir &&
+	git-update-index --add two &&
+	case "`git-ls-files`" in
+	two) echo ok two ;;
+	*) echo bad two; exit 1 ;;
+	esac &&
+	cd .. &&
+	case "`git-ls-files`" in
+	dir/two"$LF"one) echo ok both ;;
+	*) echo bad; exit 1 ;;
+	esac
+'
+
+test_expect_success 'cat-file' '
+	cd $HERE &&
+	two=`git-ls-files -s dir/two` &&
+	two=`expr "$two" : "[0-7]* \\([0-9a-f]*\\)"` &&
+	echo "$two" &&
+	git-cat-file -p "$two" >actual &&
+	cmp dir/two actual &&
+	cd dir &&
+	git-cat-file -p "$two" >actual &&
+	cmp two actual
+'
+rm -f actual dir/actual
+
+test_expect_success 'diff-files' '
+	cd $HERE &&
+	echo a >>one &&
+	echo d >>dir/two &&
+	case "`git-diff-files --name-only`" in
+	dir/two"$LF"one) echo ok top ;;
+	*) echo bad top; exit 1 ;;
+	esac &&
+	# diff should not omit leading paths
+	cd dir &&
+	case "`git-diff-files --name-only`" in
+	dir/two"$LF"one) echo ok subdir ;;
+	*) echo bad subdir; exit 1 ;;
+	esac &&
+	case "`git-diff-files --name-only .`" in
+	dir/two) echo ok subdir limited ;;
+	*) echo bad subdir limited; exit 1 ;;
+	esac
+'
+
+test_expect_success 'write-tree' '
+	cd $HERE &&
+	top=`git-write-tree` &&
+	echo $top &&
+	cd dir &&
+	sub=`git-write-tree` &&
+	echo $sub &&
+	test "z$top" = "z$sub"
+'
+
+test_expect_success 'checkout-index' '
+	cd $HERE &&
+	git-checkout-index -f -u one &&
+	cmp one original.one &&
+	cd dir &&
+	git-checkout-index -f -u two &&
+	cmp two ../original.two
+'
+
+test_expect_success 'read-tree' '
+	cd $HERE &&
+	rm -f one dir/two &&
+	tree=`git-write-tree` &&
+	git-read-tree --reset -u "$tree" &&
+	cmp one original.one &&
+	cmp dir/two original.two &&
+	cd dir &&
+	rm -f two &&
+	git-read-tree --reset -u "$tree" &&
+	cmp two ../original.two &&
+	cmp ../one ../original.one
+'
+
+test_done
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index df3e993..ddc80bb 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -14,6 +14,8 @@
 E=5555555555555555555555555555555555555555
 F=6666666666666666666666666666666666666666
 m=refs/heads/master
+n_dir=refs/heads/gu
+n=$n_dir/fixes
 
 test_expect_success \
 	"create $m" \
@@ -26,6 +28,16 @@
 rm -f .git/$m
 
 test_expect_success \
+	"fail to create $n" \
+	'touch .git/$n_dir
+	 git-update-ref $n $A >out 2>err
+	 test $? = 1 &&
+	 test "" = "$(cat out)" &&
+	 grep "error: unable to resolve reference" err &&
+	 grep $n err'
+rm -f .git/$n_dir out err
+
+test_expect_success \
 	"create $m (by HEAD)" \
 	'git-update-ref HEAD $A &&
 	 test $A = $(cat .git/$m)'
@@ -188,17 +200,29 @@
 	 echo OTHER >F &&
 	 GIT_AUTHOR_DATE="2005-05-26 23:41" \
 	 GIT_COMMITTER_DATE="2005-05-26 23:41" git-commit -F M -a &&
-	 h_OTHER=$(git-rev-parse --verify HEAD)
+	 h_OTHER=$(git-rev-parse --verify HEAD) &&
+	 echo FIXED >F &&
+	 GIT_AUTHOR_DATE="2005-05-26 23:44" \
+	 GIT_COMMITTER_DATE="2005-05-26 23:44" git-commit --amend &&
+	 h_FIXED=$(git-rev-parse --verify HEAD) &&
+	 echo TEST+FIXED >F &&
+	 echo Merged initial commit and a later commit. >M &&
+	 echo $h_TEST >.git/MERGE_HEAD &&
+	 GIT_AUTHOR_DATE="2005-05-26 23:45" \
+	 GIT_COMMITTER_DATE="2005-05-26 23:45" git-commit -F M &&
+	 h_MERGED=$(git-rev-parse --verify HEAD)
 	 rm -f M'
 
 cat >expect <<EOF
-$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	commit: add
+$Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000	commit (initial): add
 $h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000	commit: The other day this did not work.
+$h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000	commit (amend): The other day this did not work.
+$h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000	commit (merge): Merged initial commit and a later commit.
 EOF
 test_expect_success \
 	'git-commit logged updates' \
 	'diff expect .git/logs/$m'
-unset h_TEST h_OTHER
+unset h_TEST h_OTHER h_FIXED h_MERGED
 
 test_expect_success \
 	'git-cat-file blob master:F (expect OTHER)' \
diff --git a/t/t3800-mktag.sh b/t/t3800-mktag.sh
new file mode 100755
index 0000000..5b23b77
--- /dev/null
+++ b/t/t3800-mktag.sh
@@ -0,0 +1,227 @@
+#!/bin/sh
+#
+#
+
+test_description='git-mktag: tag object verify test'
+
+. ./test-lib.sh
+
+###########################################################
+# check the tag.sig file, expecting verify_tag() to fail,
+# and checking that the error message matches the pattern
+# given in the expect.pat file.
+
+check_verify_failure () {
+    test_expect_success \
+        "$1" \
+        'git-mktag <tag.sig 2>message ||
+         egrep -q -f expect.pat message'
+}
+
+###########################################################
+# first create a commit, so we have a valid object/type
+# for the tag.
+echo Hello >A
+git-update-index --add A
+git-commit -m "Initial commit"
+head=$(git-rev-parse --verify HEAD)
+
+############################################################
+#  1. length check
+
+cat >tag.sig <<EOF
+too short for a tag
+EOF
+
+cat >expect.pat <<EOF
+^error: .*size wrong.*$
+EOF
+
+check_verify_failure 'Tag object length check'
+
+############################################################
+#  2. object line label check
+
+cat >tag.sig <<EOF
+xxxxxx 139e9b33986b1c2670fff52c5067603117b3e895
+type tag
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char0: .*"object "$
+EOF
+
+check_verify_failure '"object" line label check'
+
+############################################################
+#  3. object line SHA1 check
+
+cat >tag.sig <<EOF
+object zz9e9b33986b1c2670fff52c5067603117b3e895
+type tag
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char7: .*SHA1 hash$
+EOF
+
+check_verify_failure '"object" line SHA1 check'
+
+############################################################
+#  4. type line label check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+xxxx tag
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char47: .*"[\]ntype "$
+EOF
+
+check_verify_failure '"type" line label check'
+
+############################################################
+#  5. type line eol check
+
+echo "object 779e9b33986b1c2670fff52c5067603117b3e895" >tag.sig
+echo -n "type tagsssssssssssssssssssssssssssssss" >>tag.sig
+
+cat >expect.pat <<EOF
+^error: char48: .*"[\]n"$
+EOF
+
+check_verify_failure '"type" line eol check'
+
+############################################################
+#  6. tag line label check #1
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type tag
+xxx mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char57: no "tag " found$
+EOF
+
+check_verify_failure '"tag" line label check #1'
+
+############################################################
+#  7. tag line label check #2
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type taggggggggggggggggggggggggggggggg
+tag
+EOF
+
+cat >expect.pat <<EOF
+^error: char87: no "tag " found$
+EOF
+
+check_verify_failure '"tag" line label check #2'
+
+############################################################
+#  8. type line type-name length check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type taggggggggggggggggggggggggggggggg
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char53: type too long$
+EOF
+
+check_verify_failure '"type" line type-name length check'
+
+############################################################
+#  9. verify object (SHA1/type) check
+
+cat >tag.sig <<EOF
+object 779e9b33986b1c2670fff52c5067603117b3e895
+type tagggg
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char7: could not verify object.*$
+EOF
+
+check_verify_failure 'verify object (SHA1/type) check'
+
+############################################################
+# 10. verify tag-name check
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag my	tag
+EOF
+
+cat >expect.pat <<EOF
+^error: char67: could not verify tag name$
+EOF
+
+check_verify_failure 'verify tag-name check'
+
+############################################################
+# 11. tagger line lable check #1
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+EOF
+
+cat >expect.pat <<EOF
+^error: char70: could not find "tagger"$
+EOF
+
+check_verify_failure '"tagger" line label check #1'
+
+############################################################
+# 12. tagger line lable check #2
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger
+EOF
+
+cat >expect.pat <<EOF
+^error: char70: could not find "tagger"$
+EOF
+
+check_verify_failure '"tagger" line label check #2'
+
+############################################################
+# 13. create valid tag
+
+cat >tag.sig <<EOF
+object $head
+type commit
+tag mytag
+tagger another@example.com
+EOF
+
+test_expect_success \
+    'create valid tag' \
+    'git-mktag <tag.sig >.git/refs/tags/mytag 2>message'
+
+############################################################
+# 14. check mytag
+
+test_expect_success \
+    'check mytag' \
+    'git-tag -l | grep mytag'
+
+
+test_done
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
new file mode 100755
index 0000000..71c4543
--- /dev/null
+++ b/t/t4013-diff-various.sh
@@ -0,0 +1,252 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='Various diff formatting options'
+
+. ./test-lib.sh
+
+LF='
+'
+
+test_expect_success setup '
+
+	GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:00:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	mkdir dir &&
+	for i in 1 2 3; do echo $i; done >file0 &&
+	for i in A B; do echo $i; done >dir/sub &&
+	cat file0 >file2 &&
+	git add file0 file2 dir/sub &&
+	git commit -m Initial &&
+
+	git branch initial &&
+	git branch side &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:01:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:01:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	for i in 4 5 6; do echo $i; done >>file0 &&
+	for i in C D; do echo $i; done >>dir/sub &&
+	rm -f file2 &&
+	git update-index --remove file0 file2 dir/sub &&
+	git commit -m "Second${LF}${LF}This is the second commit." &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:02:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:02:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	for i in A B C; do echo $i; done >file1 &&
+	git add file1 &&
+	for i in E F; do echo $i; done >>dir/sub &&
+	git update-index dir/sub &&
+	git commit -m Third &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:03:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:03:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	git checkout side &&
+	for i in A B C; do echo $i; done >>file0 &&
+	for i in 1 2; do echo $i; done >>dir/sub &&
+	cat dir/sub >file3 &&
+	git add file3 &&
+	git update-index file0 dir/sub &&
+	git commit -m Side &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:04:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:04:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	git checkout master &&
+	git pull -s ours . side &&
+
+	GIT_AUTHOR_DATE="2006-06-26 00:05:00 +0000" &&
+	GIT_COMMITTER_DATE="2006-06-26 00:05:00 +0000" &&
+	export GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&
+
+	for i in A B C; do echo $i; done >>file0 &&
+	for i in 1 2; do echo $i; done >>dir/sub &&
+	git update-index file0 dir/sub &&
+
+	git commit --amend &&
+	git show-branch
+'
+
+: <<\EOF
+! [initial] Initial
+ * [master] Merge branch 'side'
+  ! [side] Side
+---
+ -  [master] Merge branch 'side'
+ *+ [side] Side
+ *  [master^] Second
++*+ [initial] Initial
+EOF
+
+V=`git version | sed -e 's/^git version //' -e 's/\./\\./g'`
+while read cmd
+do
+	case "$cmd" in
+	'' | '#'*) continue ;;
+	esac
+	test=`echo "$cmd" | sed -e 's|[/ ][/ ]*|_|g'`
+	cnt=`expr $test_count + 1`
+	pfx=`printf "%04d" $cnt`
+	expect="../t4013/diff.$test"
+	actual="$pfx-diff.$test"
+
+	test_expect_success "git $cmd" '
+		{
+			echo "\$ git $cmd"
+			git $cmd |
+			sed -e "s/^\\(-*\\)$V\\(-*\\)\$/\\1g-i-t--v-e-r-s-i-o-n\2/" \
+			    -e "s/^\\( *boundary=\"-*\\)$V\\(-*\\)\"\$/\\1g-i-t--v-e-r-s-i-o-n\2\"/"
+			echo "\$"
+		} >"$actual" &&
+		if test -f "$expect"
+		then
+			diff -u "$expect" "$actual" &&
+			rm -f "$actual"
+		else
+			# this is to help developing new tests.
+			cp "$actual" "$expect"
+			false
+		fi
+	'
+done <<\EOF
+diff-tree initial
+diff-tree -r initial
+diff-tree -r --abbrev initial
+diff-tree -r --abbrev=4 initial
+diff-tree --root initial
+diff-tree --root --abbrev initial
+diff-tree --root -r initial
+diff-tree --root -r --abbrev initial
+diff-tree --root -r --abbrev=4 initial
+diff-tree -p initial
+diff-tree --root -p initial
+diff-tree --patch-with-stat initial
+diff-tree --root --patch-with-stat initial
+diff-tree --patch-with-raw initial
+diff-tree --root --patch-with-raw initial
+
+diff-tree --pretty initial
+diff-tree --pretty --root initial
+diff-tree --pretty -p initial
+diff-tree --pretty --stat initial
+diff-tree --pretty --summary initial
+diff-tree --pretty --stat --summary initial
+diff-tree --pretty --root -p initial
+diff-tree --pretty --root --stat initial
+# improved by Timo's patch
+diff-tree --pretty --root --summary initial
+# improved by Timo's patch
+diff-tree --pretty --root --summary -r initial
+diff-tree --pretty --root --stat --summary initial
+diff-tree --pretty --patch-with-stat initial
+diff-tree --pretty --root --patch-with-stat initial
+diff-tree --pretty --patch-with-raw initial
+diff-tree --pretty --root --patch-with-raw initial
+
+diff-tree --pretty=oneline initial
+diff-tree --pretty=oneline --root initial
+diff-tree --pretty=oneline -p initial
+diff-tree --pretty=oneline --root -p initial
+diff-tree --pretty=oneline --patch-with-stat initial
+# improved by Timo's patch
+diff-tree --pretty=oneline --root --patch-with-stat initial
+diff-tree --pretty=oneline --patch-with-raw initial
+diff-tree --pretty=oneline --root --patch-with-raw initial
+
+diff-tree --pretty side
+diff-tree --pretty -p side
+diff-tree --pretty --patch-with-stat side
+
+diff-tree master
+diff-tree -p master
+diff-tree -p -m master
+diff-tree -c master
+diff-tree -c --abbrev master
+diff-tree --cc master
+# stat only should show the diffstat with the first parent
+diff-tree -c --stat master
+diff-tree --cc --stat master
+diff-tree -c --stat --summary master
+diff-tree --cc --stat --summary master
+# stat summary should show the diffstat and summary with the first parent
+diff-tree -c --stat --summary side
+diff-tree --cc --stat --summary side
+# improved by Timo's patch
+diff-tree --cc --patch-with-stat master
+# improved by Timo's patch
+diff-tree --cc --patch-with-stat --summary master
+# this is correct
+diff-tree --cc --patch-with-stat --summary side
+
+log master
+log -p master
+log --root master
+log --root -p master
+log --patch-with-stat master
+log --root --patch-with-stat master
+log --root --patch-with-stat --summary master
+# improved by Timo's patch
+log --root -c --patch-with-stat --summary master
+# improved by Timo's patch
+log --root --cc --patch-with-stat --summary master
+log -SF master
+log -SF -p master
+
+whatchanged master
+whatchanged -p master
+whatchanged --root master
+whatchanged --root -p master
+whatchanged --patch-with-stat master
+whatchanged --root --patch-with-stat master
+whatchanged --root --patch-with-stat --summary master
+# improved by Timo's patch
+whatchanged --root -c --patch-with-stat --summary master
+# improved by Timo's patch
+whatchanged --root --cc --patch-with-stat --summary master
+whatchanged -SF master
+whatchanged -SF -p master
+
+log --patch-with-stat master -- dir/
+whatchanged --patch-with-stat master -- dir/
+log --patch-with-stat --summary master -- dir/
+whatchanged --patch-with-stat --summary master -- dir/
+
+show initial
+show --root initial
+show side
+show master
+show --stat side
+show --stat --summary side
+show --patch-with-stat side
+show --patch-with-raw side
+show --patch-with-stat --summary side
+
+format-patch --stdout initial..side
+format-patch --stdout initial..master^
+format-patch --stdout initial..master
+format-patch --attach --stdout initial..side
+format-patch --attach --stdout initial..master^
+format-patch --attach --stdout initial..master
+
+diff --abbrev initial..side
+diff -r initial..side
+diff --stat initial..side
+diff -r --stat initial..side
+diff initial..side
+diff --patch-with-stat initial..side
+diff --patch-with-raw initial..side
+diff --patch-with-stat -r initial..side
+diff --patch-with-raw -r initial..side
+EOF
+
+test_done
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..3a9f78a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,34 @@
+$ git diff-tree --cc --patch-with-stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
new file mode 100644
index 0000000..a61ad8c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_--summary_side
@@ -0,0 +1,39 @@
+$ git diff-tree --cc --patch-with-stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
new file mode 100644
index 0000000..49f23b9
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--patch-with-stat_master
@@ -0,0 +1,34 @@
+$ git diff-tree --cc --patch-with-stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_master b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
new file mode 100644
index 0000000..cc6eb3b
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_master
@@ -0,0 +1,6 @@
+$ git diff-tree --cc --stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_--summary_side b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
new file mode 100644
index 0000000..50362be
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_--summary_side
@@ -0,0 +1,8 @@
+$ git diff-tree --cc --stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.diff-tree_--cc_--stat_master b/t/t4013/diff.diff-tree_--cc_--stat_master
new file mode 100644
index 0000000..fae7f33
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_--stat_master
@@ -0,0 +1,6 @@
+$ git diff-tree --cc --stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--cc_master b/t/t4013/diff.diff-tree_--cc_master
new file mode 100644
index 0000000..5ecb4e1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--cc_master
@@ -0,0 +1,30 @@
+$ git diff-tree --cc master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.diff-tree_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--patch-with-raw_initial
new file mode 100644
index 0000000..fc177ab
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--patch-with-stat_initial
new file mode 100644
index 0000000..bd905b1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial
new file mode 100644
index 0000000..7bb8b45
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial
new file mode 100644
index 0000000..cbdde4f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..cd79f1a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-raw_initial
@@ -0,0 +1,33 @@
+$ git diff-tree --pretty=oneline --root --patch-with-raw initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A	dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..d5c333a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_--patch-with-stat_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --pretty=oneline --root --patch-with-stat initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial
new file mode 100644
index 0000000..3c5092c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_-p_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --pretty=oneline --root -p initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial b/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial
new file mode 100644
index 0000000..08920ac
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_--root_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --pretty=oneline --root initial
+444ac553ac7612cc88969031b02b3767fb8a353a Initial
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A	dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial b/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial
new file mode 100644
index 0000000..94b76bf
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline -p initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty=oneline_initial b/t/t4013/diff.diff-tree_--pretty=oneline_initial
new file mode 100644
index 0000000..d50970d
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty=oneline_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty=oneline initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial
new file mode 100644
index 0000000..3a85316
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-raw_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --patch-with-raw initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial
new file mode 100644
index 0000000..2e08239
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --patch-with-stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
new file mode 100644
index 0000000..4d30e7e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--patch-with-stat_side
@@ -0,0 +1,43 @@
+$ git diff-tree --pretty --patch-with-stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..a3203bd
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-raw_initial
@@ -0,0 +1,38 @@
+$ git diff-tree --pretty --root --patch-with-raw initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A	dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..7dfa6af
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--patch-with-stat_initial
@@ -0,0 +1,39 @@
+$ git diff-tree --pretty --root --patch-with-stat initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
new file mode 100644
index 0000000..43bfce2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_--summary_initial
@@ -0,0 +1,15 @@
+$ git diff-tree --pretty --root --stat --summary initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
new file mode 100644
index 0000000..9154aa4
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--stat_initial
@@ -0,0 +1,12 @@
+$ git diff-tree --pretty --root --stat initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial
new file mode 100644
index 0000000..ccdaafb
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_-r_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root --summary -r initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
new file mode 100644
index 0000000..ea48205
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_--summary_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root --summary initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+ create mode 040000 dir
+ create mode 100644 file0
+ create mode 100644 file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_-p_initial b/t/t4013/diff.diff-tree_--pretty_--root_-p_initial
new file mode 100644
index 0000000..d0411f6
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_-p_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --pretty --root -p initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--root_initial b/t/t4013/diff.diff-tree_--pretty_--root_initial
new file mode 100644
index 0000000..94e32ea
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--root_initial
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty --root initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A	dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial
new file mode 100644
index 0000000..c22983a
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--stat_--summary_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --stat --summary initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--stat_initial b/t/t4013/diff.diff-tree_--pretty_--stat_initial
new file mode 100644
index 0000000..8fdcfb4
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--stat_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --stat initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_--summary_initial b/t/t4013/diff.diff-tree_--pretty_--summary_initial
new file mode 100644
index 0000000..9bc2c4f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_--summary_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty --summary initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_-p_initial b/t/t4013/diff.diff-tree_--pretty_-p_initial
new file mode 100644
index 0000000..3c9942f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty -p initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_-p_side b/t/t4013/diff.diff-tree_--pretty_-p_side
new file mode 100644
index 0000000..b993aa7
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_-p_side
@@ -0,0 +1,38 @@
+$ git diff-tree --pretty -p side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_initial b/t/t4013/diff.diff-tree_--pretty_initial
new file mode 100644
index 0000000..14715bf
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_initial
@@ -0,0 +1,2 @@
+$ git diff-tree --pretty initial
+$
diff --git a/t/t4013/diff.diff-tree_--pretty_side b/t/t4013/diff.diff-tree_--pretty_side
new file mode 100644
index 0000000..e9b6e1c
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--pretty_side
@@ -0,0 +1,11 @@
+$ git diff-tree --pretty side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+:040000 040000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 f977ed46ae6873c1c30ab878e15a4accedc3618b M	dir
+:100644 100644 01e79c32a8c99c557f0757da7cb6d65b3414466d f4615da674c09df322d6ba8d6b21ecfb1b1ba510 M	file0
+:000000 100644 0000000000000000000000000000000000000000 7289e35bff32727c08dda207511bec138fdb9ea5 A	file3
+$
diff --git a/t/t4013/diff.diff-tree_--root_--abbrev_initial b/t/t4013/diff.diff-tree_--root_--abbrev_initial
new file mode 100644
index 0000000..5aa84b2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 040000 0000000... da7a33f... A	dir
+:000000 100644 0000000... 01e79c3... A	file0
+:000000 100644 0000000... 01e79c3... A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial b/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial
new file mode 100644
index 0000000..d295e47
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-raw_initial
@@ -0,0 +1,33 @@
+$ git diff-tree --root --patch-with-raw initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A	dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
new file mode 100644
index 0000000..1562b62
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_--patch-with-stat_initial
@@ -0,0 +1,34 @@
+$ git diff-tree --root --patch-with-stat initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-p_initial b/t/t4013/diff.diff-tree_--root_-p_initial
new file mode 100644
index 0000000..3219c72
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-p_initial
@@ -0,0 +1,29 @@
+$ git diff-tree --root -p initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial b/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial
new file mode 100644
index 0000000..0c53616
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_--abbrev=4_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev=4 initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000... 35d2... A	dir/sub
+:000000 100644 0000... 01e7... A	file0
+:000000 100644 0000... 01e7... A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial b/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial
new file mode 100644
index 0000000..c7b460f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_--abbrev_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r --abbrev initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000... 35d242b... A	dir/sub
+:000000 100644 0000000... 01e79c3... A	file0
+:000000 100644 0000000... 01e79c3... A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_-r_initial b/t/t4013/diff.diff-tree_--root_-r_initial
new file mode 100644
index 0000000..eed435e
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_-r_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root -r initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 100644 0000000000000000000000000000000000000000 35d242ba79ae89ac695e26b3d4c27a8e6f028f9e A	dir/sub
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+$
diff --git a/t/t4013/diff.diff-tree_--root_initial b/t/t4013/diff.diff-tree_--root_initial
new file mode 100644
index 0000000..ddf6b06
--- /dev/null
+++ b/t/t4013/diff.diff-tree_--root_initial
@@ -0,0 +1,6 @@
+$ git diff-tree --root initial
+444ac553ac7612cc88969031b02b3767fb8a353a
+:000000 040000 0000000000000000000000000000000000000000 da7a33fa77d8066d6698643940ce5860fe2d7fb3 A	dir
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file0
+:000000 100644 0000000000000000000000000000000000000000 01e79c32a8c99c557f0757da7cb6d65b3414466d A	file2
+$
diff --git a/t/t4013/diff.diff-tree_-c_--abbrev_master b/t/t4013/diff.diff-tree_-c_--abbrev_master
new file mode 100644
index 0000000..b8e4aa2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--abbrev_master
@@ -0,0 +1,5 @@
+$ git diff-tree -c --abbrev master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+::100644 100644 100644 cead32e... 7289e35... 992913c... MM	dir/sub
+::100644 100644 100644 b414108... f4615da... 10a8a9f... MM	file0
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_master b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
new file mode 100644
index 0000000..ac9f641
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_master
@@ -0,0 +1,6 @@
+$ git diff-tree -c --stat --summary master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_--summary_side b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
new file mode 100644
index 0000000..2afcca1
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_--summary_side
@@ -0,0 +1,8 @@
+$ git diff-tree -c --stat --summary side
+c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.diff-tree_-c_--stat_master b/t/t4013/diff.diff-tree_-c_--stat_master
new file mode 100644
index 0000000..c2fe6a9
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_--stat_master
@@ -0,0 +1,6 @@
+$ git diff-tree -c --stat master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff-tree_-c_master b/t/t4013/diff.diff-tree_-c_master
new file mode 100644
index 0000000..e2d2bb2
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-c_master
@@ -0,0 +1,5 @@
+$ git diff-tree -c master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+::100644 100644 100644 cead32e925b1420c84c14cbf7cf755e7e45af8ad 7289e35bff32727c08dda207511bec138fdb9ea5 992913c5aa0a5476d10c49ed0f21fc0c6d1aedf3 MM	dir/sub
+::100644 100644 100644 b414108e81e5091fe0974a1858b4d0d22b107f70 f4615da674c09df322d6ba8d6b21ecfb1b1ba510 10a8a9f3657f91a156b9f0184ed79a20adef9f7f MM	file0
+$
diff --git a/t/t4013/diff.diff-tree_-p_-m_master b/t/t4013/diff.diff-tree_-p_-m_master
new file mode 100644
index 0000000..b60bea0
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_-m_master
@@ -0,0 +1,80 @@
+$ git diff-tree -p -m master
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --git a/dir/sub b/dir/sub
+index cead32e..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -4,3 +4,5 @@ C
+ D
+ E
+ F
++1
++2
+diff --git a/file0 b/file0
+index b414108..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -4,3 +4,6 @@
+ 4
+ 5
+ 6
++A
++B
++C
+59d314ad6f356dd08601a4cd5e530381da3e3c64
+diff --git a/dir/sub b/dir/sub
+index 7289e35..992913c 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,4 +1,8 @@
+ A
+ B
++C
++D
++E
++F
+ 1
+ 2
+diff --git a/file0 b/file0
+index f4615da..10a8a9f 100644
+--- a/file0
++++ b/file0
+@@ -1,6 +1,9 @@
+ 1
+ 2
+ 3
++4
++5
++6
+ A
+ B
+ C
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+diff --git a/file3 b/file3
+deleted file mode 100644
+index 7289e35..0000000
+--- a/file3
++++ /dev/null
+@@ -1,4 +0,0 @@
+-A
+-B
+-1
+-2
+$
diff --git a/t/t4013/diff.diff-tree_-p_initial b/t/t4013/diff.diff-tree_-p_initial
new file mode 100644
index 0000000..e20ce88
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -p initial
+$
diff --git a/t/t4013/diff.diff-tree_-p_master b/t/t4013/diff.diff-tree_-p_master
new file mode 100644
index 0000000..b182875
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-p_master
@@ -0,0 +1,2 @@
+$ git diff-tree -p master
+$
diff --git a/t/t4013/diff.diff-tree_-r_--abbrev=4_initial b/t/t4013/diff.diff-tree_-r_--abbrev=4_initial
new file mode 100644
index 0000000..c5a3aa5
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_--abbrev=4_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r --abbrev=4 initial
+$
diff --git a/t/t4013/diff.diff-tree_-r_--abbrev_initial b/t/t4013/diff.diff-tree_-r_--abbrev_initial
new file mode 100644
index 0000000..0b689b7
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_--abbrev_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r --abbrev initial
+$
diff --git a/t/t4013/diff.diff-tree_-r_initial b/t/t4013/diff.diff-tree_-r_initial
new file mode 100644
index 0000000..1765d83
--- /dev/null
+++ b/t/t4013/diff.diff-tree_-r_initial
@@ -0,0 +1,2 @@
+$ git diff-tree -r initial
+$
diff --git a/t/t4013/diff.diff-tree_initial b/t/t4013/diff.diff-tree_initial
new file mode 100644
index 0000000..b49fc53
--- /dev/null
+++ b/t/t4013/diff.diff-tree_initial
@@ -0,0 +1,2 @@
+$ git diff-tree initial
+$
diff --git a/t/t4013/diff.diff-tree_master b/t/t4013/diff.diff-tree_master
new file mode 100644
index 0000000..fe9226f
--- /dev/null
+++ b/t/t4013/diff.diff-tree_master
@@ -0,0 +1,2 @@
+$ git diff-tree master
+$
diff --git a/t/t4013/diff.diff_--abbrev_initial..side b/t/t4013/diff.diff_--abbrev_initial..side
new file mode 100644
index 0000000..a88e66f
--- /dev/null
+++ b/t/t4013/diff.diff_--abbrev_initial..side
@@ -0,0 +1,32 @@
+$ git diff --abbrev initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-raw_-r_initial..side b/t/t4013/diff.diff_--patch-with-raw_-r_initial..side
new file mode 100644
index 0000000..3590dc7
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-raw_-r_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw -r initial..side
+:100644 100644 35d242b... 7289e35... M	dir/sub
+:100644 100644 01e79c3... f4615da... M	file0
+:000000 100644 0000000... 7289e35... A	file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-raw_initial..side b/t/t4013/diff.diff_--patch-with-raw_initial..side
new file mode 100644
index 0000000..b21d5dc
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-raw_initial..side
@@ -0,0 +1,36 @@
+$ git diff --patch-with-raw initial..side
+:100644 100644 35d242b... 7289e35... M	dir/sub
+:100644 100644 01e79c3... f4615da... M	file0
+:000000 100644 0000000... 7289e35... A	file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_-r_initial..side b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
new file mode 100644
index 0000000..9ed317a
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-stat_-r_initial..side
@@ -0,0 +1,37 @@
+$ git diff --patch-with-stat -r initial..side
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--patch-with-stat_initial..side b/t/t4013/diff.diff_--patch-with-stat_initial..side
new file mode 100644
index 0000000..8b50629
--- /dev/null
+++ b/t/t4013/diff.diff_--patch-with-stat_initial..side
@@ -0,0 +1,37 @@
+$ git diff --patch-with-stat initial..side
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_--stat_initial..side b/t/t4013/diff.diff_--stat_initial..side
new file mode 100644
index 0000000..0517b5d
--- /dev/null
+++ b/t/t4013/diff.diff_--stat_initial..side
@@ -0,0 +1,6 @@
+$ git diff --stat initial..side
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff_-r_--stat_initial..side b/t/t4013/diff.diff_-r_--stat_initial..side
new file mode 100644
index 0000000..245220d
--- /dev/null
+++ b/t/t4013/diff.diff_-r_--stat_initial..side
@@ -0,0 +1,6 @@
+$ git diff -r --stat initial..side
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.diff_-r_initial..side b/t/t4013/diff.diff_-r_initial..side
new file mode 100644
index 0000000..5bb2fe2
--- /dev/null
+++ b/t/t4013/diff.diff_-r_initial..side
@@ -0,0 +1,32 @@
+$ git diff -r initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.diff_initial..side b/t/t4013/diff.diff_initial..side
new file mode 100644
index 0000000..c8adaf5
--- /dev/null
+++ b/t/t4013/diff.diff_initial..side
@@ -0,0 +1,32 @@
+$ git diff initial..side
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
new file mode 100644
index 0000000..b4745e1
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master
@@ -0,0 +1,170 @@
+$ git format-patch --attach --stdout initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..master^ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
new file mode 100644
index 0000000..a9d1cd3
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..master^
@@ -0,0 +1,110 @@
+$ git format-patch --attach --stdout initial..master^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--attach_--stdout_initial..side b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
new file mode 100644
index 0000000..57b9d0b
--- /dev/null
+++ b/t/t4013/diff.format-patch_--attach_--stdout_initial..side
@@ -0,0 +1,61 @@
+$ git format-patch --attach --stdout initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="------------g-i-t--v-e-r-s-i-o-n"
+
+This is a multi-part message in MIME format.
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/plain; charset=UTF-8; format=fixed
+Content-Transfer-Encoding: 8bit
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+--------------g-i-t--v-e-r-s-i-o-n
+Content-Type: text/x-patch;
+ name="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+Content-Transfer-Encoding: 8bit
+Content-Disposition: inline;
+ filename="c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a.diff"
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+--------------g-i-t--v-e-r-s-i-o-n--
+
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master b/t/t4013/diff.format-patch_--stdout_initial..master
new file mode 100644
index 0000000..c33302e
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..master
@@ -0,0 +1,124 @@
+$ git format-patch --stdout initial..master
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..master^ b/t/t4013/diff.format-patch_--stdout_initial..master^
new file mode 100644
index 0000000..03d0f96
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..master^
@@ -0,0 +1,79 @@
+$ git format-patch --stdout initial..master^
+From 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:01:00 +0000
+Subject: [PATCH] Second
+
+This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+
+From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:02:00 +0000
+Subject: [PATCH] Third
+
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.format-patch_--stdout_initial..side b/t/t4013/diff.format-patch_--stdout_initial..side
new file mode 100644
index 0000000..d10a465
--- /dev/null
+++ b/t/t4013/diff.format-patch_--stdout_initial..side
@@ -0,0 +1,46 @@
+$ git format-patch --stdout initial..side
+From c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a Mon Sep 17 00:00:00 2001
+From: A U Thor <author@example.com>
+Date: Mon, 26 Jun 2006 00:03:00 +0000
+Subject: [PATCH] Side
+
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+-- 
+g-i-t--v-e-r-s-i-o-n
+
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
new file mode 100644
index 0000000..3ceb8e7
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_--summary_master_--_dir_
@@ -0,0 +1,74 @@
+$ git log --patch-with-stat --summary master -- dir/
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_master b/t/t4013/diff.log_--patch-with-stat_master
new file mode 100644
index 0000000..43d7776
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_master
@@ -0,0 +1,129 @@
+$ git log --patch-with-stat master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--patch-with-stat_master_--_dir_ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
new file mode 100644
index 0000000..5187a26
--- /dev/null
+++ b/t/t4013/diff.log_--patch-with-stat_master_--_dir_
@@ -0,0 +1,74 @@
+$ git log --patch-with-stat master -- dir/
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..c964097
--- /dev/null
+++ b/t/t4013/diff.log_--root_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git log --root --cc --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..ad050af
--- /dev/null
+++ b/t/t4013/diff.log_--root_--patch-with-stat_--summary_master
@@ -0,0 +1,167 @@
+$ git log --root --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_--patch-with-stat_master b/t/t4013/diff.log_--root_--patch-with-stat_master
new file mode 100644
index 0000000..628c6c0
--- /dev/null
+++ b/t/t4013/diff.log_--root_--patch-with-stat_master
@@ -0,0 +1,161 @@
+$ git log --root --patch-with-stat master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..5d4e0f1
--- /dev/null
+++ b/t/t4013/diff.log_--root_-c_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git log --root -c --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_-p_master b/t/t4013/diff.log_--root_-p_master
new file mode 100644
index 0000000..217a2eb
--- /dev/null
+++ b/t/t4013/diff.log_--root_-p_master
@@ -0,0 +1,142 @@
+$ git log --root -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.log_--root_master b/t/t4013/diff.log_--root_master
new file mode 100644
index 0000000..e17ccfc
--- /dev/null
+++ b/t/t4013/diff.log_--root_master
@@ -0,0 +1,34 @@
+$ git log --root master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_-SF_-p_master b/t/t4013/diff.log_-SF_-p_master
new file mode 100644
index 0000000..5e32438
--- /dev/null
+++ b/t/t4013/diff.log_-SF_-p_master
@@ -0,0 +1,18 @@
+$ git log -SF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.log_-SF_master b/t/t4013/diff.log_-SF_master
new file mode 100644
index 0000000..6162ed2
--- /dev/null
+++ b/t/t4013/diff.log_-SF_master
@@ -0,0 +1,8 @@
+$ git log -SF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+$
diff --git a/t/t4013/diff.log_-p_master b/t/t4013/diff.log_-p_master
new file mode 100644
index 0000000..f8fefef
--- /dev/null
+++ b/t/t4013/diff.log_-p_master
@@ -0,0 +1,115 @@
+$ git log -p master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.log_master b/t/t4013/diff.log_master
new file mode 100644
index 0000000..e9d9e7b
--- /dev/null
+++ b/t/t4013/diff.log_master
@@ -0,0 +1,34 @@
+$ git log master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.show_--patch-with-raw_side b/t/t4013/diff.show_--patch-with-raw_side
new file mode 100644
index 0000000..221b46a
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-raw_side
@@ -0,0 +1,42 @@
+$ git show --patch-with-raw side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+:100644 100644 35d242b... 7289e35... M	dir/sub
+:100644 100644 01e79c3... f4615da... M	file0
+:000000 100644 0000000... 7289e35... A	file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--patch-with-stat_--summary_side b/t/t4013/diff.show_--patch-with-stat_--summary_side
new file mode 100644
index 0000000..377f2b7
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-stat_--summary_side
@@ -0,0 +1,44 @@
+$ git show --patch-with-stat --summary side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--patch-with-stat_side b/t/t4013/diff.show_--patch-with-stat_side
new file mode 100644
index 0000000..fb14c53
--- /dev/null
+++ b/t/t4013/diff.show_--patch-with-stat_side
@@ -0,0 +1,43 @@
+$ git show --patch-with-stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.show_--root_initial b/t/t4013/diff.show_--root_initial
new file mode 100644
index 0000000..8c89136
--- /dev/null
+++ b/t/t4013/diff.show_--root_initial
@@ -0,0 +1,34 @@
+$ git show --root initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.show_--stat_--summary_side b/t/t4013/diff.show_--stat_--summary_side
new file mode 100644
index 0000000..5bd5977
--- /dev/null
+++ b/t/t4013/diff.show_--stat_--summary_side
@@ -0,0 +1,13 @@
+$ git show --stat --summary side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+$
diff --git a/t/t4013/diff.show_--stat_side b/t/t4013/diff.show_--stat_side
new file mode 100644
index 0000000..3b22327
--- /dev/null
+++ b/t/t4013/diff.show_--stat_side
@@ -0,0 +1,12 @@
+$ git show --stat side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+$
diff --git a/t/t4013/diff.show_initial b/t/t4013/diff.show_initial
new file mode 100644
index 0000000..4c4066a
--- /dev/null
+++ b/t/t4013/diff.show_initial
@@ -0,0 +1,7 @@
+$ git show initial
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+$
diff --git a/t/t4013/diff.show_master b/t/t4013/diff.show_master
new file mode 100644
index 0000000..9e6e1f2
--- /dev/null
+++ b/t/t4013/diff.show_master
@@ -0,0 +1,36 @@
+$ git show master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+$
diff --git a/t/t4013/diff.show_side b/t/t4013/diff.show_side
new file mode 100644
index 0000000..530a073
--- /dev/null
+++ b/t/t4013/diff.show_side
@@ -0,0 +1,38 @@
+$ git show side
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
new file mode 100644
index 0000000..6a467cc
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_--summary_master_--_dir_
@@ -0,0 +1,61 @@
+$ git whatchanged --patch-with-stat --summary master -- dir/
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master b/t/t4013/diff.whatchanged_--patch-with-stat_master
new file mode 100644
index 0000000..1e1bbe1
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master
@@ -0,0 +1,116 @@
+$ git whatchanged --patch-with-stat master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+$
diff --git a/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
new file mode 100644
index 0000000..13789f1
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--patch-with-stat_master_--_dir_
@@ -0,0 +1,61 @@
+$ git whatchanged --patch-with-stat master -- dir/
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+$
diff --git a/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..5facf25
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--cc_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git whatchanged --root --cc --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --cc dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --cc file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..0291153
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_--summary_master
@@ -0,0 +1,160 @@
+$ git whatchanged --root --patch-with-stat --summary master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_--patch-with-stat_master b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
new file mode 100644
index 0000000..9b0349c
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_--patch-with-stat_master
@@ -0,0 +1,154 @@
+$ git whatchanged --root --patch-with-stat master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
new file mode 100644
index 0000000..10f6767
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_-c_--patch-with-stat_--summary_master
@@ -0,0 +1,199 @@
+$ git whatchanged --root -c --patch-with-stat --summary master
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64
+Merge: 9a6d494... c7a2ab9...
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:04:00 2006 +0000
+
+    Merge branch 'side'
+
+ dir/sub |    2 ++
+ file0   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --combined dir/sub
+index cead32e,7289e35..992913c
+--- a/dir/sub
++++ b/dir/sub
+@@@ -1,6 -1,4 +1,8 @@@
+  A
+  B
+ +C
+ +D
+ +E
+ +F
++ 1
++ 2
+diff --combined file0
+index b414108,f4615da..10a8a9f
+--- a/file0
++++ b/file0
+@@@ -1,6 -1,6 +1,9 @@@
+  1
+  2
+  3
+ +4
+ +5
+ +6
++ A
++ B
++ C
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file3   |    4 ++++
+ 3 files changed, 9 insertions(+), 0 deletions(-)
+ create mode 100644 file3
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+---
+ dir/sub |    2 ++
+ file1   |    3 +++
+ 2 files changed, 5 insertions(+), 0 deletions(-)
+ create mode 100644 file1
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 ---
+ 3 files changed, 5 insertions(+), 3 deletions(-)
+ delete mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+---
+ dir/sub |    2 ++
+ file0   |    3 +++
+ file2   |    3 +++
+ 3 files changed, 8 insertions(+), 0 deletions(-)
+ create mode 100644 dir/sub
+ create mode 100644 file0
+ create mode 100644 file2
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_-p_master b/t/t4013/diff.whatchanged_--root_-p_master
new file mode 100644
index 0000000..ebf1f06
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_-p_master
@@ -0,0 +1,135 @@
+$ git whatchanged --root -p master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+diff --git a/dir/sub b/dir/sub
+new file mode 100644
+index 0000000..35d242b
+--- /dev/null
++++ b/dir/sub
+@@ -0,0 +1,2 @@
++A
++B
+diff --git a/file0 b/file0
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file0
+@@ -0,0 +1,3 @@
++1
++2
++3
+diff --git a/file2 b/file2
+new file mode 100644
+index 0000000..01e79c3
+--- /dev/null
++++ b/file2
+@@ -0,0 +1,3 @@
++1
++2
++3
+$
diff --git a/t/t4013/diff.whatchanged_--root_master b/t/t4013/diff.whatchanged_--root_master
new file mode 100644
index 0000000..a405cb6
--- /dev/null
+++ b/t/t4013/diff.whatchanged_--root_master
@@ -0,0 +1,42 @@
+$ git whatchanged --root master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+:100644 100644 35d242b... 7289e35... M	dir/sub
+:100644 100644 01e79c3... f4615da... M	file0
+:000000 100644 0000000... 7289e35... A	file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+:100644 100644 8422d40... cead32e... M	dir/sub
+:000000 100644 0000000... b1e6722... A	file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+:100644 100644 35d242b... 8422d40... M	dir/sub
+:100644 100644 01e79c3... b414108... M	file0
+:100644 000000 01e79c3... 0000000... D	file2
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:00:00 2006 +0000
+
+    Initial
+
+:000000 100644 0000000... 35d242b... A	dir/sub
+:000000 100644 0000000... 01e79c3... A	file0
+:000000 100644 0000000... 01e79c3... A	file2
+$
diff --git a/t/t4013/diff.whatchanged_-SF_-p_master b/t/t4013/diff.whatchanged_-SF_-p_master
new file mode 100644
index 0000000..f39da84
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-SF_-p_master
@@ -0,0 +1,18 @@
+$ git whatchanged -SF -p master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+$
diff --git a/t/t4013/diff.whatchanged_-SF_master b/t/t4013/diff.whatchanged_-SF_master
new file mode 100644
index 0000000..0499321
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-SF_master
@@ -0,0 +1,9 @@
+$ git whatchanged -SF master
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+:100644 100644 8422d40... cead32e... M	dir/sub
+$
diff --git a/t/t4013/diff.whatchanged_-p_master b/t/t4013/diff.whatchanged_-p_master
new file mode 100644
index 0000000..f18d432
--- /dev/null
+++ b/t/t4013/diff.whatchanged_-p_master
@@ -0,0 +1,102 @@
+$ git whatchanged -p master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..7289e35 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++1
++2
+diff --git a/file0 b/file0
+index 01e79c3..f4615da 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++A
++B
++C
+diff --git a/file3 b/file3
+new file mode 100644
+index 0000000..7289e35
+--- /dev/null
++++ b/file3
+@@ -0,0 +1,4 @@
++A
++B
++1
++2
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+diff --git a/dir/sub b/dir/sub
+index 8422d40..cead32e 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -2,3 +2,5 @@ A
+ B
+ C
+ D
++E
++F
+diff --git a/file1 b/file1
+new file mode 100644
+index 0000000..b1e6722
+--- /dev/null
++++ b/file1
+@@ -0,0 +1,3 @@
++A
++B
++C
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+diff --git a/dir/sub b/dir/sub
+index 35d242b..8422d40 100644
+--- a/dir/sub
++++ b/dir/sub
+@@ -1,2 +1,4 @@
+ A
+ B
++C
++D
+diff --git a/file0 b/file0
+index 01e79c3..b414108 100644
+--- a/file0
++++ b/file0
+@@ -1,3 +1,6 @@
+ 1
+ 2
+ 3
++4
++5
++6
+diff --git a/file2 b/file2
+deleted file mode 100644
+index 01e79c3..0000000
+--- a/file2
++++ /dev/null
+@@ -1,3 +0,0 @@
+-1
+-2
+-3
+$
diff --git a/t/t4013/diff.whatchanged_master b/t/t4013/diff.whatchanged_master
new file mode 100644
index 0000000..cd3bcc2
--- /dev/null
+++ b/t/t4013/diff.whatchanged_master
@@ -0,0 +1,32 @@
+$ git whatchanged master
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:03:00 2006 +0000
+
+    Side
+
+:100644 100644 35d242b... 7289e35... M	dir/sub
+:100644 100644 01e79c3... f4615da... M	file0
+:000000 100644 0000000... 7289e35... A	file3
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:02:00 2006 +0000
+
+    Third
+
+:100644 100644 8422d40... cead32e... M	dir/sub
+:000000 100644 0000000... b1e6722... A	file1
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date:   Mon Jun 26 00:01:00 2006 +0000
+
+    Second
+    
+    This is the second commit.
+
+:100644 100644 35d242b... 8422d40... M	dir/sub
+:100644 100644 01e79c3... b414108... M	file0
+:100644 000000 01e79c3... 0000000... D	file2
+$
diff --git a/t/t4102-apply-rename.sh b/t/t4102-apply-rename.sh
index fbb508d..22da6a0 100755
--- a/t/t4102-apply-rename.sh
+++ b/t/t4102-apply-rename.sh
@@ -13,8 +13,8 @@
 cat >test-patch <<\EOF
 diff --git a/foo b/bar
 similarity index 47%
-copy from foo
-copy to bar
+rename from foo
+rename to bar
 --- a/foo
 +++ b/bar
 @@ -1 +1 @@
@@ -39,4 +39,24 @@
 	    'test -f bar && ls -l bar | grep "^-..x......"'
 fi
 
+test_expect_success 'apply reverse' \
+    'git-apply -R --index --stat --summary --apply test-patch &&
+     test "$(cat foo)" = "This is foo"'
+
+cat >test-patch <<\EOF
+diff --git a/foo b/bar
+similarity index 47%
+copy from foo
+copy to bar
+--- a/foo
++++ b/bar
+@@ -1 +1 @@
+-This is foo
++This is bar
+EOF
+
+test_expect_success 'apply copy' \
+    'git-apply --index --stat --summary --apply test-patch &&
+     test "$(cat bar)" = "This is bar" -a "$(cat foo)" = "This is foo"'
+
 test_done
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 00bd8b1..ff05269 100755
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -35,8 +35,8 @@
 git-diff-tree -p master binary >B.diff
 git-diff-tree -p -C master binary >C.diff
 
-git-diff-tree -p --full-index master binary >BF.diff
-git-diff-tree -p --full-index -C master binary >CF.diff
+git-diff-tree -p --binary master binary >BF.diff
+git-diff-tree -p --binary -C master binary >CF.diff
 
 test_expect_success 'stat binary diff -- should not fail.' \
 	'git-checkout master
diff --git a/t/t4112-apply-renames.sh b/t/t4112-apply-renames.sh
index a06f695..69e9603 100755
--- a/t/t4112-apply-renames.sh
+++ b/t/t4112-apply-renames.sh
@@ -11,31 +11,7 @@
 
 # setup
 
-mkdir -p include/arch/x86_64/klibc klibc/arch/x86_64/include/klibc
-
-cat >include/arch/x86_64/klibc/archsetjmp.h <<\EOF
-/*
- * arch/x86_64/include/klibc/archsetjmp.h
- */
-
-#ifndef _KLIBC_ARCHSETJMP_H
-#define _KLIBC_ARCHSETJMP_H
-
-struct __jmp_buf {
-  unsigned long __rbx;
-  unsigned long __rsp;
-  unsigned long __rbp;
-  unsigned long __r12;
-  unsigned long __r13;
-  unsigned long __r14;
-  unsigned long __r15;
-  unsigned long __rip;
-};
-
-typedef struct __jmp_buf jmp_buf[1];
-
-#endif /* _SETJMP_H */
-EOF
+mkdir -p klibc/arch/x86_64/include/klibc
 
 cat >klibc/arch/x86_64/include/klibc/archsetjmp.h <<\EOF
 /*
@@ -139,7 +115,7 @@
 +#endif /* _KLIBC_ARCHSETJMP_H */
 EOF
 
-find include klibc -type f -print | xargs git-update-index --add --
+find klibc -type f -print | xargs git-update-index --add --
 
 test_expect_success 'check rename/copy patch' 'git-apply --check patch'
 
diff --git a/t/t4114-apply-typechange.sh b/t/t4114-apply-typechange.sh
new file mode 100755
index 0000000..ca81d721
--- /dev/null
+++ b/t/t4114-apply-typechange.sh
@@ -0,0 +1,105 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Eric Wong
+#
+
+test_description='git-apply should not get confused with type changes.
+
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup repository and commits' '
+	echo "hello world" > foo &&
+	echo "hi planet" > bar &&
+	git update-index --add foo bar &&
+	git commit -m initial &&
+	git branch initial &&
+	rm -f foo &&
+	ln -s bar foo &&
+	git update-index foo &&
+	git commit -m "foo symlinked to bar" &&
+	git branch foo-symlinked-to-bar &&
+	rm -f foo &&
+	echo "how far is the sun?" > foo &&
+	git update-index foo &&
+	git commit -m "foo back to file" &&
+	git branch foo-back-to-file &&
+	rm -f foo &&
+	git update-index --remove foo &&
+	mkdir foo &&
+	echo "if only I knew" > foo/baz &&
+	git update-index --add foo/baz &&
+	git commit -m "foo becomes a directory" &&
+	git branch "foo-becomes-a-directory" &&
+	echo "hello world" > foo/baz &&
+	git update-index foo/baz &&
+	git commit -m "foo/baz is the original foo" &&
+	git branch foo-baz-renamed-from-foo
+	'
+
+test_expect_success 'file renamed from foo to foo/baz' '
+	git checkout -f initial &&
+	git diff-tree -M -p HEAD foo-baz-renamed-from-foo > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'file renamed from foo/baz to foo' '
+	git checkout -f foo-baz-renamed-from-foo &&
+	git diff-tree -M -p HEAD initial > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes file' '
+	git checkout -f foo-becomes-a-directory &&
+	git diff-tree -p HEAD initial > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes directory' '
+	git checkout -f initial &&
+	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'file becomes symlink' '
+	git checkout -f initial &&
+	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes file' '
+	git checkout -f foo-symlinked-to-bar &&
+	git diff-tree -p HEAD foo-back-to-file > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'symlink becomes directory' '
+	git checkout -f foo-symlinked-to-bar &&
+	git diff-tree -p HEAD foo-becomes-a-directory > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_expect_success 'directory becomes symlink' '
+	git checkout -f foo-becomes-a-directory &&
+	git diff-tree -p HEAD foo-symlinked-to-bar > patch &&
+	git apply --index < patch
+	'
+test_debug 'cat patch'
+
+
+test_done
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
new file mode 100755
index 0000000..d5f2cfb
--- /dev/null
+++ b/t/t4115-apply-symlink.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='git-apply symlinks and partial files
+
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	ln -s path1/path2/path3/path4/path5 link1 &&
+	git add link? &&
+	git commit -m initial &&
+
+	git branch side &&
+
+	rm -f link? &&
+
+	ln -s htap6 link1 &&
+	git update-index link? &&
+	git commit -m second &&
+
+	git diff-tree -p HEAD^ HEAD >patch  &&
+	git apply --stat --summary patch
+
+'
+
+test_expect_success 'apply symlink patch' '
+
+	git checkout side &&
+	git apply patch &&
+	git diff-files -p >patched &&
+	diff -u patch patched
+
+'
+
+test_expect_success 'apply --index symlink patch' '
+
+	git checkout -f side &&
+	git apply --index patch &&
+	git diff-index --cached -p HEAD >patched &&
+	diff -u patch patched
+
+'
+
+test_done
diff --git a/t/t5700-clone-reference.sh b/t/t5700-clone-reference.sh
index 916ee15..dd9caad 100755
--- a/t/t5700-clone-reference.sh
+++ b/t/t5700-clone-reference.sh
@@ -31,7 +31,7 @@
 
 cd "$base_dir"
 
-test_expect_success 'existance of info/alternates' \
+test_expect_success 'existence of info/alternates' \
 'test `wc -l <C/.git/objects/info/alternates` = 2'
 
 cd "$base_dir"
diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh
index 097d037..2e1b48a 100755
--- a/t/t5710-info-alternate.sh
+++ b/t/t5710-info-alternate.sh
@@ -81,7 +81,7 @@
 
 cd "$base_dir"
 
-test_expect_failure 'that info/alternates is neccessary' \
+test_expect_failure 'that info/alternates is necessary' \
 'cd C &&
 rm .git/objects/info/alternates &&
 test_valid_repo'
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index 693de9b..7831e34 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -154,7 +154,7 @@
 	test_bisection_diff 0 $_bisect_option u5 ^U
 	
 #
-# the following illustrate's Linus' binary bug blatt idea. 
+# the following illustrates Linus' binary bug blatt idea.
 #
 # assume the bug is actually at l3, but you don't know that - all you know is that l3 is broken
 # and it wasn't broken before
diff --git a/t/t6004-rev-list-path-optim.sh b/t/t6004-rev-list-path-optim.sh
new file mode 100755
index 0000000..5182dbb
--- /dev/null
+++ b/t/t6004-rev-list-path-optim.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='git-rev-list trivial path optimization test'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+echo Hello > a &&
+git add a &&
+git commit -m "Initial commit" a
+'
+
+test_expect_success path-optimization '
+    commit=$(echo "Unchanged tree" | git-commit-tree "HEAD^{tree}" -p HEAD) &&
+    test $(git-rev-list $commit | wc -l) = 2 &&
+    test $(git-rev-list $commit -- . | wc -l) = 1
+'
+
+test_done
diff --git a/t/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 1dce123..b15920b 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -44,6 +44,43 @@
 G=$(doit 7 G $B $E)
 H=$(doit 8 H $A $F)
 
+# Setup for second test to demonstrate that relying on timestamps in a
+# distributed SCM to provide a _consistent_ partial ordering of commits
+# leads to insanity.
+#
+#               Relative
+# Structure     timestamps
+#
+#   PL  PR        +4  +4
+#  /  \/  \      /  \/  \
+# L2  C2  R2    +3  -1  +3
+# |   |   |     |   |   |
+# L1  C1  R1    +2  -2  +2
+# |   |   |     |   |   |
+# L0  C0  R0    +1  -3  +1
+#   \ |  /        \ |  /
+#     S             0
+#
+# The left and right chains of commits can be of any length and complexity as
+# long as all of the timestamps are greater than that of S.
+
+S=$(doit  0 S)
+
+C0=$(doit -3 C0 $S)
+C1=$(doit -2 C1 $C0)
+C2=$(doit -1 C2 $C1)
+
+L0=$(doit  1 L0 $S)
+L1=$(doit  2 L1 $L0)
+L2=$(doit  3 L2 $L1)
+
+R0=$(doit  1 R0 $S)
+R1=$(doit  2 R1 $R0)
+R2=$(doit  3 R2 $R1)
+
+PL=$(doit  4 PL $L2 $C2)
+PR=$(doit  4 PR $C2 $R2)
+
 test_expect_success 'compute merge-base (single)' \
     'MB=$(git-merge-base G H) &&
      expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
@@ -56,4 +93,12 @@
     'MB=$(git-show-branch --merge-base G H) &&
      expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/B"'
 
+test_expect_success 'compute merge-base (single)' \
+    'MB=$(git-merge-base PL PR) &&
+     expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
+test_expect_success 'compute merge-base (all)' \
+    'MB=$(git-merge-base --all PL PR) &&
+     expr "$(git-name-rev "$MB")" : "[0-9a-f]* tags/C2"'
+
 test_done
diff --git a/t/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 2623813..8f7366d 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -4,7 +4,7 @@
 #
 
 # See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
-# nice decription of what this is about.
+# nice description of what this is about.
 
 
 test_description='Test criss-cross merge'
diff --git a/t/t6200-fmt-merge-msg.sh b/t/t6200-fmt-merge-msg.sh
new file mode 100755
index 0000000..63e49f3
--- /dev/null
+++ b/t/t6200-fmt-merge-msg.sh
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, Junio C Hamano
+#
+
+test_description='fmt-merge-msg test'
+
+. ./test-lib.sh
+
+datestamp=1151939923
+setdate () {
+	GIT_COMMITTER_DATE="$datestamp +0200"
+	GIT_AUTHOR_DATE="$datestamp +0200"
+	datestamp=`expr "$datestamp" + 1`
+	export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_expect_success setup '
+	echo one >one &&
+	git add one &&
+	setdate &&
+	git commit -m "Initial" &&
+
+	echo uno >one &&
+	echo dos >two &&
+	git add two &&
+	setdate &&
+	git commit -a -m "Second" &&
+
+	git checkout -b left &&
+
+	echo $datestamp >one &&
+	setdate &&
+	git commit -a -m "Common #1" &&
+
+	echo $datestamp >one &&
+	setdate &&
+	git commit -a -m "Common #2" &&
+
+	git branch right &&
+
+	echo $datestamp >two &&
+	setdate &&
+	git commit -a -m "Left #3" &&
+
+	echo $datestamp >two &&
+	setdate &&
+	git commit -a -m "Left #4" &&
+
+	echo $datestamp >two &&
+	setdate &&
+	git commit -a -m "Left #5" &&
+
+	git checkout right &&
+
+	echo $datestamp >three &&
+	git add three &&
+	setdate &&
+	git commit -a -m "Right #3" &&
+
+	echo $datestamp >three &&
+	setdate &&
+	git commit -a -m "Right #4" &&
+
+	echo $datestamp >three &&
+	setdate &&
+	git commit -a -m "Right #5" &&
+
+	git show-branch
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+EOF
+
+test_expect_success 'merge-msg test #1' '
+
+	git checkout master &&
+	git fetch . left &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branch 'left' of ../trash
+EOF
+
+test_expect_success 'merge-msg test #2' '
+
+	git checkout master &&
+	git fetch ../trash left &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branch 'left'
+
+* left:
+  Left #5
+  Left #4
+  Left #3
+  Common #2
+  Common #1
+EOF
+
+test_expect_success 'merge-msg test #3' '
+
+	git repo-config merge.summary true &&
+
+	git checkout master &&
+	setdate &&
+	git fetch . left &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	diff -u actual expected
+'
+
+cat >expected <<\EOF
+Merge branches 'left' and 'right'
+
+* left:
+  Left #5
+  Left #4
+  Left #3
+  Common #2
+  Common #1
+
+* right:
+  Right #5
+  Right #4
+  Right #3
+  Common #2
+  Common #1
+EOF
+
+test_expect_success 'merge-msg test #4' '
+
+	git repo-config merge.summary true &&
+
+	git checkout master &&
+	setdate &&
+	git fetch . left right &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	diff -u actual expected
+'
+
+test_expect_success 'merge-msg test #5' '
+
+	git repo-config merge.summary yes &&
+
+	git checkout master &&
+	setdate &&
+	git fetch . left right &&
+
+	git fmt-merge-msg <.git/FETCH_HEAD >actual &&
+	diff -u actual expected
+'
+
+test_done
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 811a479..900ca93 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -38,4 +38,44 @@
     'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
     grep -E "^R100.+path1/COPYING.+path0/COPYING"'
 
+test_expect_success \
+    'adding another file' \
+    'cp ../../README path0/README &&
+     git-add path0/README &&
+     git-commit -m add2 -a'
+
+test_expect_success \
+    'moving whole subdirectory' \
+    'git-mv path0 path2'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path0/COPYING.+path2/COPYING" &&
+     git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path0/README.+path2/README"'
+
+test_expect_success \
+    'moving whole subdirectory into subdirectory' \
+    'git-mv path2 path1'
+
+test_expect_success \
+    'commiting the change' \
+    'git-commit -m dir-move -a'
+
+test_expect_success \
+    'checking the commit' \
+    'git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path2/COPYING.+path1/path2/COPYING" &&
+     git-diff-tree -r -M --name-status  HEAD^ HEAD | \
+     grep -E "^R100.+path2/README.+path1/path2/README"'
+
+test_expect_failure \
+    'do not move directory over existing directory' \
+    'mkdir path0 && mkdir path0/path2 && git-mv path2 path0'
+
 test_done
diff --git a/t/t7002-grep.sh b/t/t7002-grep.sh
new file mode 100755
index 0000000..00a7d76
--- /dev/null
+++ b/t/t7002-grep.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+#
+# Copyright (c) 2006 Junio C Hamano
+#
+
+test_description='git grep -w
+'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+	{
+		echo foo mmap bar
+		echo foo_mmap bar
+		echo foo_mmap bar mmap
+		echo foo mmap bar_mmap
+		echo foo_mmap bar mmap baz
+	} >file &&
+	echo x x xx x >x &&
+	echo y yy >y &&
+	echo zzz > z &&
+	git add file x y z &&
+	git commit -m initial
+'
+
+for H in HEAD ''
+do
+	case "$H" in
+	HEAD)	HC='HEAD:' L='HEAD' ;;
+	'')	HC= L='in working tree' ;;
+	esac
+
+	test_expect_success "grep -w $L" '
+		{
+			echo ${HC}file:1:foo mmap bar
+			echo ${HC}file:3:foo_mmap bar mmap
+			echo ${HC}file:4:foo mmap bar_mmap
+			echo ${HC}file:5:foo_mmap bar mmap baz
+		} >expected &&
+		git grep -n -w -e mmap $H >actual &&
+		diff expected actual
+	'
+
+	test_expect_success "grep -w $L (x)" '
+		{
+			echo ${HC}x:1:x x xx x
+		} >expected &&
+		git grep -n -w -e "x xx* x" $H >actual &&
+		diff expected actual
+	'
+
+	test_expect_success "grep -w $L (y-1)" '
+		{
+			echo ${HC}y:1:y yy
+		} >expected &&
+		git grep -n -w -e "^y" $H >actual &&
+		diff expected actual
+	'
+
+	test_expect_success "grep -w $L (y-2)" '
+		: >expected &&
+		if git grep -n -w -e "^y y" $H >actual
+		then
+			echo should not have matched
+			cat actual
+			false
+		else
+			diff expected actual
+		fi
+	'
+
+	test_expect_success "grep -w $L (z)" '
+		: >expected &&
+		if git grep -n -w -e "^z" $H >actual
+		then
+			echo should not have matched
+			cat actual
+			false
+		else
+			diff expected actual
+		fi
+	'
+done
+
+test_done
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 2496397..3a6490e 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,4 +6,10 @@
 PROG='git annotate'
 . ../annotate-tests.sh
 
+test_expect_success \
+    'Annotating an old revision works' \
+    '[ $(git annotate file master | awk "{print \$3}" | grep -c "^A$") -eq 2 ] && \
+     [ $(git annotate file master | awk "{print \$3}" | grep -c "^B$") -eq 2 ]'
+
+
 test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index a61da1e..e9ea33c 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -25,10 +25,13 @@
      git add fake.sendmail
      GIT_AUTHOR_NAME="A" git commit -a -m "Second."'
 
-test_expect_success \
-    'Extract patches and send' \
-    'git format-patch -n HEAD^1
-     git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" ./0001*txt'
+test_expect_success 'Extract patches' '
+    patches=`git format-patch -n HEAD^1`
+'
+
+test_expect_success 'Send patches' '
+     git send-email -from="Example <nobody@example.com>" --to=nobody@example.com --smtp-server="$(pwd)/fake.sendmail" $patches 2>errors
+'
 
 cat >expected <<\EOF
 !nobody@example.com!
diff --git a/contrib/git-svn/t/t0000-contrib-git-svn.sh b/t/t9100-git-svn-basic.sh
old mode 100644
new mode 100755
similarity index 95%
rename from contrib/git-svn/t/t0000-contrib-git-svn.sh
rename to t/t9100-git-svn-basic.sh
index b482bb6..34a3ccd
--- a/contrib/git-svn/t/t0000-contrib-git-svn.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -3,7 +3,7 @@
 # Copyright (c) 2006 Eric Wong
 #
 
-test_description='git-svn tests'
+test_description='git-svn basic tests'
 GIT_SVN_LC_ALL=$LC_ALL
 
 case "$LC_ALL" in
@@ -17,6 +17,8 @@
 
 . ./lib-git-svn.sh
 
+echo 'define NO_SVN_TESTS to skip git-svn tests'
+
 mkdir import
 cd import
 
@@ -168,7 +170,7 @@
 	     test -L $SVN_TREE/exec-2.sh"
 
 	name='modify a symlink to become a file'
-	git help > help || true
+	echo git help > help || true
 	rm exec-2.sh
 	cp help exec-2.sh
 	git update-index exec-2.sh
@@ -215,10 +217,10 @@
 rm -f expected
 if test "$have_utf8" = t
 then
-	echo tree f735671b89a7eb30cab1d8597de35bd4271ab813 > expected
+	echo tree bf522353586b1b883488f2bc73dab0d9f774b9a9 > expected
 fi
 cat >> expected <<\EOF
-tree 4b9af72bb861eaed053854ec502cf7df72618f0f
+tree 83654bb36f019ae4fe77a0171f81075972087624
 tree 031b8d557afc6fea52894eaebb45bec52f1ba6d1
 tree 0b094cbff17168f24c302e297f55bfac65eb8bd3
 tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
@@ -229,4 +231,3 @@
 test_expect_success "$name" "diff -u a expected"
 
 test_done
-
diff --git a/contrib/git-svn/t/t0001-contrib-git-svn-props.sh b/t/t9101-git-svn-props.sh
old mode 100644
new mode 100755
similarity index 100%
rename from contrib/git-svn/t/t0001-contrib-git-svn-props.sh
rename to t/t9101-git-svn-props.sh
diff --git a/contrib/git-svn/t/t0002-deep-rmdir.sh b/t/t9102-git-svn-deep-rmdir.sh
old mode 100644
new mode 100755
similarity index 100%
rename from contrib/git-svn/t/t0002-deep-rmdir.sh
rename to t/t9102-git-svn-deep-rmdir.sh
diff --git a/contrib/git-svn/t/t0003-graft-branches.sh b/t/t9103-git-svn-graft-branches.sh
old mode 100644
new mode 100755
similarity index 100%
rename from contrib/git-svn/t/t0003-graft-branches.sh
rename to t/t9103-git-svn-graft-branches.sh
diff --git a/contrib/git-svn/t/t0004-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
old mode 100644
new mode 100755
similarity index 100%
rename from contrib/git-svn/t/t0004-follow-parent.sh
rename to t/t9104-git-svn-follow-parent.sh
diff --git a/contrib/git-svn/t/t0005-commit-diff.sh b/t/t9105-git-svn-commit-diff.sh
old mode 100644
new mode 100755
similarity index 100%
rename from contrib/git-svn/t/t0005-commit-diff.sh
rename to t/t9105-git-svn-commit-diff.sh
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 05f6e79..470a909 100755
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -9,6 +9,8 @@
 PAGER=cat
 TZ=UTC
 export LANG LC_ALL PAGER TZ
+EDITOR=:
+VISUAL=:
 unset AUTHOR_DATE
 unset AUTHOR_EMAIL
 unset AUTHOR_NAME
@@ -26,10 +28,12 @@
 unset GIT_EXTERNAL_DIFF
 unset GIT_INDEX_FILE
 unset GIT_OBJECT_DIRECTORY
+unset GIT_TRACE
 unset SHA1_FILE_DIRECTORIES
 unset SHA1_FILE_DIRECTORY
 export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
 export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
+export EDITOR VISUAL
 
 # Each test should start with something like this, after copyright notices:
 #
diff --git a/tag.c b/tag.c
index 74d0dab..864ac1b 100644
--- a/tag.c
+++ b/tag.c
@@ -5,7 +5,7 @@
 
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 {
-	while (o && o->type == TYPE_TAG)
+	while (o && o->type == OBJ_TAG)
 		o = parse_object(((struct tag *)o)->tagged->sha1);
 	if (!o && warn) {
 		if (!warnlen)
@@ -21,12 +21,12 @@
         if (!obj) {
                 struct tag *ret = alloc_tag_node();
                 created_object(sha1, &ret->object);
-                ret->object.type = TYPE_TAG;
+                ret->object.type = OBJ_TAG;
                 return ret;
         }
 	if (!obj->type)
-		obj->type = TYPE_TAG;
-        if (obj->type != TYPE_TAG) {
+		obj->type = OBJ_TAG;
+        if (obj->type != OBJ_TAG) {
                 error("Object %s is a %s, not a tree",
                       sha1_to_hex(sha1), typename(obj->type));
                 return NULL;
diff --git a/templates/Makefile b/templates/Makefile
index 8f7f4fe..9e1ae1a 100644
--- a/templates/Makefile
+++ b/templates/Makefile
@@ -6,11 +6,9 @@
 template_dir ?= $(prefix)/share/git-core/templates/
 # DESTDIR=
 
-# Shell quote;
-# Result of this needs to be placed inside ''
-shq = $(subst ','\'',$(1))
-# This has surrounding ''
-shellquote = '$(call shq,$(1))'
+# Shell quote (do not use $(call) to accomodate ancient setups);
+DESTDIR_SQ = $(subst ','\'',$(DESTDIR))
+template_dir_SQ = $(subst ','\'',$(template_dir))
 
 all: boilerplates.made custom
 
@@ -43,6 +41,6 @@
 	rm -rf blt boilerplates.made
 
 install: all
-	$(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(template_dir))
+	$(INSTALL) -d -m755 '$(DESTDIR_SQ)$(template_dir_SQ)'
 	(cd blt && $(TAR) cf - .) | \
-	(cd $(call shellquote,$(DESTDIR)$(template_dir)) && $(TAR) xf -)
+	(cd '$(DESTDIR_SQ)$(template_dir_SQ)' && $(TAR) xf -)
diff --git a/templates/hooks--update b/templates/hooks--update
index d7a8f0a..76d5ac2 100644
--- a/templates/hooks--update
+++ b/templates/hooks--update
@@ -60,7 +60,7 @@
 			echo "Changes since $prev:"
 			git rev-list --pretty $prev..$3 | $short
 			echo ---
-			git diff $prev..$3 | diffstat -p1
+			git diff --stat $prev..$3
 			echo ---
 		fi
 		;;
@@ -75,7 +75,7 @@
 	base=$(git-merge-base "$2" "$3")
 	case "$base" in
 	"$2")
-		git diff "$3" "^$base" | diffstat -p1
+		git diff --stat "$3" "^$base"
 		echo
 		echo "New commits:"
 		;;
diff --git a/test-sha1.c b/test-sha1.c
new file mode 100644
index 0000000..78d7e98
--- /dev/null
+++ b/test-sha1.c
@@ -0,0 +1,47 @@
+#include "cache.h"
+
+int main(int ac, char **av)
+{
+	SHA_CTX ctx;
+	unsigned char sha1[20];
+	unsigned bufsz = 8192;
+	char *buffer;
+
+	if (ac == 2)
+		bufsz = strtoul(av[1], NULL, 10) * 1024 * 1024;
+
+	if (!bufsz)
+		bufsz = 8192;
+
+	while ((buffer = malloc(bufsz)) == NULL) {
+		fprintf(stderr, "bufsz %u is too big, halving...\n", bufsz);
+		bufsz /= 2;
+		if (bufsz < 1024)
+			die("OOPS");
+	}
+
+	SHA1_Init(&ctx);
+
+	while (1) {
+		ssize_t sz, this_sz;
+		char *cp = buffer;
+		unsigned room = bufsz;
+		this_sz = 0;
+		while (room) {
+			sz = xread(0, cp, room);
+			if (sz == 0)
+				break;
+			if (sz < 0)
+				die("test-sha1: %s", strerror(errno));
+			this_sz += sz;
+			cp += sz;
+			room -= sz;
+		}
+		if (this_sz == 0)
+			break;
+		SHA1_Update(&ctx, buffer, this_sz);
+	}
+	SHA1_Final(sha1, &ctx);
+	puts(sha1_to_hex(sha1));
+	exit(0);
+}
diff --git a/test-sha1.sh b/test-sha1.sh
new file mode 100755
index 0000000..640856a
--- /dev/null
+++ b/test-sha1.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+dd if=/dev/zero bs=1048576 count=100 2>/dev/null |
+/usr/bin/time ./test-sha1 >/dev/null
+
+while read expect cnt pfx
+do
+	case "$expect" in '#'*) continue ;; esac
+	actual=`
+		{
+			test -z "$pfx" || echo "$pfx"
+			dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+			tr '[\0]' '[g]'
+		} | ./test-sha1 $cnt
+	`
+	if test "$expect" = "$actual"
+	then
+		echo "OK: $expect $cnt $pfx"
+	else
+		echo >&2 "OOPS: $cnt"
+		echo >&2 "expect: $expect"
+		echo >&2 "actual: $actual"
+		exit 1
+	fi
+done <<EOF
+da39a3ee5e6b4b0d3255bfef95601890afd80709 0
+3f786850e387550fdab836ed7e6dc881de23001b 0 a
+5277cbb45a15902137d332d97e89cf8136545485 0 ab
+03cfd743661f07975fa2f1220c5194cbaff48451 0 abc
+3330b4373640f9e4604991e73c7e86bfd8da2dc3 0 abcd
+ec11312386ad561674f724b8cca7cf1796e26d1d 0 abcde
+bdc37c074ec4ee6050d68bc133c6b912f36474df 0 abcdef
+69bca99b923859f2dc486b55b87f49689b7358c7 0 abcdefg
+e414af7161c9554089f4106d6f1797ef14a73666 0 abcdefgh
+0707f2970043f9f7c22029482db27733deaec029 0 abcdefghi
+a4dd8aa74a5636728fe52451636e2e17726033aa 1
+9986b45e2f4d7086372533bb6953a8652fa3644a 1 frotz
+23d8d4f788e8526b4877548a32577543cbaaf51f 10
+8cd23f822ab44c7f481b8c92d591f6d1fcad431c 10 frotz
+f3b5604a4e604899c1233edb3bf1cc0ede4d8c32 512
+b095bd837a371593048136e429e9ac4b476e1bb3 512 frotz
+08fa81d6190948de5ccca3966340cc48c10cceac 1200 xyzzy
+e33a291f42c30a159733dd98b8b3e4ff34158ca0 4090 4G
+#a3bf783bc20caa958f6cb24dd140a7b21984838d 9999 nitfol
+EOF
+
+exit
+
+# generating test vectors
+# inputs are number of megabytes followed by some random string to prefix.
+
+while read cnt pfx
+do
+	actual=`
+		{
+			test -z "$pfx" || echo "$pfx"
+			dd if=/dev/zero bs=1048576 count=$cnt 2>/dev/null |
+			tr '[\0]' '[g]'
+		} | sha1sum |
+		sed -e 's/ .*//'
+	`
+	echo "$actual $cnt $pfx"
+done <<EOF
+0
+0 a
+0 ab
+0 abc
+0 abcd
+0 abcde
+0 abcdef
+0 abcdefg
+0 abcdefgh
+0 abcdefghi
+1
+1 frotz
+10
+10 frotz
+512
+512 frotz
+1200 xyzzy
+4090 4G
+9999 nitfol
+EOF
diff --git a/tree.c b/tree.c
index 1023655..a6032e3 100644
--- a/tree.c
+++ b/tree.c
@@ -131,12 +131,12 @@
 	if (!obj) {
 		struct tree *ret = alloc_tree_node();
 		created_object(sha1, &ret->object);
-		ret->object.type = TYPE_TREE;
+		ret->object.type = OBJ_TREE;
 		return ret;
 	}
 	if (!obj->type)
-		obj->type = TYPE_TREE;
-	if (obj->type != TYPE_TREE) {
+		obj->type = OBJ_TREE;
+	if (obj->type != OBJ_TREE) {
 		error("Object %s is a %s, not a tree",
 		      sha1_to_hex(sha1), typename(obj->type));
 		return NULL;
@@ -216,11 +216,11 @@
 	do {
 		if (!obj)
 			return NULL;
-		if (obj->type == TYPE_TREE)
+		if (obj->type == OBJ_TREE)
 			return (struct tree *) obj;
-		else if (obj->type == TYPE_COMMIT)
+		else if (obj->type == OBJ_COMMIT)
 			obj = &(((struct commit *) obj)->tree->object);
-		else if (obj->type == TYPE_TAG)
+		else if (obj->type == OBJ_TAG)
 			obj = ((struct tag *) obj)->tagged;
 		else
 			return NULL;
diff --git a/unpack-objects.c b/unpack-objects.c
index 3b824b0..48c1ee7 100644
--- a/unpack-objects.c
+++ b/unpack-objects.c
@@ -241,11 +241,6 @@
 	}
 }
 
-/*
- * We unpack from the end, older files first. Now, usually
- * there are deltas etc, so we'll not actually write the
- * objects in that order, but we might as well try..
- */
 static void unpack_all(void)
 {
 	int i;
diff --git a/upload-pack.c b/upload-pack.c
index 638e257..07ecdb4 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -51,6 +51,10 @@
 		if (fd == 3)
 			/* emergency quit */
 			fd = 2;
+		if (fd == 2) {
+			xwrite(fd, data, sz);
+			return sz;
+		}
 		return safe_write(fd, data, sz);
 	}
 	p = data;
@@ -328,7 +332,7 @@
 			o = parse_object(sha1);
 		if (!o)
 			die("oops (%s)", sha1_to_hex(sha1));
-		if (o->type == TYPE_COMMIT) {
+		if (o->type == OBJ_COMMIT) {
 			struct commit_list *parents;
 			if (o->flags & THEY_HAVE)
 				return 0;
@@ -459,7 +463,7 @@
 		o->flags |= OUR_REF;
 		nr_our_refs++;
 	}
-	if (o->type == TYPE_TAG) {
+	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, refname, 0);
 		packet_write(1, "%s %s^{}\n", sha1_to_hex(o->sha1), refname);
 	}
diff --git a/verify-pack.c b/verify-pack.c
index c99db9dd..357970d 100644
--- a/verify-pack.c
+++ b/verify-pack.c
@@ -1,43 +1,60 @@
 #include "cache.h"
 #include "pack.h"
 
-static int verify_one_pack(char *arg, int verbose)
+static int verify_one_pack(const char *path, int verbose)
 {
-	int len = strlen(arg);
-	struct packed_git *g;
-	
-	while (1) {
-		/* Should name foo.idx, but foo.pack may be named;
-		 * convert it to foo.idx
-		 */
-		if (!strcmp(arg + len - 5, ".pack")) {
-			strcpy(arg + len - 5, ".idx");
-			len--;
-		}
-		/* Should name foo.idx now */
-		if ((g = add_packed_git(arg, len, 1)))
-			break;
-		/* No?  did you name just foo? */
+	char arg[PATH_MAX];
+	int len;
+	struct packed_git *pack;
+	int err;
+
+	len = strlcpy(arg, path, PATH_MAX);
+	if (len >= PATH_MAX)
+		return error("name too long: %s", path);
+
+	/*
+	 * In addition to "foo.idx" we accept "foo.pack" and "foo";
+	 * normalize these forms to "foo.idx" for add_packed_git().
+	 */
+	if (has_extension(arg, ".pack")) {
+		strcpy(arg + len - 5, ".idx");
+		len--;
+	} else if (!has_extension(arg, ".idx")) {
+		if (len + 4 >= PATH_MAX)
+			return error("name too long: %s.idx", arg);
 		strcpy(arg + len, ".idx");
 		len += 4;
-		if ((g = add_packed_git(arg, len, 1)))
-			break;
-		return error("packfile %s not found.", arg);
 	}
-	return verify_pack(g, verbose);
+
+	/*
+	 * add_packed_git() uses our buffer (containing "foo.idx") to
+	 * build the pack filename ("foo.pack").  Make sure it fits.
+	 */
+	if (len + 1 >= PATH_MAX) {
+		arg[len - 4] = '\0';
+		return error("name too long: %s.pack", arg);
+	}
+
+	pack = add_packed_git(arg, len, 1);
+	if (!pack)
+		return error("packfile %s not found.", arg);
+
+	err = verify_pack(pack, verbose);
+	free(pack);
+
+	return err;
 }
 
 static const char verify_pack_usage[] = "git-verify-pack [-v] <pack>...";
 
 int main(int ac, char **av)
 {
-	int errs = 0;
+	int err = 0;
 	int verbose = 0;
 	int no_more_options = 0;
+	int nothing_done = 1;
 
 	while (1 < ac) {
-		char path[PATH_MAX];
-
 		if (!no_more_options && av[1][0] == '-') {
 			if (!strcmp("-v", av[1]))
 				verbose = 1;
@@ -47,11 +64,15 @@
 				usage(verify_pack_usage);
 		}
 		else {
-			strcpy(path, av[1]);
-			if (verify_one_pack(path, verbose))
-				errs++;
+			if (verify_one_pack(av[1], verbose))
+				err = 1;
+			nothing_done = 0;
 		}
 		ac--; av++;
 	}
-	return !!errs;
+
+	if (nothing_done)
+		usage(verify_pack_usage);
+
+	return err;
 }
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 2ce10b4..c9f8178 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -39,6 +39,7 @@
 #define XDL_PATCH_IGNOREBSPACE (1 << 8)
 
 #define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_COMMON (1 << 1)
 
 #define XDL_MMB_READONLY (1 << 0)
 
diff --git a/xdiff/xdiffi.c b/xdiff/xdiffi.c
index ed7ad20..d76e76a 100644
--- a/xdiff/xdiffi.c
+++ b/xdiff/xdiffi.c
@@ -84,7 +84,7 @@
 		 * We need to extent the diagonal "domain" by one. If the next
 		 * values exits the box boundaries we need to change it in the
 		 * opposite direction because (max - min) must be a power of two.
-		 * Also we initialize the extenal K value to -1 so that we can
+		 * Also we initialize the external K value to -1 so that we can
 		 * avoid extra conditions check inside the core loop.
 		 */
 		if (fmin > dmin)
@@ -119,7 +119,7 @@
 		 * We need to extent the diagonal "domain" by one. If the next
 		 * values exits the box boundaries we need to change it in the
 		 * opposite direction because (max - min) must be a power of two.
-		 * Also we initialize the extenal K value to -1 so that we can
+		 * Also we initialize the external K value to -1 so that we can
 		 * avoid extra conditions check inside the core loop.
 		 */
 		if (bmin > dmin)
@@ -405,7 +405,7 @@
 	/*
 	 * This is the same of what GNU diff does. Move back and forward
 	 * change groups for a consistent and pretty diff output. This also
-	 * helps in finding joineable change groups and reduce the diff size.
+	 * helps in finding joinable change groups and reduce the diff size.
 	 */
 	for (ix = ixo = 0;;) {
 		/*
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index ad5bfb1..714c563 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -100,6 +100,21 @@
 }
 
 
+int xdl_emit_common(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+		    xdemitconf_t const *xecfg) {
+	xdfile_t *xdf = &xe->xdf1;
+	const char *rchg = xdf->rchg;
+	long ix;
+
+	for (ix = 0; ix < xdf->nrec; ix++) {
+		if (rchg[ix])
+			continue;
+		if (xdl_emit_record(xdf, ix, "", ecb))
+			return -1;
+	}
+	return 0;
+}
+
 int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 		  xdemitconf_t const *xecfg) {
 	long s1, s2, e1, e2, lctx;
@@ -107,6 +122,9 @@
 	char funcbuf[40];
 	long funclen = 0;
 
+	if (xecfg->flags & XDL_EMIT_COMMON)
+		return xdl_emit_common(xe, xscr, ecb, xecfg);
+
 	for (xch = xche = xscr; xch; xch = xche->next) {
 		xche = xdl_get_hunk(xch, xecfg);